~behda/+junk/udisks2.original

« back to all changes in this revision

Viewing changes to src/udisksbasejob.c

  • Committer: behda
  • Date: 2014-05-24 15:15:11 UTC
  • Revision ID: pauvitk@gmail.com-20140524151511-3vtr0uubjewx3z2j
Initial commit of source code and Debian packaging.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
 
2
 *
 
3
 * Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or modify
 
6
 * it under the terms of the GNU General Public License as published by
 
7
 * the Free Software Foundation; either version 2 of the License, or
 
8
 * (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this program; if not, write to the Free Software
 
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include "config.h"
 
22
#include <glib/gi18n-lib.h>
 
23
 
 
24
#include <sys/types.h>
 
25
#include <sys/wait.h>
 
26
 
 
27
#include "udisksbasejob.h"
 
28
#include "udisksdaemon.h"
 
29
#include "udisksdaemonutil.h"
 
30
#include "udisks-daemon-marshal.h"
 
31
 
 
32
#define MAX_SAMPLES 100
 
33
 
 
34
typedef struct
 
35
{
 
36
  gint64 time_usec;
 
37
  gdouble value;
 
38
} Sample;
 
39
 
 
40
/**
 
41
 * SECTION:udisksbasejob
 
42
 * @title: UDisksBaseJob
 
43
 * @short_description: Base class for jobs.
 
44
 *
 
45
 * This type provides common features needed by all job types.
 
46
 */
 
47
 
 
48
struct _UDisksBaseJobPrivate
 
49
{
 
50
  GCancellable *cancellable;
 
51
  UDisksDaemon *daemon;
 
52
 
 
53
  gboolean auto_estimate;
 
54
  gulong notify_progress_signal_handler_id;
 
55
 
 
56
  Sample *samples;
 
57
  guint num_samples;
 
58
};
 
59
 
 
60
static void job_iface_init (UDisksJobIface *iface);
 
61
 
 
62
enum
 
63
{
 
64
  PROP_0,
 
65
  PROP_DAEMON,
 
66
  PROP_CANCELLABLE,
 
67
  PROP_AUTO_ESTIMATE,
 
68
};
 
69
 
 
70
G_DEFINE_ABSTRACT_TYPE_WITH_CODE (UDisksBaseJob, udisks_base_job, UDISKS_TYPE_JOB_SKELETON,
 
71
                                  G_IMPLEMENT_INTERFACE (UDISKS_TYPE_JOB, job_iface_init));
 
72
 
 
73
static void
 
74
udisks_base_job_finalize (GObject *object)
 
75
{
 
76
  UDisksBaseJob *job = UDISKS_BASE_JOB (object);
 
77
 
 
78
 
 
79
  g_free (job->priv->samples);
 
80
 
 
81
  if (job->priv->cancellable != NULL)
 
82
    {
 
83
      g_object_unref (job->priv->cancellable);
 
84
      job->priv->cancellable = NULL;
 
85
    }
 
86
 
 
87
  if (G_OBJECT_CLASS (udisks_base_job_parent_class)->finalize != NULL)
 
88
    G_OBJECT_CLASS (udisks_base_job_parent_class)->finalize (object);
 
89
}
 
90
 
 
91
static void
 
92
udisks_base_job_get_property (GObject    *object,
 
93
                                  guint       prop_id,
 
94
                                  GValue     *value,
 
95
                                  GParamSpec *pspec)
 
96
{
 
97
  UDisksBaseJob *job = UDISKS_BASE_JOB (object);
 
98
 
 
99
  switch (prop_id)
 
100
    {
 
101
    case PROP_DAEMON:
 
102
      g_value_set_object (value, udisks_base_job_get_daemon (job));
 
103
      break;
 
104
 
 
105
    case PROP_CANCELLABLE:
 
106
      g_value_set_object (value, job->priv->cancellable);
 
107
      break;
 
108
 
 
109
    case PROP_AUTO_ESTIMATE:
 
110
      g_value_set_boolean (value, job->priv->auto_estimate);
 
111
      break;
 
112
 
 
113
    default:
 
114
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
115
      break;
 
116
    }
 
117
}
 
118
 
 
119
static void
 
120
udisks_base_job_set_property (GObject      *object,
 
121
                                  guint         prop_id,
 
122
                                  const GValue *value,
 
123
                                  GParamSpec   *pspec)
 
124
{
 
125
  UDisksBaseJob *job = UDISKS_BASE_JOB (object);
 
126
 
 
127
  switch (prop_id)
 
128
    {
 
129
    case PROP_DAEMON:
 
130
      g_assert (job->priv->daemon == NULL);
 
131
      /* we don't take a reference to the daemon */
 
132
      job->priv->daemon = g_value_get_object (value);
 
133
      break;
 
134
 
 
135
    case PROP_CANCELLABLE:
 
136
      g_assert (job->priv->cancellable == NULL);
 
137
      job->priv->cancellable = g_value_dup_object (value);
 
138
      break;
 
139
 
 
140
    case PROP_AUTO_ESTIMATE:
 
141
      udisks_base_job_set_auto_estimate (job, g_value_get_boolean (value));
 
142
      break;
 
143
 
 
144
    default:
 
145
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
146
      break;
 
147
    }
 
148
}
 
149
 
 
150
/* ---------------------------------------------------------------------------------------------------- */
 
151
 
 
152
static void
 
153
udisks_base_job_constructed (GObject *object)
 
154
{
 
155
  UDisksBaseJob *job = UDISKS_BASE_JOB (object);
 
156
 
 
157
  if (job->priv->cancellable == NULL)
 
158
    job->priv->cancellable = g_cancellable_new ();
 
159
 
 
160
  if (G_OBJECT_CLASS (udisks_base_job_parent_class)->constructed != NULL)
 
161
    G_OBJECT_CLASS (udisks_base_job_parent_class)->constructed (object);
 
162
}
 
163
 
 
164
/* ---------------------------------------------------------------------------------------------------- */
 
165
 
 
166
static void
 
167
udisks_base_job_init (UDisksBaseJob *job)
 
168
{
 
169
  gint64 now_usec;
 
170
 
 
171
  job->priv = G_TYPE_INSTANCE_GET_PRIVATE (job, UDISKS_TYPE_BASE_JOB, UDisksBaseJobPrivate);
 
172
 
 
173
  now_usec = g_get_real_time ();
 
174
  udisks_job_set_start_time (UDISKS_JOB (job), now_usec);
 
175
}
 
176
 
 
177
static void
 
178
udisks_base_job_class_init (UDisksBaseJobClass *klass)
 
179
{
 
180
  GObjectClass *gobject_class;
 
181
 
 
182
  gobject_class = G_OBJECT_CLASS (klass);
 
183
  gobject_class->finalize     = udisks_base_job_finalize;
 
184
  gobject_class->constructed  = udisks_base_job_constructed;
 
185
  gobject_class->set_property = udisks_base_job_set_property;
 
186
  gobject_class->get_property = udisks_base_job_get_property;
 
187
 
 
188
  /**
 
189
   * UDisksBaseJob:daemon:
 
190
   *
 
191
   * The #UDisksDaemon the object is for.
 
192
   */
 
193
  g_object_class_install_property (gobject_class,
 
194
                                   PROP_DAEMON,
 
195
                                   g_param_spec_object ("daemon",
 
196
                                                        "Daemon",
 
197
                                                        "The daemon the object is for",
 
198
                                                        UDISKS_TYPE_DAEMON,
 
199
                                                        G_PARAM_READABLE |
 
200
                                                        G_PARAM_WRITABLE |
 
201
                                                        G_PARAM_CONSTRUCT_ONLY |
 
202
                                                        G_PARAM_STATIC_STRINGS));
 
203
 
 
204
  /**
 
205
   * UDisksBaseJob:cancellable:
 
206
   *
 
207
   * The #GCancellable to use.
 
208
   */
 
209
  g_object_class_install_property (gobject_class,
 
210
                                   PROP_CANCELLABLE,
 
211
                                   g_param_spec_object ("cancellable",
 
212
                                                        "Cancellable",
 
213
                                                        "The GCancellable to use",
 
214
                                                        G_TYPE_CANCELLABLE,
 
215
                                                        G_PARAM_READABLE |
 
216
                                                        G_PARAM_WRITABLE |
 
217
                                                        G_PARAM_CONSTRUCT_ONLY |
 
218
                                                        G_PARAM_STATIC_STRINGS));
 
219
 
 
220
  /**
 
221
   * UDisksBaseJob:auto-estimate:
 
222
   *
 
223
   * If %TRUE, the #UDisksJob:expected-end-time property will be
 
224
   * automatically updated every time the #UDisksJob:progress property
 
225
   * is updated.
 
226
   */
 
227
  g_object_class_install_property (gobject_class,
 
228
                                   PROP_AUTO_ESTIMATE,
 
229
                                   g_param_spec_boolean ("auto-estimate",
 
230
                                                         "Auto Estimate",
 
231
                                                         "Whether to automatically estimate end time",
 
232
                                                         FALSE,
 
233
                                                         G_PARAM_READABLE |
 
234
                                                         G_PARAM_WRITABLE |
 
235
                                                         G_PARAM_STATIC_STRINGS));
 
236
 
 
237
  g_type_class_add_private (klass, sizeof (UDisksBaseJobPrivate));
 
238
}
 
239
 
 
240
/* ---------------------------------------------------------------------------------------------------- */
 
241
 
 
242
/**
 
243
 * udisks_base_job_get_cancellable:
 
244
 * @job: A #UDisksBaseJob.
 
245
 *
 
246
 * Gets the #GCancellable for @job.
 
247
 *
 
248
 * Returns: A #GCancellable. Do not free, the object belongs to @job.
 
249
 */
 
250
GCancellable *
 
251
udisks_base_job_get_cancellable  (UDisksBaseJob  *job)
 
252
{
 
253
  g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), NULL);
 
