1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3 * Copyright (C) 2010 Robert Ancell.
4 * Copyright (C) 2014 Canonical, Ltd.
5 * Authors: Robert Ancell <robert.ancell@canonical.com>
6 * Michael Terry <michael.terry@canonical.com>
8 * This library is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 2 or version 3 of the License.
11 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
18 #include <sys/utsname.h>
23 #include "user-list.h"
37 USER_PROP_DISPLAY_NAME,
38 USER_PROP_HOME_DIRECTORY,
47 USER_PROP_HAS_MESSAGES,
59 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
66 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
70 /* Bus connection being communicated on */
73 /* D-Bus signals for accounts service events */
74 guint user_added_signal;
75 guint user_removed_signal;
77 /* D-Bus signals for display manager events */
78 guint session_added_signal;
79 guint session_removed_signal;
81 /* File monitor for password file */
82 GFileMonitor *passwd_monitor;
84 /* TRUE if have scanned users */
90 /* List of sessions */
92 } CommonUserListPrivate;
96 /* User list this user is part of */
97 CommonUserList *user_list;
99 /* TRUE if have loaded the DMRC file */
100 gboolean loaded_dmrc;
102 /* Accounts service path */
105 /* Update signal from accounts service */
106 guint changed_signal;
111 /* Descriptive name for user */
114 /* Home directory of user */
115 gchar *home_directory;
120 /* TRUE if a system account */
121 gboolean system_account;
126 /* Background image for users */
129 /* TRUE if this user has messages available */
130 gboolean has_messages;
138 /* User chosen language */
141 /* User layout preferences */
144 /* User default session */
150 GObject parent_instance;
157 GObjectClass parent_class;
158 } CommonSessionClass;
160 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
161 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
162 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
163 GType common_session_get_type (void);
164 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
166 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
167 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
169 #define PASSWD_FILE "/etc/passwd"
170 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
172 static CommonUserList *singleton = NULL;
175 * common_user_list_get_instance:
179 * Return value: (transfer none): the #CommonUserList
182 common_user_list_get_instance (void)
185 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
190 common_user_list_cleanup (void)
193 g_object_unref (singleton);
198 get_user_by_name (CommonUserList *user_list, const gchar *username)
200 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
203 for (link = priv->users; link; link = link->next)
205 CommonUser *user = link->data;
206 if (g_strcmp0 (common_user_get_name (user), username) == 0)
214 get_user_by_path (CommonUserList *user_list, const gchar *path)
216 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
219 for (link = priv->users; link; link = link->next)
221 CommonUser *user = link->data;
222 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
230 compare_user (gconstpointer a, gconstpointer b)
232 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
233 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
237 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
239 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
241 /* Skip if already set to this */
242 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
243 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
244 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
245 g_strcmp0 (common_user_get_image (user), image) == 0)
248 g_free (priv->real_name);
249 priv->real_name = g_strdup (real_name);
250 g_free (priv->home_directory);
251 priv->home_directory = g_strdup (home_directory);
252 g_free (priv->shell);
253 priv->shell = g_strdup (shell);
254 g_free (priv->image);
255 priv->image = g_strdup (image);
261 user_changed_cb (CommonUser *user)
263 g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
267 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
269 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
270 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
272 gchar *real_name, *image;
274 tokens = g_strsplit (entry->pw_gecos, ",", -1);
275 if (tokens[0] != NULL && tokens[0][0] != '\0')
276 real_name = g_strdup (tokens[0]);
278 real_name = g_strdup ("");
281 image = g_build_filename (entry->pw_dir, ".face", NULL);
282 if (!g_file_test (image, G_FILE_TEST_EXISTS))
285 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
286 if (!g_file_test (image, G_FILE_TEST_EXISTS))
293 priv->user_list = user_list;
294 priv->name = g_strdup (entry->pw_name);
295 priv->real_name = real_name;
296 priv->home_directory = g_strdup (entry->pw_dir);
297 priv->shell = g_strdup (entry->pw_shell);
299 priv->uid = entry->pw_uid;
300 priv->gid = entry->pw_gid;
306 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
308 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
312 gchar **hidden_users, **hidden_shells;
313 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
314 GError *error = NULL;
316 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
318 config = g_key_file_new ();
319 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
320 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
321 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
322 g_clear_error (&error);
324 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
325 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
329 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
331 value = g_strdup ("nobody nobody4 noaccess");
332 hidden_users = g_strsplit (value, " ", -1);
335 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
337 value = g_strdup ("/bin/false /usr/sbin/nologin");
338 hidden_shells = g_strsplit (value, " ", -1);
341 g_key_file_free (config);
347 struct passwd *entry;
356 /* Ignore system users */
357 if (entry->pw_uid < minimum_uid)
360 /* Ignore users disabled by shell */
363 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
364 if (hidden_shells[i])
368 /* Ignore certain users */
369 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
373 user = make_passwd_user (user_list, entry);
375 /* Update existing users if have them */
376 for (link = priv->users; link; link = link->next)
378 CommonUser *info = link->data;
379 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
381 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)))
382 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
383 g_object_unref (user);
390 /* Only notify once we have loaded the user list */
391 if (priv->have_users)
392 new_users = g_list_insert_sorted (new_users, user, compare_user);
394 users = g_list_insert_sorted (users, user, compare_user);
396 g_strfreev (hidden_users);
397 g_strfreev (hidden_shells);
400 g_warning ("Failed to read password database: %s", strerror (errno));
404 /* Use new user list */
405 old_users = priv->users;
408 /* Notify of changes */
409 for (link = new_users; link; link = link->next)
411 CommonUser *info = link->data;
412 g_debug ("User %s added", common_user_get_name (info));
413 g_signal_connect (info, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), NULL);
415 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
417 g_list_free (new_users);
418 for (link = changed_users; link; link = link->next)
420 CommonUser *info = link->data;
421 g_debug ("User %s changed", common_user_get_name (info));
422 g_signal_emit (info, user_signals[CHANGED], 0);
424 g_list_free (changed_users);
425 for (link = old_users; link; link = link->next)
429 /* See if this user is in the current list */
430 for (new_link = priv->users; new_link; new_link = new_link->next)
432 if (new_link->data == link->data)
438 CommonUser *info = link->data;
439 g_debug ("User %s removed", common_user_get_name (info));
440 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
441 g_object_unref (info);
444 g_list_free (old_users);
448 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
450 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
452 g_debug ("%s changed, reloading user list", g_file_get_path (file));
453 load_passwd_file (user_list, TRUE);
458 update_user_property (CommonUser *user, const gchar *name, GVariant *value)
460 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
462 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
465 priv->name = g_variant_dup_string (value, NULL);
469 if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
471 g_free (priv->real_name);
472 priv->real_name = g_variant_dup_string (value, NULL);
476 if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
478 g_free (priv->home_directory);
479 priv->home_directory = g_variant_dup_string (value, NULL);
483 if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
485 g_free (priv->shell);
486 priv->shell = g_variant_dup_string (value, NULL);
490 if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
492 priv->system_account = g_variant_get_boolean (value);
496 if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
499 g_free (priv->language);
500 priv->language = g_variant_dup_string (value, NULL);
504 if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
506 g_free (priv->image);
507 priv->image = g_variant_dup_string (value, NULL);
508 if (strcmp (priv->image, "") == 0)
510 g_free (priv->image);
516 if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
518 g_free (priv->session);
519 priv->session = g_variant_dup_string (value, NULL);
523 if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
525 g_free (priv->background);
526 priv->background = g_variant_dup_string (value, NULL);
527 if (strcmp (priv->background, "") == 0)
529 g_free (priv->background);
530 priv->background = NULL;
535 if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
537 g_strfreev (priv->layouts);
538 priv->layouts = g_variant_dup_strv (value, NULL);
541 priv->layouts = g_malloc (sizeof (gchar *) * 1);
542 priv->layouts[0] = NULL;
547 if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
549 priv->has_messages = g_variant_get_boolean (value);
553 if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
555 priv->uid = g_variant_get_uint64 (value);
563 accounts_user_changed_cb (GDBusConnection *connection,
564 const gchar *sender_name,
565 const gchar *object_path,
566 const gchar *interface_name,
567 const gchar *signal_name,
568 GVariant *parameters,
571 CommonUser *user = data;
572 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
573 gboolean changed = FALSE;
575 GVariantIter *invalidated_properties;
579 g_variant_get (parameters, "(sa{sv}as)", NULL, &iter, &invalidated_properties);
580 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
582 if (update_user_property (user, name, value))
585 g_variant_iter_free (iter);
586 while (g_variant_iter_loop (invalidated_properties, "&s", &name))
589 GError *error = NULL;
591 result = g_dbus_connection_call_sync (connection,
592 "org.freedesktop.Accounts",
594 "org.freedesktop.DBus.Properties",
596 g_variant_new ("(ss)", "org.freedesktop.Accounts.User", name),
597 G_VARIANT_TYPE ("(v)"),
598 G_DBUS_CALL_FLAGS_NONE,
603 g_warning ("Error updating user property %s: %s", name, error->message);
604 g_clear_error (&error);
610 g_variant_get (result, "(v)", &value);
611 if (update_user_property (user, name, value))
613 g_variant_unref (value);
614 g_variant_unref (result);
620 g_debug ("User %s changed", priv->path);
621 g_signal_emit (user, user_signals[CHANGED], 0);
626 load_accounts_user (CommonUser *user)
628 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
629 GVariant *result, *value;
632 GError *error = NULL;
634 /* Get the properties for this user */
635 if (!priv->changed_signal)
636 priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
637 "org.freedesktop.Accounts",
638 "org.freedesktop.DBus.Properties",
641 "org.freedesktop.Accounts.User",
642 G_DBUS_SIGNAL_FLAGS_NONE,
643 accounts_user_changed_cb,
646 result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
647 "org.freedesktop.Accounts",
649 "org.freedesktop.DBus.Properties",
651 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
652 G_VARIANT_TYPE ("(a{sv})"),
653 G_DBUS_CALL_FLAGS_NONE,
658 g_warning ("Error updating user %s: %s", priv->path, error->message);
659 g_clear_error (&error);
663 /* Store the properties we need */
664 g_variant_get (result, "(a{sv})", &iter);
665 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
666 update_user_property (user, name, value);
667 g_variant_iter_free (iter);
669 g_variant_unref (result);
671 return !priv->system_account;
675 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
677 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
679 CommonUserPrivate *priv;
681 user = g_object_new (COMMON_TYPE_USER, NULL);
682 priv = GET_USER_PRIVATE (user);
684 g_debug ("User %s added", path);
685 priv->user_list = user_list;
686 priv->path = g_strdup (path);
687 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), NULL);
688 if (load_accounts_user (user))
690 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
692 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
695 g_object_unref (user);
699 accounts_user_added_cb (GDBusConnection *connection,
700 const gchar *sender_name,
701 const gchar *object_path,
702 const gchar *interface_name,
703 const gchar *signal_name,
704 GVariant *parameters,
707 CommonUserList *user_list = data;
711 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
713 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
717 g_variant_get (parameters, "(&o)", &path);
719 /* Add user if we haven't got them */
720 user = get_user_by_path (user_list, path);
722 add_accounts_user (user_list, path, TRUE);
726 accounts_user_deleted_cb (GDBusConnection *connection,
727 const gchar *sender_name,
728 const gchar *object_path,
729 const gchar *interface_name,
730 const gchar *signal_name,
731 GVariant *parameters,
734 CommonUserList *user_list = data;
735 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
739 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
741 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
745 g_variant_get (parameters, "(&o)", &path);
747 /* Delete user if we know of them */
748 user = get_user_by_path (user_list, path);
751 g_debug ("User %s deleted", path);
752 priv->users = g_list_remove (priv->users, user);
754 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
756 g_object_unref (user);
760 static CommonSession *
761 load_session (CommonUserList *user_list, const gchar *path)
763 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
764 CommonSession *session = NULL;
765 GVariant *result, *username;
766 GError *error = NULL;
768 result = g_dbus_connection_call_sync (priv->bus,
769 "org.freedesktop.DisplayManager",
771 "org.freedesktop.DBus.Properties",
773 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
774 G_VARIANT_TYPE ("(v)"),
775 G_DBUS_CALL_FLAGS_NONE,
780 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
781 g_clear_error (&error);
785 g_variant_get (result, "(v)", &username);
786 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
790 g_variant_get (username, "&s", &name);
792 g_debug ("Loaded session %s (%s)", path, name);
793 session = g_object_new (common_session_get_type (), NULL);
794 session->username = g_strdup (name);
795 session->path = g_strdup (path);
796 priv->sessions = g_list_append (priv->sessions, session);
798 g_variant_unref (username);
799 g_variant_unref (result);
805 session_added_cb (GDBusConnection *connection,
806 const gchar *sender_name,
807 const gchar *object_path,
808 const gchar *interface_name,
809 const gchar *signal_name,
810 GVariant *parameters,
813 CommonUserList *user_list = data;
815 CommonSession *session;
816 CommonUser *user = NULL;
818 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
820 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
824 g_variant_get (parameters, "(&o)", &path);
825 session = load_session (user_list, path);
827 user = get_user_by_name (user_list, session->username);
829 g_signal_emit (user, user_signals[CHANGED], 0);
833 session_removed_cb (GDBusConnection *connection,
834 const gchar *sender_name,
835 const gchar *object_path,
836 const gchar *interface_name,
837 const gchar *signal_name,
838 GVariant *parameters,
841 CommonUserList *user_list = data;
842 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
846 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
848 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
852 g_variant_get (parameters, "(&o)", &path);
854 for (link = priv->sessions; link; link = link->next)
856 CommonSession *session = link->data;
857 if (strcmp (session->path, path) == 0)
861 g_debug ("Session %s removed", path);
862 priv->sessions = g_list_delete_link (priv->sessions, link);
863 user = get_user_by_name (user_list, session->username);
865 g_signal_emit (user, user_signals[CHANGED], 0);
866 g_object_unref (session);
873 load_sessions (CommonUserList *user_list)
875 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
877 GError *error = NULL;
879 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
880 "org.freedesktop.DisplayManager",
881 "org.freedesktop.DisplayManager",
883 "/org/freedesktop/DisplayManager",
885 G_DBUS_SIGNAL_FLAGS_NONE,
889 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
890 "org.freedesktop.DisplayManager",
891 "org.freedesktop.DisplayManager",
893 "/org/freedesktop/DisplayManager",
895 G_DBUS_SIGNAL_FLAGS_NONE,
899 result = g_dbus_connection_call_sync (priv->bus,
900 "org.freedesktop.DisplayManager",
901 "/org/freedesktop/DisplayManager",
902 "org.freedesktop.DBus.Properties",
904 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
905 G_VARIANT_TYPE ("(v)"),
906 G_DBUS_CALL_FLAGS_NONE,
911 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
912 g_clear_error (&error);
915 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
921 g_variant_get (result, "(v)", &value);
923 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
924 g_variant_get (value, "ao", &iter);
925 while (g_variant_iter_loop (iter, "&o", &path))
926 load_session (user_list, path);
927 g_variant_iter_free (iter);
929 g_variant_unref (value);
932 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
934 g_variant_unref (result);
939 load_users (CommonUserList *user_list)
941 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
943 GError *error = NULL;
945 if (priv->have_users)
947 priv->have_users = TRUE;
949 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
950 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
951 "org.freedesktop.Accounts",
952 "org.freedesktop.Accounts",
954 "/org/freedesktop/Accounts",
956 G_DBUS_SIGNAL_FLAGS_NONE,
957 accounts_user_added_cb,
960 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
961 "org.freedesktop.Accounts",
962 "org.freedesktop.Accounts",
964 "/org/freedesktop/Accounts",
966 G_DBUS_SIGNAL_FLAGS_NONE,
967 accounts_user_deleted_cb,
970 result = g_dbus_connection_call_sync (priv->bus,
971 "org.freedesktop.Accounts",
972 "/org/freedesktop/Accounts",
973 "org.freedesktop.Accounts",
975 g_variant_new ("()"),
976 G_VARIANT_TYPE ("(ao)"),
977 G_DBUS_CALL_FLAGS_NONE,
982 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
983 g_clear_error (&error);
989 g_debug ("Loading users from org.freedesktop.Accounts");
990 g_variant_get (result, "(ao)", &iter);
991 while (g_variant_iter_loop (iter, "&o", &path))
992 add_accounts_user (user_list, path, FALSE);
993 g_variant_iter_free (iter);
994 g_variant_unref (result);
1000 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1001 priv->user_added_signal = 0;
1002 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1003 priv->user_removed_signal = 0;
1005 load_passwd_file (user_list, FALSE);
1007 /* Watch for changes to user list */
1009 passwd_file = g_file_new_for_path (PASSWD_FILE);
1010 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
1011 g_object_unref (passwd_file);
1013 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
1015 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
1016 g_clear_error (&error);
1021 * common_user_list_get_length:
1022 * @user_list: a #CommonUserList
1024 * Return value: The number of users able to log in
1027 common_user_list_get_length (CommonUserList *user_list)
1029 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
1030 load_users (user_list);
1031 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
1035 * common_user_list_get_users:
1036 * @user_list: A #CommonUserList
1038 * Get a list of users to present to the user. This list may be a subset of the
1039 * available users and may be empty depending on the server configuration.
1041 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
1044 common_user_list_get_users (CommonUserList *user_list)
1046 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
1047 load_users (user_list);
1048 return GET_LIST_PRIVATE (user_list)->users;
1052 * common_user_list_get_user_by_name:
1053 * @user_list: A #CommonUserList
1054 * @username: Name of user to get.
1056 * Get infomation about a given user or #NULL if this user doesn't exist.
1057 * Includes hidden and system users, unlike the list from
1058 * common_user_list_get_users.
1060 * Return value: (transfer full): A #CommonUser entry for the given user.
1063 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
1065 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
1066 g_return_val_if_fail (username != NULL, NULL);
1068 load_users (user_list);
1070 CommonUser *user = get_user_by_name (user_list, username);
1072 return g_object_ref (user);
1074 /* Sometimes we need to look up users that aren't in AccountsService.
1075 Notably we need to look up the user that the greeter runs as, which
1076 is usually 'lightdm'. For such cases, we manually create a one-off
1077 CommonUser object and pre-seed with passwd info. */
1078 struct passwd *entry = getpwnam (username);
1080 return make_passwd_user (user_list, entry);
1086 common_user_list_init (CommonUserList *user_list)
1088 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1090 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1094 common_user_list_set_property (GObject *object,
1096 const GValue *value,
1099 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1103 common_user_list_get_property (GObject *object,
1108 CommonUserList *self;
1110 self = COMMON_USER_LIST (object);
1114 case LIST_PROP_NUM_USERS:
1115 g_value_set_int (value, common_user_list_get_length (self));
1118 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1124 common_user_list_finalize (GObject *object)
1126 CommonUserList *self = COMMON_USER_LIST (object);
1127 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1129 /* Remove children first, they might access us */
1130 g_list_free_full (priv->users, g_object_unref);
1131 g_list_free_full (priv->sessions, g_object_unref);
1133 if (priv->user_added_signal)
1134 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1135 if (priv->user_removed_signal)
1136 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1137 if (priv->session_added_signal)
1138 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1139 if (priv->session_removed_signal)
1140 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1141 g_object_unref (priv->bus);
1142 if (priv->passwd_monitor)
1143 g_object_unref (priv->passwd_monitor);
1145 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1149 common_user_list_class_init (CommonUserListClass *klass)
1151 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1153 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1155 object_class->set_property = common_user_list_set_property;
1156 object_class->get_property = common_user_list_get_property;
1157 object_class->finalize = common_user_list_finalize;
1159 g_object_class_install_property (object_class,
1160 LIST_PROP_NUM_USERS,
1161 g_param_spec_int ("num-users",
1163 "Number of login users",
1167 * CommonUserList::user-added:
1168 * @user_list: A #CommonUserList
1169 * @user: The #CommonUser that has been added.
1171 * The ::user-added signal gets emitted when a user account is created.
1173 list_signals[USER_ADDED] =
1174 g_signal_new (USER_LIST_SIGNAL_USER_ADDED,
1175 G_TYPE_FROM_CLASS (klass),
1177 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1180 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1183 * CommonUserList::user-changed:
1184 * @user_list: A #CommonUserList
1185 * @user: The #CommonUser that has been changed.
1187 * The ::user-changed signal gets emitted when a user account is modified.
1189 list_signals[USER_CHANGED] =
1190 g_signal_new (USER_LIST_SIGNAL_USER_CHANGED,
1191 G_TYPE_FROM_CLASS (klass),
1193 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1196 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1199 * CommonUserList::user-removed:
1200 * @user_list: A #CommonUserList
1201 * @user: The #CommonUser that has been removed.
1203 * The ::user-removed signal gets emitted when a user account is removed.
1205 list_signals[USER_REMOVED] =
1206 g_signal_new (USER_LIST_SIGNAL_USER_REMOVED,
1207 G_TYPE_FROM_CLASS (klass),
1209 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1212 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1216 call_method (CommonUser *user, const gchar *method, GVariant *args,
1217 const gchar *expected, GVariant **result)
1220 GError *error = NULL;
1221 CommonUserPrivate *user_priv = GET_USER_PRIVATE (user);
1222 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_priv->user_list);
1224 answer = g_dbus_connection_call_sync (list_priv->bus,
1225 "org.freedesktop.Accounts",
1227 "org.freedesktop.Accounts.User",
1230 G_VARIANT_TYPE (expected),
1231 G_DBUS_CALL_FLAGS_NONE,
1236 g_warning ("Could not call %s: %s", method, error->message);
1237 g_clear_error (&error);
1245 g_variant_unref (answer);
1251 save_string_to_dmrc (CommonUser *user, const gchar *group,
1252 const gchar *key, const gchar *value)
1256 dmrc = dmrc_load (user);
1257 g_key_file_set_string (dmrc, group, key, value);
1258 dmrc_save (dmrc, user);
1260 g_key_file_free (dmrc);
1263 /* Loads language/layout/session info for user */
1265 load_dmrc (CommonUser *user)
1267 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1270 /* We're using Accounts service instead */
1274 if (priv->loaded_dmrc)
1276 priv->loaded_dmrc = TRUE;
1277 dmrc = dmrc_load (user);
1279 // FIXME: Watch for changes
1281 /* The Language field contains the locale */
1282 g_free (priv->language);
1283 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1285 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1287 g_strfreev (priv->layouts);
1288 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1289 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1290 priv->layouts[1] = NULL;
1293 g_free (priv->session);
1294 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1296 g_key_file_free (dmrc);
1300 * common_user_get_name:
1301 * @user: A #CommonUser
1303 * Get the name of a user.
1305 * Return value: The name of the given user
1308 common_user_get_name (CommonUser *user)
1310 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1311 return GET_USER_PRIVATE (user)->name;
1315 * common_user_get_real_name:
1316 * @user: A #CommonUser
1318 * Get the real name of a user.
1320 * Return value: The real name of the given user
1323 common_user_get_real_name (CommonUser *user)
1325 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1326 return GET_USER_PRIVATE (user)->real_name;
1330 * common_user_get_display_name:
1331 * @user: A #CommonUser
1333 * Get the display name of a user.
1335 * Return value: The display name of the given user
1338 common_user_get_display_name (CommonUser *user)
1340 CommonUserPrivate *priv;
1342 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1344 priv = GET_USER_PRIVATE (user);
1345 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1348 return priv->real_name;
1352 * common_user_get_home_directory:
1353 * @user: A #CommonUser
1355 * Get the home directory for a user.
1357 * Return value: The users home directory
1360 common_user_get_home_directory (CommonUser *user)
1362 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1363 return GET_USER_PRIVATE (user)->home_directory;
1367 * common_user_get_shell:
1368 * @user: A #CommonUser
1370 * Get the shell for a user.
1372 * Return value: The user's shell
1375 common_user_get_shell (CommonUser *user)
1377 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1378 return GET_USER_PRIVATE (user)->shell;
1382 * common_user_get_image:
1383 * @user: A #CommonUser
1385 * Get the image URI for a user.
1387 * Return value: The image URI for the given user or #NULL if no URI
1390 common_user_get_image (CommonUser *user)
1392 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1393 return GET_USER_PRIVATE (user)->image;
1397 * common_user_get_background:
1398 * @user: A #CommonUser
1400 * Get the background file path for a user.
1402 * Return value: The background file path for the given user or #NULL if no path
1405 common_user_get_background (CommonUser *user)
1407 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1408 return GET_USER_PRIVATE (user)->background;
1412 * common_user_get_language:
1413 * @user: A #CommonUser
1415 * Get the language for a user.
1417 * 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.
1420 common_user_get_language (CommonUser *user)
1422 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1424 const gchar *language = GET_USER_PRIVATE (user)->language;
1425 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1429 * common_user_set_language:
1430 * @user: A #CommonUser
1431 * @language: The user's new language
1433 * Set the language for a user.
1436 common_user_set_language (CommonUser *user, const gchar *language)
1438 g_return_if_fail (COMMON_IS_USER (user));
1439 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1441 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1442 save_string_to_dmrc (user, "Desktop", "Language", language);
1447 * common_user_get_layout:
1448 * @user: A #CommonUser
1450 * Get the keyboard layout for a user.
1452 * 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.
1455 common_user_get_layout (CommonUser *user)
1457 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1459 return GET_USER_PRIVATE (user)->layouts[0];
1463 * common_user_get_layouts:
1464 * @user: A #CommonUser
1466 * Get the configured keyboard layouts for a user.
1468 * 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.
1470 const gchar * const *
1471 common_user_get_layouts (CommonUser *user)
1473 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1475 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1479 * common_user_get_session:
1480 * @user: A #CommonUser
1482 * Get the session for a user.
1484 * Return value: The session for the given user or #NULL if using system defaults.
1487 common_user_get_session (CommonUser *user)
1489 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1491 const gchar *session = GET_USER_PRIVATE (user)->session;
1492 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1496 * common_user_set_session:
1497 * @user: A #CommonUser
1498 * @language: The user's new session
1500 * Set the session for a user.
1503 common_user_set_session (CommonUser *user, const gchar *session)
1505 g_return_if_fail (COMMON_IS_USER (user));
1506 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1508 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1509 save_string_to_dmrc (user, "Desktop", "Session", session);
1514 * common_user_get_logged_in:
1515 * @user: A #CommonUser
1517 * Check if a user is logged in.
1519 * Return value: #TRUE if the user is currently logged in.
1522 common_user_get_logged_in (CommonUser *user)
1524 CommonUserPrivate *priv;
1525 CommonUserListPrivate *list_priv;
1528 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1530 priv = GET_USER_PRIVATE (user);
1531 list_priv = GET_LIST_PRIVATE (priv->user_list);
1533 // Lazily decide to load/listen to sessions
1534 if (list_priv->session_added_signal == 0)
1535 load_sessions (priv->user_list);
1537 for (link = list_priv->sessions; link; link = link->next)
1539 CommonSession *session = link->data;
1540 if (strcmp (session->username, priv->name) == 0)
1548 * common_user_get_has_messages:
1549 * @user: A #CommonUser
1551 * Check if a user has waiting messages.
1553 * Return value: #TRUE if the user has waiting messages.
1556 common_user_get_has_messages (CommonUser *user)
1558 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1559 return GET_USER_PRIVATE (user)->has_messages;
1563 * common_user_get_uid:
1564 * @user: A #CommonUser
1566 * Get the uid of a user
1568 * Return value: The user's uid
1571 common_user_get_uid (CommonUser *user)
1573 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1574 return GET_USER_PRIVATE (user)->uid;
1578 * common_user_get_gid:
1579 * @user: A #CommonUser
1581 * Get the gid of a user
1583 * Return value: The user's gid
1586 common_user_get_gid (CommonUser *user)
1588 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1589 /* gid is not actually stored in AccountsService, so if our user is from
1590 AccountsService, we have to look up manually in passwd. gid won't
1591 change, so just look up the first time we're asked and never again. */
1592 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1593 if (priv->uid != 0 && priv->gid == 0)
1595 struct passwd *entry = getpwuid (priv->uid);
1597 priv->gid = entry->pw_gid;
1603 common_user_init (CommonUser *user)
1605 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1606 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1607 priv->layouts[0] = NULL;
1611 common_user_set_property (GObject *object,
1613 const GValue *value,
1616 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1620 common_user_get_property (GObject *object,
1627 self = COMMON_USER (object);
1631 case USER_PROP_NAME:
1632 g_value_set_string (value, common_user_get_name (self));
1634 case USER_PROP_REAL_NAME:
1635 g_value_set_string (value, common_user_get_real_name (self));
1637 case USER_PROP_DISPLAY_NAME:
1638 g_value_set_string (value, common_user_get_display_name (self));
1640 case USER_PROP_HOME_DIRECTORY:
1641 g_value_set_string (value, common_user_get_home_directory (self));
1643 case USER_PROP_SHELL:
1644 g_value_set_string (value, common_user_get_shell (self));
1646 case USER_PROP_IMAGE:
1647 g_value_set_string (value, common_user_get_image (self));
1649 case USER_PROP_BACKGROUND:
1650 g_value_set_string (value, common_user_get_background (self));
1652 case USER_PROP_LANGUAGE:
1653 g_value_set_string (value, common_user_get_language (self));
1655 case USER_PROP_LAYOUT:
1656 g_value_set_string (value, common_user_get_layout (self));
1658 case USER_PROP_LAYOUTS:
1659 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1661 case USER_PROP_SESSION:
1662 g_value_set_string (value, common_user_get_session (self));
1664 case USER_PROP_LOGGED_IN:
1665 g_value_set_boolean (value, common_user_get_logged_in (self));
1667 case USER_PROP_HAS_MESSAGES:
1668 g_value_set_boolean (value, common_user_get_has_messages (self));
1671 g_value_set_uint64 (value, common_user_get_uid (self));
1674 g_value_set_uint64 (value, common_user_get_gid (self));
1677 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1683 common_user_finalize (GObject *object)
1685 CommonUser *self = COMMON_USER (object);
1686 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1688 g_free (priv->path);
1689 if (priv->changed_signal)
1690 g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1691 g_free (priv->name);
1692 g_free (priv->real_name);
1693 g_free (priv->home_directory);
1694 g_free (priv->shell);
1695 g_free (priv->image);
1696 g_free (priv->background);
1697 g_free (priv->language);
1698 g_strfreev (priv->layouts);
1699 g_free (priv->session);
1703 common_user_class_init (CommonUserClass *klass)
1705 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1707 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1709 object_class->set_property = common_user_set_property;
1710 object_class->get_property = common_user_get_property;
1711 object_class->finalize = common_user_finalize;
1713 g_object_class_install_property (object_class,
1715 g_param_spec_string ("name",
1719 G_PARAM_READWRITE));
1720 g_object_class_install_property (object_class,
1721 USER_PROP_REAL_NAME,
1722 g_param_spec_string ("real-name",
1726 G_PARAM_READWRITE));
1727 g_object_class_install_property (object_class,
1728 USER_PROP_DISPLAY_NAME,
1729 g_param_spec_string ("display-name",
1731 "Users display name",
1734 g_object_class_install_property (object_class,
1735 USER_PROP_HOME_DIRECTORY,
1736 g_param_spec_string ("home-directory",
1740 G_PARAM_READWRITE));
1741 g_object_class_install_property (object_class,
1743 g_param_spec_string ("shell",
1747 G_PARAM_READWRITE));
1748 g_object_class_install_property (object_class,
1750 g_param_spec_string ("image",
1754 G_PARAM_READWRITE));
1755 g_object_class_install_property (object_class,
1756 USER_PROP_BACKGROUND,
1757 g_param_spec_string ("background",
1761 G_PARAM_READWRITE));
1762 g_object_class_install_property (object_class,
1764 g_param_spec_string ("language",
1766 "Language used by this user",
1769 g_object_class_install_property (object_class,
1771 g_param_spec_string ("layout",
1773 "Keyboard layout used by this user",
1776 g_object_class_install_property (object_class,
1778 g_param_spec_boxed ("layouts",
1780 "Keyboard layouts used by this user",
1783 g_object_class_install_property (object_class,
1785 g_param_spec_string ("session",
1787 "Session used by this user",
1790 g_object_class_install_property (object_class,
1791 USER_PROP_LOGGED_IN,
1792 g_param_spec_boolean ("logged-in",
1794 "TRUE if the user is currently in a session",
1796 G_PARAM_READWRITE));
1797 g_object_class_install_property (object_class,
1798 USER_PROP_LOGGED_IN,
1799 g_param_spec_boolean ("has-messages",
1801 "TRUE if the user is has waiting messages",
1803 G_PARAM_READWRITE));
1804 g_object_class_install_property (object_class,
1806 g_param_spec_uint64 ("uid",
1812 G_PARAM_READWRITE));
1813 g_object_class_install_property (object_class,
1815 g_param_spec_uint64 ("gd",
1821 G_PARAM_READWRITE));
1824 * CommonUser::changed:
1825 * @user: A #CommonUser
1827 * The ::changed signal gets emitted this user account is modified.
1829 user_signals[CHANGED] =
1830 g_signal_new (USER_SIGNAL_CHANGED,
1831 G_TYPE_FROM_CLASS (klass),
1833 G_STRUCT_OFFSET (CommonUserClass, changed),
1840 common_session_init (CommonSession *common_session)
1845 common_session_finalize (GObject *object)
1847 CommonSession *self = COMMON_SESSION (object);
1849 g_free (self->path);
1850 g_free (self->username);
1854 common_session_class_init (CommonSessionClass *klass)
1856 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1857 object_class->finalize = common_session_finalize;