diff options
-rw-r--r-- | AUTHORS | 2 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | debian/changelog | 16 | ||||
-rw-r--r-- | src/Makefile.am | 29 | ||||
-rw-r--r-- | src/dbus-shared-names.h | 14 | ||||
-rw-r--r-- | src/gtk-dialog/gtk-logout-helper.c | 7 | ||||
-rw-r--r-- | src/session-service.c | 125 | ||||
-rw-r--r-- | src/status-service.c | 16 | ||||
-rw-r--r-- | src/users-service-dbus.c | 1147 | ||||
-rw-r--r-- | src/users-service-dbus.h | 81 | ||||
-rw-r--r-- | src/users-service.c | 335 | ||||
-rw-r--r-- | src/users-service.list | 1 | ||||
-rw-r--r-- | src/users-service.xml | 56 |
13 files changed, 1746 insertions, 85 deletions
@@ -0,0 +1,2 @@ +Ted Gould <ted@canonical.com> +Cody Russell <crussell@canonical.com> diff --git a/configure.ac b/configure.ac index ad743f1..0ce9fbc 100644 --- a/configure.ac +++ b/configure.ac @@ -4,7 +4,7 @@ AC_INIT(src/indicator-session.c) AC_PREREQ(2.53) AM_CONFIG_HEADER(config.h) -AM_INIT_AUTOMAKE(indicator-session, 0.1.4) +AM_INIT_AUTOMAKE(indicator-session, 0.1.5) AM_MAINTAINER_MODE diff --git a/debian/changelog b/debian/changelog index 90cef72..53011cd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,18 +1,16 @@ -indicator-session (0.1.4-0ubuntu3~ppa2) karmic; urgency=low +indicator-session (0.1.5-0ubuntu1~ppa1) UNRELEASED; urgency=low - * Merging in MC5 Support branch + * Upstream release 0.1.5 + * PolicyKit-1 support (LP: #418643) + * GDM User list support (LP: #422052) + * MissionControl5 support (LP: #427643) + * Better locking of the screensaver (LP: #428115) * debian/control: Adding in a libempathy-dev build dependency as it's now required by upstream. - - -- Ted Gould <ted@ubuntu.com> Thu, 24 Sep 2009 12:11:06 -0500 - -indicator-session (0.1.4-0ubuntu3~ppa1) karmic; urgency=low - - * Merging in trunk (PK Changes) * Removing patches 01_polkit-1.patch and 99_autoreconf.patch as they were merged upstream. - -- Ted Gould <ted@ubuntu.com> Tue, 22 Sep 2009 21:35:10 -0500 + -- Ted Gould <ted@ubuntu.com> Thu, 24 Sep 2009 17:03:58 -0500 indicator-session (0.1.4-0ubuntu2) karmic; urgency=low diff --git a/src/Makefile.am b/src/Makefile.am index ae941e1..17d14e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -11,7 +11,8 @@ sessionlib_LTLIBRARIES = libsession.la libsession_la_SOURCES = \ indicator-session.c \ dbus-shared-names.h \ - status-service-client.h + status-service-client.h \ + users-service-client.h libsession_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror libsession_la_LIBADD = $(APPLET_LIBS) libsession_la_LDFLAGS = -module -avoid-version @@ -25,6 +26,10 @@ indicator_status_service_SOURCES = \ status-service-dbus.h \ status-service-dbus.c \ status-service-server.h \ + users-service-dbus.h \ + users-service-dbus.c \ + users-service-marshal.c \ + users-service-marshal.h \ status-provider.h \ status-provider.c \ status-provider-mc5.h \ @@ -42,6 +47,13 @@ indicator_status_service_SOURCES = \ indicator_status_service_CFLAGS = $(STATUSSERVICE_CFLAGS) -Wall -Werror indicator_status_service_LDADD = $(STATUSSERVICE_LIBS) +users-service-client.h: $(srcdir)/users-service.xml + dbus-binding-tool \ + --prefix=_users_service_client \ + --mode=glib-client \ + --output=users-service-client.h \ + $(srcdir)/users-service.xml + status-service-client.h: $(srcdir)/status-service.xml dbus-binding-tool \ --prefix=_status_service_client \ @@ -56,6 +68,16 @@ status-service-server.h: $(srcdir)/status-service.xml --output=status-service-server.h \ $(srcdir)/status-service.xml +users-service-marshal.h: $(srcdir)/users-service.list + glib-genmarshal --header \ + --prefix=_users_service_marshal $(srcdir)/users-service.list \ + > users-service-marshal.h + +users-service-marshal.c: $(srcdir)/users-service.list + glib-genmarshal --body \ + --prefix=_users_service_marshal $(srcdir)/users-service.list \ + > users-service-marshal.c + status-provider-pidgin-marshal.h: $(srcdir)/status-provider-pidgin.list glib-genmarshal --header \ --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \ @@ -90,7 +112,7 @@ status-provider-mc5-marshal.c: $(srcdir)/status-provider-mc5.list # Users Stuff ############### -indicator_users_service_SOURCES = users-service.c +indicator_users_service_SOURCES = users-service.c users-service-dbus.c users-service-marshal.c indicator_users_service_CFLAGS = $(USERSSERVICE_CFLAGS) -Wall -Werror indicator_users_service_LDADD = $(USERSSERVICE_LIBS) @@ -107,10 +129,13 @@ indicator_session_service_LDADD = $(SESSIONSERVICE_LIBS) $(GCONF_LIBS) ############### BUILT_SOURCES = \ + users-service-client.h \ status-service-client.h \ status-service-server.h \ status-provider-mc5-marshal.h \ status-provider-mc5-marshal.c \ + users-service-marshal.h \ + users-service-marshal.c \ status-provider-pidgin-marshal.h \ status-provider-pidgin-marshal.c \ status-provider-telepathy-marshal.h \ diff --git a/src/dbus-shared-names.h b/src/dbus-shared-names.h index a645830..7e7d456 100644 --- a/src/dbus-shared-names.h +++ b/src/dbus-shared-names.h @@ -7,16 +7,16 @@ Copyright 2009 Canonical Ltd. Authors: Ted Gould <ted@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 +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 +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 +You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -31,6 +31,8 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #define INDICATOR_USERS_DBUS_NAME "org.ayatana.indicator.users" #define INDICATOR_USERS_DBUS_OBJECT "/org/ayatana/indicator/users/menu" +#define INDICATOR_USERS_SERVICE_DBUS_OBJECT "/org/gnome/DisplayManager/UserManager" +#define INDICATOR_USERS_SERVICE_DBUS_INTERFACE "org.gnome.DisplayManager.UserManager" #define INDICATOR_SESSION_DBUS_NAME "org.ayatana.indicator.session" #define INDICATOR_SESSION_DBUS_OBJECT "/org/ayatana/indicator/session/menu" diff --git a/src/gtk-dialog/gtk-logout-helper.c b/src/gtk-dialog/gtk-logout-helper.c index 0c03e86..d8aea6b 100644 --- a/src/gtk-dialog/gtk-logout-helper.c +++ b/src/gtk-dialog/gtk-logout-helper.c @@ -144,7 +144,12 @@ main (int argc, char * argv[]) INDICATOR_ICONS_DIR); GtkWidget * dialog = NULL; - if (!pk_require_auth(type) && !supress_confirmations()) { + /* TODO: We're disabling PolicyKit checking here because there + is a bug in ConsoleKit where the dialog doesn't come up until + the session is entirely closed. Stupid, but it's better than + not getting a dialog at all. */ + /* if (!pk_require_auth(type) && !supress_confirmations()) { */ + if (!supress_confirmations()) { dialog = logout_dialog_new(type); } diff --git a/src/session-service.c b/src/session-service.c index 9c3c4ef..0242b17 100644 --- a/src/session-service.c +++ b/src/session-service.c @@ -44,6 +44,10 @@ static GMainLoop * mainloop = NULL; static DBusGProxy * dkp_main_proxy = NULL; static DBusGProxy * dkp_prop_proxy = NULL; +static DBusGProxy * gdm_settings_proxy = NULL; +static gboolean gdm_auto_login = FALSE; +static const gchar * gdm_auto_login_string = "daemon/AutomaticLoginEnable"; + static DBusGProxyCall * suspend_call = NULL; static DBusGProxyCall * hibernate_call = NULL; @@ -53,6 +57,123 @@ static DbusmenuMenuitem * logout_mi = NULL; static DbusmenuMenuitem * restart_mi = NULL; static DbusmenuMenuitem * shutdown_mi = NULL; + +/* Respond to the signal of autologin changing to see if the + setting for timed login changes. */ +static void +gdm_settings_change (DBusGProxy * proxy, const gchar * value, const gchar * old, const gchar * new, gpointer data) +{ + if (g_strcmp0(value, gdm_auto_login_string)) { + /* This is not a setting that we care about, + there is only one. */ + return; + } + g_debug("GDM Settings change: %s", new); + + if (g_strcmp0(new, "true") == 0) { + gdm_auto_login = TRUE; + } else { + gdm_auto_login = FALSE; + } + + return; +} + +/* Get back the data from querying to see if there is auto + login enabled in GDM */ +static void +gdm_get_autologin (DBusGProxy * proxy, DBusGProxyCall * call, gpointer data) +{ + GError * error = NULL; + gchar * value = NULL; + + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_STRING, &value, G_TYPE_INVALID)) { + g_warning("Unable to get autologin setting: %s", error != NULL ? error->message : "null"); + g_error_free(error); + return; + } + + g_return_if_fail(value != NULL); + gdm_settings_change(proxy, gdm_auto_login_string, NULL, value, NULL); + + return; +} + +/* Sets up the proxy and queries for the setting to know + whether we're doing an autologin. */ +static gboolean +build_gdm_proxy (gpointer null_data) +{ + g_return_val_if_fail(gdm_settings_proxy == NULL, FALSE); + + /* Grab the system bus */ + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); + g_return_val_if_fail(bus != NULL, FALSE); + + /* Get the settings proxy */ + gdm_settings_proxy = dbus_g_proxy_new_for_name_owner(bus, + "org.gnome.DisplayManager", + "/org/gnome/DisplayManager/Settings", + "org.gnome.DisplayManager.Settings", NULL); + g_return_val_if_fail(gdm_settings_proxy != NULL, FALSE); + + /* Signal for value changed */ + dbus_g_proxy_add_signal(gdm_settings_proxy, + "ValueChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(gdm_settings_proxy, + "ValueChanged", + G_CALLBACK(gdm_settings_change), + NULL, + NULL); + + /* Start to get the initial value */ + dbus_g_proxy_begin_call(gdm_settings_proxy, + "GetValue", + gdm_get_autologin, + NULL, + NULL, + G_TYPE_STRING, + gdm_auto_login_string, + G_TYPE_INVALID); + + return FALSE; +} + +/* A fun little function to actually lock the screen. If, + that's what you want, let's do it! */ +static void +lock_screen (void) +{ + g_debug("Lock Screen"); + if (gdm_auto_login) { + g_debug("\tGDM set to autologin, blocking lock"); + return; + } + + DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); + g_return_if_fail(session_bus != NULL); + + DBusGProxy * proxy = dbus_g_proxy_new_for_name_owner(session_bus, + "org.gnome.ScreenSaver", + "/", + "org.gnome.ScreenSaver", + NULL); + g_return_if_fail(proxy != NULL); + + dbus_g_proxy_call_no_reply(proxy, + "Lock", + G_TYPE_INVALID, + G_TYPE_INVALID); + + g_object_unref(proxy); + + return; +} + /* Let's put this machine to sleep, with some info on how it should sleep. */ static void @@ -64,6 +185,8 @@ sleep (DbusmenuMenuitem * mi, gpointer userdata) g_warning("Can not %s as no DeviceKit Power Proxy", type); } + lock_screen(); + dbus_g_proxy_call_no_reply(dkp_main_proxy, type, G_TYPE_INVALID, @@ -304,6 +427,8 @@ main (int argc, char ** argv) return 1; } + g_idle_add(build_gdm_proxy, NULL); + root_menuitem = dbusmenu_menuitem_new(); g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); diff --git a/src/status-service.c b/src/status-service.c index d8b3631..a448ada 100644 --- a/src/status-service.c +++ b/src/status-service.c @@ -7,16 +7,16 @@ Copyright 2009 Canonical Ltd. Authors: Ted Gould <ted@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 +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 +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 +You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ @@ -240,12 +240,12 @@ main (int argc, char ** argv) if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_STATUS_DBUS_NAME, 0, &nameret, &error)) { g_error("Unable to call to request name"); return 1; - } + } if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { g_error("Unable to get name"); return 1; - } + } g_idle_add(build_providers, NULL); diff --git a/src/users-service-dbus.c b/src/users-service-dbus.c new file mode 100644 index 0000000..e05eaf5 --- /dev/null +++ b/src/users-service-dbus.c @@ -0,0 +1,1147 @@ +/* + * Copyright 2009 Canonical Ltd. + * + * Authors: + * Cody Russell <crussell@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/>. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <errno.h> +#include <pwd.h> + +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib.h> +#include <dbus/dbus-glib-lowlevel.h> + +#include "dbus-shared-names.h" +#include "users-service-dbus.h" +#include "users-service-client.h" +#include "users-service-marshal.h" + +static void users_service_dbus_class_init (UsersServiceDbusClass *klass); +static void users_service_dbus_init (UsersServiceDbus *self); +static void users_service_dbus_dispose (GObject *object); +static void users_service_dbus_finalize (GObject *object); +static void create_gdm_proxy (UsersServiceDbus *self); +static void create_seat_proxy (UsersServiceDbus *self); +static void create_ck_proxy (UsersServiceDbus *self); +static void create_cksession_proxy (UsersServiceDbus *self); +static gchar *get_seat (UsersServiceDbus *service); +static void users_loaded (DBusGProxy *proxy, + gpointer user_data); +static void user_added (DBusGProxy *proxy, + gint64 uid, + gpointer user_data); +static void user_removed (DBusGProxy *proxy, + gint64 uid, + gpointer user_data); +static void user_updated (DBusGProxy *proxy, + guint uid, + gpointer user_data); +static void seat_proxy_session_added (DBusGProxy *seat_proxy, + const gchar *session_id, + UsersServiceDbus *service); +static void seat_proxy_session_removed (DBusGProxy *seat_proxy, + const gchar *session_id, + UsersServiceDbus *service); +static gboolean do_add_session (UsersServiceDbus *service, + UserData *user, + const gchar *ssid); +static gchar * get_seat_internal (UsersServiceDbus *self); + +/* Private */ +typedef struct _UsersServiceDbusPrivate UsersServiceDbusPrivate; + +struct _UsersServiceDbusPrivate +{ + GHashTable *users; + gint count; + gchar *seat; + gchar *ssid; + + DBusGConnection *system_bus; + + DBusGProxy *gdm_proxy; + DBusGProxy *ck_proxy; + DBusGProxy *seat_proxy; + DBusGProxy *session_proxy; + + GHashTable *exclusions; + GHashTable *sessions; +}; + +#define USERS_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusPrivate)) + +/* Signals */ +enum { + USERS_LOADED, + USER_ADDED, + USER_REMOVED, + USER_UPDATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* GObject Boilerplate */ +G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT); + +static void +users_service_dbus_class_init (UsersServiceDbusClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate)); + + object_class->dispose = users_service_dbus_dispose; + object_class->finalize = users_service_dbus_finalize; + + signals[USERS_LOADED] = g_signal_new ("users-loaded", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (UsersServiceDbusClass, users_loaded), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + signals[USER_ADDED] = g_signal_new ("user-added", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (UsersServiceDbusClass, user_added), + NULL, NULL, + _users_service_marshal_VOID__INT64, + G_TYPE_NONE, 1, G_TYPE_INT64); + + signals[USER_REMOVED] = g_signal_new ("user-removed", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (UsersServiceDbusClass, user_removed), + NULL, NULL, + _users_service_marshal_VOID__INT64, + G_TYPE_NONE, 1, G_TYPE_INT64); + + signals[USER_UPDATED] = g_signal_new ("user-updated", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (UsersServiceDbusClass, user_updated), + NULL, NULL, + _users_service_marshal_VOID__INT64, + G_TYPE_NONE, 1, G_TYPE_INT64); +} + +static void +users_service_dbus_init (UsersServiceDbus *self) +{ + GError *error = NULL; + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + + priv->users = NULL; + priv->count = 0; + + /* Get the system bus */ + priv->system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error); + if (error != NULL) + { + g_error ("Unable to get system bus: %s", error->message); + g_error_free(error); + + return; + } + + priv->sessions = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + g_free); + + priv->users = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + dbus_g_object_register_marshaller (_users_service_marshal_VOID__INT64, + G_TYPE_NONE, + G_TYPE_INT64, + G_TYPE_INVALID); + + create_gdm_proxy (self); + create_ck_proxy (self); + create_seat_proxy (self); + + users_loaded (priv->gdm_proxy, self); +} + +static void +users_service_dbus_dispose (GObject *object) +{ + G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object); +} + +static void +users_service_dbus_finalize (GObject *object) +{ + G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object); +} + +static void +create_gdm_proxy (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + + priv->gdm_proxy = dbus_g_proxy_new_for_name_owner (priv->system_bus, + "org.gnome.DisplayManager", + "/org/gnome/DisplayManager/UserManager", + "org.gnome.DisplayManager.UserManager", + &error); + + if (!priv->gdm_proxy) + { + if (error != NULL) + { + g_error ("Unable to get DisplayManager proxy on system bus: %s", error->message); + g_error_free (error); + } + + return; + } + + dbus_g_proxy_add_signal (priv->gdm_proxy, + "UsersLoaded", + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->gdm_proxy, + "UserAdded", + G_TYPE_INT64, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->gdm_proxy, + "UserRemoved", + G_TYPE_INT64, + G_TYPE_INVALID); + + dbus_g_proxy_add_signal (priv->gdm_proxy, + "UserUpdated", + G_TYPE_INT64, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->gdm_proxy, + "UsersLoaded", + G_CALLBACK (users_loaded), + self, + NULL); + + dbus_g_proxy_connect_signal (priv->gdm_proxy, + "UserAdded", + G_CALLBACK (user_added), + self, + NULL); + + dbus_g_proxy_connect_signal (priv->gdm_proxy, + "UserRemoved", + G_CALLBACK (user_removed), + self, + NULL); + + dbus_g_proxy_connect_signal (priv->gdm_proxy, + "UserUpdated", + G_CALLBACK (user_updated), + self, + NULL); +} + +static void +create_ck_proxy (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + + priv->ck_proxy = dbus_g_proxy_new_for_name (priv->system_bus, + "org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager"); + + if (!priv->ck_proxy) + { + g_warning ("Failed to get ConsoleKit proxy."); + return; + } +} + +static void +create_seat_proxy (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + + priv->seat = get_seat (self); + if (priv->seat == NULL) + { + return; + } + + priv->seat_proxy = dbus_g_proxy_new_for_name_owner (priv->system_bus, + "org.freedesktop.ConsoleKit", + priv->seat, + "org.freedesktop.ConsoleKit.Seat", + &error); + + if (!priv->seat_proxy) + { + if (error != NULL) + { + g_warning ("Failed to connect to the ConsoleKit seat: %s", error->message); + g_error_free (error); + } + + return; + } + + dbus_g_proxy_add_signal (priv->seat_proxy, + "SessionAdded", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + dbus_g_proxy_add_signal (priv->seat_proxy, + "SessionRemoved", + DBUS_TYPE_G_OBJECT_PATH, + G_TYPE_INVALID); + + dbus_g_proxy_connect_signal (priv->seat_proxy, + "SessionAdded", + G_CALLBACK (seat_proxy_session_added), + self, + NULL); + dbus_g_proxy_connect_signal (priv->seat_proxy, + "SessionRemoved", + G_CALLBACK (seat_proxy_session_removed), + self, + NULL); +} + +static void +create_cksession_proxy (UsersServiceDbus *service) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + + priv->session_proxy = dbus_g_proxy_new_for_name (priv->system_bus, + "org.freedesktop.ConsoleKit", + priv->ssid, + "org.freedesktop.ConsoleKit.Session"); + + if (!priv->session_proxy) + { + g_warning ("Failed to get ConsoleKit session proxy"); + return; + } +} + +static gchar * +get_seat (UsersServiceDbus *service) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + GError *error = NULL; + gchar *ssid = NULL; + gchar *seat; + + if (!dbus_g_proxy_call (priv->ck_proxy, + "GetCurrentSession", + &error, + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, + &ssid, + G_TYPE_INVALID)) + { + if (error) + { + g_debug ("Failed to call GetCurrentSession: %s", error->message); + g_error_free (error); + } + + if (ssid) + g_free (ssid); + + return NULL; + } + + priv->ssid = ssid; + create_cksession_proxy (service); + + seat = get_seat_internal (service); + + return seat; +} + +static gchar * +get_seat_internal (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + gchar *seat = NULL; + + if (!dbus_g_proxy_call (priv->session_proxy, + "GetSeatId", + &error, + G_TYPE_INVALID, + DBUS_TYPE_G_OBJECT_PATH, &seat, + G_TYPE_INVALID)) + { + if (error) + { + g_debug ("Failed to call GetSeatId: %s", error->message); + + return NULL; + } + } + + return seat; +} + +static gboolean +get_unix_user (UsersServiceDbus *service, + const gchar *session_id, + uid_t *uidp) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + GError *error = NULL; + guint uid; + + if (dbus_g_proxy_call (priv->session_proxy, + "GetUnixUser", + &error, + G_TYPE_INVALID, + G_TYPE_UINT, &uid, + G_TYPE_INVALID)) + { + if (error) + { + g_warning ("Failed to call GetUnixUser: %s", error->message); + g_error_free (error); + } + + return FALSE; + } + + if (uidp != NULL) + { + *uidp = (uid_t)uid; + } + + return TRUE; +} + +static gchar * +get_session_for_user (UsersServiceDbus *service, + UserData *user) +{ + GList *l; + + if (!users_service_dbus_can_activate_session (service)) + { + return NULL; + } + + if (!user->sessions || g_list_length (user->sessions) == 0) + { + return NULL; + } + + for (l = user->sessions; l != NULL; l = l->next) + { + const char *ssid; + + ssid = l->data; + + if (ssid) + { + return g_strdup (ssid); + } + } + + return NULL; +} + +static gboolean +do_add_session (UsersServiceDbus *service, + UserData *user, + const gchar *ssid) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + GError *error = NULL; + gchar *seat = NULL; + gchar *xdisplay = NULL; + GList *l; + + seat = get_seat_internal (service); + + if (!seat || !priv->seat || strcmp (seat, priv->seat) != 0) + return FALSE; + + if (!dbus_g_proxy_call (priv->session_proxy, + "GetX11Display", + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &xdisplay, + G_TYPE_INVALID)) + { + if (error) + { + g_debug ("Failed to call GetX11Display: %s", error->message); + g_error_free (error); + } + + return FALSE; + } + + if (!xdisplay || xdisplay[0] == '\0') + return FALSE; + + g_hash_table_insert (priv->sessions, + g_strdup (ssid), + g_strdup (user->user_name)); + + l = g_list_find_custom (user->sessions, ssid, (GCompareFunc)g_strcmp0); + if (l == NULL) + { + g_debug ("Adding session %s", ssid); + + user->sessions = g_list_prepend (user->sessions, g_strdup (ssid)); + } + else + { + g_debug ("User %s already has session %s", user->user_name, ssid); + } + + return TRUE; +} + +static void +add_sessions_for_user (UsersServiceDbus *self, + UserData *user) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error; + GPtrArray *sessions; + int i; + + error = NULL; + if (!dbus_g_proxy_call (priv->ck_proxy, + "GetSessionsForUnixUser", + &error, + G_TYPE_UINT, user->uid, + G_TYPE_INVALID, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), + &sessions, + G_TYPE_INVALID)) + { + g_debug ("Failed to call GetSessionsForUnixUser: %s", error->message); + g_error_free (error); + + return; + } + + for (i = 0; i < sessions->len; i++) + { + char *ssid; + + ssid = g_ptr_array_index (sessions, i); + do_add_session (self, user, ssid); + } + + g_ptr_array_foreach (sessions, (GFunc)g_free, NULL); + g_ptr_array_free (sessions, TRUE); +} + + +static void +seat_proxy_session_added (DBusGProxy *seat_proxy, + const gchar *session_id, + UsersServiceDbus *service) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + uid_t uid; + gboolean res; + struct passwd *pwent; + UserData *user; + + if (!get_unix_user (service, session_id, &uid)) + { + g_warning ("Failed to lookup user for session"); + return; + } + + errno = 0; + pwent = getpwuid (uid); + if (!pwent) + { + g_warning ("Failed to lookup user id %d: %s", (int)uid, g_strerror (errno)); + return; + } + + user = g_hash_table_lookup (priv->users, pwent->pw_name); + if (!user) + { + return; + } + + res = do_add_session (service, user, session_id); +} + +static void +seat_proxy_session_removed (DBusGProxy *seat_proxy, + const gchar *session_id, + UsersServiceDbus *service) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + UserData *user; + gchar *username; + GList *l; + + username = g_hash_table_lookup (priv->sessions, session_id); + if (!username) + return; + + user = g_hash_table_lookup (priv->users, username); + if (!user) + return; + + l = g_list_find_custom (user->sessions, + session_id, + (GCompareFunc)g_strcmp0); + if (l) + { + g_debug ("Removing session %s", session_id); + + g_free (l->data); + user->sessions = g_list_delete_link (user->sessions, l); + } + else + { + g_debug ("Session not found: %s", session_id); + } +} + +static void +sync_users (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + + if (g_hash_table_size (priv->users) > 0) + { + return; + } + + if (priv->count > MINIMUM_USERS && priv->count < MAXIMUM_USERS) + { + GArray *uids = NULL; + GPtrArray *users_info = NULL; + GError *error = NULL; + gint i; + + uids = g_array_new (FALSE, FALSE, sizeof (gint64)); + + if (!org_gnome_DisplayManager_UserManager_get_user_list (priv->gdm_proxy, + &uids, + &error)) + { + g_warning ("failed to retrieve user list: %s", error->message); + g_error_free (error); + + return; + } + + users_info = g_ptr_array_new (); + + if (!org_gnome_DisplayManager_UserManager_get_users_info (priv->gdm_proxy, + uids, + &users_info, + &error)) + { + g_warning ("failed to retrieve user info: %s", error->message); + g_error_free (error); + + return; + } + + for (i = 0; i < users_info->len; i++) + { + GValueArray *values; + UserData *user; + + values = g_ptr_array_index (users_info, i); + + user = g_new0 (UserData, 1); + + user->uid = g_value_get_int64 (g_value_array_get_nth (values, 0)); + user->user_name = g_strdup (g_value_get_string (g_value_array_get_nth (values, 1))); + user->real_name = g_strdup (g_value_get_string (g_value_array_get_nth (values, 2))); + user->shell = g_strdup (g_value_get_string (g_value_array_get_nth (values, 3))); + user->login_count = g_value_get_int (g_value_array_get_nth (values, 4)); + user->icon_url = g_strdup (g_value_get_string (g_value_array_get_nth (values, 5))); + + g_hash_table_insert (priv->users, + g_strdup (user->user_name), + user); + + add_sessions_for_user (self, user); + } + } +} + +static void +users_loaded (DBusGProxy *proxy, + gpointer user_data) +{ + UsersServiceDbus *service; + UsersServiceDbusPrivate *priv; + GError *error = NULL; + gint count; + + service = (UsersServiceDbus *)user_data; + priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + + if (!org_gnome_DisplayManager_UserManager_count_users (proxy, + &count, + &error)) + { + g_warning ("failed to retrieve user count: %s", error->message); + g_error_free (error); + + return; + } + + priv->count = count; + + sync_users (service); +} + +static gboolean +session_is_login_window (UsersServiceDbus *self, + const char *ssid) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + DBusGProxy *proxy = NULL; + GError *error = NULL; + char *type = NULL; + + if (!(proxy = dbus_g_proxy_new_for_name (priv->system_bus, + "org.freedesktop.ConsoleKit", + ssid, + "org.freedesktop.ConsoleKit.Session"))) + { + g_warning ("Failed to get ConsoleKit proxy"); + + return FALSE; + } + + if (!dbus_g_proxy_call (proxy, + "GetSessionType", + &error, + G_TYPE_INVALID, + G_TYPE_STRING, &type, + G_TYPE_INVALID)) + { + g_warning ("Can't call GetSessionType: %s", error->message); + g_error_free (error); + + if (proxy) + g_object_unref (proxy); + + return FALSE; + } + + if (proxy) + g_object_unref (proxy); + + return (type && type[0] != '\0' && strcmp (type, "LoginWindow") == 0); +} + +static char * +get_login_session (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + gboolean can_activate; + GError *error = NULL; + GPtrArray *sessions = NULL; + char *ssid = NULL; + int i; + + if (!priv->seat || priv->seat[0] == '\0') + { + return NULL; + } + + if (!dbus_g_proxy_call (priv->seat_proxy, + "CanActivateSessions", + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_activate, + G_TYPE_INVALID)) + { + g_warning ("Failed to call CanActivateSessions: %s", error->message); + g_error_free (error); + + return NULL; + } + + if (!can_activate) + { + return NULL; + } + + error = NULL; + if (!dbus_g_proxy_call (priv->seat_proxy, + "GetSessions", + &error, + G_TYPE_INVALID, + dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions, + G_TYPE_INVALID)) + { + g_warning ("Failed to call GetSessions: %s", error->message); + g_error_free (error); + + return NULL; + } + + for (i = 0; i < sessions->len; i++) + { + char *s = g_ptr_array_index (sessions, i); + + if (session_is_login_window (self, s)) + { + ssid = g_strdup (s); + break; + } + } + + g_ptr_array_foreach (sessions, (GFunc)g_free, NULL); + g_ptr_array_free (sessions, TRUE); + + return ssid; +} + +static gboolean +activate_user_session (UsersServiceDbus *self, + const char *seat, + const char *ssid) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusError error; + + if (!(message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", + seat, + "org.freedesktop.ConsoleKit.Seat", + "ActivateSession"))) + { + return FALSE; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_OBJECT_PATH, &ssid, + DBUS_TYPE_INVALID)) + { + return FALSE; + } + + dbus_error_init (&error); + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (priv->system_bus), + message, + -1, + &error))) + { + if (dbus_error_is_set (&error)) + { + g_warning ("Can't activate session: %s", error.message); + dbus_error_free (&error); + + return FALSE; + } + } + + if (message) + { + dbus_message_unref (message); + } + + if (reply) + { + dbus_message_unref (reply); + } + + return TRUE; +} + +gboolean +start_new_user_session (UsersServiceDbus *self, + UserData *user) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + char *ssid; + + ssid = get_login_session (self); + if (ssid) + { + if (!activate_user_session (self, priv->seat, ssid)) + { + return FALSE; + } + } + + if (!g_spawn_command_line_async ("gdmflexiserver -s", &error)) + { + g_warning ("Failed to start new login session: %s", error->message); + g_error_free (error); + + return FALSE; + } + + return TRUE; +} + +static void +user_added (DBusGProxy *proxy, + gint64 uid, + gpointer user_data) +{ + UsersServiceDbus *service = (UsersServiceDbus *)user_data; + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + UserData *user = g_new0 (UserData, 1); + GError *error = NULL; + + priv->count++; + + if (priv->count < MAXIMUM_USERS) + { + if ((priv->count - g_hash_table_size (priv->users)) > 1) + { + sync_users (service); + } + else + { + if (!org_gnome_DisplayManager_UserManager_get_user_info (proxy, + uid, + &user->user_name, + &user->real_name, + &user->shell, + &user->login_count, + &user->icon_url, + &error)) + { + g_warning ("unable to retrieve user info: %s", error->message); + g_error_free (error); + + g_free (user); + + return; + } + + user->uid = uid; + + g_hash_table_insert (priv->users, + g_strdup (user->user_name), + user); + + g_signal_emit (G_OBJECT (service), signals[USER_ADDED], 0, user, TRUE); + } + } +} + +static gboolean +compare_users_by_uid (gpointer key, + gpointer value, + gpointer user_data) +{ + return (GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (user_data)); +} + +static void +user_removed (DBusGProxy *proxy, + gint64 uid, + gpointer user_data) +{ + UsersServiceDbus *service = (UsersServiceDbus *)user_data; + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + UserData *user; + gint size; + + size = g_hash_table_size (priv->users); + + priv->count--; + + if (size == 0 || (size - priv->count) > 1) + { + sync_users (service); + } + else + { + user = g_hash_table_find (priv->users, + compare_users_by_uid, + GUINT_TO_POINTER ((gint)uid)); + + if (user != NULL) + { + g_hash_table_remove (priv->users, + user->user_name); + } + } +} + +static void +user_updated (DBusGProxy *proxy, + guint uid, + gpointer user_data) +{ +#if 0 + // XXX - TODO + UsersServiceDbus *service = (UsersServiceDbus *)user_data; + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + UserData *user; + + user = g_hash_table_find (priv->users, + compare_users_by_uid, + GUINT_TO_POINTER (uid)); +#endif +} + +gint +users_service_dbus_get_user_count (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + + return priv->count; +} + +GList * +users_service_dbus_get_user_list (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + + return g_hash_table_get_values (priv->users); +} + +/* + * XXX - TODO: Right now we switch to a session that another user + * already has open, but if there are no open sessions + * for this user we go to the login screen and the + * user at the seat must select a user and enter a + * password. This kind of defeats the purpose of + * actually selecting a username, since selecting any + * user will do the same thing here. We need to change + * it so you only need to enter a password for the + * specified user. + */ +gboolean +users_service_dbus_activate_user_session (UsersServiceDbus *self, + UserData *user) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + DBusMessage *message = NULL; + DBusMessage *reply = NULL; + DBusError error; + gchar *ssid; + + dbus_error_init (&error); + + if (!priv->seat) + priv->seat = get_seat (self); + + ssid = get_session_for_user (self, user); + + if (!ssid) + { + return start_new_user_session (self, user); + } + + if (!(message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit", + priv->seat, + "org.freedesktop.ConsoleKit.Seat", + "ActivateSession"))) + { + g_warning ("failed to create new message"); + return FALSE; + } + + if (!dbus_message_append_args (message, + DBUS_TYPE_OBJECT_PATH, + &ssid, + DBUS_TYPE_INVALID)) + { + g_warning ("failed to append args"); + return FALSE; + } + + if (!(reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (priv->system_bus), + message, + -1, + &error))) + { + if (dbus_error_is_set (&error)) + { + g_warning ("Failed to send message: %s", error.message); + dbus_error_free (&error); + + return FALSE; + } + } + + if (message) + { + dbus_message_unref (message); + } + + if (reply) + { + dbus_message_unref (reply); + } + + return TRUE; +} + +gboolean +users_service_dbus_can_activate_session (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + gboolean can_activate = FALSE; + GError *error = NULL; + + if (!priv->seat_proxy) + { + create_seat_proxy (self); + } + + if (!priv->seat || priv->seat[0] == '\0') + { + return FALSE; + } + + if (!dbus_g_proxy_call (priv->seat_proxy, + "CanActivateSessions", + &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &can_activate, + G_TYPE_INVALID)) + { + g_warning ("Failed to determine if seat can activate sessions: %s", error->message); + g_error_free (error); + + return FALSE; + } + + return can_activate; +} diff --git a/src/users-service-dbus.h b/src/users-service-dbus.h new file mode 100644 index 0000000..fc14beb --- /dev/null +++ b/src/users-service-dbus.h @@ -0,0 +1,81 @@ +/* + * Copyright 2009 Canonical Ltd. + * + * Authors: + * Cody Russell <crussell@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_SERVICE_DBUS_H__ +#define __USERS_SERVICE_DBUS_H__ + +#include <glib.h> +#include <glib-object.h> + +G_BEGIN_DECLS + +#define USERS_SERVICE_DBUS_TYPE (users_service_dbus_get_type ()) +#define USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbus)) +#define USERS_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusClass)) +#define IS_USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), USERS_SERVICE_DBUS_TYPE)) +#define IS_USERS_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), USERS_SERVICE_DBUS_TYPE)) +#define USERS_SERVICE_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusClass)) + +typedef struct _UsersServiceDbus UsersServiceDbus; +typedef struct _UsersServiceDbusClass UsersServiceDbusClass; +typedef struct _UserData UserData; + +struct _UserData +{ + gint64 uid; + gchar *user_name; + gchar *real_name; + gchar *shell; + gint login_count; + gchar *icon_url; + + GList *sessions; + + UsersServiceDbus *service; +}; + +#define MINIMUM_USERS 1 +#define MAXIMUM_USERS 7 + +struct _UsersServiceDbus { + GObject parent; +}; + +struct _UsersServiceDbusClass { + GObjectClass parent_class; + + /* Signals */ + void (* users_loaded) (UsersServiceDbus *self, gpointer user_data); + + void (* user_added) (UsersServiceDbus *self, gint64 uid, gpointer user_data); + void (* user_removed) (UsersServiceDbus *self, gint64 uid, gpointer user_data); + void (* user_updated) (UsersServiceDbus *self, gint64 uid, gpointer user_data); +}; + +GType users_service_dbus_get_type (void) G_GNUC_CONST; + +gint users_service_dbus_get_user_count (UsersServiceDbus *self); +GList *users_service_dbus_get_user_list (UsersServiceDbus *self); +gboolean users_service_dbus_can_activate_session (UsersServiceDbus *self); +gboolean users_service_dbus_activate_user_session (UsersServiceDbus *self, + UserData *user); + +G_END_DECLS + +#endif diff --git a/src/users-service.c b/src/users-service.c index 7ad1732..dc11e8d 100644 --- a/src/users-service.c +++ b/src/users-service.c @@ -1,24 +1,25 @@ /* -A small wrapper utility to load indicators and put them as menu items -into the gnome-panel using it's applet interface. - -Copyright 2009 Canonical Ltd. - -Authors: - Ted Gould <ted@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/>. -*/ + * A small wrapper utility to load indicators and put them as menu items + * into the gnome-panel using it's applet interface. + * + * Copyright 2009 Canonical Ltd. + * + * Authors: + * Ted Gould <ted@canonical.com> + * Cody Russell <crussell@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 <config.h> @@ -33,15 +34,116 @@ with this program. If not, see <http://www.gnu.org/licenses/>. #include <libdbusmenu-glib/menuitem.h> #include "dbus-shared-names.h" +#include "users-service-dbus.h" #define GUEST_SESSION_LAUNCHER "/usr/share/gdm/guest-session/guest-session-launch" -static DBusGConnection * session_bus = NULL; -static DBusGConnection * system_bus = NULL; -static DBusGProxy * bus_proxy = NULL; -static DBusGProxy * gdm_proxy = NULL; -static DbusmenuMenuitem * root_menuitem = NULL; -static GMainLoop * mainloop = NULL; +typedef struct _ActivateData ActivateData; +struct _ActivateData +{ + UsersServiceDbus *service; + UserData *user; +}; + +static DBusGConnection *session_bus = NULL; +static DBusGConnection *system_bus = NULL; +static DBusGProxy *bus_proxy = NULL; +static DBusGProxy *gdm_proxy = NULL; +static DbusmenuMenuitem *root_menuitem = NULL; +static GMainLoop *mainloop = NULL; +static UsersServiceDbus *dbus_interface = NULL; + +static DBusGProxy * gdm_settings_proxy = NULL; +static gboolean gdm_auto_login = FALSE; +static const gchar * gdm_auto_login_string = "daemon/AutomaticLoginEnable"; + +static gint count; +static GList *users; + +/* Respond to the signal of autologin changing to see if the + setting for timed login changes. */ +static void +gdm_settings_change (DBusGProxy * proxy, const gchar * value, const gchar * old, const gchar * new, gpointer data) +{ + if (g_strcmp0(value, gdm_auto_login_string)) { + /* This is not a setting that we care about, + there is only one. */ + return; + } + g_debug("GDM Settings change: %s", new); + + if (g_strcmp0(new, "true") == 0) { + gdm_auto_login = TRUE; + } else { + gdm_auto_login = FALSE; + } + + return; +} + +/* Get back the data from querying to see if there is auto + login enabled in GDM */ +static void +gdm_get_autologin (DBusGProxy * proxy, DBusGProxyCall * call, gpointer data) +{ + GError * error = NULL; + gchar * value = NULL; + + if (!dbus_g_proxy_end_call(proxy, call, &error, G_TYPE_STRING, &value, G_TYPE_INVALID)) { + g_warning("Unable to get autologin setting: %s", error != NULL ? error->message : "null"); + g_error_free(error); + return; + } + + g_return_if_fail(value != NULL); + gdm_settings_change(proxy, gdm_auto_login_string, NULL, value, NULL); + + return; +} + +/* Sets up the proxy and queries for the setting to know + whether we're doing an autologin. */ +static gboolean +build_gdm_proxy (gpointer null_data) +{ + g_return_val_if_fail(gdm_settings_proxy == NULL, FALSE); + + /* Grab the system bus */ + DBusGConnection * bus = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL); + g_return_val_if_fail(bus != NULL, FALSE); + + /* Get the settings proxy */ + gdm_settings_proxy = dbus_g_proxy_new_for_name_owner(bus, + "org.gnome.DisplayManager", + "/org/gnome/DisplayManager/Settings", + "org.gnome.DisplayManager.Settings", NULL); + g_return_val_if_fail(gdm_settings_proxy != NULL, FALSE); + + /* Signal for value changed */ + dbus_g_proxy_add_signal(gdm_settings_proxy, + "ValueChanged", + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_STRING, + G_TYPE_INVALID); + dbus_g_proxy_connect_signal(gdm_settings_proxy, + "ValueChanged", + G_CALLBACK(gdm_settings_change), + NULL, + NULL); + + /* Start to get the initial value */ + dbus_g_proxy_begin_call(gdm_settings_proxy, + "GetValue", + gdm_get_autologin, + NULL, + NULL, + G_TYPE_STRING, + gdm_auto_login_string, + G_TYPE_INVALID); + + return FALSE; +} static gboolean check_guest_session (void) @@ -115,6 +217,10 @@ static void lock_screen (DbusmenuMenuitem * mi, gpointer data) { g_debug("Lock Screen"); + if (gdm_auto_login) { + g_debug("\tGDM set to autologin, blocking lock"); + return; + } DBusGConnection * session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); g_return_if_fail(session_bus != NULL); @@ -137,29 +243,129 @@ lock_screen (DbusmenuMenuitem * mi, gpointer data) } static void -create_items (DbusmenuMenuitem * root) { - DbusmenuMenuitem * mi = NULL; - - mi = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Lock Screen")); - g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(lock_screen), NULL); - dbusmenu_menuitem_child_append(root, mi); - - if (check_guest_session()) { - mi = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Guest Session")); - dbusmenu_menuitem_child_append(root, mi); - g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_guest_session), NULL); - } +activate_user_session (DbusmenuMenuitem *mi, gpointer user_data) +{ + UserData *user = (UserData *)user_data; + UsersServiceDbus *service = user->service; - if (check_new_session()) { - mi = dbusmenu_menuitem_new(); - dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Switch User...")); - dbusmenu_menuitem_child_append(root, mi); - g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_new_session), NULL); - } + users_service_dbus_activate_user_session (service, user); +} - return; +static gint +compare_users_by_username (const gchar *a, + const gchar *b) +{ + UserData *user1 = (UserData *)a; + UserData *user2 = (UserData *)b; + + return g_strcmp0 (user1->user_name, user2->user_name); +} + +static void +rebuild_items (DbusmenuMenuitem *root, + UsersServiceDbus *service) +{ + DbusmenuMenuitem *mi = NULL; + GList *u; + UserData *user; + gboolean can_activate; + GList *children; + + can_activate = users_service_dbus_can_activate_session (service); + + children = dbusmenu_menuitem_take_children (root); + g_list_foreach (children, (GFunc)g_object_unref, NULL); + g_list_free (children); + + mi = dbusmenu_menuitem_new(); + dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Lock Screen")); + g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(lock_screen), NULL); + dbusmenu_menuitem_child_append(root, mi); + + if (can_activate == TRUE) + { + if (check_guest_session ()) + { + mi = dbusmenu_menuitem_new (); + dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Guest Session")); + dbusmenu_menuitem_child_append (root, mi); + g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_guest_session), NULL); + } + + if (count > MINIMUM_USERS && count < MAXIMUM_USERS) + { + if (users != NULL) + { + GList *l = NULL; + + for (l = users; l != NULL; l = l->next) + { + users = g_list_delete_link (users, l); + } + + users = NULL; + } + + users = users_service_dbus_get_user_list (service); + + users = g_list_sort (users, (GCompareFunc)compare_users_by_username); + + for (u = users; u != NULL; u = g_list_next (u)) + { + user = u->data; + + user->service = service; + + mi = dbusmenu_menuitem_new (); + dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, user->real_name); + dbusmenu_menuitem_child_append (root, mi); + g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_user_session), user); + } + } + + if (check_new_session ()) + { + mi = dbusmenu_menuitem_new (); + dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, _("New Session...")); + dbusmenu_menuitem_child_append (root, mi); + g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_new_session), NULL); + } + } +} + +static void +user_added (UsersServiceDbus *service, + UserData *user, + gpointer user_data) +{ + DbusmenuMenuitem *root = (DbusmenuMenuitem *)user_data; + + count++; + + rebuild_items (root, service); +} + +static void +user_removed (UsersServiceDbus *service, + UserData *user, + gpointer user_data) +{ + DbusmenuMenuitem *root = (DbusmenuMenuitem *)user_data; + + count--; + + rebuild_items (root, service); +} + +static void +create_items (DbusmenuMenuitem *root, + UsersServiceDbus *service) +{ + g_return_if_fail (IS_USERS_SERVICE_DBUS (service)); + + count = users_service_dbus_get_user_count (service); + + rebuild_items (root, service); } int @@ -167,35 +373,48 @@ main (int argc, char ** argv) { g_type_init(); - /* Setting up i18n and gettext. Apparently, we need - all of these. */ - setlocale (LC_ALL, ""); - bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); - textdomain (GETTEXT_PACKAGE); + /* Setting up i18n and gettext. Apparently, we need + all of these. */ + setlocale (LC_ALL, ""); + bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR); + textdomain (GETTEXT_PACKAGE); session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL); - bus_proxy = dbus_g_proxy_new_for_name(session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); + bus_proxy = dbus_g_proxy_new_for_name (session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS); GError * error = NULL; guint nameret = 0; if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_USERS_DBUS_NAME, 0, &nameret, &error)) { g_error("Unable to call to request name"); return 1; - } + } if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { g_error("Unable to get name"); return 1; - } + } - root_menuitem = dbusmenu_menuitem_new(); - g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem)); + g_idle_add(build_gdm_proxy, NULL); - create_items(root_menuitem); + dbus_interface = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL); + + root_menuitem = dbusmenu_menuitem_new (); + g_debug ("Root ID: %d", dbusmenu_menuitem_get_id (root_menuitem)); + + create_items (root_menuitem, dbus_interface); DbusmenuServer * server = dbusmenu_server_new(INDICATOR_USERS_DBUS_OBJECT); dbusmenu_server_set_root(server, root_menuitem); + g_signal_connect (G_OBJECT (dbus_interface), + "user-added", + G_CALLBACK (user_added), + root_menuitem); + g_signal_connect (G_OBJECT (dbus_interface), + "user-removed", + G_CALLBACK (user_removed), + root_menuitem); + mainloop = g_main_loop_new(NULL, FALSE); g_main_loop_run(mainloop); diff --git a/src/users-service.list b/src/users-service.list new file mode 100644 index 0000000..36f34ba --- /dev/null +++ b/src/users-service.list @@ -0,0 +1 @@ +VOID:INT64 diff --git a/src/users-service.xml b/src/users-service.xml new file mode 100644 index 0000000..c90f1e8 --- /dev/null +++ b/src/users-service.xml @@ -0,0 +1,56 @@ +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> +<node name="/org/gnome/DisplayManager/UserManager"> + <interface name="org.gnome.DisplayManager.UserManager"> + + <!-- Get the number of known users --> + <method name="CountUsers"> + <arg name="user_count" direction="out" type="i"/> + </method> + + <!-- Get the list of known UIDs --> + <method name="GetUserList"> + <arg name="uids" direction="out" type="ax"/> + </method> + + <!-- Get user info for a user --> + <method name="GetUserInfo"> + <arg name="uid" direction="in" type="x"/> + <arg name="user_name" direction="out" type="s"/> + <arg name="real_name" direction="out" type="s"/> + <arg name="shell" direction="out" type="s"/> + <arg name="login_count" direction="out" type="i"/> + <arg name="icon_url" direction="out" type="s"/> + </method> + + <!-- Get user info for a list of users --> + <method name="GetUsersInfo"> + <arg name="uid" direction="in" type="ax"/> + <!-- (uid, user_name, real_name, shell, icon_url) --> + <arg name="user_info" direction="out" type="a(xsssis)"/> + </method> + + <!-- Query if the initial user list is loaded --> + <method name="GetUsersLoaded"> + <arg name="is_loaded" direction="out" type="b"/> + </method> + + <!-- Triggered when the initial user list is loaded --> + <signal name="UsersLoaded"></signal> + + <!-- Triggered when a users are added to/removed from the system. + Clients should monitor these signals as soon as they connect to + this object --> + <signal name="UserAdded"> + <arg name="uid" type="x"/> + </signal> + <signal name="UserRemoved"> + <arg name="uid" type="x"/> + </signal> + + <!-- Triggered when a user has updated information --> + <signal name="UserUpdated"> + <arg name="uid" type="x"/> + </signal> + + </interface> +</node> |