254
  return job->priv->cancellable;
 
255
}
 
256
 
 
257
/* ---------------------------------------------------------------------------------------------------- */
 
258
 
 
259
/**
 
260
 * udisks_base_job_get_daemon:
 
261
 * @job: A #UDisksBaseJob.
 
262
 *
 
263
 * Gets the #UDisksDaemon for @job.
 
264
 *
 
265
 * Returns: A #UDisksDaemon. Do not free, the object belongs to @job.
 
266
 */
 
267
UDisksDaemon *
 
268
udisks_base_job_get_daemon  (UDisksBaseJob  *job)
 
269
{
 
270
  g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), NULL);
 
271
  return job->priv->daemon;
 
272
}
 
273
 
 
274
/* ---------------------------------------------------------------------------------------------------- */
 
275
 
 
276
/**
 
277
 * udisks_base_job_add_object:
 
278
 * @job: A #UDisksBaseJob.
 
279
 * @object: A #UDisksObject.
 
280
 *
 
281
 * Adds the object path for @object to the <link
 
282
 * linkend="gdbus-property-org-freedesktop-UDisks2-Job.Objects">Objects</link>
 
283
 * array. If the object path is already in the array, does nothing.
 
284
 */
 
285
void
 
286
udisks_base_job_add_object (UDisksBaseJob  *job,
 
287
                            UDisksObject   *object)
 
