add mkfs support and iterate over how we handle jobs
authorDavid Zeuthen <davidz@redhat.com>
Mon, 17 Mar 2008 20:46:05 +0000 (16:46 -0400)
committerDavid Zeuthen <davidz@redhat.com>
Mon, 17 Mar 2008 20:46:05 +0000 (16:46 -0400)
15 files changed:
configure.in
policy/org.freedesktop.devicekit.disks.policy.in
src/Makefile.am
src/devkit-disks-daemon.c
src/devkit-disks-device-private.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/devkit-disks-marshal.list [new file with mode: 0644]
src/job-erase.c [new file with mode: 0644]
src/job-mkfs.c [new file with mode: 0644]
src/job-shared.h [new file with mode: 0644]
src/org.freedesktop.DeviceKit.Disks.Device.xml
src/org.freedesktop.DeviceKit.Disks.xml
tools/Makefile.am
tools/devkit-disks.c

index 1ace3c7..8d37718 100644 (file)
@@ -111,7 +111,7 @@ PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.0])
 AC_SUBST(DBUS_CFLAGS)
 AC_SUBST(DBUS_LIBS)
 
-PKG_CHECK_MODULES(DBUS_GLIB, [dbus-glib-1 >= 0.73])
+PKG_CHECK_MODULES(DBUS_GLIB, [dbus-glib-1 >= 0.75])
 AC_SUBST(DBUS_GLIB_CFLAGS)
 AC_SUBST(DBUS_GLIB_LIBS)
 
index 736e2f0..b878d2b 100644 (file)
@@ -39,4 +39,14 @@ file are instantly applied.
     </defaults>
   </action>
 
+  <action id="org.freedesktop.devicekit.disks.erase">
+    <_description>Erase a disk</_description>
+    <_message>Authentication is required to erase a disk</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>auth_admin</allow_active>
+    </defaults>
+  </action>
+
 </policyconfig>
index 698ccf6..819d4bf 100644 (file)
@@ -16,9 +16,16 @@ INCLUDES = \
        $(GLIB_CFLAGS) \
        $(GIO_CFLAGS)
 
-BUILT_SOURCES =                                                \
-       devkit-disks-daemon-glue.h                      \
-       devkit-disks-device-glue.h
+BUILT_SOURCES =                                                        \
+       devkit-disks-daemon-glue.h                              \
+       devkit-disks-device-glue.h                              \
+       devkit-disks-marshal.h          devkit-disks-marshal.c
+
+devkit-disks-marshal.h: devkit-disks-marshal.list
+       glib-genmarshal $< --prefix=devkit_disks_marshal --header > $@
+
+devkit-disks-marshal.c: devkit-disks-marshal.list
+       echo "#include \"devkit-disks-marshal.h\"" > $@ && glib-genmarshal $< --prefix=devkit_disks_marshal --body >> $@
 
 devkit-disks-daemon-glue.h: org.freedesktop.DeviceKit.Disks.xml Makefile.am
        dbus-binding-tool --prefix=devkit_disks_daemon --mode=glib-server --output=devkit-disks-daemon-glue.h org.freedesktop.DeviceKit.Disks.xml
@@ -26,7 +33,7 @@ devkit-disks-daemon-glue.h: org.freedesktop.DeviceKit.Disks.xml Makefile.am
 devkit-disks-device-glue.h: org.freedesktop.DeviceKit.Disks.Device.xml Makefile.am
        dbus-binding-tool --prefix=devkit_disks_device --mode=glib-server --output=devkit-disks-device-glue.h org.freedesktop.DeviceKit.Disks.Device.xml
 
-libexec_PROGRAMS = devkit-disks-daemon
+libexec_PROGRAMS = devkit-disks-daemon devkit-disks-helper-erase devkit-disks-helper-mkfs
 
 dbusifdir = $(datadir)/dbus-1/interfaces
 dbusif_DATA = org.freedesktop.DeviceKit.Disks.xml org.freedesktop.DeviceKit.Disks.Device.xml
@@ -60,6 +67,14 @@ devkit_disks_daemon_LDADD =                          \
        $(POLKIT_DBUS_LIBS)                             \
        $(DEVKIT_LIBS)
 
+devkit_disks_helper_erase_SOURCES = job-shared.h job-erase.c
+devkit_disks_helper_erase_CPPFLAGS = $(AM_CPPFLAGS)
+devkit_disks_helper_erase_LDADD = $(GLIB_LIBS)
+
+devkit_disks_helper_mkfs_SOURCES = job-shared.h job-mkfs.c
+devkit_disks_helper_mkfs_CPPFLAGS = $(AM_CPPFLAGS)
+devkit_disks_helper_mkfs_LDADD = $(GLIB_LIBS)
+
 servicedir       = $(datadir)/dbus-1/system-services
 service_in_files = org.freedesktop.DeviceKit.Disks.service.in
 service_DATA     = $(service_in_files:.service.in=.service)
index 959dbd3..a12d5d7 100644 (file)
@@ -54,6 +54,7 @@
 #include "mounts-file.h"
 
 #include "devkit-disks-daemon-glue.h"
+#include "devkit-disks-marshal.h"
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
@@ -62,6 +63,7 @@ enum
         DEVICE_ADDED_SIGNAL,
         DEVICE_REMOVED_SIGNAL,
         DEVICE_CHANGED_SIGNAL,
+        DEVICE_JOB_CHANGED_SIGNAL,
         LAST_SIGNAL,
 };
 
@@ -333,6 +335,25 @@ devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass)
                               g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE, 1, G_TYPE_STRING);
 
+        signals[DEVICE_JOB_CHANGED_SIGNAL] =
+                g_signal_new ("device-job-changed",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              devkit_disks_marshal_VOID__STRING_BOOLEAN_STRING_BOOLEAN_INT_INT_STRING_DOUBLE,
+                              G_TYPE_NONE,
+                              8,
+                              G_TYPE_STRING,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_STRING,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_INT,
+                              G_TYPE_INT,
+                              G_TYPE_STRING,
+                              G_TYPE_DOUBLE);
+
+
         dbus_g_object_type_install_info (DEVKIT_TYPE_DISKS_DAEMON, &dbus_glib_devkit_disks_daemon_object_info);
 
         dbus_g_error_domain_register (DEVKIT_DISKS_DAEMON_ERROR,
index 32c7623..eec886d 100644 (file)
 
 G_BEGIN_DECLS
 
+struct Job;
+typedef struct Job Job;
+
 struct DevkitDisksDevicePrivate
 {
         DBusGConnection *system_bus_connection;
         DBusGProxy      *system_bus_proxy;
         DevkitDisksDaemon *daemon;
-        char *object_path;
 
+        Job *job;
+
+        char *object_path;
         char *native_path;
 
+        gboolean job_in_progress;
+        char *job_id;
+        gboolean job_is_cancellable;
+        int job_num_tasks;
+        int job_cur_task;
+        char *job_cur_task_id;
+        double job_cur_task_percentage;
+
         struct {
                 char *device_file;
                 GPtrArray *device_file_by_id;
index ba58b9a..2279100 100644 (file)
@@ -34,7 +34,6 @@
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <fcntl.h>
-#include <signal.h>
 #include <pwd.h>
 #include <grp.h>
 #include <linux/fs.h>
@@ -52,6 +51,7 @@
 
 #include "devkit-disks-device.h"
 #include "devkit-disks-device-private.h"
+#include "devkit-disks-marshal.h"
 #include "mounts-file.h"
 
 /*--------------------------------------------------------------------------------------------------------------*/
@@ -84,6 +84,14 @@ enum
         PROP_DEVICE_IS_MOUNTED,
         PROP_DEVICE_MOUNT_PATH,
 
+        PROP_JOB_IN_PROGRESS,
+        PROP_JOB_ID,
+        PROP_JOB_IS_CANCELLABLE,
+        PROP_JOB_NUM_TASKS,
+        PROP_JOB_CUR_TASK,
+        PROP_JOB_CUR_TASK_ID,
+        PROP_JOB_CUR_TASK_PERCENTAGE,
+
         PROP_ID_USAGE,
         PROP_ID_TYPE,
         PROP_ID_VERSION,
@@ -115,6 +123,7 @@ enum
 enum
 {
         CHANGED_SIGNAL,
+        JOB_CHANGED_SIGNAL,
         LAST_SIGNAL,
 };
 
@@ -150,7 +159,7 @@ devkit_disks_device_error_get_type (void)
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_GENERAL, "GeneralError"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_NOT_SUPPORTED, "NotSupported"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTABLE, "NotMountable"),
-                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_ALREADY_MOUNTED, "AlreadyMounted"),
+                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_MOUNTED, "Mounted"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED, "NotMounted"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED_BY_DK, "NotMountedByDeviceKit"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_FSTAB_ENTRY, "FstabEntry"),
@@ -158,6 +167,10 @@ devkit_disks_device_error_get_type (void)
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_FILESYSTEM_BUSY, "FilesystemBusy"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_CANNOT_REMOUNT, "CannotRemount"),
                         ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_UNMOUNT_OPTION_NOT_ALLOWED, "UnmountOptionNotAllowed"),
+                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_NO_JOB_IN_PROGRESS, "NoJobInProgress"),
+                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_JOB_ALREADY_IN_PROGRESS, "JobAlreadyInProgress"),
+                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_JOB_CANNOT_BE_CANCELLED, "JobCannotBeCancelled"),
+                        ENUM_ENTRY (DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED, "JobWasCancelled"),
                         { 0, 0, 0 }
                 };
                 g_assert (DEVKIT_DISKS_DEVICE_NUM_ERRORS == G_N_ELEMENTS (values) - 1);
