1
/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3
* Copyright (C) 2010 Robert Ancell.
4
* Copyright (C) 2014 Canonical, Ltd.
5
* Authors: Robert Ancell <robert.ancell@canonical.com>
6
* Michael Terry <michael.terry@canonical.com>
8
* This library is free software; you can redistribute it and/or modify it under
9
* the terms of the GNU Lesser General Public License as published by the Free
10
* Software Foundation; either version 2 or version 3 of the License.
11
* See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
18
#include <sys/utsname.h>
23
#include "user-list.h"
37
USER_PROP_DISPLAY_NAME,
38
USER_PROP_HOME_DIRECTORY,
47
USER_PROP_HAS_MESSAGES,
59
static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
66
static guint user_signals[LAST_USER_SIGNAL] = { 0 };
70
/* Bus connection being communicated on */
73
/* D-Bus signals for accounts service events */
74
guint user_added_signal;
75
guint user_removed_signal;
77
/* D-Bus signals for display manager events */
78
guint session_added_signal;
79
guint session_removed_signal;
81
/* File monitor for password file */
82
GFileMonitor *passwd_monitor;
84
/* TRUE if have scanned users */
90
/* List of sessions */
92
} CommonUserListPrivate;
96
/* User list this user is part of */
97
CommonUserList *user_list;
99
/* TRUE if have loaded user properties */
100
gboolean loaded_values;
102
/* Accounts service path */
105
/* Update signal from accounts service */
106
guint changed_signal;
111
/* Descriptive name for user */
114
/* Home directory of user */
115
gchar *home_directory;
123
/* Background image for users */
126
/* TRUE if this user has messages available */
127
gboolean has_messages;
135
/* User chosen language */
138
/* User layout preferences */
141
/* User default session */
147
GObject parent_instance;
154
GObjectClass parent_class;
155
} CommonSessionClass;
157
G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
158
G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
159
#define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
160
GType common_session_get_type (void);
161
G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
163
#define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
164
#define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
166
#define PASSWD_FILE "/etc/passwd"
167
#define USER_CONFIG_FILE "/etc/lightdm/users.conf"
169
static CommonUserList *singleton = NULL;
172
* common_user_list_get_instance:
176
* Return value: (transfer none): the #CommonUserList
179
common_user_list_get_instance (void)
182
singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
187
common_user_list_cleanup (void)
190
g_object_unref (singleton);
195
get_user_by_name (CommonUserList *user_list, const gchar *username)
197
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
200
for (link = priv->users; link; link = link->next)
202
CommonUser *user = link->data;
203
if (g_strcmp0 (common_user_get_name (user), username) == 0)
211
get_user_by_path (CommonUserList *user_list, const gchar *path)
213
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
216
for (link = priv->users; link; link = link->next)
218
CommonUser *user = link->data;
219
if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
227
compare_user (gconstpointer a, gconstpointer b)
229
CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
230
return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
234
update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
236
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
238
/* Skip if already set to this */
239
if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
240
g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
241
g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
242
g_strcmp0 (common_user_get_image (user), image) == 0)
245
g_free (priv->real_name);
246
priv->real_name = g_strdup (real_name);
247
g_free (priv->home_directory);
248
priv->home_directory = g_strdup (home_directory);
249
g_free (priv->shell);
250
priv->shell = g_strdup (shell);
251
g_free (priv->image);
252
priv->image = g_strdup (image);
258
user_changed_cb (CommonUser *user)
260
g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
264
make_passwd_user (CommonUserList *user_list, struct passwd *entry)
266
CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
267
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
269
gchar *real_name, *image;
271
tokens = g_strsplit (entry->pw_gecos, ",", -1);
272
if (tokens[0] != NULL && tokens[0][0] != '\0')
273
real_name = g_strdup (tokens[0]);
275
real_name = g_strdup ("");
278
image = g_build_filename (entry->pw_dir, ".face", NULL);
279
if (!g_file_test (image, G_FILE_TEST_EXISTS))
282
image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
283
if (!g_file_test (image, G_FILE_TEST_EXISTS))
290
priv->user_list = user_list;
291
priv->name = g_strdup (entry->pw_name);
292
priv->real_name = real_name;
293
priv->home_directory = g_strdup (entry->pw_dir);
294
priv->shell = g_strdup (entry->pw_shell);
296
priv->uid = entry->pw_uid;
297
priv->gid = entry->pw_gid;
303
load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
305
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
309
gchar **hidden_users, **hidden_shells;
310
GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
311
GError *error = NULL;
313
g_debug ("Loading user config from %s", USER_CONFIG_FILE);
315
config = g_key_file_new ();
316
g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
317
if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
318
g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
319
g_clear_error (&error);
321
if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
322
minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
326
value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
328
value = g_strdup ("nobody nobody4 noaccess");
329
hidden_users = g_strsplit (value, " ", -1);
332
value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
334
value = g_strdup ("/bin/false /usr/sbin/nologin");
335
hidden_shells = g_strsplit (value, " ", -1);
338
g_key_file_free (config);
344
struct passwd *entry;
353
/* Ignore system users */
354
if (entry->pw_uid < minimum_uid)
357
/* Ignore users disabled by shell */
360
for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
361
if (hidden_shells[i])
365
/* Ignore certain users */
366
for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
370
user = make_passwd_user (user_list, entry);
372
/* Update existing users if have them */
373
for (link = priv->users; link; link = link->next)
375
CommonUser *info = link->data;
376
if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
378
if (update_passwd_user (info, common_user_get_real_name (user), common_user_get_home_directory (user), common_user_get_shell (user), common_user_get_image (user)))
379
changed_users = g_list_insert_sorted (changed_users, info, compare_user);
380
g_object_unref (user);
387
/* Only notify once we have loaded the user list */
388
if (priv->have_users)
389
new_users = g_list_insert_sorted (new_users, user, compare_user);
391
users = g_list_insert_sorted (users, user, compare_user);
393
g_strfreev (hidden_users);
394
g_strfreev (hidden_shells);
397
g_warning ("Failed to read password database: %s", strerror (errno));
401
/* Use new user list */
402
old_users = priv->users;
405
/* Notify of changes */
406
for (link = new_users; link; link = link->next)
408
CommonUser *info = link->data;
409
g_debug ("User %s added", common_user_get_name (info));
410
g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), NULL);
412
g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
414
g_list_free (new_users);
415
for (link = changed_users; link; link = link->next)
417
CommonUser *info = link->data;
418
g_debug ("User %s changed", common_user_get_name (info));
419
g_signal_emit (info, user_signals[CHANGED], 0);
421
g_list_free (changed_users);
422
for (link = old_users; link; link = link->next)
426
/* See if this user is in the current list */
427
for (new_link = priv->users; new_link; new_link = new_link->next)
429
if (new_link->data == link->data)
435
CommonUser *info = link->data;
436
g_debug ("User %s removed", common_user_get_name (info));
437
g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
438
g_object_unref (info);
441
g_list_free (old_users);
445
passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
447
if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
449
g_debug ("%s changed, reloading user list", g_file_get_path (file));
450
load_passwd_file (user_list, TRUE);
454
static gboolean load_accounts_user (CommonUser *user);
457
accounts_user_changed_cb (GDBusConnection *connection,
458
const gchar *sender_name,
459
const gchar *object_path,
460
const gchar *interface_name,
461
const gchar *signal_name,
462
GVariant *parameters,
465
CommonUser *user = data;
466
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
468
g_debug ("User %s changed", priv->path);
469
if (load_accounts_user (user))
470
g_signal_emit (user, user_signals[CHANGED], 0);
474
load_accounts_user (CommonUser *user)
476
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
477
GVariant *result, *value;
480
gboolean system_account = FALSE;
481
GError *error = NULL;
483
/* Get the properties for this user */
484
if (!priv->changed_signal)
485
priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
486
"org.freedesktop.Accounts",
487
"org.freedesktop.Accounts.User",
491
G_DBUS_SIGNAL_FLAGS_NONE,
492
accounts_user_changed_cb,
495
result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
496
"org.freedesktop.Accounts",
498
"org.freedesktop.DBus.Properties",
500
g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
501
G_VARIANT_TYPE ("(a{sv})"),
502
G_DBUS_CALL_FLAGS_NONE,
507
g_warning ("Error updating user %s: %s", priv->path, error->message);
508
g_clear_error (&error);
512
/* Store the properties we need */
513
g_variant_get (result, "(a{sv})", &iter);
514
while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
516
if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
519
priv->name = g_variant_dup_string (value, NULL);
521
else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
523
g_free (priv->real_name);
524
priv->real_name = g_variant_dup_string (value, NULL);
526
else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
528
g_free (priv->home_directory);
529
priv->home_directory = g_variant_dup_string (value, NULL);
531
else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
533
g_free (priv->shell);
534
priv->shell = g_variant_dup_string (value, NULL);
536
else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
537
system_account = g_variant_get_boolean (value);
538
else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
541
g_free (priv->language);
542
priv->language = g_variant_dup_string (value, NULL);
544
else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
546
g_free (priv->image);
547
priv->image = g_variant_dup_string (value, NULL);
548
if (strcmp (priv->image, "") == 0)
550
g_free (priv->image);
554
else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
556
g_free (priv->session);
557
priv->session = g_variant_dup_string (value, NULL);
559
else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
561
g_free (priv->background);
562
priv->background = g_variant_dup_string (value, NULL);
563
if (strcmp (priv->background, "") == 0)
565
g_free (priv->background);
566
priv->background = NULL;
569
else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
571
g_strfreev (priv->layouts);
572
priv->layouts = g_variant_dup_strv (value, NULL);
575
priv->layouts = g_malloc (sizeof (gchar *) * 1);
576
priv->layouts[0] = NULL;
579
else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
580
priv->has_messages = g_variant_get_boolean (value);
581
else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
582
priv->uid = g_variant_get_uint64 (value);
584
g_variant_iter_free (iter);
586
g_variant_unref (result);
588
priv->loaded_values = TRUE;
590
return !system_account;
594
add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
596
CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
598
CommonUserPrivate *priv;
600
user = g_object_new (COMMON_TYPE_USER, NULL);
601
priv = GET_USER_PRIVATE (user);
603
g_debug ("User %s added", path);
604
priv->user_list = user_list;
605
priv->path = g_strdup (path);
606
g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
607
if (load_accounts_user (user))
609
list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
611
g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
614
g_object_unref (user);
618
accounts_user_added_cb (GDBusConnection *connection,
619
const gchar *sender_name,
620
const gchar *object_path,
621
const gchar *interface_name,
622
const gchar *signal_name,
623
GVariant *parameters,
626
CommonUserList *user_list = data;
630
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
632
g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
636
g_variant_get (parameters, "(&o)", &path);
638
/* Add user if we haven't got them */
639
user = get_user_by_path (user_list, path);
641
add_accounts_user (user_list, path, TRUE);
645
accounts_user_deleted_cb (GDBusConnection *connection,
646
const gchar *sender_name,
647
const gchar *object_path,
648
const gchar *interface_name,
649
const gchar *signal_name,
650
GVariant *parameters,
653
CommonUserList *user_list = data;
654
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
658
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
660
g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
664
g_variant_get (parameters, "(&o)", &path);
666
/* Delete user if we know of them */
667
user = get_user_by_path (user_list, path);
670
g_debug ("User %s deleted", path);
671
priv->users = g_list_remove (priv->users, user);
673
g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
675
g_object_unref (user);
679
static CommonSession *
680
load_session (CommonUserList *user_list, const gchar *path)
682
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
683
CommonSession *session = NULL;
684
GVariant *result, *username;
685
GError *error = NULL;
687
result = g_dbus_connection_call_sync (priv->bus,
688
"org.freedesktop.DisplayManager",
690
"org.freedesktop.DBus.Properties",
692
g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
693
G_VARIANT_TYPE ("(v)"),
694
G_DBUS_CALL_FLAGS_NONE,
699
g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
700
g_clear_error (&error);
704
g_variant_get (result, "(v)", &username);
705
if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
709
g_variant_get (username, "&s", &name);
711
g_debug ("Loaded session %s (%s)", path, name);
712
session = g_object_new (common_session_get_type (), NULL);
713
session->username = g_strdup (name);
714
session->path = g_strdup (path);
715
priv->sessions = g_list_append (priv->sessions, session);
717
g_variant_unref (username);
718
g_variant_unref (result);
724
session_added_cb (GDBusConnection *connection,
725
const gchar *sender_name,
726
const gchar *object_path,
727
const gchar *interface_name,
728
const gchar *signal_name,
729
GVariant *parameters,
732
CommonUserList *user_list = data;
734
CommonSession *session;
735
CommonUser *user = NULL;
737
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
739
g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
743
g_variant_get (parameters, "(&o)", &path);
744
session = load_session (user_list, path);
746
user = get_user_by_name (user_list, session->username);
748
g_signal_emit (user, user_signals[CHANGED], 0);
752
session_removed_cb (GDBusConnection *connection,
753
const gchar *sender_name,
754
const gchar *object_path,
755
const gchar *interface_name,
756
const gchar *signal_name,
757
GVariant *parameters,
760
CommonUserList *user_list = data;
761
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
765
if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
767
g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
771
g_variant_get (parameters, "(&o)", &path);
773
for (link = priv->sessions; link; link = link->next)
775
CommonSession *session = link->data;
776
if (strcmp (session->path, path) == 0)
780
g_debug ("Session %s removed", path);
781
priv->sessions = g_list_delete_link (priv->sessions, link);
782
user = get_user_by_name (user_list, session->username);
784
g_signal_emit (user, user_signals[CHANGED], 0);
785
g_object_unref (session);
792
load_sessions (CommonUserList *user_list)
794
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
796
GError *error = NULL;
798
priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
799
"org.freedesktop.DisplayManager",
800
"org.freedesktop.DisplayManager",
802
"/org/freedesktop/DisplayManager",
804
G_DBUS_SIGNAL_FLAGS_NONE,
808
priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
809
"org.freedesktop.DisplayManager",
810
"org.freedesktop.DisplayManager",
812
"/org/freedesktop/DisplayManager",
814
G_DBUS_SIGNAL_FLAGS_NONE,
818
result = g_dbus_connection_call_sync (priv->bus,
819
"org.freedesktop.DisplayManager",
820
"/org/freedesktop/DisplayManager",
821
"org.freedesktop.DBus.Properties",
823
g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
824
G_VARIANT_TYPE ("(v)"),
825
G_DBUS_CALL_FLAGS_NONE,
830
g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
831
g_clear_error (&error);
834
if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
840
g_variant_get (result, "(v)", &value);
842
g_debug ("Loading sessions from org.freedesktop.DisplayManager");
843
g_variant_get (value, "ao", &iter);
844
while (g_variant_iter_loop (iter, "&o", &path))
845
load_session (user_list, path);
846
g_variant_iter_free (iter);
848
g_variant_unref (value);
851
g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
853
g_variant_unref (result);
858
load_users (CommonUserList *user_list)
860
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
862
GError *error = NULL;
864
if (priv->have_users)
866
priv->have_users = TRUE;
868
/* Get user list from accounts service and fall back to /etc/passwd if that fails */
869
priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
870
"org.freedesktop.Accounts",
871
"org.freedesktop.Accounts",
873
"/org/freedesktop/Accounts",
875
G_DBUS_SIGNAL_FLAGS_NONE,
876
accounts_user_added_cb,
879
priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
880
"org.freedesktop.Accounts",
881
"org.freedesktop.Accounts",
883
"/org/freedesktop/Accounts",
885
G_DBUS_SIGNAL_FLAGS_NONE,
886
accounts_user_deleted_cb,
889
result = g_dbus_connection_call_sync (priv->bus,
890
"org.freedesktop.Accounts",
891
"/org/freedesktop/Accounts",
892
"org.freedesktop.Accounts",
894
g_variant_new ("()"),
895
G_VARIANT_TYPE ("(ao)"),
896
G_DBUS_CALL_FLAGS_NONE,
901
g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
902
g_clear_error (&error);
908
g_debug ("Loading users from org.freedesktop.Accounts");
909
g_variant_get (result, "(ao)", &iter);
910
while (g_variant_iter_loop (iter, "&o", &path))
911
add_accounts_user (user_list, path, FALSE);
912
g_variant_iter_free (iter);
913
g_variant_unref (result);
919
g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
920
priv->user_added_signal = 0;
921
g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
922
priv->user_removed_signal = 0;
924
load_passwd_file (user_list, FALSE);
926
/* Watch for changes to user list */
928
passwd_file = g_file_new_for_path (PASSWD_FILE);
929
priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
930
g_object_unref (passwd_file);
932
g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
934
g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
935
g_clear_error (&error);
940
* common_user_list_get_length:
941
* @user_list: a #CommonUserList
943
* Return value: The number of users able to log in
946
common_user_list_get_length (CommonUserList *user_list)
948
g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
949
load_users (user_list);
950
return g_list_length (GET_LIST_PRIVATE (user_list)->users);
954
* common_user_list_get_users:
955
* @user_list: A #CommonUserList
957
* Get a list of users to present to the user. This list may be a subset of the
958
* available users and may be empty depending on the server configuration.
960
* Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
963
common_user_list_get_users (CommonUserList *user_list)
965
g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
966
load_users (user_list);
967
return GET_LIST_PRIVATE (user_list)->users;
971
* common_user_list_get_user_by_name:
972
* @user_list: A #CommonUserList
973
* @username: Name of user to get.
975
* Get infomation about a given user or #NULL if this user doesn't exist.
976
* Includes hidden and system users, unlike the list from
977
* common_user_list_get_users.
979
* Return value: (transfer full): A #CommonUser entry for the given user.
982
common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
984
g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
985
g_return_val_if_fail (username != NULL, NULL);
987
load_users (user_list);
989
CommonUser *user = get_user_by_name (user_list, username);
991
return g_object_ref (user);
993
/* Sometimes we need to look up users that aren't in AccountsService.
994
Notably we need to look up the user that the greeter runs as, which
995
is usually 'lightdm'. For such cases, we manually create a one-off
996
CommonUser object and pre-seed with passwd info. */
997
struct passwd *entry = getpwnam (username);
999
return make_passwd_user (user_list, entry);
1005
common_user_list_init (CommonUserList *user_list)
1007
CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1009
priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1013
common_user_list_set_property (GObject *object,
1015
const GValue *value,
1018
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1022
common_user_list_get_property (GObject *object,
1027
CommonUserList *self;
1029
self = COMMON_USER_LIST (object);
1033
case LIST_PROP_NUM_USERS:
1034
g_value_set_int (value, common_user_list_get_length (self));
1037
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1043
common_user_list_finalize (GObject *object)
1045
CommonUserList *self = COMMON_USER_LIST (object);
1046
CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1048
/* Remove children first, they might access us */
1049
g_list_free_full (priv->users, g_object_unref);
1050
g_list_free_full (priv->sessions, g_object_unref);
1052
if (priv->user_added_signal)
1053
g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1054
if (priv->user_removed_signal)
1055
g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1056
if (priv->session_added_signal)
1057
g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1058
if (priv->session_removed_signal)
1059
g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1060
g_object_unref (priv->bus);
1061
if (priv->passwd_monitor)
1062
g_object_unref (priv->passwd_monitor);
1064
G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1068
common_user_list_class_init (CommonUserListClass *klass)
1070
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1072
g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1074
object_class->set_property = common_user_list_set_property;
1075
object_class->get_property = common_user_list_get_property;
1076
object_class->finalize = common_user_list_finalize;
1078
g_object_class_install_property (object_class,
1079
LIST_PROP_NUM_USERS,
1080
g_param_spec_int ("num-users",
1082
"Number of login users",
1086
* CommonUserList::user-added:
1087
* @user_list: A #CommonUserList
1088
* @user: The #CommonUser that has been added.
1090
* The ::user-added signal gets emitted when a user account is created.
1092
list_signals[USER_ADDED] =
1093
g_signal_new ("user-added",
1094
G_TYPE_FROM_CLASS (klass),
1096
G_STRUCT_OFFSET (CommonUserListClass, user_added),
1099
G_TYPE_NONE, 1, COMMON_TYPE_USER);
1102
* CommonUserList::user-changed:
1103
* @user_list: A #CommonUserList
1104
* @user: The #CommonUser that has been changed.
1106
* The ::user-changed signal gets emitted when a user account is modified.
1108
list_signals[USER_CHANGED] =
1109
g_signal_new ("user-changed",
1110
G_TYPE_FROM_CLASS (klass),
1112
G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1115
G_TYPE_NONE, 1, COMMON_TYPE_USER);
1118
* CommonUserList::user-removed:
1119
* @user_list: A #CommonUserList
1120
* @user: The #CommonUser that has been removed.
1122
* The ::user-removed signal gets emitted when a user account is removed.
1124
list_signals[USER_REMOVED] =
1125
g_signal_new ("user-removed",
1126
G_TYPE_FROM_CLASS (klass),
1128
G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1131
G_TYPE_NONE, 1, COMMON_TYPE_USER);
1135
call_method (CommonUser *user, const gchar *method, GVariant *args,
1136
const gchar *expected, GVariant **result)
1139
GError *error = NULL;
1140
CommonUserPrivate *user_priv = GET_USER_PRIVATE (user);
1141
CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_priv->user_list);
1143
answer = g_dbus_connection_call_sync (list_priv->bus,
1144
"org.freedesktop.Accounts",
1146
"org.freedesktop.Accounts.User",
1149
G_VARIANT_TYPE (expected),
1150
G_DBUS_CALL_FLAGS_NONE,
1155
g_warning ("Could not call %s: %s", method, error->message);
1156
g_clear_error (&error);
1164
g_variant_unref (answer);
1170
save_string_to_dmrc (CommonUser *user, const gchar *group,
1171
const gchar *key, const gchar *value)
1175
dmrc = dmrc_load (user);
1176
g_key_file_set_string (dmrc, group, key, value);
1177
dmrc_save (dmrc, user);
1179
g_key_file_free (dmrc);
1183
load_dmrc (CommonUser *user)
1185
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1188
dmrc = dmrc_load (user);
1190
// FIXME: Watch for changes
1192
/* The Language field contains the locale */
1193
g_free (priv->language);
1194
priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1196
if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1198
g_strfreev (priv->layouts);
1199
priv->layouts = g_malloc (sizeof (gchar *) * 2);
1200
priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1201
priv->layouts[1] = NULL;
1204
g_free (priv->session);
1205
priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1207
g_key_file_free (dmrc);
1210
/* Loads language/layout/session info for user */
1212
load_user_values (CommonUser *user)
1214
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1216
if (priv->loaded_values)
1218
priv->loaded_values = TRUE;
1225
* common_user_get_name:
1226
* @user: A #CommonUser
1228
* Get the name of a user.
1230
* Return value: The name of the given user
1233
common_user_get_name (CommonUser *user)
1235
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1236
load_user_values (user);
1237
return GET_USER_PRIVATE (user)->name;
1241
* common_user_get_real_name:
1242
* @user: A #CommonUser
1244
* Get the real name of a user.
1246
* Return value: The real name of the given user
1249
common_user_get_real_name (CommonUser *user)
1251
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1252
load_user_values (user);
1253
return GET_USER_PRIVATE (user)->real_name;
1257
* common_user_get_display_name:
1258
* @user: A #CommonUser
1260
* Get the display name of a user.
1262
* Return value: The display name of the given user
1265
common_user_get_display_name (CommonUser *user)
1267
CommonUserPrivate *priv;
1269
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1271
load_user_values (user);
1273
priv = GET_USER_PRIVATE (user);
1274
if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1277
return priv->real_name;
1281
* common_user_get_home_directory:
1282
* @user: A #CommonUser
1284
* Get the home directory for a user.
1286
* Return value: The users home directory
1289
common_user_get_home_directory (CommonUser *user)
1291
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1292
load_user_values (user);
1293
return GET_USER_PRIVATE (user)->home_directory;
1297
* common_user_get_shell:
1298
* @user: A #CommonUser
1300
* Get the shell for a user.
1302
* Return value: The user's shell
1305
common_user_get_shell (CommonUser *user)
1307
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1308
load_user_values (user);
1309
return GET_USER_PRIVATE (user)->shell;
1313
* common_user_get_image:
1314
* @user: A #CommonUser
1316
* Get the image URI for a user.
1318
* Return value: The image URI for the given user or #NULL if no URI
1321
common_user_get_image (CommonUser *user)
1323
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1324
load_user_values (user);
1325
return GET_USER_PRIVATE (user)->image;
1329
* common_user_get_background:
1330
* @user: A #CommonUser
1332
* Get the background file path for a user.
1334
* Return value: The background file path for the given user or #NULL if no path
1337
common_user_get_background (CommonUser *user)
1339
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1340
load_user_values (user);
1341
return GET_USER_PRIVATE (user)->background;
1345
* common_user_get_language:
1346
* @user: A #CommonUser
1348
* Get the language for a user.
1350
* Return value: The language in the form of a local specification (e.g. "de_DE.UTF-8") for the given user or #NULL if using the system default locale.
1353
common_user_get_language (CommonUser *user)
1355
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1356
load_user_values (user);
1357
const gchar *language = GET_USER_PRIVATE (user)->language;
1358
return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1362
* common_user_set_language:
1363
* @user: A #CommonUser
1364
* @language: The user's new language
1366
* Set the language for a user.
1369
common_user_set_language (CommonUser *user, const gchar *language)
1371
g_return_if_fail (COMMON_IS_USER (user));
1372
if (g_strcmp0 (common_user_get_language (user), language) != 0)
1374
call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1375
save_string_to_dmrc (user, "Desktop", "Language", language);
1380
* common_user_get_layout:
1381
* @user: A #CommonUser
1383
* Get the keyboard layout for a user.
1385
* Return value: The keyboard layout for the given user or #NULL if using system defaults. Copy the value if you want to use it long term.
1388
common_user_get_layout (CommonUser *user)
1390
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1391
load_user_values (user);
1392
return GET_USER_PRIVATE (user)->layouts[0];
1396
* common_user_get_layouts:
1397
* @user: A #CommonUser
1399
* Get the configured keyboard layouts for a user.
1401
* Return value: (transfer none): A NULL-terminated array of keyboard layouts for the given user. Copy the values if you want to use them long term.
1403
const gchar * const *
1404
common_user_get_layouts (CommonUser *user)
1406
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1407
load_user_values (user);
1408
return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1412
* common_user_get_session:
1413
* @user: A #CommonUser
1415
* Get the session for a user.
1417
* Return value: The session for the given user or #NULL if using system defaults.
1420
common_user_get_session (CommonUser *user)
1422
g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1423
load_user_values (user);
1424
const gchar *session = GET_USER_PRIVATE (user)->session;
1425
return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1429
* common_user_set_session:
1430
* @user: A #CommonUser
1431
* @language: The user's new session
1433
* Set the session for a user.
1436
common_user_set_session (CommonUser *user, const gchar *session)
1438
g_return_if_fail (COMMON_IS_USER (user));
1439
if (g_strcmp0 (common_user_get_session (user), session) != 0)
1441
call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1442
save_string_to_dmrc (user, "Desktop", "Session", session);
1447
* common_user_get_logged_in:
1448
* @user: A #CommonUser
1450
* Check if a user is logged in.
1452
* Return value: #TRUE if the user is currently logged in.
1455
common_user_get_logged_in (CommonUser *user)
1457
CommonUserPrivate *priv;
1458
CommonUserListPrivate *list_priv;
1461
g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1463
priv = GET_USER_PRIVATE (user);
1464
list_priv = GET_LIST_PRIVATE (priv->user_list);
1466
// Lazily decide to load/listen to sessions
1467
if (list_priv->session_added_signal == 0)
1468
load_sessions (priv->user_list);
1470
for (link = list_priv->sessions; link; link = link->next)
1472
CommonSession *session = link->data;
1473
if (strcmp (session->username, priv->name) == 0)
1481
* common_user_get_has_messages:
1482
* @user: A #CommonUser
1484
* Check if a user has waiting messages.
1486
* Return value: #TRUE if the user has waiting messages.
1489
common_user_get_has_messages (CommonUser *user)
1491
g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1492
load_user_values (user);
1493
return GET_USER_PRIVATE (user)->has_messages;
1497
* common_user_get_uid:
1498
* @user: A #CommonUser
1500
* Get the uid of a user
1502
* Return value: The user's uid
1505
common_user_get_uid (CommonUser *user)
1507
g_return_val_if_fail (COMMON_IS_USER (user), 0);
1508
load_user_values (user);
1509
return GET_USER_PRIVATE (user)->uid;
1513
* common_user_get_gid:
1514
* @user: A #CommonUser
1516
* Get the gid of a user
1518
* Return value: The user's gid
1521
common_user_get_gid (CommonUser *user)
1523
g_return_val_if_fail (COMMON_IS_USER (user), 0);
1524
load_user_values (user);
1525
/* gid is not actually stored in AccountsService, so if our user is from
1526
AccountsService, we have to look up manually in passwd. gid won't
1527
change, so just look up the first time we're asked and never again. */
1528
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1529
if (priv->uid != 0 && priv->gid == 0)
1531
struct passwd *entry = getpwuid (priv->uid);
1533
priv->gid = entry->pw_gid;
1539
common_user_init (CommonUser *user)
1541
CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1542
priv->layouts = g_malloc (sizeof (gchar *) * 1);
1543
priv->layouts[0] = NULL;
1547
common_user_set_property (GObject *object,
1549
const GValue *value,
1552
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1556
common_user_get_property (GObject *object,
1563
self = COMMON_USER (object);
1567
case USER_PROP_NAME:
1568
g_value_set_string (value, common_user_get_name (self));
1570
case USER_PROP_REAL_NAME:
1571
g_value_set_string (value, common_user_get_real_name (self));
1573
case USER_PROP_DISPLAY_NAME:
1574
g_value_set_string (value, common_user_get_display_name (self));
1576
case USER_PROP_HOME_DIRECTORY:
1577
g_value_set_string (value, common_user_get_home_directory (self));
1579
case USER_PROP_SHELL:
1580
g_value_set_string (value, common_user_get_shell (self));
1582
case USER_PROP_IMAGE:
1583
g_value_set_string (value, common_user_get_image (self));
1585
case USER_PROP_BACKGROUND:
1586
g_value_set_string (value, common_user_get_background (self));
1588
case USER_PROP_LANGUAGE:
1589
g_value_set_string (value, common_user_get_language (self));
1591
case USER_PROP_LAYOUT:
1592
g_value_set_string (value, common_user_get_layout (self));
1594
case USER_PROP_LAYOUTS:
1595
g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1597
case USER_PROP_SESSION:
1598
g_value_set_string (value, common_user_get_session (self));
1600
case USER_PROP_LOGGED_IN:
1601
g_value_set_boolean (value, common_user_get_logged_in (self));
1603
case USER_PROP_HAS_MESSAGES:
1604
g_value_set_boolean (value, common_user_get_has_messages (self));
1607
g_value_set_uint64 (value, common_user_get_uid (self));
1610
g_value_set_uint64 (value, common_user_get_gid (self));
1613
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1619
common_user_finalize (GObject *object)
1621
CommonUser *self = COMMON_USER (object);
1622
CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1624
g_free (priv->path);
1625
if (priv->changed_signal)
1626
g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1627
g_free (priv->name);
1628
g_free (priv->real_name);
1629
g_free (priv->home_directory);
1630
g_free (priv->shell);
1631
g_free (priv->image);
1632
g_free (priv->background);
1633
g_free (priv->language);
1634
g_strfreev (priv->layouts);
1635
g_free (priv->session);
1639
common_user_class_init (CommonUserClass *klass)
1641
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1643
g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1645
object_class->set_property = common_user_set_property;
1646
object_class->get_property = common_user_get_property;
1647
object_class->finalize = common_user_finalize;
1649
g_object_class_install_property (object_class,
1651
g_param_spec_string ("name",
1655
G_PARAM_READWRITE));
1656
g_object_class_install_property (object_class,
1657
USER_PROP_REAL_NAME,
1658
g_param_spec_string ("real-name",
1662
G_PARAM_READWRITE));
1663
g_object_class_install_property (object_class,
1664
USER_PROP_DISPLAY_NAME,
1665
g_param_spec_string ("display-name",
1667
"Users display name",
1670
g_object_class_install_property (object_class,
1671
USER_PROP_HOME_DIRECTORY,
1672
g_param_spec_string ("home-directory",
1676
G_PARAM_READWRITE));
1677
g_object_class_install_property (object_class,
1679
g_param_spec_string ("shell",
1683
G_PARAM_READWRITE));
1684
g_object_class_install_property (object_class,
1686
g_param_spec_string ("image",
1690
G_PARAM_READWRITE));
1691
g_object_class_install_property (object_class,
1692
USER_PROP_BACKGROUND,
1693
g_param_spec_string ("background",
1697
G_PARAM_READWRITE));
1698
g_object_class_install_property (object_class,
1700
g_param_spec_string ("language",
1702
"Language used by this user",
1705
g_object_class_install_property (object_class,
1707
g_param_spec_string ("layout",
1709
"Keyboard layout used by this user",
1712
g_object_class_install_property (object_class,
1714
g_param_spec_boxed ("layouts",
1716
"Keyboard layouts used by this user",
1719
g_object_class_install_property (object_class,
1721
g_param_spec_string ("session",
1723
"Session used by this user",
1726
g_object_class_install_property (object_class,
1727
USER_PROP_LOGGED_IN,
1728
g_param_spec_boolean ("logged-in",
1730
"TRUE if the user is currently in a session",
1732
G_PARAM_READWRITE));
1733
g_object_class_install_property (object_class,
1734
USER_PROP_LOGGED_IN,
1735
g_param_spec_boolean ("has-messages",
1737
"TRUE if the user is has waiting messages",
1739
G_PARAM_READWRITE));
1740
g_object_class_install_property (object_class,
1742
g_param_spec_uint64 ("uid",
1748
G_PARAM_READWRITE));
1749
g_object_class_install_property (object_class,
1751
g_param_spec_uint64 ("gd",
1757
G_PARAM_READWRITE));
1760
* CommonUser::changed:
1761
* @user: A #CommonUser
1763
* The ::changed signal gets emitted this user account is modified.
1765
user_signals[CHANGED] =
1766
g_signal_new ("changed",
1767
G_TYPE_FROM_CLASS (klass),
1769
G_STRUCT_OFFSET (CommonUserClass, changed),
1776
common_session_init (CommonSession *common_session)
1781
common_session_finalize (GObject *object)
1783
CommonSession *self = COMMON_SESSION (object);
1785
g_free (self->path);
1786
g_free (self->username);
1790
common_session_class_init (CommonSessionClass *klass)
1792
GObjectClass *object_class = G_OBJECT_CLASS (klass);
1793
object_class->finalize = common_session_finalize;