288
{
 
289
  const gchar *object_path;
 
290
  const gchar *const *paths;
 
291
  const gchar **p;
 
292
  guint n;
 
293
 
 
294
  g_return_if_fail (UDISKS_IS_BASE_JOB (job));
 
295
  g_return_if_fail (UDISKS_IS_OBJECT (object));
 
296
 
 
297
  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
 
298
  paths = udisks_job_get_objects (UDISKS_JOB (job));
 
299
  for (n = 0; paths != NULL && paths[n] != NULL; n++)
 
300
    {
 
301
      if (g_strcmp0 (paths[n], object_path) == 0)
 
302
        goto out;
 
303
    }
 
304
 
 
305
  p = g_new0 (const gchar *, n + 2);
 
306
  p[n] = object_path;
 
307
  udisks_job_set_objects (UDISKS_JOB (job), p);
 
308
  g_free (p);
 
309
 
 
310
 out:
 
311
  ;
 
312
}
 
313
 
 
314
/**
 
315
 * udisks_base_job_remove_object:
 
316
 * @job: A #UDisksBaseJob.
 
317
 * @object: A #UDisksObject.
 
318
 *
 
319
 * Removes the object path for @object to the <link
 
320
 * linkend="gdbus-property-org-freedesktop-UDisks2-Job.Objects">Objects</link>
 
321
 * array. If the object path is not in the array, does nothing.
 
322
 */
 
323
void
 
324
udisks_base_job_remove_object (UDisksBaseJob  *job,
 
325
                               UDisksObject   *object)
 
326
{
 
327
  const gchar *object_path;
 
328
  const gchar *const *paths;
 
329
  GPtrArray *p = NULL;
 
330
  guint n;
 
331
 
 
332
  g_return_if_fail (UDISKS_IS_BASE_JOB (job));
 
333
  g_return_if_fail (UDISKS_IS_OBJECT (object));
 
334
 
 
335
  object_path = g_dbus_object_get_object_path (G_DBUS_OBJECT (object));
 
336
  paths = udisks_job_get_objects (UDISKS_JOB (job));
 
337
  for (n = 0; paths != NULL && paths[n] != NULL; n++)
 
338
    {
 
339
      if (g_strcmp0 (paths[n], object_path) != 0)
 
340
        {
 
341
          if (p == NULL)
 
342
            p = g_ptr_array_new ();
 
343
          g_ptr_array_add (p, (gpointer) paths[n]);
 
344
        }
 
345
    }
 
346
 
 
347
  if (p != NULL)
 
348
    {
 
349
      g_ptr_array_add (p, NULL);
 
350
      udisks_job_set_objects (UDISKS_JOB (job), (const gchar *const *) p->pdata);
 
351
      g_ptr_array_free (p, TRUE);
 
352
    }
 
353
  else
 
354
    {
 
355
      udisks_job_set_objects (UDISKS_JOB (job), NULL);
 
356
    }
 
357
}
 