@@ -234,6 +247,28 @@ get_property (GObject         *object,
                g_value_set_string (value, device->priv->info.device_mount_path);
                break;
 
+       case PROP_JOB_IN_PROGRESS:
+               g_value_set_boolean (value, device->priv->job_in_progress);
+               break;
+       case PROP_JOB_ID:
+               g_value_set_string (value, device->priv->job_id);
+               break;
+       case PROP_JOB_IS_CANCELLABLE:
+               g_value_set_boolean (value, device->priv->job_is_cancellable);
+               break;
+       case PROP_JOB_NUM_TASKS:
+               g_value_set_int (value, device->priv->job_num_tasks);
+               break;
+       case PROP_JOB_CUR_TASK:
+               g_value_set_int (value, device->priv->job_cur_task);
+               break;
+       case PROP_JOB_CUR_TASK_ID:
+               g_value_set_string (value, device->priv->job_cur_task_id);
+               break;
+       case PROP_JOB_CUR_TASK_PERCENTAGE:
+               g_value_set_double (value, device->priv->job_cur_task_percentage);
+               break;
+
         case PROP_ID_USAGE:
                 g_value_set_string (value, device->priv->info.id_usage);
                 break;
@@ -335,6 +370,23 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE, 0);
 
+        signals[JOB_CHANGED_SIGNAL] =
+                g_signal_new ("job-changed",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              devkit_disks_marshal_VOID__BOOLEAN_STRING_BOOLEAN_INT_INT_STRING_DOUBLE,
+                              G_TYPE_NONE,
+                              7,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_STRING,
+                              G_TYPE_BOOLEAN,
+                              G_TYPE_INT,
+                              G_TYPE_INT,
+                              G_TYPE_STRING,
+                              G_TYPE_DOUBLE);
+
         dbus_g_object_type_install_info (DEVKIT_TYPE_DISKS_DEVICE, &dbus_glib_devkit_disks_device_object_info);
 
         dbus_g_error_domain_register (DEVKIT_DISKS_DEVICE_ERROR,
@@ -400,6 +452,35 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
 
         g_object_class_install_property (
                 object_class,
+                PROP_JOB_IN_PROGRESS,
+                g_param_spec_boolean ("job-in-progress", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_ID,
+                g_param_spec_string ("job-id", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_IS_CANCELLABLE,
+                g_param_spec_boolean ("job-is-cancellable", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_NUM_TASKS,
+                g_param_spec_int ("job-num-tasks", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_CUR_TASK,
+                g_param_spec_int ("job-cur-task", NULL, NULL, 0, G_MAXINT, 0, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_CUR_TASK_ID,
+                g_param_spec_string ("job-cur-task-id", NULL, NULL, NULL, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_JOB_CUR_TASK_PERCENTAGE,
+                g_param_spec_double ("job-cur-task-percentage", NULL, NULL, -1, 100, -1, G_PARAM_READABLE));
+
+        g_object_class_install_property (
+                object_class,
                 PROP_ID_USAGE,
                 g_param_spec_string ("id-usage", NULL, NULL, NULL, G_PARAM_READABLE));
         g_object_class_install_property (
@@ -650,6 +731,26 @@ sysfs_file_exists (const char *dir, const char *attribute)
 }
 
 static void
+devkit_device_emit_changed_to_kernel (DevkitDisksDevice *device)
+{
+        FILE *f;
+        char *filename;
+
+        filename = g_build_filename (device->priv->native_path, "uevent", NULL);
+        f = fopen (filename, "w");
+        if (f == NULL) {
+                g_warning ("error opening %s for writing: %m", filename);
+        } else {
+                /* TODO: change 'add' to 'change' when new udev rules are released */
+                if (fputs ("add", f) == EOF) {
+                        g_warning ("error writing 'add' to %s: %m", filename);
+                }
+                fclose (f);
+        }
+        g_free (filename);
+}
+
+static void
 free_info (DevkitDisksDevice *device)
 {
         g_free (device->priv->info.device_file);
@@ -953,6 +1054,31 @@ out:
 }
 
 static void
+emit_job_changed (DevkitDisksDevice *device)
+{
+        g_print ("emitting job-changed on %s\n", device->priv->native_path);
+        g_signal_emit_by_name (device->priv->daemon,
+                               "device-job-changed",
+                               device->priv->object_path,
+                               device->priv->job_in_progress,
+                               device->priv->job_id,
+                               device->priv->job_is_cancellable,
+                               device->priv->job_num_tasks,
+                               device->priv->job_cur_task,
+                               device->priv->job_cur_task_id,
+                               device->priv->job_cur_task_percentage,
+                               NULL);
+        g_signal_emit (device, signals[JOB_CHANGED_SIGNAL], 0,
+                       device->priv->job_in_progress,
+                       device->priv->job_id,
+                       device->priv->job_is_cancellable,
+                       device->priv->job_num_tasks,
+                       device->priv->job_cur_task,
+                       device->priv->job_cur_task_id,
+                       device->priv->job_cur_task_percentage);
+}
+
+static void
 emit_changed (DevkitDisksDevice *device)
 {
         g_print ("emitting changed on %s\n", device->priv->native_path);
@@ -1137,11 +1263,12 @@ throw_error (DBusGMethodInvocation *context, int error_code, const char *format,
 typedef void (*JobCompletedFunc) (DBusGMethodInvocation *context,
                                   DevkitDisksDevice *device,
                                   PolKitCaller *caller,
+                                  gboolean was_cancelled,
                                   int status,
                                   const char *stderr,
                                   gpointer user_data);
 
-typedef struct {
+struct Job {
         DevkitDisksDevice *device;
         PolKitCaller *pk_caller;
         DBusGMethodInvocation *context;
@@ -1149,24 +1276,35 @@ typedef struct {
         GPid pid;
         gpointer user_data;
         GDestroyNotify user_data_destroy_func;
+        gboolean was_cancelled;
 
         int stderr_fd;
         GIOChannel *error_channel;
         guint error_channel_source_id;
         GString *error_string;
-} Job;
+
+        int stdout_fd;
+        GIOChannel *out_channel;
+        guint out_channel_source_id;
+};
 
 static void
 job_free (Job *job)
 {
+        if (job->user_data_destroy_func != NULL)
+                job->user_data_destroy_func (job->user_data);
         if (job->device != NULL)
                 g_object_unref (job->device);
         if (job->pk_caller != NULL)
                 polkit_caller_unref (job->pk_caller);
         if (job->stderr_fd >= 0)
                 close (job->stderr_fd);
+        if (job->stdout_fd >= 0)
+                close (job->stdout_fd);
         g_source_remove (job->error_channel_source_id);
+        g_source_remove (job->out_channel_source_id);
         g_io_channel_unref (job->error_channel);
+        g_io_channel_unref (job->out_channel);
         g_string_free (job->error_string, TRUE);
         g_free (job);
 }
@@ -1176,15 +1314,44 @@ job_child_watch_cb (GPid pid, int status, gpointer user_data)
 {
         Job *job = user_data;
 
+        g_print ("helper(pid %5d): completed with exit code %d\n", job->pid, WEXITSTATUS (status));
+
         job->job_completed_func (job->context,
                                  job->device,
                                  job->pk_caller,
+                                 job->was_cancelled,
                                  status,
                                  job->error_string->str,
                                  job->user_data);
+
+        job->device->priv->job_in_progress = FALSE;
+        g_free (job->device->priv->job_id);
+        job->device->priv->job_id = NULL;
+        job->device->priv->job_is_cancellable = FALSE;
+        job->device->priv->job_num_tasks = 0;
+        job->device->priv->job_cur_task = 0;
+        g_free (job->device->priv->job_cur_task_id);
+        job->device->priv->job_cur_task_id = NULL;
+        job->device->priv->job_cur_task_percentage = -1.0;
+
+        job->device->priv->job = NULL;
+
+        emit_job_changed (job->device);
+
         job_free (job);
 }
 
+static void
+job_cancel (DevkitDisksDevice *device)
+{
+        g_return_if_fail (device->priv->job != NULL);
+
+        device->priv->job->was_cancelled = TRUE;
+
+        /* TODO: maybe wait and user a bigger hammer? (SIGKILL) */
+        kill (device->priv->job->pid, SIGTERM);
+}
+
 static gboolean
 job_read_error (GIOChannel *channel,
                 GIOCondition condition,
@@ -1201,19 +1368,65 @@ job_read_error (GIOChannel *channel,
 }
 
 static gboolean
+job_read_out (GIOChannel *channel,
+              GIOCondition condition,
+              gpointer user_data)
+{
+  char *str;
+  gsize str_len;
+  Job *job = user_data;
+
+  g_io_channel_read_line (channel, &str, &str_len, NULL, NULL);
+  g_print ("helper(pid %5d): %s", job->pid, str);
+
+  if (strlen (str) < 256) {
+          int cur_task;
+          int num_tasks;
+          double cur_task_percentage;;
+          char cur_task_id[256];
+
+          if (sscanf (str, "progress: %d %d %lg %s",
+                      &cur_task,
+                      &num_tasks,
+                      &cur_task_percentage,
+                      (char *) &cur_task_id) == 4) {
+                  job->device->priv->job_num_tasks = num_tasks;
+                  job->device->priv->job_cur_task = cur_task;
+                  g_free (job->device->priv->job_cur_task_id);
+                  job->device->priv->job_cur_task_id = g_strdup (cur_task_id);
+                  job->device->priv->job_cur_task_percentage = cur_task_percentage;
+                  emit_job_changed (job->device);
+          }
+  }
+
+  g_free (str);
+  return TRUE;
+}
+
+static gboolean
 job_new (DBusGMethodInvocation *context,
-         DevkitDisksDevice *device,
-         PolKitCaller *pk_caller,
-         char **argv,
-         JobCompletedFunc job_completed_func,
-         GError **error,
-         gpointer user_data,
-         GDestroyNotify user_data_destroy_func)
+         const char            *job_id,
+         gboolean               is_cancellable,
+         DevkitDisksDevice     *device,
+         PolKitCaller          *pk_caller,
+         char                 **argv,
+         JobCompletedFunc       job_completed_func,
+         gpointer               user_data,
+         GDestroyNotify         user_data_destroy_func)
 {
         Job *job;
         gboolean ret;
+        GError *error;
 
         ret = FALSE;
+        job = NULL;
+
+        if (device->priv->job != NULL) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_JOB_ALREADY_IN_PROGRESS,
+                             "There is already a job running");
+                goto out;
+        }
 
         job = g_new0 (Job, 1);
         job->context = context;
@@ -1223,7 +1436,11 @@ job_new (DBusGMethodInvocation *context,
         job->user_data = user_data;
         job->user_data_destroy_func = user_data_destroy_func;
         job->stderr_fd = -1;
+        job->stdout_fd = -1;
+        g_free (job->device->priv->job_id);
+        job->device->priv->job_id = g_strdup (job_id);
 
+        error = NULL;
         if (!g_spawn_async_with_pipes (NULL,
                                        argv,
                                        NULL,
@@ -1232,10 +1449,11 @@ job_new (DBusGMethodInvocation *context,
                                        NULL,
                                        &(job->pid),
                                        NULL,
-                                       NULL,
+                                       &(job->stdout_fd),
                                        &(job->stderr_fd),
-                                       error)) {
-                job->user_data_destroy_func (job->user_data);
+                                       &error)) {
+                throw_error (context, DEVKIT_DISKS_DEVICE_ERROR_GENERAL, "Error starting job: %s", error->message);
+                g_error_free (error);
                 goto out;
         }
 
@@ -1245,10 +1463,27 @@ job_new (DBusGMethodInvocation *context,
         job->error_channel = g_io_channel_unix_new (job->stderr_fd);
         job->error_channel_source_id = g_io_add_watch (job->error_channel, G_IO_IN, job_read_error, job);
 
+        job->out_channel = g_io_channel_unix_new (job->stdout_fd);
+        job->out_channel_source_id = g_io_add_watch (job->out_channel, G_IO_IN, job_read_out, job);
+
         ret = TRUE;
 
+        device->priv->job_in_progress = TRUE;
+        device->priv->job_is_cancellable = is_cancellable;
+        device->priv->job_num_tasks = 0;
+        device->priv->job_cur_task = 0;
+        g_free (device->priv->job_cur_task_id);
+        device->priv->job_cur_task_id = NULL;
+        device->priv->job_cur_task_percentage = -1.0;
+
+        device->priv->job = job;
+
+        emit_job_changed (device);
+
+        g_print ("helper(pid %5d): launched job %s on %s\n", job->pid, argv[0], device->priv->info.device_file);
+
 out:
-        if (!ret)
+        if (!ret && job != NULL)
                 job_free (job);
         return ret;
 }
@@ -1627,6 +1862,7 @@ static void
 mount_completed_cb (DBusGMethodInvocation *context,
                     DevkitDisksDevice *device,
                     PolKitCaller *pk_caller,
+                    gboolean job_was_cancelled,
                     int status,
                     const char *stderr,
                     gpointer user_data)
@@ -1638,7 +1874,7 @@ mount_completed_cb (DBusGMethodInvocation *context,
         if (pk_caller != NULL)
                 polkit_caller_get_uid (pk_caller, &uid);
 
-        if (WEXITSTATUS (status) == 0) {
+        if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
                 if (!data->is_remount) {
                         devkit_disks_device_local_set_mounted (device, data->mount_point);
                         mounts_file_add (device, uid, data->remove_dir_on_unmount);
@@ -1650,9 +1886,17 @@ mount_completed_cb (DBusGMethodInvocation *context,
                                 g_warning ("Error removing dir in late mount error path: %m");
                         }
                 }
-                throw_error (context,
-                             DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
-                             "Error mounting: mount exited with exit code %d: %s", WEXITSTATUS (status), stderr);
+
+                if (job_was_cancelled) {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED,
+                                     "Job was cancelled");
+                } else {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                                     "Error mounting: mount exited with exit code %d: %s",
+                                     WEXITSTATUS (status), stderr);
+                }
         }
 }
 
@@ -1726,8 +1970,8 @@ devkit_disks_device_mount (DevkitDisksDevice     *device,
 
         fsmo = find_mount_options_for_fs (fstype);
 
-        /* always prepend some reasonable default mount options; these should be
-         * chosen here so the user can override them later on.
+        /* always prepend some reasonable default mount options; these are
+         * chosen here; the user can override them if he wants to
          */
         options = prepend_default_mount_options (fsmo, caller_uid, given_options);
 
@@ -1776,7 +2020,7 @@ devkit_disks_device_mount (DevkitDisksDevice     *device,
 
         if (device->priv->info.device_is_mounted) {
                 if (!is_remount) {
-                        throw_error (context, DEVKIT_DISKS_DEVICE_ERROR_ALREADY_MOUNTED,
+                        throw_error (context, DEVKIT_DISKS_DEVICE_ERROR_MOUNTED,
                                      "Device is already mounted");
                         goto out;
                 }
@@ -1853,15 +2097,14 @@ try_another_mount_point:
 
         error = NULL;
         if (!job_new (context,
+                      "Mount",
+                      FALSE,
                       device,
                       pk_caller,
                       argv,
                       mount_completed_cb,
-                      &error,
                       mount_data_new (mount_point, remove_dir_on_unmount, is_remount),
                       (GDestroyNotify) mount_data_free)) {
-                throw_error (context, DEVKIT_DISKS_DEVICE_ERROR_GENERAL, "Error mounting: %s", error->message);
-                g_error_free (error);
                 if (!is_remount) {
                         if (g_rmdir (mount_point) != 0) {
                                 g_warning ("Error removing dir in early mount error path: %m");
@@ -1886,27 +2129,34 @@ static void
 unmount_completed_cb (DBusGMethodInvocation *context,
                       DevkitDisksDevice *device,
                       PolKitCaller *pk_caller,
+                      gboolean job_was_cancelled,
                       int status,
                       const char *stderr,
                       gpointer user_data)
 {
         char *mount_path = user_data;
 
-        if (WEXITSTATUS (status) == 0) {
+        if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
                 devkit_disks_device_local_set_unmounted (device);
                 mounts_file_remove (device, mount_path);
                 dbus_g_method_return (context);
         } else {
-                if (strstr (stderr, "device is busy") != NULL) {
+                if (job_was_cancelled) {
                         throw_error (context,
-                                     DEVKIT_DISKS_DEVICE_ERROR_FILESYSTEM_BUSY,
-                                     "Cannot unmount because file system on device is busy");
+                                     DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED,
+                                     "Job was cancelled");
                 } else {
-                        throw_error (context,
-                                     DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
-                                     "Error unmounting: umount exited with exit code %d: %s",
-                                     WEXITSTATUS (status),
-                                     stderr);
+                        if (strstr (stderr, "device is busy") != NULL) {
+                                throw_error (context,
+                                             DEVKIT_DISKS_DEVICE_ERROR_FILESYSTEM_BUSY,
+                                             "Cannot unmount because file system on device is busy");
+                        } else {
+                                throw_error (context,
+                                             DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                                             "Error unmounting: umount exited with exit code %d: %s",
+                                             WEXITSTATUS (status),
+                                             stderr);
+                        }
                 }
         }
 }
@@ -1962,7 +2212,7 @@ devkit_disks_device_unmount (DevkitDisksDevice     *device,
                 } else {
                         throw_error (context,
                                      DEVKIT_DISKS_DEVICE_ERROR_UNMOUNT_OPTION_NOT_ALLOWED,
-                                     "Device is not mounted by DeviceKit-disks");
+                                     "Unknown option %s", option);
                 }
         }
 
@@ -1977,15 +2227,14 @@ devkit_disks_device_unmount (DevkitDisksDevice     *device,
 
         error = NULL;
         if (!job_new (context,
+                      "Unmount",
+                      FALSE,
                       device,
                       pk_caller,
                       argv,
                       unmount_completed_cb,
-                      &error,
                       g_strdup (device->priv->info.device_mount_path),
                       g_free)) {
-                throw_error (context, DEVKIT_DISKS_DEVICE_ERROR_GENERAL, "Error unmounting: %s", error->message);
-                g_error_free (error);
                 goto out;
         }
 
@@ -1995,3 +2244,219 @@ out:
         return TRUE;
 }
 
+/*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+erase_completed_cb (DBusGMethodInvocation *context,
+                    DevkitDisksDevice *device,
+                    PolKitCaller *pk_caller,
+                    gboolean job_was_cancelled,
+                    int status,
+                    const char *stderr,
+                    gpointer user_data)
+{
+        /* either way, poke the kernel so we can reread the data */
+        devkit_device_emit_changed_to_kernel (device);
+
+        if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
+                dbus_g_method_return (context);
+        } else {
+                if (job_was_cancelled) {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED,
+                                     "Job was cancelled");
+                } else {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                                     "Error erasing: helper exited with exit code %d: %s",
+                                     WEXITSTATUS (status),
+                                     stderr);
+                }
+        }
+}
+
+gboolean
+devkit_disks_device_erase (DevkitDisksDevice     *device,
+                           char                 **options,
+                           DBusGMethodInvocation *context)
+{
+        int n;
+        char *argv[16];
+        GError *error;
+        PolKitCaller *pk_caller;
+
+        if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
+                goto out;
+
+        if (device->priv->info.device_is_mounted) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED,
+                             "Device is mounted");
+                goto out;
+        }
+
+        if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
+                                                  pk_caller,
+                                                  /* TODO: revisit authorization */
+                                                  "org.freedesktop.devicekit.disks.erase",
+                                                  context))
+                goto out;
+
+        /* TODO: options: quick, full, secure_gutmann_35pass etc. */
+
+        n = 0;
+        argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-erase";
+        argv[n++] = device->priv->info.device_file;
+        argv[n++] = NULL;
+
+        error = NULL;
+        if (!job_new (context,
+                      "Erase",
+                      TRUE,
+                      device,
+                      pk_caller,
+                      argv,
+                      erase_completed_cb,
+                      NULL,
+                      NULL)) {
+                goto out;
+        }
+
+out:
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+create_filesystem_completed_cb (DBusGMethodInvocation *context,
+                                DevkitDisksDevice *device,
+                                PolKitCaller *pk_caller,
+                                gboolean job_was_cancelled,
+                                int status,
+                                const char *stderr,
+                                gpointer user_data)
+{
+        /* either way, poke the kernel so we can reread the data */
+        devkit_device_emit_changed_to_kernel (device);
+
+        if (WEXITSTATUS (status) == 0 && !job_was_cancelled) {
+                dbus_g_method_return (context);
+        } else {
+                if (job_was_cancelled) {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED,
+                                     "Job was cancelled");
+                } else {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                                     "Error creating file system: helper exited with exit code %d: %s",
+                                     WEXITSTATUS (status),
+                                     stderr);
+                }
+        }
+}
+
+gboolean
+devkit_disks_device_create_filesystem (DevkitDisksDevice     *device,
+                                       const char            *fstype,
+                                       char                 **options,
+                                       DBusGMethodInvocation *context)
+{
+        int n;
+        int m;
+        char *argv[128];
+        GError *error;
+        PolKitCaller *pk_caller;
+
+        if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
+                goto out;
+
+        if (device->priv->info.device_is_mounted) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED,
+                             "Device is mounted");
+                goto out;
+        }
+
+        if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
+                                                  pk_caller,
+                                                  /* TODO: revisit authorization */
+                                                  "org.freedesktop.devicekit.disks.erase",
+                                                  context))
+                goto out;
+
+        if (strlen (fstype) == 0) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                             "fstype not specified");
+                goto out;
+        }
+
+        n = 0;
+        argv[n++] = PACKAGE_LIBEXEC_DIR "/devkit-disks-helper-mkfs";
+        argv[n++] = (char *) fstype;
+        argv[n++] = device->priv->info.device_file;
+        for (m = 0; options[m] != NULL; m++) {
+                if (n >= (int) sizeof (argv) - 1) {
+                        throw_error (context,
+                                     DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
+                                     "Too many options");
+                        goto out;
+                }
+                /* the helper will validate each option */
+                argv[n++] = (char *) options[m];
+        }
+        argv[n++] = NULL;
+
+        error = NULL;
+        if (!job_new (context,
+                      "CreateFilesystem",
+                      TRUE,
+                      device,
+                      pk_caller,
+                      argv,
+                      create_filesystem_completed_cb,
+                      NULL,
+                      NULL)) {
+                goto out;
+        }
+
+out:
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+gboolean
+devkit_disks_device_cancel_job (DevkitDisksDevice     *device,
+                                DBusGMethodInvocation *context)
+{
+        if (!device->priv->job_in_progress) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_NO_JOB_IN_PROGRESS,
+                             "There is no job to cancel");
+                goto out;
+        }
+
+        if (!device->priv->job_is_cancellable) {
+                throw_error (context,
+                             DEVKIT_DISKS_DEVICE_ERROR_JOB_CANNOT_BE_CANCELLED,
+                             "Job cannot be cancelled");
+                goto out;
+        }
+
+        /* TODO: check authorization */
+
+        job_cancel (device);
+
+        /* TODO: wait returning once the job is actually cancelled? */
+        dbus_g_method_return (context);
+
+out:
+        return TRUE;
+}
index ed6979a..626314f 100644 (file)
@@ -53,7 +53,7 @@ typedef enum
         DEVKIT_DISKS_DEVICE_ERROR_GENERAL,
         DEVKIT_DISKS_DEVICE_ERROR_NOT_SUPPORTED,
         DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTABLE,
