poll for media, also add methods to inhibit polling and manual polling
authorDavid Zeuthen <davidz@redhat.com>
Fri, 20 Feb 2009 00:36:47 +0000 (19:36 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Fri, 20 Feb 2009 00:36:47 +0000 (19:36 -0500)
For this to work well, the polling that happens in HAL needs to be
disabled. This can be achieved by this fdi file

<?xml version="1.0" encoding="UTF-8"?>
<deviceinfo version="0.2">
  <device>
    <match key="storage.removable" bool="true">
      <merge key="storage.media_check_enabled" type="bool">false</merge>
    </match>
  </device>
</deviceinfo>

There's also a TODO in the code to properly detect SATA AN drives and
avoid polling these.

16 files changed:
policy/org.freedesktop.devicekit.disks.policy.in
src/Makefile.am
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/devkit-disks-device-private.h
src/devkit-disks-device.c
src/devkit-disks-device.h
src/devkit-disks-inhibitor.c [new file with mode: 0644]
src/devkit-disks-inhibitor.h [new file with mode: 0644]
src/main.c
src/org.freedesktop.DeviceKit.Disks.Device.xml
src/org.freedesktop.DeviceKit.Disks.xml
src/poller.c [new file with mode: 0644]
src/poller.h [new file with mode: 0644]
tools/devkit-disks-bash-completion.sh
tools/devkit-disks.c

index b8ce54b..a3f592e 100644 (file)
@@ -240,4 +240,14 @@ file are instantly applied.
     </defaults>
   </action>
 
+  <action id="org.freedesktop.devicekit.disks.inhibit-polling">
+    <_description>Inhibit media detection</_description>
+    <_message>Authentication is required to inhibit media detection</_message>
+    <defaults>
+      <allow_any>no</allow_any>
+      <allow_inactive>no</allow_inactive>
+      <allow_active>yes</allow_active>
+    </defaults>
+  </action>
+
 </policyconfig>
index c30e525..9aee887 100644 (file)
@@ -45,6 +45,8 @@ devkit_disks_daemon_SOURCES =                                                 \
        devkit-disks-device-private.h                                   \
        mounts-file.h                   mounts-file.c                   \
        devkit-disks-mount-monitor.h    devkit-disks-mount-monitor.c    \
+       devkit-disks-inhibitor.h        devkit-disks-inhibitor.c        \
+       poller.h                        poller.c                        \
        main.c                                                          \
        $(BUILT_SOURCES)
 
index 13acdd4..17335ed 100644 (file)
@@ -54,6 +54,8 @@
 #include "devkit-disks-device-private.h"
 #include "mounts-file.h"
 #include "devkit-disks-mount-monitor.h"
+#include "poller.h"
+#include "devkit-disks-inhibitor.h"
 
 #include "devkit-disks-daemon-glue.h"
 #include "devkit-disks-marshal.h"
@@ -99,12 +101,17 @@ struct DevkitDisksDaemonPrivate
         guint                    smart_refresh_timer_id;
 
         DevkitDisksLogger       *logger;
+
+        GList *polling_inhibitors;
 };
 
 static void     devkit_disks_daemon_class_init  (DevkitDisksDaemonClass *klass);
 static void     devkit_disks_daemon_init        (DevkitDisksDaemon      *seat);
 static void     devkit_disks_daemon_finalize    (GObject     *object);
 
+static void     daemon_polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+                                                          DevkitDisksDaemon    *daemon);
+
 G_DEFINE_TYPE (DevkitDisksDaemon, devkit_disks_daemon, G_TYPE_OBJECT)
 
 #define DEVKIT_DISKS_DAEMON_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), DEVKIT_TYPE_DISKS_DAEMON, DevkitDisksDaemonPrivate))
@@ -563,6 +570,7 @@ static void
 devkit_disks_daemon_finalize (GObject *object)
 {
         DevkitDisksDaemon *daemon;
+        GList *l;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (DEVKIT_IS_DISKS_DAEMON (object));
@@ -610,6 +618,13 @@ devkit_disks_daemon_finalize (GObject *object)
                 g_object_unref (daemon->priv->logger);
         }
 
+        for (l = daemon->priv->polling_inhibitors; l != NULL; l = l->next) {
+                DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+                g_signal_handlers_disconnect_by_func (inhibitor, daemon_polling_inhibitor_disconnected_cb, daemon);
+                g_object_unref (inhibitor);
+        }
+        g_list_free (daemon->priv->polling_inhibitors);
+
         G_OBJECT_CLASS (devkit_disks_daemon_parent_class)->finalize (object);
 }
 
@@ -647,6 +662,8 @@ pk_io_remove_watch (PolKitContext *pk_context, int watch_id)
         g_source_remove (watch_id);
 }
 
+void devkit_disks_inhibitor_name_owner_changed (DBusMessage *message);
+
 static DBusHandlerResult
 _filter (DBusConnection *connection, DBusMessage *message, void *user_data)
 {
@@ -658,6 +675,9 @@ _filter (DBusConnection *connection, DBusMessage *message, void *user_data)
         if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
                 /* pass NameOwnerChanged signals from the bus to PolKitTracker */
                 polkit_tracker_dbus_func (daemon->priv->pk_tracker, message);
+
+                /* for now, pass NameOwnerChanged to DevkitDisksInhibitor */
+                devkit_disks_inhibitor_name_owner_changed (message);
         }
 
         if (interface != NULL && g_str_has_prefix (interface, "org.freedesktop.ConsoleKit")) {
@@ -718,6 +738,7 @@ device_changed (DevkitDisksDaemon *daemon, DevkitDevice *d, gboolean synthesized
                         device_remove (daemon, d);
                 } else {
                         g_print ("changed %s\n", native_path);
+                        devkit_disks_daemon_local_update_poller (daemon);
                 }
         } else {
                 g_print ("treating change event as add on %s\n", native_path);
@@ -767,6 +788,7 @@ device_add (DevkitDisksDaemon *daemon, DevkitDevice *d, gboolean emit_event)
                                 object_path = devkit_disks_device_local_get_object_path (device);
                                 g_signal_emit (daemon, signals[DEVICE_ADDED_SIGNAL], 0, object_path);
                         }
+                        devkit_disks_daemon_local_update_poller (daemon);
                 } else {
                         g_print ("ignoring add event on %s\n", native_path);
                 }
@@ -788,6 +810,7 @@ device_remove (DevkitDisksDaemon *daemon, DevkitDevice *d)
                 g_signal_emit (daemon, signals[DEVICE_REMOVED_SIGNAL], 0,
                                devkit_disks_device_local_get_object_path (device));
                 g_object_unref (device);
+                devkit_disks_daemon_local_update_poller (daemon);
         }
 }
 
@@ -1159,6 +1182,27 @@ devkit_disks_damon_local_check_auth (DevkitDisksDaemon     *daemon,
         return ret;
 }
 
+/*--------------------------------------------------------------------------------------------------------------*/
+
+void
+devkit_disks_daemon_local_update_poller (DevkitDisksDaemon *daemon)
+{
+        GHashTableIter hash_iter;
+        DevkitDisksDevice *device;
+        GList *devices_to_poll;
+
+        devices_to_poll = NULL;
+
+        g_hash_table_iter_init (&hash_iter, daemon->priv->map_object_path_to_device);
+        while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &device)) {
+                if (device->priv->info.device_is_media_change_detected)
+                        devices_to_poll = g_list_prepend (devices_to_poll, device);
+        }
+
+        poller_set_devices (devices_to_poll);
+
+        g_list_free (devices_to_poll);
+}
 
 /*--------------------------------------------------------------------------------------------------------------*/
 
