1
/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
3
* Copyright 2009 Canonical Ltd.
6
* Cody Russell <crussell@canonical.com>
7
* Charles Kerr <charles.kerr@canonical.com>
9
* This program is free software: you can redistribute it and/or modify it
10
* under the terms of the GNU General Public License version 3, as published
11
* by the Free Software Foundation.
13
* This program is distributed in the hope that it will be useful, but
14
* WITHOUT ANY WARRANTY; without even the implied warranties of
15
* MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16
* PURPOSE. See the GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License along
19
* with this program. If not, see <http://www.gnu.org/licenses/>.
30
#include <pwd.h> /* getpwuid() */
32
#include "dbus-accounts.h"
33
#include "dbus-consolekit-manager.h"
34
#include "dbus-consolekit-seat.h"
35
#include "dbus-consolekit-session.h"
36
#include "dbus-display-manager.h"
37
#include "dbus-user.h"
38
#include "shared-names.h"
39
#include "users-service-dbus.h"
41
#define CK_ADDR "org.freedesktop.ConsoleKit"
42
#define CK_SESSION_IFACE "org.freedesktop.ConsoleKit.Session"
48
static void update_user_list (UsersServiceDbus * self);
50
static gchar* get_seat (UsersServiceDbus * service);
52
static void on_user_added (Accounts * o,
53
const gchar * user_object_path,
54
UsersServiceDbus * service);
56
static void on_user_deleted (Accounts * o,
57
const gchar * user_object_path,
58
UsersServiceDbus * service);
60
static void on_session_added (ConsoleKitSeat * seat,
62
UsersServiceDbus * service);
64
static void on_session_removed (ConsoleKitSeat * seat,
66
UsersServiceDbus * service);
68
static void on_session_list (ConsoleKitSeat * seat,
69
GAsyncResult * result,
70
UsersServiceDbus * service);
76
struct _UsersServiceDbusPrivate
81
/* ssid -> AccountsUser lookup */
82
GHashTable * sessions;
84
/* user object path -> AccountsUser lookup */
87
GCancellable * cancellable;
88
ConsoleKitSeat * seat_proxy;
89
ConsoleKitManager * ck_manager_proxy;
90
Accounts * accounts_proxy;
100
USER_LOGGED_IN_CHANGED,
101
GUEST_LOGGED_IN_CHANGED,
105
static guint signals[N_SIGNALS] = { 0 };
107
G_DEFINE_TYPE (UsersServiceDbus, users_service_dbus, G_TYPE_OBJECT);
110
users_service_dbus_dispose (GObject *object)
112
UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
114
g_clear_object (&priv->accounts_proxy);
115
g_clear_object (&priv->seat_proxy);
116
g_clear_object (&priv->ck_manager_proxy);
118
if (priv->cancellable != NULL)
120
g_cancellable_cancel (priv->cancellable);
121
g_clear_object (&priv->cancellable);
124
if (priv->users != NULL)
126
g_hash_table_destroy (priv->users);
130
if (priv->sessions != NULL)
132
g_hash_table_destroy (priv->sessions);
133
priv->sessions = NULL;
136
G_OBJECT_CLASS (users_service_dbus_parent_class)->dispose (object);
140
users_service_dbus_finalize (GObject *object)
142
UsersServiceDbusPrivate * priv = USERS_SERVICE_DBUS(object)->priv;
144
g_free (priv->guest_ssid);
147
G_OBJECT_CLASS (users_service_dbus_parent_class)->finalize (object);
151
users_service_dbus_class_init (UsersServiceDbusClass *klass)
153
GObjectClass *object_class = G_OBJECT_CLASS (klass);
155
g_type_class_add_private (object_class, sizeof (UsersServiceDbusPrivate));
157
object_class->dispose = users_service_dbus_dispose;
158
object_class->finalize = users_service_dbus_finalize;
160
signals[USER_LIST_CHANGED] = g_signal_new (
162
G_TYPE_FROM_CLASS (klass),
164
G_STRUCT_OFFSET (UsersServiceDbusClass, user_list_changed),
166
g_cclosure_marshal_VOID__VOID,
169
signals[USER_LOGGED_IN_CHANGED] = g_signal_new (
170
"user-logged-in-changed",
171
G_TYPE_FROM_CLASS (klass),
173
G_STRUCT_OFFSET (UsersServiceDbusClass, user_logged_in_changed),
175
g_cclosure_marshal_VOID__OBJECT,
176
G_TYPE_NONE, 1, G_TYPE_OBJECT);
178
signals[GUEST_LOGGED_IN_CHANGED] = g_signal_new (
179
"guest-logged-in-changed",
180
G_TYPE_FROM_CLASS (klass),
182
G_STRUCT_OFFSET (UsersServiceDbusClass, guest_logged_in_changed),
184
g_cclosure_marshal_VOID__VOID,
189
users_service_dbus_init (UsersServiceDbus *self)
191
GError * error = NULL;
193
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
194
USERS_SERVICE_DBUS_TYPE,
195
UsersServiceDbusPrivate);
197
UsersServiceDbusPrivate * p = self->priv;
199
p->cancellable = g_cancellable_new ();
201
/* ssid -> AccountsUser */
202
p->sessions = g_hash_table_new_full (g_str_hash,
207
/* user object path -> AccountsUser */
208
p->users = g_hash_table_new_full (g_str_hash,
214
*** create the consolekit manager proxy...
217
p->ck_manager_proxy = console_kit_manager_proxy_new_for_bus_sync (
219
G_DBUS_PROXY_FLAGS_NONE,
220
"org.freedesktop.ConsoleKit",
221
"/org/freedesktop/ConsoleKit/Manager",
226
g_warning ("%s: %s", G_STRLOC, error->message);
227
g_clear_error (&error);
230
p->seat = get_seat (self);
233
*** create the consolekit seat proxy...
238
ConsoleKitSeat * proxy = console_kit_seat_proxy_new_for_bus_sync (
240
G_DBUS_PROXY_FLAGS_NONE,
241
"org.freedesktop.ConsoleKit",
248
g_warning ("Failed to connect to the ConsoleKit seat: %s", error->message);
249
g_clear_error (&error);
253
g_signal_connect (proxy, "session-added",
254
G_CALLBACK (on_session_added), self);
255
g_signal_connect (proxy, "session-removed",
256
G_CALLBACK (on_session_removed), self);
257
console_kit_seat_call_get_sessions (proxy, p->cancellable,
258
(GAsyncReadyCallback)on_session_list, self);
259
p->seat_proxy = proxy;
264
*** create the accounts manager proxy...
267
Accounts * proxy = accounts_proxy_new_for_bus_sync (
269
G_DBUS_PROXY_FLAGS_NONE,
270
"org.freedesktop.Accounts",
271
"/org/freedesktop/Accounts",
276
g_warning ("%s: %s", G_STRFUNC, error->message);
277
g_clear_error (&error);
281
g_signal_connect (proxy, "user-added", G_CALLBACK(on_user_added), self);
282
g_signal_connect (proxy, "user-deleted", G_CALLBACK(on_user_deleted), self);
283
p->accounts_proxy = proxy;
284
update_user_list (self);
293
emit_user_list_changed (UsersServiceDbus * self)
295
g_signal_emit (self, signals[USER_LIST_CHANGED], 0);
299
emit_user_login_changed (UsersServiceDbus * self, AccountsUser * user)
301
g_signal_emit (self, signals[USER_LOGGED_IN_CHANGED], 0, user);
305
emit_guest_login_changed (UsersServiceDbus * self)
307
g_signal_emit (self, signals[GUEST_LOGGED_IN_CHANGED], 0);
314
static ConsoleKitSession*
315
create_consolekit_session_proxy (const char * ssid)
317
GError * error = NULL;
319
ConsoleKitSession * p = console_kit_session_proxy_new_for_bus_sync (
321
G_DBUS_PROXY_FLAGS_NONE,
328
g_warning ("%s: %s", G_STRLOC, error->message);
329
g_error_free (error);
336
get_seat_from_session_proxy (ConsoleKitSession * session_proxy)
340
GError * error = NULL;
341
console_kit_session_call_get_seat_id_sync (session_proxy,
347
g_debug ("%s: %s", G_STRLOC, error->message);
348
g_error_free (error);
355
get_seat (UsersServiceDbus *service)
359
GError * error = NULL;
360
UsersServiceDbusPrivate * priv = service->priv;
362
console_kit_manager_call_get_current_session_sync (priv->ck_manager_proxy,
369
g_debug ("%s: %s", G_STRLOC, error->message);
370
g_error_free (error);
374
ConsoleKitSession * session = create_consolekit_session_proxy (ssid);
378
seat = get_seat_from_session_proxy (session);
379
g_object_unref (session);
387
**** AccountsUser add-ons for tracking sessions
391
user_get_sessions_hashset (AccountsUser * user)
397
q = g_quark_from_static_string ("sessions");
400
GObject * o = G_OBJECT (user);
401
GHashTable * h = g_object_get_qdata (o, q);
404
h = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
405
g_object_set_qdata_full (o, q, h, (GDestroyNotify)g_hash_table_destroy);
412
user_add_session (AccountsUser * user, const char * ssid)
414
g_hash_table_add (user_get_sessions_hashset(user), g_strdup(ssid));
418
user_remove_session (AccountsUser * user, const char * ssid)
420
g_hash_table_remove (user_get_sessions_hashset(user), ssid);
424
user_count_sessions (AccountsUser * user)
426
return g_hash_table_size (user_get_sessions_hashset(user));
433
/* adds this user session to the user's and service's session tables */
435
add_user_session (UsersServiceDbus * service,
439
ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
440
if (session_proxy != NULL)
442
UsersServiceDbusPrivate * priv = service->priv;
443
gchar * seat = get_seat_from_session_proxy (session_proxy);
445
/* is this session in our seat? */
446
if (seat && priv->seat && !g_strcmp0 (seat, priv->seat))
448
/* does this session have a display? */
449
gchar * display = NULL;
450
console_kit_session_call_get_x11_display_sync (session_proxy,
453
const gboolean has_display = display && *display;
458
const gchar * username = accounts_user_get_user_name (user);
459
g_debug ("%s adding %s's session '%s' to our tables",
460
G_STRLOC, username, ssid);
462
g_hash_table_insert (priv->sessions,
464
g_object_ref (user));
466
user_add_session (user, ssid);
471
g_object_unref (session_proxy);
475
/* calls add_user_session() for each of this user's sessions */
477
add_user_sessions (UsersServiceDbus *self, AccountsUser * user)
479
const guint64 uid = accounts_user_get_uid (user);
480
const char * username = accounts_user_get_user_name (user);
481
g_debug ("%s adding %s (%i)", G_STRLOC, username, (int)uid);
483
GError * error = NULL;
484
gchar ** sessions = NULL;
485
console_kit_manager_call_get_sessions_for_unix_user_sync (
486
self->priv->ck_manager_proxy,
494
g_debug ("%s: %s", G_STRLOC, error->message);
495
g_error_free (error);
497
else if (sessions != NULL)
501
for (i=0; sessions[i]; i++)
503
const char * const ssid = sessions[i];
504
g_debug ("%s adding %s's session %s", G_STRLOC, username, ssid);
505
add_user_session (self, user, ssid);
508
g_strfreev (sessions);
513
copy_proxy_properties (GDBusProxy * source, GDBusProxy * target)
515
gchar ** keys = g_dbus_proxy_get_cached_property_names (source);
520
GVariantBuilder builder;
521
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
523
for (i=0; keys[i]; i++)
525
const gchar * const key = keys[i];
526
GVariant * value = g_dbus_proxy_get_cached_property (source, key);
527
g_dbus_proxy_set_cached_property (target, key, value);
528
g_variant_builder_add (&builder, "{sv}", key, value);
529
g_variant_unref (value);
532
g_signal_emit_by_name (target, "g-properties-changed", g_variant_builder_end(&builder), keys);
538
* The AccountsUserProxy's properties aren't being updated automatically
539
* for some reason... the only update we get is the 'changed' signal.
540
* This function is a workaround to update our User object's properties.
543
on_user_changed (AccountsUser * user, UsersServiceDbus * service)
545
AccountsUser * tmp = accounts_user_proxy_new_for_bus_sync (
547
G_DBUS_PROXY_FLAGS_NONE,
548
"org.freedesktop.Accounts",
549
g_dbus_proxy_get_object_path (G_DBUS_PROXY(user)),
554
copy_proxy_properties (G_DBUS_PROXY(tmp), G_DBUS_PROXY(user));
555
g_object_unref (tmp);
560
add_user_from_object_path (UsersServiceDbus * self,
561
const char * user_object_path)
563
GError * error = NULL;
565
AccountsUser * user = accounts_user_proxy_new_for_bus_sync (
567
G_DBUS_PROXY_FLAGS_NONE,
568
"org.freedesktop.Accounts",
575
g_warning ("%s: %s", G_STRLOC, error->message);
576
g_clear_error (&error);
580
AccountsUser * prev = g_hash_table_lookup (self->priv->users, user_object_path);
582
if (prev != NULL) /* we've already got this user... sync its properties */
584
copy_proxy_properties (G_DBUS_PROXY(user), G_DBUS_PROXY(prev));
585
g_object_unref (user);
588
else /* ooo, we got a new user */
590
g_signal_connect (user, "changed", G_CALLBACK(on_user_changed), self);
591
g_hash_table_insert (self->priv->users, g_strdup(user_object_path), user);
594
add_user_sessions (self, user);
599
/* asks org.freedesktop.Accounts for a list of users and
600
* calls add_user_from_object_path() on each of those users */
602
update_user_list (UsersServiceDbus *self)
604
g_return_if_fail(IS_USERS_SERVICE_DBUS(self));
606
GError * error = NULL;
607
char ** object_paths = NULL;
608
UsersServiceDbusPrivate * priv = self->priv;
610
accounts_call_list_cached_users_sync (priv->accounts_proxy,
617
g_warning ("%s: %s", G_STRFUNC, error->message);
618
g_clear_error (&error);
620
else if (object_paths != NULL)
624
for (i=0; object_paths[i] != NULL; ++i)
626
add_user_from_object_path (self, object_paths[i]);
629
emit_user_list_changed (self);
631
g_strfreev (object_paths);
634
g_debug ("%s finished updating the user list", G_STRLOC);
638
on_user_added (Accounts * o G_GNUC_UNUSED,
639
const gchar * user_path G_GNUC_UNUSED,
640
UsersServiceDbus * service)
642
/* We see a new user but we might not want to list it --
643
for example, lightdm shows up when we switch to the greeter.
644
So instead of adding the user directly here, let's ask
645
org.freedesktop.Accounts for a fresh list of users
646
because it filters out special cases. */
647
update_user_list (service);
651
on_user_deleted (Accounts * o G_GNUC_UNUSED,
652
const gchar * user_path,
653
UsersServiceDbus * service)
655
AccountsUser * user = g_hash_table_lookup (service->priv->users, user_path);
659
GObject * o = g_object_ref (G_OBJECT(user));
660
g_hash_table_remove (service->priv->users, user_path);
661
emit_user_list_changed (service);
666
static AccountsUser *
667
find_user_from_username (UsersServiceDbus * self,
668
const gchar * username)
670
AccountsUser * match = NULL;
672
g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), match);
676
g_hash_table_iter_init (&iter, self->priv->users);
677
while (!match && g_hash_table_iter_next (&iter, NULL, &user))
679
if (!g_strcmp0 (username, accounts_user_get_user_name (user)))
693
on_session_removed (ConsoleKitSeat * seat_proxy,
695
UsersServiceDbus * service)
697
g_return_if_fail (IS_USERS_SERVICE_DBUS (service));
699
UsersServiceDbusPrivate * priv = service->priv;
700
g_debug ("%s %s() session removed %s", G_STRLOC, G_STRFUNC, ssid);
702
if (!g_strcmp0 (ssid, priv->guest_ssid))
704
g_debug ("%s removing guest session %s", G_STRLOC, ssid);
705
g_clear_pointer (&priv->guest_ssid, g_free);
706
emit_guest_login_changed (service);
710
AccountsUser * user = g_hash_table_lookup (priv->sessions, ssid);
713
g_debug ("%s we're not tracking ssid %s", G_STRLOC, ssid);
717
GObject * o = g_object_ref (G_OBJECT(user));
718
g_hash_table_remove (service->priv->users, ssid);
719
user_remove_session (user, ssid);
720
emit_user_login_changed (service, user);
727
get_unix_username_from_ssid (UsersServiceDbus * self,
730
gchar * username = NULL;
732
ConsoleKitSession * session_proxy = create_consolekit_session_proxy (ssid);
733
if (session_proxy != NULL)
736
GError * error = NULL;
737
console_kit_session_call_get_unix_user_sync (session_proxy,
742
g_warning ("%s: %s", G_STRLOC, error->message);
743
g_clear_error (&error);
748
const struct passwd * pwent = getpwuid (uid);
751
g_warning ("Failed to lookup user id %d: %s", (int)uid, g_strerror(errno));
755
username = g_strdup (pwent->pw_name);
759
g_object_unref (session_proxy);
766
is_guest_username (const char * username)
768
if (!g_strcmp0 (username, "guest"))
771
if (username && g_str_has_prefix (username, "guest-"))
777
/* If the new session belongs to 'guest', update our guest_ssid.
778
Otherwise, call add_user_session() to update our session tables */
780
on_session_added (ConsoleKitSeat * seat_proxy G_GNUC_UNUSED,
782
UsersServiceDbus * service)
784
g_return_if_fail (IS_USERS_SERVICE_DBUS(service));
786
gchar * username = get_unix_username_from_ssid (service, ssid);
787
g_debug ("%s %s() username %s has new session %s", G_STRLOC, G_STRFUNC, username, ssid);
789
if (is_guest_username (username))
791
/* handle guest as a special case -- it's not in the GDM
792
user tables and there isn't be an AccountsUser for it */
793
g_debug("Found guest session: %s", ssid);
794
g_free (service->priv->guest_ssid);
795
service->priv->guest_ssid = g_strdup (ssid);
796
emit_guest_login_changed (service);
800
AccountsUser * user = find_user_from_username (service, username);
804
add_user_session (service, user, ssid);
805
emit_user_login_changed (service, user);
812
/* Receives a list of sessions and calls on_session_added() for each of them */
814
on_session_list (ConsoleKitSeat * seat_proxy,
815
GAsyncResult * result,
816
UsersServiceDbus * self)
818
GError * error = NULL;
819
gchar ** sessions = NULL;
820
g_debug ("%s bootstrapping the session list", G_STRLOC);
822
console_kit_seat_call_get_sessions_finish (seat_proxy,
829
g_debug ("%s: %s", G_STRLOC, error->message);
830
g_error_free (error);
832
else if (sessions != NULL)
836
for (i=0; sessions[i]; i++)
838
g_debug ("%s adding initial session '%s'", G_STRLOC, sessions[i]);
839
on_session_added (seat_proxy, sessions[i], self);
842
g_strfreev (sessions);
845
g_debug ("%s done bootstrapping the session list", G_STRLOC);
848
static DisplayManagerSeat *
849
create_display_proxy (UsersServiceDbus * self)
851
const gchar * const seat = g_getenv ("XDG_SEAT_PATH");
852
g_debug ("%s creating a DisplayManager proxy for seat %s", G_STRLOC, seat);
854
GError * error = NULL;
855
DisplayManagerSeat * p = display_manager_seat_proxy_new_for_bus_sync (
857
G_DBUS_PROXY_FLAGS_NONE,
858
"org.freedesktop.DisplayManager",
865
g_warning ("%s: %s", G_STRLOC, error->message);
866
g_error_free (error);
877
* users_service_dbus_get_user_list:
879
* Returns: (transfer container): a list of AccountsUser objects
882
users_service_dbus_get_user_list (UsersServiceDbus * self)
884
g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), NULL);
886
return g_hash_table_get_values (self->priv->users);
890
* users_service_dbus_show_greeter:
892
* Ask the Display Mnaager to switch to the greeter screen.
895
users_service_dbus_show_greeter (UsersServiceDbus * self)
897
g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
899
DisplayManagerSeat * dp = create_display_proxy (self);
900
display_manager_seat_call_switch_to_greeter_sync (dp, NULL, NULL);
901
g_clear_object (&dp);
905
* users_service_dbus_activate_guest_session:
907
* Activates the guest account.
910
users_service_dbus_activate_guest_session (UsersServiceDbus * self)
912
g_return_if_fail(IS_USERS_SERVICE_DBUS(self));
914
DisplayManagerSeat * dp = create_display_proxy (self);
915
display_manager_seat_call_switch_to_guest_sync (dp, "", NULL, NULL);
916
g_clear_object (&dp);
920
* users_service_dbus_activate_user_session:
922
* Activates a specific user.
925
users_service_dbus_activate_user_session (UsersServiceDbus * self,
928
g_return_if_fail (IS_USERS_SERVICE_DBUS(self));
930
const char * const username = accounts_user_get_user_name (user);
931
DisplayManagerSeat * dp = create_display_proxy (self);
932
display_manager_seat_call_switch_to_user_sync (dp, username, "", NULL, NULL);
933
g_clear_object (&dp);
937
* users_service_dbus_guest_session_enabled:
939
* Tells whether or not guest sessions are allowed.
942
users_service_dbus_guest_session_enabled (UsersServiceDbus * self)
944
g_return_val_if_fail(IS_USERS_SERVICE_DBUS(self), FALSE);
946
DisplayManagerSeat * dp = create_display_proxy (self);
947
const gboolean enabled = display_manager_seat_get_has_guest_account (dp);
948
g_clear_object (&dp);
953
users_service_dbus_can_activate_session (UsersServiceDbus * self)
955
gboolean can_activate = FALSE;
957
g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), can_activate);
959
GError * error = NULL;
960
console_kit_seat_call_can_activate_sessions_sync (self->priv->seat_proxy,
966
g_warning ("%s: %s", G_STRLOC, error->message);
967
g_error_free (error);
974
users_service_dbus_is_guest_logged_in (UsersServiceDbus * self)
976
g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
978
return self->priv->guest_ssid != NULL;
982
users_service_dbus_is_user_logged_in (UsersServiceDbus * self,
985
g_return_val_if_fail (IS_USERS_SERVICE_DBUS(self), FALSE);
986
g_return_val_if_fail (IS_ACCOUNTS_USER(user), FALSE);
988
return user_count_sessions (user) > 0;