1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
3
* Copyright (C) 2007-2010 David Zeuthen <zeuthen@gmail.com>
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.
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.
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
22
#include <glib/gi18n-lib.h>
24
#include <sys/types.h>
27
#include "udisksbasejob.h"
28
#include "udisksdaemon.h"
29
#include "udisksdaemonutil.h"
30
#include "udisks-daemon-marshal.h"
32
#define MAX_SAMPLES 100
41
* SECTION:udisksbasejob
42
* @title: UDisksBaseJob
43
* @short_description: Base class for jobs.
45
* This type provides common features needed by all job types.
48
struct _UDisksBaseJobPrivate
50
GCancellable *cancellable;
53
gboolean auto_estimate;
54
gulong notify_progress_signal_handler_id;
60
static void job_iface_init (UDisksJobIface *iface);
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));
74
udisks_base_job_finalize (GObject *object)
76
UDisksBaseJob *job = UDISKS_BASE_JOB (object);
79
g_free (job->priv->samples);
81
if (job->priv->cancellable != NULL)
83
g_object_unref (job->priv->cancellable);
84
job->priv->cancellable = NULL;
87
if (G_OBJECT_CLASS (udisks_base_job_parent_class)->finalize != NULL)
88
G_OBJECT_CLASS (udisks_base_job_parent_class)->finalize (object);
92
udisks_base_job_get_property (GObject *object,
97
UDisksBaseJob *job = UDISKS_BASE_JOB (object);
102
g_value_set_object (value, udisks_base_job_get_daemon (job));
105
case PROP_CANCELLABLE:
106
g_value_set_object (value, job->priv->cancellable);
109
case PROP_AUTO_ESTIMATE:
110
g_value_set_boolean (value, job->priv->auto_estimate);
114
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
120
udisks_base_job_set_property (GObject *object,
125
UDisksBaseJob *job = UDISKS_BASE_JOB (object);
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);
135
case PROP_CANCELLABLE:
136
g_assert (job->priv->cancellable == NULL);
137
job->priv->cancellable = g_value_dup_object (value);
140
case PROP_AUTO_ESTIMATE:
141
udisks_base_job_set_auto_estimate (job, g_value_get_boolean (value));
145
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
150
/* ---------------------------------------------------------------------------------------------------- */
153
udisks_base_job_constructed (GObject *object)
155
UDisksBaseJob *job = UDISKS_BASE_JOB (object);
157
if (job->priv->cancellable == NULL)
158
job->priv->cancellable = g_cancellable_new ();
160
if (G_OBJECT_CLASS (udisks_base_job_parent_class)->constructed != NULL)
161
G_OBJECT_CLASS (udisks_base_job_parent_class)->constructed (object);
164
/* ---------------------------------------------------------------------------------------------------- */
167
udisks_base_job_init (UDisksBaseJob *job)
171
job->priv = G_TYPE_INSTANCE_GET_PRIVATE (job, UDISKS_TYPE_BASE_JOB, UDisksBaseJobPrivate);
173
now_usec = g_get_real_time ();
174
udisks_job_set_start_time (UDISKS_JOB (job), now_usec);
178
udisks_base_job_class_init (UDisksBaseJobClass *klass)
180
GObjectClass *gobject_class;
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;
189
* UDisksBaseJob:daemon:
191
* The #UDisksDaemon the object is for.
193
g_object_class_install_property (gobject_class,
195
g_param_spec_object ("daemon",
197
"The daemon the object is for",
201
G_PARAM_CONSTRUCT_ONLY |
202
G_PARAM_STATIC_STRINGS));
205
* UDisksBaseJob:cancellable:
207
* The #GCancellable to use.
209
g_object_class_install_property (gobject_class,
211
g_param_spec_object ("cancellable",
213
"The GCancellable to use",
217
G_PARAM_CONSTRUCT_ONLY |
218
G_PARAM_STATIC_STRINGS));
221
* UDisksBaseJob:auto-estimate:
223
* If %TRUE, the #UDisksJob:expected-end-time property will be
224
* automatically updated every time the #UDisksJob:progress property
227
g_object_class_install_property (gobject_class,
229
g_param_spec_boolean ("auto-estimate",
231
"Whether to automatically estimate end time",
235
G_PARAM_STATIC_STRINGS));
237
g_type_class_add_private (klass, sizeof (UDisksBaseJobPrivate));
240
/* ---------------------------------------------------------------------------------------------------- */
243
* udisks_base_job_get_cancellable:
244
* @job: A #UDisksBaseJob.
246
* Gets the #GCancellable for @job.
248
* Returns: A #GCancellable. Do not free, the object belongs to @job.
251
udisks_base_job_get_cancellable (UDisksBaseJob *job)
253
g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), NULL);
254
return job->priv->cancellable;
257
/* ---------------------------------------------------------------------------------------------------- */
260
* udisks_base_job_get_daemon:
261
* @job: A #UDisksBaseJob.
263
* Gets the #UDisksDaemon for @job.
265
* Returns: A #UDisksDaemon. Do not free, the object belongs to @job.
268
udisks_base_job_get_daemon (UDisksBaseJob *job)
270
g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), NULL);
271
return job->priv->daemon;
274
/* ---------------------------------------------------------------------------------------------------- */
277
* udisks_base_job_add_object:
278
* @job: A #UDisksBaseJob.
279
* @object: A #UDisksObject.
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.
286
udisks_base_job_add_object (UDisksBaseJob *job,
287
UDisksObject *object)
289
const gchar *object_path;
290
const gchar *const *paths;
294
g_return_if_fail (UDISKS_IS_BASE_JOB (job));
295
g_return_if_fail (UDISKS_IS_OBJECT (object));
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++)
301
if (g_strcmp0 (paths[n], object_path) == 0)
305
p = g_new0 (const gchar *, n + 2);
307
udisks_job_set_objects (UDISKS_JOB (job), p);
315
* udisks_base_job_remove_object:
316
* @job: A #UDisksBaseJob.
317
* @object: A #UDisksObject.
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.
324
udisks_base_job_remove_object (UDisksBaseJob *job,
325
UDisksObject *object)
327
const gchar *object_path;
328
const gchar *const *paths;
332
g_return_if_fail (UDISKS_IS_BASE_JOB (job));
333
g_return_if_fail (UDISKS_IS_OBJECT (object));
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++)
339
if (g_strcmp0 (paths[n], object_path) != 0)
342
p = g_ptr_array_new ();
343
g_ptr_array_add (p, (gpointer) paths[n]);
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);
355
udisks_job_set_objects (UDISKS_JOB (job), NULL);
359
/* ---------------------------------------------------------------------------------------------------- */
362
handle_cancel (UDisksJob *_job,
363
GDBusMethodInvocation *invocation,
366
UDisksBaseJob *job = UDISKS_BASE_JOB (_job);
367
UDisksObject *object = NULL;
368
const gchar *action_id;
369
const gchar *message;
372
GError *error = NULL;
374
object = udisks_daemon_util_dup_object (job, &error);
377
g_dbus_method_invocation_take_error (invocation, error);
381
if (!udisks_daemon_util_get_caller_uid_sync (job->priv->daemon,
383
NULL /* GCancellable */,
389
g_dbus_method_invocation_take_error (invocation, error);
393
if (!udisks_job_get_cancelable (_job))
395
g_dbus_method_invocation_return_error (invocation,
398
"The job cannot be canceled");
402
/* Translators: Shown in authentication dialog when canceling a job.
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";
409
if (!udisks_daemon_util_check_authorization_sync (job->priv->daemon,
417
if (g_cancellable_is_cancelled (job->priv->cancellable))
419
g_dbus_method_invocation_return_error (invocation,
421
UDISKS_ERROR_ALREADY_CANCELLED,
422
"The job has already been cancelled");
426
g_cancellable_cancel (job->priv->cancellable);
427
udisks_job_complete_cancel (UDISKS_JOB (job), invocation);
431
g_clear_object (&object);
435
/* ---------------------------------------------------------------------------------------------------- */
438
job_iface_init (UDisksJobIface *iface)
440
iface->handle_cancel = handle_cancel;
443
/* ---------------------------------------------------------------------------------------------------- */
446
* udisks_base_job_get_auto_estimate:
447
* @job: A #UDisksBaseJob.
449
* Gets whether auto-estimation is being used.
451
* Returns: %TRUE if auto-estimation is being used, %FALSE otherwise.
454
udisks_base_job_get_auto_estimate (UDisksBaseJob *job)
456
g_return_val_if_fail (UDISKS_IS_BASE_JOB (job), FALSE);
457
return job->priv->auto_estimate;
462
on_notify_progress (GObject *object,
466
UDisksBaseJob *job = UDISKS_BASE_JOB (user_data);
469
gdouble sum_of_speeds;
472
gint64 usec_remaining;
475
gdouble current_progress;
477
now = g_get_real_time ();
478
current_progress = udisks_job_get_progress (UDISKS_JOB (job));
480
/* first add new sample... */
481
if (job->priv->num_samples == MAX_SAMPLES)
483
memmove (job->priv->samples, job->priv->samples + 1, sizeof (Sample) * (MAX_SAMPLES - 1));
484
job->priv->num_samples -= 1;
486
sample = &job->priv->samples[job->priv->num_samples++];
487
sample->time_usec = now;
488
sample->value = current_progress;
490
/* ... then update expected-end-time from samples - we want at
491
* least five samples before making an estimate...
493
if (job->priv->num_samples < 5)
498
for (n = 1; n < job->priv->num_samples; n++)
500
Sample *a = &job->priv->samples[n-1];
501
Sample *b = &job->priv->samples[n];
503
speed = (b->value - a->value) / (b->time_usec - a->time_usec);
504
sum_of_speeds += speed;
507
avg_speed = sum_of_speeds / num_speeds;
509
bytes = udisks_job_get_bytes (UDISKS_JOB (job));
512
udisks_job_set_rate (UDISKS_JOB (job), bytes * avg_speed * G_USEC_PER_SEC);
516
udisks_job_set_rate (UDISKS_JOB (job), 0);
519
usec_remaining = (1.0 - current_progress) / avg_speed;
520
udisks_job_set_expected_end_time (UDISKS_JOB (job), now + usec_remaining);
527
* udisks_base_job_set_auto_estimate:
528
* @job: A #UDisksBaseJob.
529
* @value: %TRUE if auto-estimation is to be use, %FALSE otherwise.
531
* Sets whether auto-estimation is being used.
534
udisks_base_job_set_auto_estimate (UDisksBaseJob *job,
537
g_return_if_fail (UDISKS_IS_BASE_JOB (job));
539
if (!!value == !!job->priv->auto_estimate)
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,
549
G_CALLBACK (on_notify_progress),
551
g_assert_cmpint (job->priv->notify_progress_signal_handler_id, !=, 0);
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;
560
job->priv->auto_estimate = !!value;
561
g_object_notify (G_OBJECT (job), "auto-estimate");