2
* Copyright 2013 Canonical Ltd.
5
* Charles Kerr <charles.kerr@canonical.com>
7
* This program is free software: you can redistribute it and/or modify it
8
* under the terms of the GNU General Public License version 3, as published
9
* by the Free Software Foundation.
11
* This program is distributed in the hope that it will be useful, but
12
* WITHOUT ANY WARRANTY; without even the implied warranties of
13
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
14
* PURPOSE. See the GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License along
17
* with this program. If not, see <http://www.gnu.org/licenses/>.
20
#include "dbus-user.h"
24
struct _IndicatorSessionUsersDbusPriv
26
Login1Manager * login1_manager;
27
Login1Seat * login1_seat;
28
DisplayManagerSeat * dm_seat;
31
/* hash table of int uids to UserRecord* */
32
GHashTable * uid_to_account;
34
/* a hashset of int uids of users who are logged in */
37
/* the user-id of the owner of the active session */
40
/* true if this is a live session */
43
GCancellable * cancellable;
45
guint update_list_tag;
48
typedef IndicatorSessionUsersDbusPriv priv_t;
50
G_DEFINE_TYPE (IndicatorSessionUsersDbus,
51
indicator_session_users_dbus,
52
INDICATOR_TYPE_SESSION_USERS)
66
user_record_new (AccountsUser * user, gulong signal_id)
68
struct UserRecord * rec;
69
rec = g_new (struct UserRecord, 1);
70
rec->user = g_object_ref (user);
71
rec->signal_id = signal_id;
76
user_record_free (struct UserRecord * rec)
78
g_signal_handler_disconnect (rec->user, rec->signal_id);
79
g_object_unref (G_OBJECT (rec->user));
87
/* get our private org.freedesktop.Accounts.User proxy for the given uid */
89
get_user_for_uid (IndicatorSessionUsersDbus * self, guint uid)
91
struct UserRecord * rec;
93
if ((rec = g_hash_table_lookup (self->priv->uid_to_account,
94
GUINT_TO_POINTER(uid))))
101
is_tracked_uid (IndicatorSessionUsersDbus * self, guint uid)
103
return get_user_for_uid (self, uid) != NULL;
107
emit_user_added (IndicatorSessionUsersDbus * self, guint32 uid)
109
if (is_tracked_uid (self, uid))
110
indicator_session_users_added (INDICATOR_SESSION_USERS(self), uid);
114
emit_user_changed (IndicatorSessionUsersDbus * self, guint32 uid)
116
if (is_tracked_uid (self, uid))
117
indicator_session_users_changed (INDICATOR_SESSION_USERS(self), uid);
121
emit_user_removed (IndicatorSessionUsersDbus * self, guint32 uid)
123
indicator_session_users_removed (INDICATOR_SESSION_USERS(self), uid);
131
set_is_live_session_flag (IndicatorSessionUsersDbus * self, gboolean b)
133
priv_t * p = self->priv;
139
indicator_session_users_notify_is_live_session (INDICATOR_SESSION_USERS (self));
144
set_active_uid (IndicatorSessionUsersDbus * self, guint uid)
146
priv_t * p = self->priv;
148
if (p->active_uid != uid)
150
const guint old_uid = p->active_uid;
154
emit_user_changed (self, old_uid);
155
emit_user_changed (self, uid);
160
set_logins (IndicatorSessionUsersDbus * self, GHashTable * logins)
162
GHashTable * old_logins = self->priv->logins;
166
self->priv->logins = g_hash_table_ref (logins);
168
/* fire 'user changed' event for users who logged out */
169
g_hash_table_iter_init (&iter, old_logins);
170
while ((g_hash_table_iter_next (&iter, &uid, NULL)))
171
if (!g_hash_table_contains (logins, uid))
172
emit_user_changed (self, GPOINTER_TO_UINT(uid));
174
/* fire 'user changed' event for users who logged in */
175
g_hash_table_iter_init (&iter, logins);
176
while ((g_hash_table_iter_next (&iter, &uid, NULL)))
177
if (!g_hash_table_contains (old_logins, uid))
178
emit_user_changed (self, GPOINTER_TO_UINT(uid));
180
g_hash_table_destroy (old_logins);
184
**** User Account Tracking
187
static void create_user_proxy_for_path (IndicatorSessionUsersDbus *, const char *);
189
/* called when a user proxy gets the 'Changed' signal */
191
on_user_changed (AccountsUser * user, gpointer gself)
193
/* Accounts.User doesn't update properties in the standard way,
194
* so create a new proxy to pull in the new properties.
195
* The older proxy is freed when it's replaced in our accounts hash */
196
const char * path = g_dbus_proxy_get_object_path (G_DBUS_PROXY(user));
197
create_user_proxy_for_path (gself, path);
201
track_user (IndicatorSessionUsersDbus * self,
204
const guint32 uid = accounts_user_get_uid (user);
205
priv_t * p = self->priv;
207
const gboolean already_had_user = is_tracked_uid (self, uid);
209
id = g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self);
210
g_hash_table_insert (p->uid_to_account,
211
GUINT_TO_POINTER (uid),
212
user_record_new (user, id));
214
if (already_had_user)
215
emit_user_changed (self, uid);
217
emit_user_added (self, uid);
221
untrack_user (IndicatorSessionUsersDbus * self,
228
priv_t * p = self->priv;
230
/* find the uid matching this object path */
232
g_hash_table_iter_init (&iter, p->uid_to_account);
233
while (!uid && g_hash_table_iter_next (&iter, &key, &val))
235
struct UserRecord * rec = val;
236
GDBusProxy * proxy = G_DBUS_PROXY (rec->user);
237
if (!g_strcmp0 (path, g_dbus_proxy_get_object_path (proxy)))
238
uid = GPOINTER_TO_UINT (key);
243
g_hash_table_remove (p->uid_to_account, GUINT_TO_POINTER(uid));
245
emit_user_removed (self, uid);
249
/* We got a new org.freedesktop.Accounts.User proxy.
250
If it's one we want to remember, pass it to track_user() */
252
on_user_proxy_ready (GObject * o G_GNUC_UNUSED,
260
user = accounts_user_proxy_new_for_bus_finish (res, &err);
263
if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
264
g_warning ("%s: %s", G_STRFUNC, err->message);
270
if (!accounts_user_get_system_account (user))
271
track_user (self, user);
273
g_object_unref (user);
278
create_user_proxy_for_path (IndicatorSessionUsersDbus * self,
281
accounts_user_proxy_new_for_bus (G_BUS_TYPE_SYSTEM,
282
G_DBUS_PROXY_FLAGS_GET_INVALIDATED_PROPERTIES,
283
"org.freedesktop.Accounts",
285
self->priv->cancellable,
286
on_user_proxy_ready, self);
289
/* create proxy objects for everything in Account's user-list */
291
on_user_list_ready (GObject * o, GAsyncResult * res, gpointer gself)
298
accounts_call_list_cached_users_finish (ACCOUNTS(o), &paths, res, &err);
301
if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
302
g_warning ("%s %s: %s", G_STRLOC, G_STRFUNC, err->message);
310
for (i=0; paths && paths[i]; ++i)
311
create_user_proxy_for_path (gself, paths[i]);
318
set_account_manager (IndicatorSessionUsersDbus * self, Accounts * a)
320
priv_t * p = self->priv;
322
if (p->accounts != NULL)
324
g_signal_handlers_disconnect_by_data (p->accounts, self);
325
g_clear_object (&p->accounts);
330
p->accounts = g_object_ref (a);
332
accounts_call_list_cached_users (a,
333
self->priv->cancellable,
337
g_signal_connect_swapped (a, "user-added",
338
G_CALLBACK(create_user_proxy_for_path), self);
340
g_signal_connect_swapped (a, "user-deleted",
341
G_CALLBACK(untrack_user), self);
349
/* Based on the login1 manager's list of current sessions,
350
update our 'logins', 'is_live', and 'active_uid' fields */
352
on_login1_manager_session_list_ready (GObject * o,
361
login1_manager_call_list_sessions_finish (LOGIN1_MANAGER(o),
368
if (!g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))
369
g_warning ("%s: %s", G_STRFUNC, err->message);
375
const gchar * const current_seat_id = g_getenv ("XDG_SEAT");
376
const gchar * const current_session_id = g_getenv ("XDG_SESSION_ID");
377
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself);
378
const gchar * session_id = NULL;
380
const gchar * user_name = NULL;
381
const gchar * seat_id = NULL;
382
const gchar * path = NULL;
383
gboolean is_live_session = FALSE;
384
GHashTable * logins = g_hash_table_new (g_direct_hash, g_direct_equal);
387
g_variant_iter_init (&iter, sessions);
388
while (g_variant_iter_loop (&iter, "(&su&s&s&o)", &session_id,
394
/* only track sessions on our seat */
395
if (g_strcmp0 (seat_id, current_seat_id))
398
if (!g_strcmp0 (session_id, current_session_id))
400
set_active_uid (self, uid);
402
if ((uid==999) && !g_strcmp0 (user_name, "ubuntu"))
403
is_live_session = TRUE;
406
g_hash_table_add (logins, GINT_TO_POINTER(uid));
409
set_is_live_session_flag (self, is_live_session);
410
set_logins (self, logins);
412
g_hash_table_unref (logins);
413
g_variant_unref (sessions);
418
update_session_list (IndicatorSessionUsersDbus * self)
420
priv_t * p = self->priv;
422
if (p->login1_manager != NULL)
424
login1_manager_call_list_sessions (p->login1_manager,
426
on_login1_manager_session_list_ready,
432
on_update_session_list_timer (gpointer gself)
434
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (gself);
436
update_session_list (self);
438
self->priv->update_list_tag = 0;
439
return G_SOURCE_REMOVE;
442
/* A dead session can still show up in list-sessions for a few seconds.
443
So just to be safe, queue up a rebuild for a few seconds from now */
445
update_session_list_twice (IndicatorSessionUsersDbus * self)
447
priv_t * p = self->priv;
449
update_session_list (self);
451
if (p->update_list_tag == 0)
452
p->update_list_tag = g_timeout_add_seconds (5,
453
on_update_session_list_timer,
458
set_login1_manager (IndicatorSessionUsersDbus * self,
459
Login1Manager * login1_manager)
461
priv_t * p = self->priv;
463
if (p->login1_manager != NULL)
465
g_signal_handlers_disconnect_by_data (p->login1_manager, self);
467
g_clear_object (&p->login1_manager);
470
if (login1_manager != NULL)
472
p->login1_manager = g_object_ref (login1_manager);
474
g_signal_connect_swapped (login1_manager, "session-new",
475
G_CALLBACK(update_session_list), self);
476
g_signal_connect_swapped (login1_manager, "session-removed",
477
G_CALLBACK(update_session_list_twice), self);
478
g_signal_connect_swapped (login1_manager, "user-new",
479
G_CALLBACK(update_session_list), self);
480
g_signal_connect_swapped (login1_manager, "user-removed",
481
G_CALLBACK(update_session_list_twice), self);
482
update_session_list (self);
487
set_login1_seat (IndicatorSessionUsersDbus * self,
488
Login1Seat * login1_seat)
490
priv_t * p = self->priv;
492
if (p->login1_seat != NULL)
494
g_signal_handlers_disconnect_by_data (p->login1_seat, self);
496
g_clear_object (&p->login1_seat);
499
if (login1_seat != NULL)
501
p->login1_seat = g_object_ref (login1_seat);
503
g_signal_connect_swapped (login1_seat, "notify::active-session",
504
G_CALLBACK(update_session_list), self);
505
update_session_list (self);
510
set_display_manager_seat (IndicatorSessionUsersDbus * self,
511
DisplayManagerSeat * dm_seat)
513
priv_t * p = self->priv;
515
g_clear_object (&p->dm_seat);
518
p->dm_seat = g_object_ref (dm_seat);
522
**** IndicatorSessionUsers virtual functions
525
/* switch to (or create) a session for the specified user */
527
my_activate_user (IndicatorSessionUsers * users, guint uid)
529
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS(users);
530
priv_t * p = self->priv;
532
const char * username;
534
au = get_user_for_uid (self, uid);
535
username = au ? accounts_user_get_user_name (au) : NULL;
539
g_warning ("%s %s can't find user '%u'", G_STRLOC, G_STRFUNC, uid);
543
g_return_if_fail (p->dm_seat != NULL);
545
display_manager_seat_call_switch_to_user (p->dm_seat,
554
/* returns true if this is a live session */
556
my_is_live_session (IndicatorSessionUsers * users)
558
g_return_val_if_fail (INDICATOR_IS_SESSION_USERS_DBUS(users), FALSE);
560
return INDICATOR_SESSION_USERS_DBUS(users)->priv->is_live;
563
/* get a list of our user ids */
565
my_get_uids (IndicatorSessionUsers * users)
567
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users);
568
return g_hash_table_get_keys (self->priv->uid_to_account);
571
/* build a new struct populated with info on the specified user */
572
static IndicatorSessionUser *
573
my_get_user (IndicatorSessionUsers * users, guint uid)
575
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (users);
576
priv_t * p = self->priv;
577
IndicatorSessionUser * ret;
581
au = get_user_for_uid (self, uid);
582
if (au && !accounts_user_get_system_account(au))
584
g_assert (uid == accounts_user_get_uid (au));
586
ret = g_new0 (IndicatorSessionUser, 1);
588
ret->user_name = g_strdup (accounts_user_get_user_name (au));
589
ret->real_name = g_strdup (accounts_user_get_real_name (au));
590
ret->icon_file = g_strdup (accounts_user_get_icon_file (au));
591
ret->login_frequency = accounts_user_get_login_frequency (au);
592
ret->is_logged_in = g_hash_table_contains (p->logins, GINT_TO_POINTER(uid));
593
ret->is_current_user = uid == p->active_uid;
600
**** GObject virtual functions
604
my_dispose (GObject * o)
606
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o);
607
priv_t * p = self->priv;
609
if (p->update_list_tag != 0)
611
g_source_remove (p->update_list_tag);
612
p->update_list_tag = 0;
617
g_cancellable_cancel (p->cancellable);
618
g_clear_object (&p->cancellable);
621
set_account_manager (self, NULL);
622
set_display_manager_seat (self, NULL);
623
set_login1_seat (self, NULL);
624
set_login1_manager (self, NULL);
626
g_hash_table_remove_all (p->uid_to_account);
628
G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->dispose (o);
632
my_finalize (GObject * o)
634
IndicatorSessionUsersDbus * self = INDICATOR_SESSION_USERS_DBUS (o);
635
priv_t * p = self->priv;
637
g_hash_table_destroy (p->logins);
638
g_hash_table_destroy (p->uid_to_account);
640
G_OBJECT_CLASS (indicator_session_users_dbus_parent_class)->finalize (o);
644
indicator_session_users_dbus_class_init (IndicatorSessionUsersDbusClass * klass)
646
GObjectClass * object_class;
647
IndicatorSessionUsersClass * users_class;
649
object_class = G_OBJECT_CLASS (klass);
650
object_class->dispose = my_dispose;
651
object_class->finalize = my_finalize;
653
users_class = INDICATOR_SESSION_USERS_CLASS (klass);
654
users_class->is_live_session = my_is_live_session;
655
users_class->get_uids = my_get_uids;
656
users_class->get_user = my_get_user;
657
users_class->activate_user = my_activate_user;
659
g_type_class_add_private (klass, sizeof (IndicatorSessionUsersDbusPriv));
663
indicator_session_users_dbus_init (IndicatorSessionUsersDbus * self)
667
p = G_TYPE_INSTANCE_GET_PRIVATE (self,
668
INDICATOR_TYPE_SESSION_USERS_DBUS,
669
IndicatorSessionUsersDbusPriv);
671
p->cancellable = g_cancellable_new ();
673
p->uid_to_account = g_hash_table_new_full (g_direct_hash,
676
(GDestroyNotify)user_record_free);
678
p->logins = g_hash_table_new (g_direct_hash, g_direct_equal);
685
IndicatorSessionUsers *
686
indicator_session_users_dbus_new (void)
688
gpointer o = g_object_new (INDICATOR_TYPE_SESSION_USERS_DBUS, NULL);
690
return INDICATOR_SESSION_USERS (o);
694
indicator_session_users_dbus_set_proxies (IndicatorSessionUsersDbus * self,
695
Login1Manager * login1_manager,
696
Login1Seat * login1_seat,
697
DisplayManagerSeat * dm_seat,
700
g_return_if_fail (INDICATOR_IS_SESSION_USERS_DBUS (self));
702
set_login1_manager (self, login1_manager);
703
set_login1_seat (self, login1_seat);
704
set_display_manager_seat (self, dm_seat);
705
set_account_manager (self, accounts);