-        DEVKIT_DISKS_DEVICE_ERROR_ALREADY_MOUNTED,
+        DEVKIT_DISKS_DEVICE_ERROR_MOUNTED,
         DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED,
         DEVKIT_DISKS_DEVICE_ERROR_NOT_MOUNTED_BY_DK,
         DEVKIT_DISKS_DEVICE_ERROR_FSTAB_ENTRY,
@@ -61,6 +61,10 @@ typedef enum
         DEVKIT_DISKS_DEVICE_ERROR_FILESYSTEM_BUSY,
         DEVKIT_DISKS_DEVICE_ERROR_CANNOT_REMOUNT,
         DEVKIT_DISKS_DEVICE_ERROR_UNMOUNT_OPTION_NOT_ALLOWED,
+        DEVKIT_DISKS_DEVICE_ERROR_NO_JOB_IN_PROGRESS,
+        DEVKIT_DISKS_DEVICE_ERROR_JOB_ALREADY_IN_PROGRESS,
+        DEVKIT_DISKS_DEVICE_ERROR_JOB_CANNOT_BE_CANCELLED,
+        DEVKIT_DISKS_DEVICE_ERROR_JOB_WAS_CANCELLED,
         DEVKIT_DISKS_DEVICE_NUM_ERRORS
 } DevkitDisksDeviceError;
 
