summaryrefslogtreecommitdiffstats
path: root/src/ck-manager.c
diff options
context:
space:
mode:
authorWilliam Jon McCann <mccann@jhu.edu>2008-01-22 17:03:46 -0500
committerWilliam Jon McCann <mccann@jhu.edu>2008-01-22 17:43:22 -0500
commit5ce97e6f22fd25279793fbc75211d2e86413ae73 (patch)
tree5de0565516b1d90ed382dc5f0ed969c8e512706c /src/ck-manager.c
parent2fba24e67597bf59ae00db2867df7a348c81b094 (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/ck-manager.c')
-rw-r--r--src/ck-manager.c667
1 files changed, 609 insertions, 58 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) {