358
 
 
359
/* ---------------------------------------------------------------------------------------------------- */
 
360
 
 
361
static gboolean
 
362
handle_cancel (UDisksJob              *_job,
 
363
               GDBusMethodInvocation  *invocation,
 
364
               GVariant               *options)
 
365
{
 
366
  UDisksBaseJob *job = UDISKS_BASE_JOB (_job);
 
367
  UDisksObject *object = NULL;
 
368
  const gchar *action_id;
 
369
  const gchar *message;
 
370
  uid_t caller_uid;
 
371
  gid_t caller_gid;
 
372
  GError *error = NULL;
 
373
 
 
374
  object = udisks_daemon_util_dup_object (job, &error);
 
375
  if (object == NULL)
 
376
    {
 
377
      g_dbus_method_invocation_take_error (invocation, error);
 
378
      goto out;
 
379
    }
 
380
 
 
381
  if (!udisks_daemon_util_get_caller_uid_sync (job->priv->daemon,
 
382
                                               invocation,
 
383
                                               NULL /* GCancellable */,
 
384
                                               &caller_uid,
 
385
                                               &caller_gid,
 
386
                                               NULL,
 
387
                                               &error))
 
388
    {
 
389
      g_dbus_method_invocation_take_error (invocation, error);
 
390
      goto out;
 
391
    }
 
392
 
 
393
  if (!udisks_job_get_cancelable (_job))
 
394
    {
 
395
      g_dbus_method_invocation_return_error (invocation,
 
396
                                             UDISKS_ERROR,
 
397
                                             UDISKS_ERROR_FAILED,
 
398
                                             "The job cannot be canceled");
 
399
      goto out;
 
400
    }
 
401
 
 
402
  /* Translators: Shown in authentication dialog when canceling a job.
 
403
   */
 
404
  message = N_("Authentication is required to cancel a job");
 
405
  action_id = "org.freedesktop.udisks2.cancel-job";
 
406
  if (caller_uid != udisks_job_get_started_by_uid (UDISKS_JOB (job)))
 
407
    action_id = "org.freedesktop.udisks2.cancel-job-other-user";
 
408
 
 
409
  if (!udisks_daemon_util_check_authorization_sync (job->priv->daemon,
 
410
                                                    object,
 
411
                                                    action_id,
 
412
                                                    options,
 
413
                                                    message,
 
414
                                                    invocation))
 
415
    goto out;
 
416
 
 
417
  if (g_cancellable_is_cancelled (job->priv->cancellable))
 
418
    {
 
419
      g_dbus_method_invocation_return_error (invocation,
 
420
                                             UDISKS_ERROR,
 
421
                                             UDISKS_ERROR_ALREADY_CANCELLED,
 
422
                                             "The job has already been cancelled");
 
423
    }
 
424
  else
 
425
    {
 
426
      g_cancellable_cancel (job->priv->cancellable);
 
427
      udisks_job_complete_cancel (UDISKS_JOB (job), invocation);
 
428
    }
 
429
 
 
430
 out:
 
431
  g_clear_object (&object);
 
432
  return TRUE;
 
433
}
 
434
 
 
435
/* ---------------------------------------------------------------------------------------------------- */
 
436
 
 
437
static void
 
438
job_iface_init (UDisksJobIface *iface)
 
439
{
 
440
  iface->handle_cancel   = handle_cancel;
 
441
}
 
442
 
 
443
/* ---------------------------------------------------------------------------------------------------- */
 
444
 
 
445
/**
 
446
 * udisks_base_job_get_auto_estimate:
 
447
 * @job: A #UDisksBaseJob.
 
448
 *
 
449
 * Gets whether auto-estimation is being used.
 
450
 *
 
451
 * Returns: %TRUE if auto-estimation is being used, %FALSE otherwise.
 
452
 */
 
453
gboolean
 
454
udisks_base_job_get_auto_estimate (UDisksBaseJob  *job)
 
455
{
 
456
  g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), FALSE);
 
457
  return job->priv->auto_estimate;
 
458
}
 
459
 
 
460
 
 
461
static void
 