@@ -90,6 +94,9 @@ void               devkit_disks_device_local_set_unmounted (DevkitDisksDevice *d
 
 /* exported methods */
 
+gboolean devkit_disks_device_cancel_job (DevkitDisksDevice     *device,
+                                         DBusGMethodInvocation *context);
+
 gboolean devkit_disks_device_mount (DevkitDisksDevice     *device,
                                     const char            *filesystem_type,
                                     char                 **options,
@@ -99,6 +106,15 @@ gboolean devkit_disks_device_unmount (DevkitDisksDevice     *device,
                                       char                 **options,
                                       DBusGMethodInvocation *context);
 
+gboolean devkit_disks_device_erase (DevkitDisksDevice     *device,
+                                    char                 **options,
+                                    DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_create_filesystem (DevkitDisksDevice     *device,
+                                                const char            *fstype,
+                                                char                 **options,
+                                                DBusGMethodInvocation *context);
+
 G_END_DECLS
 
 #endif /* __DEVKIT_DISKS_DEVICE_H__ */
diff --git a/src/devkit-disks-marshal.list b/src/devkit-disks-marshal.list
new file mode 100644 (file)
index 0000000..98218d7
--- /dev/null
@@ -0,0 +1,2 @@
+VOID:BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE
+VOID:STRING,BOOLEAN,STRING,BOOLEAN,INT,INT,STRING,DOUBLE
diff --git a/src/job-erase.c b/src/job-erase.c
new file mode 100644 (file)
index 0000000..1c949b3
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "job-shared.h"
+
+int
+main (int argc, char **argv)
+{
+        int ret;
+        int num_passes;
+        const char *device;
+
+        ret = 1;
+
+        if (argc != 2) {
+                g_printerr ("wrong usage\n");
+                goto out;
+        }
+        device = argv[1];
+
+        /* TODO: parse options etc. */
+        num_passes = 1;
+        if (!task_zero_device (device, num_passes, 0, 2))
+                goto out;
+
+        ret = 0;
+
+out:
+        return ret;
+}
diff --git a/src/job-mkfs.c b/src/job-mkfs.c
new file mode 100644 (file)
index 0000000..ac14ec5
--- /dev/null
@@ -0,0 +1,192 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+
+#include "job-shared.h"
+
+static gboolean
+validate_and_escape_label (char **label, int max_len)
+{
+        int n;
+        gboolean ret;
+        GString *s;
+
+        ret = FALSE;
+
+        if ((int) strlen (*label) > max_len) {
+                g_printerr ("given file system label exceeds %d characters\n", max_len);
+                goto out;
+        }
+
+        /* escape '"' */
+        s = g_string_new (*label);
+        for (n = 0; n < (int) s->len; n++) {
+                if (s->str[n] == '"') {
+                        g_string_insert_c (s, n, '\\');
+                        n++;
+                }
+        }
+        g_free (*label);
+        *label = g_string_free (s, FALSE);
+
+        ret = TRUE;
+out:
+        return ret;
+}
+
+int
+main (int argc, char **argv)
+{
+        int ret;
+        int exit_status;
+        GError *error;
+        const char *fstype;
+        const char *device;
+        char *command_line;
+        char *standard_error;
+        char **options;
+        GString *s;
+        char *label;
+        char *erase;
+        int num_erase_passes;
+        int n;
+
+        ret = 1;
+        command_line = NULL;
+        standard_error = NULL;
+        erase = NULL;
+
+        if (argc < 3) {
+                g_printerr ("wrong usage\n");
+                goto out;
+        }
+        fstype = argv[1];
+        device = argv[2];
+        options = argv + 3;
+
+        /* TODO: clean out metadata in start and beginning */
+
+        if (strcmp (fstype, "vfat") == 0) {
+                s = g_string_new ("mkfs.vfat");
+                for (n = 0; options[n] != NULL; n++) {
+                        if (g_str_has_prefix (options[n], "label=")) {
+                                label = strdup (options[n] + sizeof ("label=") - 1);
+                                if (!validate_and_escape_label (&label, 11)) {
+                                        g_string_free (s, TRUE);
+                                        goto out;
+                                }
+                                g_string_append_printf (s, " -n \"%s\"", label);
+                                g_free (label);
+                        } else if (g_str_has_prefix (options[n], "erase=")) {
+                                erase = strdup (options[n] + sizeof ("erase=") - 1);
+                        } else {
+                                g_printerr ("option %s not supported\n", options[n]);
+                                goto out;
+                        }
+                }
+                g_string_append_printf (s, " %s", device);
+                command_line = g_string_free (s, FALSE);
+        } else if (strcmp (fstype, "ext3") == 0) {
+
+                s = g_string_new ("mkfs.ext3");
+                for (n = 0; options[n] != NULL; n++) {
+                        if (g_str_has_prefix (options[n], "label=")) {
+                                label = strdup (options[n] + sizeof ("label=") - 1);
+                                if (!validate_and_escape_label (&label, 16)) {
+                                        g_string_free (s, TRUE);
+                                        goto out;
+                                }
+                                g_string_append_printf (s, " -L \"%s\"", label);
+                                g_free (label);
+                        } else if (g_str_has_prefix (options[n], "erase=")) {
+                                erase = strdup (options[n] + sizeof ("erase=") - 1);
+                        } else {
+                                g_printerr ("option %s not supported\n", options[n]);
+                                goto out;
+                        }
+                }
+                g_string_append_printf (s, " %s", device);
+                command_line = g_string_free (s, FALSE);
+
+        } else if (strcmp (fstype, "empty") == 0) {
+                command_line = NULL;
+                for (n = 0; options[n] != NULL; n++) {
+                        if (g_str_has_prefix (options[n], "erase=")) {
+                                erase = strdup (options[n] + sizeof ("erase=") - 1);
+                        } else {
+                                g_printerr ("option %s not supported\n", options[n]);
+                                goto out;
+                        }
+                }
+        } else {
+                g_printerr ("fstype %s not supported\n", fstype);
+                goto out;
+        }
+
+        /* TODO: do erase */
+        num_erase_passes = task_zero_device_parse_option (erase);
+        if (num_erase_passes == -1) {
+                g_printerr ("invalid erase=%s option\n", erase);
+                goto out;
+        }
+        if (!task_zero_device (device, num_erase_passes, 0, num_erase_passes + 2))
+                goto out;
+
+        g_print ("progress: %d %d -1 mkfs\n", num_erase_passes + 1, num_erase_passes + 2);
+
+        if (command_line != NULL) {
+                if (!g_spawn_command_line_sync (command_line,
+                                                NULL,
+                                                &standard_error,
+                                                &exit_status,
+                                                &error)) {
+                        g_printerr ("cannot spawn '%s'\n", command_line);
+                        goto out;
+                }
+                if (WEXITSTATUS (exit_status) != 0) {
+                        g_printerr ("helper failed with:\n%s", standard_error);
+                        goto out;
+                }
+        }
+
+        ret = 0;
+
+out:
+        g_free (standard_error);
+        g_free (command_line);
+        g_free (erase);
+        return ret;
+}
diff --git a/src/job-shared.h b/src/job-shared.h
new file mode 100644 (file)
index 0000000..97f705b
--- /dev/null
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008 David Zeuthen <david@fubar.dk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+
+#include <glib.h>
+
+#ifndef __JOB_SHARED_H__
+#define __JOB_SHARED_H__
+
+/* TODO: maybe move to private static library if there's a lot of shared stuff */
+
+#define ERASE_SIZE (128*1024)
+
+/**
+ * parse_passes_from_erase:
+ * @str: string to pass, e.g. #NULL, "none", "full", "full3pass",
+ * "full7pass", "full35pass".
+ *
+ * Parses an erase option string and returns the number of passes;
+ * #NULL and "none" maps into 0, "full" maps into 1, "full3pass" maps
+ * into 3, "full7pass" maps into 7, and "full35pass" maps into 35. If
+ * the string cannot be parsed, -1 is returned.
+ *
+ * Returns: Number of passes or -1 on error.
+ **/
+static inline int
+task_zero_device_parse_option (const char *str)
+{
+        int ret;
+
+        if (str == NULL) {
+                ret = 0;
+        } else if (strcmp (str, "none") == 0) {
+                ret = 0;
+        } else if (strcmp (str, "full") == 0) {
+                ret = 1;
+        } else if (strcmp (str, "full3pass") == 0) {
+                ret = 3;
+        } else if (strcmp (str, "full7pass") == 0) {
+                ret = 7;
+        } else if (strcmp (str, "full35pass") == 0) {
+                ret = 35;
+        } else {
+                ret = -1;
+        }
+
+        return ret;
+}
+
+static inline gboolean
+do_write (int fd, void *buf, int num)
+{
+        gboolean ret;
+        ret = FALSE;
+again:
+        if (write (fd, buf, num) != (int) num) {
+                if (errno == EAGAIN) {
+                        goto again;
+                } else {
+                        g_printerr ("%d: error writing %d bytes: %m\n", getpid (), num);
+                        goto out;
+                }
+        }
+        ret = TRUE;
+out:
+        return ret;
+}
+
+
+/**
+ * task_zero_device:
+ * @device: device to zero
+ * @num_passes: number of passes, 0, 1, 3, 7 and 35 supported. See
+ * the function task_zero_device_parse_option() for details.
+ * @cur_task: current task
+ * @num_tasks: number of tasks
+ *
+ * Zeroes (parts of) a device. If @num_passes is 0 then only the areas
+ * where file system signatures are normally stored are zeroed,
+ * otherwise the device is cleared @num_passes times using methods
+ * compliant with US DoD 5220.
+ *
+ * This task will use @num_passes + 1 task slots.
+ *
+ * See these websites.
+ *
+ *   http://ask.metafilter.com/83005/How-long-does-a-7-Pass-US-DoD-5220-method-take
+ *   http://en.wikipedia.org/wiki/Gutmann_method
+ *   http://en.wikipedia.org/wiki/National_Industrial_Security_Program
+ *
+ * for more details on secure erase.
+ *
+ * Returns: #TRUE unless erasing failed or an incoming parameter was
+ * invalid.
+ **/
+static inline gboolean
+task_zero_device (const char *device, int num_passes, int cur_task, int num_tasks)
+{
+        int fd;
+        gboolean ret;
+        guint64 size;
+        guint64 cursor;
+        int percent;
+        int old_percent;
+        char buf[ERASE_SIZE];
+
+        fd = 0;
+        ret = FALSE;
+
+        fd = open (device, O_WRONLY);
+        if (fd < 0) {
+                g_printerr ("cannot open device: %m\n");
+                goto out;
+        }
+
+       if (ioctl (fd, BLKGETSIZE64, &size) != 0) {
+               g_printerr ("cannot determine size of device: %m\n");
+               goto out;
+       }
+
+        memset (buf, '\0', sizeof (buf));
+
+
+        if (num_passes == 0) {
+                guint64 wipe_size;
+
+                g_print ("progress: %d %d 0 zeroing\n", cur_task, num_tasks);
+
+                /* wipe first and last 16kb. TODO: check 16kb is the right number */
+                wipe_size = 16 * 1024;
+                g_assert (sizeof (buf) >= wipe_size);
+
+                if (wipe_size > size) {
+                        wipe_size = size;
+                }
+
+                if (!do_write (fd, buf, wipe_size))
+                        goto out;
+
+                if (lseek (fd, size - wipe_size, SEEK_SET) == (off_t) -1) {
+                        g_printerr ("cannot seek to %lld: %m", size - wipe_size);
+                        goto out;
+                }
+
+                if (!do_write (fd, buf, wipe_size))
+                        goto out;
+
+        } else if (num_passes == 1) {
+                cursor = 0;
+                old_percent = 0;
+                g_print ("progress: %d %d 0 zeroing\n", cur_task, num_tasks);
+                while (cursor < size) {
+                        guint64 num;
+
+                        num = sizeof (buf);
+                        if (size - cursor < num)
+                                num = size - cursor;
+                        if (!do_write (fd, buf, num))
+                                goto out;
+
+                        cursor += num;
+
+                        percent = 100 * cursor / size;
+                        if (percent > old_percent) {
+                                g_print ("progress: %d %d %d zeroing\n", cur_task, num_tasks, percent);
+                                old_percent = percent;
+                        }
+                }
+                g_print ("progress: %d %d -1 sync\n", cur_task + 1, num_tasks);
+                fsync (fd);
+        } else if (num_passes == 3) {
+                g_printerr ("only 0 and 1 erase passes is implemented for now\n");
+                goto out;
+        } else if (num_passes == 7) {
+                g_printerr ("only 0 and 1 erase passes is implemented for now\n");
+                goto out;
+        } else if (num_passes == 35) {
+                g_printerr ("only 0 and 1 erase passes is implemented for now\n");
+                goto out;
+        }
+
+        ret = TRUE;
+
+out:
+        if (fd >= 0)
+                close (fd);
+        return ret;
+}
+
+
+#endif /* __JOB_SHARED_H__ */
index ae38485..04007c0 100644 (file)
     </method>
 
     <!-- TODO: docs -->
+    <method name="CancelJob">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+    </method>
+
+    <!-- TODO: docs -->
     <method name="Unmount">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="options" direction="in" type="as"/>
     </method>
 
+    <!-- TODO: docs -->
+    <method name="Erase">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="options" direction="in" type="as"/>
+    </method>
+
+    <!-- CreateFilesystem:
+         @fstype:   the of file system to create. Pass "empty" to
+                    not create a file system. Use @TODO@ to get
+                    a list of file systems that can be created.
+
+         @options:  List of options. Known options that are file system
+                    independent are:
+                     - label=: for setting the file system label
+                     - erase=: whether to erase the device before
+                               creating a file system. Valid values
+                               include "none", "full", "full7pass" and
+                               "full35pass".
+
+         Create a file system on a device.
+     -->
+    <method name="CreateFilesystem">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="fstype" direction="in" type="s"/>
+      <arg name="options" direction="in" type="as"/>
+    </method>
+
     <!-- BEGIN LIST OF SIGNALS -->
 
-    <!-- Something about the device changed -->
+    <!-- Something about the device changed. Changes in job state
+         wont trigger this signal; see the 'JobChanged' signal -->
     <signal name="Changed"/>
 
+    <!-- Job state changed; clients should listen to this signal
+         to avoid polling the daemon for job state -->
+    <signal name="JobChanged">
+      <arg name="job-in-progress" type="b"/>
+      <arg name="job-is-cancellable" type="b"/>
+      <arg name="job-id" type="s"/>
+      <arg name="job-num-tasks" type="i"/>
+      <arg name="job-cur-task" type="i"/>
+      <arg name="job-cur-task-id" type="s"/>
+      <arg name="job-cur-task-percentage" type="d"/>
+    </signal>
+
     <!-- BEGIN LIST OF PROPERTIES
 
          A general note about properties: the set of values returned
     <property name="device-size" type="t" access="read"/>
     <property name="device-block-size" type="t" access="read"/>
 
+    <!-- The job properties specify if a job initiated via the
+         DeviceKit-disks daemon is currently in progress.  A job may
+         be split into several sequential tasks; in that case
+         'job-num-tasks' will be greater than zero. If it is 0 none
+         of the 'job-cur-*' properties will be set.
+
+         The 'job-id' property is used to identify the job and maps
+         1-1 with the names of the method calls on this interface,
+         e.g. 'Erase', 'CreateFilesystem' and so on.
+
+         Note that the 'job-cur-task' percentage starts at 0.  The
+         percentage is a number between -1 and 100. If set to -1 it
+         means the progress of the current is unknown. The task id is
+         a well-defined name defining the current subtask of the job;
+         known tasks id's are
+
+          - zeroing: zeroing data on the device
+          - sync:    flushing data to the device
+          - mkfs:    creating file system
+      -->
+    <property name="job-in-progress" type="b" access="read"/>
+    <property name="job-id" type="s" access="read"/>
+    <property name="job-is-cancellable" type="b" access="read"/>
+    <property name="job-num-tasks" type="i" access="read"/>
+    <property name="job-cur-task" type="i" access="read"/>
+    <property name="job-cur-task-id" type="s" access="read"/>
+    <property name="job-cur-task-percentage" type="d" access="read"/>
+
     <!-- the usage is a result of probing for signatures on the block
          device; known values are
 
index 9dc7617..f06ad2c 100644 (file)
       <arg name="device" type="o"/>
     </signal>
 
+    <!-- Job state on a device was changed -->
+    <signal name="DeviceJobChanged">
+      <arg name="device" type="o"/>
+      <arg name="job-in-progress" type="b"/>
+      <arg name="job-is-cancellable" type="b"/>
+      <arg name="job-id" type="s"/>
+      <arg name="job-num-tasks" type="i"/>
+      <arg name="job-cur-task" type="i"/>
+      <arg name="job-cur-task-id" type="s"/>
+      <arg name="job-cur-task-percentage" type="d"/>
+    </signal>
+
   </interface>
 
 </node>
index 20df142..7d57b6b 100644 (file)
@@ -14,9 +14,16 @@ INCLUDES = \
        $(POLKIT_DBUS_CFLAGS) \
        $(GLIB_CFLAGS)
 
-BUILT_SOURCES =                                                \
-       devkit-disks-daemon-glue.h                      \
-       devkit-disks-device-glue.h
+BUILT_SOURCES =                                                        \
+       devkit-disks-daemon-glue.h                              \
+       devkit-disks-device-glue.h                              \
+       devkit-disks-marshal.h          devkit-disks-marshal.c
+
+devkit-disks-marshal.h: $(top_srcdir)/src/devkit-disks-marshal.list
+       glib-genmarshal $< --prefix=devkit_disks_marshal --header > $@
+
+devkit-disks-marshal.c: $(top_srcdir)/src/devkit-disks-marshal.list
+       echo "#include \"devkit-disks-marshal.h\"" > $@ && glib-genmarshal $< --prefix=devkit_disks_marshal --body >> $@
 
 devkit-disks-daemon-glue.h: $(top_srcdir)/src/org.freedesktop.DeviceKit.Disks.xml Makefile.am
        dbus-binding-tool --prefix=devkit_disks_daemon --mode=glib-client --output=devkit-disks-daemon-glue.h $(top_srcdir)/src/org.freedesktop.DeviceKit.Disks.xml
index a4123cc..e8e84da 100644 (file)
 
 #include "devkit-disks-daemon-glue.h"
 #include "devkit-disks-device-glue.h"
+#include "devkit-disks-marshal.h"
 
 static DBusGConnection     *bus = NULL;
 static DBusGProxy          *disks_proxy = NULL;
 static GMainLoop           *loop;
 
-static gboolean      opt_inhibit         = FALSE;
-static gboolean      opt_enumerate       = FALSE;
-static gboolean      opt_monitor         = FALSE;
-static gboolean      opt_monitor_detail  = FALSE;
-static char         *opt_show_info       = NULL;
-static char         *opt_mount           = NULL;
-static char         *opt_mount_fstype    = NULL;
-static char         *opt_mount_options   = NULL;
-static char         *opt_unmount         = NULL;
-static char         *opt_unmount_options = NULL;
+static gboolean      opt_inhibit           = FALSE;
+static gboolean      opt_enumerate         = FALSE;
+static gboolean      opt_monitor           = FALSE;
+static gboolean      opt_monitor_detail    = FALSE;
+static char         *opt_show_info         = NULL;
+static char         *opt_mount             = NULL;
+static char         *opt_mount_fstype      = NULL;
+static char         *opt_mount_options     = NULL;
+static char         *opt_unmount           = NULL;
+static char         *opt_unmount_options   = NULL;
+static char         *opt_erase             = NULL;
+static char         *opt_erase_options     = NULL;
+static char         *opt_create_fs         = NULL;
+static char         *opt_create_fs_type    = NULL;
+static char         *opt_create_fs_options = NULL;
 
 static gboolean do_monitor (void);
 static void do_show_info (const char *object_path);
@@ -209,325 +215,226 @@ out:
 }
 
 static void
-device_added_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
+do_create_fs (const char *object_path,
+              const char *fs_type,
+              const char *options)
 {
-  g_print ("added:   %s\n", object_path);
-  if (opt_monitor_detail) {
-          do_show_info (object_path);
-          g_print ("\n");
-  }
-}
-
-static void
-device_changed_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
-{
-  g_print ("changed:   %s\n", object_path);
-  if (opt_monitor_detail) {
-          /* TODO: would be nice to just show the diff */
-          do_show_info (object_path);
-          g_print ("\n");
-  }
-}
+        DBusGProxy *proxy;
+        GError *error;
+        char **split_options;
 
-static void
-device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
-{
-  g_print ("removed: %s\n", object_path);
-}
+        split_options = NULL;
+        if (options != NULL)
+                split_options = g_strsplit (options, ",", 0);
 
-/* --- SUCKY CODE BEGIN --- */
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           "org.freedesktop.DeviceKit.Disks",
+                                           object_path,
+                                           "org.freedesktop.DeviceKit.Disks.Device");
 
-/* This totally sucks; dbus-bindings-tool and dbus-glib should be able
- * to do this for us.
- *
- * TODO: keep in sync with code in tools/devkit-disks in DeviceKit-disks.
- */
+try_again:
+        error = NULL;
+        /* Stupid glue don't let me tweak the timeout */
+        if (!dbus_g_proxy_call_with_timeout (proxy,
+                                             "CreateFilesystem",
+                                             INT_MAX,
+                                             &error,
+                                             G_TYPE_STRING,
+                                             fs_type,
+                                             G_TYPE_STRV,
+                                             split_options,
+                                             G_TYPE_INVALID,
+                                             G_TYPE_INVALID)) {
+                PolKitAction *pk_action;
+                PolKitResult pk_result;
 
-static char *
-get_property_object_path (DBusGConnection *bus,
-                          const char *svc_name,
-                          const char *obj_path,
-                          const char *if_name,
-                          const char *prop_name)
-{
-        char *ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
+                if (polkit_dbus_gerror_parse (error, &pk_action, &pk_result)) {
+                        if (pk_result != POLKIT_RESULT_NO) {
+                                char *action_id;
+                                DBusError d_error;
 
-        ret = NULL;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
+                                polkit_action_get_action_id (pk_action, &action_id);
+                                dbus_error_init (&d_error);
+                                if (polkit_auth_obtain (action_id,
+                                                        0,
+                                                        getpid (),
+                                                        &d_error)) {
+                                        polkit_action_unref (pk_action);
+                                        goto try_again;
+                                } else {
+                                        g_print ("Obtaining authorization failed: %s: %s\n",
+                                                 d_error.name, d_error.message);
+                                        dbus_error_free (&d_error);
+                                        goto out;
+                                }
+                        }
+                        polkit_action_unref (pk_action);
+                        g_error_free (error);
+                        goto out;
+                } else {
+                        g_print ("CreateFilesystem failed: %s\n", error->message);
+                        g_error_free (error);
+                        goto out;
+                }
         }
-
-        ret = (char *) g_value_get_boxed (&value);
-
 out:
-        g_object_unref (proxy);
-        return ret;
+        g_strfreev (split_options);
 }
 
-static char *
-get_property_string (DBusGConnection *bus,
-                     const char *svc_name,
-                     const char *obj_path,
-                     const char *if_name,
-                     const char *prop_name)
+static void
+do_erase (const char *object_path,
+          const char *options)
 {
-        char *ret;
         DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
+        GError *error;
+        char **split_options;
 
-        ret = NULL;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
-        }
+        split_options = NULL;
+        if (options != NULL)
+                split_options = g_strsplit (options, ",", 0);
 
-        ret = (char *) g_value_get_string (&value);
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           "org.freedesktop.DeviceKit.Disks",
+                                           object_path,
+                                           "org.freedesktop.DeviceKit.Disks.Device");
 
