make it possible to inhibit all operations on the daemon
authorDavid Zeuthen <davidz@redhat.com>
Mon, 2 Mar 2009 19:39:30 +0000 (14:39 -0500)
committerDavid Zeuthen <davidz@redhat.com>
Mon, 2 Mar 2009 19:39:30 +0000 (14:39 -0500)
This is needed for things like OS installers to inhibit automounting
etc.

doc/man/devkit-disks.xml
src/devkit-disks-daemon.c
src/devkit-disks-daemon.h
src/org.freedesktop.DeviceKit.Disks.Device.xml
src/org.freedesktop.DeviceKit.Disks.xml
tools/devkit-disks.c

index 1a9c196..422ae94 100644 (file)
       </varlistentry>
 
       <varlistentry>
+        <term>
+          <option>--inhibit</option>
+          <arg><option>-- program arg ...</option></arg>
+        </term>
+        <listitem>
+          <para>
+             Inhibits clients from invoking methods on the daemon of
+             the daemon that require authorization (all methods will
+             return the
+             <literal>org.freedesktop.DeviceKit.Disks.Error.Inhibited</literal> error) if
+             the caller is not the super user. This is typically used
+             by OS installers and other programs that expects full
+             control of the system, specifically to avoid automounting
+             devices. Only the super user can do this.
+          </para>
+          <para>
+            If no program is given,
+            polling is inhibited until Ctrl+C is pressed. Otherwise the program is spawned and the polling
+            is only inhibited until the program terminates.
+          </para>
+        </listitem>
+      </varlistentry>
+
+      <varlistentry>
         <term><option>--help</option></term>
         <listitem>
           <para>
index 80abee0..944a231 100644 (file)
@@ -69,6 +69,7 @@ enum
 {
         PROP_0,
         PROP_DAEMON_VERSION,
+        PROP_DAEMON_IS_INHIBITED,
         PROP_SUPPORTS_LUKS_DEVICES,
         PROP_KNOWN_FILESYSTEMS,
 };
@@ -106,6 +107,8 @@ struct DevkitDisksDaemonPrivate
         DevkitDisksLogger       *logger;
 
         GList *polling_inhibitors;
+
+        GList *inhibitors;
 };
 
 static void     devkit_disks_daemon_class_init  (DevkitDisksDaemonClass *klass);
@@ -115,6 +118,9 @@ static void     devkit_disks_daemon_finalize    (GObject     *object);
 static void     daemon_polling_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
                                                           DevkitDisksDaemon    *daemon);
 
+static void     daemon_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_DISKS_TYPE_DAEMON, DevkitDisksDaemonPrivate))
@@ -146,6 +152,7 @@ devkit_disks_error_get_type (void)
                 static const GEnumValue values[] =
                         {
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_FAILED, "Failed"),
+                                ENUM_ENTRY (DEVKIT_DISKS_ERROR_INHIBITED, "Inhibited"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_BUSY, "Busy"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_CANCELLED, "Cancelled"),
                                 ENUM_ENTRY (DEVKIT_DISKS_ERROR_INVALID_OPTION, "InvalidOption"),
@@ -382,6 +389,10 @@ get_property (GObject         *object,
                 g_value_set_string (value, VERSION);
                 break;
 
+        case PROP_DAEMON_IS_INHIBITED:
+                g_value_set_boolean (value, (daemon->priv->inhibitors != NULL));
+                break;
+
         case PROP_SUPPORTS_LUKS_DEVICES:
                 /* TODO: probably Linux only */
                 g_value_set_boolean (value, TRUE);
@@ -473,6 +484,11 @@ devkit_disks_daemon_class_init (DevkitDisksDaemonClass *klass)
 
         g_object_class_install_property (
                 object_class,
+                PROP_DAEMON_IS_INHIBITED,
+                g_param_spec_boolean ("daemon-is-inhibited", NULL, NULL, FALSE, G_PARAM_READABLE));
+
+        g_object_class_install_property (
+                object_class,
                 PROP_SUPPORTS_LUKS_DEVICES,
                 g_param_spec_boolean ("supports-luks-devices", NULL, NULL, FALSE, G_PARAM_READABLE));
 
@@ -564,6 +580,13 @@ devkit_disks_daemon_finalize (GObject *object)
         }
         g_list_free (daemon->priv->polling_inhibitors);
 
