From 31fdf9eb550e0f2e69fa1ba96a1d7d78b8d2e060 Mon Sep 17 00:00:00 2001 From: Stefano Karapetsas Date: Tue, 5 Mar 2013 20:39:33 +0100 Subject: Add initial systemd support --- configure.ac | 26 +- mate-session/Makefile.am | 6 +- mate-session/gsm-logout-dialog.c | 60 ++- mate-session/gsm-manager.c | 264 +++++++++++-- mate-session/gsm-systemd.c | 776 +++++++++++++++++++++++++++++++++++++++ mate-session/gsm-systemd.h | 104 ++++++ mate-session/main.c | 41 ++- 7 files changed, 1238 insertions(+), 39 deletions(-) create mode 100644 mate-session/gsm-systemd.c create mode 100644 mate-session/gsm-systemd.h diff --git a/configure.ac b/configure.ac index d450884..26025e3 100644 --- a/configure.ac +++ b/configure.ac @@ -104,6 +104,29 @@ dnl GSettings dnl ==================================================================== GLIB_GSETTINGS + +dnl ==================================================================== +dnl Systemd +dnl ==================================================================== + +AC_ARG_WITH(systemd, + AS_HELP_STRING([--with-systemd], + [Add systemd support]),, + with_systemd=auto) + +use_systemd=no +if test "x$with_systemd" != "xno" ; then + PKG_CHECK_MODULES(SYSTEMD, libsystemd-login libsystemd-daemon, use_systemd=yes, use_systemd=no) + + if test "x$use_systemd" = "xyes"; then + AC_DEFINE(HAVE_SYSTEMD, 1, [systemd support]) + AC_SUBST(SYSTEMD_CFLAGS) + AC_SUBST(SYSTEMD_LIBS) + fi +fi +AM_CONDITIONAL(HAVE_SYSTEMD, test "x$use_systemd" = "xyes") +AC_SUBST(HAVE_SYSTEMD) + dnl ==================================================================== dnl X development libraries check dnl ==================================================================== @@ -137,7 +160,7 @@ fi AC_CHECK_LIB(Xau, XauFileName, [X_LIBS="$X_LIBS -lXau"], [AC_MSG_ERROR([ -*** Can't find the Xauth library. It is needed to compile mate-session.])], +*** Cant find the Xauth library. It is needed to compile mate-session.])], $X_LIBS) AC_SUBST(X_LIBS) @@ -361,6 +384,7 @@ echo " GTK+ version: ${with_gtk} Default WM: ${with_default_wm} + Systemd support: ${use_systemd} IPv6 support: ${have_full_ipv6} Backtrace support: ${have_backtrace} XRender support: ${have_xrender} diff --git a/mate-session/Makefile.am b/mate-session/Makefile.am index b2ddcbd..f37a0e2 100644 --- a/mate-session/Makefile.am +++ b/mate-session/Makefile.am @@ -5,7 +5,8 @@ noinst_PROGRAMS = \ test-inhibit AM_CPPFLAGS = \ - $(MATE_SESSION_CFLAGS) \ + $(MATE_SESSION_CFLAGS) \ + $(SYSTEMD_CFLAGS) \ $(DISABLE_DEPRECATED_CFLAGS) AM_CFLAGS = $(WARN_CFLAGS) @@ -25,6 +26,8 @@ mate_session_SOURCES = \ gsm-marshal.c \ gsm-consolekit.c \ gsm-consolekit.h \ + gsm-systemd.c \ + gsm-systemd.h \ gsm-logout-dialog.h \ gsm-logout-dialog.c \ gsm-inhibit-dialog.h \ @@ -72,6 +75,7 @@ mate_session_LDADD = \ $(XTEST_LIBS) \ $(XEXT_LIBS) \ $(MATE_SESSION_LIBS) \ + $(SYSTEMD_LIBS) \ $(EXECINFO_LIBS) libgsmutil_la_SOURCES = \ diff --git a/mate-session/gsm-logout-dialog.c b/mate-session/gsm-logout-dialog.c index ad40367..ba28105 100644 --- a/mate-session/gsm-logout-dialog.c +++ b/mate-session/gsm-logout-dialog.c @@ -30,6 +30,10 @@ #include #include "gsm-logout-dialog.h" +#ifdef HAVE_SYSTEMD +#include "gsm-systemd.h" +#include +#endif #include "gsm-consolekit.h" #include "mdm.h" @@ -51,6 +55,9 @@ struct _GsmLogoutDialogPrivate GsmDialogLogoutType type; UpClient *up_client; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd; +#endif GsmConsolekit *consolekit; int timeout; @@ -144,6 +151,11 @@ gsm_logout_dialog_init (GsmLogoutDialog *logout_dialog) logout_dialog->priv->up_client = up_client_new (); +#ifdef HAVE_SYSTEMD + if (sd_booted() > 0) + logout_dialog->priv->systemd = gsm_get_systemd (); + else +#endif logout_dialog->priv->consolekit = gsm_get_consolekit (); g_signal_connect (logout_dialog, @@ -171,6 +183,13 @@ gsm_logout_dialog_destroy (GsmLogoutDialog *logout_dialog, logout_dialog->priv->up_client = NULL; } +#ifdef HAVE_SYSTEMD + if (logout_dialog->priv->systemd) { + g_object_unref (logout_dialog->priv->systemd); + logout_dialog->priv->systemd = NULL; + } +#endif + if (logout_dialog->priv->consolekit) { g_object_unref (logout_dialog->priv->consolekit); logout_dialog->priv->consolekit = NULL; @@ -196,6 +215,11 @@ gsm_logout_supports_switch_user (GsmLogoutDialog *logout_dialog) { gboolean ret; +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) + ret = gsm_systemd_can_switch_user (logout_dialog->priv->systemd); + else +#endif ret = gsm_consolekit_can_switch_user (logout_dialog->priv->consolekit); return ret; @@ -206,6 +230,11 @@ gsm_logout_supports_reboot (GsmLogoutDialog *logout_dialog) { gboolean ret; +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) + ret = gsm_systemd_can_restart (logout_dialog->priv->systemd); + else +#endif ret = gsm_consolekit_can_restart (logout_dialog->priv->consolekit); if (!ret) { ret = mdm_supports_logout_action (MDM_LOGOUT_ACTION_REBOOT); @@ -219,6 +248,11 @@ gsm_logout_supports_shutdown (GsmLogoutDialog *logout_dialog) { gboolean ret; +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) + ret = gsm_systemd_can_stop (logout_dialog->priv->systemd); + else +#endif ret = gsm_consolekit_can_stop (logout_dialog->priv->consolekit); if (!ret) { @@ -242,6 +276,7 @@ gsm_logout_dialog_timeout (gpointer data) char *secondary_text; int seconds_to_show; static char *session_type = NULL; + static gboolean is_not_login; logout_dialog = (GsmLogoutDialog *) data; @@ -283,14 +318,27 @@ gsm_logout_dialog_timeout (gpointer data) } if (session_type == NULL) { - GsmConsolekit *consolekit; - +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + GsmSystemd *systemd; + systemd = gsm_get_systemd (); + session_type = gsm_systemd_get_current_session_type (systemd); + g_object_unref (systemd); + is_not_login = (g_strcmp0 (session_type, GSM_SYSTEMD_SESSION_TYPE_LOGIN_WINDOW) != 0); + } + else { +#endif + GsmConsolekit *consolekit; consolekit = gsm_get_consolekit (); session_type = gsm_consolekit_get_current_session_type (consolekit); g_object_unref (consolekit); + is_not_login = (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) != 0); +#ifdef HAVE_SYSTEMD + } +#endif } - if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) != 0) { + if (is_not_login) { char *name, *tmp; name = g_locale_to_utf8 (g_get_real_name (), -1, NULL, NULL, NULL); @@ -308,9 +356,9 @@ gsm_logout_dialog_timeout (gpointer data) g_free (tmp); g_free (name); - } else { - secondary_text = g_strdup (seconds_warning); - } + } else { + secondary_text = g_strdup (seconds_warning); + } gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (logout_dialog), secondary_text, diff --git a/mate-session/gsm-manager.c b/mate-session/gsm-manager.c index ad4ed89..b817fb2 100644 --- a/mate-session/gsm-manager.c +++ b/mate-session/gsm-manager.c @@ -60,6 +60,10 @@ #include "gsm-logout-dialog.h" #include "gsm-inhibit-dialog.h" #include "gsm-consolekit.h" +#ifdef HAVE_SYSTEMD +#include "gsm-systemd.h" +#include +#endif #include "gsm-session-save.h" #define GSM_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_MANAGER, GsmManagerPrivate)) @@ -391,9 +395,9 @@ phase_num_to_name (guint phase) static void start_phase (GsmManager *manager); static void -quit_request_completed (GsmConsolekit *consolekit, - GError *error, - gpointer user_data) +quit_request_completed_consolekit (GsmConsolekit *consolekit, + GError *error, + gpointer user_data) { MdmLogoutAction fallback_action = GPOINTER_TO_INT (user_data); @@ -406,10 +410,31 @@ quit_request_completed (GsmConsolekit *consolekit, gtk_main_quit (); } +#ifdef HAVE_SYSTEMD +static void +quit_request_completed_systemd (GsmSystemd *systemd, + GError *error, + gpointer user_data) +{ + MdmLogoutAction fallback_action = GPOINTER_TO_INT (user_data); + + if (error != NULL) { + mdm_set_logout_action (fallback_action); + } + + g_object_unref (systemd); + + gtk_main_quit (); +} +#endif + static void gsm_manager_quit (GsmManager *manager) { GsmConsolekit *consolekit; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd; +#endif /* See the comment in request_reboot() for some more details about how * this works. */ @@ -422,12 +447,26 @@ gsm_manager_quit (GsmManager *manager) case GSM_MANAGER_LOGOUT_REBOOT_INTERACT: mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE); + #ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + g_signal_connect (systemd, + "request-completed", + G_CALLBACK (quit_request_completed_systemd), + GINT_TO_POINTER (MDM_LOGOUT_ACTION_REBOOT)); + gsm_systemd_attempt_restart (systemd); + } + else { + #endif consolekit = gsm_get_consolekit (); g_signal_connect (consolekit, "request-completed", - G_CALLBACK (quit_request_completed), + G_CALLBACK (quit_request_completed_consolekit), GINT_TO_POINTER (MDM_LOGOUT_ACTION_REBOOT)); gsm_consolekit_attempt_restart (consolekit); + #ifdef HAVE_SYSTEMD + } + #endif break; case GSM_MANAGER_LOGOUT_REBOOT_MDM: mdm_set_logout_action (MDM_LOGOUT_ACTION_REBOOT); @@ -437,12 +476,26 @@ gsm_manager_quit (GsmManager *manager) case GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT: mdm_set_logout_action (MDM_LOGOUT_ACTION_NONE); + #ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + g_signal_connect (systemd, + "request-completed", + G_CALLBACK (quit_request_completed_systemd), + GINT_TO_POINTER (MDM_LOGOUT_ACTION_SHUTDOWN)); + gsm_systemd_attempt_stop (systemd); + } + else { + #endif consolekit = gsm_get_consolekit (); g_signal_connect (consolekit, "request-completed", - G_CALLBACK (quit_request_completed), + G_CALLBACK (quit_request_completed_consolekit), GINT_TO_POINTER (MDM_LOGOUT_ACTION_SHUTDOWN)); gsm_consolekit_attempt_stop (consolekit); + #ifdef HAVE_SYSTEMD + } + #endif break; case GSM_MANAGER_LOGOUT_SHUTDOWN_MDM: mdm_set_logout_action (MDM_LOGOUT_ACTION_SHUTDOWN); @@ -1822,16 +1875,33 @@ auto_save_is_enabled (GsmManager *manager) static void maybe_save_session (GsmManager *manager) { - GsmConsolekit *consolekit; + GsmConsolekit *consolekit = NULL; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd = NULL; +#endif char *session_type; GError *error; +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + session_type = gsm_systemd_get_current_session_type (systemd); + + if (g_strcmp0 (session_type, GSM_SYSTEMD_SESSION_TYPE_LOGIN_WINDOW) == 0) { + goto out; + } + } + else { +#endif consolekit = gsm_get_consolekit (); session_type = gsm_consolekit_get_current_session_type (consolekit); if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) == 0) { goto out; } +#ifdef HAVE_SYSTEMD + } +#endif /* We only allow session saving when session is running or when * logging out */ @@ -1849,7 +1919,12 @@ maybe_save_session (GsmManager *manager) } out: - g_object_unref (consolekit); + if (consolekit != NULL) + g_object_unref (consolekit); +#ifdef HAVE_SYSTEMD + if (systemd != NULL) + g_object_unref (systemd); +#endif g_free (session_type); } @@ -2335,11 +2410,24 @@ on_presence_status_changed (GsmPresence *presence, guint status, GsmManager *manager) { +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + GsmSystemd *systemd; + + systemd = gsm_get_systemd (); + gsm_systemd_set_session_idle (systemd, + (status == GSM_PRESENCE_STATUS_IDLE)); + } + else { +#endif GsmConsolekit *consolekit; consolekit = gsm_get_consolekit (); gsm_consolekit_set_session_idle (consolekit, (status == GSM_PRESENCE_STATUS_IDLE)); +#ifdef HAVE_SYSTEMD + } +#endif } static void @@ -2520,17 +2608,17 @@ gsm_manager_is_suspend_inhibited (GsmManager *manager) } static void -request_reboot_privileges_completed (GsmConsolekit *consolekit, - gboolean success, - gboolean ask_later, - GError *error, - GsmManager *manager) +request_reboot_privileges_completed_consolekit (GsmConsolekit *consolekit, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) { /* make sure we disconnect the signal handler so that it's not called * again next time the event is fired -- this can happen if the reboot * is cancelled. */ g_signal_handlers_disconnect_by_func (consolekit, - request_reboot_privileges_completed, + request_reboot_privileges_completed_consolekit, manager); g_object_unref (consolekit); @@ -2546,10 +2634,42 @@ request_reboot_privileges_completed (GsmConsolekit *consolekit, } } +#ifdef HAVE_SYSTEMD +static void +request_reboot_privileges_completed_systemd (GsmSystemd *systemd, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) +{ + /* make sure we disconnect the signal handler so that it's not called + * again next time the event is fired -- this can happen if the reboot + * is cancelled. */ + g_signal_handlers_disconnect_by_func (systemd, + request_reboot_privileges_completed_systemd, + manager); + + g_object_unref (systemd); + + if (success) { + if (ask_later) { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_INTERACT; + } else { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT; + } + + end_phase (manager); + } +} +#endif + static void request_reboot (GsmManager *manager) { GsmConsolekit *consolekit; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd; +#endif gboolean success; g_debug ("GsmManager: requesting reboot"); @@ -2557,6 +2677,7 @@ request_reboot (GsmManager *manager) /* We request the privileges before doing anything. There are a few * different cases here: * + * - no systemd: we fallback on ConsoleKit * - no ConsoleKit: we fallback on MDM * - no password required: everything is fine * - password asked once: we ask for it now. If the user enters it @@ -2575,36 +2696,60 @@ request_reboot (GsmManager *manager) * just work fine. */ +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + g_signal_connect (systemd, + "privileges-completed", + G_CALLBACK (request_reboot_privileges_completed_systemd), + manager); + success = gsm_systemd_get_restart_privileges (systemd); + + if (!success) { + g_signal_handlers_disconnect_by_func (systemd, + request_reboot_privileges_completed_systemd, + manager); + g_object_unref (systemd); + + manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_MDM; + end_phase (manager); + } + } + else { +#endif consolekit = gsm_get_consolekit (); g_signal_connect (consolekit, "privileges-completed", - G_CALLBACK (request_reboot_privileges_completed), + G_CALLBACK (request_reboot_privileges_completed_consolekit), manager); success = gsm_consolekit_get_restart_privileges (consolekit); if (!success) { g_signal_handlers_disconnect_by_func (consolekit, - request_reboot_privileges_completed, + request_reboot_privileges_completed_consolekit, manager); g_object_unref (consolekit); manager->priv->logout_type = GSM_MANAGER_LOGOUT_REBOOT_MDM; end_phase (manager); } +#ifdef HAVE_SYSTEMD + } +#endif } static void -request_shutdown_privileges_completed (GsmConsolekit *consolekit, - gboolean success, - gboolean ask_later, - GError *error, - GsmManager *manager) +request_shutdown_privileges_completed_consolekit (GsmConsolekit *consolekit, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) { /* make sure we disconnect the signal handler so that it's not called * again next time the event is fired -- this can happen if the reboot * is cancelled. */ g_signal_handlers_disconnect_by_func (consolekit, - request_shutdown_privileges_completed, + request_shutdown_privileges_completed_consolekit, manager); g_object_unref (consolekit); @@ -2620,10 +2765,42 @@ request_shutdown_privileges_completed (GsmConsolekit *consolekit, } } +#ifdef HAVE_SYSTEMD +static void +request_shutdown_privileges_completed_systemd (GsmSystemd *systemd, + gboolean success, + gboolean ask_later, + GError *error, + GsmManager *manager) +{ + /* make sure we disconnect the signal handler so that it's not called + * again next time the event is fired -- this can happen if the reboot + * is cancelled. */ + g_signal_handlers_disconnect_by_func (systemd, + request_shutdown_privileges_completed_systemd, + manager); + + g_object_unref (systemd); + + if (success) { + if (ask_later) { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_INTERACT; + } else { + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN; + } + + end_phase (manager); + } +} +#endif + static void request_shutdown (GsmManager *manager) { GsmConsolekit *consolekit; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd; +#endif gboolean success; g_debug ("GsmManager: requesting shutdown"); @@ -2631,22 +2808,46 @@ request_shutdown (GsmManager *manager) /* See the comment in request_reboot() for some more details about how * this works. */ +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + g_signal_connect (systemd, + "privileges-completed", + G_CALLBACK (request_shutdown_privileges_completed_systemd), + manager); + success = gsm_systemd_get_stop_privileges (systemd); + + if (!success) { + g_signal_handlers_disconnect_by_func (systemd, + request_shutdown_privileges_completed_systemd, + manager); + g_object_unref (systemd); + + manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_MDM; + end_phase (manager); + } + } + else { +#endif consolekit = gsm_get_consolekit (); g_signal_connect (consolekit, "privileges-completed", - G_CALLBACK (request_shutdown_privileges_completed), + G_CALLBACK (request_shutdown_privileges_completed_consolekit), manager); success = gsm_consolekit_get_stop_privileges (consolekit); if (!success) { g_signal_handlers_disconnect_by_func (consolekit, - request_shutdown_privileges_completed, + request_shutdown_privileges_completed_consolekit, manager); g_object_unref (consolekit); manager->priv->logout_type = GSM_MANAGER_LOGOUT_SHUTDOWN_MDM; end_phase (manager); } +#ifdef HAVE_SYSTEMD + } +#endif } static void @@ -2938,6 +3139,9 @@ gsm_manager_can_shutdown (GsmManager *manager, GError **error) { GsmConsolekit *consolekit; +#ifdef HAVE_SYSTEMD + GsmSystemd *systemd; +#endif gboolean can_suspend; gboolean can_hibernate; @@ -2950,12 +3154,26 @@ gsm_manager_can_shutdown (GsmManager *manager, g_return_val_if_fail (GSM_IS_MANAGER (manager), FALSE); +#ifdef HAVE_SYSTEMD + if (sd_booted () > 0) { + systemd = gsm_get_systemd (); + *shutdown_available = gsm_systemd_can_stop (systemd) + || gsm_systemd_can_restart (systemd) + || can_suspend + || can_hibernate; + g_object_unref (systemd); + } + else { +#endif consolekit = gsm_get_consolekit (); *shutdown_available = gsm_consolekit_can_stop (consolekit) || gsm_consolekit_can_restart (consolekit) || can_suspend || can_hibernate; g_object_unref (consolekit); +#ifdef HAVE_SYSTEMD + } +#endif return TRUE; } diff --git a/mate-session/gsm-systemd.c b/mate-session/gsm-systemd.c new file mode 100644 index 0000000..050d931 --- /dev/null +++ b/mate-session/gsm-systemd.c @@ -0,0 +1,776 @@ +/* + * Copyright (C) 2013 Stefano Karapetsas + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef HAVE_SYSTEMD +#include +#endif + +#include "gsm-marshal.h" +#include "gsm-systemd.h" + +#define SD_NAME "org.freedesktop.login1" +#define SD_PATH "/org/freedesktop/login1" +#define SD_INTERFACE "org.freedesktop.login1.Manager" +#define SD_SEAT_INTERFACE "org.freedesktop.login1.Seat" +#define SD_SESSION_INTERFACE "org.freedesktop.login1.Session" + +#define GSM_SYSTEMD_GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE ((o), GSM_TYPE_SYSTEMD, GsmSystemdPrivate)) + +struct _GsmSystemdPrivate +{ + DBusGConnection *dbus_connection; + DBusGProxy *bus_proxy; + DBusGProxy *sd_proxy; + guint32 is_connected : 1; +}; + +enum { + PROP_0, + PROP_IS_CONNECTED +}; + +enum { + REQUEST_COMPLETED = 0, + PRIVILEGES_COMPLETED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +static void gsm_systemd_class_init (GsmSystemdClass *klass); +static void gsm_systemd_init (GsmSystemd *sd); +static void gsm_systemd_finalize (GObject *object); + +static void gsm_systemd_free_dbus (GsmSystemd *manager); + +static DBusHandlerResult gsm_systemd_dbus_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data); + +static void gsm_systemd_on_name_owner_changed (DBusGProxy *bus_proxy, + const char *name, + const char *prev_owner, + const char *new_owner, + GsmSystemd *manager); + +G_DEFINE_TYPE (GsmSystemd, gsm_systemd, G_TYPE_OBJECT); + +static void +gsm_systemd_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GsmSystemd *manager = GSM_SYSTEMD (object); + + switch (prop_id) { + case PROP_IS_CONNECTED: + g_value_set_boolean (value, + manager->priv->is_connected); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, + prop_id, + pspec); + } +} + +static void +gsm_systemd_class_init (GsmSystemdClass *manager_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (manager_class); + + object_class->finalize = gsm_systemd_finalize; + object_class->get_property = gsm_systemd_get_property; + + param_spec = g_param_spec_boolean ("is-connected", + "Is connected", + "Whether the session is connected to Systemd", + FALSE, + G_PARAM_READABLE); + + g_object_class_install_property (object_class, PROP_IS_CONNECTED, + param_spec); + + signals [REQUEST_COMPLETED] = + g_signal_new ("request-completed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmSystemdClass, request_completed), + NULL, + NULL, + g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, + 1, G_TYPE_POINTER); + + signals [PRIVILEGES_COMPLETED] = + g_signal_new ("privileges-completed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GsmSystemdClass, privileges_completed), + NULL, + NULL, + gsm_marshal_VOID__BOOLEAN_BOOLEAN_POINTER, + G_TYPE_NONE, + 3, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER); + + g_type_class_add_private (manager_class, sizeof (GsmSystemdPrivate)); +} + +static DBusHandlerResult +gsm_systemd_dbus_filter (DBusConnection *connection, + DBusMessage *message, + void *user_data) +{ + GsmSystemd *manager; + + manager = GSM_SYSTEMD (user_data); + + if (dbus_message_is_signal (message, + DBUS_INTERFACE_LOCAL, "Disconnected") && + strcmp (dbus_message_get_path (message), DBUS_PATH_LOCAL) == 0) { + gsm_systemd_free_dbus (manager); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static gboolean +gsm_systemd_ensure_sd_connection (GsmSystemd *manager, + GError **error) +{ + GError *connection_error; + gboolean is_connected; + + connection_error = NULL; + + if (manager->priv->dbus_connection == NULL) { + DBusConnection *connection; + + manager->priv->dbus_connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, + &connection_error); + + if (manager->priv->dbus_connection == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + + connection = dbus_g_connection_get_connection (manager->priv->dbus_connection); + dbus_connection_set_exit_on_disconnect (connection, FALSE); + dbus_connection_add_filter (connection, + gsm_systemd_dbus_filter, + manager, NULL); + } + + if (manager->priv->bus_proxy == NULL) { + manager->priv->bus_proxy = + dbus_g_proxy_new_for_name_owner (manager->priv->dbus_connection, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &connection_error); + + if (manager->priv->bus_proxy == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + + dbus_g_proxy_add_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (manager->priv->bus_proxy, + "NameOwnerChanged", + G_CALLBACK (gsm_systemd_on_name_owner_changed), + manager, NULL); + } + + if (manager->priv->sd_proxy == NULL) { + manager->priv->sd_proxy = + dbus_g_proxy_new_for_name_owner (manager->priv->dbus_connection, + SD_NAME, + SD_PATH, + SD_INTERFACE, + &connection_error); + + if (manager->priv->sd_proxy == NULL) { + g_propagate_error (error, connection_error); + is_connected = FALSE; + goto out; + } + } + + is_connected = TRUE; + +out: + if (manager->priv->is_connected != is_connected) { + manager->priv->is_connected = is_connected; + g_object_notify (G_OBJECT (manager), "is-connected"); + } + + if (!is_connected) { + if (manager->priv->dbus_connection == NULL) { + if (manager->priv->bus_proxy != NULL) { + g_object_unref (manager->priv->bus_proxy); + manager->priv->bus_proxy = NULL; + } + + if (manager->priv->sd_proxy != NULL) { + g_object_unref (manager->priv->sd_proxy); + manager->priv->sd_proxy = NULL; + } + } else if (manager->priv->bus_proxy == NULL) { + if (manager->priv->sd_proxy != NULL) { + g_object_unref (manager->priv->sd_proxy); + manager->priv->sd_proxy = NULL; + } + } + } + + return is_connected; +} + +static void +gsm_systemd_on_name_owner_changed (DBusGProxy *bus_proxy, + const char *name, + const char *prev_owner, + const char *new_owner, + GsmSystemd *manager) +{ + if (name != NULL && g_strcmp0 (name, SD_NAME) != 0) { + return; + } + + if (manager->priv->sd_proxy != NULL) { + g_object_unref (manager->priv->sd_proxy); + manager->priv->sd_proxy = NULL; + } + + gsm_systemd_ensure_sd_connection (manager, NULL); +} + +static void +gsm_systemd_init (GsmSystemd *manager) +{ + GError *error; + + manager->priv = GSM_SYSTEMD_GET_PRIVATE (manager); + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + g_error_free (error); + } +} + +static void +gsm_systemd_free_dbus (GsmSystemd *manager) +{ + if (manager->priv->bus_proxy != NULL) { + g_object_unref (manager->priv->bus_proxy); + manager->priv->bus_proxy = NULL; + } + + if (manager->priv->sd_proxy != NULL) { + g_object_unref (manager->priv->sd_proxy); + manager->priv->sd_proxy = NULL; + } + + if (manager->priv->dbus_connection != NULL) { + DBusConnection *connection; + connection = dbus_g_connection_get_connection (manager->priv->dbus_connection); + dbus_connection_remove_filter (connection, + gsm_systemd_dbus_filter, + manager); + + dbus_g_connection_unref (manager->priv->dbus_connection); + manager->priv->dbus_connection = NULL; + } +} + +static void +gsm_systemd_finalize (GObject *object) +{ + GsmSystemd *manager; + GObjectClass *parent_class; + + manager = GSM_SYSTEMD (object); + + parent_class = G_OBJECT_CLASS (gsm_systemd_parent_class); + + gsm_systemd_free_dbus (manager); + + if (parent_class->finalize != NULL) { + parent_class->finalize (object); + } +} + +GQuark +gsm_systemd_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) { + error_quark = g_quark_from_static_string ("gsm-systemd-error"); + } + + return error_quark; +} + +GsmSystemd * +gsm_systemd_new (void) +{ + GsmSystemd *manager; + + manager = g_object_new (GSM_TYPE_SYSTEMD, NULL); + + return manager; +} + +static void +emit_restart_complete (GsmSystemd *manager, + GError *error) +{ + GError *call_error; + + call_error = NULL; + + if (error != NULL) { + call_error = g_error_new_literal (GSM_SYSTEMD_ERROR, + GSM_SYSTEMD_ERROR_RESTARTING, + error->message); + } + + g_signal_emit (G_OBJECT (manager), + signals [REQUEST_COMPLETED], + 0, call_error); + + if (call_error != NULL) { + g_error_free (call_error); + } +} + +static void +emit_stop_complete (GsmSystemd *manager, + GError *error) +{ + GError *call_error; + + call_error = NULL; + + if (error != NULL) { + call_error = g_error_new_literal (GSM_SYSTEMD_ERROR, + GSM_SYSTEMD_ERROR_STOPPING, + error->message); + } + + g_signal_emit (G_OBJECT (manager), + signals [REQUEST_COMPLETED], + 0, call_error); + + if (call_error != NULL) { + g_error_free (call_error); + } +} + +void +gsm_systemd_attempt_restart (GsmSystemd *manager) +{ + gboolean res; + GError *error; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + emit_restart_complete (manager, error); + g_error_free (error); + return; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->sd_proxy, + "Reboot", + INT_MAX, + &error, + G_TYPE_BOOLEAN, TRUE, /* interactive */ + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!res) { + g_warning ("Unable to restart system: %s", error->message); + emit_restart_complete (manager, error); + g_error_free (error); + } else { + emit_restart_complete (manager, NULL); + } +} + +void +gsm_systemd_attempt_stop (GsmSystemd *manager) +{ + gboolean res; + GError *error; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + emit_stop_complete (manager, error); + g_error_free (error); + return; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->sd_proxy, + "PowerOff", + INT_MAX, + &error, + G_TYPE_BOOLEAN, TRUE, /* interactive */ + G_TYPE_INVALID, + G_TYPE_INVALID); + + if (!res) { + g_warning ("Unable to stop system: %s", error->message); + emit_stop_complete (manager, error); + g_error_free (error); + } else { + emit_stop_complete (manager, NULL); + } +} + +static void +gsm_systemd_get_session_path (DBusConnection *connection, + char **session_path) +{ + DBusError local_error; + DBusMessage *message; + DBusMessage *reply; + DBusMessageIter iter; + gchar *session_id = NULL; + +#ifdef HAVE_SYSTEMD + sd_pid_get_session (getpid (), &session_id); +#endif + + if (session_id == NULL) + return; + + reply = NULL; + + dbus_error_init (&local_error); + message = dbus_message_new_method_call (SD_NAME, + SD_PATH, + SD_INTERFACE, + "GetSession"); + if (message == NULL) { + goto out; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &session_id); + + dbus_error_init (&local_error); + reply = dbus_connection_send_with_reply_and_block (connection, + message, + -1, + &local_error); + if (reply == NULL) { + if (dbus_error_is_set (&local_error)) { + g_warning ("Unable to get session path: %s", local_error.message); + dbus_error_free (&local_error); + goto out; + } + } + + dbus_message_iter_init (reply, &iter); + dbus_message_iter_get_basic (&iter, session_path); + +out: + if (message != NULL) { + dbus_message_unref (message); + } + if (reply != NULL) { + dbus_message_unref (reply); + } + if (session_id != NULL) { + g_free (session_id); + } +} + + +void +gsm_systemd_set_session_idle (GsmSystemd *manager, + gboolean is_idle) +{ + GError *error; + char *session_path; + DBusMessage *message; + DBusMessage *reply; + DBusError dbus_error; + DBusMessageIter iter; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + g_error_free (error); + return; + } + + gsm_systemd_get_session_path (dbus_g_connection_get_connection (manager->priv->dbus_connection), &session_path); + + g_debug ("Updating Systemd idle status: %d", is_idle); + message = dbus_message_new_method_call (SD_NAME, + session_path, + SD_SESSION_INTERFACE, + "SetIdleHint"); + if (message == NULL) { + g_debug ("Couldn't allocate the D-Bus message"); + return; + } + + dbus_message_iter_init_append (message, &iter); + dbus_message_iter_append_basic (&iter, DBUS_TYPE_BOOLEAN, &is_idle); + + /* FIXME: use async? */ + dbus_error_init (&dbus_error); + reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->dbus_connection), + message, + -1, + &dbus_error); + dbus_message_unref (message); + + if (reply != NULL) { + dbus_message_unref (reply); + } + + if (dbus_error_is_set (&dbus_error)) { + g_debug ("%s raised:\n %s\n\n", dbus_error.name, dbus_error.message); + dbus_error_free (&dbus_error); + } + + g_free (session_path); +} + +gboolean +gsm_systemd_can_switch_user (GsmSystemd *manager) +{ + GError *error; + char *session_id = NULL; + char *seat_id = NULL; + int ret = 0; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + g_error_free (error); + return FALSE; + } + +#ifdef HAVE_SYSTEMD + sd_pid_get_session (getpid (), &session_id); +#endif + + if (session_id == NULL) + return FALSE; + +#ifdef HAVE_SYSTEMD + sd_session_get_seat (session_id, &seat_id); + ret = sd_seat_can_multi_session (seat_id); + + g_free (session_id); + g_free (seat_id); +#endif + + return ret > 0; +} + +gboolean +gsm_systemd_get_restart_privileges (GsmSystemd *manager) +{ + g_signal_emit (G_OBJECT (manager), + signals [PRIVILEGES_COMPLETED], + 0, TRUE, TRUE, NULL); + + return TRUE; +} + +gboolean +gsm_systemd_get_stop_privileges (GsmSystemd *manager) +{ + g_signal_emit (G_OBJECT (manager), + signals [PRIVILEGES_COMPLETED], + 0, TRUE, TRUE, NULL); + + return TRUE; +} + +gboolean +gsm_systemd_can_restart (GsmSystemd *manager) +{ + gboolean res; + gchar *value; + gboolean can_restart; + GError *error; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + g_error_free (error); + return FALSE; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->sd_proxy, + "CanReboot", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &value, + G_TYPE_INVALID); + if (res == FALSE) { + g_warning ("Could not make DBUS call: %s", + error->message); + g_error_free (error); + return FALSE; + } + + can_restart = g_strcmp0 (value, "yes") == 0 || + g_strcmp0 (value, "challenge") == 0; + g_free (value); + return can_restart; +} + +gboolean +gsm_systemd_can_stop (GsmSystemd *manager) +{ + gboolean res; + gchar *value; + gboolean can_stop; + GError *error; + + error = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &error)) { + g_warning ("Could not connect to Systemd: %s", + error->message); + g_error_free (error); + return FALSE; + } + + res = dbus_g_proxy_call_with_timeout (manager->priv->sd_proxy, + "CanPowerOff", + INT_MAX, + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &value, + G_TYPE_INVALID); + + if (res == FALSE) { + g_warning ("Could not make DBUS call: %s", + error->message); + g_error_free (error); + return FALSE; + } + + can_stop = g_strcmp0 (value, "yes") == 0 || + g_strcmp0 (value, "challenge") == 0; + g_free (value); + return can_stop; +} + +gchar * +gsm_systemd_get_current_session_type (GsmSystemd *manager) +{ + GError *gerror; + int session_id; + gchar *session_class = NULL; + int res; + + gerror = NULL; + + if (!gsm_systemd_ensure_sd_connection (manager, &gerror)) { + g_warning ("Could not connect to Systemd: %s", + gerror->message); + g_error_free (gerror); + return NULL; + } + +#ifdef HAVE_SYSTEMD + sd_pid_get_session (getpid (), &session_id); +#endif + + if (session_id == NULL) + return NULL; + +#ifdef HAVE_SYSTEMD + res = sd_session_get_class (session_id, &session_class); + if (res < 0) { + g_warning ("Could not get Systemd session class!"); + return NULL; + } +#endif + + return session_class; +} + + +GsmSystemd * +gsm_get_systemd (void) +{ + static GsmSystemd *manager = NULL; + + if (manager == NULL) { + manager = gsm_systemd_new (); + } + + return g_object_ref (manager); +} diff --git a/mate-session/gsm-systemd.h b/mate-session/gsm-systemd.h new file mode 100644 index 0000000..ad38ac7 --- /dev/null +++ b/mate-session/gsm-systemd.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2013 Stefano Karapetsas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * Authors: + * Stefano Karapetsas + */ + +#ifndef __GSM_SYSTEMD_H__ +#define __GSM_SYSTEMD_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GSM_TYPE_SYSTEMD (gsm_systemd_get_type ()) +#define GSM_SYSTEMD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GSM_TYPE_SYSTEMD, GsmSystemd)) +#define GSM_SYSTEMD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GSM_TYPE_SYSTEMD, GsmSystemdClass)) +#define GSM_IS_SYSTEMD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GSM_TYPE_SYSTEMD)) +#define GSM_IS_SYSTEMD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GSM_TYPE_SYSTEMD)) +#define GSM_SYSTEMD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GSM_TYPE_SYSTEMD, GsmSystemdClass)) +#define GSM_SYSTEMD_ERROR (gsm_systemd_error_quark ()) + +typedef struct _GsmSystemd GsmSystemd; +typedef struct _GsmSystemdClass GsmSystemdClass; +typedef struct _GsmSystemdPrivate GsmSystemdPrivate; +typedef enum _GsmSystemdError GsmSystemdError; + +struct _GsmSystemd +{ + GObject parent; + + GsmSystemdPrivate *priv; +}; + +struct _GsmSystemdClass +{ + GObjectClass parent_class; + + void (* request_completed) (GsmSystemd *manager, + GError *error); + + void (* privileges_completed) (GsmSystemd *manager, + gboolean success, + gboolean ask_later, + GError *error); +}; + +enum _GsmSystemdError { + GSM_SYSTEMD_ERROR_RESTARTING = 0, + GSM_SYSTEMD_ERROR_STOPPING +}; + +#define GSM_SYSTEMD_SESSION_TYPE_LOGIN_WINDOW "greeter" + +GType gsm_systemd_get_type (void); + +GQuark gsm_systemd_error_quark (void); + +GsmSystemd *gsm_systemd_new (void) G_GNUC_MALLOC; + +gboolean gsm_systemd_can_switch_user (GsmSystemd *manager); + +gboolean gsm_systemd_get_restart_privileges (GsmSystemd *manager); + +gboolean gsm_systemd_get_stop_privileges (GsmSystemd *manager); + +gboolean gsm_systemd_can_stop (GsmSystemd *manager); + +gboolean gsm_systemd_can_restart (GsmSystemd *manager); + +void gsm_systemd_attempt_stop (GsmSystemd *manager); + +void gsm_systemd_attempt_restart (GsmSystemd *manager); + +void gsm_systemd_set_session_idle (GsmSystemd *manager, + gboolean is_idle); + +gchar *gsm_systemd_get_current_session_type (GsmSystemd *manager); + +GsmSystemd *gsm_get_systemd (void); + +#ifdef __cplusplus +} +#endif + +#endif /* __GSM_SYSTEMD_H__ */ diff --git a/mate-session/main.c b/mate-session/main.c index 0a6259c..da2a687 100644 --- a/mate-session/main.c +++ b/mate-session/main.c @@ -42,6 +42,10 @@ #include "mdm-log.h" #include "gsm-consolekit.h" +#ifdef HAVE_SYSTEMD +#include "gsm-systemd.h" +#include +#endif #include "gsm-util.h" #include "gsm-manager.h" #include "gsm-xsmp-server.h" @@ -259,26 +263,47 @@ static void append_required_apps(GsmManager* manager) static void maybe_load_saved_session_apps(GsmManager* manager) { - GsmConsolekit* consolekit; + GsmConsolekit* consolekit = NULL; +#ifdef HAVE_SYSTEMD + GsmSystemd* systemd = NULL; +#endif char* session_type; + gboolean is_login; +#ifdef HAVE_SYSTEMD + if (sd_booted() > 0) { + systemd = gsm_get_systemd(); + session_type = gsm_systemd_get_current_session_type(systemd); + is_login = g_strcmp0 (session_type, GSM_SYSTEMD_SESSION_TYPE_LOGIN_WINDOW) == 0; + } + else { +#endif consolekit = gsm_get_consolekit(); session_type = gsm_consolekit_get_current_session_type(consolekit); + is_login = g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) == 0; +#ifdef HAVE_SYSTEMD + } +#endif - if (g_strcmp0 (session_type, GSM_CONSOLEKIT_SESSION_TYPE_LOGIN_WINDOW) != 0) + if (!is_login) { - GSettings* settings; - gboolean autostart; + GSettings* settings; + gboolean autostart; settings = g_settings_new (GSM_SCHEMA); - autostart = g_settings_get_boolean (settings, KEY_AUTOSAVE); - g_object_unref (settings); + autostart = g_settings_get_boolean (settings, KEY_AUTOSAVE); + g_object_unref (settings); - if (autostart == TRUE) + if (autostart == TRUE) gsm_manager_add_autostart_apps_from_dir(manager, gsm_util_get_saved_session_dir()); } - g_object_unref(consolekit); + if (consolekit != NULL) + g_object_unref(consolekit); +#ifdef HAVE_SYSTEMD + if (systemd != NULL) + g_object_unref(systemd); +#endif g_free(session_type); } -- cgit v1.2.1