-out:
-        g_object_unref (proxy);
-        return ret;
-}
+try_again:
+        error = NULL;
+        /* Stupid glue don't let me tweak the timeout */
+        if (!dbus_g_proxy_call_with_timeout (proxy,
+                                             "Erase",
+                                             INT_MAX,
+                                             &error,
+                                             G_TYPE_STRV,
+                                             split_options,
+                                             G_TYPE_INVALID,
+                                             G_TYPE_INVALID)) {
+                PolKitAction *pk_action;
+                PolKitResult pk_result;
 
-static gboolean
-get_property_boolean (DBusGConnection *bus,
-                      const char *svc_name,
-                      const char *obj_path,
-                      const char *if_name,
-                      const char *prop_name)
-{
-        gboolean ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
+                if (polkit_dbus_gerror_parse (error, &pk_action, &pk_result)) {
+                        if (pk_result != POLKIT_RESULT_NO) {
+                                char *action_id;
+                                DBusError d_error;
 
-        ret = FALSE;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
+                                polkit_action_get_action_id (pk_action, &action_id);
+                                dbus_error_init (&d_error);
+                                if (polkit_auth_obtain (action_id,
+                                                        0,
+                                                        getpid (),
+                                                        &d_error)) {
+                                        polkit_action_unref (pk_action);
+                                        goto try_again;
+                                } else {
+                                        g_print ("Obtaining authorization failed: %s: %s\n",
+                                                 d_error.name, d_error.message);
+                                        dbus_error_free (&d_error);
+                                        goto out;
+                                }
+                        }
+                        polkit_action_unref (pk_action);
+                        g_error_free (error);
+                        goto out;
+                } else {
+                        g_print ("Erase failed: %s\n", error->message);
+                        g_error_free (error);
+                        goto out;
+                }
         }
-
-        ret = (gboolean) g_value_get_boolean (&value);
-
 out:
-        g_object_unref (proxy);
-        return ret;
+        g_strfreev (split_options);
 }
 