+        for (l = daemon->priv->inhibitors; l != NULL; l = l->next) {
+                DevkitDisksInhibitor *inhibitor = DEVKIT_DISKS_INHIBITOR (l->data);
+                g_signal_handlers_disconnect_by_func (inhibitor, daemon_inhibitor_disconnected_cb, daemon);
+                g_object_unref (inhibitor);
+        }
+        g_list_free (daemon->priv->inhibitors);
+
         G_OBJECT_CLASS (devkit_disks_daemon_parent_class)->finalize (object);
 }
 
@@ -1139,6 +1162,30 @@ devkit_disks_damon_local_get_caller_for_context (DevkitDisksDaemon *daemon,
         return pk_caller;
 }
 
+/*--------------------------------------------------------------------------------------------------------------*/
+
+static gboolean
+throw_error (DBusGMethodInvocation *context, int error_code, const char *format, ...)
+{
+        GError *error;
+        va_list args;
+        char *message;
+
+        va_start (args, format);
+        message = g_strdup_vprintf (format, args);
+        va_end (args);
+
+        error = g_error_new (DEVKIT_DISKS_ERROR,
+                             error_code,
+                             "%s", message);
+        dbus_g_method_return_error (context, error);
+        g_error_free (error);
+        g_free (message);
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
+
 gboolean
 devkit_disks_damon_local_check_auth (DevkitDisksDaemon     *daemon,
                                      PolKitCaller          *pk_caller,
@@ -1153,6 +1200,18 @@ devkit_disks_damon_local_check_auth (DevkitDisksDaemon     *daemon,
 
         ret = FALSE;
 
+        if (daemon->priv->inhibitors != NULL) {
+                uid_t uid;
+
+                uid = (uid_t) -1;
+                if (!polkit_caller_get_uid (pk_caller, &uid) || uid != 0) {
+                        throw_error (context,
+                                     DEVKIT_DISKS_ERROR_INHIBITED,
+                                     "Daemon is being inhibited");
+                }
+                goto out;
+        }
+
         pk_action = polkit_action_new ();
         polkit_action_set_action_id (pk_action, action_id);
         pk_result = polkit_context_is_caller_authorized (daemon->priv->pk_context,
@@ -1173,6 +1232,8 @@ devkit_disks_damon_local_check_auth (DevkitDisksDaemon     *daemon,
                 dbus_error_free (&d_error);
         }
         polkit_action_unref (pk_action);
+
+ out:
         return ret;
 }
 
@@ -1199,28 +1260,6 @@ devkit_disks_daemon_local_update_poller (DevkitDisksDaemon *daemon)
 }
 
 /*--------------------------------------------------------------------------------------------------------------*/
-
-static gboolean
-throw_error (DBusGMethodInvocation *context, int error_code, const char *format, ...)
-{
-        GError *error;
-        va_list args;
-        char *message;
-
-        va_start (args, format);
-        message = g_strdup_vprintf (format, args);
-        va_end (args);
-
-        error = g_error_new (DEVKIT_DISKS_ERROR,
-                             error_code,
-                             "%s", message);
-        dbus_g_method_return_error (context, error);
-        g_error_free (error);
-        g_free (message);
-        return TRUE;
-}
-
-/*--------------------------------------------------------------------------------------------------------------*/
 /* exported methods */
 
 static void
@@ -1387,3 +1426,94 @@ devkit_disks_daemon_drive_uninhibit_all_polling (DevkitDisksDaemon     *daemon,
 }
 
 /*--------------------------------------------------------------------------------------------------------------*/
+
+static void
+daemon_inhibitor_disconnected_cb (DevkitDisksInhibitor *inhibitor,
+                                  DevkitDisksDaemon    *daemon)
+{
+        daemon->priv->inhibitors = g_list_remove (daemon->priv->inhibitors, inhibitor);
+        g_signal_handlers_disconnect_by_func (inhibitor, daemon_inhibitor_disconnected_cb, daemon);
+        g_object_unref (inhibitor);
+}
+
+gboolean
+devkit_disks_daemon_local_has_inhibitors (DevkitDisksDaemon *daemon)
+{
+        return daemon->priv->inhibitors != NULL;
+}
+
+gboolean
+devkit_disks_daemon_inhibit (DevkitDisksDaemon     *daemon,
+                             DBusGMethodInvocation *context)
+{
+        DevkitDisksInhibitor *inhibitor;
+        PolKitCaller *pk_caller;
+        uid_t uid;
+
+
+        if ((pk_caller = devkit_disks_damon_local_get_caller_for_context (daemon, context)) == NULL)
+                goto out;
+
+        uid = (uid_t) -1;
+        if (!polkit_caller_get_uid (pk_caller, &uid) || uid != 0) {
+                throw_error (context,
+                             DEVKIT_DISKS_ERROR_FAILED,
+                             "Only uid 0 is authorized to inhibit the daemon");
+                goto out;
+        }
+
+        inhibitor = devkit_disks_inhibitor_new (context);
+
+        daemon->priv->inhibitors = g_list_prepend (daemon->priv->inhibitors, inhibitor);
+        g_signal_connect (inhibitor, "disconnected", G_CALLBACK (daemon_inhibitor_disconnected_cb), 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_uninhibit (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->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->inhibitors = g_list_remove (daemon->priv->inhibitors, inhibitor);
+        g_object_unref (inhibitor);
+
+        dbus_g_method_return (context);
+
+ out:
+        return TRUE;
+}
+
+/*--------------------------------------------------------------------------------------------------------------*/
index 735a0ec..a31cc28 100644 (file)
@@ -53,6 +53,7 @@ struct DevkitDisksDaemonClass
 typedef enum
 {
         DEVKIT_DISKS_ERROR_FAILED,
+        DEVKIT_DISKS_ERROR_INHIBITED,
         DEVKIT_DISKS_ERROR_BUSY,
         DEVKIT_DISKS_ERROR_CANCELLED,
         DEVKIT_DISKS_ERROR_INVALID_OPTION,
@@ -115,6 +116,8 @@ void               devkit_disks_daemon_local_update_poller       (DevkitDisksDae
 
 gboolean           devkit_disks_daemon_local_has_polling_inhibitors (DevkitDisksDaemon       *daemon);
 
+gboolean           devkit_disks_daemon_local_has_inhibitors (DevkitDisksDaemon       *daemon);
+
 DevkitDisksLogger *devkit_disks_daemon_local_get_logger (DevkitDisksDaemon *daemon);
 
 DevkitDisksMountMonitor *devkit_disks_daemon_local_get_mount_monitor (DevkitDisksDaemon *daemon);
@@ -161,6 +164,13 @@ gboolean devkit_disks_daemon_drive_uninhibit_all_polling (DevkitDisksDaemon
                                                           char                  *cookie,
                                                           DBusGMethodInvocation *context);
 
+gboolean devkit_disks_daemon_inhibit (DevkitDisksDaemon     *daemon,
+                                      DBusGMethodInvocation *context);
+
+gboolean devkit_disks_daemon_uninhibit (DevkitDisksDaemon     *daemon,
+                                        char                  *cookie,
+                                        DBusGMethodInvocation *context);
+
 G_END_DECLS
 
 #endif /* __DEVKIT_DISKS_DAEMON_H__ */
index 898acd8..fa1e9de 100644 (file)
@@ -2,6 +2,7 @@
 "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" [
   <!ENTITY ERROR_FAILED "org.freedesktop.DeviceKit.Disks.Error.Failed">
+  <!ENTITY ERROR_INHIBITED "org.freedesktop.DeviceKit.Disks.Error.Inhibited">
   <!ENTITY ERROR_BUSY "org.freedesktop.DeviceKit.Disks.Error.Busy">
   <!ENTITY ERROR_CANCELLED "org.freedesktop.DeviceKit.Disks.Error.Cancelled">
   <!ENTITY ERROR_INVALID_OPTION "org.freedesktop.DeviceKit.Disks.Error.InvalidOption">
index c18c101..e5d3822 100644 (file)
@@ -2,6 +2,7 @@
 "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" [
   <!ENTITY ERROR_FAILED "org.freedesktop.DeviceKit.Disks.Error.Failed">
+  <!ENTITY ERROR_INHIBITED "org.freedesktop.DeviceKit.Disks.Error.Inhibited">
   <!ENTITY ERROR_BUSY "org.freedesktop.DeviceKit.Disks.Error.Busy">
   <!ENTITY ERROR_CANCELLED "org.freedesktop.DeviceKit.Disks.Error.Cancelled">
   <!ENTITY ERROR_INVALID_OPTION "org.freedesktop.DeviceKit.Disks.Error.InvalidOption">
 
     <!-- ************************************************************ -->
 
+    <method name="Inhibit">
+      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>
+      <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.Uninhibit">Uninhibit()</doc:ref> method.
+            to stop inhibiting the daemon.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Inhibits clients from invoking methods on the daemon
+            of the daemon that require authorization (all methods
+            will return the <doc:tt>&ERROR_INHIBITED;</doc:tt> error)
+            if the caller is not the super user.
+            This is typically used by OS installers and other
+            programs that expects full control of the system, specifically
+            to avoid automounting devices.
+          </doc:para>
+        </doc:description>
+        <doc:permission>
+          Only the super user can invoke this method.
+        </doc:permission>
+        <doc:errors>
+          <doc:error name="&ERROR_NOT_AUTHORIZED;">if the caller is not the super user</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+    <!-- ************************************************************ -->
+
+    <method name="Uninhibit">
+      <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.Inhibit">Inhibit()</doc:ref> method.
+        </doc:summary></doc:doc>
+      </arg>
+
+      <doc:doc>
+        <doc:description>
+          <doc:para>
+            Uninhibits other clients from using the daemon.
+          </doc:para>
+        </doc:description>
+        <doc:errors>
+          <doc:error name="&ERROR_FAILED;">if the given cookie is malformed</doc:error>
+        </doc:errors>
+      </doc:doc>
+    </method>
+
+
+    <!-- ************************************************************ -->
+
     <signal name="DeviceAdded">
       <arg name="device" type="o">
         <doc:doc><doc:summary>Object path of device that was added.</doc:summary></doc:doc>
       </doc:para></doc:description></doc:doc>
     </property>
 
+    <property name="daemon-is-inhibited" type="b" access="read">
+      <doc:doc><doc:description><doc:para>
+            TRUE only if the daemon is inhibited.
+      </doc:para></doc:description></doc:doc>
+    </property>
+
     <property name="supports-luks-devices" type="b" access="read">
       <doc:doc><doc:description><doc:para>
             TRUE only if the daemon can create encrypted LUKS block devices, see the
index f283571..5ad1d78 100644 (file)
@@ -58,6 +58,7 @@ 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           = FALSE;
 static gboolean      opt_inhibit_all_polling = FALSE;
 static char         *opt_mount             = NULL;
 static char         *opt_mount_fstype      = NULL;
@@ -1107,7 +1108,6 @@ out:
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-
 static gint
 do_inhibit_all_polling (gint         argc,
                         gchar       *argv[])
@@ -1175,6 +1175,70 @@ out:
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+static gint
+do_inhibit (gint         argc,
+            gchar       *argv[])
+{
+        char *cookie;
+        DBusGProxy *proxy;
+        GError *error;
+        gint ret;
+
+        cookie = NULL;
+        ret = 127;
+
+       proxy = dbus_g_proxy_new_for_name (bus,
+                                           "org.freedesktop.DeviceKit.Disks",
+                                           "/",
+                                           "org.freedesktop.DeviceKit.Disks");
+
+        error = NULL;
+        if (!org_freedesktop_DeviceKit_Disks_inhibit (proxy,
+                                                      &cookie,
+                                                      &error)) {
+                g_print ("Inhibit all polling failed: %s\n", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        if (argc == 0) {
+                g_print ("Inhibiting the daemon. Press Ctrl+C to exit.\n");
+                while (TRUE)
+                        sleep (100000000);
+        } else {
+                GError *error;
+                gint exit_status;
+
+                error = NULL;
+                if (!g_spawn_sync (NULL,  /* working dir */
+                                   argv,
+                                   NULL,  /* envp */
+                                   G_SPAWN_SEARCH_PATH,
+                                   NULL, /* child_setup */
+                                   NULL, /* user_data */
+                                   NULL, /* standard_output */
+                                   NULL, /* standard_error */
+                                   &exit_status, /* exit_status */
+                                   &error)) {
+                        g_printerr ("Error launching program: %s\n", error->message);
+                        g_error_free (error);
+                        ret = 126;
+                        goto out;
+                }
+
+                if (WIFEXITED (exit_status))
+                        ret = WEXITSTATUS (exit_status);
+                else
+                        ret = 125;
+        }
+
+out:
+        g_free (cookie);
+        return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 int
 main (int argc, char **argv)
 {
@@ -1189,6 +1253,7 @@ main (int argc, char **argv)
                 { "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 },
+                { "inhibit", 0, 0, G_OPTION_ARG_NONE, &opt_inhibit, "Inhibit the daemon", 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 },
@@ -1278,6 +1343,9 @@ main (int argc, char **argv)
         } else if (opt_inhibit_all_polling) {
                 ret = do_inhibit_all_polling (argc - 1, argv + 1);
                 goto out;
+        } else if (opt_inhibit) {
+                ret = do_inhibit (argc - 1, argv + 1);
+                goto out;
         } else if (opt_mount != NULL) {
                 do_mount (opt_mount, opt_mount_fstype, opt_mount_options);
         } else if (opt_unmount != NULL) {