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"
27 LIST_PROP_NUM_USERS = 1,
35 USER_PROP_DISPLAY_NAME,
36 USER_PROP_HOME_DIRECTORY,
45 USER_PROP_HAS_MESSAGES,
57 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
65 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
69 /* Bus connection being communicated on */
72 /* D-Bus signals for accounts service events */
73 guint user_added_signal;
74 guint user_removed_signal;
76 /* D-Bus signals for display manager events */
77 guint session_added_signal;
78 guint session_removed_signal;
80 /* File monitor for password file */
81 GFileMonitor *passwd_monitor;
83 /* TRUE if have scanned users */
89 /* List of sessions */
91 } CommonUserListPrivate;
95 /* TRUE if have loaded the DMRC file */
98 /* Bus we are listening for accounts service on */
101 /* Accounts service path */
104 /* Update signal from accounts service */
105 guint changed_signal;
110 /* Descriptive name for user */
113 /* Home directory of user */
114 gchar *home_directory;
122 /* Background image for users */
125 /* TRUE if this user has messages available */
126 gboolean has_messages;
134 /* User chosen language */
137 /* User layout preferences */
140 /* User default session */
146 GObject parent_instance;
153 GObjectClass parent_class;
154 } CommonSessionClass;
156 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
157 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
158 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
159 GType common_session_get_type (void);
160 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
162 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
163 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
165 #define PASSWD_FILE "/etc/passwd"
166 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
168 static CommonUserList *singleton = NULL;
171 * common_user_list_get_instance:
175 * Return value: (transfer none): the #CommonUserList
178 common_user_list_get_instance (void)
181 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
186 common_user_list_cleanup (void)
188 g_clear_object (&singleton);
192 get_user_by_name (CommonUserList *user_list, const gchar *username)
194 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
197 for (link = priv->users; link; link = link->next)
199 CommonUser *user = link->data;
200 if (g_strcmp0 (common_user_get_name (user), username) == 0)
208 get_user_by_path (CommonUserList *user_list, const gchar *path)
210 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
213 for (link = priv->users; link; link = link->next)
215 CommonUser *user = link->data;
216 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
224 compare_user (gconstpointer a, gconstpointer b)
226 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
227 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
231 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
233 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
235 /* Skip if already set to this */
236 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
237 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
238 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
239 g_strcmp0 (common_user_get_image (user), image) == 0)
242 g_free (priv->real_name);
243 priv->real_name = g_strdup (real_name);
244 g_free (priv->home_directory);
245 priv->home_directory = g_strdup (home_directory);
246 g_free (priv->shell);
247 priv->shell = g_strdup (shell);
248 g_free (priv->image);
249 priv->image = g_strdup (image);
254 static void load_sessions (CommonUserList *user_list);
257 get_logged_in_cb (CommonUser *user, CommonUserList *user_list)
259 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
260 const gchar *username;
263 // Lazily decide to load/listen to sessions
264 if (priv->session_added_signal == 0)
265 load_sessions (user_list);
267 username = GET_USER_PRIVATE (user)->name;
268 for (link = priv->sessions; link; link = link->next)
270 CommonSession *session = link->data;
271 if (strcmp (session->username, username) == 0)
279 user_changed_cb (CommonUser *user, CommonUserList *user_list)
281 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
285 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
287 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
288 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
290 gchar *real_name, *image;
292 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
294 tokens = g_strsplit (entry->pw_gecos, ",", -1);
295 if (tokens[0] != NULL && tokens[0][0] != '\0')
296 real_name = g_strdup (tokens[0]);
298 real_name = g_strdup ("");
301 image = g_build_filename (entry->pw_dir, ".face", NULL);
302 if (!g_file_test (image, G_FILE_TEST_EXISTS))
305 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
306 if (!g_file_test (image, G_FILE_TEST_EXISTS))
313 priv->name = g_strdup (entry->pw_name);
314 priv->real_name = real_name;
315 priv->home_directory = g_strdup (entry->pw_dir);
316 priv->shell = g_strdup (entry->pw_shell);
318 priv->uid = entry->pw_uid;
319 priv->gid = entry->pw_gid;
325 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
327 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
331 gchar **hidden_users, **hidden_shells;
332 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
333 GError *error = NULL;
335 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
337 config = g_key_file_new ();
338 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
339 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
340 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
341 g_clear_error (&error);
343 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
344 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
348 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
350 value = g_strdup ("nobody nobody4 noaccess");
351 hidden_users = g_strsplit (value, " ", -1);
354 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
356 value = g_strdup ("/bin/false /usr/sbin/nologin");
357 hidden_shells = g_strsplit (value, " ", -1);
360 g_key_file_free (config);
366 struct passwd *entry;
375 /* Ignore system users */
376 if (entry->pw_uid < minimum_uid)
379 /* Ignore users disabled by shell */
382 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
383 if (hidden_shells[i])
387 /* Ignore certain users */
388 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
392 user = make_passwd_user (user_list, entry);
394 /* Update existing users if have them */
395 for (link = priv->users; link; link = link->next)
397 CommonUser *info = link->data;
398 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
400 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)))
401 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
402 g_object_unref (user);
409 /* Only notify once we have loaded the user list */
410 if (priv->have_users)
411 new_users = g_list_insert_sorted (new_users, user, compare_user);
413 users = g_list_insert_sorted (users, user, compare_user);
415 g_strfreev (hidden_users);
416 g_strfreev (hidden_shells);
419 g_warning ("Failed to read password database: %s", strerror (errno));
423 /* Use new user list */
424 old_users = priv->users;
427 /* Notify of changes */
428 for (link = new_users; link; link = link->next)
430 CommonUser *info = link->data;
431 g_debug ("User %s added", common_user_get_name (info));
432 g_signal_connect (info, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
434 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
436 g_list_free (new_users);
437 for (link = changed_users; link; link = link->next)
439 CommonUser *info = link->data;
440 g_debug ("User %s changed", common_user_get_name (info));
441 g_signal_emit (info, user_signals[CHANGED], 0);
443 g_list_free (changed_users);
444 for (link = old_users; link; link = link->next)
448 /* See if this user is in the current list */
449 for (new_link = priv->users; new_link; new_link = new_link->next)
451 if (new_link->data == link->data)
457 CommonUser *info = link->data;
458 g_debug ("User %s removed", common_user_get_name (info));
459 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
460 g_object_unref (info);
463 g_list_free (old_users);
467 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
469 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
471 g_debug ("%s changed, reloading user list", g_file_get_path (file));
472 load_passwd_file (user_list, TRUE);
476 static gboolean load_accounts_user (CommonUser *user);
479 accounts_user_changed_cb (GDBusConnection *connection,
480 const gchar *sender_name,
481 const gchar *object_path,
482 const gchar *interface_name,
483 const gchar *signal_name,
484 GVariant *parameters,
487 CommonUser *user = data;
488 /*CommonUserPrivate *priv = GET_USER_PRIVATE (user);*/
490 /* Log message disabled as AccountsService can have arbitrary plugins that
491 * might cause us to log when properties change we don't use. LP: #1376357
493 /*g_debug ("User %s changed", priv->path);*/
494 if (load_accounts_user (user))
495 g_signal_emit (user, user_signals[CHANGED], 0);
499 load_accounts_user (CommonUser *user)
501 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
502 GVariant *result, *value;
505 gboolean system_account = FALSE;
506 GError *error = NULL;
508 /* Get the properties for this user */
509 if (!priv->changed_signal)
510 priv->changed_signal = g_dbus_connection_signal_subscribe (priv->bus,
511 "org.freedesktop.Accounts",
512 "org.freedesktop.Accounts.User",
516 G_DBUS_SIGNAL_FLAGS_NONE,
517 accounts_user_changed_cb,
520 result = g_dbus_connection_call_sync (priv->bus,
521 "org.freedesktop.Accounts",
523 "org.freedesktop.DBus.Properties",
525 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
526 G_VARIANT_TYPE ("(a{sv})"),
527 G_DBUS_CALL_FLAGS_NONE,
532 g_warning ("Error updating user %s: %s", priv->path, error->message);
533 g_clear_error (&error);
537 /* Store the properties we need */
538 g_variant_get (result, "(a{sv})", &iter);
539 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
541 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
544 priv->name = g_variant_dup_string (value, NULL);
546 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
548 g_free (priv->real_name);
549 priv->real_name = g_variant_dup_string (value, NULL);
551 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
553 g_free (priv->home_directory);
554 priv->home_directory = g_variant_dup_string (value, NULL);
556 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
558 g_free (priv->shell);
559 priv->shell = g_variant_dup_string (value, NULL);
561 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
562 system_account = g_variant_get_boolean (value);
563 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
566 g_free (priv->language);
567 priv->language = g_variant_dup_string (value, NULL);
569 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
571 g_free (priv->image);
572 priv->image = g_variant_dup_string (value, NULL);
573 if (strcmp (priv->image, "") == 0)
575 g_free (priv->image);
579 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
581 g_free (priv->session);
582 priv->session = g_variant_dup_string (value, NULL);
584 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
586 g_free (priv->background);
587 priv->background = g_variant_dup_string (value, NULL);
588 if (strcmp (priv->background, "") == 0)
590 g_free (priv->background);
591 priv->background = NULL;
594 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
596 g_strfreev (priv->layouts);
597 priv->layouts = g_variant_dup_strv (value, NULL);
600 priv->layouts = g_malloc (sizeof (gchar *) * 1);
601 priv->layouts[0] = NULL;
604 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
605 priv->has_messages = g_variant_get_boolean (value);
606 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
607 priv->uid = g_variant_get_uint64 (value);
609 g_variant_iter_free (iter);
611 g_variant_unref (result);
613 return !system_account;
617 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
619 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
621 CommonUserPrivate *priv;
623 user = g_object_new (COMMON_TYPE_USER, NULL);
624 priv = GET_USER_PRIVATE (user);
626 g_debug ("User %s added", path);
627 priv->bus = g_object_ref (list_priv->bus);
628 priv->path = g_strdup (path);
629 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
630 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
631 if (load_accounts_user (user))
633 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
635 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
638 g_object_unref (user);
642 accounts_user_added_cb (GDBusConnection *connection,
643 const gchar *sender_name,
644 const gchar *object_path,
645 const gchar *interface_name,
646 const gchar *signal_name,
647 GVariant *parameters,
650 CommonUserList *user_list = data;
654 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
656 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
660 g_variant_get (parameters, "(&o)", &path);
662 /* Add user if we haven't got them */
663 user = get_user_by_path (user_list, path);
665 add_accounts_user (user_list, path, TRUE);
669 accounts_user_deleted_cb (GDBusConnection *connection,
670 const gchar *sender_name,
671 const gchar *object_path,
672 const gchar *interface_name,
673 const gchar *signal_name,
674 GVariant *parameters,
677 CommonUserList *user_list = data;
678 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
682 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
684 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
688 g_variant_get (parameters, "(&o)", &path);
690 /* Delete user if we know of them */
691 user = get_user_by_path (user_list, path);
694 g_debug ("User %s deleted", path);
695 priv->users = g_list_remove (priv->users, user);
697 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
699 g_object_unref (user);
703 static CommonSession *
704 load_session (CommonUserList *user_list, const gchar *path)
706 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
707 CommonSession *session = NULL;
708 GVariant *result, *username;
709 GError *error = NULL;
711 result = g_dbus_connection_call_sync (priv->bus,
712 "org.freedesktop.DisplayManager",
714 "org.freedesktop.DBus.Properties",
716 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
717 G_VARIANT_TYPE ("(v)"),
718 G_DBUS_CALL_FLAGS_NONE,
723 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
724 g_clear_error (&error);
728 g_variant_get (result, "(v)", &username);
729 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
733 g_variant_get (username, "&s", &name);
735 g_debug ("Loaded session %s (%s)", path, name);
736 session = g_object_new (common_session_get_type (), NULL);
737 session->username = g_strdup (name);
738 session->path = g_strdup (path);
739 priv->sessions = g_list_append (priv->sessions, session);
741 g_variant_unref (username);
742 g_variant_unref (result);
748 session_added_cb (GDBusConnection *connection,
749 const gchar *sender_name,
750 const gchar *object_path,
751 const gchar *interface_name,
752 const gchar *signal_name,
753 GVariant *parameters,
756 CommonUserList *user_list = data;
758 CommonSession *session;
759 CommonUser *user = NULL;
761 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
763 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
767 g_variant_get (parameters, "(&o)", &path);
768 session = load_session (user_list, path);
770 user = get_user_by_name (user_list, session->username);
772 g_signal_emit (user, user_signals[CHANGED], 0);
776 session_removed_cb (GDBusConnection *connection,
777 const gchar *sender_name,
778 const gchar *object_path,
779 const gchar *interface_name,
780 const gchar *signal_name,
781 GVariant *parameters,
784 CommonUserList *user_list = data;
785 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
789 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
791 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
795 g_variant_get (parameters, "(&o)", &path);
797 for (link = priv->sessions; link; link = link->next)
799 CommonSession *session = link->data;
800 if (strcmp (session->path, path) == 0)
804 g_debug ("Session %s removed", path);
805 priv->sessions = g_list_delete_link (priv->sessions, link);
806 user = get_user_by_name (user_list, session->username);
808 g_signal_emit (user, user_signals[CHANGED], 0);
809 g_object_unref (session);
816 load_sessions (CommonUserList *user_list)
818 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
820 GError *error = NULL;
822 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
823 "org.freedesktop.DisplayManager",
824 "org.freedesktop.DisplayManager",
826 "/org/freedesktop/DisplayManager",
828 G_DBUS_SIGNAL_FLAGS_NONE,
832 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
833 "org.freedesktop.DisplayManager",
834 "org.freedesktop.DisplayManager",
836 "/org/freedesktop/DisplayManager",
838 G_DBUS_SIGNAL_FLAGS_NONE,
842 result = g_dbus_connection_call_sync (priv->bus,
843 "org.freedesktop.DisplayManager",
844 "/org/freedesktop/DisplayManager",
845 "org.freedesktop.DBus.Properties",
847 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
848 G_VARIANT_TYPE ("(v)"),
849 G_DBUS_CALL_FLAGS_NONE,
854 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
855 g_clear_error (&error);
858 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
864 g_variant_get (result, "(v)", &value);
866 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
867 g_variant_get (value, "ao", &iter);
868 while (g_variant_iter_loop (iter, "&o", &path))
869 load_session (user_list, path);
870 g_variant_iter_free (iter);
872 g_variant_unref (value);
875 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
877 g_variant_unref (result);
882 load_users (CommonUserList *user_list)
884 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
886 GError *error = NULL;
888 if (priv->have_users)
890 priv->have_users = TRUE;
892 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
893 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
894 "org.freedesktop.Accounts",
895 "org.freedesktop.Accounts",
897 "/org/freedesktop/Accounts",
899 G_DBUS_SIGNAL_FLAGS_NONE,
900 accounts_user_added_cb,
903 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
904 "org.freedesktop.Accounts",
905 "org.freedesktop.Accounts",
907 "/org/freedesktop/Accounts",
909 G_DBUS_SIGNAL_FLAGS_NONE,
910 accounts_user_deleted_cb,
913 result = g_dbus_connection_call_sync (priv->bus,
914 "org.freedesktop.Accounts",
915 "/org/freedesktop/Accounts",
916 "org.freedesktop.Accounts",
918 g_variant_new ("()"),
919 G_VARIANT_TYPE ("(ao)"),
920 G_DBUS_CALL_FLAGS_NONE,
925 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
926 g_clear_error (&error);
932 g_debug ("Loading users from org.freedesktop.Accounts");
933 g_variant_get (result, "(ao)", &iter);
934 while (g_variant_iter_loop (iter, "&o", &path))
935 add_accounts_user (user_list, path, FALSE);
936 g_variant_iter_free (iter);
937 g_variant_unref (result);
943 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
944 priv->user_added_signal = 0;
945 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
946 priv->user_removed_signal = 0;
948 load_passwd_file (user_list, FALSE);
950 /* Watch for changes to user list */
952 passwd_file = g_file_new_for_path (PASSWD_FILE);
953 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
954 g_object_unref (passwd_file);
956 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
958 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
959 g_clear_error (&error);
964 * common_user_list_get_length:
965 * @user_list: a #CommonUserList
967 * Return value: The number of users able to log in
970 common_user_list_get_length (CommonUserList *user_list)
972 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
973 load_users (user_list);
974 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
978 * common_user_list_get_users:
979 * @user_list: A #CommonUserList
981 * Get a list of users to present to the user. This list may be a subset of the
982 * available users and may be empty depending on the server configuration.
984 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
987 common_user_list_get_users (CommonUserList *user_list)
989 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
990 load_users (user_list);
991 return GET_LIST_PRIVATE (user_list)->users;
995 * common_user_list_get_user_by_name:
996 * @user_list: A #CommonUserList
997 * @username: Name of user to get.
999 * Get infomation about a given user or #NULL if this user doesn't exist.
1000 * Includes hidden and system users, unlike the list from
1001 * common_user_list_get_users.
1003 * Return value: (transfer full): A #CommonUser entry for the given user.
1006 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
1008 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
1009 g_return_val_if_fail (username != NULL, NULL);
1011 load_users (user_list);
1013 CommonUser *user = get_user_by_name (user_list, username);
1015 return g_object_ref (user);
1017 /* Sometimes we need to look up users that aren't in AccountsService.
1018 Notably we need to look up the user that the greeter runs as, which
1019 is usually 'lightdm'. For such cases, we manually create a one-off
1020 CommonUser object and pre-seed with passwd info. */
1021 struct passwd *entry = getpwnam (username);
1023 return make_passwd_user (user_list, entry);
1029 common_user_list_init (CommonUserList *user_list)
1031 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1033 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1037 common_user_list_set_property (GObject *object,
1039 const GValue *value,
1042 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1046 common_user_list_get_property (GObject *object,
1051 CommonUserList *self;
1053 self = COMMON_USER_LIST (object);
1057 case LIST_PROP_NUM_USERS:
1058 g_value_set_int (value, common_user_list_get_length (self));
1061 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1067 common_user_list_finalize (GObject *object)
1069 CommonUserList *self = COMMON_USER_LIST (object);
1070 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1072 /* Remove children first, they might access us */
1073 g_list_free_full (priv->users, g_object_unref);
1074 g_list_free_full (priv->sessions, g_object_unref);
1076 if (priv->user_added_signal)
1077 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1078 if (priv->user_removed_signal)
1079 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1080 if (priv->session_added_signal)
1081 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1082 if (priv->session_removed_signal)
1083 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1084 g_object_unref (priv->bus);
1085 g_clear_object (&priv->passwd_monitor);
1087 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1091 common_user_list_class_init (CommonUserListClass *klass)
1093 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1095 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1097 object_class->set_property = common_user_list_set_property;
1098 object_class->get_property = common_user_list_get_property;
1099 object_class->finalize = common_user_list_finalize;
1101 g_object_class_install_property (object_class,
1102 LIST_PROP_NUM_USERS,
1103 g_param_spec_int ("num-users",
1105 "Number of login users",
1109 * CommonUserList::user-added:
1110 * @user_list: A #CommonUserList
1111 * @user: The #CommonUser that has been added.
1113 * The ::user-added signal gets emitted when a user account is created.
1115 list_signals[USER_ADDED] =
1116 g_signal_new (USER_LIST_SIGNAL_USER_ADDED,
1117 G_TYPE_FROM_CLASS (klass),
1119 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1122 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1125 * CommonUserList::user-changed:
1126 * @user_list: A #CommonUserList
1127 * @user: The #CommonUser that has been changed.
1129 * The ::user-changed signal gets emitted when a user account is modified.
1131 list_signals[USER_CHANGED] =
1132 g_signal_new (USER_LIST_SIGNAL_USER_CHANGED,
1133 G_TYPE_FROM_CLASS (klass),
1135 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1138 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1141 * CommonUserList::user-removed:
1142 * @user_list: A #CommonUserList
1143 * @user: The #CommonUser that has been removed.
1145 * The ::user-removed signal gets emitted when a user account is removed.
1147 list_signals[USER_REMOVED] =
1148 g_signal_new (USER_LIST_SIGNAL_USER_REMOVED,
1149 G_TYPE_FROM_CLASS (klass),
1151 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1154 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1158 call_method (CommonUser *user, const gchar *method, GVariant *args,
1159 const gchar *expected, GVariant **result)
1162 GError *error = NULL;
1163 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1165 answer = g_dbus_connection_call_sync (priv->bus,
1166 "org.freedesktop.Accounts",
1168 "org.freedesktop.Accounts.User",
1171 G_VARIANT_TYPE (expected),
1172 G_DBUS_CALL_FLAGS_NONE,
1177 g_warning ("Could not call %s: %s", method, error->message);
1178 g_clear_error (&error);
1186 g_variant_unref (answer);
1192 save_string_to_dmrc (CommonUser *user, const gchar *group,
1193 const gchar *key, const gchar *value)
1197 dmrc = dmrc_load (user);
1198 g_key_file_set_string (dmrc, group, key, value);
1199 dmrc_save (dmrc, user);
1201 g_key_file_free (dmrc);
1204 /* Loads language/layout/session info for user */
1206 load_dmrc (CommonUser *user)
1208 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1211 /* We're using Accounts service instead */
1215 if (priv->loaded_dmrc)
1217 priv->loaded_dmrc = TRUE;
1218 dmrc = dmrc_load (user);
1220 // FIXME: Watch for changes
1222 /* The Language field contains the locale */
1223 g_free (priv->language);
1224 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1226 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1228 g_strfreev (priv->layouts);
1229 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1230 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1231 priv->layouts[1] = NULL;
1234 g_free (priv->session);
1235 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1237 g_key_file_free (dmrc);
1241 * common_user_get_name:
1242 * @user: A #CommonUser
1244 * Get the name of a user.
1246 * Return value: The name of the given user
1249 common_user_get_name (CommonUser *user)
1251 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1252 return GET_USER_PRIVATE (user)->name;
1256 * common_user_get_real_name:
1257 * @user: A #CommonUser
1259 * Get the real name of a user.
1261 * Return value: The real name of the given user
1264 common_user_get_real_name (CommonUser *user)
1266 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1267 return GET_USER_PRIVATE (user)->real_name;
1271 * common_user_get_display_name:
1272 * @user: A #CommonUser
1274 * Get the display name of a user.
1276 * Return value: The display name of the given user
1279 common_user_get_display_name (CommonUser *user)
1281 CommonUserPrivate *priv;
1283 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1285 priv = GET_USER_PRIVATE (user);
1286 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1289 return priv->real_name;
1293 * common_user_get_home_directory:
1294 * @user: A #CommonUser
1296 * Get the home directory for a user.
1298 * Return value: The users home directory
1301 common_user_get_home_directory (CommonUser *user)
1303 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1304 return GET_USER_PRIVATE (user)->home_directory;
1308 * common_user_get_shell:
1309 * @user: A #CommonUser
1311 * Get the shell for a user.
1313 * Return value: The user's shell
1316 common_user_get_shell (CommonUser *user)
1318 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1319 return GET_USER_PRIVATE (user)->shell;
1323 * common_user_get_image:
1324 * @user: A #CommonUser
1326 * Get the image URI for a user.
1328 * Return value: The image URI for the given user or #NULL if no URI
1331 common_user_get_image (CommonUser *user)
1333 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1334 return GET_USER_PRIVATE (user)->image;
1338 * common_user_get_background:
1339 * @user: A #CommonUser
1341 * Get the background file path for a user.
1343 * Return value: The background file path for the given user or #NULL if no path
1346 common_user_get_background (CommonUser *user)
1348 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1349 return GET_USER_PRIVATE (user)->background;
1353 * common_user_get_language:
1354 * @user: A #CommonUser
1356 * Get the language for a user.
1358 * 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.
1361 common_user_get_language (CommonUser *user)
1363 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1365 const gchar *language = GET_USER_PRIVATE (user)->language;
1366 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1370 * common_user_set_language:
1371 * @user: A #CommonUser
1372 * @language: The user's new language
1374 * Set the language for a user.
1377 common_user_set_language (CommonUser *user, const gchar *language)
1379 g_return_if_fail (COMMON_IS_USER (user));
1380 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1382 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1383 save_string_to_dmrc (user, "Desktop", "Language", language);
1388 * common_user_get_layout:
1389 * @user: A #CommonUser
1391 * Get the keyboard layout for a user.
1393 * 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.
1396 common_user_get_layout (CommonUser *user)
1398 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1400 return GET_USER_PRIVATE (user)->layouts[0];
1404 * common_user_get_layouts:
1405 * @user: A #CommonUser
1407 * Get the configured keyboard layouts for a user.
1409 * 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.
1411 const gchar * const *
1412 common_user_get_layouts (CommonUser *user)
1414 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1416 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1420 * common_user_get_session:
1421 * @user: A #CommonUser
1423 * Get the session for a user.
1425 * Return value: The session for the given user or #NULL if using system defaults.
1428 common_user_get_session (CommonUser *user)
1430 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1432 const gchar *session = GET_USER_PRIVATE (user)->session;
1433 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1437 * common_user_set_session:
1438 * @user: A #CommonUser
1439 * @language: The user's new session
1441 * Set the session for a user.
1444 common_user_set_session (CommonUser *user, const gchar *session)
1446 g_return_if_fail (COMMON_IS_USER (user));
1447 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1449 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1450 save_string_to_dmrc (user, "Desktop", "Session", session);
1455 * common_user_get_logged_in:
1456 * @user: A #CommonUser
1458 * Check if a user is logged in.
1460 * Return value: #TRUE if the user is currently logged in.
1463 common_user_get_logged_in (CommonUser *user)
1467 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1469 g_signal_emit (user, user_signals[GET_LOGGED_IN], 0, &result);
1475 * common_user_get_has_messages:
1476 * @user: A #CommonUser
1478 * Check if a user has waiting messages.
1480 * Return value: #TRUE if the user has waiting messages.
1483 common_user_get_has_messages (CommonUser *user)
1485 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1486 return GET_USER_PRIVATE (user)->has_messages;
1490 * common_user_get_uid:
1491 * @user: A #CommonUser
1493 * Get the uid of a user
1495 * Return value: The user's uid
1498 common_user_get_uid (CommonUser *user)
1500 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1501 return GET_USER_PRIVATE (user)->uid;
1505 * common_user_get_gid:
1506 * @user: A #CommonUser
1508 * Get the gid of a user
1510 * Return value: The user's gid
1513 common_user_get_gid (CommonUser *user)
1515 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1516 /* gid is not actually stored in AccountsService, so if our user is from
1517 AccountsService, we have to look up manually in passwd. gid won't
1518 change, so just look up the first time we're asked and never again. */
1519 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1520 if (priv->uid != 0 && priv->gid == 0)
1522 struct passwd *entry = getpwuid (priv->uid);
1524 priv->gid = entry->pw_gid;
1530 common_user_init (CommonUser *user)
1532 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1533 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1534 priv->layouts[0] = NULL;
1538 common_user_set_property (GObject *object,
1540 const GValue *value,
1543 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1547 common_user_get_property (GObject *object,
1554 self = COMMON_USER (object);
1558 case USER_PROP_NAME:
1559 g_value_set_string (value, common_user_get_name (self));
1561 case USER_PROP_REAL_NAME:
1562 g_value_set_string (value, common_user_get_real_name (self));
1564 case USER_PROP_DISPLAY_NAME:
1565 g_value_set_string (value, common_user_get_display_name (self));
1567 case USER_PROP_HOME_DIRECTORY:
1568 g_value_set_string (value, common_user_get_home_directory (self));
1570 case USER_PROP_SHELL:
1571 g_value_set_string (value, common_user_get_shell (self));
1573 case USER_PROP_IMAGE:
1574 g_value_set_string (value, common_user_get_image (self));
1576 case USER_PROP_BACKGROUND:
1577 g_value_set_string (value, common_user_get_background (self));
1579 case USER_PROP_LANGUAGE:
1580 g_value_set_string (value, common_user_get_language (self));
1582 case USER_PROP_LAYOUT:
1583 g_value_set_string (value, common_user_get_layout (self));
1585 case USER_PROP_LAYOUTS:
1586 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1588 case USER_PROP_SESSION:
1589 g_value_set_string (value, common_user_get_session (self));
1591 case USER_PROP_LOGGED_IN:
1592 g_value_set_boolean (value, common_user_get_logged_in (self));
1594 case USER_PROP_HAS_MESSAGES:
1595 g_value_set_boolean (value, common_user_get_has_messages (self));
1598 g_value_set_uint64 (value, common_user_get_uid (self));
1601 g_value_set_uint64 (value, common_user_get_gid (self));
1604 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1610 common_user_finalize (GObject *object)
1612 CommonUser *self = COMMON_USER (object);
1613 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1615 g_free (priv->path);
1616 if (priv->changed_signal)
1617 g_dbus_connection_signal_unsubscribe (priv->bus, priv->changed_signal);
1618 g_clear_object (&priv->bus);
1619 g_free (priv->name);
1620 g_free (priv->real_name);
1621 g_free (priv->home_directory);
1622 g_free (priv->shell);
1623 g_free (priv->image);
1624 g_free (priv->background);
1625 g_free (priv->language);
1626 g_strfreev (priv->layouts);
1627 g_free (priv->session);
1631 common_user_class_init (CommonUserClass *klass)
1633 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1635 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1637 object_class->set_property = common_user_set_property;
1638 object_class->get_property = common_user_get_property;
1639 object_class->finalize = common_user_finalize;
1641 g_object_class_install_property (object_class,
1643 g_param_spec_string ("name",
1647 G_PARAM_READWRITE));
1648 g_object_class_install_property (object_class,
1649 USER_PROP_REAL_NAME,
1650 g_param_spec_string ("real-name",
1654 G_PARAM_READWRITE));
1655 g_object_class_install_property (object_class,
1656 USER_PROP_DISPLAY_NAME,
1657 g_param_spec_string ("display-name",
1659 "Users display name",
1662 g_object_class_install_property (object_class,
1663 USER_PROP_HOME_DIRECTORY,
1664 g_param_spec_string ("home-directory",
1668 G_PARAM_READWRITE));
1669 g_object_class_install_property (object_class,
1671 g_param_spec_string ("shell",
1675 G_PARAM_READWRITE));
1676 g_object_class_install_property (object_class,
1678 g_param_spec_string ("image",
1682 G_PARAM_READWRITE));
1683 g_object_class_install_property (object_class,
1684 USER_PROP_BACKGROUND,
1685 g_param_spec_string ("background",
1689 G_PARAM_READWRITE));
1690 g_object_class_install_property (object_class,
1692 g_param_spec_string ("language",
1694 "Language used by this user",
1697 g_object_class_install_property (object_class,
1699 g_param_spec_string ("layout",
1701 "Keyboard layout used by this user",
1704 g_object_class_install_property (object_class,
1706 g_param_spec_boxed ("layouts",
1708 "Keyboard layouts used by this user",
1711 g_object_class_install_property (object_class,
1713 g_param_spec_string ("session",
1715 "Session used by this user",
1718 g_object_class_install_property (object_class,
1719 USER_PROP_LOGGED_IN,
1720 g_param_spec_boolean ("logged-in",
1722 "TRUE if the user is currently in a session",
1724 G_PARAM_READWRITE));
1725 g_object_class_install_property (object_class,
1726 USER_PROP_LOGGED_IN,
1727 g_param_spec_boolean ("has-messages",
1729 "TRUE if the user is has waiting messages",
1731 G_PARAM_READWRITE));
1732 g_object_class_install_property (object_class,
1734 g_param_spec_uint64 ("uid",
1740 G_PARAM_READWRITE));
1741 g_object_class_install_property (object_class,
1743 g_param_spec_uint64 ("gd",
1749 G_PARAM_READWRITE));
1752 * CommonUser::changed:
1753 * @user: A #CommonUser
1755 * The ::changed signal gets emitted this user account is modified.
1757 user_signals[CHANGED] =
1758 g_signal_new (USER_SIGNAL_CHANGED,
1759 G_TYPE_FROM_CLASS (klass),
1761 G_STRUCT_OFFSET (CommonUserClass, changed),
1766 user_signals[GET_LOGGED_IN] =
1767 g_signal_new ("get-logged-in",
1768 G_TYPE_FROM_CLASS (klass),
1771 g_signal_accumulator_first_wins,
1778 common_session_init (CommonSession *common_session)
1783 common_session_finalize (GObject *object)
1785 CommonSession *self = COMMON_SESSION (object);
1787 g_free (self->path);
1788 g_free (self->username);
1792 common_session_class_init (CommonSessionClass *klass)
1794 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1795 object_class->finalize = common_session_finalize;