Merge lp:~bratsche/indicator-session/users-service into lp:indicator-session/0.1

Proposed by Cody Russell
Status: Merged
Merged at revision: not available
Proposed branch: lp:~bratsche/indicator-session/users-service
Merge into: lp:indicator-session/0.1
Diff against target: 1768 lines
9 files modified
AUTHORS (+2/-0)
src/Makefile.am (+27/-2)
src/dbus-shared-names.h (+8/-6)
src/status-service.c (+8/-8)
src/users-service-dbus.c (+1147/-0)
src/users-service-dbus.h (+81/-0)
src/users-service.c (+191/-62)
src/users-service.list (+1/-0)
src/users-service.xml (+56/-0)
To merge this branch: bzr merge lp:~bratsche/indicator-session/users-service
Reviewer Review Type Date Requested Status
Ted Gould (community) Approve
Indicator Applet Developers Pending
Review via email: mp+12316@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Ted Gould (ted) wrote :

      * In users-service.c I don't see where count is updated. I think
        it should be incremented/decremented on user added/removed.
      * There should be icons on the users if they are set (probably
        should go in another branch/bug)
      * There doesn't seem to be any sorting of the user list. They
        should be sorted alphabetical by first name.
      * There are six signals defined in the Class structure but only 4
        created on class init.
      * I don't think that you need a call to
        dbus_g_object_type_install_info, that should only be needed if
        this object is being shared over DBus (server) but I think it's
        only a client.
      * Confirm with Robert, but I'm pretty sure the excludes list is
        already being applied on the GDM side. We shouldn't need to
        apply it on this side as well. That way there's only one copy
        of the list.
      * I think that dbus_g_connection_register_g_object can be dropped
        as well. Which I think is the only reason you're using the
        session bus, so I think that can be dropped.
      * Oh, now realizing more of the issue. Makefile.am shouldn't even
        be generating user-service-server.h. Drop that file.
      * I don't think that dbus_proxy_system and dbus_proxy_session are
        needed as we're not asking DBus itself for any information.
        We're just using it to communicate. We'd only need those
        proxies to query information about the bus itself.
      * Can session_compare be replaced with g_strcmp0? It seems about
        the same.
      * I was looking at "CanActivateSessions" call to ConsoleKit. Do
        you think we should check this earlier and make the menu items
        sensitive or insensitive based on this?
      * It looks like you're always getting the information on all the
        users. I think that we really don't care if it's over our max,
        so can we just not bother getting all that data if we're in
        overflow?
      * The guest user needs to be handled as well. If there is a guest
        already logged in selecting the guest on the menu shouldn't
        start a new session, but should go to the guest that is already
        running.

  review needs-fixing

review: Needs Fixing
42. By Cody Russell

modify count in user_added()/user_removed()

Revision history for this message
Cody Russell (bratsche) wrote :

> * I was looking at "CanActivateSessions" call to ConsoleKit. Do
> you think we should check this earlier and make the menu items
> sensitive or insensitive based on this?

Should the menuitems even be there if you can never click them? I went ahead and made an API on UsersServiceDbus that can check if the seat can activate sessions, and we can either make the menuitems insensitive or not add them or whatever. For now I didn't change the menu, just added the API.

I'm fixing up the remaining issues now.

Revision history for this message
Cody Russell (bratsche) wrote :

I'm going to sleep, but figured I'd document the status of this now.

> * In users-service.c I don't see where count is updated. I think
> it should be incremented/decremented on user added/removed.

Fixed.

> * There should be icons on the users if they are set (probably
> should go in another branch/bug)

TODO. Can we do this in a separate branch?

> * There doesn't seem to be any sorting of the user list. They
> should be sorted alphabetical by first name.

Fixed.

> * There are six signals defined in the Class structure but only 4
> created on class init.

TODO.

> * I don't think that you need a call to
> dbus_g_object_type_install_info, that should only be needed if
> this object is being shared over DBus (server) but I think it's
> only a client.

Fixed.

> * Confirm with Robert, but I'm pretty sure the excludes list is
> already being applied on the GDM side. We shouldn't need to
> apply it on this side as well. That way there's only one copy
> of the list.

Confirmed with Robert, and fixed.

> * I think that dbus_g_connection_register_g_object can be dropped
> as well. Which I think is the only reason you're using the
> session bus, so I think that can be dropped.

Fixed.

> * Oh, now realizing more of the issue. Makefile.am shouldn't even
> be generating user-service-server.h. Drop that file.

Fixed.

> * I don't think that dbus_proxy_system and dbus_proxy_session are
> needed as we're not asking DBus itself for any information.
> We're just using it to communicate. We'd only need those
> proxies to query information about the bus itself.

Fixed.

> * Can session_compare be replaced with g_strcmp0? It seems about
> the same.

Fixed.

> * I was looking at "CanActivateSessions" call to ConsoleKit. Do
> you think we should check this earlier and make the menu items
> sensitive or insensitive based on this?

See previous comment.

> * It looks like you're always getting the information on all the
> users. I think that we really don't care if it's over our max,
> so can we just not bother getting all that data if we're in
> overflow?

Fixed.

> * The guest user needs to be handled as well. If there is a guest
> already logged in selecting the guest on the menu shouldn't
> start a new session, but should go to the guest that is already
> running.

TODO.

Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2009-09-24 at 06:31 +0000, Cody Russell wrote:
> > * There should be icons on the users if they are set (probably
> > should go in another branch/bug)
>
> TODO. Can we do this in a separate branch?

Yeah, sounds good.

> > * The guest user needs to be handled as well. If there is a guest
> > already logged in selecting the guest on the menu shouldn't
> > start a new session, but should go to the guest that is already
> > running.
>
> TODO.

Let's put this in a new branch as well. We screwed this up in Jaunty,
so it's not a regression :)

43. By Cody Russell

don't need to dbus_g_object_type_install_info()

44. By Cody Russell

remove unnecessary stuff

45. By Cody Russell

remove session proxy fu

46. By Cody Russell

remove the server code

47. By Cody Russell

remove system proxy

48. By Cody Russell

use g_strcmp0

49. By Cody Russell

expose users_service_dbus_can_activate_session()

50. By Cody Russell

don't load all the users unless needed

51. By Cody Russell

cleanup

52. By Cody Russell

sort users

Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2009-09-24 at 04:37 +0000, Cody Russell wrote:
> > * I was looking at "CanActivateSessions" call to ConsoleKit. Do
> > you think we should check this earlier and make the menu items
> > sensitive or insensitive based on this?
>
> Should the menuitems even be there if you can never click them? I
> went ahead and made an API on UsersServiceDbus that can check if
> the seat can activate sessions, and we can either make the menuitems
> insensitive or not add them or whatever. For now I didn't change the
> menu, just added the API.

My thought is let's just hide them. The reason being that then sys
admins can use this as a lockdown mode. We have a bug on this from OEM
as well. But we need to hide all the switching items.

CC'ing MPT as well to get his comments.

Revision history for this message
Matthew Paul Thomas (mpt) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Ted Gould wrote on 24/09/09 14:59:
> On Thu, 2009-09-24 at 04:37 +0000, Cody Russell wrote:
>...
>> Should the menuitems even be there if you can never click them? I
>> went ahead and made an API on UsersServiceDbus that can check if
>> the seat can activate sessions, and we can either make the menuitems
>> insensitive or not add them or whatever. For now I didn't change the
>> menu, just added the API.
>
> My thought is let's just hide them. The reason being that then sys
> admins can use this as a lockdown mode. We have a bug on this from OEM
> as well. But we need to hide all the switching items.
>
> CC'ing MPT as well to get his comments.

What menu items are you talking about?

- --
Matthew Paul Thomas
http://mpt.net.nz/
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkq7fPAACgkQ6PUxNfU6ecrSFACfT4ei1ceDUHLVHTXLZL3RRG47
tKoAoIUnp8iDhrj2GuDM8fMgH+5+dm4q
=9HY0
-----END PGP SIGNATURE-----

Revision history for this message
Ted Gould (ted) wrote :

On Thu, 2009-09-24 at 14:09 +0000, Matthew Paul Thomas wrote:
> Ted Gould wrote on 24/09/09 14:59:
> > On Thu, 2009-09-24 at 04:37 +0000, Cody Russell wrote:
> >...
> >> Should the menuitems even be there if you can never click them? I
> >> went ahead and made an API on UsersServiceDbus that can check if
> >> the seat can activate sessions, and we can either make the menuitems
> >> insensitive or not add them or whatever. For now I didn't change the
> >> menu, just added the API.
> >
> > My thought is let's just hide them. The reason being that then sys
> > admins can use this as a lockdown mode. We have a bug on this from OEM
> > as well. But we need to hide all the switching items.
> >
> > CC'ing MPT as well to get his comments.
>
> What menu items are you talking about?

