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 /* Watch for changes to user list */
950 passwd_file = g_file_new_for_path (PASSWD_FILE);
951 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
952 g_object_unref (passwd_file);
954 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
956 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
957 g_clear_error (&error);
962 * common_user_list_get_length:
963 * @user_list: a #CommonUserList
965 * Return value: The number of users able to log in
968 common_user_list_get_length (CommonUserList *user_list)
970 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
971 load_users (user_list);
972 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
976 * common_user_list_get_users:
977 * @user_list: A #CommonUserList
979 * Get a list of users to present to the user. This list may be a subset of the
980 * available users and may be empty depending on the server configuration.
982 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
985 common_user_list_get_users (CommonUserList *user_list)
987 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
988 load_users (user_list);
989 return GET_LIST_PRIVATE (user_list)->users;
993 * common_user_list_get_user_by_name:
994 * @user_list: A #CommonUserList
995 * @username: Name of user to get.
997 * Get infomation about a given user or #NULL if this user doesn't exist.
998 * Includes hidden and system users, unlike the list from
999 * common_user_list_get_users.
1001 * Return value: (transfer full): A #CommonUser entry for the given user.
1004 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
1006 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
1007 g_return_val_if_fail (username != NULL, NULL);
1009 /* Load users from AccountsService, do nothing when it does not run */
1010 load_users (user_list);
1012 CommonUser *user = get_user_by_name (user_list, username);
1014 return g_object_ref (user);
1016 /* Sometimes we need to look up users that aren't in AccountsService.
1017 Notably we need to look up the user that the greeter runs as, which
1018 is usually 'lightdm'. For such cases, we manually create a one-off
1019 CommonUser object and pre-seed with passwd info. */
1020 struct passwd *entry = getpwnam (username);
1022 return make_passwd_user (user_list, entry);
1028 common_user_list_init (CommonUserList *user_list)
1030 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1032 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1036 common_user_list_set_property (GObject *object,
1038 const GValue *value,
1041 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1045 common_user_list_get_property (GObject *object,
1050 CommonUserList *self;
1052 self = COMMON_USER_LIST (object);
1056 case LIST_PROP_NUM_USERS:
1057 g_value_set_int (value, common_user_list_get_length (self));
1060 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1066 common_user_list_finalize (GObject *object)
1068 CommonUserList *self = COMMON_USER_LIST (object);
1069 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1071 /* Remove children first, they might access us */
1072 g_list_free_full (priv->users, g_object_unref);
1073 g_list_free_full (priv->sessions, g_object_unref);
1075 if (priv->user_added_signal)
1076 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1077 if (priv->user_removed_signal)
1078 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1079 if (priv->session_added_signal)
1080 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1081 if (priv->session_removed_signal)
1082 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1083 g_object_unref (priv->bus);
1084 g_clear_object (&priv->passwd_monitor);
1086 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1090 common_user_list_class_init (CommonUserListClass *klass)
1092 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1094 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1096 object_class->set_property = common_user_list_set_property;
1097 object_class->get_property = common_user_list_get_property;
1098 object_class->finalize = common_user_list_finalize;
1100 g_object_class_install_property (object_class,
1101 LIST_PROP_NUM_USERS,
1102 g_param_spec_int ("num-users",
1104 "Number of login users",
1108 * CommonUserList::user-added:
1109 * @user_list: A #CommonUserList
1110 * @user: The #CommonUser that has been added.
1112 * The ::user-added signal gets emitted when a user account is created.
1114 list_signals[USER_ADDED] =
1115 g_signal_new (USER_LIST_SIGNAL_USER_ADDED,
1116 G_TYPE_FROM_CLASS (klass),
1118 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1121 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1124 * CommonUserList::user-changed:
1125 * @user_list: A #CommonUserList
1126 * @user: The #CommonUser that has been changed.
1128 * The ::user-changed signal gets emitted when a user account is modified.
1130 list_signals[USER_CHANGED] =
1131 g_signal_new (USER_LIST_SIGNAL_USER_CHANGED,
1132 G_TYPE_FROM_CLASS (klass),
1134 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1137 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1140 * CommonUserList::user-removed:
1141 * @user_list: A #CommonUserList
1142 * @user: The #CommonUser that has been removed.
1144 * The ::user-removed signal gets emitted when a user account is removed.
1146 list_signals[USER_REMOVED] =
1147 g_signal_new (USER_LIST_SIGNAL_USER_REMOVED,
1148 G_TYPE_FROM_CLASS (klass),
1150 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1153 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1157 call_method (CommonUser *user, const gchar *method, GVariant *args,
1158 const gchar *expected, GVariant **result)
1161 GError *error = NULL;
1162 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1164 answer = g_dbus_connection_call_sync (priv->bus,
1165 "org.freedesktop.Accounts",
1167 "org.freedesktop.Accounts.User",
1170 G_VARIANT_TYPE (expected),
1171 G_DBUS_CALL_FLAGS_NONE,
1176 g_warning ("Could not call %s: %s", method, error->message);
1177 g_clear_error (&error);
1185 g_variant_unref (answer);
1191 save_string_to_dmrc (CommonUser *user, const gchar *group,
1192 const gchar *key, const gchar *value)
1196 dmrc = dmrc_load (user);
1197 g_key_file_set_string (dmrc, group, key, value);
1198 dmrc_save (dmrc, user);
1200 g_key_file_free (dmrc);
1203 /* Loads language/layout/session info for user */
1205 load_dmrc (CommonUser *user)
1207 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1210 /* We're using Accounts service instead */
1214 if (priv->loaded_dmrc)
1216 priv->loaded_dmrc = TRUE;
1217 dmrc = dmrc_load (user);
1219 // FIXME: Watch for changes
1221 /* The Language field contains the locale */
1222 g_free (priv->language);
1223 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1225 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1227 g_strfreev (priv->layouts);
1228 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1229 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1230 priv->layouts[1] = NULL;
1233 g_free (priv->session);
1234 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1236 g_key_file_free (dmrc);
1240 * common_user_get_name:
1241 * @user: A #CommonUser
1243 * Get the name of a user.
1245 * Return value: The name of the given user
1248 common_user_get_name (CommonUser *user)
1250 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1251 return GET_USER_PRIVATE (user)->name;
1255 * common_user_get_real_name:
1256 * @user: A #CommonUser
1258 * Get the real name of a user.
1260 * Return value: The real name of the given user
1263 common_user_get_real_name (CommonUser *user)
1265 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1266 return GET_USER_PRIVATE (user)->real_name;
1270 * common_user_get_display_name:
1271 * @user: A #CommonUser
1273 * Get the display name of a user.
1275 * Return value: The display name of the given user
1278 common_user_get_display_name (CommonUser *user)
1280 CommonUserPrivate *priv;
1282 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1284 priv = GET_USER_PRIVATE (user);
1285 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1288 return priv->real_name;
1292 * common_user_get_home_directory:
1293 * @user: A #CommonUser
1295 * Get the home directory for a user.
1297 * Return value: The users home directory
1300 common_user_get_home_directory (CommonUser *user)
1302 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1303 return GET_USER_PRIVATE (user)->home_directory;
1307 * common_user_get_shell:
1308 * @user: A #CommonUser
1310 * Get the shell for a user.
1312 * Return value: The user's shell
1315 common_user_get_shell (CommonUser *user)
1317 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1318 return GET_USER_PRIVATE (user)->shell;
1322 * common_user_get_image:
1323 * @user: A #CommonUser
1325 * Get the image URI for a user.
1327 * Return value: The image URI for the given user or #NULL if no URI
1330 common_user_get_image (CommonUser *user)
1332 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1333 return GET_USER_PRIVATE (user)->image;
1337 * common_user_get_background:
1338 * @user: A #CommonUser
1340 * Get the background file path for a user.
1342 * Return value: The background file path for the given user or #NULL if no path
1345 common_user_get_background (CommonUser *user)
1347 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1348 return GET_USER_PRIVATE (user)->background;
1352 * common_user_get_language:
1353 * @user: A #CommonUser
1355 * Get the language for a user.
1357 * 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.
1360 common_user_get_language (CommonUser *user)
1362 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1364 const gchar *language = GET_USER_PRIVATE (user)->language;
1365 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1369 * common_user_set_language:
1370 * @user: A #CommonUser
1371 * @language: The user's new language
1373 * Set the language for a user.
1376 common_user_set_language (CommonUser *user, const gchar *language)
1378 g_return_if_fail (COMMON_IS_USER (user));
1379 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1381 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1382 save_string_to_dmrc (user, "Desktop", "Language", language);
1387 * common_user_get_layout:
1388 * @user: A #CommonUser
1390 * Get the keyboard layout for a user.
1392 * 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.
1395 common_user_get_layout (CommonUser *user)
1397 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1399 return GET_USER_PRIVATE (user)->layouts[0];
1403 * common_user_get_layouts:
1404 * @user: A #CommonUser
1406 * Get the configured keyboard layouts for a user.
1408 * 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.
1410 const gchar * const *
1411 common_user_get_layouts (CommonUser *user)
1413 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1415 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1419 * common_user_get_session:
1420 * @user: A #CommonUser
1422 * Get the session for a user.
1424 * Return value: The session for the given user or #NULL if using system defaults.
1427 common_user_get_session (CommonUser *user)
1429 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1431 const gchar *session = GET_USER_PRIVATE (user)->session;
1432 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1436 * common_user_set_session:
1437 * @user: A #CommonUser
1438 * @language: The user's new session
1440 * Set the session for a user.
1443 common_user_set_session (CommonUser *user, const gchar *session)
1445 g_return_if_fail (COMMON_IS_USER (user));
1446 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1448 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1449 save_string_to_dmrc (user, "Desktop", "Session", session);
1454 * common_user_get_logged_in:
1455 * @user: A #CommonUser
1457 * Check if a user is logged in.
1459 * Return value: #TRUE if the user is currently logged in.
1462 common_user_get_logged_in (CommonUser *user)
1466 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1468 g_signal_emit (user, user_signals[GET_LOGGED_IN], 0, &result);
1474 * common_user_get_has_messages:
1475 * @user: A #CommonUser
1477 * Check if a user has waiting messages.
1479 * Return value: #TRUE if the user has waiting messages.
1482 common_user_get_has_messages (CommonUser *user)
1484 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1485 return GET_USER_PRIVATE (user)->has_messages;
1489 * common_user_get_uid:
1490 * @user: A #CommonUser
1492 * Get the uid of a user
1494 * Return value: The user's uid
1497 common_user_get_uid (CommonUser *user)
1499 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1500 return GET_USER_PRIVATE (user)->uid;
1504 * common_user_get_gid:
1505 * @user: A #CommonUser
1507 * Get the gid of a user
1509 * Return value: The user's gid
1512 common_user_get_gid (CommonUser *user)
1514 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1515 /* gid is not actually stored in AccountsService, so if our user is from
1516 AccountsService, we have to look up manually in passwd. gid won't
1517 change, so just look up the first time we're asked and never again. */
1518 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1519 if (priv->uid != 0 && priv->gid == 0)
1521 struct passwd *entry = getpwuid (priv->uid);
1523 priv->gid = entry->pw_gid;
1529 common_user_init (CommonUser *user)
1531 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1532 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1533 priv->layouts[0] = NULL;
1537 common_user_set_property (GObject *object,
1539 const GValue *value,
1542 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1546 common_user_get_property (GObject *object,
1553 self = COMMON_USER (object);
1557 case USER_PROP_NAME:
1558 g_value_set_string (value, common_user_get_name (self));
1560 case USER_PROP_REAL_NAME:
1561 g_value_set_string (value, common_user_get_real_name (self));
1563 case USER_PROP_DISPLAY_NAME:
1564 g_value_set_string (value, common_user_get_display_name (self));
1566 case USER_PROP_HOME_DIRECTORY:
1567 g_value_set_string (value, common_user_get_home_directory (self));
1569 case USER_PROP_SHELL:
1570 g_value_set_string (value, common_user_get_shell (self));
1572 case USER_PROP_IMAGE:
1573 g_value_set_string (value, common_user_get_image (self));
1575 case USER_PROP_BACKGROUND:
1576 g_value_set_string (value, common_user_get_background (self));
1578 case USER_PROP_LANGUAGE:
1579 g_value_set_string (value, common_user_get_language (self));
1581 case USER_PROP_LAYOUT:
1582 g_value_set_string (value, common_user_get_layout (self));
1584 case USER_PROP_LAYOUTS:
1585 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1587 case USER_PROP_SESSION:
1588 g_value_set_string (value, common_user_get_session (self));
1590 case USER_PROP_LOGGED_IN:
1591 g_value_set_boolean (value, common_user_get_logged_in (self));
1593 case USER_PROP_HAS_MESSAGES:
1594 g_value_set_boolean (value, common_user_get_has_messages (self));
1597 g_value_set_uint64 (value, common_user_get_uid (self));
1600 g_value_set_uint64 (value, common_user_get_gid (self));
1603 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1609 common_user_finalize (GObject *object)
1611 CommonUser *self = COMMON_USER (object);
1612 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1614 g_free (priv->path);
1615 if (priv->changed_signal)
1616 g_dbus_connection_signal_unsubscribe (priv->bus, priv->changed_signal);
1617 g_clear_object (&priv->bus);
1618 g_free (priv->name);
1619 g_free (priv->real_name);
1620 g_free (priv->home_directory);
1621 g_free (priv->shell);
1622 g_free (priv->image);
1623 g_free (priv->background);
1624 g_free (priv->language);
1625 g_strfreev (priv->layouts);
1626 g_free (priv->session);
1630 common_user_class_init (CommonUserClass *klass)
1632 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1634 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1636 object_class->set_property = common_user_set_property;
1637 object_class->get_property = common_user_get_property;
1638 object_class->finalize = common_user_finalize;
1640 g_object_class_install_property (object_class,
1642 g_param_spec_string ("name",
1646 G_PARAM_READWRITE));
1647 g_object_class_install_property (object_class,
1648 USER_PROP_REAL_NAME,
1649 g_param_spec_string ("real-name",
1653 G_PARAM_READWRITE));
1654 g_object_class_install_property (object_class,
1655 USER_PROP_DISPLAY_NAME,
1656 g_param_spec_string ("display-name",
1658 "Users display name",
1661 g_object_class_install_property (object_class,
1662 USER_PROP_HOME_DIRECTORY,
1663 g_param_spec_string ("home-directory",
1667 G_PARAM_READWRITE));
1668 g_object_class_install_property (object_class,
1670 g_param_spec_string ("shell",
1674 G_PARAM_READWRITE));
1675 g_object_class_install_property (object_class,
1677 g_param_spec_string ("image",
1681 G_PARAM_READWRITE));
1682 g_object_class_install_property (object_class,
1683 USER_PROP_BACKGROUND,
1684 g_param_spec_string ("background",
1688 G_PARAM_READWRITE));
1689 g_object_class_install_property (object_class,
1691 g_param_spec_string ("language",
1693 "Language used by this user",
1696 g_object_class_install_property (object_class,
1698 g_param_spec_string ("layout",
1700 "Keyboard layout used by this user",
1703 g_object_class_install_property (object_class,
1705 g_param_spec_boxed ("layouts",
1707 "Keyboard layouts used by this user",
1710 g_object_class_install_property (object_class,
1712 g_param_spec_string ("session",
1714 "Session used by this user",
1717 g_object_class_install_property (object_class,
1718 USER_PROP_LOGGED_IN,
1719 g_param_spec_boolean ("logged-in",
1721 "TRUE if the user is currently in a session",
1723 G_PARAM_READWRITE));
1724 g_object_class_install_property (object_class,
1725 USER_PROP_LOGGED_IN,
1726 g_param_spec_boolean ("has-messages",
1728 "TRUE if the user is has waiting messages",
1730 G_PARAM_READWRITE));
1731 g_object_class_install_property (object_class,
1733 g_param_spec_uint64 ("uid",
1739 G_PARAM_READWRITE));
1740 g_object_class_install_property (object_class,
1742 g_param_spec_uint64 ("gd",
1748 G_PARAM_READWRITE));
1751 * CommonUser::changed:
1752 * @user: A #CommonUser
1754 * The ::changed signal gets emitted this user account is modified.
1756 user_signals[CHANGED] =
1757 g_signal_new (USER_SIGNAL_CHANGED,
1758 G_TYPE_FROM_CLASS (klass),
1760 G_STRUCT_OFFSET (CommonUserClass, changed),
1765 user_signals[GET_LOGGED_IN] =
1766 g_signal_new ("get-logged-in",
1767 G_TYPE_FROM_CLASS (klass),
1770 g_signal_accumulator_first_wins,
1777 common_session_init (CommonSession *common_session)
1782 common_session_finalize (GObject *object)
1784 CommonSession *self = COMMON_SESSION (object);
1786 g_free (self->path);
1787 g_free (self->username);
1791 common_session_class_init (CommonSessionClass *klass)
1793 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1794 object_class->finalize = common_session_finalize;