-static guint64
-get_property_uint64 (DBusGConnection *bus,
-                     const char *svc_name,
-                     const char *obj_path,
-                     const char *if_name,
-                     const char *prop_name)
+static void
+device_added_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
 {
-        guint64 ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
-
-        ret = 0;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
-        }
-
-        ret = (guint64) g_value_get_uint64 (&value);
-
-out:
-        g_object_unref (proxy);
-        return ret;
+  g_print ("added:     %s\n", object_path);
+  if (opt_monitor_detail) {
+          do_show_info (object_path);
+          g_print ("\n");
+  }
 }
 
-static GArray *
-get_property_uint64_array (DBusGConnection *bus,
-                           const char *svc_name,
-                           const char *obj_path,
-                           const char *if_name,
-                           const char *prop_name)
+static void
+device_changed_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
 {
-        GArray *ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
-
-        ret = 0;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
-        }
-
-        ret = (GArray*) g_value_get_boxed (&value);
-
-out:
-        g_object_unref (proxy);
-        return ret;
+  g_print ("changed:     %s\n", object_path);
+  if (opt_monitor_detail) {
+          /* TODO: would be nice to just show the diff */
+          do_show_info (object_path);
+          g_print ("\n");
+  }
 }
 
-static int
-get_property_int (DBusGConnection *bus,
-                  const char *svc_name,
-                  const char *obj_path,
-                  const char *if_name,
-                  const char *prop_name)
+static void
+print_job (gboolean    job_in_progress,
+           const char *job_id,
+           gboolean    job_is_cancellable,
+           int         job_num_tasks,
+           int         job_cur_task,
+           const char *job_cur_task_id,
+           double      job_cur_task_percentage)
 {
-        int ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
-
-        ret = 0;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
+        if (job_in_progress) {
+                if (job_num_tasks > 0) {
+                        g_print ("  job underway:  %s: %d/%d tasks (%s",
+                                 job_id,
+                                 job_cur_task + 1,
+                                 job_num_tasks,
+                                 job_cur_task_id);
+                        if (job_cur_task_percentage >= 0)
+                                g_print (" @ %3.0lf%%", job_cur_task_percentage);
+                        if (job_is_cancellable)
+                                g_print (", cancellable");
+                        g_print (")\n");
+                } else {
+                        g_print ("  job underway:  %s: unknown progress", job_id);
+                        if (job_is_cancellable)
+                                g_print (", cancellable");
+                        g_print ("\n");
+                }
+        } else {
+                g_print ("  job underway:  no\n");
         }
-
-        ret = (guint64) g_value_get_int (&value);
-
-out:
-        g_object_unref (proxy);
-        return ret;
 }
 
