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 };
67 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
71 /* Bus connection being communicated on */
74 /* D-Bus signals for accounts service events */
75 guint user_added_signal;
76 guint user_removed_signal;
78 /* D-Bus signals for display manager events */
79 guint session_added_signal;
80 guint session_removed_signal;
82 /* File monitor for password file */
83 GFileMonitor *passwd_monitor;
85 /* TRUE if have scanned users */
91 /* List of sessions */
93 } CommonUserListPrivate;
97 /* TRUE if have loaded the DMRC file */
100 /* Bus we are listening for accounts service on */
101 GDBusConnection *bus;
103 /* Accounts service path */
106 /* Update signal from accounts service */
107 guint changed_signal;
112 /* Descriptive name for user */
115 /* Home directory of user */
116 gchar *home_directory;
124 /* Background image for users */
127 /* TRUE if this user has messages available */
128 gboolean has_messages;
136 /* User chosen language */
139 /* User layout preferences */
142 /* User default session */
148 GObject parent_instance;
155 GObjectClass parent_class;
156 } CommonSessionClass;
158 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
159 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
160 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
161 GType common_session_get_type (void);
162 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
164 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
165 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
167 #define PASSWD_FILE "/etc/passwd"
168 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
170 static CommonUserList *singleton = NULL;
173 * common_user_list_get_instance:
177 * Return value: (transfer none): the #CommonUserList
180 common_user_list_get_instance (void)
183 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
188 common_user_list_cleanup (void)
190 g_clear_object (&singleton);
194 get_user_by_name (CommonUserList *user_list, const gchar *username)
196 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
199 for (link = priv->users; link; link = link->next)
201 CommonUser *user = link->data;
202 if (g_strcmp0 (common_user_get_name (user), username) == 0)
210 get_user_by_path (CommonUserList *user_list, const gchar *path)
212 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
215 for (link = priv->users; link; link = link->next)
217 CommonUser *user = link->data;
218 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
226 compare_user (gconstpointer a, gconstpointer b)
228 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
229 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
233 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
235 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
237 /* Skip if already set to this */
238 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
239 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
240 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
241 g_strcmp0 (common_user_get_image (user), image) == 0)
244 g_free (priv->real_name);
245 priv->real_name = g_strdup (real_name);
246 g_free (priv->home_directory);
247 priv->home_directory = g_strdup (home_directory);
248 g_free (priv->shell);
249 priv->shell = g_strdup (shell);
250 g_free (priv->image);
251 priv->image = g_strdup (image);
256 static void load_sessions (CommonUserList *user_list);
259 get_logged_in_cb (CommonUser *user, CommonUserList *user_list)
261 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
262 const gchar *username;
265 // Lazily decide to load/listen to sessions
266 if (priv->session_added_signal == 0)
267 load_sessions (user_list);
269 username = GET_USER_PRIVATE (user)->name;
270 for (link = priv->sessions; link; link = link->next)
272 CommonSession *session = link->data;
273 if (strcmp (session->username, username) == 0)
281 user_changed_cb (CommonUser *user, CommonUserList *user_list)
283 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
287 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
289 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
290 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
292 gchar *real_name, *image;
294 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
296 tokens = g_strsplit (entry->pw_gecos, ",", -1);
297 if (tokens[0] != NULL && tokens[0][0] != '\0')
298 real_name = g_strdup (tokens[0]);
300 real_name = g_strdup ("");
303 image = g_build_filename (entry->pw_dir, ".face", NULL);
304 if (!g_file_test (image, G_FILE_TEST_EXISTS))
307 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
308 if (!g_file_test (image, G_FILE_TEST_EXISTS))
315 priv->name = g_strdup (entry->pw_name);
316 priv->real_name = real_name;
317 priv->home_directory = g_strdup (entry->pw_dir);
318 priv->shell = g_strdup (entry->pw_shell);
320 priv->uid = entry->pw_uid;
321 priv->gid = entry->pw_gid;
327 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
329 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
333 gchar **hidden_users, **hidden_shells;
334 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
335 GError *error = NULL;
337 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
339 config = g_key_file_new ();
340 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
341 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
342 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
343 g_clear_error (&error);
345 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
346 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
350 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
352 value = g_strdup ("nobody nobody4 noaccess");
353 hidden_users = g_strsplit (value, " ", -1);
356 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
358 value = g_strdup ("/bin/false /usr/sbin/nologin");
359 hidden_shells = g_strsplit (value, " ", -1);
362 g_key_file_free (config);
368 struct passwd *entry;
377 /* Ignore system users */
378 if (entry->pw_uid < minimum_uid)
381 /* Ignore users disabled by shell */
384 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
385 if (hidden_shells[i])
389 /* Ignore certain users */
390 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
394 user = make_passwd_user (user_list, entry);
396 /* Update existing users if have them */
397 for (link = priv->users; link; link = link->next)
399 CommonUser *info = link->data;
400 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
402 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)))
403 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
404 g_object_unref (user);
411 /* Only notify once we have loaded the user list */
412 if (priv->have_users)
413 new_users = g_list_insert_sorted (new_users, user, compare_user);
415 users = g_list_insert_sorted (users, user, compare_user);
417 g_strfreev (hidden_users);
418 g_strfreev (hidden_shells);
421 g_warning ("Failed to read password database: %s", strerror (errno));
425 /* Use new user list */
426 old_users = priv->users;
429 /* Notify of changes */
430 for (link = new_users; link; link = link->next)
432 CommonUser *info = link->data;
433 g_debug ("User %s added", common_user_get_name (info));
434 g_signal_connect (info, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
436 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
438 g_list_free (new_users);
439 for (link = changed_users; link; link = link->next)
441 CommonUser *info = link->data;
442 g_debug ("User %s changed", common_user_get_name (info));
443 g_signal_emit (info, user_signals[CHANGED], 0);
445 g_list_free (changed_users);
446 for (link = old_users; link; link = link->next)
450 /* See if this user is in the current list */
451 for (new_link = priv->users; new_link; new_link = new_link->next)
453 if (new_link->data == link->data)
459 CommonUser *info = link->data;
460 g_debug ("User %s removed", common_user_get_name (info));
461 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
462 g_object_unref (info);
465 g_list_free (old_users);
469 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
471 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
473 g_debug ("%s changed, reloading user list", g_file_get_path (file));
474 load_passwd_file (user_list, TRUE);
478 static gboolean load_accounts_user (CommonUser *user);
481 accounts_user_changed_cb (GDBusConnection *connection,
482 const gchar *sender_name,
483 const gchar *object_path,
484 const gchar *interface_name,
485 const gchar *signal_name,
486 GVariant *parameters,
489 CommonUser *user = data;
490 /*CommonUserPrivate *priv = GET_USER_PRIVATE (user);*/
492 /* Log message disabled as AccountsService can have arbitrary plugins that
493 * might cause us to log when properties change we don't use. LP: #1376357
495 /*g_debug ("User %s changed", priv->path);*/
496 if (load_accounts_user (user))
497 g_signal_emit (user, user_signals[CHANGED], 0);
501 load_accounts_user (CommonUser *user)
503 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
504 GVariant *result, *value;
507 gboolean system_account = FALSE;
508 GError *error = NULL;
510 /* Get the properties for this user */
511 if (!priv->changed_signal)
512 priv->changed_signal = g_dbus_connection_signal_subscribe (priv->bus,
513 "org.freedesktop.Accounts",
514 "org.freedesktop.Accounts.User",
518 G_DBUS_SIGNAL_FLAGS_NONE,
519 accounts_user_changed_cb,
522 result = g_dbus_connection_call_sync (priv->bus,
523 "org.freedesktop.Accounts",
525 "org.freedesktop.DBus.Properties",
527 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
528 G_VARIANT_TYPE ("(a{sv})"),
529 G_DBUS_CALL_FLAGS_NONE,
534 g_warning ("Error updating user %s: %s", priv->path, error->message);
535 g_clear_error (&error);
539 /* Store the properties we need */
540 g_variant_get (result, "(a{sv})", &iter);
541 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
543 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
546 priv->name = g_variant_dup_string (value, NULL);
548 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
550 g_free (priv->real_name);
551 priv->real_name = g_variant_dup_string (value, NULL);
553 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
555 g_free (priv->home_directory);
556 priv->home_directory = g_variant_dup_string (value, NULL);
558 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
560 g_free (priv->shell);
561 priv->shell = g_variant_dup_string (value, NULL);
563 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
564 system_account = g_variant_get_boolean (value);
565 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
568 g_free (priv->language);
569 priv->language = g_variant_dup_string (value, NULL);
571 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
573 g_free (priv->image);
574 priv->image = g_variant_dup_string (value, NULL);
575 if (strcmp (priv->image, "") == 0)
577 g_free (priv->image);
581 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
583 g_free (priv->session);
584 priv->session = g_variant_dup_string (value, NULL);
586 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
588 g_free (priv->background);
589 priv->background = g_variant_dup_string (value, NULL);
590 if (strcmp (priv->background, "") == 0)
592 g_free (priv->background);
593 priv->background = NULL;
596 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
598 g_strfreev (priv->layouts);
599 priv->layouts = g_variant_dup_strv (value, NULL);
602 priv->layouts = g_malloc (sizeof (gchar *) * 1);
603 priv->layouts[0] = NULL;
606 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
607 priv->has_messages = g_variant_get_boolean (value);
608 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
609 priv->uid = g_variant_get_uint64 (value);
611 g_variant_iter_free (iter);
613 g_variant_unref (result);
615 return !system_account;
619 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
621 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
623 CommonUserPrivate *priv;
625 user = g_object_new (COMMON_TYPE_USER, NULL);
626 priv = GET_USER_PRIVATE (user);
628 g_debug ("User %s added", path);
629 priv->bus = g_object_ref (list_priv->bus);
630 priv->path = g_strdup (path);
631 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), user_list);
632 g_signal_connect (user, "get-logged-in", G_CALLBACK (get_logged_in_cb), user_list);
633 if (load_accounts_user (user))
635 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
637 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
640 g_object_unref (user);
644 accounts_user_added_cb (GDBusConnection *connection,
645 const gchar *sender_name,
646 const gchar *object_path,
647 const gchar *interface_name,
648 const gchar *signal_name,
649 GVariant *parameters,
652 CommonUserList *user_list = data;
656 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
658 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
662 g_variant_get (parameters, "(&o)", &path);
664 /* Add user if we haven't got them */
665 user = get_user_by_path (user_list, path);
667 add_accounts_user (user_list, path, TRUE);
671 accounts_user_deleted_cb (GDBusConnection *connection,
672 const gchar *sender_name,
673 const gchar *object_path,
674 const gchar *interface_name,
675 const gchar *signal_name,
676 GVariant *parameters,
679 CommonUserList *user_list = data;
680 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
684 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
686 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
690 g_variant_get (parameters, "(&o)", &path);
692 /* Delete user if we know of them */
693 user = get_user_by_path (user_list, path);
696 g_debug ("User %s deleted", path);
697 priv->users = g_list_remove (priv->users, user);
699 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
701 g_object_unref (user);
705 static CommonSession *
706 load_session (CommonUserList *user_list, const gchar *path)
708 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
709 CommonSession *session = NULL;
710 GVariant *result, *username;
711 GError *error = NULL;
713 result = g_dbus_connection_call_sync (priv->bus,
714 "org.freedesktop.DisplayManager",
716 "org.freedesktop.DBus.Properties",
718 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
719 G_VARIANT_TYPE ("(v)"),
720 G_DBUS_CALL_FLAGS_NONE,
725 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
726 g_clear_error (&error);
730 g_variant_get (result, "(v)", &username);
731 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
735 g_variant_get (username, "&s", &name);
737 g_debug ("Loaded session %s (%s)", path, name);
738 session = g_object_new (common_session_get_type (), NULL);
739 session->username = g_strdup (name);
740 session->path = g_strdup (path);
741 priv->sessions = g_list_append (priv->sessions, session);
743 g_variant_unref (username);
744 g_variant_unref (result);
750 session_added_cb (GDBusConnection *connection,
751 const gchar *sender_name,
752 const gchar *object_path,
753 const gchar *interface_name,
754 const gchar *signal_name,
755 GVariant *parameters,
758 CommonUserList *user_list = data;
760 CommonSession *session;
761 CommonUser *user = NULL;
763 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
765 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
769 g_variant_get (parameters, "(&o)", &path);
770 session = load_session (user_list, path);
772 user = get_user_by_name (user_list, session->username);
774 g_signal_emit (user, user_signals[CHANGED], 0);
778 session_removed_cb (GDBusConnection *connection,
779 const gchar *sender_name,
780 const gchar *object_path,
781 const gchar *interface_name,
782 const gchar *signal_name,
783 GVariant *parameters,
786 CommonUserList *user_list = data;
787 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
791 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
793 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
797 g_variant_get (parameters, "(&o)", &path);
799 for (link = priv->sessions; link; link = link->next)
801 CommonSession *session = link->data;
802 if (strcmp (session->path, path) == 0)
806 g_debug ("Session %s removed", path);
807 priv->sessions = g_list_delete_link (priv->sessions, link);
808 user = get_user_by_name (user_list, session->username);
810 g_signal_emit (user, user_signals[CHANGED], 0);
811 g_object_unref (session);
818 load_sessions (CommonUserList *user_list)
820 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
822 GError *error = NULL;
824 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
825 "org.freedesktop.DisplayManager",
826 "org.freedesktop.DisplayManager",
828 "/org/freedesktop/DisplayManager",
830 G_DBUS_SIGNAL_FLAGS_NONE,
834 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
835 "org.freedesktop.DisplayManager",
836 "org.freedesktop.DisplayManager",
838 "/org/freedesktop/DisplayManager",
840 G_DBUS_SIGNAL_FLAGS_NONE,
844 result = g_dbus_connection_call_sync (priv->bus,
845 "org.freedesktop.DisplayManager",
846 "/org/freedesktop/DisplayManager",
847 "org.freedesktop.DBus.Properties",
849 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
850 G_VARIANT_TYPE ("(v)"),
851 G_DBUS_CALL_FLAGS_NONE,
856 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
857 g_clear_error (&error);
860 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
866 g_variant_get (result, "(v)", &value);
868 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
869 g_variant_get (value, "ao", &iter);
870 while (g_variant_iter_loop (iter, "&o", &path))
871 load_session (user_list, path);
872 g_variant_iter_free (iter);
874 g_variant_unref (value);
877 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
879 g_variant_unref (result);
884 load_users (CommonUserList *user_list)
886 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
888 GError *error = NULL;
890 if (priv->have_users)
892 priv->have_users = TRUE;
894 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
895 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
896 "org.freedesktop.Accounts",
897 "org.freedesktop.Accounts",
899 "/org/freedesktop/Accounts",
901 G_DBUS_SIGNAL_FLAGS_NONE,
902 accounts_user_added_cb,
905 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
906 "org.freedesktop.Accounts",
907 "org.freedesktop.Accounts",
909 "/org/freedesktop/Accounts",
911 G_DBUS_SIGNAL_FLAGS_NONE,
912 accounts_user_deleted_cb,
915 result = g_dbus_connection_call_sync (priv->bus,
916 "org.freedesktop.Accounts",
917 "/org/freedesktop/Accounts",
918 "org.freedesktop.Accounts",
920 g_variant_new ("()"),
921 G_VARIANT_TYPE ("(ao)"),
922 G_DBUS_CALL_FLAGS_NONE,
927 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
928 g_clear_error (&error);
934 g_debug ("Loading users from org.freedesktop.Accounts");
935 g_variant_get (result, "(ao)", &iter);
936 while (g_variant_iter_loop (iter, "&o", &path))
937 add_accounts_user (user_list, path, FALSE);
938 g_variant_iter_free (iter);
939 g_variant_unref (result);
945 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
946 priv->user_added_signal = 0;
947 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
948 priv->user_removed_signal = 0;
950 load_passwd_file (user_list, FALSE);
952 /* Watch for changes to user list */
954 passwd_file = g_file_new_for_path (PASSWD_FILE);
955 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
956 g_object_unref (passwd_file);
958 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
960 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
961 g_clear_error (&error);
966 * common_user_list_get_length:
967 * @user_list: a #CommonUserList
969 * Return value: The number of users able to log in
972 common_user_list_get_length (CommonUserList *user_list)
974 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
975 load_users (user_list);
976 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
980 * common_user_list_get_users:
981 * @user_list: A #CommonUserList
983 * Get a list of users to present to the user. This list may be a subset of the
984 * available users and may be empty depending on the server configuration.
986 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
989 common_user_list_get_users (CommonUserList *user_list)
991 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
992 load_users (user_list);
993 return GET_LIST_PRIVATE (user_list)->users;
997 * common_user_list_get_user_by_name:
998 * @user_list: A #CommonUserList
999 * @username: Name of user to get.
1001 * Get infomation about a given user or #NULL if this user doesn't exist.
1002 * Includes hidden and system users, unlike the list from
1003 * common_user_list_get_users.
1005 * Return value: (transfer full): A #CommonUser entry for the given user.
1008 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
1010 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
1011 g_return_val_if_fail (username != NULL, NULL);
1013 load_users (user_list);
1015 CommonUser *user = get_user_by_name (user_list, username);
1017 return g_object_ref (user);
1019 /* Sometimes we need to look up users that aren't in AccountsService.
1020 Notably we need to look up the user that the greeter runs as, which
1021 is usually 'lightdm'. For such cases, we manually create a one-off
1022 CommonUser object and pre-seed with passwd info. */
1023 struct passwd *entry = getpwnam (username);
1025 return make_passwd_user (user_list, entry);
1031 common_user_list_init (CommonUserList *user_list)
1033 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1035 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1039 common_user_list_set_property (GObject *object,
1041 const GValue *value,
1044 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1048 common_user_list_get_property (GObject *object,
1053 CommonUserList *self;
1055 self = COMMON_USER_LIST (object);
1059 case LIST_PROP_NUM_USERS:
1060 g_value_set_int (value, common_user_list_get_length (self));
1063 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1069 common_user_list_finalize (GObject *object)
1071 CommonUserList *self = COMMON_USER_LIST (object);
1072 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1074 /* Remove children first, they might access us */
1075 g_list_free_full (priv->users, g_object_unref);
1076 g_list_free_full (priv->sessions, g_object_unref);
1078 if (priv->user_added_signal)
1079 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1080 if (priv->user_removed_signal)
1081 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1082 if (priv->session_added_signal)
1083 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1084 if (priv->session_removed_signal)
1085 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1086 g_object_unref (priv->bus);
1087 g_clear_object (&priv->passwd_monitor);
1089 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1093 common_user_list_class_init (CommonUserListClass *klass)
1095 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1097 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1099 object_class->set_property = common_user_list_set_property;
1100 object_class->get_property = common_user_list_get_property;
1101 object_class->finalize = common_user_list_finalize;
1103 g_object_class_install_property (object_class,
1104 LIST_PROP_NUM_USERS,
1105 g_param_spec_int ("num-users",
1107 "Number of login users",
1111 * CommonUserList::user-added:
1112 * @user_list: A #CommonUserList
1113 * @user: The #CommonUser that has been added.
1115 * The ::user-added signal gets emitted when a user account is created.
1117 list_signals[USER_ADDED] =
1118 g_signal_new (USER_LIST_SIGNAL_USER_ADDED,
1119 G_TYPE_FROM_CLASS (klass),
1121 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1124 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1127 * CommonUserList::user-changed:
1128 * @user_list: A #CommonUserList
1129 * @user: The #CommonUser that has been changed.
1131 * The ::user-changed signal gets emitted when a user account is modified.
1133 list_signals[USER_CHANGED] =
1134 g_signal_new (USER_LIST_SIGNAL_USER_CHANGED,
1135 G_TYPE_FROM_CLASS (klass),
1137 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1140 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1143 * CommonUserList::user-removed:
1144 * @user_list: A #CommonUserList
1145 * @user: The #CommonUser that has been removed.
1147 * The ::user-removed signal gets emitted when a user account is removed.
1149 list_signals[USER_REMOVED] =
1150 g_signal_new (USER_LIST_SIGNAL_USER_REMOVED,
1151 G_TYPE_FROM_CLASS (klass),
1153 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1156 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1160 call_method (CommonUser *user, const gchar *method, GVariant *args,
1161 const gchar *expected, GVariant **result)
1164 GError *error = NULL;
1165 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1167 answer = g_dbus_connection_call_sync (priv->bus,
1168 "org.freedesktop.Accounts",
1170 "org.freedesktop.Accounts.User",
1173 G_VARIANT_TYPE (expected),
1174 G_DBUS_CALL_FLAGS_NONE,
1179 g_warning ("Could not call %s: %s", method, error->message);
1180 g_clear_error (&error);
1188 g_variant_unref (answer);
1194 save_string_to_dmrc (CommonUser *user, const gchar *group,
1195 const gchar *key, const gchar *value)
1199 dmrc = dmrc_load (user);
1200 g_key_file_set_string (dmrc, group, key, value);
1201 dmrc_save (dmrc, user);
1203 g_key_file_free (dmrc);
1206 /* Loads language/layout/session info for user */
1208 load_dmrc (CommonUser *user)
1210 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1213 /* We're using Accounts service instead */
1217 if (priv->loaded_dmrc)
1219 priv->loaded_dmrc = TRUE;
1220 dmrc = dmrc_load (user);
1222 // FIXME: Watch for changes
1224 /* The Language field contains the locale */
1225 g_free (priv->language);
1226 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1228 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1230 g_strfreev (priv->layouts);
1231 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1232 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1233 priv->layouts[1] = NULL;
1236 g_free (priv->session);
1237 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1239 g_key_file_free (dmrc);
1243 * common_user_get_name:
1244 * @user: A #CommonUser
1246 * Get the name of a user.
1248 * Return value: The name of the given user
1251 common_user_get_name (CommonUser *user)
1253 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1254 return GET_USER_PRIVATE (user)->name;
1258 * common_user_get_real_name:
1259 * @user: A #CommonUser
1261 * Get the real name of a user.
1263 * Return value: The real name of the given user
1266 common_user_get_real_name (CommonUser *user)
1268 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1269 return GET_USER_PRIVATE (user)->real_name;
1273 * common_user_get_display_name:
1274 * @user: A #CommonUser
1276 * Get the display name of a user.
1278 * Return value: The display name of the given user
1281 common_user_get_display_name (CommonUser *user)
1283 CommonUserPrivate *priv;
1285 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1287 priv = GET_USER_PRIVATE (user);
1288 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1291 return priv->real_name;
1295 * common_user_get_home_directory:
1296 * @user: A #CommonUser
1298 * Get the home directory for a user.
1300 * Return value: The users home directory
1303 common_user_get_home_directory (CommonUser *user)
1305 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1306 return GET_USER_PRIVATE (user)->home_directory;
1310 * common_user_get_shell:
1311 * @user: A #CommonUser
1313 * Get the shell for a user.
1315 * Return value: The user's shell
1318 common_user_get_shell (CommonUser *user)
1320 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1321 return GET_USER_PRIVATE (user)->shell;
1325 * common_user_get_image:
1326 * @user: A #CommonUser
1328 * Get the image URI for a user.
1330 * Return value: The image URI for the given user or #NULL if no URI
1333 common_user_get_image (CommonUser *user)
1335 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1336 return GET_USER_PRIVATE (user)->image;
1340 * common_user_get_background:
1341 * @user: A #CommonUser
1343 * Get the background file path for a user.
1345 * Return value: The background file path for the given user or #NULL if no path
1348 common_user_get_background (CommonUser *user)
1350 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1351 return GET_USER_PRIVATE (user)->background;
1355 * common_user_get_language:
1356 * @user: A #CommonUser
1358 * Get the language for a user.
1360 * 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.
1363 common_user_get_language (CommonUser *user)
1365 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1367 const gchar *language = GET_USER_PRIVATE (user)->language;
1368 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1372 * common_user_set_language:
1373 * @user: A #CommonUser
1374 * @language: The user's new language
1376 * Set the language for a user.
1379 common_user_set_language (CommonUser *user, const gchar *language)
1381 g_return_if_fail (COMMON_IS_USER (user));
1382 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1384 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1385 save_string_to_dmrc (user, "Desktop", "Language", language);
1390 * common_user_get_layout:
1391 * @user: A #CommonUser
1393 * Get the keyboard layout for a user.
1395 * 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.
1398 common_user_get_layout (CommonUser *user)
1400 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1402 return GET_USER_PRIVATE (user)->layouts[0];
1406 * common_user_get_layouts:
1407 * @user: A #CommonUser
1409 * Get the configured keyboard layouts for a user.
1411 * 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.
1413 const gchar * const *
1414 common_user_get_layouts (CommonUser *user)
1416 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1418 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1422 * common_user_get_session:
1423 * @user: A #CommonUser
1425 * Get the session for a user.
1427 * Return value: The session for the given user or #NULL if using system defaults.
1430 common_user_get_session (CommonUser *user)
1432 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1434 const gchar *session = GET_USER_PRIVATE (user)->session;
1435 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1439 * common_user_set_session:
1440 * @user: A #CommonUser
1441 * @language: The user's new session
1443 * Set the session for a user.
1446 common_user_set_session (CommonUser *user, const gchar *session)
1448 g_return_if_fail (COMMON_IS_USER (user));
1449 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1451 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1452 save_string_to_dmrc (user, "Desktop", "Session", session);
1457 * common_user_get_logged_in:
1458 * @user: A #CommonUser
1460 * Check if a user is logged in.
1462 * Return value: #TRUE if the user is currently logged in.
1465 common_user_get_logged_in (CommonUser *user)
1469 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1471 g_signal_emit (user, user_signals[GET_LOGGED_IN], 0, &result);
1477 * common_user_get_has_messages:
1478 * @user: A #CommonUser
1480 * Check if a user has waiting messages.
1482 * Return value: #TRUE if the user has waiting messages.
1485 common_user_get_has_messages (CommonUser *user)
1487 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1488 return GET_USER_PRIVATE (user)->has_messages;
1492 * common_user_get_uid:
1493 * @user: A #CommonUser
1495 * Get the uid of a user
1497 * Return value: The user's uid
1500 common_user_get_uid (CommonUser *user)
1502 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1503 return GET_USER_PRIVATE (user)->uid;
1507 * common_user_get_gid:
1508 * @user: A #CommonUser
1510 * Get the gid of a user
1512 * Return value: The user's gid
1515 common_user_get_gid (CommonUser *user)
1517 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1518 /* gid is not actually stored in AccountsService, so if our user is from
1519 AccountsService, we have to look up manually in passwd. gid won't
1520 change, so just look up the first time we're asked and never again. */
1521 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1522 if (priv->uid != 0 && priv->gid == 0)
1524 struct passwd *entry = getpwuid (priv->uid);
1526 priv->gid = entry->pw_gid;
1532 common_user_init (CommonUser *user)
1534 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1535 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1536 priv->layouts[0] = NULL;
1540 common_user_set_property (GObject *object,
1542 const GValue *value,
1545 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1549 common_user_get_property (GObject *object,
1556 self = COMMON_USER (object);
1560 case USER_PROP_NAME:
1561 g_value_set_string (value, common_user_get_name (self));
1563 case USER_PROP_REAL_NAME:
1564 g_value_set_string (value, common_user_get_real_name (self));
1566 case USER_PROP_DISPLAY_NAME:
1567 g_value_set_string (value, common_user_get_display_name (self));
1569 case USER_PROP_HOME_DIRECTORY:
1570 g_value_set_string (value, common_user_get_home_directory (self));
1572 case USER_PROP_SHELL:
1573 g_value_set_string (value, common_user_get_shell (self));
1575 case USER_PROP_IMAGE:
1576 g_value_set_string (value, common_user_get_image (self));
1578 case USER_PROP_BACKGROUND:
1579 g_value_set_string (value, common_user_get_background (self));
1581 case USER_PROP_LANGUAGE:
1582 g_value_set_string (value, common_user_get_language (self));
1584 case USER_PROP_LAYOUT:
1585 g_value_set_string (value, common_user_get_layout (self));
1587 case USER_PROP_LAYOUTS:
1588 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1590 case USER_PROP_SESSION:
1591 g_value_set_string (value, common_user_get_session (self));
1593 case USER_PROP_LOGGED_IN:
1594 g_value_set_boolean (value, common_user_get_logged_in (self));
1596 case USER_PROP_HAS_MESSAGES:
1597 g_value_set_boolean (value, common_user_get_has_messages (self));
1600 g_value_set_uint64 (value, common_user_get_uid (self));
1603 g_value_set_uint64 (value, common_user_get_gid (self));
1606 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1612 common_user_finalize (GObject *object)
1614 CommonUser *self = COMMON_USER (object);
1615 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1617 g_free (priv->path);
1618 if (priv->changed_signal)
1619 g_dbus_connection_signal_unsubscribe (priv->bus, priv->changed_signal);
1620 g_clear_object (&priv->bus);
1621 g_free (priv->name);
1622 g_free (priv->real_name);
1623 g_free (priv->home_directory);
1624 g_free (priv->shell);
1625 g_free (priv->image);
1626 g_free (priv->background);
1627 g_free (priv->language);
1628 g_strfreev (priv->layouts);
1629 g_free (priv->session);
1633 common_user_class_init (CommonUserClass *klass)
1635 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1637 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1639 object_class->set_property = common_user_set_property;
1640 object_class->get_property = common_user_get_property;
1641 object_class->finalize = common_user_finalize;
1643 g_object_class_install_property (object_class,
1645 g_param_spec_string ("name",
1649 G_PARAM_READWRITE));
1650 g_object_class_install_property (object_class,
1651 USER_PROP_REAL_NAME,
1652 g_param_spec_string ("real-name",
1656 G_PARAM_READWRITE));
1657 g_object_class_install_property (object_class,
1658 USER_PROP_DISPLAY_NAME,
1659 g_param_spec_string ("display-name",
1661 "Users display name",
1664 g_object_class_install_property (object_class,
1665 USER_PROP_HOME_DIRECTORY,
1666 g_param_spec_string ("home-directory",
1670 G_PARAM_READWRITE));
1671 g_object_class_install_property (object_class,
1673 g_param_spec_string ("shell",
1677 G_PARAM_READWRITE));
1678 g_object_class_install_property (object_class,
1680 g_param_spec_string ("image",
1684 G_PARAM_READWRITE));
1685 g_object_class_install_property (object_class,
1686 USER_PROP_BACKGROUND,
1687 g_param_spec_string ("background",
1691 G_PARAM_READWRITE));
1692 g_object_class_install_property (object_class,
1694 g_param_spec_string ("language",
1696 "Language used by this user",
1699 g_object_class_install_property (object_class,
1701 g_param_spec_string ("layout",
1703 "Keyboard layout used by this user",
1706 g_object_class_install_property (object_class,
1708 g_param_spec_boxed ("layouts",
1710 "Keyboard layouts used by this user",
1713 g_object_class_install_property (object_class,
1715 g_param_spec_string ("session",
1717 "Session used by this user",
1720 g_object_class_install_property (object_class,
1721 USER_PROP_LOGGED_IN,
1722 g_param_spec_boolean ("logged-in",
1724 "TRUE if the user is currently in a session",
1726 G_PARAM_READWRITE));
1727 g_object_class_install_property (object_class,
1728 USER_PROP_LOGGED_IN,
1729 g_param_spec_boolean ("has-messages",
1731 "TRUE if the user is has waiting messages",
1733 G_PARAM_READWRITE));
1734 g_object_class_install_property (object_class,
1736 g_param_spec_uint64 ("uid",
1742 G_PARAM_READWRITE));
1743 g_object_class_install_property (object_class,
1745 g_param_spec_uint64 ("gd",
1751 G_PARAM_READWRITE));
1754 * CommonUser::changed:
1755 * @user: A #CommonUser
1757 * The ::changed signal gets emitted this user account is modified.
1759 user_signals[CHANGED] =
1760 g_signal_new (USER_SIGNAL_CHANGED,
1761 G_TYPE_FROM_CLASS (klass),
1763 G_STRUCT_OFFSET (CommonUserClass, changed),
1768 user_signals[GET_LOGGED_IN] =
1769 g_signal_new ("get-logged-in",
1770 G_TYPE_FROM_CLASS (klass),
1773 g_signal_accumulator_first_wins,
1780 common_session_init (CommonSession *common_session)
1785 common_session_finalize (GObject *object)
1787 CommonSession *self = COMMON_SESSION (object);
1789 g_free (self->path);
1790 g_free (self->username);
1794 common_session_class_init (CommonSessionClass *klass)
1796 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1797 object_class->finalize = common_session_finalize;