diff options
-rw-r--r-- | src/Makefile.am | 38 | ||||
-rw-r--r-- | src/dbus-shared-names.h | 14 | ||||
-rw-r--r-- | src/status-service.c | 16 | ||||
-rw-r--r-- | src/users-service-dbus.c | 1281 | ||||
-rw-r--r-- | src/users-service-dbus.h | 80 | ||||
-rw-r--r-- | src/users-service.c | 191 | ||||
-rw-r--r-- | src/users-service.list | 1 | ||||
-rw-r--r-- | src/users-service.xml | 56 |
8 files changed, 1617 insertions, 60 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index e2f0008..6404af0 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,11 @@ 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-server.h \ + users-service-marshal.c \ + users-service-marshal.h \ status-provider.h \ status-provider.c \ status-provider-pidgin.h \ @@ -38,6 +44,20 @@ 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 + +users-service-server.h: $(srcdir)/users-service.xml + dbus-binding-tool \ + --prefix=_users_service_server \ + --mode=glib-server \ + --output=users-service-server.h \ + $(srcdir)/users-service.xml + status-service-client.h: $(srcdir)/status-service.xml dbus-binding-tool \ --prefix=_status_service_client \ @@ -52,6 +72,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 \ @@ -76,7 +106,7 @@ status-provider-telepathy-marshal.c: $(srcdir)/status-provider-telepathy.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) @@ -93,8 +123,12 @@ indicator_session_service_LDADD = $(SESSIONSERVICE_LIBS) $(GCONF_LIBS) ############### BUILT_SOURCES = \ + users-service-client.h \ + users-service-server.h \ status-service-client.h \ status-service-server.h \ + 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/status-service.c b/src/status-service.c index 0cb9185..247726b 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/>. */ @@ -238,12 +238,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..15da3d2 --- /dev/null +++ b/src/users-service-dbus.c @@ -0,0 +1,1281 @@ +/* + * 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 gboolean _users_service_server_count_users (UsersServiceDbus *service, + gint *count, + GError **error); +static gboolean _users_service_server_get_users_loaded (UsersServiceDbus *self, + gboolean *is_loaded, + GError **error); +static gboolean _users_service_server_get_user_list (UsersServiceDbus *service, + GArray **list, + GError **error); +static gboolean _users_service_server_get_user_info (UsersServiceDbus *service, + const gint64 uid, + gchar **username, + gchar **real_name, + gchar **shell, + gint *login_count, + gchar **icon_url, + GError **error); +static gboolean _users_service_server_get_users_info (UsersServiceDbus *service, + const GArray *uids, + GPtrArray **user_info, + GError **error); +static void create_session_proxy (UsersServiceDbus *self); +static void create_system_proxy (UsersServiceDbus *self); +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, + guint uid, + gpointer user_data); +static void user_removed (DBusGProxy *proxy, + guint 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); + +#include "users-service-server.h" + +/* Private */ +typedef struct _UsersServiceDbusPrivate UsersServiceDbusPrivate; + +struct _UsersServiceDbusPrivate +{ + GHashTable *users; + gint count; + gchar *seat; + gchar *ssid; + + DBusGConnection *session_bus; + DBusGConnection *system_bus; + + DBusGProxy *dbus_proxy_session; + DBusGProxy *dbus_proxy_system; + 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); + + dbus_g_object_type_install_info (USERS_SERVICE_DBUS_TYPE, &dbus_glib__users_service_server_object_info); +} + +static void +users_service_dbus_init (UsersServiceDbus *self) +{ + GError *error = NULL; + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + gint i = 0; + const gchar *excludes[] = + { "bin", + "root", /* excludes taken from gdm */ + "daemon", + "adm", + "lp", + "sync", + "shutdown", + "halt", + "mail", + "news", + "uucp", + "operator", + "nobody", + "nobody4", + "noaccess", + "gdm", + "postgres", + "pvm", + "rpm", + "nfsnobody", + "pcap", + NULL + }; + + priv->users = NULL; + priv->count = 0; + + /* Get the buses */ + priv->session_bus = dbus_g_bus_get (DBUS_BUS_SESSION, &error); + if (error != NULL) + { + g_error ("Unable to get session bus: %s", error->message); + g_error_free (error); + + return; + } + + 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->exclusions = g_hash_table_new_full (g_str_hash, + g_str_equal, + g_free, + NULL); + + for (i = 0; excludes[i] != NULL; i++) + { + g_hash_table_insert (priv->exclusions, + g_strdup (excludes [i]), + GUINT_TO_POINTER (TRUE)); + } + + 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_connection_register_g_object (priv->session_bus, + INDICATOR_USERS_SERVICE_DBUS_OBJECT, + G_OBJECT (self)); + + dbus_g_object_register_marshaller (_users_service_marshal_VOID__INT64, + G_TYPE_NONE, + G_TYPE_INT64, + G_TYPE_INVALID); + + create_session_proxy (self); + create_system_proxy (self); + 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_session_proxy (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + + priv->dbus_proxy_session = dbus_g_proxy_new_for_name_owner (priv->session_bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + if (error != NULL) + { + g_error ("Unable to get dbus proxy on session bus: %s", error->message); + g_error_free (error); + + return; + } +} + +static void +create_system_proxy (UsersServiceDbus *self) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self); + GError *error = NULL; + + priv->dbus_proxy_system = dbus_g_proxy_new_for_name_owner (priv->system_bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + &error); + + if (!priv->dbus_proxy_system) + { + if (error != NULL) + { + g_error ("Unable to get dbus proxy on system bus: %s", error->message); + g_error_free (error); + + return; + } + } +} + +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 gint +session_compare (const gchar *a, + const gchar *b) +{ + if (a == NULL) + return 1; + else if (b == NULL) + return -1; + + return strcmp (a, b); +} + +static gchar * +get_session_for_user (UsersServiceDbus *service, + UserData *user) +{ + UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service); + gboolean can_activate; + GError *error = NULL; + GList *l; + + if (!priv->seat_proxy) + create_seat_proxy (service); + + if (priv->seat == NULL || 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 determine if seat can activate sessions: %s", error->message); + g_error_free (error); + + return NULL; + } + + if (!can_activate) + { + 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; + + if (g_hash_table_lookup (priv->exclusions, user->user_name)) + 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)session_compare); + 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; + } + + if (g_hash_table_lookup (priv->exclusions, pwent->pw_name)) + { + g_debug ("Excluding user %s", pwent->pw_name); + 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; + + // remove_session_from_user (user, session_id); ?? + + l = g_list_find_custom (user->sessions, + session_id, + (GCompareFunc)session_compare); + 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 +users_loaded (DBusGProxy *proxy, + gpointer user_data) +{ + UsersServiceDbus *service; + UsersServiceDbusPrivate *priv; + GError *error = NULL; + GArray *uids = NULL; + GPtrArray *users_info = NULL; + gint count; + int i; + + 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; + + uids = g_array_new (FALSE, FALSE, sizeof (gint64)); + + if (!org_gnome_DisplayManager_UserManager_get_user_list (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 (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 (service, user); + } +} + +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); +} + +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; +} + +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; +} + +static void +user_added (DBusGProxy *proxy, + guint 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 (!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, + guint uid, + gpointer user_data) +{ + 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)); + + g_hash_table_remove (priv->users, + user->user_name); + + priv->count--; +} + +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 +} + +static gboolean +_users_service_server_count_users (UsersServiceDbus *service, + gint *uids, + GError **error) +{ + return TRUE; +} + +static gboolean +_users_service_server_get_user_list (UsersServiceDbus *service, + GArray **list, + GError **error) +{ + return TRUE; +} + +static gboolean +_users_service_server_get_users_loaded (UsersServiceDbus *self, + gboolean *is_loaded, + GError **error) +{ + return TRUE; +} + +static gboolean +_users_service_server_get_user_info (UsersServiceDbus *service, + const gint64 uid, + gchar **username, + gchar **real_name, + gchar **shell, + gint *login_count, + char **icon_url, + GError **error) +{ + return TRUE; +} + +static gboolean +_users_service_server_get_users_info (UsersServiceDbus *service, + const GArray *uids, + GPtrArray **user_info, + GError **error) +{ + return TRUE; +} diff --git a/src/users-service-dbus.h b/src/users-service-dbus.h new file mode 100644 index 0000000..95db35d --- /dev/null +++ b/src/users-service-dbus.h @@ -0,0 +1,80 @@ +/* + * 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; +}; + +struct _UsersServiceDbus { + GObject parent; +}; + +struct _UsersServiceDbusClass { + GObjectClass parent_class; + + /* Signals */ + gboolean (*user_changed) (UsersServiceDbus *self, gchar **name, gpointer user_data); + gboolean (*users_icons_changed) (UsersServiceDbus *self, GArray **icons, gpointer user_data); + + 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_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..9fd123f 100644 --- a/src/users-service.c +++ b/src/users-service.c @@ -6,17 +6,18 @@ 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 +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/>. */ @@ -33,15 +34,29 @@ 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" +#define MINIMUM_USERS 1 +#define MAXIMUM_USERS 7 -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 gint count; +static GList *users; static gboolean check_guest_session (void) @@ -137,29 +152,106 @@ 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 void +remove_menu_item (DbusmenuMenuitem *root, gpointer user_data) +{ + DbusmenuMenuitem *child = (DbusmenuMenuitem *)user_data; + + dbusmenu_menuitem_child_delete (root, child); +} + +static void +rebuild_items (DbusmenuMenuitem *root, + UsersServiceDbus *service) +{ + DbusmenuMenuitem *mi = NULL; + GList *u; + UserData *user; + + dbusmenu_menuitem_foreach (root, remove_menu_item, 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); + } + + if (count > 1 && count < 7) + { + 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; + + users = g_list_append (users, user); + + rebuild_items (root, service); +} + +static void +user_removed (UsersServiceDbus *service, + UserData *user, + gpointer user_data) +{ + DbusmenuMenuitem *root = (DbusmenuMenuitem *)user_data; + + users = g_list_remove (users, user); + + 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); + if (count > 1 && count < 7) + { + users = users_service_dbus_get_user_list (service); + } + + rebuild_items (root, service); } int @@ -167,35 +259,46 @@ 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)); + dbus_interface = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL); - create_items(root_menuitem); + 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> |