diff options
Diffstat (limited to 'src/backend-dbus')
21 files changed, 4579 insertions, 0 deletions
diff --git a/src/backend-dbus/CMakeLists.txt b/src/backend-dbus/CMakeLists.txt new file mode 100644 index 0000000..fa41534 --- /dev/null +++ b/src/backend-dbus/CMakeLists.txt @@ -0,0 +1,57 @@ +include (GdbusCodegen) + +set(BACKEND_GENERATED_SOURCES +) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-display-manager + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.DisplayManager.Seat.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-webcredentials + com.canonical.indicators + ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.indicators.webcredentials.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-accounts + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-user + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.Accounts.User.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-manager + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Manager.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-seat + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.Seat.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-login1-user + org.freedesktop + ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.login1.User.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-screen-saver + org + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.ScreenSaver.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES gnome-session-manager + org + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.xml) + +add_gdbus_codegen (BACKEND_GENERATED_SOURCES dbus-end-session-dialog + org.gnome.SessionManager + ${CMAKE_CURRENT_SOURCE_DIR}/org.gnome.SessionManager.EndSessionDialog.xml) + +set (SOURCES actions.c guest.c users.c backend-dbus.c utils.c) + +# add warnings/coverage info on handwritten files +# but not the autogenerated ones... +set_source_files_properties (${SOURCES} + PROPERTIES COMPILE_FLAGS " -g ${CC_WARNING_ARGS} ${GCOV_FLAGS}") + +# add the bin dir to our include path s.t. our code can find the autogenerated header files +include_directories (${CMAKE_CURRENT_BINARY_DIR} ${SERVICE_INCLUDE_DIRS}) + +add_library (backenddbus STATIC ${SOURCES} ${BACKEND_GENERATED_SOURCES}) + diff --git a/src/backend-dbus/actions.c b/src/backend-dbus/actions.c new file mode 100644 index 0000000..fcf850d --- /dev/null +++ b/src/backend-dbus/actions.c @@ -0,0 +1,978 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> +#include <glib/gi18n.h> + +#include "dbus-end-session-dialog.h" +#include "dbus-login1-manager.h" +#include "dbus-webcredentials.h" +#include "gnome-screen-saver.h" +#include "gnome-session-manager.h" + +#include "actions.h" + +enum +{ + END_SESSION_TYPE_LOGOUT = 0, + END_SESSION_TYPE_SHUTDOWN, + END_SESSION_TYPE_REBOOT +}; + +struct _IndicatorSessionActionsDbusPriv +{ + GCancellable * cancellable; + + GSettings * lockdown_settings; + GSettings * indicator_settings; + GnomeScreenSaver * screen_saver; + GnomeSessionManager * session_manager; + Login1Manager * login1_manager; + GCancellable * login1_manager_cancellable; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + GCancellable * dm_seat_cancellable; + Webcredentials * webcredentials; + EndSessionDialog * end_session_dialog; + char * zenity; + + gboolean can_suspend; + gboolean can_hibernate; + gboolean seat_allows_activation; +}; + +typedef IndicatorSessionActionsDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionActionsDbus, + indicator_session_actions_dbus, + INDICATOR_TYPE_SESSION_ACTIONS) + +/*** +**** +***/ + +static void +log_and_clear_error (GError ** err, const char * loc, const char * func) +{ + if (*err) + { + if (!g_error_matches (*err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", loc, func, (*err)->message); + + g_clear_error (err); + } +} + +/*** +**** +***/ + +typedef enum +{ + PROMPT_NONE, + PROMPT_WITH_ZENITY, + PROMPT_WITH_UNITY +} +prompt_status_t; + +static prompt_status_t +get_prompt_status (IndicatorSessionActionsDbus * self) +{ + prompt_status_t prompt = PROMPT_NONE; + const priv_t * p = self->priv; + + if (!g_settings_get_boolean (p->indicator_settings, "suppress-logout-restart-shutdown")) + { + /* can we use the Unity prompt? */ + if ((prompt == PROMPT_NONE) && p && p->end_session_dialog) + { + GDBusProxy * proxy = G_DBUS_PROXY (p->end_session_dialog); + char * name = g_dbus_proxy_get_name_owner (proxy); + if (name != NULL) + prompt = PROMPT_WITH_UNITY; + g_free (name); + } + + /* can we use zenity? */ + if ((prompt == PROMPT_NONE) && p && p->zenity) + prompt = PROMPT_WITH_ZENITY; + } + + return prompt; +} + +/*** +**** +***/ + +static void +on_seat_notify_multi_session (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + gboolean b; + + b = login1_seat_get_can_multi_session (p->login1_seat); + + if (p->seat_allows_activation != b) + { + p->seat_allows_activation = b; + + indicator_session_actions_notify_can_switch (INDICATOR_SESSION_ACTIONS(self)); + } +} + +static void +set_login1_seat (IndicatorSessionActionsDbus * self, Login1Seat * seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + g_clear_object (&p->login1_seat); + } + + if (seat != NULL) + { + p->login1_seat = g_object_ref (seat); + + g_signal_connect_swapped (seat, "notify::can-multi-session", + G_CALLBACK(on_seat_notify_multi_session), self); + } +} + +/*** +**** +***/ + +static void +set_dm_seat (IndicatorSessionActionsDbus * self, DisplayManagerSeat * seat) +{ + priv_t * p = self->priv; + + if (p->dm_seat != NULL) + { + g_cancellable_cancel (p->dm_seat_cancellable); + g_clear_object (&p->dm_seat_cancellable); + g_clear_object (&p->dm_seat); + } + + if (seat != NULL) + { + p->dm_seat = g_object_ref (seat); + p->dm_seat_cancellable = g_cancellable_new (); + } +} + +static void +on_screensaver_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + GnomeScreenSaver * ss; + + err = NULL; + ss = gnome_screen_saver_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->screen_saver = ss; + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_can_suspend_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + char * str; + GError * err; + + str = NULL; + err = NULL; + login1_manager_call_can_suspend_finish (LOGIN1_MANAGER(o), &str, res, &err); + if (err == NULL) + { + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv; + + const gboolean b = !g_strcmp0 (str, "yes"); + + if (p->can_suspend != b) + { + p->can_suspend = b; + indicator_session_actions_notify_can_suspend (gself); + } + + g_free (str); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_can_hibernate_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + gchar * str; + GError * err; + + str = NULL; + err = NULL; + login1_manager_call_can_hibernate_finish (LOGIN1_MANAGER(o), &str, res, &err); + if (err == NULL) + { + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv; + + const gboolean b = !g_strcmp0 (str, "yes"); + + if (p->can_hibernate != b) + { + p->can_hibernate = b; + indicator_session_actions_notify_can_hibernate (gself); + } + + g_free (str); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +set_login1_manager (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_cancellable_cancel (p->login1_manager_cancellable); + g_clear_object (&p->login1_manager_cancellable); + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager_cancellable = g_cancellable_new (); + + p->login1_manager = g_object_ref (login1_manager); + + login1_manager_call_can_suspend (p->login1_manager, + p->login1_manager_cancellable, + on_can_suspend_ready, + self); + + login1_manager_call_can_hibernate (p->login1_manager, + p->login1_manager_cancellable, + on_can_hibernate_ready, + self); + } +} + +static void +on_session_manager_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + GnomeSessionManager * sm; + + err = NULL; + sm = gnome_session_manager_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->session_manager = sm; + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_webcredentials_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + Webcredentials * webcredentials; + + err = NULL; + webcredentials = webcredentials_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->webcredentials = webcredentials; + + g_signal_connect_swapped (webcredentials, "notify::error-status", + G_CALLBACK(indicator_session_actions_notify_has_online_account_error), gself); + + if (webcredentials_get_error_status (webcredentials)) + indicator_session_actions_notify_has_online_account_error (gself); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +on_end_session_dialog_proxy_ready (GObject * o G_GNUC_UNUSED, GAsyncResult * res, gpointer gself) +{ + GError * err; + EndSessionDialog * end_session_dialog; + + err = NULL; + end_session_dialog = end_session_dialog_proxy_new_for_bus_finish (res, &err); + if (err == NULL) + { + INDICATOR_SESSION_ACTIONS_DBUS(gself)->priv->end_session_dialog = end_session_dialog; + + indicator_session_actions_notify_can_prompt (INDICATOR_SESSION_ACTIONS(gself)); + indicator_session_actions_notify_can_reboot (INDICATOR_SESSION_ACTIONS(gself)); + } + + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +/*** +**** Virtual Functions +***/ + +static gboolean +my_can_lock (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return !g_settings_get_boolean (p->lockdown_settings, "disable-lock-screen"); +} + +static gboolean +my_can_logout (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + if (g_settings_get_boolean (p->indicator_settings, "suppress-logout-menuitem")) + return FALSE; + + if (g_settings_get_boolean (p->lockdown_settings, "disable-log-out")) + return FALSE; + + return TRUE; +} + +static gboolean +my_can_reboot (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS(actions); + priv_t * p = self->priv; + + if (g_settings_get_boolean (p->indicator_settings, "suppress-restart-menuitem")) + return FALSE; + + /* Shutdown and Restart are the same dialog prompt in Unity, + so disable the redundant 'Restart' menuitem in that mode */ + if (!g_settings_get_boolean (p->indicator_settings, "suppress-shutdown-menuitem")) + if (get_prompt_status(self) == PROMPT_WITH_UNITY) + return FALSE; + + return TRUE; +} + +static gboolean +my_can_switch (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p->seat_allows_activation + && !g_settings_get_boolean (p->lockdown_settings, "disable-user-switching"); +} + +static gboolean +my_can_suspend (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && p->can_suspend; +} + +static gboolean +my_can_hibernate (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && p->can_hibernate; +} + +static gboolean +my_can_prompt (IndicatorSessionActions * self) +{ + return get_prompt_status(INDICATOR_SESSION_ACTIONS_DBUS(self)) != PROMPT_NONE; +} + +static gboolean +my_has_online_account_error (IndicatorSessionActions * self) +{ + const priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + return p && (p->webcredentials) && (webcredentials_get_error_status (p->webcredentials)); +} + +static void +my_suspend (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_suspend (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +static void +my_hibernate (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_hibernate (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +/*** +**** End Session Dialog +***/ + +static void +logout_now (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + + g_return_if_fail (p->session_manager != NULL); + + gnome_session_manager_call_logout (p->session_manager, + 1, /* don't prompt */ + p->cancellable, + NULL, + NULL); +} + +static void +on_reboot_response (GObject * o, + GAsyncResult * res, + gpointer unused G_GNUC_UNUSED) +{ + GError * err = NULL; + login1_manager_call_reboot_finish (LOGIN1_MANAGER(o), res, &err); + if (err != NULL) + { + g_warning ("Unable to reboot: %s", err->message); + g_error_free (err); + } +} + +static void +reboot_now (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_reboot (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + on_reboot_response, + NULL); +} + +static void +power_off_now (IndicatorSessionActionsDbus * self) +{ + priv_t * p = self->priv; + + g_return_if_fail (p->login1_manager != NULL); + + login1_manager_call_power_off (p->login1_manager, + FALSE, + p->login1_manager_cancellable, + NULL, + NULL); +} + +static void +stop_listening_to_dialog (IndicatorSessionActionsDbus * self) +{ + g_signal_handlers_disconnect_by_data (self->priv->end_session_dialog, self); +} +static void +on_end_session_dialog_canceled (IndicatorSessionActionsDbus * self) +{ + stop_listening_to_dialog (self); +} +static void +on_end_session_dialog_closed (IndicatorSessionActionsDbus * self) +{ + stop_listening_to_dialog (self); +} + +static void +on_open_end_session_dialog_ready (GObject * o, + GAsyncResult * res, + gpointer gself G_GNUC_UNUSED) +{ + GError * err = NULL; + end_session_dialog_call_open_finish (END_SESSION_DIALOG(o), res, &err); + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +show_unity_end_session_dialog (IndicatorSessionActionsDbus * self, int type) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + gpointer o = p->end_session_dialog; + const char * inhibitor_paths[] = { NULL }; + + g_assert (o != NULL); + + g_signal_connect_swapped (o, "confirmed-logout", G_CALLBACK(logout_now), self); + g_signal_connect_swapped (o, "confirmed-reboot", G_CALLBACK(reboot_now), self); + g_signal_connect_swapped (o, "confirmed-shutdown", G_CALLBACK(power_off_now), self); + g_signal_connect_swapped (o, "canceled", G_CALLBACK(on_end_session_dialog_canceled), self); + g_signal_connect_swapped (o, "closed", G_CALLBACK(on_end_session_dialog_closed), self); + + end_session_dialog_call_open (p->end_session_dialog, type, 0, 0, inhibitor_paths, + p->cancellable, + on_open_end_session_dialog_ready, + self); +} + +static gboolean +zenity_question (IndicatorSessionActionsDbus * self, + const char * icon_name, + const char * title, + const char * text, + const char * ok_label, + const char * cancel_label) +{ + char * command_line; + int exit_status; + gboolean confirmed; + + command_line = g_strdup_printf ("%s" + " --question" + " --icon-name=\"%s\"" + " --title=\"%s\"" + " --text=\"%s\"" + " --ok-label=\"%s\"" + " --cancel-label=\"%s\"" + " --no-wrap", + self->priv->zenity, + icon_name, + title, + text, + ok_label, + cancel_label); + + exit_status = -1; + if (!g_spawn_command_line_sync (command_line, NULL, NULL, &exit_status, NULL)) + { + /* Treat failure-to-prompt as user confirmation. + Otherwise how will the user ever log out? */ + confirmed = TRUE; + } + else + { + confirmed = exit_status == 0; + } + + g_free (command_line); + return confirmed; +} + +static void +my_logout (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + show_unity_end_session_dialog (self, END_SESSION_TYPE_LOGOUT); + break; + + case PROMPT_NONE: + logout_now (self); + break; + + case PROMPT_WITH_ZENITY: + { + const char * primary = _("Are you sure you want to close all programs and log out?"); + const char * secondary = _("Some software updates won't be applied until the computer next restarts."); + char * text = g_strdup_printf ("<big><b>%s</b></big>\n \n%s", primary, secondary); + + gboolean confirmed = zenity_question (self, + "system-log-out", + _("Log Out"), + text, + _("Log Out"), + _("Cancel")); + + g_free (text); + + if (confirmed) + logout_now (self); + break; + } + } +} + +static void +my_reboot (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT); + break; + + case PROMPT_NONE: + reboot_now (self); + break; + + case PROMPT_WITH_ZENITY: + if (zenity_question (self, + "system-restart", + _("Restart"), + _("Are you sure you want to close all programs and restart the computer?"), + _("Restart"), + _("Cancel"))) + reboot_now (self); + break; + } +} + +static void +my_power_off (IndicatorSessionActions * actions) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + switch (get_prompt_status (self)) + { + case PROMPT_WITH_UNITY: + /* NB: TYPE_REBOOT instead of TYPE_SHUTDOWN because + the latter adds lock & logout options in Unity... */ + show_unity_end_session_dialog (self, END_SESSION_TYPE_REBOOT); + break; + + case PROMPT_WITH_ZENITY: + if (zenity_question (self, + "system-shutdown", + _("Shut Down"), + _("Are you sure you want to close all programs and shut down the computer?"), + _("Shut Down"), + _("Cancel"))) + power_off_now (self); + break; + + case PROMPT_NONE: + power_off_now (self); + break; + } +} + +/*** +**** +***/ + +static void +run_outside_app (const char * cmd) +{ + GError * err = NULL; + g_debug ("%s calling \"%s\"", G_STRFUNC, cmd); + g_spawn_command_line_async (cmd, &err); + log_and_clear_error (&err, G_STRLOC, G_STRFUNC); +} + +static void +my_help (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("yelp"); +} + +static void +my_settings (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center"); +} + +static void +my_online_accounts (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center credentials"); +} + +static void +my_about (IndicatorSessionActions * self G_GNUC_UNUSED) +{ + run_outside_app ("gnome-control-center info"); +} + +/*** +**** +***/ + +static void +lock_current_session (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->screen_saver != NULL); + + gnome_screen_saver_call_lock (p->screen_saver, p->cancellable, NULL, NULL); +} + +static void +my_switch_to_screensaver (IndicatorSessionActions * self) +{ + lock_current_session (self); +} + +static void +my_switch_to_greeter (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_greeter (p->dm_seat, + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_switch_to_guest (IndicatorSessionActions * self) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + lock_current_session (self); + + display_manager_seat_call_switch_to_guest (p->dm_seat, "", + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_switch_to_username (IndicatorSessionActions * self, const char * username) +{ + priv_t * p = INDICATOR_SESSION_ACTIONS_DBUS(self)->priv; + + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_user (p->dm_seat, username, "", + p->dm_seat_cancellable, + NULL, NULL); +} + +static void +my_dispose (GObject * o) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o); + priv_t * p = self->priv; + + if (p->cancellable != NULL) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + if (p->indicator_settings != NULL) + { + g_signal_handlers_disconnect_by_data (p->indicator_settings, self); + g_clear_object (&p->indicator_settings); + } + + if (p->lockdown_settings != NULL) + { + g_signal_handlers_disconnect_by_data (p->lockdown_settings, self); + g_clear_object (&p->lockdown_settings); + } + + if (p->webcredentials != NULL) + { + g_signal_handlers_disconnect_by_data (p->webcredentials, self); + g_clear_object (&p->webcredentials); + } + + if (p->end_session_dialog != NULL) + { + stop_listening_to_dialog (self); + g_clear_object (&p->end_session_dialog); + } + + g_clear_object (&p->screen_saver); + g_clear_object (&p->session_manager); + set_dm_seat (self, NULL); + set_login1_manager (self, NULL); + set_login1_seat (self, NULL); + + G_OBJECT_CLASS (indicator_session_actions_dbus_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorSessionActionsDbus * self = INDICATOR_SESSION_ACTIONS_DBUS (o); + priv_t * p = self->priv; + + g_free (p->zenity); +} + + +/*** +**** GObject Boilerplate +***/ + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_dbus_class_init (IndicatorSessionActionsDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionActionsClass * actions_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + actions_class = INDICATOR_SESSION_ACTIONS_CLASS (klass); + actions_class->can_lock = my_can_lock; + actions_class->can_logout = my_can_logout; + actions_class->can_reboot = my_can_reboot; + actions_class->can_switch = my_can_switch; + actions_class->can_suspend = my_can_suspend; + actions_class->can_hibernate = my_can_hibernate; + actions_class->can_prompt = my_can_prompt; + actions_class->has_online_account_error = my_has_online_account_error; + actions_class->logout = my_logout; + actions_class->suspend = my_suspend; + actions_class->hibernate = my_hibernate; + actions_class->reboot = my_reboot; + actions_class->power_off = my_power_off; + actions_class->settings = my_settings; + actions_class->online_accounts = my_online_accounts; + actions_class->help = my_help; + actions_class->about = my_about; + actions_class->switch_to_screensaver = my_switch_to_screensaver; + actions_class->switch_to_greeter = my_switch_to_greeter; + actions_class->switch_to_guest = my_switch_to_guest; + actions_class->switch_to_username = my_switch_to_username; + + g_type_class_add_private (klass, sizeof (IndicatorSessionActionsDbusPriv)); +} + +static void +/* cppcheck-suppress unusedFunction */ +indicator_session_actions_dbus_init (IndicatorSessionActionsDbus * self) +{ + priv_t * p; + GSettings * s; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_ACTIONS_DBUS, + IndicatorSessionActionsDbusPriv); + p->cancellable = g_cancellable_new (); + p->seat_allows_activation = TRUE; + self->priv = p; + + p->zenity = g_find_program_in_path ("zenity"); + + s = g_settings_new ("org.gnome.desktop.lockdown"); + g_signal_connect_swapped (s, "changed::disable-lock-screen", + G_CALLBACK(indicator_session_actions_notify_can_lock), self); + g_signal_connect_swapped (s, "changed::disable-log-out", + G_CALLBACK(indicator_session_actions_notify_can_logout), self); + g_signal_connect_swapped (s, "changed::disable-user-switching", + G_CALLBACK(indicator_session_actions_notify_can_switch), self); + p->lockdown_settings = s; + + s = g_settings_new ("com.canonical.indicator.session"); + g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(indicator_session_actions_notify_can_prompt), self); + g_signal_connect_swapped (s, "changed::suppress-logout-restart-shutdown", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + g_signal_connect_swapped (s, "changed::suppress-restart-menuitem", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + g_signal_connect_swapped (s, "changed::suppress-shutdown-menuitem", + G_CALLBACK(indicator_session_actions_notify_can_reboot), self); + p->indicator_settings = s; + + gnome_screen_saver_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.gnome.ScreenSaver", + "/org/gnome/ScreenSaver", + p->cancellable, + on_screensaver_proxy_ready, + self); + + gnome_session_manager_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + "org.gnome.SessionManager", + "/org/gnome/SessionManager", + p->cancellable, + on_session_manager_proxy_ready, + self); + + webcredentials_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "com.canonical.indicators.webcredentials", + "/com/canonical/indicators/webcredentials", + p->cancellable, + on_webcredentials_proxy_ready, + self); + + end_session_dialog_proxy_new_for_bus (G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, + "com.canonical.Unity", + "/org/gnome/SessionManager/EndSessionDialog", + p->cancellable, + on_end_session_dialog_proxy_ready, + self); +} + +/*** +**** Public +***/ + +IndicatorSessionActions * +indicator_session_actions_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_ACTIONS_DBUS, NULL); + + return INDICATOR_SESSION_ACTIONS (o); +} + +void +indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat) +{ + g_return_if_fail (INDICATOR_IS_SESSION_ACTIONS_DBUS(self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_dm_seat (self, dm_seat); +} diff --git a/src/backend-dbus/actions.h b/src/backend-dbus/actions.h new file mode 100644 index 0000000..d3d722d --- /dev/null +++ b/src/backend-dbus/actions.h @@ -0,0 +1,71 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_ACTIONS_DBUS_H__ +#define __INDICATOR_SESSION_ACTIONS_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../actions.h" /* parent class */ +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_ACTIONS_DBUS (indicator_session_actions_dbus_get_type()) +#define INDICATOR_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbus)) +#define INDICATOR_SESSION_ACTIONS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS, IndicatorSessionActionsDbusClass)) +#define INDICATOR_IS_SESSION_ACTIONS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_ACTIONS_DBUS)) + +typedef struct _IndicatorSessionActionsDbus IndicatorSessionActionsDbus; +typedef struct _IndicatorSessionActionsDbusPriv IndicatorSessionActionsDbusPriv; +typedef struct _IndicatorSessionActionsDbusClass IndicatorSessionActionsDbusClass; + +/** + * An implementation of IndicatorSessionActions that gets its user information + * from org.freedesktop.login1 org.freedesktop.DisplayManager over DBus. + */ +struct _IndicatorSessionActionsDbus +{ + /*< private >*/ + IndicatorSessionActions parent; + IndicatorSessionActionsDbusPriv * priv; +}; + +struct _IndicatorSessionActionsDbusClass +{ + IndicatorSessionActionsClass parent_class; +}; + +GType indicator_session_actions_dbus_get_type (void); + +IndicatorSessionActions * indicator_session_actions_dbus_new (void); + +void indicator_session_actions_dbus_set_proxies (IndicatorSessionActionsDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat); + + +G_END_DECLS + +#endif /* __INDICATOR_SESSION_ACTIONS_DBUS_H__ */ diff --git a/src/backend-dbus/backend-dbus.c b/src/backend-dbus/backend-dbus.c new file mode 100644 index 0000000..547c6ab --- /dev/null +++ b/src/backend-dbus/backend-dbus.c @@ -0,0 +1,115 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#include "actions.h" +#include "backend-dbus.h" +#include "guest.h" +#include "users.h" +#include "utils.h" + +struct dbus_world_data +{ + GCancellable * cancellable; + IndicatorSessionActionsDbus * actions; + IndicatorSessionUsersDbus * users; + IndicatorSessionGuestDbus * guest; +}; + +static void +on_proxies_ready (Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat, + Accounts * account_manager, + GCancellable * cancellable, + gpointer gdata) +{ + struct dbus_world_data * data = gdata; + + if (!g_cancellable_is_cancelled (cancellable)) + { + if (data->actions != NULL) + indicator_session_actions_dbus_set_proxies (data->actions, + login1_manager, + login1_seat, + display_manager_seat); + + if (data->users != NULL) + indicator_session_users_dbus_set_proxies (data->users, + login1_manager, + login1_seat, + display_manager_seat, + account_manager); + + if (data->guest != NULL) + indicator_session_guest_dbus_set_proxies (data->guest, + login1_manager, + login1_seat, + display_manager_seat); + } + + g_free (data); +} + +/*** +**** +***/ + +void +backend_get (GCancellable * cancellable, + IndicatorSessionActions ** setme_actions, + IndicatorSessionUsers ** setme_users, + IndicatorSessionGuest ** setme_guest) +{ + struct dbus_world_data * data; + + data = g_new0 (struct dbus_world_data, 1); + + if (setme_actions != NULL) + { + IndicatorSessionActions * actions; + actions = indicator_session_actions_dbus_new (); + data->actions = INDICATOR_SESSION_ACTIONS_DBUS (actions); + + *setme_actions = actions; + } + + if (setme_users != NULL) + { + IndicatorSessionUsers * users; + users = indicator_session_users_dbus_new (); + data->users = INDICATOR_SESSION_USERS_DBUS (users); + + *setme_users = users; + } + + if (setme_guest != NULL) + { + IndicatorSessionGuest * guest; + guest = indicator_session_guest_dbus_new (); + data->guest = INDICATOR_SESSION_GUEST_DBUS (guest); + + *setme_guest = guest; + } + + data->cancellable = g_object_ref (cancellable); + + indicator_session_util_get_session_proxies (on_proxies_ready, + data->cancellable, + data); +} diff --git a/src/backend-dbus/backend-dbus.h b/src/backend-dbus/backend-dbus.h new file mode 100644 index 0000000..daf6ac3 --- /dev/null +++ b/src/backend-dbus/backend-dbus.h @@ -0,0 +1,38 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __INDICATOR_SESSION_BACKEND_DESKTOP_H__ +#define __INDICATOR_SESSION_BACKEND_DESKTOP_H__ + +#include <gio/gio.h> /* GCancellable */ + +#include "../actions.h" +#include "../guest.h" +#include "../users.h" + +G_BEGIN_DECLS + +void backend_get (GCancellable * cancellable, + IndicatorSessionActions ** setme_actions, + IndicatorSessionUsers ** setme_users, + IndicatorSessionGuest ** setme_guest); + +G_END_DECLS + +#endif diff --git a/src/backend-dbus/com.canonical.indicators.webcredentials.xml b/src/backend-dbus/com.canonical.indicators.webcredentials.xml new file mode 100644 index 0000000..d215081 --- /dev/null +++ b/src/backend-dbus/com.canonical.indicators.webcredentials.xml @@ -0,0 +1,82 @@ +<node> +<!-- + com.canonical.indicators.webcredentials: + @short_description: interface for handling login failures. + + The service implementing this interface keeps track of login failures. + Failures are reported (usually by signon-ui) using the ReportFailure method, + are listed in the Failures property and can be removed by calling + RemoveFailures. + + The ClearErrorStatus method can be called to clear the error indicator from + the system user menu. +--> +<interface name="com.canonical.indicators.webcredentials"> + <!-- + ReportFailure: + @account-id: the libaccounts ID of the account which failed to login. + @notification: dictionary of parameters for the OSD notification. + + Inform the service about a failing account. The @account-id is added to the + list of the accounts in the Failures property, and a notification might be + displayed to the user. + + The parameters currently recognized for the @notification argument are: + - DisplayName: string, description of the account (usually it's the + username) + --> + <method name="ReportFailure"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/> + <arg name="account_id" type="u" direction="in"/> + <arg name="notification" type="a{sv}" direction="in"/> + </method> + + <!-- + RemoveFailures: + @account-ids: the libaccounts IDs of the accounts. + + Remove the given account IDs from the list of the failed accounts. + --> + <method name="RemoveFailures"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QSet<uint>"/> + <arg name="account_ids" type="au" direction="in"/> + </method> + + <!-- + ReauthenticateAccount: + @account-id: the libaccounts ID of the account. + @extra-parameters: dictionary of extra parameters (typically used to + specify a XWindowID). + @reauthenticated: %TRUE if the account could be reauthenticated and the + failure status has been cleared, %FALSE otherwise. + + Tries to replay the failed authentications on the account. If all of them + succeed, then the account failure is cleared. + --> + <method name="ReauthenticateAccount"> + <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="QVariantMap"/> + <arg name="account_id" type="u" direction="in"/> + <arg name="extra_parameters" type="a{sv}" direction="in"/> + <arg name="reauthenticated" type="b" direction="out"/> + </method> + + <!-- + ClearErrorStatus: + + Unsets the error indicator (if any) from the system user menu. + --> + <method name="ClearErrorStatus"/> + + <!-- + Failures: list of the libaccounts IDs of the failing accounts. + --> + <property name="Failures" type="au" access="read"> + <annotation name="com.trolltech.QtDBus.QtTypeName" value="QSet<uint>"/> + </property> + + <!-- + ErrorStatus: true if the indicator should display an error status. + --> + <property name="ErrorStatus" type="b" access="read"/> +</interface> +</node> diff --git a/src/backend-dbus/guest.c b/src/backend-dbus/guest.c new file mode 100644 index 0000000..3269097 --- /dev/null +++ b/src/backend-dbus/guest.c @@ -0,0 +1,394 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#include <glib.h> + +#include "guest.h" + +struct _IndicatorSessionGuestDbusPriv +{ + GCancellable * cancellable; + + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + + gboolean guest_is_active; + gboolean guest_is_logged_in; + gboolean guest_is_allowed; +}; + +typedef IndicatorSessionGuestDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionGuestDbus, + indicator_session_guest_dbus, + INDICATOR_TYPE_SESSION_GUEST) + +/*** +**** +***/ + +static void +set_guest_is_allowed_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_allowed != b) + { + p->guest_is_allowed = b; + + indicator_session_guest_notify_allowed (INDICATOR_SESSION_GUEST (self)); + } +} + +static void +set_guest_is_logged_in_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_logged_in != b) + { + p->guest_is_logged_in = b; + + indicator_session_guest_notify_logged_in (INDICATOR_SESSION_GUEST (self)); + } +} + +static void +set_guest_is_active_flag (IndicatorSessionGuestDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->guest_is_active != b) + { + p->guest_is_active = b; + + indicator_session_guest_notify_active (INDICATOR_SESSION_GUEST(self)); + } +} + +/*** +**** +***/ + +/* walk the sessions to see if guest is logged in or active */ +static void +on_login1_manager_session_list_ready (GObject * o, + GAsyncResult * res, + gpointer gself) +{ + GVariant * sessions; + GError * err; + + sessions = NULL; + err = NULL; + login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o), + &sessions, + res, + &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + const gchar * const current_seat_id = g_getenv ("XDG_SEAT"); + const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID"); + gboolean is_logged_in = FALSE; + gboolean is_active = FALSE; + const gchar * session_id = NULL; + guint32 uid = 0; + const gchar * user_name = NULL; + const gchar * seat_id = NULL; + GVariantIter iter; + + g_variant_iter_init (&iter, sessions); + while (g_variant_iter_loop (&iter, "(&su&s&so)", &session_id, + &uid, + &user_name, + &seat_id, + NULL)) + { + gboolean is_current_session; + gboolean is_guest; + + is_current_session = !g_strcmp0 (current_seat_id, seat_id) + && !g_strcmp0 (current_session_id, session_id); + + is_guest = g_str_has_prefix (user_name, "guest-") + && (uid < 1000); + + if (is_guest) + { + is_logged_in = TRUE; + + is_active = is_current_session; + } + } + + set_guest_is_logged_in_flag (gself, is_logged_in); + set_guest_is_active_flag (gself, is_active); + + g_variant_unref (sessions); + } +} + +static void +update_session_list (IndicatorSessionGuestDbus * self) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + login1_manager_call_list_sessions (p->login1_manager, + p->cancellable, + on_login1_manager_session_list_ready, + self); + } +} + +static void +set_login1_manager (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_manager, self); + + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager = g_object_ref (login1_manager); + + g_signal_connect_swapped (login1_manager, "session-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "session-removed", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +static void +set_login1_seat (IndicatorSessionGuestDbus * self, + Login1Seat * login1_seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + g_clear_object (&p->login1_seat); + } + + if (login1_seat != NULL) + { + p->login1_seat = g_object_ref (login1_seat); + + g_signal_connect_swapped (login1_seat, "notify::active-session", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +/*** +**** +***/ + +static void +on_switch_to_guest_done (GObject * o, + GAsyncResult * res, + gpointer unused G_GNUC_UNUSED) +{ + GError * err; + + err = NULL; + display_manager_seat_call_switch_to_guest_finish (DISPLAY_MANAGER_SEAT(o), + res, + &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } +} + +static void +my_switch_to_guest (IndicatorSessionGuest * guest) +{ + IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS(guest); + + g_return_if_fail (self != NULL); + g_return_if_fail (self->priv->dm_seat != NULL); + + display_manager_seat_call_switch_to_guest (self->priv->dm_seat, + "", + self->priv->cancellable, + on_switch_to_guest_done, + self); +} + +static void +on_notify_has_guest_account (DisplayManagerSeat * dm_seat, + GParamSpec * pspec G_GNUC_UNUSED, + IndicatorSessionGuestDbus * self) +{ + gboolean guest_exists = display_manager_seat_get_has_guest_account (dm_seat); + set_guest_is_allowed_flag (self, guest_exists); +} + +static void +set_display_manager_seat (IndicatorSessionGuestDbus * self, + DisplayManagerSeat * dm_seat) +{ + priv_t * p = self->priv; + + if (p->dm_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->dm_seat, self); + + g_clear_object (&p->dm_seat); + } + + if (dm_seat != NULL) + { + p->dm_seat = g_object_ref (dm_seat); + + g_signal_connect (dm_seat, "notify::has-guest-account", + G_CALLBACK(on_notify_has_guest_account), self); + on_notify_has_guest_account (dm_seat, NULL, self); + } +} + +/*** +**** IndiatorSessionGuest Virtual Functions +***/ + +static gboolean +my_is_allowed (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_allowed; +} + +static gboolean +my_is_logged_in (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_logged_in; +} + +static gboolean +my_is_active (IndicatorSessionGuest * self) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self), FALSE); + + return INDICATOR_SESSION_GUEST_DBUS(self)->priv->guest_is_active; +} + +/*** +**** GObject Virtual Functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorSessionGuestDbus * self = INDICATOR_SESSION_GUEST_DBUS (o); + + if (self->priv->cancellable != NULL) + { + g_cancellable_cancel (self->priv->cancellable); + g_clear_object (&self->priv->cancellable); + } + + set_login1_seat (self, NULL); + set_login1_manager (self, NULL); + set_display_manager_seat (self, NULL); + + G_OBJECT_CLASS (indicator_session_guest_dbus_parent_class)->dispose (o); +} + +/*** +**** Instantiation +***/ + +static void +indicator_session_guest_dbus_class_init (IndicatorSessionGuestDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionGuestClass * guest_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + + guest_class = INDICATOR_SESSION_GUEST_CLASS (klass); + guest_class->is_allowed = my_is_allowed; + guest_class->is_logged_in = my_is_logged_in; + guest_class->is_active = my_is_active; + guest_class->switch_to_guest = my_switch_to_guest; + + g_type_class_add_private (klass, sizeof (IndicatorSessionGuestDbusPriv)); +} + +static void +indicator_session_guest_dbus_init (IndicatorSessionGuestDbus * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_GUEST_DBUS, + IndicatorSessionGuestDbusPriv); + p->cancellable = g_cancellable_new (); + self->priv = p; +} + +/*** +**** Public +***/ + +IndicatorSessionGuest * +indicator_session_guest_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_GUEST_DBUS, NULL); + + return INDICATOR_SESSION_GUEST (o); +} + +void +indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat) +{ + g_return_if_fail (INDICATOR_IS_SESSION_GUEST_DBUS(self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_display_manager_seat (self, dm_seat); +} diff --git a/src/backend-dbus/guest.h b/src/backend-dbus/guest.h new file mode 100644 index 0000000..73bb3ca --- /dev/null +++ b/src/backend-dbus/guest.h @@ -0,0 +1,70 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __GUEST_DBUS_H__ +#define __GUEST_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../guest.h" /* parent class */ +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_GUEST_DBUS (indicator_session_guest_dbus_get_type()) +#define INDICATOR_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbus)) +#define INDICATOR_SESSION_GUEST_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS, IndicatorSessionGuestDbusClass)) +#define INDICATOR_IS_SESSION_GUEST_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_GUEST_DBUS)) + +typedef struct _IndicatorSessionGuestDbus IndicatorSessionGuestDbus; +typedef struct _IndicatorSessionGuestDbusPriv IndicatorSessionGuestDbusPriv; +typedef struct _IndicatorSessionGuestDbusClass IndicatorSessionGuestDbusClass; + +/** + * An implementation of IndicatorSessionGuest that gets its user information + * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus. + */ +struct _IndicatorSessionGuestDbus +{ + /*< private >*/ + IndicatorSessionGuest parent; + IndicatorSessionGuestDbusPriv * priv; +}; + +struct _IndicatorSessionGuestDbusClass +{ + IndicatorSessionGuestClass parent_class; +}; + +GType indicator_session_guest_dbus_get_type (void); + +IndicatorSessionGuest * indicator_session_guest_dbus_new (void); + +void indicator_session_guest_dbus_set_proxies (IndicatorSessionGuestDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat); + +G_END_DECLS + +#endif diff --git a/src/backend-dbus/org.freedesktop.Accounts.User.xml b/src/backend-dbus/org.freedesktop.Accounts.User.xml new file mode 100644 index 0000000..53f54d4 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.Accounts.User.xml @@ -0,0 +1,744 @@ +<!DOCTYPE node PUBLIC +"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" > +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + + <interface name="org.freedesktop.Accounts.User"> + + <method name="SetUserName"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="name" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new username. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users username. Note that it is usually not allowed + to have multiple users with the same username. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the username of any user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetRealName"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="name" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new name, typically in the form "Firstname Lastname". + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users real name. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own name</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the name of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetEmail"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="email" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new email address. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users email address. + </doc:para> + <doc:para> + Note that setting an email address in the AccountsService is + not the same as configuring a mail client. Mail clients might + default to email address that is configured here, though. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own email address</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the email address of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetLanguage"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="language" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new language, as a locale specification like "de_DE.UTF-8". + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users language. + </doc:para> + <doc:para> + The expectation is that display managers will start the + users session with this locale. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own language</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the language of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetXSession"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="user_set_x_session"/> + <arg name="x_session" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new xsession to start (e.g. "gnome") + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users x session. + </doc:para> + <doc:para> + The expectation is that display managers will log the user in to this + specified session, if available. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own language</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the language of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetLocation"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="location" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new location as a freeform string. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users location. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own location</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the location of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetHomeDirectory"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="homedir" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new homedir as an absolute path. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users home directory. + </doc:para> + <doc:para> + Note that changing the users home directory moves all the content + from the old location to the new one, and is potentially an + expensive operation. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the home directory of a user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetShell"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="shell" direction="in" type="s"> + <doc:doc> + <doc:summary> + The new user shell. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users shell. + </doc:para> + <doc:para> + Note that setting the shell to a non-allowed program may + prevent the user from logging in. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the shell of a user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetIconFile"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="filename" direction="in" type="s"> + <doc:doc> + <doc:summary> + The absolute filename of a png file to use as the users icon. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets the users icon. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.change-own-user-data</doc:term> + <doc:definition>To change his own icon</doc:definition> + </doc:item> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the icon of another user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetLocked"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="locked" direction="in" type="b"> + <doc:doc> + <doc:summary> + Whether to lock or unlock the users account. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Locks or unlocks a users account. + </doc:para> + <doc:para> + Locking an account prevents the user from logging in. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To lock or unlock user accounts</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetAccountType"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="accountType" direction="in" type="i"> + <doc:doc> + <doc:summary> + The new account type, encoded as an integer: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Standard user</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Administrator</doc:definition> + </doc:item> + </doc:list> + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Changes the users account type. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change an account type</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetPasswordMode"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="mode" direction="in" type="i"> + <doc:doc> + <doc:summary> + The new password mode, encoded as an integer: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Regular password</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Password must be set at next login</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>No password</doc:definition> + </doc:item> + </doc:list> + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Changes the users password mode. + </doc:para> + <doc:para> + Note that changing the password mode has the side-effect of + unlocking the account. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change a users password mode</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetPassword"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="password" direction="in" type="s"> + <doc:doc> + <doc:summary> + The crypted password. + </doc:summary> + </doc:doc> + </arg> + <arg name="hint" direction="in" type="s"> + <doc:doc> + <doc:summary> + The password hint. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Sets a new password for this user. + </doc:para> + <doc:para> + Note that setting a password has the side-effect of + unlocking the account. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.user-administration</doc:term> + <doc:definition>To change the password of a user</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="SetAutomaticLogin"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="enabled" direction="in" type="b"> + <doc:doc> + <doc:summary> + Whether to enable automatic login for this user. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Enables or disables automatic login for a user. + </doc:para> + <doc:para> + Note that usually only one user can have automatic login + enabled, so turning it on for a user will disable it for + the previously configured autologin user. + </doc:para> + </doc:description> + <doc:permission> + The caller needs one of the following PolicyKit authorizations: + <doc:list> + <doc:item> + <doc:term>org.freedesktop.accounts.set-login-option</doc:term> + <doc:definition>To change the login screen configuration</doc:definition> + </doc:item> + </doc:list> + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <property name="Uid" type="t" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The uid of the user. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="UserName" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The username of the user. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="RealName" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users real name. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="AccountType" type="i" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users account type, encoded as an integer: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Standard user</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Administrator</doc:definition> + </doc:item> + </doc:list> + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="HomeDirectory" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users home directory. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Shell" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users shell. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Email" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The email address. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Language" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users language, as a locale specification like "de_DE.UTF-8". + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="XSession" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users x session. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Location" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The users location. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="LoginFrequency" type="t" access="read"> + <doc:doc> + <doc:description> + <doc:para> + How often the user has logged in. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="IconFile" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The filename of a png file containing the users icon. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="Locked" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Whether the users account is locked. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="PasswordMode" type="i" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The password mode for the user account, encoded as an integer: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Regular password</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Password must be set at next login</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>No password</doc:definition> + </doc:item> + </doc:list> + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="PasswordHint" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The password hint for the user. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="AutomaticLogin" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Whether automatic login is enabled for the user. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="SystemAccount" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para> + Whether this is a 'system' account, like 'root' or 'nobody'. + System accounts should normally not appear in lists of + users, and ListCachedUsers will not include such accounts. + </doc:para> + </doc:description> + </doc:doc> + </property> + + <signal name="Changed"> + <doc:doc> + <doc:description> + <doc:para> + Emitted when the user is changed. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + </interface> +</node> diff --git a/src/backend-dbus/org.freedesktop.Accounts.xml b/src/backend-dbus/org.freedesktop.Accounts.xml new file mode 100644 index 0000000..9c19761 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.Accounts.xml @@ -0,0 +1,194 @@ +<!DOCTYPE node PUBLIC +"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd" > +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.freedesktop.Accounts"> + + <!-- ************************************************************ --> + + <method name="ListCachedUsers"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="users" direction="out" type="ao"> + <doc:doc><doc:summary>Object paths of cached users</doc:summary></doc:doc> + </arg> + + <doc:doc> + <doc:description> + <doc:para> + Lists users which have logged into the system locally before. + This is not meant to return an exhaustive list of all users. + It is possible for <doc:ref type="method" to="Accounts.FindUserByName">FindUserByName()</doc:ref> + to return a user that's not on the list. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="FindUserById"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="id" direction="in" type="x"> + <doc:doc><doc:summary>The uid to look up</doc:summary></doc:doc> + </arg> + <arg name="user" direction="out" type="o"> + <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc> + </arg> + + <doc:doc> + <doc:description> + <doc:para> + Finds a user by uid. + </doc:para> + </doc:description> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given uid exists</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="FindUserByName"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="name" direction="in" type="s"> + <doc:doc><doc:summary>The username to look up</doc:summary></doc:doc> + </arg> + <arg name="user" direction="out" type="o"> + <doc:doc><doc:summary>Object path of user</doc:summary></doc:doc> + </arg> + + <doc:doc> + <doc:description> + <doc:para> + Finds a user by its username. + </doc:para> + </doc:description> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if no user with the given username exists</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="CreateUser"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="name" direction="in" type="s"> + <doc:doc><doc:summary>The username for the new user</doc:summary></doc:doc> + </arg> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="fullname" direction="in" type="s"> + <doc:doc><doc:summary>The real name for the new user</doc:summary></doc:doc> + </arg> + <arg name="user" direction="out" type="o"> + <doc:doc><doc:summary>Object path of the new user</doc:summary></doc:doc> + </arg> + <arg name="accountType" direction="in" type="i"> + <doc:doc> + <doc:summary>The account type, encoded as an integer</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Creates a new user account. + </doc:para> + <doc:para> + The accountType argument can take the following values: + </doc:para> + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Standard user</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Administrator</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Supervised user</doc:definition> + </doc:item> + </doc:list> + </doc:description> + <doc:permission> + The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization. + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <method name="DeleteUser"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg name="id" direction="in" type="x"> + <doc:doc><doc:summary>The uid to delete</doc:summary></doc:doc> + </arg> + <arg name="removeFiles" direction="in" type="b"> + <doc:doc><doc:summary>Whether to remove the users files</doc:summary></doc:doc> + </arg> + + <doc:doc> + <doc:description> + <doc:para> + Deletes a user account. + </doc:para> + </doc:description> + <doc:permission> + The caller needs the org.freedesktop.accounts.user-administration PolicyKit authorization. + </doc:permission> + <doc:errors> + <doc:error name="org.freedesktop.Accounts.Error.PermissionDenied">if the caller lacks the appropriate PolicyKit authorization</doc:error> + <doc:error name="org.freedesktop.Accounts.Error.Failed">if the operation failed</doc:error> + </doc:errors> + </doc:doc> + </method> + + <signal name="UserAdded"> + <arg name="user" type="o"> + <doc:doc><doc:summary>Object path of the user that was added.</doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Emitted when a user is added. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="UserDeleted"> + <arg name="user" type="o"> + <doc:doc><doc:summary>Object path of the user that was deleted.</doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Emitted when a user is deleted. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="UserChanged"> + <arg name="user" type="o"> + <doc:doc><doc:summary>Object path of the user that was changed.</doc:summary></doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para> + Emitted when a user is changed. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <property name="DaemonVersion" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para> + The version of the running daemon. + </doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> +</node> diff --git a/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml new file mode 100644 index 0000000..07b5f29 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.DisplayManager.Seat.xml @@ -0,0 +1,30 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> + +<node> + <interface name="org.freedesktop.DisplayManager.Seat"> + + <property name="CanSwitch" type="b" access="read"/> + + <property name="HasGuestAccount" type="b" access="read"/> + + <property name="Sessions" type="ao" access="read"/> + + <!-- Show greeter to allow new login / switch users --> + <method name="SwitchToGreeter"/> + + <!-- Switch to a user, starting a new display if required --> + <method name="SwitchToUser"> + <arg name="username" direction="in" type="s"/> + <arg name="session_name" direction="in" type="s"/> + </method> + + <!-- Switch to the guest user --> + <method name="SwitchToGuest"> + <arg name="session_name" direction="in" type="s"/> + </method> + + <method name='Lock'/> + + </interface> + +</node> diff --git a/src/backend-dbus/org.freedesktop.login1.Manager.xml b/src/backend-dbus/org.freedesktop.login1.Manager.xml new file mode 100644 index 0000000..91da5f2 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.login1.Manager.xml @@ -0,0 +1,199 @@ +<?xml version="1.0"?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.login1.Manager"> + <method name="GetSession"> + <arg name="id" type="s" direction="in"/> + <arg name="session" type="o" direction="out"/> + </method> + <method name="GetSessionByPID"> + <arg name="pid" type="u" direction="in"/> + <arg name="session" type="o" direction="out"/> + </method> + <method name="GetUser"> + <arg name="uid" type="u" direction="in"/> + <arg name="user" type="o" direction="out"/> + </method> + <method name="GetSeat"> + <arg name="id" type="s" direction="in"/> + <arg name="seat" type="o" direction="out"/> + </method> + <method name="ListSessions"> + <arg name="sessions" type="a(susso)" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="SessionObjectList"/> + </method> + <method name="ListUsers"> + <arg name="users" type="a(uso)" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="UintStringPathList"/> + </method> + <method name="ListSeats"> + <arg name="seats" type="a(so)" direction="out"/> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="StringPathList"/> + </method> + <method name="CreateSession"> + <arg name="uid" type="u" direction="in"/> + <arg name="leader" type="u" direction="in"/> + <arg name="sevice" type="s" direction="in"/> + <arg name="type" type="s" direction="in"/> + <arg name="klass" type="s" direction="in"/> + <arg name="seat" type="s" direction="in"/> + <arg name="vtnr" type="u" direction="in"/> + <arg name="tty" type="s" direction="in"/> + <arg name="display" type="s" direction="in"/> + <arg name="remote" type="b" direction="in"/> + <arg name="remote_user" type="s" direction="in"/> + <arg name="remote_host" type="s" direction="in"/> + <arg name="controllers" type="as" direction="in"/> + <arg name="reset_controllers" type="as" direction="in"/> + <arg name="kill_processes" type="b" direction="in"/> + <arg name="id" type="s" direction="out"/> + <arg name="path" type="o" direction="out"/> + <arg name="runtime_path" type="o" direction="out"/> + <arg name="fd" type="h" direction="out"/> + <arg name="seat" type="s" direction="out"/> + <arg name="vtnr" type="u" direction="out"/> + <arg name="existing" type="b" direction="out"/> + </method> + <method name="ReleaseSession"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="ActivateSession"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="ActivateSessionOnSeat"> + <arg name="id" type="s" direction="in"/> + <arg name="seat" type="s" direction="in"/> + </method> + <method name="LockSession"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="UnlockSession"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="LockSessions"/> + <method name="KillSession"> + <arg name="id" type="s" direction="in"/> + <arg name="who" type="s" direction="in"/> + <arg name="signal" type="s" direction="in"/> + </method> + <method name="KillUser"> + <arg name="uid" type="u" direction="in"/> + <arg name="signal" type="s" direction="in"/> + </method> + <method name="TerminateSession"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="TerminateUser"> + <arg name="uid" type="u" direction="in"/> + </method> + <method name="TerminateSeat"> + <arg name="id" type="s" direction="in"/> + </method> + <method name="SetUserLinger"> + <arg name="uid" type="u" direction="in"/> + <arg name="b" type="b" direction="in"/> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="AttachDevice"> + <arg name="seat" type="s" direction="in"/> + <arg name="sysfs" type="s" direction="in"/> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="FlushDevices"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="PowerOff"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="Reboot"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="Suspend"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="Hibernate"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="HybridSleep"> + <arg name="interactive" type="b" direction="in"/> + </method> + <method name="CanPowerOff"> + <arg name="result" type="s" direction="out"/> + </method> + <method name="CanReboot"> + <arg name="result" type="s" direction="out"/> + </method> + <method name="CanSuspend"> + <arg name="result" type="s" direction="out"/> + </method> + <method name="CanHibernate"> + <arg name="result" type="s" direction="out"/> + </method> + <method name="CanHybridSleep"> + <arg name="result" type="s" direction="out"/> + </method> + <method name="Inhibit"> + <arg name="what" type="s" direction="in"/> + <arg name="who" type="s" direction="in"/> + <arg name="why" type="s" direction="in"/> + <arg name="mode" type="s" direction="in"/> + <arg name="fd" type="h" direction="out"/> + </method> + <method name="ListInhibitors"> + <arg name="inhibitors" type="a(ssssuu)" direction="out"/> + <!-- TODO: Create an appropriate type --> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/> + </method> + <signal name="SessionNew"> + <arg name="id" type="s"/> + <arg name="path" type="o"/> + </signal> + <signal name="SessionRemoved"> + <arg name="id" type="s"/> + <arg name="path" type="o"/> + </signal> + <signal name="UserNew"> + <arg name="uid" type="u"/> + <arg name="path" type="o"/> + </signal> + <signal name="UserRemoved"> + <arg name="uid" type="u"/> + <arg name="path" type="o"/> + </signal> + <signal name="SeatNew"> + <arg name="id" type="s"/> + <arg name="path" type="o"/> + </signal> + <signal name="SeatRemoved"> + <arg name="id" type="s"/> + <arg name="path" type="o"/> + </signal> + <signal name="PrepareForShutdown"> + <arg name="active" type="b"/> + </signal> + <signal name="PrepareForSleep"> + <arg name="active" type="b"/> + </signal> + <property name="ControlGroupHierarchy" type="s" access="read"/> + <property name="Controllers" type="as" access="read"/> + <property name="ResetControllers" type="as" access="read"/> + <property name="NAutoVTs" type="u" access="read"/> + <property name="KillOnlyUsers" type="as" access="read"/> + <property name="KillExcludeUsers" type="as" access="read"/> + <property name="KillUserProcesses" type="b" access="read"/> + <property name="IdleHint" type="b" access="read"/> + <property name="IdleSinceHint" type="t" access="read"/> + <property name="IdleSinceHintMonotonic" type="t" access="read"/> + <property name="BlockInhibited" type="s" access="read"/> + <property name="DelayInhibited" type="s" access="read"/> + <property name="InhibitDelayMaxUSec" type="t" access="read"/> + <property name="HandlePowerKey" type="s" access="read"/> + <property name="HandleSuspendKey" type="s" access="read"/> + <property name="HandleHibernateKey" type="s" access="read"/> + <property name="HandleLidSwitch" type="s" access="read"/> + <property name="IdleAction" type="s" access="read"/> + <property name="IdleActionUSec" type="t" access="read"/> + <property name="PreparingForShutdown" type="b" access="read"/> + <property name="PreparingForSleep" type="b" access="read"/> + </interface> +</node> diff --git a/src/backend-dbus/org.freedesktop.login1.Seat.xml b/src/backend-dbus/org.freedesktop.login1.Seat.xml new file mode 100644 index 0000000..b73f724 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.login1.Seat.xml @@ -0,0 +1,21 @@ +<?xml version="1.0"?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.login1.Seat"> + <method name="Terminate"/> + <method name="ActivateSession"> + <arg name="id" direction="in" type="s"/> + </method> + <property name="Id" type="s" access="read"/> + <property name="ActiveSession" type="(so)" access="read"/> + <property name="CanMultiSession" type="b" access="read"/> + <property name="CanTTY" type="b" access="read"/> + <property name="CanGraphical" type="b" access="read"/> + <property name="Sessions" type="a(so)" access="read"> + <annotation name="org.qtproject.QtDBus.QtTypeName" value="StringPathList"/> + </property> + <property name="IdleHint" type="b" access="read"/> + <property name="IdleSinceHint" type="t" access="read"/> + <property name="IdleSinceHintMonotonic" type="t" access="read"/> + </interface> +</node> diff --git a/src/backend-dbus/org.freedesktop.login1.User.xml b/src/backend-dbus/org.freedesktop.login1.User.xml new file mode 100644 index 0000000..8253706 --- /dev/null +++ b/src/backend-dbus/org.freedesktop.login1.User.xml @@ -0,0 +1,56 @@ +<?xml version="1.0"?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node> + <interface name="org.freedesktop.login1.User"> + <method name="Terminate"/> + <method name="Kill"> + <arg name="signal" direction="in" type="s"/> + </method> + <property name="UID" type="u" access="read"/> + <property name="GID" type="u" access="read"/> + <property name="Name" type="s" access="read"/> + <property name="Timestamp" type="t" access="read"/> + <property name="TimestampMonotonic" type="t" access="read"/> + <property name="RuntimePath" type="s" access="read"/> + <property name="DefaultControlGroup" type="s" access="read"/> + <property name="Service" type="s" access="read"/> + <property name="Display" type="(so)" access="read"/> + <property name="State" type="s" access="read"/> + <property name="Sessions" type="a(so)" access="read"/> + <property name="IdleHint" type="b" access="read"/> + <property name="IdleSinceHint" type="t" access="read"/> + <property name="IdleSinceHintMonotonic" type="t" access="read"/> + </interface> + <interface name="org.freedesktop.DBus.Properties"> + <method name="Get"> + <arg name="interface" direction="in" type="s"/> + <arg name="property" direction="in" type="s"/> + <arg name="value" direction="out" type="v"/> + </method> + <method name="GetAll"> + <arg name="interface" direction="in" type="s"/> + <arg name="properties" direction="out" type="a{sv}"/> + </method> + <method name="Set"> + <arg name="interface" direction="in" type="s"/> + <arg name="property" direction="in" type="s"/> + <arg name="value" direction="in" type="v"/> + </method> + <signal name="PropertiesChanged"> + <arg type="s" name="interface"/> + <arg type="a{sv}" name="changed_properties"/> + <arg type="as" name="invalidated_properties"/> + </signal> + </interface> +<interface name="org.freedesktop.DBus.Peer"> + <method name="Ping"/> + <method name="GetMachineId"> + <arg type="s" name="machine_uuid" direction="out"/> + </method> +</interface> + <interface name="org.freedesktop.DBus.Introspectable"> + <method name="Introspect"> + <arg name="data" type="s" direction="out"/> + </method> + </interface> +</node> diff --git a/src/backend-dbus/org.gnome.ScreenSaver.xml b/src/backend-dbus/org.gnome.ScreenSaver.xml new file mode 100644 index 0000000..c21fdc5 --- /dev/null +++ b/src/backend-dbus/org.gnome.ScreenSaver.xml @@ -0,0 +1,16 @@ +<!DOCTYPE node PUBLIC +"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" +"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + + <interface name="org.gnome.ScreenSaver"> + + <method name="Lock"> + </method> + + <method name="SimulateUserActivity"> + </method> + + </interface> + +</node> diff --git a/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml new file mode 100644 index 0000000..5392de0 --- /dev/null +++ b/src/backend-dbus/org.gnome.SessionManager.EndSessionDialog.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.gnome.SessionManager.EndSessionDialog"> + <method name="Open"> + <arg type="u" name="type" direction="in"> + <doc:doc> + <doc:summary> + The type of dialog to show. + 0 for logout, 1 for shutdown, 2 for restart. + </doc:summary> + </doc:doc> + </arg> + <arg type="u" name="timestamp" direction="in"> + <doc:doc> + <doc:summary> + Timestamp of the user-initiated event which triggered + the call, or 0 if the call was not triggered by an event. + </doc:summary> + </doc:doc> + </arg> + <arg type="u" name="seconds_to_stay_open" direction="in"> + <doc:doc> + <doc:summary> + The number of seconds which the dialog should stay open + before automatic action is taken. + </doc:summary> + </doc:doc> + </arg> + <arg type="ao" name="inhibitor_object_paths" direction="in"> + <doc:doc> + <doc:summary> + The object paths of all inhibitors that are registered + for the action. + </doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:summary> + This function opens a dialog which asks the user for confirmation + of a logout, poweroff or reboot action. The dialog has a timeout + after which the action is automatically taken, and it should show + the inhibitors to the user. + </doc:summary> + </doc:doc> + </method> + <signal name="ConfirmedLogout" /> + <signal name="ConfirmedReboot" /> + <signal name="ConfirmedShutdown" /> + <signal name="Canceled" /> + <signal name="Closed" /> + </interface> +</node> diff --git a/src/backend-dbus/org.gnome.SessionManager.xml b/src/backend-dbus/org.gnome.SessionManager.xml new file mode 100644 index 0000000..eb69180 --- /dev/null +++ b/src/backend-dbus/org.gnome.SessionManager.xml @@ -0,0 +1,451 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd"> + <interface name="org.gnome.SessionManager"> + + <!-- Initialization phase interfaces --> + + <method name="Setenv"> + <arg name="variable" type="s" direction="in"> + <doc:doc> + <doc:summary>The variable name</doc:summary> + </doc:doc> + </arg> + <arg name="value" type="s" direction="in"> + <doc:doc> + <doc:summary>The value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Adds the variable name to the application launch environment with the specified value. May only be used during the Session Manager initialization phase.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="GetLocale"> + <arg name="category" type="i" direction="in"> + <doc:doc> + <doc:summary>The locale category</doc:summary> + </doc:doc> + </arg> + <arg name="value" type="s" direction="out"> + <doc:doc> + <doc:summary>The value</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Reads the current state of the specific locale category.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="InitializationError"> + <arg name="message" type="s" direction="in"> + <doc:doc> + <doc:summary>The error message</doc:summary> + </doc:doc> + </arg> + <arg name="fatal" type="b" direction="in"> + <doc:doc> + <doc:summary>Whether the error should be treated as fatal</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>May be used by applications launched during the Session Manager initialization phase to indicate there was a problem.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Running phase interfaces --> + + <method name="RegisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="client_startup_id" direction="in"> + <doc:doc> + <doc:summary>Client startup identifier</doc:summary> + </doc:doc> + </arg> + <arg type="o" name="client_id" direction="out"> + <doc:doc> + <doc:summary>The object path of the newly registered client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Register the caller as a Session Management client.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="UnregisterClient"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="o" name="client_id" direction="in"> + <doc:doc> + <doc:summary>The object path of the client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Unregister the specified client from Session Management.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Inhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="s" name="app_id" direction="in"> + <doc:doc> + <doc:summary>The application identifier</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="toplevel_xid" direction="in"> + <doc:doc> + <doc:summary>The toplevel X window identifier</doc:summary> + </doc:doc> + </arg> + <arg type="s" name="reason" direction="in"> + <doc:doc> + <doc:summary>The reason for the inhibit</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that specify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="u" name="inhibit_cookie" direction="out"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:summary> + Proactively indicates that the calling application is performing an action that should not be interrupted and sets a reason to be displayed to the user when an interruption is about to take placea. + </doc:summary> + <doc:description> + <doc:para>Applications should invoke this method when they begin an operation that + should not be interrupted, such as creating a CD or DVD. The types of actions + that may be blocked are specified by the flags parameter. When the application + completes the operation it should call <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref> + or disconnect from the session bus. + </doc:para> + <doc:para> + Applications should not expect that they will always be able to block the + action. In most cases, users will be given the option to force the action + to take place. + </doc:para> + <doc:para> + Reasons should be short and to the point. + </doc:para> + <doc:para> + The flags parameter must include at least one of the following: + <doc:list> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>Inhibit logging out</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Inhibit user switching</doc:definition> + </doc:item> + <doc:item> + <doc:term>4</doc:term> + <doc:definition>Inhibit suspending the session or computer</doc:definition> + </doc:item> + <doc:item> + <doc:term>8</doc:term> + <doc:definition>Inhibit the session being marked as idle</doc:definition> + </doc:item> + <doc:item> + <doc:term>16</doc:term> + <doc:definition>Inhibit auto-mounting removable media for the session</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + <doc:para> + The returned cookie is used to uniquely identify this request. It should be used + as an argument to <doc:ref type="method" to="org.gnome.SessionManager.Uninhibit">Uninhibit()</doc:ref> in + order to remove the request. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Uninhibit"> + <annotation name="org.freedesktop.DBus.GLib.Async" value=""/> + <arg type="u" name="inhibit_cookie" direction="in"> + <doc:doc> + <doc:summary>The cookie</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Cancel a previous call to <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref> identified by the cookie.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="IsInhibited"> + <arg type="u" name="flags" direction="in"> + <doc:doc> + <doc:summary>Flags that spefify what should be inhibited</doc:summary> + </doc:doc> + </arg> + <arg type="b" name="is_inhibited" direction="out"> + <doc:doc> + <doc:summary>Returns TRUE if any of the operations in the bitfield flags are inhibited</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Determine if operation(s) specified by the flags + are currently inhibited. Flags are same as those accepted + by the + <doc:ref type="method" to="org.gnome.SessionManager.Inhibit">Inhibit()</doc:ref> + method.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="GetClients"> + <arg name="clients" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of client IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Client">Clients</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Client ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.gnome.SessionManager.Client">Client</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Client">org.gnome.SessionManager.Client</doc:ref></doc:seealso> + </doc:doc> + </method> + + <method name="GetInhibitors"> + <arg name="inhibitors" direction="out" type="ao"> + <doc:doc> + <doc:summary>an array of inhibitor IDs</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>This gets a list of all the <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitors</doc:ref> + that are currently known to the session manager.</doc:para> + <doc:para>Each Inhibitor ID is an D-Bus object path for the object that implements the + <doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">Inhibitor</doc:ref> interface.</doc:para> + </doc:description> + <doc:seealso><doc:ref type="interface" to="org.gnome.SessionManager.Inhibitor">org.gnome.SessionManager.Inhibitor</doc:ref></doc:seealso> + </doc:doc> + </method> + + + <method name="IsAutostartConditionHandled"> + <arg name="condition" direction="in" type="s"> + <doc:doc> + <doc:summary>The autostart condition string</doc:summary> + </doc:doc> + </arg> + <arg name="handled" direction="out" type="b"> + <doc:doc> + <doc:summary>True if condition is handled, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether the session manager is + handling changes to the specified autostart condition.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Shutdown"> + <doc:doc> + <doc:description> + <doc:para>Request a shutdown dialog.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Reboot"> + <doc:doc> + <doc:description> + <doc:para>Request a reboot dialog.</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="CanShutdown"> + <arg name="is_available" direction="out" type="b"> + <doc:doc> + <doc:summary>True if shutdown is available to the user, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether or not it's okay to show + a shutdown option in the UI</doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="Logout"> + <arg name="mode" type="u" direction="in"> + <doc:doc> + <doc:summary>The type of logout that is being requested</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Request a logout dialog</doc:para> + <doc:para> + Allowed values for the mode parameter are: + <doc:list> + <doc:item> + <doc:term>0</doc:term> + <doc:definition>Normal.</doc:definition> + </doc:item> + <doc:item> + <doc:term>1</doc:term> + <doc:definition>No confirmation inferface should be shown.</doc:definition> + </doc:item> + <doc:item> + <doc:term>2</doc:term> + <doc:definition>Forcefully logout. No confirmation will be shown and any inhibitors will be ignored.</doc:definition> + </doc:item> + </doc:list> + Values for flags may be bitwise or'ed together. + </doc:para> + </doc:description> + </doc:doc> + </method> + + <method name="IsSessionRunning"> + <arg name="running" direction="out" type="b"> + <doc:doc> + <doc:summary>True if the session has entered the Running phase, false otherwise</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Allows the caller to determine whether the session manager + has entered the Running phase, in case the client missed the + SessionRunning signal.</doc:para> + </doc:description> + </doc:doc> + </method> + + <!-- Signals --> + + <signal name="ClientAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="ClientRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed client</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when a client has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="InhibitorAdded"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the added inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been added to the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + <signal name="InhibitorRemoved"> + <arg name="id" type="o"> + <doc:doc> + <doc:summary>The object path for the removed inhibitor</doc:summary> + </doc:doc> + </arg> + <doc:doc> + <doc:description> + <doc:para>Emitted when an inhibitor has been removed from the session manager. + </doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionRunning"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session has entered the Running phase.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <signal name="SessionOver"> + <doc:doc> + <doc:description> + <doc:para>Indicates the session is about to end.</doc:para> + </doc:description> + </doc:doc> + </signal> + + <!-- Properties --> + + <property name="SessionName" type="s" access="read"> + <doc:doc> + <doc:description> + <doc:para>The name of the session that has been loaded.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="SessionIsActive" type="b" access="read"> + <doc:doc> + <doc:description> + <doc:para>If true, the session is currently in the + foreground and available for user input.</doc:para> + </doc:description> + </doc:doc> + </property> + + <property name="InhibitedActions" type="u" access="read"> + <doc:doc> + <doc:description> + <doc:para>A bitmask of flags to indicate which actions + are inhibited. See the Inhibit() function's description + for a list of possible values.</doc:para> + </doc:description> + </doc:doc> + </property> + + </interface> +</node> diff --git a/src/backend-dbus/users.c b/src/backend-dbus/users.c new file mode 100644 index 0000000..f770695 --- /dev/null +++ b/src/backend-dbus/users.c @@ -0,0 +1,706 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#include "dbus-user.h" + +#include "users.h" + +struct _IndicatorSessionUsersDbusPriv +{ + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + Accounts * accounts; + + /* hash table of int uids to UserRecord* */ + GHashTable * uid_to_account; + + /* a hashset of int uids of users who are logged in */ + GHashTable * logins; + + /* the user-id of the owner of the active session */ + guint active_uid; + + /* true if this is a live session */ + gboolean is_live; + + GCancellable * cancellable; + + guint update_list_tag; +}; + +typedef IndicatorSessionUsersDbusPriv priv_t; + +G_DEFINE_TYPE (IndicatorSessionUsersDbus, + indicator_session_users_dbus, + INDICATOR_TYPE_SESSION_USERS) + +/*** +**** +***/ + +struct UserRecord +{ + AccountsUser * user; + + gulong signal_id; +}; + +struct UserRecord * +user_record_new (AccountsUser * user, gulong signal_id) +{ + struct UserRecord * rec; + rec = g_new (struct UserRecord, 1); + rec->user = g_object_ref (user); + rec->signal_id = signal_id; + return rec; +} + +static void +user_record_free (struct UserRecord * rec) +{ + g_signal_handler_disconnect (rec->user, rec->signal_id); + g_object_unref (G_OBJECT (rec->user)); + g_free (rec); +} + +/*** +**** +***/ + +/* get our private org.freedesktop.Accounts.User proxy for the given uid */ +static AccountsUser * +get_user_for_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + struct UserRecord * rec; + + if ((rec = g_hash_table_lookup (self->priv->uid_to_account, + GUINT_TO_POINTER(uid)))) + return rec->user; + + return NULL; +} + +static gboolean +is_tracked_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + return get_user_for_uid (self, uid) != NULL; +} + +static void +emit_user_added (IndicatorSessionUsersDbus * self, guint32 uid) +{ + if (is_tracked_uid (self, uid)) + indicator_session_users_added (INDICATOR_SESSION_USERS(self), uid); +} + +static void +emit_user_changed (IndicatorSessionUsersDbus * self, guint32 uid) +{ + if (is_tracked_uid (self, uid)) + indicator_session_users_changed (INDICATOR_SESSION_USERS(self), uid); +} + +static void +emit_user_removed (IndicatorSessionUsersDbus * self, guint32 uid) +{ + indicator_session_users_removed (INDICATOR_SESSION_USERS(self), uid); +} + +/*** +**** +***/ + +static void +set_is_live_session_flag (IndicatorSessionUsersDbus * self, gboolean b) +{ + priv_t * p = self->priv; + + if (p->is_live != b) + { + p->is_live = b; + + indicator_session_users_notify_is_live_session (INDICATOR_SESSION_USERS (self)); + } +} + +static void +set_active_uid (IndicatorSessionUsersDbus * self, guint uid) +{ + priv_t * p = self->priv; + + if (p->active_uid != uid) + { + const guint old_uid = p->active_uid; + + p->active_uid = uid; + + emit_user_changed (self, old_uid); + emit_user_changed (self, uid); + } +} + +static void +set_logins (IndicatorSessionUsersDbus * self, GHashTable * logins) +{ + GHashTable * old_logins = self->priv->logins; + gpointer uid; + GHashTableIter iter; + + self->priv->logins = g_hash_table_ref (logins); + + /* fire 'user changed' event for users who logged out */ + g_hash_table_iter_init (&iter, old_logins); + while ((g_hash_table_iter_next (&iter, &uid, NULL))) + if (!g_hash_table_contains (logins, uid)) + emit_user_changed (self, GPOINTER_TO_UINT(uid)); + + /* fire 'user changed' event for users who logged in */ + g_hash_table_iter_init (&iter, logins); + while ((g_hash_table_iter_next (&iter, &uid, NULL))) + if (!g_hash_table_contains (old_logins, uid)) + emit_user_changed (self, GPOINTER_TO_UINT(uid)); + + g_hash_table_destroy (old_logins); +} + +/*** +**** User Account Tracking +***/ + +static void create_user_proxy_for_path (IndicatorSessionUsersDbus *, const char *); + +/* called when a user proxy gets the 'Changed' signal */ +static void +on_user_changed (AccountsUser * user, gpointer gself) +{ + /* Accounts.User doesn't update properties in the standard way, + * so create a new proxy to pull in the new properties. + * The older proxy is freed when it's replaced in our accounts hash */ + const char * path = g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)); + create_user_proxy_for_path (gself, path); +} + +static void +track_user (IndicatorSessionUsersDbus * self, + AccountsUser * user) +{ + const guint32 uid = accounts_user_get_uid (user); + priv_t * p = self->priv; + gulong id; + const gboolean already_had_user = is_tracked_uid (self, uid); + + id = g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self); + g_hash_table_insert (p->uid_to_account, + GUINT_TO_POINTER (uid), + user_record_new (user, id)); + + if (already_had_user) + emit_user_changed (self, uid); + else + emit_user_added (self, uid); +} + +static void +untrack_user (IndicatorSessionUsersDbus * self, + const gchar * path) +{ + guint uid; + gpointer key; + gpointer val; + GHashTableIter iter; + priv_t * p = self->priv; + + /* find the uid matching this object path */ + uid = 0; + g_hash_table_iter_init (&iter, p->uid_to_account); + while (!uid && g_hash_table_iter_next (&iter, &key, &val)) + { + struct UserRecord * rec = val; + GDBusProxy * proxy = G_DBUS_PROXY (rec->user); + if (!g_strcmp0 (path, g_dbus_proxy_get_object_path (proxy))) + uid = GPOINTER_TO_UINT (key); + } + + if (uid) + { + g_hash_table_remove (p->uid_to_account, GUINT_TO_POINTER(uid)); + + emit_user_removed (self, uid); + } +} + +/* We got a new org.freedesktop.Accounts.User proxy. + If it's one we want to remember, pass it to track_user() */ +static void +on_user_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer self) +{ + GError * err; + AccountsUser * user; + + err = NULL; + user = accounts_user_proxy_new_for_bus_finish (res, &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + if (!accounts_user_get_system_account (user)) + track_user (self, user); + + g_object_unref (user); + } +} + +static void +create_user_proxy_for_path (IndicatorSessionUsersDbus * self, + const char * path) +{ + accounts_user_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.Accounts", + path, + self->priv->cancellable, + on_user_proxy_ready, self); +} + +/* create proxy objects for everything in Account's user-list */ +static void +on_user_list_ready (GObject * o, GAsyncResult * res, gpointer gself) +{ + GError * err; + gchar ** paths; + + err = NULL; + paths = NULL; + accounts_call_list_cached_users_finish (ACCOUNTS(o), &paths, res, &err); + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + int i; + + for (i=0; paths && paths[i]; ++i) + create_user_proxy_for_path (gself, paths[i]); + + g_strfreev (paths); + } +} + +static void +set_account_manager (IndicatorSessionUsersDbus * self, Accounts * a) +{ + priv_t * p = self->priv; + + if (p->accounts != NULL) + { + g_signal_handlers_disconnect_by_data (p->accounts, self); + g_clear_object (&p->accounts); + } + + if (a != NULL) + { + p->accounts = g_object_ref (a); + + accounts_call_list_cached_users (a, + self->priv->cancellable, + on_user_list_ready, + self); + + g_signal_connect_swapped (a, "user-added", + G_CALLBACK(create_user_proxy_for_path), self); + + g_signal_connect_swapped (a, "user-deleted", + G_CALLBACK(untrack_user), self); + } +} + +/*** +**** +***/ + +/* Based on the login1 manager's list of current sessions, + update our 'logins', 'is_live', and 'active_uid' fields */ +static void +on_login1_manager_session_list_ready (GObject * o, + GAsyncResult * res, + gpointer gself) +{ + GVariant * sessions; + GError * err; + + sessions = NULL; + err = NULL; + login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o), + &sessions, + res, + &err); + + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s: %s", G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + const gchar * const current_seat_id = g_getenv ("XDG_SEAT"); + const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID"); + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself); + const gchar * session_id = NULL; + guint32 uid = 0; + const gchar * user_name = NULL; + const gchar * seat_id = NULL; + const gchar * path = NULL; + gboolean is_live_session = FALSE; + GHashTable * logins = g_hash_table_new (g_direct_hash, g_direct_equal); + GVariantIter iter; + + g_variant_iter_init (&iter, sessions); + while (g_variant_iter_loop (&iter, "(&su&s&s&o)", &session_id, + &uid, + &user_name, + &seat_id, + &path)) + { + /* only track sessions on our seat */ + if (g_strcmp0 (seat_id, current_seat_id)) + continue; + + if (!g_strcmp0 (session_id, current_session_id)) + { + set_active_uid (self, uid); + + if ((uid==999) && !g_strcmp0 (user_name, "ubuntu")) + is_live_session = TRUE; + } + + g_hash_table_add (logins, GINT_TO_POINTER(uid)); + } + + set_is_live_session_flag (self, is_live_session); + set_logins (self, logins); + + g_hash_table_unref (logins); + g_variant_unref (sessions); + } +} + +static void +update_session_list (IndicatorSessionUsersDbus * self) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + login1_manager_call_list_sessions (p->login1_manager, + p->cancellable, + on_login1_manager_session_list_ready, + self); + } +} + +static gboolean +on_update_session_list_timer (gpointer gself) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself); + + update_session_list (self); + + self->priv->update_list_tag = 0; + return G_SOURCE_REMOVE; +} + +/* A dead session can still show up in list-sessions for a few seconds. + So just to be safe, queue up a rebuild for a few seconds from now */ +static void +update_session_list_twice (IndicatorSessionUsersDbus * self) +{ + priv_t * p = self->priv; + + update_session_list (self); + + if (p->update_list_tag == 0) + p->update_list_tag = g_timeout_add_seconds (5, + on_update_session_list_timer, + self); +} + +static void +set_login1_manager (IndicatorSessionUsersDbus * self, + Login1Manager * login1_manager) +{ + priv_t * p = self->priv; + + if (p->login1_manager != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_manager, self); + + g_clear_object (&p->login1_manager); + } + + if (login1_manager != NULL) + { + p->login1_manager = g_object_ref (login1_manager); + + g_signal_connect_swapped (login1_manager, "session-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "session-removed", + G_CALLBACK(update_session_list_twice), self); + g_signal_connect_swapped (login1_manager, "user-new", + G_CALLBACK(update_session_list), self); + g_signal_connect_swapped (login1_manager, "user-removed", + G_CALLBACK(update_session_list_twice), self); + update_session_list (self); + } +} + +static void +set_login1_seat (IndicatorSessionUsersDbus * self, + Login1Seat * login1_seat) +{ + priv_t * p = self->priv; + + if (p->login1_seat != NULL) + { + g_signal_handlers_disconnect_by_data (p->login1_seat, self); + + g_clear_object (&p->login1_seat); + } + + if (login1_seat != NULL) + { + p->login1_seat = g_object_ref (login1_seat); + + g_signal_connect_swapped (login1_seat, "notify::active-session", + G_CALLBACK(update_session_list), self); + update_session_list (self); + } +} + +static void +set_display_manager_seat (IndicatorSessionUsersDbus * self, + DisplayManagerSeat * dm_seat) +{ + priv_t * p = self->priv; + + g_clear_object (&p->dm_seat); + + if (dm_seat != NULL) + p->dm_seat = g_object_ref (dm_seat); +} + +/*** +**** IndicatorSessionUsers virtual functions +***/ + +/* switch to (or create) a session for the specified user */ +static void +my_activate_user (IndicatorSessionUsers * users, guint uid) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS(users); + priv_t * p = self->priv; + AccountsUser * au; + const char * username; + + au = get_user_for_uid (self, uid); + username = au ? accounts_user_get_user_name (au) : NULL; + + if (!username) + { + g_warning ("%s %s can't find user '%u'", G_STRLOC, G_STRFUNC, uid); + } + else + { + g_return_if_fail (p->dm_seat != NULL); + + display_manager_seat_call_switch_to_user (p->dm_seat, + username, + "", + p->cancellable, + NULL, + NULL); + } +} + +/* returns true if this is a live session */ +static gboolean +my_is_live_session (IndicatorSessionUsers * users) +{ + g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_DBUS(users), FALSE); + + return INDICATOR_SESSION_USERS_DBUS(users)->priv->is_live; +} + +/* get a list of our user ids */ +static GList * +my_get_uids (IndicatorSessionUsers * users) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users); + return g_hash_table_get_keys (self->priv->uid_to_account); +} + +/* build a new struct populated with info on the specified user */ +static IndicatorSessionUser * +my_get_user (IndicatorSessionUsers * users, guint uid) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users); + priv_t * p = self->priv; + IndicatorSessionUser * ret; + AccountsUser * au; + + ret = NULL; + au = get_user_for_uid (self, uid); + if (au && !accounts_user_get_system_account(au)) + { + g_assert (uid == accounts_user_get_uid (au)); + + ret = g_new0 (IndicatorSessionUser, 1); + ret->uid = uid; + ret->user_name = g_strdup (accounts_user_get_user_name (au)); + ret->real_name = g_strdup (accounts_user_get_real_name (au)); + ret->icon_file = g_strdup (accounts_user_get_icon_file (au)); + ret->login_frequency = accounts_user_get_login_frequency (au); + ret->is_logged_in = g_hash_table_contains (p->logins, GINT_TO_POINTER(uid)); + ret->is_current_user = uid == p->active_uid; + } + + return ret; +} + +/*** +**** GObject virtual functions +***/ + +static void +my_dispose (GObject * o) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o); + priv_t * p = self->priv; + + if (p->update_list_tag != 0) + { + g_source_remove (p->update_list_tag); + p->update_list_tag = 0; + } + + if (p->cancellable) + { + g_cancellable_cancel (p->cancellable); + g_clear_object (&p->cancellable); + } + + set_account_manager (self, NULL); + set_display_manager_seat (self, NULL); + set_login1_seat (self, NULL); + set_login1_manager (self, NULL); + + g_hash_table_remove_all (p->uid_to_account); + + G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->dispose (o); +} + +static void +my_finalize (GObject * o) +{ + IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o); + priv_t * p = self->priv; + + g_hash_table_destroy (p->logins); + g_hash_table_destroy (p->uid_to_account); + + G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->finalize (o); +} + +static void +indicator_session_users_dbus_class_init (IndicatorSessionUsersDbusClass * klass) +{ + GObjectClass * object_class; + IndicatorSessionUsersClass * users_class; + + object_class = G_OBJECT_CLASS (klass); + object_class->dispose = my_dispose; + object_class->finalize = my_finalize; + + users_class = INDICATOR_SESSION_USERS_CLASS (klass); + users_class->is_live_session = my_is_live_session; + users_class->get_uids = my_get_uids; + users_class->get_user = my_get_user; + users_class->activate_user = my_activate_user; + + g_type_class_add_private (klass, sizeof (IndicatorSessionUsersDbusPriv)); +} + +static void +indicator_session_users_dbus_init (IndicatorSessionUsersDbus * self) +{ + priv_t * p; + + p = G_TYPE_INSTANCE_GET_PRIVATE (self, + INDICATOR_TYPE_SESSION_USERS_DBUS, + IndicatorSessionUsersDbusPriv); + self->priv = p; + p->cancellable = g_cancellable_new (); + + p->uid_to_account = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify)user_record_free); + + p->logins = g_hash_table_new (g_direct_hash, g_direct_equal); +} + +/*** +**** Public +***/ + +IndicatorSessionUsers * +indicator_session_users_dbus_new (void) +{ + gpointer o = g_object_new (INDICATOR_TYPE_SESSION_USERS_DBUS, NULL); + + return INDICATOR_SESSION_USERS (o); +} + +void +indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus * self, + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * dm_seat, + Accounts * accounts) +{ + g_return_if_fail (INDICATOR_IS_SESSION_USERS_DBUS (self)); + + set_login1_manager (self, login1_manager); + set_login1_seat (self, login1_seat); + set_display_manager_seat (self, dm_seat); + set_account_manager (self, accounts); +} diff --git a/src/backend-dbus/users.h b/src/backend-dbus/users.h new file mode 100644 index 0000000..d6c17df --- /dev/null +++ b/src/backend-dbus/users.h @@ -0,0 +1,73 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __USERS_DBUS_H__ +#define __USERS_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "../users.h" /* parent class */ +#include "dbus-accounts.h" +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" +#include "dbus-display-manager.h" + +G_BEGIN_DECLS + +#define INDICATOR_TYPE_SESSION_USERS_DBUS (indicator_session_users_dbus_get_type()) +#define INDICATOR_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbus)) +#define INDICATOR_SESSION_USERS_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), INDICATOR_TYPE_SESSION_USERS_DBUS, IndicatorSessionUsersDbusClass)) +#define INDICATOR_IS_SESSION_USERS_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), INDICATOR_TYPE_SESSION_USERS_DBUS)) + +typedef struct _IndicatorSessionUsersDbus IndicatorSessionUsersDbus; +typedef struct _IndicatorSessionUsersDbusPriv IndicatorSessionUsersDbusPriv; +typedef struct _IndicatorSessionUsersDbusClass IndicatorSessionUsersDbusClass; + +/** + * An implementation of IndicatorSessionUsers that gets its user information + * from org.freedesktop.login1 and org.freedesktop.Accounts over DBus. + */ +struct _IndicatorSessionUsersDbus +{ + /*< private >*/ + IndicatorSessionUsers parent; + IndicatorSessionUsersDbusPriv * priv; +}; + +struct _IndicatorSessionUsersDbusClass +{ + IndicatorSessionUsersClass parent_class; +}; + +GType indicator_session_users_dbus_get_type (void); + +IndicatorSessionUsers * indicator_session_users_dbus_new (void); + +void indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus *, + Login1Manager *, + Login1Seat *, + DisplayManagerSeat *, + Accounts *); + + + +G_END_DECLS + +#endif diff --git a/src/backend-dbus/utils.c b/src/backend-dbus/utils.c new file mode 100644 index 0000000..bc862e8 --- /dev/null +++ b/src/backend-dbus/utils.c @@ -0,0 +1,182 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#include "utils.h" + +/*** +**** indicator_session_util_get_session_proxies() +***/ + +struct session_proxy_data +{ + Login1Manager * login1_manager; + Login1Seat * login1_seat; + DisplayManagerSeat * dm_seat; + Accounts * account_manager; + + GCancellable * cancellable; + int pending; + + indicator_session_util_session_proxies_func callback; + gpointer user_data; +}; + + +static void +on_proxy_ready_impl (struct session_proxy_data * data, + gsize member_offset, + GError * err, + gpointer proxy) +{ + if (err != NULL) + { + if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message); + + g_error_free (err); + } + else + { + *((gpointer*)G_STRUCT_MEMBER_P(data, member_offset)) = proxy; + } + + if (!--data->pending) + { + data->callback (data->login1_manager, + data->login1_seat, + data->dm_seat, + data->account_manager, + data->cancellable, + data->user_data); + + g_clear_object (&data->login1_manager); + g_clear_object (&data->login1_seat); + g_clear_object (&data->dm_seat); + g_clear_object (&data->account_manager); + g_clear_object (&data->cancellable); + g_free (data); + } +} + +static void +on_display_manager_seat_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, dm_seat); + GError * err = NULL; + gpointer proxy = display_manager_seat_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_login1_seat_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_seat); + GError * err = NULL; + gpointer proxy = login1_seat_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_login1_manager_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, login1_manager); + GError * err = NULL; + gpointer proxy = login1_manager_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +static void +on_accounts_proxy_ready (GObject * o G_GNUC_UNUSED, + GAsyncResult * res, + gpointer gdata) +{ + gsize offset = G_STRUCT_OFFSET (struct session_proxy_data, account_manager); + GError * err = NULL; + gpointer proxy = accounts_proxy_new_for_bus_finish (res, &err); + on_proxy_ready_impl (gdata, offset, err, proxy); +} + +/* helper utility to get the dbus proxies used by the backend-dbus classes */ +void +indicator_session_util_get_session_proxies ( + indicator_session_util_session_proxies_func func, + GCancellable * cancellable, + gpointer user_data) +{ + struct session_proxy_data * data; + const char * str; + + data = g_new0 (struct session_proxy_data, 1); + data->callback = func; + data->user_data = user_data; + data->cancellable = g_object_ref (cancellable); + + /* login1 */ + data->pending++; + login1_manager_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.login1", + "/org/freedesktop/login1", + data->cancellable, + on_login1_manager_ready, data); + + /* login1 seat */ + if ((str = g_getenv ("XDG_SEAT"))) + { + char * path; + data->pending++; + path = g_strconcat ("/org/freedesktop/login1/seat/", str, NULL); + login1_seat_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.login1", + path, + data->cancellable, + on_login1_seat_ready, + data); + g_free (path); + } + + /* Accounts */ + data->pending++; + accounts_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.Accounts", + "/org/freedesktop/Accounts", + data->cancellable, + on_accounts_proxy_ready, data); + + /* DisplayManager seat */ + if ((str = g_getenv ("XDG_SEAT_PATH"))) + { + data->pending++; + display_manager_seat_proxy_new_for_bus ( + G_BUS_TYPE_SYSTEM, + G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES, + "org.freedesktop.DisplayManager", + str, + data->cancellable, + on_display_manager_seat_proxy_ready, data); + } +} diff --git a/src/backend-dbus/utils.h b/src/backend-dbus/utils.h new file mode 100644 index 0000000..802dd5e --- /dev/null +++ b/src/backend-dbus/utils.h @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Canonical Ltd. + * + * Authors: + * Charles Kerr <charles.kerr@canonical.com> + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 3, as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranties of + * MERCHANTABILITY, SATISFACTORY QUALITY, 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __DBUS_UTILS_H__ +#define __DBUS_UTILS_H__ + +#include <glib.h> +#include <glib-object.h> + +#include "dbus-accounts.h" +#include "dbus-display-manager.h" +#include "dbus-login1-manager.h" +#include "dbus-login1-seat.h" + +typedef void (*indicator_session_util_session_proxies_func)( + Login1Manager * login1_manager, + Login1Seat * login1_seat, + DisplayManagerSeat * display_manager_seat, + Accounts * account_manager, + GCancellable * cancellable, + gpointer user_data); + +/** + * Both users-dbus and guest-dbus need some of these proxies. + * Getting them all involves a lot of steps, so instead of repeating + * ourselves, the common dbus steps are extracted to this func. + */ +void indicator_session_util_get_session_proxies ( + indicator_session_util_session_proxies_func func, + GCancellable * cancellable, + gpointer user_data); + +#endif |