From 52f4dc6163ecbc334f9ba949d6436f650ecf138d Mon Sep 17 00:00:00 2001 From: Alexandre Rostovtsev Date: Mon, 6 Feb 2012 05:19:08 -0500 Subject: Check polkit authorization asynchronously The other alternative (spawning a thread for each authorization request) seems less elegant, since such requests can take arbitrarily long to complete. --- src/bus-utils.c | 137 +++++++++++++++++++++------- src/bus-utils.h | 13 ++- src/hostnamed.c | 275 ++++++++++++++++++++++++++++++++++++++------------------ 3 files changed, 302 insertions(+), 123 deletions(-) diff --git a/src/bus-utils.c b/src/bus-utils.c index d3bc7b2..aef8037 100644 --- a/src/bus-utils.c +++ b/src/bus-utils.c @@ -20,44 +20,115 @@ #include #include +#include "bus-utils.h" + +struct check_polkit_data { + const gchar *unique_name; + const gchar *action_id; + gboolean user_interaction; + GAsyncReadyCallback callback; + gpointer user_data; + + PolkitAuthority *authority; + PolkitSubject *subject; +}; + +void +check_polkit_data_free (struct check_polkit_data *data) +{ + if (data == NULL) + return; + + if (data->subject != NULL) + g_object_unref (data->subject); + if (data->authority != NULL) + g_object_unref (data->authority); + + g_free (data); +} + gboolean -check_polkit (const gchar *unique_name, - const gchar *action_id, - const gboolean user_interaction, - GError **error) +check_polkit_finish (GAsyncResult *res, + GError **error) { - gboolean ret = FALSE; - GDBusConnection *connection = NULL; - PolkitAuthority *authority = NULL; - PolkitSubject *subject = NULL; - PolkitAuthorizationResult *result = NULL; - - if ((authority = polkit_authority_get_sync (NULL, error)) == NULL) - goto end; - - if (unique_name == NULL || action_id == NULL || - (subject = polkit_system_bus_name_new (unique_name)) == NULL) { - g_propagate_error (error, - g_error_new (POLKIT_ERROR, POLKIT_ERROR_FAILED, - "Authorizing for '%s': failed sanity check", action_id)); - goto end; - } + GSimpleAsyncResult *simple; + + simple = G_SIMPLE_ASYNC_RESULT (res); + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + return g_simple_async_result_get_op_res_gboolean (simple); +} + +static void +check_polkit_authorization_cb (GObject *source_object, + GAsyncResult *res, + gpointer _data) +{ + struct check_polkit_data *data; + PolkitAuthorizationResult *result; + GSimpleAsyncResult *simple; + GError *err = NULL; - if ((result = polkit_authority_check_authorization_sync (authority, subject, action_id, NULL, (PolkitCheckAuthorizationFlags) user_interaction, NULL, error)) == NULL) - goto end; + data = (struct check_polkit_data *) _data; + if ((result = polkit_authority_check_authorization_finish (data->authority, res, &err)) == NULL) { + g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); + goto out; + } - if ((ret = polkit_authorization_result_get_is_authorized (result)) == FALSE) { - g_propagate_error (error, - g_error_new (POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, - "Authorizing for '%s': not authorized", action_id)); + if (!polkit_authorization_result_get_is_authorized (result)) { + g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_NOT_AUTHORIZED, "Authorizing for '%s': not authorized", data->action_id); + goto out; } - - end: + simple = g_simple_async_result_new (NULL, data->callback, data->user_data, check_polkit_async); + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + + out: + check_polkit_data_free (data); if (result != NULL) g_object_unref (result); - if (subject != NULL) - g_object_unref (subject); - if (authority != NULL) - g_object_unref (authority); - return ret; +} + +static void +check_polkit_authority_cb (GObject *source_object, + GAsyncResult *res, + gpointer _data) +{ + struct check_polkit_data *data; + GError *err = NULL; + + data = (struct check_polkit_data *) _data; + if ((data->authority = polkit_authority_get_finish (res, &err)) == NULL) { + g_simple_async_report_take_gerror_in_idle (NULL, data->callback, data->user_data, err); + check_polkit_data_free (data); + return; + } + if (data->unique_name == NULL || data->action_id == NULL || + (data->subject = polkit_system_bus_name_new (data->unique_name)) == NULL) { + g_simple_async_report_error_in_idle (NULL, data->callback, data->user_data, POLKIT_ERROR, POLKIT_ERROR_FAILED, "Authorizing for '%s': failed sanity check", data->action_id); + check_polkit_data_free (data); + return; + } + polkit_authority_check_authorization (data->authority, data->subject, data->action_id, NULL, (PolkitCheckAuthorizationFlags) data->user_interaction, NULL, check_polkit_authorization_cb, data); +} + +void +check_polkit_async (const gchar *unique_name, + const gchar *action_id, + const gboolean user_interaction, + GAsyncReadyCallback callback, + gpointer user_data) +{ + struct check_polkit_data *data; + + data = g_new0 (struct check_polkit_data, 1); + data->unique_name = unique_name; + data->action_id = action_id; + data->user_interaction = user_interaction; + data->callback = callback; + data->user_data = user_data; + + polkit_authority_get_async (NULL, check_polkit_authority_cb, data); } \ No newline at end of file diff --git a/src/bus-utils.h b/src/bus-utils.h index 31a2ef1..f2e80ea 100644 --- a/src/bus-utils.h +++ b/src/bus-utils.h @@ -21,10 +21,15 @@ #include +void +check_polkit_async (const gchar *unique_name, + const gchar *action_id, + const gboolean user_interaction, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean -check_polkit (const gchar *unique_name, - const gchar *action_id, - const gboolean user_interaction, - GError **error); +check_polkit_finish (GAsyncResult *res, + GError **error); #endif \ No newline at end of file diff --git a/src/hostnamed.c b/src/hostnamed.c index 4f95d04..c5914bd 100644 --- a/src/hostnamed.c +++ b/src/hostnamed.c @@ -36,6 +36,11 @@ #define QUOTE(macro) #macro #define STR(macro) QUOTE(macro) +struct invoked_name { + GDBusMethodInvocation *invocation; + gchar *name; /* newly allocated */ +}; + guint bus_id = 0; gboolean read_only = FALSE; @@ -103,152 +108,250 @@ guess_icon_name () icon_name = g_strdup ("computer"); } -static gboolean -on_handle_set_hostname (OpenrcSettingsdHostnamedHostname1 *hostname1, - GDBusMethodInvocation *invocation, - const gchar *name, - const gboolean user_interaction, - gpointer user_data) +static void +on_handle_set_hostname_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { GError *err = NULL; - - if (read_only) { - g_dbus_method_invocation_return_dbus_error (invocation, - DBUS_ERROR_NOT_SUPPORTED, - "openrc-settingsd hostnamed is in read-only mode"); - goto end; - } - - if (!check_polkit (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-hostname", user_interaction, &err)) { - g_dbus_method_invocation_return_gerror (invocation, err); - goto end; + struct invoked_name *data; + + data = (struct invoked_name *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; } G_LOCK (hostname); /* Don't allow an empty or invalid hostname */ - if (!hostname_is_valid (name)) { - name = hostname; - if (!hostname_is_valid (name)) - name = "localhost"; + if (!hostname_is_valid (data->name)) { + if (data->name != NULL) + g_free (data->name); + + if (hostname_is_valid (hostname)) + data->name = g_strdup (hostname); + else + data->name = g_strdup ("localhost"); } - if (sethostname (name, strlen(name))) { + if (sethostname (data->name, strlen(data->name))) { int errsv = errno; - g_dbus_method_invocation_return_dbus_error (invocation, + g_dbus_method_invocation_return_dbus_error (data->invocation, DBUS_ERROR_FAILED, strerror (errsv)); G_UNLOCK (hostname); - goto end; + goto out; } - g_strlcpy (hostname, name, HOST_NAME_MAX + 1); - openrc_settingsd_hostnamed_hostname1_complete_set_hostname (hostname1, invocation); + g_strlcpy (hostname, data->name, HOST_NAME_MAX + 1); + openrc_settingsd_hostnamed_hostname1_complete_set_hostname (hostname1, data->invocation); openrc_settingsd_hostnamed_hostname1_set_hostname (hostname1, hostname); G_UNLOCK (hostname); - end: - return TRUE; + out: + g_free (data->name); + g_free (data); + if (err != NULL) + g_error_free (err); } static gboolean -on_handle_set_static_hostname (OpenrcSettingsdHostnamedHostname1 *hostname1, - GDBusMethodInvocation *invocation, - const gchar *name, - const gboolean user_interaction, - gpointer user_data) +on_handle_set_hostname (OpenrcSettingsdHostnamedHostname1 *hostname1, + GDBusMethodInvocation *invocation, + const gchar *name, + const gboolean user_interaction, + gpointer user_data) { - ShellUtilsTrivial *confd_file = NULL; - GError *err = NULL; - - if (read_only) { + if (read_only) g_dbus_method_invocation_return_dbus_error (invocation, DBUS_ERROR_NOT_SUPPORTED, "openrc-settingsd hostnamed is in read-only mode"); - goto end; + else { + struct invoked_name *data; + data = g_new0 (struct invoked_name, 1); + data->invocation = invocation; + data->name = g_strdup (name); + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-hostname", user_interaction, on_handle_set_hostname_authorized_cb, data); } - if (!check_polkit (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-static-hostname", user_interaction, &err)) { - g_dbus_method_invocation_return_gerror (invocation, err); - goto end; + return TRUE; +} + +static void +on_handle_set_static_hostname_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *err = NULL; + struct invoked_name *data; + + data = (struct invoked_name *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; } G_LOCK (static_hostname); /* Don't allow an empty or invalid hostname */ - if (!hostname_is_valid (name)) - name = "localhost"; + if (!hostname_is_valid (data->name)) { + if (data->name != NULL) + g_free (data->name); + + data->name = g_strdup ("localhost"); + } - if (!shell_utils_trivial_set_and_save (static_hostname_file, &err, "hostname", "HOSTNAME", name, NULL)) { - g_dbus_method_invocation_return_gerror (invocation, err); + if (!shell_utils_trivial_set_and_save (static_hostname_file, &err, "hostname", "HOSTNAME", data->name, NULL)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (static_hostname); - goto end; + goto out; } g_free (static_hostname); - static_hostname = g_strdup (name); - openrc_settingsd_hostnamed_hostname1_complete_set_static_hostname (hostname1, invocation); + static_hostname = data->name; /* data->name is g_strdup-ed already */; + openrc_settingsd_hostnamed_hostname1_complete_set_static_hostname (hostname1, data->invocation); openrc_settingsd_hostnamed_hostname1_set_static_hostname (hostname1, static_hostname); G_UNLOCK (static_hostname); - end: - shell_utils_trivial_free (confd_file); + out: + g_free (data); if (err != NULL) g_error_free (err); +} + +static gboolean +on_handle_set_static_hostname (OpenrcSettingsdHostnamedHostname1 *hostname1, + GDBusMethodInvocation *invocation, + const gchar *name, + const gboolean user_interaction, + gpointer user_data) +{ + if (read_only) + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + "openrc-settingsd hostnamed is in read-only mode"); + else { + struct invoked_name *data; + data = g_new0 (struct invoked_name, 1); + data->invocation = invocation; + data->name = g_strdup (name); + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-static-hostname", user_interaction, on_handle_set_static_hostname_authorized_cb, data); + } return TRUE; /* Always return TRUE to indicate signal has been handled */ } -static gboolean -on_handle_set_machine_info (OpenrcSettingsdHostnamedHostname1 *hostname1, - GDBusMethodInvocation *invocation, - const gchar *name, - const gboolean user_interaction, - gpointer user_data) +static void +on_handle_set_pretty_hostname_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) { - ShellUtilsTrivial *confd_file = NULL; GError *err = NULL; - gboolean is_pretty_hostname = GPOINTER_TO_INT(user_data); + struct invoked_name *data; + + data = (struct invoked_name *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; + } + + G_LOCK (machine_info); + /* Don't allow a null pretty hostname */ + if (data->name == NULL) + data->name = g_strdup (""); - if (read_only) { + if (!shell_utils_trivial_set_and_save (machine_info_file, &err, "PRETTY_HOSTNAME", NULL, data->name, NULL)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + G_UNLOCK (machine_info); + goto out; + } + + g_free (pretty_hostname); + pretty_hostname = data->name; /* data->name is g_strdup-ed already */ + openrc_settingsd_hostnamed_hostname1_complete_set_pretty_hostname (hostname1, data->invocation); + openrc_settingsd_hostnamed_hostname1_set_pretty_hostname (hostname1, pretty_hostname); + G_UNLOCK (machine_info); + + out: + g_free (data); + if (err != NULL) + g_error_free (err); +} + +static gboolean +on_handle_set_pretty_hostname (OpenrcSettingsdHostnamedHostname1 *hostname1, + GDBusMethodInvocation *invocation, + const gchar *name, + const gboolean user_interaction, + gpointer user_data) +{ + if (read_only) g_dbus_method_invocation_return_dbus_error (invocation, DBUS_ERROR_NOT_SUPPORTED, "openrc-settingsd hostnamed is in read-only mode"); - goto end; + else { + struct invoked_name *data; + data = g_new0 (struct invoked_name, 1); + data->invocation = invocation; + data->name = g_strdup (name); + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-machine-info", user_interaction, on_handle_set_pretty_hostname_authorized_cb, data); } - if (!check_polkit (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-machine-info", user_interaction, &err)) { - g_dbus_method_invocation_return_gerror (invocation, err); - goto end; + return TRUE; /* Always return TRUE to indicate signal has been handled */ +} + +static void +on_handle_set_icon_name_authorized_cb (GObject *source_object, + GAsyncResult *res, + gpointer user_data) +{ + GError *err = NULL; + struct invoked_name *data; + + data = (struct invoked_name *) user_data; + if (!check_polkit_finish (res, &err)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); + goto out; } G_LOCK (machine_info); /* Don't allow a null pretty hostname */ - if (name == NULL) - name = ""; + if (data->name == NULL) + data->name = g_strdup (""); - if ((is_pretty_hostname && - !shell_utils_trivial_set_and_save (machine_info_file, &err, "PRETTY_HOSTNAME", NULL, name, NULL)) || - (!is_pretty_hostname && - !shell_utils_trivial_set_and_save (machine_info_file, &err, "ICON_NAME", NULL, name, NULL))) { - g_dbus_method_invocation_return_gerror (invocation, err); + if (!shell_utils_trivial_set_and_save (machine_info_file, &err, "ICON_NAME", NULL, data->name, NULL)) { + g_dbus_method_invocation_return_gerror (data->invocation, err); G_UNLOCK (machine_info); - goto end; + goto out; } - if (is_pretty_hostname) { - g_free (pretty_hostname); - pretty_hostname = g_strdup (name); - openrc_settingsd_hostnamed_hostname1_complete_set_pretty_hostname (hostname1, invocation); - openrc_settingsd_hostnamed_hostname1_set_pretty_hostname (hostname1, pretty_hostname); - } else { - g_free (icon_name); - icon_name = g_strdup (name); - openrc_settingsd_hostnamed_hostname1_complete_set_icon_name (hostname1, invocation); - openrc_settingsd_hostnamed_hostname1_set_icon_name (hostname1, icon_name); - } + g_free (icon_name); + icon_name = data->name; /* data->name is g_strdup-ed already */ + openrc_settingsd_hostnamed_hostname1_complete_set_icon_name (hostname1, data->invocation); + openrc_settingsd_hostnamed_hostname1_set_icon_name (hostname1, icon_name); G_UNLOCK (machine_info); - end: - shell_utils_trivial_free (confd_file); + out: + g_free (data); if (err != NULL) g_error_free (err); +} + +static gboolean +on_handle_set_icon_name (OpenrcSettingsdHostnamedHostname1 *hostname1, + GDBusMethodInvocation *invocation, + const gchar *name, + const gboolean user_interaction, + gpointer user_data) +{ + if (read_only) + g_dbus_method_invocation_return_dbus_error (invocation, + DBUS_ERROR_NOT_SUPPORTED, + "openrc-settingsd hostnamed is in read-only mode"); + else { + struct invoked_name *data; + data = g_new0 (struct invoked_name, 1); + data->invocation = invocation; + data->name = g_strdup (name); + check_polkit_async (g_dbus_method_invocation_get_sender (invocation), "org.freedesktop.hostname1.set-machine-info", user_interaction, on_handle_set_icon_name_authorized_cb, data); + } return TRUE; /* Always return TRUE to indicate signal has been handled */ } @@ -272,8 +375,8 @@ on_bus_acquired (GDBusConnection *connection, g_signal_connect (hostname1, "handle-set-hostname", G_CALLBACK (on_handle_set_hostname), NULL); g_signal_connect (hostname1, "handle-set-static-hostname", G_CALLBACK (on_handle_set_static_hostname), NULL); - g_signal_connect (hostname1, "handle-set-pretty-hostname", G_CALLBACK (on_handle_set_machine_info), GINT_TO_POINTER(TRUE)); - g_signal_connect (hostname1, "handle-set-icon-name", G_CALLBACK (on_handle_set_machine_info), GINT_TO_POINTER(FALSE)); + g_signal_connect (hostname1, "handle-set-pretty-hostname", G_CALLBACK (on_handle_set_pretty_hostname), NULL); + g_signal_connect (hostname1, "handle-set-icon-name", G_CALLBACK (on_handle_set_icon_name), NULL); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (hostname1), connection, -- cgit v1.2.3-65-gdbad