462
on_notify_progress (GObject     *object,
 
463
                    GParamSpec  *spec,
 
464
                    gpointer     user_data)
 
465
{
 
466
  UDisksBaseJob *job = UDISKS_BASE_JOB (user_data);
 
467
  Sample *sample;
 
468
  guint n;
 
469
  gdouble sum_of_speeds;
 
470
  guint num_speeds;
 
471
  gdouble avg_speed;
 
472
  gint64 usec_remaining;
 
473
  gint64 now;
 
474
  guint64 bytes;
 
475
  gdouble current_progress;
 
476
 
 
477
  now = g_get_real_time ();
 
478
  current_progress = udisks_job_get_progress (UDISKS_JOB (job));
 
479
 
 
480
  /* first add new sample... */
 
481
  if (job->priv->num_samples == MAX_SAMPLES)
 
482
    {
 
483
      memmove (job->priv->samples, job->priv->samples + 1, sizeof (Sample) * (MAX_SAMPLES - 1));
 
484
      job->priv->num_samples -= 1;
 
485
    }
 
486
  sample = &job->priv->samples[job->priv->num_samples++];
 
487
  sample->time_usec = now;
 
488
  sample->value = current_progress;
 
489
 
 
490
  /* ... then update expected-end-time from samples - we want at
 
491
   * least five samples before making an estimate...
 
492
   */
 
493
  if (job->priv->num_samples < 5)
 
494
    goto out;
 
495
 
 
496
  num_speeds = 0;
 
497
  sum_of_speeds = 0.0;
 
498
  for (n = 1; n < job->priv->num_samples; n++)
 
499
    {
 
500
      Sample *a = &job->priv->samples[n-1];
 
501
      Sample *b = &job->priv->samples[n];
 
502
      gdouble speed;
 
503
      speed = (b->value - a->value) / (b->time_usec - a->time_usec);
 
504
      sum_of_speeds += speed;
 
505
      num_speeds++;
 
506
    }
 
507
  avg_speed = sum_of_speeds / num_speeds;
 
508
 
 
509
  bytes = udisks_job_get_bytes (UDISKS_JOB (job));
 
510
  if (bytes > 0)
 
511
    {
 
512
      udisks_job_set_rate (UDISKS_JOB (job), bytes * avg_speed * G_USEC_PER_SEC);
 
513
    }
 
514
  else
 
515
    {
 
516
      udisks_job_set_rate (UDISKS_JOB (job), 0);
 
517
    }
 
518
 
 
519
  usec_remaining = (1.0 - current_progress) / avg_speed;
 
520
  udisks_job_set_expected_end_time (UDISKS_JOB (job), now + usec_remaining);
 
521
 
 
522
 out:
 
523
  ;
 
524
}
 
525
 
 
526
/**
 
527
 * udisks_base_job_set_auto_estimate:
 
528
 * @job: A #UDisksBaseJob.
 
529
 * @value: %TRUE if auto-estimation is to be use, %FALSE otherwise.
 
530
 *
 
531
 * Sets whether auto-estimation is being used.
 
532
 */
 
533
void
 
534
udisks_base_job_set_auto_estimate (UDisksBaseJob  *job,
 
535
                                   gboolean        value)
 
536
{
 
537
  g_return_if_fail (UDISKS_IS_BASE_JOB (job));
 
538
 
 
539
  if (!!value == !!job->priv->auto_estimate)
 
540
    goto out;
 
541
 
 
542
  if (value)
 
543
    {
 
544
      if (job->priv->samples == NULL)
 
545
        job->priv->samples = g_new0 (Sample, MAX_SAMPLES);
 
546
      g_assert_cmpint (job->priv->notify_progress_signal_handler_id, ==, 0);
 
547
      job->priv->notify_progress_signal_handler_id = g_signal_connect (job,
 
548
                                                                       "notify::progress",
 
549
                                                                       G_CALLBACK (on_notify_progress),
 
550
                                                                       job);
 
551
      g_assert_cmpint (job->priv->notify_progress_signal_handler_id, !=, 0);
 
552
    }
 
553
  else
 
554
    {
 
555
      g_assert_cmpint (job->priv->notify_progress_signal_handler_id, !=, 0);
 
556
      g_signal_handler_disconnect (job, job->priv->notify_progress_signal_handler_id);
 
557
      job->priv->notify_progress_signal_handler_id = 0;
 
558
    }
 
559
 
 
560
  job->priv->auto_estimate = !!value;
 
561
  g_object_notify (G_OBJECT (job), "auto-estimate");
 
562
 
 
563
 out:
 
564
  ;
 
565
}