@@ -1244,3 +1288,128 @@ out:
 }
 
 /*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+polling_update_devices (DevkitDisksDaemon *daemon)
+{
+        GHashTableIter hash_iter;
+        DevkitDisksDevice *device;
+
+        g_hash_table_iter_init (&hash_iter, daemon->priv->map_object_path_to_device);
+        while (g_hash_table_iter_next (&hash_iter, NULL, (gpointer) &device)) {
+
+                if (devkit_disks_device_local_update_media_detection (device)) {
+                        DevkitDevice *d;
+
+                        d = g_object_ref (device->priv->d);
+                        device_changed (daemon, d, TRUE);
+                        g_object_unref (d);
+                }
+        }
+
+        devkit_disks_daemon_local_update_poller (daemon);
+}
+
+static void
+daemon_polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+                                          DevkitDisksDaemon    *daemon)
+{
+        daemon->priv->polling_inhibitors = g_list_remove (daemon->priv->polling_inhibitors, inhibitor);
+        g_signal_handlers_disconnect_by_func (inhibitor, daemon_polling_inhibitor_disconnected_cb, daemon);
+        g_object_unref (inhibitor);
+
+        polling_update_devices (daemon);
+}
+
+gboolean
+devkit_disks_daemon_local_has_polling_inhibitors (DevkitDisksDaemon *daemon)
+{
+        return daemon->priv->polling_inhibitors != NULL;
+}
+
+gboolean
+devkit_disks_daemon_drive_inhibit_all_polling (DevkitDisksDaemon     *daemon,
+                                               char                 **options,
+                                               DBusGMethodInvocation *context)
+{
+        DevkitDisksInhibitor *inhibitor;
+        PolKitCaller *pk_caller;
+        guint n;
+
+        if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (daemon, context)) == NULL)
+                goto out;
+
+
+        if (!devkit_disks_damon_local_check_auth (daemon,
+                                                  pk_caller,
+                                                  "org.freedesktop.devicekit.disks.inhibit-polling",
+                                                  context))
+                goto out;
+
+        for (n = 0; options[n] != NULL; n++) {
+                const char *option = options[n];
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_INVALID_OPTION,
+                             "Unknown option %s", option);
+                goto out;
+        }
+
+        inhibitor = devkit_disks_inhibitor_new (context);
+
+        daemon->priv->polling_inhibitors = g_list_prepend (daemon->priv->polling_inhibitors, inhibitor);
+        g_signal_connect (inhibitor, "disconnected", G_CALLBACK (daemon_polling_inhibitor_disconnected_cb), daemon);
+
+        polling_update_devices (daemon);
+
+        dbus_g_method_return (context, devkit_disks_inhibitor_get_cookie (inhibitor));
+
+out:
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+gboolean
+devkit_disks_daemon_drive_uninhibit_all_polling (DevkitDisksDaemon     *daemon,
+                                                 char                  *cookie,
+                                                 DBusGMethodInvocation *context)
+{
+        const gchar *sender;
+        DevkitDisksInhibitor *inhibitor;
+        GList *l;
+
+        sender = dbus_g_method_get_sender (context);
+
+        inhibitor = NULL;
+        for (l = daemon->priv->polling_inhibitors; l != NULL; l = l->next) {
+                DevkitDisksInhibitor *i = DEVKIT_DISKS_INHIBITOR (l->data);
+
+                if (g_strcmp0 (devkit_disks_inhibitor_get_unique_dbus_name (i), sender) == 0 &&
+                    g_strcmp0 (devkit_disks_inhibitor_get_cookie (i), cookie) == 0) {
+                        inhibitor = i;
+                        break;
+                }
+        }
+
+        if (inhibitor == NULL) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_FAILED,
+                             "No such inhibitor");
+                goto out;
+        }
+
+        daemon->priv->polling_inhibitors = g_list_remove (daemon->priv->polling_inhibitors, inhibitor);
+        g_object_unref (inhibitor);
+
+        polling_update_devices (daemon);
+
+        dbus_g_method_return (context);
+
+ out:
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
index 134f98f..fd66ef1 100644 (file)
@@ -112,6 +112,10 @@ void               devkit_disks_daemon_local_update_mount_state  (DevkitDisksDae
 void               devkit_disks_daemon_local_synthesize_changed  (DevkitDisksDaemon       *daemon,
                                                                   DevkitDevice            *d);
 
+void               devkit_disks_daemon_local_update_poller       (DevkitDisksDaemon       *daemon);
+
+gboolean           devkit_disks_daemon_local_has_polling_inhibitors (DevkitDisksDaemon       *daemon);
+
 DevkitDisksLogger *devkit_disks_daemon_local_get_logger (DevkitDisksDaemon *daemon);
 
 /* exported methods */
@@ -128,6 +132,14 @@ gboolean devkit_disks_daemon_linux_md_start (DevkitDisksDaemon     *daemon,
                                              char                 **options,
                                              DBusGMethodInvocation *context);
 
+gboolean devkit_disks_daemon_drive_inhibit_all_polling (DevkitDisksDaemon     *daemon,
+                                                        char                 **options,
+                                                        DBusGMethodInvocation *context);
+
+gboolean devkit_disks_daemon_drive_uninhibit_all_polling (DevkitDisksDaemon     *daemon,
+                                                          char                  *cookie,
+                                                          DBusGMethodInvocation *context);
+
 G_END_DECLS
 
 #endif /* __DEVKIT_DISKS_DAEMON_H__ */
index 881b91f..11021bb 100644 (file)
@@ -90,6 +90,9 @@ struct DevkitDisksDevicePrivate
                 gboolean device_is_partition_table;
                 gboolean device_is_removable;
                 gboolean device_is_media_available;
+                gboolean device_is_media_change_detected;
+                gboolean device_is_media_change_detection_inhibitable;
+                gboolean device_is_media_change_detection_inhibited;
                 gboolean device_is_read_only;
                 gboolean device_is_drive;
                 gboolean device_is_optical_disc;
@@ -178,6 +181,9 @@ struct DevkitDisksDevicePrivate
                 GPtrArray *holders_objpath;
         } info;
 
+        /* A list of current polling inhibitors (DevkitDisksInhibitor objects) */
+        GList *polling_inhibitors;
+
         /* We want S.M.A.R.T. to persist over change events */
         gboolean drive_smart_is_capable;
         gboolean drive_smart_is_enabled;
index 6e2c5f8..6539481 100644 (file)
@@ -54,6 +54,8 @@
 #include "devkit-disks-device-private.h"
 #include "devkit-disks-marshal.h"
 #include "mounts-file.h"
+#include "devkit-disks-inhibitor.h"
+#include "poller.h"
 
 /*--------------------------------------------------------------------------------------------------------------*/
 #include "devkit-disks-device-glue.h"
@@ -62,6 +64,9 @@ static void     devkit_disks_device_class_init  (DevkitDisksDeviceClass *klass);
 static void     devkit_disks_device_init        (DevkitDisksDevice      *seat);
 static void     devkit_disks_device_finalize    (GObject     *object);
 
+static void     polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+                                                   DevkitDisksDevice   *device);
+
 static void     init_info                  (DevkitDisksDevice *device);
 static void     free_info                  (DevkitDisksDevice *device);
 static gboolean update_info                (DevkitDisksDevice *device);
@@ -127,6 +132,9 @@ enum
         PROP_DEVICE_IS_PARTITION_TABLE,
         PROP_DEVICE_IS_REMOVABLE,
         PROP_DEVICE_IS_MEDIA_AVAILABLE,
+        PROP_DEVICE_IS_MEDIA_CHANGE_DETECTED,
+        PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITABLE,
+        PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITED,
         PROP_DEVICE_IS_READ_ONLY,
         PROP_DEVICE_IS_DRIVE,
         PROP_DEVICE_IS_OPTICAL_DISC,