-static char **
-get_property_strlist (DBusGConnection *bus,
-                      const char *svc_name,
-                      const char *obj_path,
-                      const char *if_name,
-                      const char *prop_name)
+static void
+device_job_changed_signal_handler (DBusGProxy *proxy,
+                                   const char *object_path,
+                                   gboolean    job_in_progress,
+                                   const char *job_id,
+                                   gboolean    job_is_cancellable,
+                                   int         job_num_tasks,
+                                   int         job_cur_task,
+                                   const char *job_cur_task_id,
+                                   double      job_cur_task_percentage,
+                                   gpointer    user_data)
 {
-        char **ret;
-        DBusGProxy *proxy;
-        GValue value = { 0 };
-        GError *error = NULL;
-
-        ret = NULL;
-       proxy = dbus_g_proxy_new_for_name (bus,
-                                           svc_name,
-                                           obj_path,
-                                           "org.freedesktop.DBus.Properties");
-        if (!dbus_g_proxy_call (proxy,
-                                "Get",
-                                &error,
-                                G_TYPE_STRING,
-                                if_name,
-                                G_TYPE_STRING,
-                                prop_name,
-                                G_TYPE_INVALID,
-                                G_TYPE_VALUE,
-                                &value,
-                                G_TYPE_INVALID)) {
-                g_warning ("error: %s\n", error->message);
-                g_error_free (error);
-                goto out;
-        }
+  g_print ("job-changed: %s\n", object_path);
+  if (opt_monitor_detail) {
+          print_job (job_in_progress,
+                     job_id,
+                     job_is_cancellable,
+                     job_num_tasks,
+                     job_cur_task,
+                     job_cur_task_id,
+                     job_cur_task_percentage);
+  }
+}
 
-        ret = (char **) g_value_get_boxed (&value);
+static void
+device_removed_signal_handler (DBusGProxy *proxy, const char *object_path, gpointer user_data)
+{
+  g_print ("removed:   %s\n", object_path);
+}
 
-out:
-        /* don't crash; return an empty list */
-        if (ret == NULL) {
-                ret = g_new0 (char *,  1);
-                *ret = NULL;
-        }
+/* --- SUCKY CODE BEGIN --- */
 
-        g_object_unref (proxy);
-        return ret;
-}
+/* This totally sucks; dbus-bindings-tool and dbus-glib should be able
+ * to do this for us.
+ *
+ * TODO: keep in sync with code in tools/devkit-disks in DeviceKit-disks.
+ */
 
 typedef struct
 {
@@ -546,6 +453,14 @@ typedef struct
         guint64  device_size;
         guint64  device_block_size;
 
+        gboolean job_in_progress;
+        char    *job_id;
+        gboolean job_is_cancellable;
+        int      job_num_tasks;
+        int      job_cur_task;
+        char    *job_cur_task_id;
+        double   job_cur_task_percentage;
+
         char    *id_usage;
         char    *id_type;
         char    *id_version;
@@ -574,235 +489,155 @@ typedef struct
         char    *drive_serial;
 } DeviceProperties;
 