Basically every menu item involved with user switching. The command
that we have to console kit is whether we can do VT switching. So we'd
be disabling Guess Session, Switching to any user, or starting a new
session without logging out.

Revision history for this message
Cody Russell (bratsche) wrote :

> > * There are six signals defined in the Class structure but only 4
> > created on class init.
>
> TODO.

I think these other two must have been left over from something, because they're not in the .xml file. So I'm removing them and committing.

53. By Cody Russell

Remove user_changed and users_icons_changed from the class

54. By Cody Russell

only add menuitems if the current seat can activate new sessions

Revision history for this message
Cody Russell (bratsche) wrote :

> My thought is let's just hide them. The reason being that then sys
> admins can use this as a lockdown mode. We have a bug on this from OEM
> as well. But we need to hide all the switching items.
>
> CC'ing MPT as well to get his comments.

Okay, I've gone ahead and committed/pushed a revision that will only add the menuitems if the user has the ability to activate new sessions. If it turns out MPT doesn't like this, it'll be easy enough to change.

55. By Cody Russell

Don't check range twice here

Revision history for this message
Ted Gould (ted) wrote :

Looks good!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'AUTHORS'
2--- AUTHORS 2008-12-05 03:13:41 +0000
3+++ AUTHORS 2009-09-24 16:28:09 +0000
4@@ -0,0 +1,2 @@
5+Ted Gould <ted@canonical.com>
6+Cody Russell <crussell@canonical.com>
7
8=== modified file 'src/Makefile.am'
9--- src/Makefile.am 2009-09-02 14:17:44 +0000
10+++ src/Makefile.am 2009-09-24 16:28:09 +0000
11@@ -11,7 +11,8 @@
12 libsession_la_SOURCES = \
13 indicator-session.c \
14 dbus-shared-names.h \
15- status-service-client.h
16+ status-service-client.h \
17+ users-service-client.h
18 libsession_la_CFLAGS = $(APPLET_CFLAGS) -Wall -Werror
19 libsession_la_LIBADD = $(APPLET_LIBS)
20 libsession_la_LDFLAGS = -module -avoid-version
21@@ -25,6 +26,10 @@
22 status-service-dbus.h \
23 status-service-dbus.c \
24 status-service-server.h \
25+ users-service-dbus.h \
26+ users-service-dbus.c \
27+ users-service-marshal.c \
28+ users-service-marshal.h \
29 status-provider.h \
30 status-provider.c \
31 status-provider-pidgin.h \
32@@ -38,6 +43,13 @@
33 indicator_status_service_CFLAGS = $(STATUSSERVICE_CFLAGS) -Wall -Werror
34 indicator_status_service_LDADD = $(STATUSSERVICE_LIBS)
35
36+users-service-client.h: $(srcdir)/users-service.xml
37+ dbus-binding-tool \
38+ --prefix=_users_service_client \
39+ --mode=glib-client \
40+ --output=users-service-client.h \
41+ $(srcdir)/users-service.xml
42+
43 status-service-client.h: $(srcdir)/status-service.xml
44 dbus-binding-tool \
45 --prefix=_status_service_client \
46@@ -52,6 +64,16 @@
47 --output=status-service-server.h \
48 $(srcdir)/status-service.xml
49
50+users-service-marshal.h: $(srcdir)/users-service.list
51+ glib-genmarshal --header \
52+ --prefix=_users_service_marshal $(srcdir)/users-service.list \
53+ > users-service-marshal.h
54+
55+users-service-marshal.c: $(srcdir)/users-service.list
56+ glib-genmarshal --body \
57+ --prefix=_users_service_marshal $(srcdir)/users-service.list \
58+ > users-service-marshal.c
59+
60 status-provider-pidgin-marshal.h: $(srcdir)/status-provider-pidgin.list
61 glib-genmarshal --header \
62 --prefix=_status_provider_pidgin_marshal $(srcdir)/status-provider-pidgin.list \
63@@ -76,7 +98,7 @@
64 # Users Stuff
65 ###############
66
67-indicator_users_service_SOURCES = users-service.c
68+indicator_users_service_SOURCES = users-service.c users-service-dbus.c users-service-marshal.c
69 indicator_users_service_CFLAGS = $(USERSSERVICE_CFLAGS) -Wall -Werror
70 indicator_users_service_LDADD = $(USERSSERVICE_LIBS)
71
72@@ -93,8 +115,11 @@
73 ###############
74
75 BUILT_SOURCES = \
76+ users-service-client.h \
77 status-service-client.h \
78 status-service-server.h \
79+ users-service-marshal.h \
80+ users-service-marshal.c \
81 status-provider-pidgin-marshal.h \
82 status-provider-pidgin-marshal.c \
83 status-provider-telepathy-marshal.h \
84
85=== modified file 'src/dbus-shared-names.h'
86--- src/dbus-shared-names.h 2009-08-08 20:37:04 +0000
87+++ src/dbus-shared-names.h 2009-09-24 16:28:09 +0000
88@@ -7,16 +7,16 @@
89 Authors:
90 Ted Gould <ted@canonical.com>
91
92-This program is free software: you can redistribute it and/or modify it
93-under the terms of the GNU General Public License version 3, as published
94+This program is free software: you can redistribute it and/or modify it
95+under the terms of the GNU General Public License version 3, as published
96 by the Free Software Foundation.
97
98-This program is distributed in the hope that it will be useful, but
99-WITHOUT ANY WARRANTY; without even the implied warranties of
100-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
101+This program is distributed in the hope that it will be useful, but
102+WITHOUT ANY WARRANTY; without even the implied warranties of
103+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
104 PURPOSE. See the GNU General Public License for more details.
105
106-You should have received a copy of the GNU General Public License along
107+You should have received a copy of the GNU General Public License along
108 with this program. If not, see <http://www.gnu.org/licenses/>.
109 */
110
111@@ -31,6 +31,8 @@
112
113 #define INDICATOR_USERS_DBUS_NAME "org.ayatana.indicator.users"
114 #define INDICATOR_USERS_DBUS_OBJECT "/org/ayatana/indicator/users/menu"
115+#define INDICATOR_USERS_SERVICE_DBUS_OBJECT "/org/gnome/DisplayManager/UserManager"
116+#define INDICATOR_USERS_SERVICE_DBUS_INTERFACE "org.gnome.DisplayManager.UserManager"
117
118 #define INDICATOR_SESSION_DBUS_NAME "org.ayatana.indicator.session"
119 #define INDICATOR_SESSION_DBUS_OBJECT "/org/ayatana/indicator/session/menu"
120
121=== modified file 'src/status-service.c'
122--- src/status-service.c 2009-09-16 21:00:17 +0000
123+++ src/status-service.c 2009-09-24 16:28:09 +0000
124@@ -7,16 +7,16 @@
125 Authors:
126 Ted Gould <ted@canonical.com>
127
128-This program is free software: you can redistribute it and/or modify it
129-under the terms of the GNU General Public License version 3, as published
130+This program is free software: you can redistribute it and/or modify it
131+under the terms of the GNU General Public License version 3, as published
132 by the Free Software Foundation.
133
134-This program is distributed in the hope that it will be useful, but
135-WITHOUT ANY WARRANTY; without even the implied warranties of
136-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
137+This program is distributed in the hope that it will be useful, but
138+WITHOUT ANY WARRANTY; without even the implied warranties of
139+MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
140 PURPOSE. See the GNU General Public License for more details.
141
142-You should have received a copy of the GNU General Public License along
143+You should have received a copy of the GNU General Public License along
144 with this program. If not, see <http://www.gnu.org/licenses/>.
145 */
146
147@@ -238,12 +238,12 @@
148 if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_STATUS_DBUS_NAME, 0, &nameret, &error)) {
149 g_error("Unable to call to request name");
150 return 1;
151- }
152+ }
153
154 if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
155 g_error("Unable to get name");
156 return 1;
157- }
158+ }
159
160 g_idle_add(build_providers, NULL);
161
162
163=== added file 'src/users-service-dbus.c'
164--- src/users-service-dbus.c 1970-01-01 00:00:00 +0000
165+++ src/users-service-dbus.c 2009-09-24 16:28:09 +0000
166@@ -0,0 +1,1147 @@
167+/*
168+ * Copyright 2009 Canonical Ltd.
169+ *
170+ * Authors:
171+ * Cody Russell <crussell@canonical.com>
172+ *
173+ * This program is free software: you can redistribute it and/or modify it
174+ * under the terms of the GNU General Public License version 3, as published
175+ * by the Free Software Foundation.
176+ *
177+ * This program is distributed in the hope that it will be useful, but
178+ * WITHOUT ANY WARRANTY; without even the implied warranties of
179+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
180+ * PURPOSE. See the GNU General Public License for more details.
181+ *
182+ * You should have received a copy of the GNU General Public License along
183+ * with this program. If not, see <http://www.gnu.org/licenses/>.
184+ */
185+
186+#ifdef HAVE_CONFIG_H
187+#include "config.h"
188+#endif
189+
190+#include <string.h>
191+#include <errno.h>
192+#include <pwd.h>
193+
194+#include <dbus/dbus-glib.h>
195+#include <dbus/dbus-glib.h>
196+#include <dbus/dbus-glib-lowlevel.h>
197+
198+#include "dbus-shared-names.h"
199+#include "users-service-dbus.h"
200+#include "users-service-client.h"
201+#include "users-service-marshal.h"
202+
203+static void users_service_dbus_class_init (UsersServiceDbusClass *klass);
204+static void users_service_dbus_init (UsersServiceDbus *self);
205+static void users_service_dbus_dispose (GObject *object);
206+static void users_service_dbus_finalize (GObject *object);
207+static void create_gdm_proxy (UsersServiceDbus *self);
208+static void create_seat_proxy (UsersServiceDbus *self);
209+static void create_ck_proxy (UsersServiceDbus *self);
210+static void create_cksession_proxy (UsersServiceDbus *self);
211+static gchar *get_seat (UsersServiceDbus *service);
212+static void users_loaded (DBusGProxy *proxy,
213+ gpointer user_data);
214+static void user_added (DBusGProxy *proxy,
215+ guint uid,
216+ gpointer user_data);
217+static void user_removed (DBusGProxy *proxy,
218+ guint uid,
219+ gpointer user_data);
220+static void user_updated (DBusGProxy *proxy,
221+ guint uid,
222+ gpointer user_data);
223+static void seat_proxy_session_added (DBusGProxy *seat_proxy,
224+ const gchar *session_id,
225+ UsersServiceDbus *service);
226+static void seat_proxy_session_removed (DBusGProxy *seat_proxy,
227+ const gchar *session_id,
228+ UsersServiceDbus *service);
229+static gboolean do_add_session (UsersServiceDbus *service,
230+ UserData *user,
231+ const gchar *ssid);
232+static gchar * get_seat_internal (UsersServiceDbus *self);
233+
234+/* Private */
235+typedef struct _UsersServiceDbusPrivate UsersServiceDbusPrivate;
236+
237+struct _UsersServiceDbusPrivate
238+{
239+ GHashTable *users;
240+ gint count;
241+ gchar *seat;
242+ gchar *ssid;
243+
244+ DBusGConnection *system_bus;
245+
246+ DBusGProxy *gdm_proxy;
247+ DBusGProxy *ck_proxy;
248+ DBusGProxy *seat_proxy;
249+ DBusGProxy *session_proxy;
250+
251+ GHashTable *exclusions;
252+ GHashTable *sessions;
253+};
254+
255+#define USERS_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusPrivate))
256+
257+/* Signals */
258+enum {
259+ USERS_LOADED,
260+ USER_ADDED,
261+ USER_REMOVED,
262+ USER_UPDATED,
263+ LAST_SIGNAL
264+};
265+
266+static guint signals[LAST_SIGNAL] = { 0 };
267+
268+/* GObject Boilerplate */
269+G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT);
270+
271+static void
272+users_service_dbus_class_init (UsersServiceDbusClass *klass)
273+{
274+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
275+
276+ g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate));
277+
278+ object_class->dispose = users_service_dbus_dispose;
279+ object_class->finalize = users_service_dbus_finalize;
280+
281+ signals[USERS_LOADED] = g_signal_new ("users-loaded",
282+ G_TYPE_FROM_CLASS (klass),
283+ G_SIGNAL_RUN_LAST,
284+ G_STRUCT_OFFSET (UsersServiceDbusClass, users_loaded),
285+ NULL, NULL,
286+ g_cclosure_marshal_VOID__VOID,
287+ G_TYPE_NONE, 0);
288+
289+ signals[USER_ADDED] = g_signal_new ("user-added",
290+ G_TYPE_FROM_CLASS (klass),
291+ G_SIGNAL_RUN_LAST,
292+ G_STRUCT_OFFSET (UsersServiceDbusClass, user_added),
293+ NULL, NULL,
294+ _users_service_marshal_VOID__INT64,
295+ G_TYPE_NONE, 1, G_TYPE_INT64);
296+
297+ signals[USER_REMOVED] = g_signal_new ("user-removed",
298+ G_TYPE_FROM_CLASS (klass),
299+ G_SIGNAL_RUN_LAST,
300+ G_STRUCT_OFFSET (UsersServiceDbusClass, user_removed),
301+ NULL, NULL,
302+ _users_service_marshal_VOID__INT64,
303+ G_TYPE_NONE, 1, G_TYPE_INT64);
304+
305+ signals[USER_UPDATED] = g_signal_new ("user-updated",
306+ G_TYPE_FROM_CLASS (klass),
307+ G_SIGNAL_RUN_LAST,
308+ G_STRUCT_OFFSET (UsersServiceDbusClass, user_updated),
309+ NULL, NULL,
310+ _users_service_marshal_VOID__INT64,
311+ G_TYPE_NONE, 1, G_TYPE_INT64);
312+}
313+
314+static void
315+users_service_dbus_init (UsersServiceDbus *self)
316+{
317+ GError *error = NULL;
318+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
319+
320+ priv->users = NULL;
321+ priv->count = 0;
322+
323+ /* Get the system bus */
324+ priv->system_bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
325+ if (error != NULL)
326+ {
327+ g_error ("Unable to get system bus: %s", error->message);
328+ g_error_free(error);
329+
330+ return;
331+ }
332+
333+ priv->sessions = g_hash_table_new_full (g_str_hash,
334+ g_str_equal,
335+ g_free,
336+ g_free);
337+
338+ priv->users = g_hash_table_new_full (g_str_hash,
339+ g_str_equal,
340+ g_free,
341+ NULL);
342+
343+ dbus_g_object_register_marshaller (_users_service_marshal_VOID__INT64,
344+ G_TYPE_NONE,
345+ G_TYPE_INT64,
346+ G_TYPE_INVALID);
347+
348+ create_gdm_proxy (self);
349+ create_ck_proxy (self);
350+ create_seat_proxy (self);
351+
352+ users_loaded (priv->gdm_proxy, self);
353+}
354+
355+static void
356+users_service_dbus_dispose (GObject *object)
357+{
358+ G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object);
359+}
360+
361+static void
362+users_service_dbus_finalize (GObject *object)
363+{
364+ G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object);
365+}
366+
367+static void
368+create_gdm_proxy (UsersServiceDbus *self)
369+{
370+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
371+ GError *error = NULL;
372+
373+ priv->gdm_proxy = dbus_g_proxy_new_for_name_owner (priv->system_bus,
374+ "org.gnome.DisplayManager",
375+ "/org/gnome/DisplayManager/UserManager",
376+ "org.gnome.DisplayManager.UserManager",
377+ &error);
378+
379+ if (!priv->gdm_proxy)
380+ {
381+ if (error != NULL)
382+ {
383+ g_error ("Unable to get DisplayManager proxy on system bus: %s", error->message);
384+ g_error_free (error);
385+ }
386+
387+ return;
388+ }
389+
390+ dbus_g_proxy_add_signal (priv->gdm_proxy,
391+ "UsersLoaded",
392+ G_TYPE_INVALID);
393+
394+ dbus_g_proxy_add_signal (priv->gdm_proxy,
395+ "UserAdded",
396+ G_TYPE_INT64,
397+ G_TYPE_INVALID);
398+
399+ dbus_g_proxy_add_signal (priv->gdm_proxy,
400+ "UserRemoved",
401+ G_TYPE_INT64,
402+ G_TYPE_INVALID);
403+
404+ dbus_g_proxy_add_signal (priv->gdm_proxy,
405+ "UserUpdated",
406+ G_TYPE_INT64,
407+ G_TYPE_INVALID);
408+
409+ dbus_g_proxy_connect_signal (priv->gdm_proxy,
410+ "UsersLoaded",
411+ G_CALLBACK (users_loaded),
412+ self,
413+ NULL);
414+
415+ dbus_g_proxy_connect_signal (priv->gdm_proxy,
416+ "UserAdded",
417+ G_CALLBACK (user_added),
418+ self,
419+ NULL);
420+
421+ dbus_g_proxy_connect_signal (priv->gdm_proxy,
422+ "UserRemoved",
423+ G_CALLBACK (user_removed),
424+ self,
425+ NULL);
426+
427+ dbus_g_proxy_connect_signal (priv->gdm_proxy,
428+ "UserUpdated",
429+ G_CALLBACK (user_updated),
430+ self,
431+ NULL);
432+}
433+
434+static void
435+create_ck_proxy (UsersServiceDbus *self)
436+{
437+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
438+
439+ priv->ck_proxy = dbus_g_proxy_new_for_name (priv->system_bus,
440+ "org.freedesktop.ConsoleKit",
441+ "/org/freedesktop/ConsoleKit/Manager",
442+ "org.freedesktop.ConsoleKit.Manager");
443+
444+ if (!priv->ck_proxy)
445+ {
446+ g_warning ("Failed to get ConsoleKit proxy.");
447+ return;
448+ }
449+}
450+
451+static void
452+create_seat_proxy (UsersServiceDbus *self)
453+{
454+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
455+ GError *error = NULL;
456+
457+ priv->seat = get_seat (self);
458+ if (priv->seat == NULL)
459+ {
460+ return;
461+ }
462+
463+ priv->seat_proxy = dbus_g_proxy_new_for_name_owner (priv->system_bus,
464+ "org.freedesktop.ConsoleKit",
465+ priv->seat,
466+ "org.freedesktop.ConsoleKit.Seat",
467+ &error);
468+
469+ if (!priv->seat_proxy)
470+ {
471+ if (error != NULL)
472+ {
473+ g_warning ("Failed to connect to the ConsoleKit seat: %s", error->message);
474+ g_error_free (error);
475+ }
476+
477+ return;
478+ }
479+
480+ dbus_g_proxy_add_signal (priv->seat_proxy,
481+ "SessionAdded",
482+ DBUS_TYPE_G_OBJECT_PATH,
483+ G_TYPE_INVALID);
484+ dbus_g_proxy_add_signal (priv->seat_proxy,
485+ "SessionRemoved",
486+ DBUS_TYPE_G_OBJECT_PATH,
487+ G_TYPE_INVALID);
488+
489+ dbus_g_proxy_connect_signal (priv->seat_proxy,
490+ "SessionAdded",
491+ G_CALLBACK (seat_proxy_session_added),
492+ self,
493+ NULL);
494+ dbus_g_proxy_connect_signal (priv->seat_proxy,
495+ "SessionRemoved",
496+ G_CALLBACK (seat_proxy_session_removed),
497+ self,
498+ NULL);
499+}
500+
501+static void
502+create_cksession_proxy (UsersServiceDbus *service)
503+{
504+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
505+
506+ priv->session_proxy = dbus_g_proxy_new_for_name (priv->system_bus,
507+ "org.freedesktop.ConsoleKit",
508+ priv->ssid,
509+ "org.freedesktop.ConsoleKit.Session");
510+
511+ if (!priv->session_proxy)
512+ {
513+ g_warning ("Failed to get ConsoleKit session proxy");
514+ return;
515+ }
516+}
517+
518+static gchar *
519+get_seat (UsersServiceDbus *service)
520+{
521+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
522+ GError *error = NULL;
523+ gchar *ssid = NULL;
524+ gchar *seat;
525+
526+ if (!dbus_g_proxy_call (priv->ck_proxy,
527+ "GetCurrentSession",
528+ &error,
529+ G_TYPE_INVALID,
530+ DBUS_TYPE_G_OBJECT_PATH,
531+ &ssid,
532+ G_TYPE_INVALID))
533+ {
534+ if (error)
535+ {
536+ g_debug ("Failed to call GetCurrentSession: %s", error->message);
537+ g_error_free (error);
538+ }
539+
540+ if (ssid)
541+ g_free (ssid);
542+
543+ return NULL;
544+ }
545+
546+ priv->ssid = ssid;
547+ create_cksession_proxy (service);
548+
549+ seat = get_seat_internal (service);
550+
551+ return seat;
552+}
553+
554+static gchar *
555+get_seat_internal (UsersServiceDbus *self)
556+{
557+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
558+ GError *error = NULL;
559+ gchar *seat = NULL;
560+
561+ if (!dbus_g_proxy_call (priv->session_proxy,
562+ "GetSeatId",
563+ &error,
564+ G_TYPE_INVALID,
565+ DBUS_TYPE_G_OBJECT_PATH, &seat,
566+ G_TYPE_INVALID))
567+ {
568+ if (error)
569+ {
570+ g_debug ("Failed to call GetSeatId: %s", error->message);
571+
572+ return NULL;
573+ }
574+ }
575+
576+ return seat;
577+}
578+
579+static gboolean
580+get_unix_user (UsersServiceDbus *service,
581+ const gchar *session_id,
582+ uid_t *uidp)
583+{
584+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
585+ GError *error = NULL;
586+ guint uid;
587+
588+ if (dbus_g_proxy_call (priv->session_proxy,
589+ "GetUnixUser",
590+ &error,
591+ G_TYPE_INVALID,
592+ G_TYPE_UINT, &uid,
593+ G_TYPE_INVALID))
594+ {
595+ if (error)
596+ {
597+ g_warning ("Failed to call GetUnixUser: %s", error->message);
598+ g_error_free (error);
599+ }
600+
601+ return FALSE;
602+ }
603+
604+ if (uidp != NULL)
605+ {
606+ *uidp = (uid_t)uid;
607+ }
608+
609+ return TRUE;
610+}
611+
612+static gchar *
613+get_session_for_user (UsersServiceDbus *service,
614+ UserData *user)
615+{
616+ GList *l;
617+
618+ if (!users_service_dbus_can_activate_session (service))
619+ {
620+ return NULL;
621+ }
622+
623+ if (!user->sessions || g_list_length (user->sessions) == 0)
624+ {
625+ return NULL;
626+ }
627+
628+ for (l = user->sessions; l != NULL; l = l->next)
629+ {
630+ const char *ssid;
631+
632+ ssid = l->data;
633+
634+ if (ssid)
635+ {
636+ return g_strdup (ssid);
637+ }
638+ }
639+
640+ return NULL;
641+}
642+
643+static gboolean
644+do_add_session (UsersServiceDbus *service,
645+ UserData *user,
646+ const gchar *ssid)
647+{
648+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
649+ GError *error = NULL;
650+ gchar *seat = NULL;
651+ gchar *xdisplay = NULL;
652+ GList *l;
653+
654+ seat = get_seat_internal (service);
655+
656+ if (!seat || !priv->seat || strcmp (seat, priv->seat) != 0)
657+ return FALSE;
658+
659+ if (!dbus_g_proxy_call (priv->session_proxy,
660+ "GetX11Display",
661+ &error,
662+ G_TYPE_INVALID,
663+ G_TYPE_STRING, &xdisplay,
664+ G_TYPE_INVALID))
665+ {
666+ if (error)
667+ {
668+ g_debug ("Failed to call GetX11Display: %s", error->message);
669+ g_error_free (error);
670+ }
671+
672+ return FALSE;
673+ }
674+
675+ if (!xdisplay || xdisplay[0] == '\0')
676+ return FALSE;
677+
678+ g_hash_table_insert (priv->sessions,
679+ g_strdup (ssid),
680+ g_strdup (user->user_name));
681+
682+ l = g_list_find_custom (user->sessions, ssid, (GCompareFunc)g_strcmp0);
683+ if (l == NULL)
684+ {
685+ g_debug ("Adding session %s", ssid);
686+
687+ user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
688+ }
689+ else
690+ {
691+ g_debug ("User %s already has session %s", user->user_name, ssid);
692+ }
693+
694+ return TRUE;
695+}
696+
697+static void
698+add_sessions_for_user (UsersServiceDbus *self,
699+ UserData *user)
700+{
701+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
702+ GError *error;
703+ GPtrArray *sessions;
704+ int i;
705+
706+ error = NULL;
707+ if (!dbus_g_proxy_call (priv->ck_proxy,
708+ "GetSessionsForUnixUser",
709+ &error,
710+ G_TYPE_UINT, user->uid,
711+ G_TYPE_INVALID,
712+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
713+ &sessions,
714+ G_TYPE_INVALID))
715+ {
716+ g_debug ("Failed to call GetSessionsForUnixUser: %s", error->message);
717+ g_error_free (error);
718+
719+ return;
720+ }
721+
722+ for (i = 0; i < sessions->len; i++)
723+ {
724+ char *ssid;
725+
726+ ssid = g_ptr_array_index (sessions, i);
727+ do_add_session (self, user, ssid);
728+ }
729+
730+ g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
731+ g_ptr_array_free (sessions, TRUE);
732+}
733+
734+
735+static void
736+seat_proxy_session_added (DBusGProxy *seat_proxy,
737+ const gchar *session_id,
738+ UsersServiceDbus *service)
739+{
740+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
741+ uid_t uid;
742+ gboolean res;
743+ struct passwd *pwent;
744+ UserData *user;
745+
746+ if (!get_unix_user (service, session_id, &uid))
747+ {
748+ g_warning ("Failed to lookup user for session");
749+ return;
750+ }
751+
752+ errno = 0;
753+ pwent = getpwuid (uid);
754+ if (!pwent)
755+ {
756+ g_warning ("Failed to lookup user id %d: %s", (int)uid, g_strerror (errno));
757+ return;
758+ }
759+
760+ user = g_hash_table_lookup (priv->users, pwent->pw_name);
761+ if (!user)
762+ {
763+ return;
764+ }
765+
766+ res = do_add_session (service, user, session_id);
767+}
768+
769+static void
770+seat_proxy_session_removed (DBusGProxy *seat_proxy,
771+ const gchar *session_id,
772+ UsersServiceDbus *service)
773+{
774+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
775+ UserData *user;
776+ gchar *username;
777+ GList *l;
778+
779+ username = g_hash_table_lookup (priv->sessions, session_id);
780+ if (!username)
781+ return;
782+
783+ user = g_hash_table_lookup (priv->users, username);
784+ if (!user)
785+ return;
786+
787+ l = g_list_find_custom (user->sessions,
788+ session_id,
789+ (GCompareFunc)g_strcmp0);
790+ if (l)
791+ {
792+ g_debug ("Removing session %s", session_id);
793+
794+ g_free (l->data);
795+ user->sessions = g_list_delete_link (user->sessions, l);
796+ }
797+ else
798+ {
799+ g_debug ("Session not found: %s", session_id);
800+ }
801+}
802+
803+static void
804+sync_users (UsersServiceDbus *self)
805+{
806+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
807+
808+ if (g_hash_table_size (priv->users) > 0)
809+ {
810+ return;
811+ }
812+
813+ if (priv->count > MINIMUM_USERS && priv->count < MAXIMUM_USERS)
814+ {
815+ GArray *uids = NULL;
816+ GPtrArray *users_info = NULL;
817+ GError *error = NULL;
818+ gint i;
819+
820+ uids = g_array_new (FALSE, FALSE, sizeof (gint64));
821+
822+ if (!org_gnome_DisplayManager_UserManager_get_user_list (priv->gdm_proxy,
823+ &uids,
824+ &error))
825+ {
826+ g_warning ("failed to retrieve user list: %s", error->message);
827+ g_error_free (error);
828+
829+ return;
830+ }
831+
832+ users_info = g_ptr_array_new ();
833+
834+ if (!org_gnome_DisplayManager_UserManager_get_users_info (priv->gdm_proxy,
835+ uids,
836+ &users_info,
837+ &error))
838+ {
839+ g_warning ("failed to retrieve user info: %s", error->message);
840+ g_error_free (error);
841+
842+ return;
843+ }
844+
845+ for (i = 0; i < users_info->len; i++)
846+ {
847+ GValueArray *values;
848+ UserData *user;
849+
850+ values = g_ptr_array_index (users_info, i);
851+
852+ user = g_new0 (UserData, 1);
853+
854+ user->uid = g_value_get_int64 (g_value_array_get_nth (values, 0));
855+ user->user_name = g_strdup (g_value_get_string (g_value_array_get_nth (values, 1)));
856+ user->real_name = g_strdup (g_value_get_string (g_value_array_get_nth (values, 2)));
857+ user->shell = g_strdup (g_value_get_string (g_value_array_get_nth (values, 3)));
858+ user->login_count = g_value_get_int (g_value_array_get_nth (values, 4));
859+ user->icon_url = g_strdup (g_value_get_string (g_value_array_get_nth (values, 5)));
860+
861+ g_hash_table_insert (priv->users,
862+ g_strdup (user->user_name),
863+ user);
864+
865+ add_sessions_for_user (self, user);
866+ }
867+ }
868+}
869+
870+static void
871+users_loaded (DBusGProxy *proxy,
872+ gpointer user_data)
873+{
874+ UsersServiceDbus *service;
875+ UsersServiceDbusPrivate *priv;
876+ GError *error = NULL;
877+ gint count;
878+
879+ service = (UsersServiceDbus *)user_data;
880+ priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
881+
882+ if (!org_gnome_DisplayManager_UserManager_count_users (proxy,
883+ &count,
884+ &error))
885+ {
886+ g_warning ("failed to retrieve user count: %s", error->message);
887+ g_error_free (error);
888+
889+ return;
890+ }
891+
892+ priv->count = count;
893+
894+ sync_users (service);
895+}
896+
897+static gboolean
898+session_is_login_window (UsersServiceDbus *self,
899+ const char *ssid)
900+{
901+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
902+ DBusGProxy *proxy = NULL;
903+ GError *error = NULL;
904+ char *type = NULL;
905+
906+ if (!(proxy = dbus_g_proxy_new_for_name (priv->system_bus,
907+ "org.freedesktop.ConsoleKit",
908+ ssid,
909+ "org.freedesktop.ConsoleKit.Session")))
910+ {
911+ g_warning ("Failed to get ConsoleKit proxy");
912+
913+ return FALSE;
914+ }
915+
916+ if (!dbus_g_proxy_call (proxy,
917+ "GetSessionType",
918+ &error,
919+ G_TYPE_INVALID,
920+ G_TYPE_STRING, &type,
921+ G_TYPE_INVALID))
922+ {
923+ g_warning ("Can't call GetSessionType: %s", error->message);
924+ g_error_free (error);
925+
926+ if (proxy)
927+ g_object_unref (proxy);
928+
929+ return FALSE;
930+ }
931+
932+ if (proxy)
933+ g_object_unref (proxy);
934+
935+ return (type && type[0] != '\0' && strcmp (type, "LoginWindow") == 0);
936+}
937+
938+static char *
939+get_login_session (UsersServiceDbus *self)
940+{
941+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
942+ gboolean can_activate;
943+ GError *error = NULL;
944+ GPtrArray *sessions = NULL;
945+ char *ssid = NULL;
946+ int i;
947+
948+ if (!priv->seat || priv->seat[0] == '\0')
949+ {
950+ return NULL;
951+ }
952+
953+ if (!dbus_g_proxy_call (priv->seat_proxy,
954+ "CanActivateSessions",
955+ &error,
956+ G_TYPE_INVALID,
957+ G_TYPE_BOOLEAN, &can_activate,
958+ G_TYPE_INVALID))
959+ {
960+ g_warning ("Failed to call CanActivateSessions: %s", error->message);
961+ g_error_free (error);
962+
963+ return NULL;
964+ }
965+
966+ if (!can_activate)
967+ {
968+ return NULL;
969+ }
970+
971+ error = NULL;
972+ if (!dbus_g_proxy_call (priv->seat_proxy,
973+ "GetSessions",
974+ &error,
975+ G_TYPE_INVALID,
976+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
977+ G_TYPE_INVALID))
978+ {
979+ g_warning ("Failed to call GetSessions: %s", error->message);
980+ g_error_free (error);
981+
982+ return NULL;
983+ }
984+
985+ for (i = 0; i < sessions->len; i++)
986+ {
987+ char *s = g_ptr_array_index (sessions, i);
988+
989+ if (session_is_login_window (self, s))
990+ {
991+ ssid = g_strdup (s);
992+ break;
993+ }
994+ }
995+
996+ g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
997+ g_ptr_array_free (sessions, TRUE);
998+
999+ return ssid;
1000+}
1001+
1002+static gboolean
1003+activate_user_session (UsersServiceDbus *self,
1004+ const char *seat,
1005+ const char *ssid)
1006+{
1007+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1008+ DBusMessage *message = NULL;
1009+ DBusMessage *reply = NULL;
1010+ DBusError error;
1011+
1012+ if (!(message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
1013+ seat,
1014+ "org.freedesktop.ConsoleKit.Seat",
1015+ "ActivateSession")))
1016+ {
1017+ return FALSE;
1018+ }
1019+
1020+ if (!dbus_message_append_args (message,
1021+ DBUS_TYPE_OBJECT_PATH, &ssid,
1022+ DBUS_TYPE_INVALID))
1023+ {
1024+ return FALSE;
1025+ }
1026+
1027+ dbus_error_init (&error);
1028+ if (!(reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (priv->system_bus),
1029+ message,
1030+ -1,
1031+ &error)))
1032+ {
1033+ if (dbus_error_is_set (&error))
1034+ {
1035+ g_warning ("Can't activate session: %s", error.message);
1036+ dbus_error_free (&error);
1037+
1038+ return FALSE;
1039+ }
1040+ }
1041+
1042+ if (message)
1043+ {
1044+ dbus_message_unref (message);
1045+ }
1046+
1047+ if (reply)
1048+ {
1049+ dbus_message_unref (reply);
1050+ }
1051+
1052+ return TRUE;
1053+}
1054+
1055+gboolean
1056+start_new_user_session (UsersServiceDbus *self,
1057+ UserData *user)
1058+{
1059+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1060+ GError *error = NULL;
1061+ char *ssid;
1062+
1063+ ssid = get_login_session (self);
1064+ if (ssid)
1065+ {
1066+ if (!activate_user_session (self, priv->seat, ssid))
1067+ {
1068+ return FALSE;
1069+ }
1070+ }
1071+
1072+ if (!g_spawn_command_line_async ("gdmflexiserver -s", &error))
1073+ {
1074+ g_warning ("Failed to start new login session: %s", error->message);
1075+ g_error_free (error);
1076+
1077+ return FALSE;
1078+ }
1079+
1080+ return TRUE;
1081+}
1082+
1083+static void
1084+user_added (DBusGProxy *proxy,
1085+ guint uid,
1086+ gpointer user_data)
1087+{
1088+ UsersServiceDbus *service = (UsersServiceDbus *)user_data;
1089+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
1090+ UserData *user = g_new0 (UserData, 1);
1091+ GError *error = NULL;
1092+
1093+ priv->count++;
1094+
1095+ if (priv->count < MAXIMUM_USERS)
1096+ {
1097+ if ((priv->count - g_hash_table_size (priv->users)) > 1)
1098+ {
1099+ sync_users (service);
1100+ }
1101+ else
1102+ {
1103+ if (!org_gnome_DisplayManager_UserManager_get_user_info (proxy,
1104+ uid,
1105+ &user->user_name,
1106+ &user->real_name,
1107+ &user->shell,
1108+ &user->login_count,
1109+ &user->icon_url,
1110+ &error))
1111+ {
1112+ g_warning ("unable to retrieve user info: %s", error->message);
1113+ g_error_free (error);
1114+
1115+ g_free (user);
1116+
1117+ return;
1118+ }
1119+
1120+ user->uid = uid;
1121+
1122+ g_hash_table_insert (priv->users,
1123+ g_strdup (user->user_name),
1124+ user);
1125+
1126+ g_signal_emit (G_OBJECT (service), signals[USER_ADDED], 0, user, TRUE);
1127+ }
1128+ }
1129+}
1130+
1131+static gboolean
1132+compare_users_by_uid (gpointer key,
1133+ gpointer value,
1134+ gpointer user_data)
1135+{
1136+ return (GPOINTER_TO_UINT (value) == GPOINTER_TO_UINT (user_data));
1137+}
1138+
1139+static void
1140+user_removed (DBusGProxy *proxy,
1141+ guint uid,
1142+ gpointer user_data)
1143+{
1144+ UsersServiceDbus *service = (UsersServiceDbus *)user_data;
1145+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
1146+ UserData *user;
1147+ gint size;
1148+
1149+ size = g_hash_table_size (priv->users);
1150+
1151+ priv->count--;
1152+
1153+ if (size == 0 || (size - priv->count) > 1)
1154+ {
1155+ sync_users (service);
1156+ }
1157+ else
1158+ {
1159+ user = g_hash_table_find (priv->users,
1160+ compare_users_by_uid,
1161+ GUINT_TO_POINTER (uid));
1162+
1163+ if (user != NULL)
1164+ {
1165+ g_hash_table_remove (priv->users,
1166+ user->user_name);
1167+ }
1168+ }
1169+}
1170+
1171+static void
1172+user_updated (DBusGProxy *proxy,
1173+ guint uid,
1174+ gpointer user_data)
1175+{
1176+#if 0
1177+ // XXX - TODO
1178+ UsersServiceDbus *service = (UsersServiceDbus *)user_data;
1179+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (service);
1180+ UserData *user;
1181+
1182+ user = g_hash_table_find (priv->users,
1183+ compare_users_by_uid,
1184+ GUINT_TO_POINTER (uid));
1185+#endif
1186+}
1187+
1188+gint
1189+users_service_dbus_get_user_count (UsersServiceDbus *self)
1190+{
1191+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1192+
1193+ return priv->count;
1194+}
1195+
1196+GList *
1197+users_service_dbus_get_user_list (UsersServiceDbus *self)
1198+{
1199+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1200+
1201+ return g_hash_table_get_values (priv->users);
1202+}
1203+
1204+/*
1205+ * XXX - TODO: Right now we switch to a session that another user
1206+ * already has open, but if there are no open sessions
1207+ * for this user we go to the login screen and the
1208+ * user at the seat must select a user and enter a
1209+ * password. This kind of defeats the purpose of
1210+ * actually selecting a username, since selecting any
1211+ * user will do the same thing here. We need to change
1212+ * it so you only need to enter a password for the
1213+ * specified user.
1214+ */
1215+gboolean
1216+users_service_dbus_activate_user_session (UsersServiceDbus *self,
1217+ UserData *user)
1218+{
1219+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1220+ DBusMessage *message = NULL;
1221+ DBusMessage *reply = NULL;
1222+ DBusError error;
1223+ gchar *ssid;
1224+
1225+ dbus_error_init (&error);
1226+
1227+ if (!priv->seat)
1228+ priv->seat = get_seat (self);
1229+
1230+ ssid = get_session_for_user (self, user);
1231+
1232+ if (!ssid)
1233+ {
1234+ return start_new_user_session (self, user);
1235+ }
1236+
1237+ if (!(message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
1238+ priv->seat,
1239+ "org.freedesktop.ConsoleKit.Seat",
1240+ "ActivateSession")))
1241+ {
1242+ g_warning ("failed to create new message");
1243+ return FALSE;
1244+ }
1245+
1246+ if (!dbus_message_append_args (message,
1247+ DBUS_TYPE_OBJECT_PATH,
1248+ &ssid,
1249+ DBUS_TYPE_INVALID))
1250+ {
1251+ g_warning ("failed to append args");
1252+ return FALSE;
1253+ }
1254+
1255+ if (!(reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (priv->system_bus),
1256+ message,
1257+ -1,
1258+ &error)))
1259+ {
1260+ if (dbus_error_is_set (&error))
1261+ {
1262+ g_warning ("Failed to send message: %s", error.message);
1263+ dbus_error_free (&error);
1264+
1265+ return FALSE;
1266+ }
1267+ }
1268+
1269+ if (message)
1270+ {
1271+ dbus_message_unref (message);
1272+ }
1273+
1274+ if (reply)
1275+ {
1276+ dbus_message_unref (reply);
1277+ }
1278+
1279+ return TRUE;
1280+}
1281+
1282+gboolean
1283+users_service_dbus_can_activate_session (UsersServiceDbus *self)
1284+{
1285+ UsersServiceDbusPrivate *priv = USERS_SERVICE_DBUS_GET_PRIVATE (self);
1286+ gboolean can_activate = FALSE;
1287+ GError *error = NULL;
1288+
1289+ if (!priv->seat_proxy)
1290+ {
1291+ create_seat_proxy (self);
1292+ }
1293+
1294+ if (!priv->seat || priv->seat[0] == '\0')
1295+ {
1296+ return FALSE;
1297+ }
1298+
1299+ if (!dbus_g_proxy_call (priv->seat_proxy,
1300+ "CanActivateSessions",
1301+ &error,
1302+ G_TYPE_INVALID,
1303+ G_TYPE_BOOLEAN, &can_activate,
1304+ G_TYPE_INVALID))
1305+ {
1306+ g_warning ("Failed to determine if seat can activate sessions: %s", error->message);
1307+ g_error_free (error);
1308+
1309+ return FALSE;
1310+ }
1311+
1312+ return can_activate;
1313+}
1314
1315=== added file 'src/users-service-dbus.h'
1316--- src/users-service-dbus.h 1970-01-01 00:00:00 +0000
1317+++ src/users-service-dbus.h 2009-09-24 16:28:09 +0000
1318@@ -0,0 +1,81 @@
1319+/*
1320+ * Copyright 2009 Canonical Ltd.
1321+ *
1322+ * Authors:
1323+ * Cody Russell <crussell@canonical.com>
1324+ *
1325+ * This program is free software: you can redistribute it and/or modify it
1326+ * under the terms of the GNU General Public License version 3, as published
1327+ * by the Free Software Foundation.
1328+ *
1329+ * This program is distributed in the hope that it will be useful, but
1330+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1331+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1332+ * PURPOSE. See the GNU General Public License for more details.
1333+ *
1334+ * You should have received a copy of the GNU General Public License along
1335+ * with this program. If not, see <http://www.gnu.org/licenses/>.
1336+ */
1337+
1338+#ifndef __USERS_SERVICE_DBUS_H__
1339+#define __USERS_SERVICE_DBUS_H__
1340+
1341+#include <glib.h>
1342+#include <glib-object.h>
1343+
1344+G_BEGIN_DECLS
1345+
1346+#define USERS_SERVICE_DBUS_TYPE (users_service_dbus_get_type ())
1347+#define USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbus))
1348+#define USERS_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusClass))
1349+#define IS_USERS_SERVICE_DBUS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), USERS_SERVICE_DBUS_TYPE))
1350+#define IS_USERS_SERVICE_DBUS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), USERS_SERVICE_DBUS_TYPE))
1351+#define USERS_SERVICE_DBUS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), USERS_SERVICE_DBUS_TYPE, UsersServiceDbusClass))
1352+
1353+typedef struct _UsersServiceDbus UsersServiceDbus;
1354+typedef struct _UsersServiceDbusClass UsersServiceDbusClass;
1355+typedef struct _UserData UserData;
1356+
1357+struct _UserData
1358+{
1359+ gint64 uid;
1360+ gchar *user_name;
1361+ gchar *real_name;
1362+ gchar *shell;
1363+ gint login_count;
1364+ gchar *icon_url;
1365+
1366+ GList *sessions;
1367+
1368+ UsersServiceDbus *service;
1369+};
1370+
1371+#define MINIMUM_USERS 1
1372+#define MAXIMUM_USERS 7
1373+
1374+struct _UsersServiceDbus {
1375+ GObject parent;
1376+};
1377+
1378+struct _UsersServiceDbusClass {
1379+ GObjectClass parent_class;
1380+
1381+ /* Signals */
1382+ void (* users_loaded) (UsersServiceDbus *self, gpointer user_data);
1383+
1384+ void (* user_added) (UsersServiceDbus *self, gint64 uid, gpointer user_data);
1385+ void (* user_removed) (UsersServiceDbus *self, gint64 uid, gpointer user_data);
1386+ void (* user_updated) (UsersServiceDbus *self, gint64 uid, gpointer user_data);
1387+};
1388+
1389+GType users_service_dbus_get_type (void) G_GNUC_CONST;
1390+
1391+gint users_service_dbus_get_user_count (UsersServiceDbus *self);
1392+GList *users_service_dbus_get_user_list (UsersServiceDbus *self);
1393+gboolean users_service_dbus_can_activate_session (UsersServiceDbus *self);
1394+gboolean users_service_dbus_activate_user_session (UsersServiceDbus *self,
1395+ UserData *user);
1396+
1397+G_END_DECLS
1398+
1399+#endif
1400
1401=== modified file 'src/users-service.c'
1402--- src/users-service.c 2009-09-16 16:34:26 +0000
1403+++ src/users-service.c 2009-09-24 16:28:09 +0000
1404@@ -1,24 +1,25 @@
1405 /*
1406-A small wrapper utility to load indicators and put them as menu items
1407-into the gnome-panel using it's applet interface.
1408-
1409-Copyright 2009 Canonical Ltd.
1410-
1411-Authors:
1412- Ted Gould <ted@canonical.com>
1413-
1414-This program is free software: you can redistribute it and/or modify it
1415-under the terms of the GNU General Public License version 3, as published
1416-by the Free Software Foundation.
1417-
1418-This program is distributed in the hope that it will be useful, but
1419-WITHOUT ANY WARRANTY; without even the implied warranties of
1420-MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1421-PURPOSE. See the GNU General Public License for more details.
1422-
1423-You should have received a copy of the GNU General Public License along
1424-with this program. If not, see <http://www.gnu.org/licenses/>.
1425-*/
1426+ * A small wrapper utility to load indicators and put them as menu items
1427+ * into the gnome-panel using it's applet interface.
1428+ *
1429+ * Copyright 2009 Canonical Ltd.
1430+ *
1431+ * Authors:
1432+ * Ted Gould <ted@canonical.com>
1433+ * Cody Russell <crussell@canonical.com>
1434+ *
1435+ * This program is free software: you can redistribute it and/or modify it
1436+ * under the terms of the GNU General Public License version 3, as published
1437+ * by the Free Software Foundation.
1438+ *
1439+ * This program is distributed in the hope that it will be useful, but
1440+ * WITHOUT ANY WARRANTY; without even the implied warranties of
1441+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
1442+ * PURPOSE. See the GNU General Public License for more details.
1443+ *
1444+ * You should have received a copy of the GNU General Public License along
1445+ *with this program. If not, see <http://www.gnu.org/licenses/>.
1446+ */
1447
1448 #include <config.h>
1449
1450@@ -33,15 +34,27 @@
1451 #include <libdbusmenu-glib/menuitem.h>
1452
1453 #include "dbus-shared-names.h"
1454+#include "users-service-dbus.h"
1455
1456 #define GUEST_SESSION_LAUNCHER "/usr/share/gdm/guest-session/guest-session-launch"
1457
1458-static DBusGConnection * session_bus = NULL;
1459-static DBusGConnection * system_bus = NULL;
1460-static DBusGProxy * bus_proxy = NULL;
1461-static DBusGProxy * gdm_proxy = NULL;
1462-static DbusmenuMenuitem * root_menuitem = NULL;
1463-static GMainLoop * mainloop = NULL;
1464+typedef struct _ActivateData ActivateData;
1465+struct _ActivateData
1466+{
1467+ UsersServiceDbus *service;
1468+ UserData *user;
1469+};
1470+
1471+static DBusGConnection *session_bus = NULL;
1472+static DBusGConnection *system_bus = NULL;
1473+static DBusGProxy *bus_proxy = NULL;
1474+static DBusGProxy *gdm_proxy = NULL;
1475+static DbusmenuMenuitem *root_menuitem = NULL;
1476+static GMainLoop *mainloop = NULL;
1477+static UsersServiceDbus *dbus_interface = NULL;
1478+
1479+static gint count;
1480+static GList *users;
1481
1482 static gboolean
1483 check_guest_session (void)
1484@@ -137,29 +150,134 @@
1485 }
1486
1487 static void
1488-create_items (DbusmenuMenuitem * root) {
1489- DbusmenuMenuitem * mi = NULL;
1490-
1491- mi = dbusmenu_menuitem_new();
1492- dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Lock Screen"));
1493- g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(lock_screen), NULL);
1494- dbusmenu_menuitem_child_append(root, mi);
1495-
1496- if (check_guest_session()) {
1497- mi = dbusmenu_menuitem_new();
1498- dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Guest Session"));
1499- dbusmenu_menuitem_child_append(root, mi);
1500- g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_guest_session), NULL);
1501- }
1502-
1503- if (check_new_session()) {
1504- mi = dbusmenu_menuitem_new();
1505- dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Switch User..."));
1506- dbusmenu_menuitem_child_append(root, mi);
1507- g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(activate_new_session), NULL);
1508- }
1509-
1510- return;
1511+activate_user_session (DbusmenuMenuitem *mi, gpointer user_data)
1512+{
1513+ UserData *user = (UserData *)user_data;
1514+ UsersServiceDbus *service = user->service;
1515+
1516+ users_service_dbus_activate_user_session (service, user);
1517+}
1518+
1519+static void
1520+remove_menu_item (DbusmenuMenuitem *root, gpointer user_data)
1521+{
1522+ DbusmenuMenuitem *child = (DbusmenuMenuitem *)user_data;
1523+
1524+ dbusmenu_menuitem_child_delete (root, child);
1525+}
1526+
1527+static gint
1528+compare_users_by_username (const gchar *a,
1529+ const gchar *b)
1530+{
1531+ UserData *user1 = (UserData *)a;
1532+ UserData *user2 = (UserData *)b;
1533+
1534+ return g_strcmp0 (user1->user_name, user2->user_name);
1535+}
1536+
1537+static void
1538+rebuild_items (DbusmenuMenuitem *root,
1539+ UsersServiceDbus *service)
1540+{
1541+ DbusmenuMenuitem *mi = NULL;
1542+ GList *u;
1543+ UserData *user;
1544+ gboolean can_activate;
1545+
1546+ can_activate = users_service_dbus_can_activate_session (service);
1547+
1548+ dbusmenu_menuitem_foreach (root, remove_menu_item, NULL);
1549+
1550+ mi = dbusmenu_menuitem_new();
1551+ dbusmenu_menuitem_property_set(mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Lock Screen"));
1552+ g_signal_connect(G_OBJECT(mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(lock_screen), NULL);
1553+ dbusmenu_menuitem_child_append(root, mi);
1554+
1555+ if (can_activate == TRUE)
1556+ {
1557+ if (check_guest_session ())
1558+ {
1559+ mi = dbusmenu_menuitem_new ();
1560+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Guest Session"));
1561+ dbusmenu_menuitem_child_append (root, mi);
1562+ g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_guest_session), NULL);
1563+ }
1564+
1565+ if (count > MINIMUM_USERS && count < MAXIMUM_USERS)
1566+ {
1567+ if (users != NULL)
1568+ {
1569+ GList *l = NULL;
1570+
1571+ for (l = users; l != NULL; l = l->next)
1572+ {
1573+ users = g_list_delete_link (users, l);
1574+ }
1575+
1576+ users = NULL;
1577+ }
1578+
1579+ users = users_service_dbus_get_user_list (service);
1580+
1581+ users = g_list_sort (users, (GCompareFunc)compare_users_by_username);
1582+
1583+ for (u = users; u != NULL; u = g_list_next (u))
1584+ {
1585+ user = u->data;
1586+
1587+ user->service = service;
1588+
1589+ mi = dbusmenu_menuitem_new ();
1590+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, user->real_name);
1591+ dbusmenu_menuitem_child_append (root, mi);
1592+ g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_user_session), user);
1593+ }
1594+ }
1595+
1596+ if (check_new_session ())
1597+ {
1598+ mi = dbusmenu_menuitem_new ();
1599+ dbusmenu_menuitem_property_set (mi, DBUSMENU_MENUITEM_PROP_LABEL, _("New Session..."));
1600+ dbusmenu_menuitem_child_append (root, mi);
1601+ g_signal_connect (G_OBJECT (mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK (activate_new_session), NULL);
1602+ }
1603+ }
1604+}
1605+
1606+static void
1607+user_added (UsersServiceDbus *service,
1608+ UserData *user,
1609+ gpointer user_data)
1610+{
1611+ DbusmenuMenuitem *root = (DbusmenuMenuitem *)user_data;
1612+
1613+ count++;
1614+
1615+ rebuild_items (root, service);
1616+}
1617+
1618+static void
1619+user_removed (UsersServiceDbus *service,
1620+ UserData *user,
1621+ gpointer user_data)
1622+{
1623+ DbusmenuMenuitem *root = (DbusmenuMenuitem *)user_data;
1624+
1625+ count--;
1626+
1627+ rebuild_items (root, service);
1628+}
1629+
1630+static void
1631+create_items (DbusmenuMenuitem *root,
1632+ UsersServiceDbus *service)
1633+{
1634+ g_return_if_fail (IS_USERS_SERVICE_DBUS (service));
1635+
1636+ count = users_service_dbus_get_user_count (service);
1637+
1638+ rebuild_items (root, service);
1639 }
1640
1641 int
1642@@ -167,35 +285,46 @@
1643 {
1644 g_type_init();
1645
1646- /* Setting up i18n and gettext. Apparently, we need
1647- all of these. */
1648- setlocale (LC_ALL, "");
1649- bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
1650- textdomain (GETTEXT_PACKAGE);
1651+ /* Setting up i18n and gettext. Apparently, we need
1652+ all of these. */
1653+ setlocale (LC_ALL, "");
1654+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
1655+ textdomain (GETTEXT_PACKAGE);
1656
1657 session_bus = dbus_g_bus_get(DBUS_BUS_SESSION, NULL);
1658- bus_proxy = dbus_g_proxy_new_for_name(session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
1659+ bus_proxy = dbus_g_proxy_new_for_name (session_bus, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
1660 GError * error = NULL;
1661 guint nameret = 0;
1662
1663 if (!org_freedesktop_DBus_request_name(bus_proxy, INDICATOR_USERS_DBUS_NAME, 0, &nameret, &error)) {
1664 g_error("Unable to call to request name");
1665 return 1;
1666- }
1667+ }
1668
1669 if (nameret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
1670 g_error("Unable to get name");
1671 return 1;
1672- }
1673-
1674- root_menuitem = dbusmenu_menuitem_new();
1675- g_debug("Root ID: %d", dbusmenu_menuitem_get_id(root_menuitem));
1676-
1677- create_items(root_menuitem);
1678+ }
1679+
1680+ dbus_interface = g_object_new (USERS_SERVICE_DBUS_TYPE, NULL);
1681+
1682+ root_menuitem = dbusmenu_menuitem_new ();
1683+ g_debug ("Root ID: %d", dbusmenu_menuitem_get_id (root_menuitem));
1684+
1685+ create_items (root_menuitem, dbus_interface);
1686
1687 DbusmenuServer * server = dbusmenu_server_new(INDICATOR_USERS_DBUS_OBJECT);
1688 dbusmenu_server_set_root(server, root_menuitem);
1689
1690+ g_signal_connect (G_OBJECT (dbus_interface),
1691+ "user-added",
1692+ G_CALLBACK (user_added),
1693+ root_menuitem);
1694+ g_signal_connect (G_OBJECT (dbus_interface),
1695+ "user-removed",
1696+ G_CALLBACK (user_removed),
1697+ root_menuitem);
1698+
1699 mainloop = g_main_loop_new(NULL, FALSE);
1700 g_main_loop_run(mainloop);
1701
1702
1703=== added file 'src/users-service.list'
1704--- src/users-service.list 1970-01-01 00:00:00 +0000
1705+++ src/users-service.list 2009-09-24 16:28:09 +0000
1706@@ -0,0 +1,1 @@
1707+VOID:INT64
1708
1709=== added file 'src/users-service.xml'
1710--- src/users-service.xml 1970-01-01 00:00:00 +0000
1711+++ src/users-service.xml 2009-09-24 16:28:09 +0000
1712@@ -0,0 +1,56 @@
1713+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
1714+<node name="/org/gnome/DisplayManager/UserManager">
1715+ <interface name="org.gnome.DisplayManager.UserManager">
1716+
1717+ <!-- Get the number of known users -->
1718+ <method name="CountUsers">
1719+ <arg name="user_count" direction="out" type="i"/>
1720+ </method>
1721+
1722+ <!-- Get the list of known UIDs -->
1723+ <method name="GetUserList">
1724+ <arg name="uids" direction="out" type="ax"/>
1725+ </method>
1726+
1727+ <!-- Get user info for a user -->
1728+ <method name="GetUserInfo">
1729+ <arg name="uid" direction="in" type="x"/>
1730+ <arg name="user_name" direction="out" type="s"/>
1731+ <arg name="real_name" direction="out" type="s"/>
1732+ <arg name="shell" direction="out" type="s"/>
1733+ <arg name="login_count" direction="out" type="i"/>
1734+ <arg name="icon_url" direction="out" type="s"/>
1735+ </method>
1736+
1737+ <!-- Get user info for a list of users -->
1738+ <method name="GetUsersInfo">
1739+ <arg name="uid" direction="in" type="ax"/>
1740+ <!-- (uid, user_name, real_name, shell, icon_url) -->
1741+ <arg name="user_info" direction="out" type="a(xsssis)"/>
1742+ </method>
1743+
1744+ <!-- Query if the initial user list is loaded -->
1745+ <method name="GetUsersLoaded">
1746+ <arg name="is_loaded" direction="out" type="b"/>
1747+ </method>
1748+
1749+ <!-- Triggered when the initial user list is loaded -->
1750+ <signal name="UsersLoaded"></signal>
1751+
1752+ <!-- Triggered when a users are added to/removed from the system.
1753+ Clients should monitor these signals as soon as they connect to
1754+ this object -->
1755+ <signal name="UserAdded">
1756+ <arg name="uid" type="x"/>
1757+ </signal>
1758+ <signal name="UserRemoved">
1759+ <arg name="uid" type="x"/>
1760+ </signal>
1761+
1762+ <!-- Triggered when a user has updated information -->
1763+ <signal name="UserUpdated">
1764+ <arg name="uid" type="x"/>
1765+ </signal>
1766+
1767+ </interface>
1768+</node>

Subscribers

People subscribed via source and target branches