@@ -296,6 +304,15 @@ get_property (GObject         *object,
        case PROP_DEVICE_IS_MEDIA_AVAILABLE:
                g_value_set_boolean (value, device->priv->info.device_is_media_available);
                break;
+       case PROP_DEVICE_IS_MEDIA_CHANGE_DETECTED:
+               g_value_set_boolean (value, device->priv->info.device_is_media_change_detected);
+               break;
+       case PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITABLE:
+               g_value_set_boolean (value, device->priv->info.device_is_media_change_detection_inhibitable);
+               break;
+       case PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITED:
+               g_value_set_boolean (value, device->priv->info.device_is_media_change_detection_inhibited);
+               break;
        case PROP_DEVICE_IS_READ_ONLY:
                g_value_set_boolean (value, device->priv->info.device_is_read_only);
                break;
@@ -673,6 +690,18 @@ devkit_disks_device_class_init (DevkitDisksDeviceClass *klass)
                 g_param_spec_boolean ("device-is-media-available", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
                 object_class,
+                PROP_DEVICE_IS_MEDIA_CHANGE_DETECTED,
+                g_param_spec_boolean ("device-is-media-change-detected", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITABLE,
+                g_param_spec_boolean ("device-is-media-change-detection-inhibitable", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
+                PROP_DEVICE_IS_MEDIA_CHANGE_DETECTION_INHIBITED,
+                g_param_spec_boolean ("device-is-media-change-detection-inhibited", NULL, NULL, FALSE, G_PARAM_READABLE));
+        g_object_class_install_property (
+                object_class,
                 PROP_DEVICE_IS_READ_ONLY,
                 g_param_spec_boolean ("device-is-read-only", NULL, NULL, FALSE, G_PARAM_READABLE));
         g_object_class_install_property (
@@ -1066,6 +1095,7 @@ static void
 devkit_disks_device_finalize (GObject *object)
 {
         DevkitDisksDevice *device;
+        GList *l;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (DEVKIT_IS_DISKS_DEVICE (object));
@@ -1085,6 +1115,13 @@ devkit_disks_device_finalize (GObject *object)
         g_ptr_array_foreach (device->priv->drive_smart_attributes, (GFunc) g_value_array_free, NULL);
         g_ptr_array_free (device->priv->drive_smart_attributes, TRUE);
 
+        for (l = device->priv->polling_inhibitors; l != NULL; l = l->next) {
+                DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+                g_signal_handlers_disconnect_by_func (inhibitor, polling_inhibitor_disconnected_cb, device);
+                g_object_unref (inhibitor);
+        }
+        g_list_free (device->priv->polling_inhibitors);
+
         if (device->priv->linux_md_poll_timeout_id > 0)
                 g_source_remove (device->priv->linux_md_poll_timeout_id);
 
@@ -2672,6 +2709,9 @@ update_info (DevkitDisksDevice *device)
                 }
         }
 
+        /* figure out if we need to poll the device */
+        devkit_disks_device_local_update_media_detection (device);
+
         ret = TRUE;
 
 out:
@@ -2701,6 +2741,41 @@ out:
         return ret;
 }
 
+/* returns TRUE if something changed */
+gboolean
+devkit_disks_device_local_update_media_detection (DevkitDisksDevice *device)
+{
+        gboolean ret;
+        gboolean old_media_change_detected;
+        gboolean old_media_change_detection_inhibitable;
+        gboolean old_media_change_detection_inhibited;
+
+        old_media_change_detected              = device->priv->info.device_is_media_change_detected;
+        old_media_change_detection_inhibitable = device->priv->info.device_is_media_change_detected;
+        old_media_change_detection_inhibited   = device->priv->info.device_is_media_change_detected;
+
+        if (device->priv->info.device_is_removable) {
+                /* TODO: figure out if the device supports SATA AN */
+                device->priv->info.device_is_media_change_detection_inhibitable = TRUE;
+
+                if (device->priv->polling_inhibitors != NULL ||
+                    devkit_disks_daemon_local_has_polling_inhibitors (device->priv->daemon)) {
+                        device->priv->info.device_is_media_change_detected = FALSE;
+                        device->priv->info.device_is_media_change_detection_inhibited = TRUE;
+                } else {
+                        device->priv->info.device_is_media_change_detected = TRUE;
+                        device->priv->info.device_is_media_change_detection_inhibited = FALSE;
+                }
+        }
+
+        ret =   (old_media_change_detected              != device->priv->info.device_is_media_change_detected) ||
+                (old_media_change_detection_inhibitable != device->priv->info.device_is_media_change_detected) ||
+                (old_media_change_detection_inhibited   != device->priv->info.device_is_media_change_detected);
+
+        return ret;
+}
+
+
 gboolean
 devkit_disks_device_local_is_busy (DevkitDisksDevice *device)
 {
@@ -8354,3 +8429,147 @@ pending:
 
 
 /*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+                                   DevkitDisksDevice   *device)
+{
+        device->priv->polling_inhibitors = g_list_remove (device->priv->polling_inhibitors, inhibitor);
+        g_signal_handlers_disconnect_by_func (inhibitor, polling_inhibitor_disconnected_cb, device);
+        g_object_unref (inhibitor);
+
+        update_info (device);
+        devkit_disks_daemon_local_update_poller (device->priv->daemon);
+}
+
+gboolean
+devkit_disks_device_drive_inhibit_polling (DevkitDisksDevice     *device,
+                                           char                 **options,
+                                           DBusGMethodInvocation *context)
+{
+        DevkitDisksInhibitor *inhibitor;
+        PolKitCaller *pk_caller;
+        guint n;
+
+        if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (device->priv->daemon, context)) == NULL)
+                goto out;
+
+        if (!device->priv->info.device_is_drive) {
+                throw_error (context, DEVKIT_DISKS_ERROR_NOT_DRIVE,
+                             "Device is not a drive");
+                goto out;
+        }
+
+        if (!device->priv->info.device_is_media_change_detection_inhibitable) {
+                throw_error (context, DEVKIT_DISKS_ERROR_FAILED,
+                             "Media detection cannot be inhibited");
+                goto out;
+        }
+
+        if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
+                                                  pk_caller,
+                                                  "org.freedesktop.devicekit.disks.inhibit-polling",
+                                                  context))
+                goto out;
+
+        for (n = 0; options[n] != NULL; n++) {
+                const char *option = options[n];
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_INVALID_OPTION,
+                             "Unknown option %s", option);
+                goto out;
+        }
+
+        inhibitor = devkit_disks_inhibitor_new (context);
+
+        device->priv->polling_inhibitors = g_list_prepend (device->priv->polling_inhibitors, inhibitor);
+        g_signal_connect (inhibitor, "disconnected", G_CALLBACK (polling_inhibitor_disconnected_cb), device);
+
+        update_info (device);
+        devkit_disks_daemon_local_update_poller (device->priv->daemon);
+
+        dbus_g_method_return (context, devkit_disks_inhibitor_get_cookie (inhibitor));
+
+out:
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+gboolean
+devkit_disks_device_drive_uninhibit_polling (DevkitDisksDevice     *device,
+                                             char                  *cookie,
+                                             DBusGMethodInvocation *context)
+{
+        const gchar *sender;
+        DevkitDisksInhibitor *inhibitor;
+        GList *l;
+
+        sender = dbus_g_method_get_sender (context);
+
+        inhibitor = NULL;
+        for (l = device->priv->polling_inhibitors; l != NULL; l = l->next) {
+                DevkitDisksInhibitor *i = DEVKIT_DISKS_INHIBITOR (l->data);
+
+                if (g_strcmp0 (devkit_disks_inhibitor_get_unique_dbus_name (i), sender) == 0 &&
+                    g_strcmp0 (devkit_disks_inhibitor_get_cookie (i), cookie) == 0) {
+                        inhibitor = i;
+                        break;
+                }
+        }
+
+        if (inhibitor == NULL) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_FAILED,
+                             "No such inhibitor");
+                goto out;
+        }
+
+        device->priv->polling_inhibitors = g_list_remove (device->priv->polling_inhibitors, inhibitor);
+        g_object_unref (inhibitor);
+
+        update_info (device);
+        devkit_disks_daemon_local_update_poller (device->priv->daemon);
+
+        dbus_g_method_return (context);
+
+ out:
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
+gboolean
+devkit_disks_device_drive_poll_media (DevkitDisksDevice     *device,
+                                      DBusGMethodInvocation *context)
+{
+        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_drive) {
+                throw_error (context, DEVKIT_DISKS_ERROR_NOT_DRIVE,
+                             "Device is not a drive");
+                goto out;
+        }
+
+        if (!devkit_disks_damon_local_check_auth (device->priv->daemon,
+                                                  pk_caller,
+                                                  "org.freedesktop.devicekit.disks.inhibit-polling",
+                                                  context))
+                goto out;
+
+        poller_poll_device (device->priv->info.device_file);
+
+        dbus_g_method_return (context);
+
+out:
+        if (pk_caller != NULL)
+                polkit_caller_unref (pk_caller);
+        return TRUE;
+}
+
index 9739260..5ffbc3f 100644 (file)
@@ -79,6 +79,8 @@ void               devkit_disks_device_local_set_unmounted       (DevkitDisksDev
 gboolean           devkit_disks_device_local_is_busy             (DevkitDisksDevice *device);
 gboolean           devkit_disks_device_local_partitions_are_busy (DevkitDisksDevice *device);
 
+gboolean           devkit_disks_device_local_update_media_detection (DevkitDisksDevice *device);
+
 /* exported methods */
 
 gboolean devkit_disks_device_job_cancel (DevkitDisksDevice     *device,
@@ -185,6 +187,17 @@ gboolean devkit_disks_device_linux_md_remove_component (DevkitDisksDevice     *d
                                                         char                 **options,
                                                         DBusGMethodInvocation *context);
 
+gboolean devkit_disks_device_drive_inhibit_polling (DevkitDisksDevice     *device,
+                                                    char                 **options,
+                                                    DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_drive_uninhibit_polling (DevkitDisksDevice     *device,
+                                                      char                  *cookie,
+                                                      DBusGMethodInvocation *context);
+
+gboolean devkit_disks_device_drive_poll_media (DevkitDisksDevice     *device,
+                                               DBusGMethodInvocation *context);
+
 G_END_DECLS
 
 #endif /* __DEVKIT_DISKS_DEVICE_H__ */
diff --git a/src/devkit-disks-inhibitor.c b/src/devkit-disks-inhibitor.c
new file mode 100644 (file)
index 0000000..fbf0aa1
--- /dev/null
@@ -0,0 +1,157 @@
+/* -*- 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 <string.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "devkit-disks-inhibitor.h"
+
+struct DevkitDisksInhibitorPrivate
+{
+        gchar *unique_dbus_name;
+        gchar *cookie;
+};
+
+enum
+{
+        DISCONNECTED_SIGNAL,
+        LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (DevkitDisksInhibitor, devkit_disks_inhibitor, G_TYPE_OBJECT)
+
+static GList *inhibitors = NULL;
+
+static void
+devkit_disks_inhibitor_finalize (GObject *object)
+{
+        DevkitDisksInhibitor *inhibitor;
+
+        inhibitor = DEVKIT_DISKS_INHIBITOR (object);
+
+        inhibitors = g_list_remove (inhibitors, inhibitor);
+
+        g_free (inhibitor->priv->unique_dbus_name);
+        g_free (inhibitor->priv->cookie);
+
+        G_OBJECT_CLASS (devkit_disks_inhibitor_parent_class)->finalize (object);
+}
+
+static void
+devkit_disks_inhibitor_init (DevkitDisksInhibitor *inhibitor)
+{
+  inhibitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (inhibitor, DEVKIT_TYPE_DISKS_INHIBITOR, DevkitDisksInhibitorPrivate);
+}
+
+static void
+devkit_disks_inhibitor_class_init (DevkitDisksInhibitorClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = devkit_disks_inhibitor_finalize;
+
+        g_type_class_add_private (klass, sizeof (DevkitDisksInhibitorPrivate));
+
+        signals[DISCONNECTED_SIGNAL] =
+                g_signal_new ("disconnected",
+                              G_OBJECT_CLASS_TYPE (klass),
+                              G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                              0,
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+}
+
+DevkitDisksInhibitor *
+devkit_disks_inhibitor_new (DBusGMethodInvocation *context)
+{
+        DevkitDisksInhibitor *inhibitor;
+        static gint inhibitor_count = 0;
+
+        inhibitor = DEVKIT_DISKS_INHIBITOR (g_object_new (DEVKIT_TYPE_DISKS_INHIBITOR, NULL));
+
+        inhibitor->priv->unique_dbus_name = g_strdup (dbus_g_method_get_sender (context));
+
+        /* TODO: maybe use a real random number (if it turns out we need this to be cryptographically secure etc.) */
+        inhibitor->priv->cookie = g_strdup_printf ("devkit_disks_inhibitor_%d", inhibitor_count++);
+
+        inhibitors = g_list_prepend (inhibitors, inhibitor);
+
+        return inhibitor;
+}
+
+const gchar *
+devkit_disks_inhibitor_get_unique_dbus_name (DevkitDisksInhibitor *inhibitor)
+{
+        return inhibitor->priv->unique_dbus_name;
+}
+
+const gchar *
+devkit_disks_inhibitor_get_cookie (DevkitDisksInhibitor *inhibitor)
+{
+        return inhibitor->priv->cookie;
+}
+
+
+void devkit_disks_inhibitor_name_owner_changed (DBusMessage *message);
+
+void
+devkit_disks_inhibitor_name_owner_changed (DBusMessage *message)
+{
+
+        if (dbus_message_is_signal (message, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
+               char *name;
+               char *new_owner;
+               char *old_owner;
+
+               if (!dbus_message_get_args (message, NULL,
+                                           DBUS_TYPE_STRING, &name,
+                                           DBUS_TYPE_STRING, &old_owner,
+                                           DBUS_TYPE_STRING, &new_owner,
+                                           DBUS_TYPE_INVALID)) {
+
+                        g_warning ("The NameOwnerChanged signal has the wrong signature.");
+                       goto out;
+               }
+
+                if (strlen (new_owner) == 0) {
+                        GList *l;
+
+                        for (l = inhibitors; l != NULL; l = l->next) {
+                                DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+
+                                g_debug (" looking at %s", inhibitor->priv->unique_dbus_name);
+                                if (g_strcmp0 (name, inhibitor->priv->unique_dbus_name) == 0) {
+                                        g_signal_emit_by_name (inhibitor, "disconnected");
+                                }
+                        }
+                }
+        }
+
+ out:
+        ;
+}
+
diff --git a/src/devkit-disks-inhibitor.h b/src/devkit-disks-inhibitor.h
new file mode 100644 (file)
index 0000000..b6434dd
--- /dev/null
@@ -0,0 +1,58 @@
+/* -*- 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
+ *
+ */
+
+#ifndef __DEVKIT_DISKS_INHIBITOR_H__
+#define __DEVKIT_DISKS_INHIBITOR_H__
+
+#include <glib-object.h>
+#include <dbus/dbus-glib.h>
+
+G_BEGIN_DECLS
+
+#define DEVKIT_TYPE_DISKS_INHIBITOR         (devkit_disks_inhibitor_get_type ())
+#define DEVKIT_DISKS_INHIBITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), DEVKIT_TYPE_DISKS_INHIBITOR, DevkitDisksInhibitor))
+#define DEVKIT_DISKS_INHIBITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), DEVKIT_TYPE_DISKS_INHIBITOR, DevkitDisksInhibitorClass))
+#define DEVKIT_IS_DISKS_INHIBITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), DEVKIT_TYPE_DISKS_INHIBITOR))
+#define DEVKIT_IS_DISKS_INHIBITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), DEVKIT_TYPE_DISKS_INHIBITOR))
+#define DEVKIT_DISKS_INHIBITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), DEVKIT_TYPE_DISKS_INHIBITOR, DevkitDisksInhibitorClass))
+
+typedef struct DevkitDisksInhibitor        DevkitDisksInhibitor;
+typedef struct DevkitDisksInhibitorClass   DevkitDisksInhibitorClass;
+typedef struct DevkitDisksInhibitorPrivate DevkitDisksInhibitorPrivate;
+
+struct DevkitDisksInhibitor
+{
+        GObject        parent;
+        DevkitDisksInhibitorPrivate *priv;
+};
+
+struct DevkitDisksInhibitorClass
+{
+        GObjectClass   parent_class;
+};
+
+GType                 devkit_disks_inhibitor_get_type             (void) G_GNUC_CONST;
+DevkitDisksInhibitor *devkit_disks_inhibitor_new                  (DBusGMethodInvocation *context);
+const gchar          *devkit_disks_inhibitor_get_unique_dbus_name (DevkitDisksInhibitor *inhibitor);
+const gchar          *devkit_disks_inhibitor_get_cookie           (DevkitDisksInhibitor *inhibitor);
+
+G_END_DECLS
+
+#endif /* __DEVKIT_DISKS_INHIBITOR_H__ */
index 05fc47c..6989920 100644 (file)
@@ -43,6 +43,7 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <devkit-gobject/devkit-gobject.h>
 
+#include "poller.h"
 #include "devkit-disks-daemon.h"
 
 
@@ -130,6 +131,7 @@ main (int argc, char **argv)
         };
 
         ret = 1;
+        error = NULL;
 
         /* run with a controlled path */
         if (!g_setenv ("PATH", PACKAGE_LIBEXEC_DIR ":/sbin:/bin:/usr/sbin:/usr/bin", TRUE)) {
@@ -145,12 +147,16 @@ main (int argc, char **argv)
 
         g_type_init ();
 
+        /* fork the polling process early */
+        if (!poller_setup (argc, argv)) {
+                goto out;
+        }
+
         context = g_option_context_new ("DeviceKit Disks Daemon");
         g_option_context_add_main_entries (context, entries, NULL);
         g_option_context_parse (context, &argc, &argv, NULL);
         g_option_context_free (context);
 
-        error = NULL;
         bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
         if (bus == NULL) {
                 g_warning ("Couldn't connect to system bus: %s", error->message);
index f6bcd47..e896b1c 100644 (file)
 
     <!-- ************************************************************ -->
 
+    <method name="DriveInhibitPolling">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="options" direction="in" type="as">
+        <doc:doc><doc:summary>Inhibit options. Currently no options are recognized.</doc:summary></doc:doc>
+      </arg>
+
+      <arg name="cookie" direction="out" type="s">
+        <doc:doc><doc:summary>
+            A cookie that can be used in the
+            <doc:ref type="method" to="Device.DriveUninhibitPolling">DriveUninhibitPolling()</doc:ref> method
+            to stop inhibiting polling of the device.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Inhibits the daemon from polling the device for media changes.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller will need one of the following PolicyKit authorizations:
+          <doc:list>
+            <doc:item>
+              <doc:term>org.freedesktop.devicekit.disks.inhibit-polling</doc:term>
+              <doc:definition>To inhibit polling</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+          <doc:error name="&ERROR_FAILED;">if incoming parameters are invalid or an unknown error occured</doc:error>
+          <doc:error name="&ERROR_NOT_DRIVE;">device is not a drive</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
+    <method name="DriveUninhibitPolling">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
+      <arg name="cookie" direction="in" type="s">
+        <doc:doc><doc:summary>
+            A cookie obtained from the
+            <doc:ref type="method" to="Device.DriveInhibitPolling">DriveInhibitPolling()</doc:ref> method.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Uninhibits daemon from polling the device for media changes.
+          </doc:para>
+        </doc:description>
+        <doc:errors>
+          <doc:error name="&ERROR_FAILED;">if the given cookie is malformed</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+
+    <!-- ************************************************************ -->
+
+    <method name="DrivePollMedia">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Polls the drive for media. This is typically only useful when the
+            <doc:ref type="property" to="Device:device-is-media-change-detected">device-is-media-change-detected</doc:ref> property
+            is FALSE.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller will need one of the following PolicyKit authorizations:
+          <doc:list>
+            <doc:item>
+              <doc:term>org.freedesktop.devicekit.disks.inhibit-polling</doc:term>
+              <doc:definition>To inhibit polling</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="&ERROR_FAILED;">if an unknown error occured</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
     <method name="DriveEject">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="options" direction="in" type="as">
             TRUE if media is available in the device.
       </doc:para></doc:description></doc:doc>
     </property>
+    <property name="device-is-media-change-detected" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE if media changes are detected.
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="device-is-media-change-detection-inhibitable" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE if it is possible to inhibit media detection on the device (to avoid keeping the device in a high power state).
+      </doc:para></doc:description></doc:doc>
+    </property>
+    <property name="device-is-media-change-detection-inhibited" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE if media detection is inhibited (to avoid keeping the device in a high power state).
+      </doc:para></doc:description></doc:doc>
+    </property>
     <property name="device-is-read-only" type="b" access="read">
       <doc:doc><doc:description><doc:para>
             TRUE if the device read-only.
index 4189c0a..c18c101 100644 (file)
 
     <!-- ************************************************************ -->
 
+    <method name="DriveInhibitAllPolling">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <arg name="options" direction="in" type="as">
+        <doc:doc><doc:summary>Inhibit options. Currently no options are recognized.</doc:summary></doc:doc>
+      </arg>
+
+      <arg name="cookie" direction="out" type="s">
+        <doc:doc><doc:summary>
+            A cookie that can be used in the
+            <doc:ref type="method" to="Disks.DriveUninhibitAllPolling">DriveUninhibitAllPolling()</doc:ref> method
+            to stop inhibiting polling of all devices.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Inhibits the daemon from polling devices for media changes.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          The caller will need one of the following PolicyKit authorizations:
+          <doc:list>
+            <doc:item>
+              <doc:term>org.freedesktop.devicekit.disks.inhibit-polling</doc:term>
+              <doc:definition>To inhibit polling</doc:definition>
+            </doc:item>
+          </doc:list>
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller lacks the appropriate PolicyKit authorization</doc:error>
+          <doc:error name="&ERROR_FAILED;">if incoming parameters are invalid or an unknown error occured</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
+    <method name="DriveUninhibitAllPolling">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+
+      <arg name="cookie" direction="in" type="s">
+        <doc:doc><doc:summary>
+            A cookie obtained from the
+            <doc:ref type="method" to="Disks.DriveInhibitAllPolling">DriveInhibitAllPolling()</doc:ref> method.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Uninhibits daemon from polling devices for media changes.
+          </doc:para>
+        </doc:description>
+        <doc:errors>
+          <doc:error name="&ERROR_FAILED;">if the given cookie is malformed</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
     <method name="LinuxMdStart">
       <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
       <arg name="components" direction="in" type="ao">
diff --git a/src/poller.c b/src/poller.c
new file mode 100644 (file)
index 0000000..7dcc44c
--- /dev/null
@@ -0,0 +1,235 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 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 <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "poller.h"
+#include "devkit-disks-device.h"
+#include "devkit-disks-device-private.h"
+
+static gchar **poller_devices_to_poll = NULL;
+
+static guint poller_timeout_id = 0;
+
+void
+poller_poll_device (const gchar *device_file)
+{
+        gboolean is_cdrom;
+        int fd;
+
+        /* the device file is the canonical device file from udev */
+        is_cdrom = (g_str_has_prefix (device_file, "/dev/sr") || g_str_has_prefix (device_file, "/dev/scd"));
+
+        g_debug ("polling '%s'", device_file);
+
+        if (is_cdrom) {
+                /* optical drives need special care
+                 *
+                 *  - use O_NONBLOCK to avoid closing the door
+                 *  - use O_EXCL to avoid interferring with cd burning software / audio playback / etc
+                 */
+                fd = open (device_file, O_RDONLY | O_NONBLOCK | O_EXCL);
+                if (fd != -1) {
+                        close (fd);
+                }
+
+        } else {
+                fd = open (device_file, O_RDONLY);
+                if (fd != -1) {
+                        close (fd);
+                }
+        }
+}
+
+
+static gboolean
+poller_timeout_cb (gpointer user_data)
+{
+        guint n;
+
+        for (n = 0; poller_devices_to_poll != NULL && poller_devices_to_poll[n] != NULL; n++) {
+                const gchar *device_file = poller_devices_to_poll[n];
+
+                poller_poll_device (device_file);
+        }
+
+        /* don't remove the source */
+        return TRUE;
+}
+
+static gboolean
+poller_have_data (GIOChannel    *channel,
+                GIOCondition   condition,
+                gpointer       user_data)
+{
+        gchar *line;
+        gsize line_length;
+        GError *error;
+        gint status;
+
+        error = NULL;
+
+ again:
+        status = g_io_channel_read_line (channel,
+                                         &line,
+                                         &line_length,
+                                         NULL,
+                                         &error);
+        if (error != NULL) {
+                g_warning ("Error reading line from daemon: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+        if (status == G_IO_STATUS_AGAIN) {
+                goto again;
+        }
+
+        //g_debug ("polling process read '%s'", line);
+
+        if (line[line_length - 1] == '\n')
+                line[line_length - 1] = '\0';
+
+        if (line[line_length - 2] == ' ')
+                line[line_length - 2] = '\0';
+
+        g_strfreev (poller_devices_to_poll);
+        poller_devices_to_poll = g_strsplit (line, " ", 0);
+
+        if (g_strv_length (poller_devices_to_poll) == 0) {
+                if (poller_timeout_id > 0) {
+                        g_source_remove (poller_timeout_id);
+                        poller_timeout_id = 0;
+                }
+
+                g_print ("poller: not polling any devices\n");
+        } else {
+                g_print ("poller: polling %s", line);
+
+                if (poller_timeout_id == 0) {
+                        poller_timeout_id = g_timeout_add_seconds (2, poller_timeout_cb, NULL);
+                }
+        }
+
+        g_free (line);
+
+ out:
+        /* keep the IOChannel around */
+        return TRUE;
+}
+
+static void
+poller_run (gint fd)
+{
+        GMainLoop *loop;
+        GIOChannel *io_channel;
+
+        loop = g_main_loop_new (NULL, FALSE);
+
+        io_channel = g_io_channel_unix_new (fd);
+        g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL);
+        g_io_add_watch (io_channel, G_IO_IN, poller_have_data, NULL);
+
+        g_main_loop_run (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gint poller_daemon_write_end_fd;
+
+gboolean
+poller_setup (int argc, char *argv[])
+{
+        gint pipefds[2];
+        gboolean ret;
+
+        ret = FALSE;
+
+        if (pipe (pipefds) != 0) {
+                g_warning ("Couldn't set up polling process, pipe() failed: %m");
+                goto out;
+        }
+
+        switch (fork ()) {
+        case 0:
+                /* child */
+                close (pipefds[1]); /* close write end */
+                poller_run (pipefds[0]);
+                break;
+
+        default:
+                /* parent */
+                close (pipefds[0]); /* close read end */
+                poller_daemon_write_end_fd = pipefds[1];
+                break;
+
+        case -1:
+                g_warning ("Couldn't set up polling process, fork() failed: %m");
+                goto out;
+                break;
+        }
+
+        ret = TRUE;
+
+ out:
+        return ret;
+}
+
+void
+poller_set_devices (GList *devices)
+{
+        GList *l;
+        gchar **device_array;
+        guint n;
+        gchar *devices_to_poll;
+        static gchar *devices_currently_polled = NULL;
+
+        device_array = g_new0 (gchar *, g_list_length (devices) + 2);
+
+        for (l = devices, n = 0; l != NULL; l = l->next) {
+                DevkitDisksDevice *device = DEVKIT_DISKS_DEVICE (l->data);
+
+                device_array[n++] = device->priv->info.device_file;
+        }
+
+        g_qsort_with_data (device_array, n, sizeof (gchar *), (GCompareDataFunc) g_strcmp0, NULL);
+
+        device_array[n] = "\n";
+
+        devices_to_poll = g_strjoinv (" ", device_array);
+        g_free (device_array);
+
+        if (g_strcmp0 (devices_to_poll, devices_currently_polled) != 0) {
+                g_free (devices_currently_polled);
+                devices_currently_polled = devices_to_poll;
+
+                write (poller_daemon_write_end_fd, devices_currently_polled, strlen (devices_currently_polled));
+                //g_debug ("Wanna poll: '%s'", devices_currently_polled);
+        } else {
+                g_free (devices_to_poll);
+        }
+}
diff --git a/src/poller.h b/src/poller.h
new file mode 100644 (file)
index 0000000..c1ce073
--- /dev/null
@@ -0,0 +1,31 @@
+/* -*- 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
+ *
+ */
+
+#ifndef __POLLER_H__
+#define __POLLER_H__
+
+#include <devkit-disks-device.h>
+
+gboolean poller_setup (int argc, char *argv[]);
+void     poller_set_devices (GList *devices);
+void     poller_poll_device (const gchar *device_file);
+
+
+#endif /* __POLLER_H */
index ebb0cce..e5aa556 100644 (file)
@@ -10,6 +10,8 @@ __devkit_disks() {
 
     if [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--show-info" ] ; then
         COMPREPLY=($(compgen -W "$(devkit-disks --enumerate)" -- $cur))
+    elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--inhibit-polling" ] ; then
+        COMPREPLY=($(compgen -W "$(devkit-disks --enumerate)" -- $cur))
     elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--mount" ] ; then
         COMPREPLY=($(compgen -W "$(devkit-disks --enumerate)" -- $cur))
     elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--unmount" ] ; then
@@ -17,7 +19,7 @@ __devkit_disks() {
     elif [ "${COMP_WORDS[$(($COMP_CWORD - 1))]}" = "--create-fs" ] ; then
         COMPREPLY=($(compgen -W "$(devkit-disks --enumerate)" -- $cur))
     else
-        COMPREPLY=($(IFS=: compgen -S' ' -W "--inhibit:--enumerate:--monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-options:--unmount:--unmount-options:--create-fs:--create-fs-type:--create-fs-options" -- $cur))
+        COMPREPLY=($(IFS=: compgen -S' ' -W "--inhibit-polling:--inhibit-all-polling:--enumerate:--monitor:--monitor-detail:--show-info:--help:--mount:--mount-fstype:--mount-options:--unmount:--unmount-options:--create-fs:--create-fs-type:--create-fs-options" -- $cur))
     fi
 }
 
index ea3e2e3..356416a 100644 (file)
@@ -55,6 +55,8 @@ 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_inhibit_polling   = NULL;
+static gboolean      opt_inhibit_all_polling = FALSE;
 static char         *opt_mount             = NULL;
 static char         *opt_mount_fstype      = NULL;
 static char         *opt_mount_options     = NULL;
@@ -311,7 +313,7 @@ print_job (gboolean    job_in_progress,
 {
         if (job_in_progress) {
                 if (job_num_tasks > 0) {
-                        g_print ("  job underway:    %s: %d/%d tasks (%s",
+                        g_print ("  job underway:            %s: %d/%d tasks (%s",
                                  job_id,
                                  job_cur_task + 1,
                                  job_num_tasks,
@@ -323,14 +325,14 @@ print_job (gboolean    job_in_progress,
                         g_print (", initiated by uid %d", job_initiated_by_uid);
                         g_print (")\n");
                 } else {
-                        g_print ("  job underway:    %s: unknown progress", job_id);
+                        g_print ("  job underway:            %s: unknown progress", job_id);
                         if (job_is_cancellable)
                                 g_print (", cancellable");
                         g_print (", initiated by uid %d", job_initiated_by_uid);
                         g_print ("\n");
                 }
         } else {
-                g_print ("  job underway:    no\n");
+                g_print ("  job underway:            no\n");
         }
 }
 
@@ -405,6 +407,9 @@ typedef struct
         gboolean device_is_partition_table;
         gboolean device_is_removable;
         gboolean device_is_media_available;
+        gboolean device_is_media_change_detected;
+        gboolean device_is_media_change_detection_inhibitable;
+        gboolean device_is_media_change_detection_inhibited;
         gboolean device_is_read_only;
         gboolean device_is_drive;
         gboolean device_is_optical_disc;
@@ -531,6 +536,12 @@ collect_props (const char *key, const GValue *value, DeviceProperties *props)
                 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-media-change-detected") == 0)
+                props->device_is_media_change_detected = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-media-change-detection-inhibitable") == 0)
+                props->device_is_media_change_detection_inhibitable = g_value_get_boolean (value);
+        else if (strcmp (key, "device-is-media-change-detection-inhibited") == 0)
+                props->device_is_media_change_detection_inhibited = g_value_get_boolean (value);
         else if (strcmp (key, "device-is-read-only") == 0)
                 props->device_is_read_only = g_value_get_boolean (value);
         else if (strcmp (key, "device-is-drive") == 0)
@@ -877,22 +888,25 @@ do_show_info (const char *object_path)
                 return;
 
         g_print ("Showing information for %s\n", object_path);
-        g_print ("  native-path:     %s\n", props->native_path);
-        g_print ("  device-file:     %s\n", props->device_file);
+        g_print ("  native-path:             %s\n", props->native_path);
+        g_print ("  device-file:             %s\n", props->device_file);
         for (n = 0; props->device_file_by_id[n] != NULL; n++)
-                g_print ("    by-id:         %s\n", (char *) props->device_file_by_id[n]);
+                g_print ("    by-id:                 %s\n", (char *) props->device_file_by_id[n]);
         for (n = 0; props->device_file_by_path[n] != NULL; n++)
-                g_print ("    by-path:       %s\n", (char *) props->device_file_by_path[n]);
-        g_print ("  system internal: %d\n", props->device_is_system_internal);
-        g_print ("  removable:       %d\n", props->device_is_removable);
-        g_print ("  has media:       %d\n", props->device_is_media_available);
-        g_print ("  is read only:    %d\n", props->device_is_read_only);
-        g_print ("  is mounted:      %d\n", props->device_is_mounted);
-        g_print ("  is busy:         %d\n", props->device_is_busy);
-        g_print ("  mount path:      %s\n", props->device_mount_path);
-        g_print ("  mounted by uid:  %d\n", props->device_mounted_by_uid);
-        g_print ("  size:            %" G_GUINT64_FORMAT "\n", props->device_size);
-        g_print ("  block size:      %" G_GUINT64_FORMAT "\n", props->device_block_size);
+                g_print ("    by-path:               %s\n", (char *) props->device_file_by_path[n]);
+        g_print ("  system internal:         %d\n", props->device_is_system_internal);
+        g_print ("  removable:               %d\n", props->device_is_removable);
+        g_print ("  has media:               %d\n", props->device_is_media_available);
+        g_print ("    detects change:        %d\n", props->device_is_media_change_detected);
+        g_print ("    detection inhibitable: %d\n", props->device_is_media_change_detection_inhibitable);
+        g_print ("    detection inhibited:   %d\n", props->device_is_media_change_detection_inhibited);
+        g_print ("  is read only:            %d\n", props->device_is_read_only);
+        g_print ("  is mounted:              %d\n", props->device_is_mounted);
+        g_print ("  is busy:                 %d\n", props->device_is_busy);
+        g_print ("  mount path:              %s\n", props->device_mount_path);
+        g_print ("  mounted by uid:          %d\n", props->device_mounted_by_uid);
+        g_print ("  size:                    %" G_GUINT64_FORMAT "\n", props->device_size);
+        g_print ("  block size:              %" G_GUINT64_FORMAT "\n", props->device_block_size);
 
         print_job (props->job_in_progress,
                    props->job_id,
@@ -902,11 +916,11 @@ do_show_info (const char *object_path)
                    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);
-        g_print ("  uuid:            %s\n", props->id_uuid);
-        g_print ("  label:           %s\n", props->id_label);
+        g_print ("  usage:                   %s\n", props->id_usage);
+        g_print ("  type:                    %s\n", props->id_type);
+        g_print ("  version:                 %s\n", props->id_version);
+        g_print ("  uuid:                    %s\n", props->id_uuid);
+        g_print ("  label:                   %s\n", props->id_label);
         if (props->device_is_linux_md_component) {
                 struct tm *time_tm;
                 time_t time;
@@ -917,101 +931,101 @@ do_show_info (const char *object_path)
                 strftime (time_buf, sizeof time_buf, "%c", time_tm);
 
                 g_print ("  linux md component:\n");
-                g_print ("    RAID level:    %s\n", props->linux_md_component_level);
-                g_print ("    num comp:      %d\n", props->linux_md_component_num_raid_devices);
-                g_print ("    uuid:          %s\n", props->linux_md_component_uuid);
-                g_print ("    home host:     %s\n", props->linux_md_component_home_host);
-                g_print ("    name:          %s\n", props->linux_md_component_name);
-                g_print ("    version:       %s\n", props->linux_md_component_version);
-                g_print ("    update time: %" G_GUINT64_FORMAT " (%s)\n", props->linux_md_component_update_time, time_buf);
-                g_print ("    events:        %" G_GUINT64_FORMAT "\n", props->linux_md_component_events);
+                g_print ("    RAID level:            %s\n", props->linux_md_component_level);
+                g_print ("    num comp:              %d\n", props->linux_md_component_num_raid_devices);
+                g_print ("    uuid:                  %s\n", props->linux_md_component_uuid);
+                g_print ("    home host:             %s\n", props->linux_md_component_home_host);
+                g_print ("    name:                  %s\n", props->linux_md_component_name);
+                g_print ("    version:               %s\n", props->linux_md_component_version);
+                g_print ("    update time:         %" G_GUINT64_FORMAT " (%s)\n", props->linux_md_component_update_time, time_buf);
+                g_print ("    events:                %" G_GUINT64_FORMAT "\n", props->linux_md_component_events);
         }
         if (props->device_is_linux_md) {
                 g_print ("  linux md:\n");
-                g_print ("    RAID level:    %s\n", props->linux_md_level);
-                g_print ("    uuid:          %s\n", props->linux_md_uuid);
-                g_print ("    home host:     %s\n", props->linux_md_home_host);
-                g_print ("    name:          %s\n", props->linux_md_name);
-                g_print ("    num comp:      %d\n", props->linux_md_num_raid_devices);
-                g_print ("    version:       %s\n", props->linux_md_version);
-                g_print ("    degraded:      %d\n", props->linux_md_is_degraded);
-                g_print ("    sync action:   %s\n", props->linux_md_sync_action);
+                g_print ("    RAID level:            %s\n", props->linux_md_level);
+                g_print ("    uuid:                  %s\n", props->linux_md_uuid);
+                g_print ("    home host:             %s\n", props->linux_md_home_host);
+                g_print ("    name:                  %s\n", props->linux_md_name);
+                g_print ("    num comp:              %d\n", props->linux_md_num_raid_devices);
+                g_print ("    version:               %s\n", props->linux_md_version);
+                g_print ("    degraded:              %d\n", props->linux_md_is_degraded);
+                g_print ("    sync action:           %s\n", props->linux_md_sync_action);
                 if (strcmp (props->linux_md_sync_action, "idle") != 0) {
-                        g_print ("      complete:    %3.01f%%\n", props->linux_md_sync_percentage);
-                        g_print ("      speed:       %" G_GINT64_FORMAT " bytes/sec\n", props->linux_md_sync_speed);
+                        g_print ("      complete:            %3.01f%%\n", props->linux_md_sync_percentage);
+                        g_print ("      speed:               %" G_GINT64_FORMAT " bytes/sec\n", props->linux_md_sync_speed);
                 }
                 g_print ("    slaves:\n");
                 for (n = 0; props->linux_md_slaves[n] != NULL; n++)
-                        g_print ("          %s (state: %s)\n",
+                        g_print ("                  %s (state: %s)\n",
                                  props->linux_md_slaves[n], props->linux_md_slaves_state[n]);
         }
         if (props->device_is_luks) {
                 g_print ("  luks device:\n");
-                g_print ("    holder:        %s\n", props->luks_holder);
+                g_print ("    holder:                %s\n", props->luks_holder);
         }
         if (props->device_is_luks_cleartext) {
                 g_print ("  cleartext luks device:\n");
-                g_print ("    backed by:     %s\n", props->luks_cleartext_slave);
-                g_print ("    unlocked by:   uid %d\n", props->luks_cleartext_unlocked_by_uid);
+                g_print ("    backed by:             %s\n", props->luks_cleartext_slave);
+                g_print ("    unlocked by:           uid %d\n", props->luks_cleartext_unlocked_by_uid);
         }
         if (props->device_is_partition_table) {
                 g_print ("  partition table:\n");
-                g_print ("    scheme:        %s\n", props->partition_table_scheme);
-                g_print ("    count:         %d\n", props->partition_table_count);
-                g_print ("    max number:    %d\n", props->partition_table_max_number);
+                g_print ("    scheme:                %s\n", props->partition_table_scheme);
+                g_print ("    count:                 %d\n", props->partition_table_count);
+                g_print ("    max number:            %d\n", props->partition_table_max_number);
         }
         if (props->device_is_partition) {
                 g_print ("  partition:\n");
-                g_print ("    part of:       %s\n", props->partition_slave);
-                g_print ("    scheme:        %s\n", props->partition_scheme);
-                g_print ("    number:        %d\n", props->partition_number);
-                g_print ("    type:          %s\n", props->partition_type);
-                g_print ("    flags:        ");
+                g_print ("    part of:               %s\n", props->partition_slave);
+                g_print ("    scheme:                %s\n", props->partition_scheme);
+                g_print ("    number:                %d\n", props->partition_number);
+                g_print ("    type:                  %s\n", props->partition_type);
+                g_print ("    flags:                ");
                 for (n = 0; props->partition_flags[n] != NULL; n++)
                         g_print (" %s", (char *) props->partition_flags[n]);
                 g_print ("\n");
-                g_print ("    offset:        %" G_GINT64_FORMAT "\n", props->partition_offset);
-                g_print ("    size:          %" G_GINT64_FORMAT "\n", props->partition_size);
-                g_print ("    label:         %s\n", props->partition_label);
-                g_print ("    uuid:          %s\n", props->partition_uuid);
+                g_print ("    offset:                %" G_GINT64_FORMAT "\n", props->partition_offset);
+                g_print ("    size:                  %" G_GINT64_FORMAT "\n", props->partition_size);
+                g_print ("    label:                 %s\n", props->partition_label);
+                g_print ("    uuid:                  %s\n", props->partition_uuid);
         }
         if (props->device_is_optical_disc) {
                 g_print ("  optical disc:\n");
-                g_print ("    recordable:    %d\n", props->optical_disc_is_recordable);
-                g_print ("    rewritable:    %d\n", props->optical_disc_is_rewritable);
-                g_print ("    blank:         %d\n", props->optical_disc_is_blank);
-                g_print ("    appendable:    %d\n", props->optical_disc_is_appendable);
-                g_print ("    closed:        %d\n", props->optical_disc_is_closed);
-                g_print ("    has audio:     %d\n", props->optical_disc_has_audio);
-                g_print ("    num tracks:    %d\n", props->optical_disc_num_tracks);
-                g_print ("    num sessions:  %d\n", props->optical_disc_num_sessions);
+                g_print ("    recordable:            %d\n", props->optical_disc_is_recordable);
+                g_print ("    rewritable:            %d\n", props->optical_disc_is_rewritable);
+                g_print ("    blank:                 %d\n", props->optical_disc_is_blank);
+                g_print ("    appendable:            %d\n", props->optical_disc_is_appendable);
+                g_print ("    closed:                %d\n", props->optical_disc_is_closed);
+                g_print ("    has audio:             %d\n", props->optical_disc_has_audio);
+                g_print ("    num tracks:            %d\n", props->optical_disc_num_tracks);
+                g_print ("    num sessions:          %d\n", props->optical_disc_num_sessions);
         }
         if (props->device_is_drive) {
                 g_print ("  drive:\n");
-                g_print ("    vendor:        %s\n", props->drive_vendor);
-                g_print ("    model:         %s\n", props->drive_model);
-                g_print ("    revision:      %s\n", props->drive_revision);
-                g_print ("    serial:        %s\n", props->drive_serial);
-                g_print ("    ejectable:     %d\n", props->drive_is_media_ejectable);
-                g_print ("    require eject: %d\n", props->drive_requires_eject);
-                g_print ("    media:         %s\n", props->drive_media);
-                g_print ("      compat:     ");
+                g_print ("    vendor:                %s\n", props->drive_vendor);
+                g_print ("    model:                 %s\n", props->drive_model);
+                g_print ("    revision:              %s\n", props->drive_revision);
+                g_print ("    serial:                %s\n", props->drive_serial);
+                g_print ("    ejectable:             %d\n", props->drive_is_media_ejectable);
+                g_print ("    require eject:         %d\n", props->drive_requires_eject);
+                g_print ("    media:                 %s\n", props->drive_media);
+                g_print ("      compat:             ");
                 for (n = 0; props->drive_media_compatibility[n] != NULL; n++)
                         g_print (" %s", (char *) props->drive_media_compatibility[n]);
                 g_print ("\n");
                 if (props->drive_connection_interface == NULL || strlen (props->drive_connection_interface) == 0)
                         g_print ("    interface:     (unknown)\n");
                 else
-                        g_print ("    interface:     %s\n", props->drive_connection_interface);
+                        g_print ("    interface:             %s\n", props->drive_connection_interface);
                 if (props->drive_connection_speed == 0)
-                        g_print ("    if speed:      (unknown)\n");
+                        g_print ("    if speed:              (unknown)\n");
                 else
-                        g_print ("    if speed:      %" G_GINT64_FORMAT " bits/s\n", props->drive_connection_speed);
+                        g_print ("    if speed:              %" G_GINT64_FORMAT " bits/s\n", props->drive_connection_speed);
 
                 if (!props->drive_smart_is_capable) {
-                        g_print ("    S.M.A.R.T.:    not capable\n");
+                        g_print ("    S.M.A.R.T.:            not capable\n");
                 } else if (props->drive_smart_time_collected == 0) {
-                        g_print ("    S.M.A.R.T.:    not collected\n");
+                        g_print ("    S.M.A.R.T.:            not collected\n");
                 } else {
                         struct tm *time_tm;
                         time_t time;
@@ -1021,19 +1035,19 @@ do_show_info (const char *object_path)
                         time_tm = localtime (&time);
                         strftime (time_buf, sizeof time_buf, "%c", time_tm);
 
-                        g_print ("    S.M.A.R.T.:       Information collected at %s\n", time_buf);
+                        g_print ("    S.M.A.R.T.:            Information collected at %s\n", time_buf);
                         if (!props->drive_smart_is_capable) {
-                                g_print ("      not capable\n");
+                                g_print ("              not capable\n");
                         } else if (!props->drive_smart_is_enabled) {
-                                g_print ("      not enabled\n");
+                                g_print ("              not enabled\n");
                         } else {
 
-                                g_print ("      assessment:     %s\n",
+                                g_print ("      assessment:          %s\n",
                                          props->drive_smart_is_failing ? "FAILING" : "Passed");
-                                g_print ("      temperature:    %g° C / %g° F\n",
+                                g_print ("      temperature:         %g° C / %g° F\n",
                                          props->drive_smart_temperature,
                                          9 * props->drive_smart_temperature / 5 + 32);
-                                g_print ("      powered on:     %" G_GUINT64_FORMAT " hours\n", props->drive_smart_time_powered_on / 3600);
+                                g_print ("      powered on:          %" G_GUINT64_FORMAT " hours\n", props->drive_smart_time_powered_on / 3600);
                                 //g_print ("      196  Reallocated_Event_Count      0x0032 100   100           0 443023360\n",
 #if 0
                                 int m;
@@ -1054,6 +1068,81 @@ do_show_info (const char *object_path)
         device_properties_free (props);
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+do_inhibit_polling (const char *object_path)
+{
+        char *cookie;
+        DBusGProxy *proxy;
+        GError *error;
+        char **options;
+
+        options = NULL;
+
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           "org.freedesktop.DeviceKit.Disks",
+                                           object_path,
+                                           "org.freedesktop.DeviceKit.Disks.Device");
+
+        error = NULL;
+        if (!org_freedesktop_DeviceKit_Disks_Device_drive_inhibit_polling (proxy,
+                                                                           (const char **) options,
+                                                                           &cookie,
+                                                                           &error)) {
+                g_print ("Inhibit polling failed: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        g_print ("Inhibiting polling on %s. Press Ctrl+C to exit.\n", object_path);
+        while (TRUE)
+                sleep (100000000);
+
+        g_free (cookie);
+out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+
+static void
+do_inhibit_all_polling (void)
+{
+        char *cookie;
+        DBusGProxy *proxy;
+        GError *error;
+        char **options;
+
+        options = NULL;
+
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           "org.freedesktop.DeviceKit.Disks",
+                                           "/",
+                                           "org.freedesktop.DeviceKit.Disks");
+
+        error = NULL;
+        if (!org_freedesktop_DeviceKit_Disks_drive_inhibit_all_polling (proxy,
+                                                                        (const char **) options,
+                                                                        &cookie,
+                                                                        &error)) {
+                g_print ("Inhibit all polling failed: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        g_print ("Inhibiting polling on all devices. Press Ctrl+C to exit.\n");
+        while (TRUE)
+                sleep (100000000);
+
+        g_free (cookie);
+out:
+        ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 int
 main (int argc, char **argv)
 {
@@ -1066,6 +1155,8 @@ 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 },
+                { "inhibit-polling", 0, 0, G_OPTION_ARG_STRING, &opt_inhibit_polling, "Inhibit polling", NULL },
+                { "inhibit-all-polling", 0, 0, G_OPTION_ARG_NONE, &opt_inhibit_all_polling, "Inhibit all polling", 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 },
@@ -1150,6 +1241,10 @@ main (int argc, char **argv)
                         goto out;
         } else if (opt_show_info != NULL) {
                 do_show_info (opt_show_info);
+        } else if (opt_inhibit_polling != NULL) {
+                do_inhibit_polling (opt_inhibit_polling);
+        } else if (opt_inhibit_all_polling) {
+                do_inhibit_all_polling ();
         } else if (opt_mount != NULL) {
                 do_mount (opt_mount, opt_mount_fstype, opt_mount_options);
         } else if (opt_unmount != NULL) {