diff options
| author | William Jon McCann <mccann@jhu.edu> | 2008-01-22 17:03:46 -0500 | 
|---|---|---|
| committer | William Jon McCann <mccann@jhu.edu> | 2008-01-22 17:43:22 -0500 | 
| commit | 5ce97e6f22fd25279793fbc75211d2e86413ae73 (patch) | |
| tree | 5de0565516b1d90ed382dc5f0ed969c8e512706c /src | |
| parent | 2fba24e67597bf59ae00db2867df7a348c81b094 (diff) | |
initial stop/restart support
Add Stop and Restart methods to the Manager object.  We'll rename the
Manager object to System in the near future.  Use libpolkit to
determine authorization.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ck-manager.c | 667 | ||||
| -rw-r--r-- | src/ck-manager.h | 10 | ||||
| -rw-r--r-- | src/ck-manager.xml | 6 | 
3 files changed, 624 insertions, 59 deletions
diff --git a/src/ck-manager.c b/src/ck-manager.c index 5c77547..b6bcb2b 100644 --- a/src/ck-manager.c +++ b/src/ck-manager.c @@ -38,6 +38,8 @@  #include <dbus/dbus-glib.h>  #include <dbus/dbus-glib-lowlevel.h> +#include <polkit/polkit.h> +  #include "ck-manager.h"  #include "ck-manager-glue.h"  #include "ck-seat.h" @@ -58,6 +60,8 @@  struct CkManagerPrivate  { +        PolKitContext   *pol_ctx; +          GHashTable      *seats;          GHashTable      *sessions;          GHashTable      *leaders; @@ -634,6 +638,561 @@ log_seat_device_removed_event (CkManager   *manager,          g_free (device_id);  } +static char * +get_cookie_for_pid (CkManager *manager, +                    guint      pid) +{ +        char *cookie; + +        /* FIXME: need a better way to get the cookie */ + +        cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE"); + +        return cookie; +} + +static CkSession * +get_session_for_unix_process (CkManager *manager, +                              guint      pid) +{ +        CkSessionLeader *leader; +        CkSession       *session; +        char            *cookie; + +        session = NULL; +        leader = NULL; + +        cookie = get_cookie_for_pid (manager, pid); +        if (cookie == NULL) { +                goto out; +        } + +        leader = g_hash_table_lookup (manager->priv->leaders, cookie); +        if (leader == NULL) { +                goto out; +        } + +        session = g_hash_table_lookup (manager->priv->sessions, ck_session_leader_peek_session_id (leader)); + + out: +        g_free (cookie); + +        return session; +} + +static PolKitSession * +new_polkit_session_from_session (CkManager *manager, +                                 CkSession *ck_session) +{ +        PolKitSession *pk_session; +        PolKitSeat    *pk_seat; +        uid_t          uid; +        gboolean       is_active; +        gboolean       is_local; +        char          *sid; +        char          *ssid; +        char          *remote_host; + +        sid = NULL; +        ssid = NULL; +        remote_host = NULL; + +        ck_session_get_seat_id (ck_session, &sid, NULL); + +        g_object_get (ck_session, +                      "active", &is_active, +                      "is-local", &is_local, +                      "id", &ssid, +                      "unix-user", &uid, +                      "remote-host-name", &remote_host, +                      NULL); + +        pk_session = polkit_session_new (); +        if (pk_session == NULL) { +                goto out; +        } +        if (!polkit_session_set_uid (pk_session, uid)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!polkit_session_set_ck_objref (pk_session, ssid)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!polkit_session_set_ck_is_active (pk_session, is_active)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!polkit_session_set_ck_is_local (pk_session, is_local)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!is_local) { +                if (!polkit_session_set_ck_remote_host (pk_session, remote_host)) { +                        polkit_session_unref (pk_session); +                        pk_session = NULL; +                        goto out; +                } + +        } + + +        pk_seat = polkit_seat_new (); +        if (pk_seat == NULL) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!polkit_seat_set_ck_objref (pk_seat, sid)) { +                polkit_seat_unref (pk_seat); +                pk_seat = NULL; +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        if (!polkit_seat_validate (pk_seat)) { +                polkit_seat_unref (pk_seat); +                pk_seat = NULL; +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } + +        if (!polkit_session_set_seat (pk_session, pk_seat)) { +                polkit_seat_unref (pk_seat); +                pk_seat = NULL; +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } +        polkit_seat_unref (pk_seat); /* session object now owns this object */ +        pk_seat = NULL; + +        if (!polkit_session_validate (pk_session)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } + +out: +        g_free (ssid); +        g_free (sid); +        g_free (remote_host); + +        return pk_session; +} + +static PolKitCaller * +new_polkit_caller_from_dbus_name (CkManager  *manager, +                                  const char *dbus_name) +{ +        PolKitCaller *caller; +        pid_t pid; +        uid_t uid; +        char *selinux_context; +        PolKitSession *pk_session; +        DBusMessage *message; +        DBusMessage *reply; +        DBusMessageIter iter; +        DBusMessageIter sub_iter; +        char *str; +        int num_elems; +        DBusConnection *con; +        DBusError       error; +        CkSession      *ck_session; + +        dbus_error_init (&error); + +        con = dbus_g_connection_get_connection (manager->priv->connection); + +        g_return_val_if_fail (con != NULL, NULL); +        g_return_val_if_fail (dbus_name != NULL, NULL); + +        selinux_context = NULL; + +        caller = NULL; +        ck_session = NULL; +        pk_session = NULL; + +        uid = dbus_bus_get_unix_user (con, dbus_name, &error); +        if (dbus_error_is_set (&error)) { +                g_warning ("Could not get uid for connection: %s %s", +                           error.name, +                           error.message); +                dbus_error_free (&error); +                goto out; +        } + +        message = dbus_message_new_method_call ("org.freedesktop.DBus", +                                                "/org/freedesktop/DBus/Bus", +                                                "org.freedesktop.DBus", +                                                "GetConnectionUnixProcessID"); +        dbus_message_iter_init_append (message, &iter); +        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name); +        reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error); + +        if (reply == NULL || dbus_error_is_set (&error)) { +                g_warning ("Error doing GetConnectionUnixProcessID on Bus: %s: %s", +                           error.name, +                           error.message); +                dbus_message_unref (message); +                if (reply != NULL) { +                        dbus_message_unref (reply); +                } +                dbus_error_free (&error); +                goto out; +        } +        dbus_message_iter_init (reply, &iter); +        dbus_message_iter_get_basic (&iter, &pid); +        dbus_message_unref (message); +        dbus_message_unref (reply); + +        message = dbus_message_new_method_call ("org.freedesktop.DBus", +                                                "/org/freedesktop/DBus/Bus", +                                                "org.freedesktop.DBus", +                                                "GetConnectionSELinuxSecurityContext"); +        dbus_message_iter_init_append (message, &iter); +        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &dbus_name); +        reply = dbus_connection_send_with_reply_and_block (con, message, -1, &error); +        /* SELinux might not be enabled */ +        if (dbus_error_is_set (&error) && +            strcmp (error.name, "org.freedesktop.DBus.Error.SELinuxSecurityContextUnknown") == 0) { +                dbus_message_unref (message); +                if (reply != NULL) { +                        dbus_message_unref (reply); +                } +                dbus_error_init (&error); +        } else if (reply == NULL || dbus_error_is_set (&error)) { +                g_warning ("Error doing GetConnectionSELinuxSecurityContext on Bus: %s: %s", error.name, error.message); +                dbus_message_unref (message); +                if (reply != NULL) { +                        dbus_message_unref (reply); +                } +                goto out; +        } else { +                /* TODO: verify signature */ +                dbus_message_iter_init (reply, &iter); +                dbus_message_iter_recurse (&iter, &sub_iter); +                dbus_message_iter_get_fixed_array (&sub_iter, (void *) &str, &num_elems); +                if (str != NULL && num_elems > 0) { +                        selinux_context = g_strndup (str, num_elems); +                } +                dbus_message_unref (message); +                dbus_message_unref (reply); +        } + +        ck_session = get_session_for_unix_process (manager, pid); +        if (ck_session == NULL) { +                /* OK, this is not a catastrophe; just means the caller is not a +                 * member of any session or that ConsoleKit is not available.. +                 */ +                goto not_in_session; +        } + +        pk_session = new_polkit_session_from_session (manager, ck_session); +        if (pk_session == NULL) { +                g_warning ("Got a session but couldn't construct polkit session object!"); +                goto out; +        } +        if (!polkit_session_validate (pk_session)) { +                polkit_session_unref (pk_session); +                pk_session = NULL; +                goto out; +        } + +not_in_session: + +        caller = polkit_caller_new (); +        if (caller == NULL) { +                if (pk_session != NULL) { +                        polkit_session_unref (pk_session); +                        pk_session = NULL; +                } +                goto out; +        } + +        if (!polkit_caller_set_dbus_name (caller, dbus_name)) { +                if (pk_session != NULL) { +                        polkit_session_unref (pk_session); +                        pk_session = NULL; +                } +                polkit_caller_unref (caller); +                caller = NULL; +                goto out; +        } +        if (!polkit_caller_set_uid (caller, uid)) { +                if (pk_session != NULL) { +                        polkit_session_unref (pk_session); +                        pk_session = NULL; +                } +                polkit_caller_unref (caller); +                caller = NULL; +                goto out; +        } +        if (!polkit_caller_set_pid (caller, pid)) { +                if (pk_session != NULL) { +                        polkit_session_unref (pk_session); +                        pk_session = NULL; +                } +                polkit_caller_unref (caller); +                caller = NULL; +                goto out; +        } +        if (selinux_context != NULL) { +                if (!polkit_caller_set_selinux_context (caller, selinux_context)) { +                        if (pk_session != NULL) { +                                polkit_session_unref (pk_session); +                                pk_session = NULL; +                        } +                        polkit_caller_unref (caller); +                        caller = NULL; +                        goto out; +                } +        } +        if (pk_session != NULL) { +                if (!polkit_caller_set_ck_session (caller, pk_session)) { +                        if (pk_session != NULL) { +                                polkit_session_unref (pk_session); +                                pk_session = NULL; +                        } +                        polkit_caller_unref (caller); +                        caller = NULL; +                        goto out; +                } +                polkit_session_unref (pk_session); /* caller object now own this object */ +                pk_session = NULL; +        } + +        if (!polkit_caller_validate (caller)) { +                polkit_caller_unref (caller); +                caller = NULL; +                goto out; +        } + +out: +        g_free (selinux_context); + +        return caller; +} + +static gboolean +_check_polkit_for_action (CkManager             *manager, +                          DBusGMethodInvocation *context, +                          const char            *action) +{ +        const char   *sender; +        GError       *error; +        DBusError     dbus_error; +        PolKitCaller *pk_caller; +        PolKitAction *pk_action; +        PolKitResult  pk_result; + +        error = NULL; + +        g_debug ("constructing polkit data"); + +        /* Check that caller is privileged */ +        sender = dbus_g_method_get_sender (context); +        dbus_error_init (&dbus_error); + +        pk_caller = new_polkit_caller_from_dbus_name (manager, sender); +        if (pk_caller == NULL) { +                error = g_error_new (CK_MANAGER_ERROR, +                                     CK_MANAGER_ERROR_GENERAL, +                                     "Error getting information about caller: %s: %s", +                                     dbus_error.name, +                                     dbus_error.message); +                dbus_error_free (&dbus_error); +                dbus_g_method_return_error (context, error); +                g_error_free (error); +                return FALSE; +        } + +        pk_action = polkit_action_new (); +        polkit_action_set_action_id (pk_action, action); + +        g_debug ("checking if caller %s is authorized", sender); + +        /* this version crashes if error is used */ +        pk_result = polkit_context_is_caller_authorized (manager->priv->pol_ctx, +                                                         pk_action, +                                                         pk_caller, +                                                         TRUE, +                                                         NULL); +        g_debug ("answer is: %s", (pk_result == POLKIT_RESULT_YES) ? "yes" : "no"); + +        polkit_caller_unref (pk_caller); +        polkit_action_unref (pk_action); + +        if (pk_result != POLKIT_RESULT_YES) { +                error = g_error_new (CK_MANAGER_ERROR, +                                     CK_MANAGER_ERROR_NOT_PRIVILEGED, +                                     "Not privileged for action: %s", +                                     action); +                dbus_error_free (&dbus_error); +                dbus_g_method_return_error (context, error); +                g_error_free (error); +                return FALSE; +        } + +        return TRUE; +} + +/* adapted from PolicyKit */ +static gboolean +get_caller_info (CkManager   *manager, +                 const char  *sender, +                 uid_t       *calling_uid, +                 pid_t       *calling_pid) +{ +        gboolean res; +        GError  *error = NULL; + +        res = FALSE; + +        if (sender == NULL) { +                goto out; +        } + +        if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixUser", &error, +                                 G_TYPE_STRING, sender, +                                 G_TYPE_INVALID, +                                 G_TYPE_UINT, calling_uid, +                                 G_TYPE_INVALID)) { +                g_debug ("GetConnectionUnixUser() failed: %s", error->message); +                g_error_free (error); +                goto out; +        } + +        if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixProcessID", &error, +                                 G_TYPE_STRING, sender, +                                 G_TYPE_INVALID, +                                 G_TYPE_UINT, calling_pid, +                                 G_TYPE_INVALID)) { +                g_debug ("GetConnectionUnixProcessID() failed: %s", error->message); +                g_error_free (error); +                goto out; +        } + +        res = TRUE; + +        g_debug ("uid = %d", *calling_uid); +        g_debug ("pid = %d", *calling_pid); + +out: +        return res; +} + +/* +  Example: +  dbus-send --system --dest=org.freedesktop.ConsoleKit \ +  --type=method_call --print-reply --reply-timeout=2000 \ +  /org/freedesktop/ConsoleKit/Manager \ +  org.freedesktop.ConsoleKit.Manager.Restart +*/ +gboolean +ck_manager_restart (CkManager             *manager, +                    DBusGMethodInvocation *context) +{ +        gboolean    ret; +        gboolean    res; +        const char *action; +        GError     *error; + +        ret = FALSE; + +        if (g_hash_table_size (manager->priv->sessions) > 1) { +                action = "org.freedesktop.consolekit.system.restart-multiple-users"; +        } else { +                action = "org.freedesktop.consolekit.system.restart"; +        } + +        g_debug ("ConsoleKit Restart: %s", action); + +        res = _check_polkit_for_action (manager, context, action); + +        if (! res) { +                goto out; +        } + +        g_debug ("ConsoleKit preforming Restart: %s", action); + +        error = NULL; +        res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-restart", +                                          &error); +        if (! res) { +                GError *new_error; + +                g_warning ("Unable to restart system: %s", error->message); + +                new_error = g_error_new (CK_MANAGER_ERROR, +                                         CK_MANAGER_ERROR_GENERAL, +                                         "Unable to restart system: %s", error->message); +                dbus_g_method_return_error (context, new_error); +                g_error_free (new_error); + +                g_error_free (error); +        } else { +                ret = TRUE; +                dbus_g_method_return (context); +        } + + out: + +        return ret; +} + +gboolean +ck_manager_stop (CkManager             *manager, +                 DBusGMethodInvocation *context) +{ +        gboolean    ret; +        gboolean    res; +        const char *action; +        GError     *error; + +        ret = TRUE; + +        if (g_hash_table_size (manager->priv->sessions) > 1) { +                action = "org.freedesktop.consolekit.system.stop-multiple-users"; +        } else { +                action = "org.freedesktop.consolekit.system.stop"; +        } + +        res = _check_polkit_for_action (manager, context, action); +        if (! res) { +                goto out; +        } + +        g_debug ("Stopping system"); +        error = NULL; +        res = g_spawn_command_line_async (LIBDIR "/ConsoleKit/scripts/ck-system-stop", +                                          &error); +        if (! res) { +                GError *new_error; + +                g_warning ("Unable to stop system: %s", error->message); + +                new_error = g_error_new (CK_MANAGER_ERROR, +                                         CK_MANAGER_ERROR_GENERAL, +                                         "Unable to stop system: %s", error->message); +                dbus_g_method_return_error (context, new_error); +                g_error_free (new_error); + +                g_error_free (error); +        } else { +                ret = TRUE; +                dbus_g_method_return (context); +        } + + out: +        return ret; +} +  static void  on_seat_active_session_changed (CkSeat     *seat,                                  const char *ssid, @@ -844,51 +1403,6 @@ find_seat_for_session (CkManager *manager,          return seat;  } -/* adapted from PolicyKit */ -static gboolean -get_caller_info (CkManager   *manager, -                 const char  *sender, -                 uid_t       *calling_uid, -                 pid_t       *calling_pid) -{ -        gboolean res; -        GError  *error = NULL; - -        res = FALSE; - -        if (sender == NULL) { -                goto out; -        } - -        if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixUser", &error, -                                 G_TYPE_STRING, sender, -                                 G_TYPE_INVALID, -                                 G_TYPE_UINT, calling_uid, -                                 G_TYPE_INVALID)) { -                g_debug ("GetConnectionUnixUser() failed: %s", error->message); -                g_error_free (error); -                goto out; -        } - -        if (! dbus_g_proxy_call (manager->priv->bus_proxy, "GetConnectionUnixProcessID", &error, -                                 G_TYPE_STRING, sender, -                                 G_TYPE_INVALID, -                                 G_TYPE_UINT, calling_pid, -                                 G_TYPE_INVALID)) { -                g_debug ("GetConnectionUnixProcessID() failed: %s", error->message); -                g_error_free (error); -                goto out; -        } - -        res = TRUE; - -        g_debug ("uid = %d", *calling_uid); -        g_debug ("pid = %d", *calling_pid); - -out: -        return res; -} -  static gboolean  manager_set_system_idle_hint (CkManager *manager,                                gboolean   idle_hint) @@ -1278,19 +1792,6 @@ ck_manager_get_session_for_cookie (CkManager             *manager,          return TRUE;  } -static char * -get_cookie_for_pid (CkManager *manager, -                    guint      pid) -{ -        char *cookie; - -        /* FIXME: need a better way to get the cookie */ - -        cookie = ck_unix_pid_get_env (pid, "XDG_SESSION_COOKIE"); - -        return cookie; -} -  /*    Example:    dbus-send --system --dest=org.freedesktop.ConsoleKit \ @@ -1682,10 +2183,60 @@ bus_name_owner_changed (DBusGProxy  *bus_proxy,  }  static gboolean +pk_io_watch_have_data (GIOChannel  *channel, +                       GIOCondition condition, +                       gpointer     user_data) +{ +        int            fd; +        PolKitContext *pk_context = user_data; + +        fd = g_io_channel_unix_get_fd (channel); +        polkit_context_io_func (pk_context, fd); +        return TRUE; +} + +static int +pk_io_add_watch (PolKitContext *pk_context, +                 int            fd) +{ +        guint       id = 0; +        GIOChannel *channel; + +        channel = g_io_channel_unix_new (fd); +        if (channel == NULL) { +                goto out; +        } + +        id = g_io_add_watch (channel, G_IO_IN, pk_io_watch_have_data, pk_context); +        if (id == 0) { +                g_io_channel_unref (channel); +                goto out; +        } +        g_io_channel_unref (channel); + +out: +        return id; +} + +static void +pk_io_remove_watch (PolKitContext *pk_context, +                    int            watch_id) +{ +        g_source_remove (watch_id); +} + +static gboolean  register_manager (CkManager *manager)  {          GError *error = NULL; +        manager->priv->pol_ctx = polkit_context_new (); +        polkit_context_set_io_watch_functions (manager->priv->pol_ctx, pk_io_add_watch, pk_io_remove_watch); +        if (! polkit_context_init (manager->priv->pol_ctx, NULL)) { +                g_critical ("cannot initialize libpolkit"); +                return FALSE; +        } +          error = NULL;          manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);          if (manager->priv->connection == NULL) { diff --git a/src/ck-manager.h b/src/ck-manager.h index a6fd670..652435e 100644 --- a/src/ck-manager.h +++ b/src/ck-manager.h @@ -58,7 +58,8 @@ typedef struct  typedef enum  { -         CK_MANAGER_ERROR_GENERAL +        CK_MANAGER_ERROR_GENERAL, +        CK_MANAGER_ERROR_NOT_PRIVILEGED  } CkManagerError;  #define CK_MANAGER_ERROR ck_manager_error_quark () @@ -70,6 +71,13 @@ CkManager         * ck_manager_new                            (void);  /* unprivileged methods */ + +/* System actions */ +gboolean            ck_manager_stop                           (CkManager             *manager, +                                                               DBusGMethodInvocation *context); +gboolean            ck_manager_restart                        (CkManager             *manager, +                                                               DBusGMethodInvocation *context); +  /* Authoritative properties */  gboolean            ck_manager_open_session                   (CkManager             *manager,                                                                 DBusGMethodInvocation *context); diff --git a/src/ck-manager.xml b/src/ck-manager.xml index f40d6d5..031d8a1 100644 --- a/src/ck-manager.xml +++ b/src/ck-manager.xml @@ -1,6 +1,12 @@  <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">  <node name="/org/freedesktop/ConsoleKit/Manager">    <interface name="org.freedesktop.ConsoleKit.Manager"> +    <method name="Restart"> +      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> +    </method> +    <method name="Stop"> +      <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> +    </method>      <method name="OpenSession">        <annotation name="org.freedesktop.DBus.GLib.Async" value=""/>        <arg name="cookie" direction="out" type="s"/>  | 
