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, USER_SIGNAL_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, USER_SIGNAL_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_LIST_SIGNAL_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_LIST_SIGNAL_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_LIST_SIGNAL_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 (USER_SIGNAL_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;