+static void
+collect_props (const char *key, const GValue *value, DeviceProperties *props)
+{
+        gboolean handled = TRUE;
+
+        if (strcmp (key, "native-path") == 0)
+                props->native_path = g_strdup (g_value_get_string (value));
+
+        else if (strcmp (key, "device-file") == 0)
+                props->device_file = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "device-file-by-id") == 0)
+                props->device_file_by_id = g_strdupv (g_value_get_boxed (value));
+        else if (strcmp (key, "device-file-by-path") == 0)
+                props->device_file_by_path = g_strdupv (g_value_get_boxed (value));
+        else if (strcmp (key, "device-is-partition") == 0)
+                props->device_is_partition = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-partition-table") == 0)
+                props->device_is_partition_table = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-removable") == 0)
+                props->device_is_removable = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-media-available") == 0)
+                props->device_is_media_available = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-drive") == 0)
+                props->device_is_drive = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-mounted") == 0)
+                props->device_is_mounted = g_value_get_boolean (value);
+        else if (strcmp (key, "device-mount-path") == 0)
+                props->device_mount_path = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "device-size") == 0)
+                props->device_size = g_value_get_uint64 (value);
+        else if (strcmp (key, "device-block-size") == 0)
+                props->device_block_size = g_value_get_uint64 (value);
+
+        else if (strcmp (key, "job-in-progress") == 0)
+                props->job_in_progress = g_value_get_boolean (value);
+        else if (strcmp (key, "job-id") == 0)
+                props->job_id = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "job-is-cancellable") == 0)
+                props->job_is_cancellable = g_value_get_boolean (value);
+        else if (strcmp (key, "job-num-tasks") == 0)
+                props->job_num_tasks = g_value_get_int (value);
+        else if (strcmp (key, "job-cur-task") == 0)
+                props->job_cur_task = g_value_get_int (value);
+        else if (strcmp (key, "job-cur-task-id") == 0)
+                props->job_cur_task_id = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "job-cur-task-percentage") == 0)
+                props->job_cur_task_percentage = g_value_get_double (value);
+
+        else if (strcmp (key, "id-usage") == 0)
+                props->id_usage = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "id-type") == 0)
+                props->id_type = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "id-version") == 0)
+                props->id_version = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "id-uuid") == 0)
+                props->id_uuid = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "id-label") == 0)
+                props->id_label = g_strdup (g_value_get_string (value));
+
+        else if (strcmp (key, "partition-slave") == 0)
+                props->partition_slave = g_strdup (g_value_get_boxed (value));
+        else if (strcmp (key, "partition-scheme") == 0)
+                props->partition_scheme = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "partition-number") == 0)
+                props->partition_number = g_value_get_int (value);
+        else if (strcmp (key, "partition-type") == 0)
+                props->partition_type = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "partition-label") == 0)
+                props->partition_label = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "partition-uuid") == 0)
+                props->partition_uuid = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "partition-flags") == 0)
+                props->partition_flags = g_strdupv (g_value_get_boxed (value));
+        else if (strcmp (key, "partition-offset") == 0)
+                props->partition_offset = g_value_get_uint64 (value);
+        else if (strcmp (key, "partition-size") == 0)
+                props->partition_size = g_value_get_uint64 (value);
+
+        else if (strcmp (key, "partition-table-scheme") == 0)
+                props->partition_table_scheme = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "partition-table-count") == 0)
+                props->partition_table_count = g_value_get_int (value);
+        else if (strcmp (key, "partition-table-max-number") == 0)
+                props->partition_table_max_number = g_value_get_int (value);
+        else if (strcmp (key, "partition-table-offsets") == 0) {
+                GValue dest_value = {0,};
+                g_value_init (&dest_value, dbus_g_type_get_collection ("GArray", G_TYPE_UINT64));
+                g_value_copy (value, &dest_value);
+                props->partition_table_offsets = g_value_get_boxed (&dest_value);
+        } else if (strcmp (key, "partition-table-sizes") == 0) {
+                GValue dest_value = {0,};
+                g_value_init (&dest_value, dbus_g_type_get_collection ("GArray", G_TYPE_UINT64));
+                g_value_copy (value, &dest_value);
+                props->partition_table_sizes = g_value_get_boxed (&dest_value);
+        }
+
+        else if (strcmp (key, "drive-vendor") == 0)
+                props->drive_vendor = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "drive-model") == 0)
+                props->drive_model = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "drive-revision") == 0)
+                props->drive_revision = g_strdup (g_value_get_string (value));
+        else if (strcmp (key, "drive-serial") == 0)
+                props->drive_serial = g_strdup (g_value_get_string (value));
+
+        else
+                handled = FALSE;
+
+        if (!handled)
+                g_warning ("unhandled property '%s'", key);
+}
+
 static DeviceProperties *
 device_properties_get (DBusGConnection *bus,
                        const char *object_path)
 {
         DeviceProperties *props;
+        GError *error;
+        GHashTable *hash_table;
+        DBusGProxy *prop_proxy;
+        const char *ifname = "org.freedesktop.DeviceKit.Disks.Device";
 
         props = g_new0 (DeviceProperties, 1);
-        props->native_path = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "native-path");
-
-        props->device_file = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-file");
-        props->device_file_by_id = get_property_strlist (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-file-by-id");
-        props->device_file_by_path = get_property_strlist (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-file-by-path");
-        props->device_is_partition = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-partition");
-        props->device_is_partition_table = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-partition-table");
-        props->device_is_removable = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-removable");
-        props->device_is_media_available = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-media-available");
-        props->device_is_drive = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-drive");
-        props->device_is_mounted = get_property_boolean (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-is-mounted");
-        props->device_mount_path = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-mount-path");
-        props->device_size = get_property_uint64 (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-size");
-        props->device_block_size = get_property_uint64 (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "device-block-size");
-
-        props->id_usage = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "id-usage");
-        props->id_type = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "id-type");
-        props->id_version = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "id-version");
-        props->id_uuid = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "id-uuid");
-        props->id_label = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "id-label");
-
-        props->partition_slave = get_property_object_path (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-slave");
-        props->partition_scheme = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-scheme");
-        props->partition_number = get_property_int (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-number");
-        props->partition_type = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-type");
-        props->partition_label = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-label");
-        props->partition_uuid = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-uuid");
-        props->partition_flags = get_property_strlist (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-flags");
-        props->partition_offset = get_property_uint64 (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-offset");
-        props->partition_size = get_property_uint64 (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-size");
-
-        props->partition_table_scheme = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-table-scheme");
-        props->partition_table_count = get_property_int (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-table-count");
-        props->partition_table_max_number = get_property_int (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-table-max-number");
-        props->partition_table_offsets = get_property_uint64_array (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-table-offsets");
-        props->partition_table_sizes = get_property_uint64_array (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "partition-table-sizes");
-
-        props->drive_vendor = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "drive-vendor");
-        props->drive_model = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "drive-model");
-        props->drive_revision = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "drive-revision");
-        props->drive_serial = get_property_string (
-                bus,
-                "org.freedesktop.DeviceKit.Disks",
-                object_path,
-                "org.freedesktop.DeviceKit.Disks.Device",
-                "drive-serial");
 
+       prop_proxy = dbus_g_proxy_new_for_name (bus,
+                                                "org.freedesktop.DeviceKit.Disks",
+                                                object_path,
+                                                "org.freedesktop.DBus.Properties");
+        error = NULL;
+        if (!dbus_g_proxy_call (prop_proxy,
+                                "GetAll",
+                                &error,
+                                G_TYPE_STRING,
+                                ifname,
+                                G_TYPE_INVALID,
+                                dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
+                                &hash_table,
+                                G_TYPE_INVALID)) {
+                g_warning ("Couldn't call GetAll() to get properties for %s: %s", object_path, error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        g_hash_table_foreach (hash_table, (GHFunc) collect_props, props);
+
+        g_hash_table_unref (hash_table);
+
+out:
+        g_object_unref (prop_proxy);
         return props;
 }
 
@@ -814,6 +649,8 @@ device_properties_free (DeviceProperties *props)
         g_strfreev (props->device_file_by_id);
         g_strfreev (props->device_file_by_path);
         g_free (props->device_mount_path);
+        g_free (props->job_id);
+        g_free (props->job_cur_task_id);
         g_free (props->id_usage);
         g_free (props->id_type);
         g_free (props->id_version);
@@ -858,6 +695,8 @@ do_monitor (void)
                                      G_CALLBACK (device_removed_signal_handler), NULL, NULL);
         dbus_g_proxy_connect_signal (disks_proxy, "DeviceChanged",
                                      G_CALLBACK (device_changed_signal_handler), NULL, NULL);
+        dbus_g_proxy_connect_signal (disks_proxy, "DeviceJobChanged",
+                                     G_CALLBACK (device_job_changed_signal_handler), NULL, NULL);
         g_main_loop_run (loop);
 
 out:
@@ -884,6 +723,14 @@ do_show_info (const char *object_path)
         g_print ("  mount path:    %s\n", props->device_mount_path);
         g_print ("  size:          %lld\n", props->device_size);
         g_print ("  block size:    %lld\n", props->device_block_size);
+
+        print_job (props->job_in_progress,
+                   props->job_id,
+                   props->job_is_cancellable,
+                   props->job_num_tasks,
+                   props->job_cur_task,
+                   props->job_cur_task_id,
+                   props->job_cur_task_percentage);
         g_print ("  usage:         %s\n", props->id_usage);
         g_print ("  type:          %s\n", props->id_type);
         g_print ("  version:       %s\n", props->id_version);
@@ -933,11 +780,20 @@ main (int argc, char **argv)
                 { "monitor", 0, 0, G_OPTION_ARG_NONE, &opt_monitor, "Monitor activity from the disk daemon", NULL },
                 { "monitor-detail", 0, 0, G_OPTION_ARG_NONE, &opt_monitor_detail, "Monitor with detail", NULL },
                 { "show-info", 0, 0, G_OPTION_ARG_STRING, &opt_show_info, "Show information about object path", NULL },
+
                 { "mount", 0, 0, G_OPTION_ARG_STRING, &opt_mount, "Mount the device given by the object path", NULL },
                 { "mount-fstype", 0, 0, G_OPTION_ARG_STRING, &opt_mount_fstype, "Specify file system type", NULL },
                 { "mount-options", 0, 0, G_OPTION_ARG_STRING, &opt_mount_options, "Mount options separated by comma", NULL },
+
                 { "unmount", 0, 0, G_OPTION_ARG_STRING, &opt_unmount, "Unmount the device given by the object path", NULL },
                 { "unmount-options", 0, 0, G_OPTION_ARG_STRING, &opt_unmount_options, "Unmount options separated by comma", NULL },
+
+                { "erase", 0, 0, G_OPTION_ARG_STRING, &opt_erase, "Erase a device", NULL },
+                { "erase-options", 0, 0, G_OPTION_ARG_STRING, &opt_erase_options, "Erase options", NULL },
+
+                { "create-fs", 0, 0, G_OPTION_ARG_STRING, &opt_create_fs, "Create a file system", NULL },
+                { "create-fs-type", 0, 0, G_OPTION_ARG_STRING, &opt_create_fs_type, "File system type to create", NULL },
+                { "create-fs-options", 0, 0, G_OPTION_ARG_STRING, &opt_create_fs_options, "File system create options", NULL },
                 { NULL }
         };
 
@@ -959,6 +815,19 @@ main (int argc, char **argv)
                 goto out;
         }
 
+        dbus_g_object_register_marshaller (
+                devkit_disks_marshal_VOID__STRING_BOOLEAN_STRING_BOOLEAN_INT_INT_STRING_DOUBLE,
+                G_TYPE_NONE,
+                G_TYPE_STRING,
+                G_TYPE_BOOLEAN,
+                G_TYPE_STRING,
+                G_TYPE_BOOLEAN,
+                G_TYPE_INT,
+                G_TYPE_INT,
+                G_TYPE_STRING,
+                G_TYPE_DOUBLE,
+                G_TYPE_INVALID);
+
        disks_proxy = dbus_g_proxy_new_for_name (bus,
                                                  "org.freedesktop.DeviceKit.Disks",
                                                  "/",
@@ -966,6 +835,17 @@ main (int argc, char **argv)
         dbus_g_proxy_add_signal (disks_proxy, "DeviceAdded", G_TYPE_STRING, G_TYPE_INVALID);
         dbus_g_proxy_add_signal (disks_proxy, "DeviceRemoved", G_TYPE_STRING, G_TYPE_INVALID);
         dbus_g_proxy_add_signal (disks_proxy, "DeviceChanged", G_TYPE_STRING, G_TYPE_INVALID);
+        dbus_g_proxy_add_signal (disks_proxy,
+                                 "DeviceJobChanged",
+                                 G_TYPE_STRING,
+                                 G_TYPE_BOOLEAN,
+                                 G_TYPE_STRING,
+                                 G_TYPE_BOOLEAN,
+                                 G_TYPE_INT,
+                                 G_TYPE_INT,
+                                 G_TYPE_STRING,
+                                 G_TYPE_DOUBLE,
+                                 G_TYPE_INVALID);
 
         if (opt_inhibit) {
                 char *cookie;
@@ -1000,6 +880,10 @@ main (int argc, char **argv)
                 do_mount (opt_mount, opt_mount_fstype, opt_mount_options);
         } else if (opt_unmount != NULL) {
                 do_unmount (opt_unmount, opt_unmount_options);
+        } else if (opt_erase != NULL) {
+                do_erase (opt_erase, opt_erase_options);
+        } else if (opt_create_fs != NULL && opt_create_fs_type != NULL) {
+                do_create_fs (opt_create_fs, opt_create_fs_type, opt_create_fs_options);
         }
 
         ret = 0;