1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3 * Copyright (C) 2010 Robert Ancell.
4 * Copyright (C) 2014 Canonical, Ltd.
5 * Authors: Robert Ancell <robert.ancell@canonical.com>
6 * Michael Terry <michael.terry@canonical.com>
8 * This library is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU Lesser General Public License as published by the Free
10 * Software Foundation; either version 2 or version 3 of the License.
11 * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
18 #include <sys/utsname.h>
23 #include "user-list.h"
37 USER_PROP_DISPLAY_NAME,
38 USER_PROP_HOME_DIRECTORY,
47 USER_PROP_HAS_MESSAGES,
59 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
66 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
70 /* Bus connection being communicated on */
73 /* D-Bus signals for accounts service events */
74 guint user_added_signal;
75 guint user_removed_signal;
77 /* D-Bus signals for display manager events */
78 guint session_added_signal;
79 guint session_removed_signal;
81 /* File monitor for password file */
82 GFileMonitor *passwd_monitor;
84 /* TRUE if have scanned users */
90 /* List of sessions */
92 } CommonUserListPrivate;
96 /* User list this user is part of */
97 CommonUserList *user_list;
99 /* TRUE if have loaded user properties */
100 gboolean loaded_values;
102 /* Accounts service path */
105 /* Update signal from accounts service */
106 guint changed_signal;
111 /* Descriptive name for user */
114 /* Home directory of user */
115 gchar *home_directory;
123 /* Background image for users */
126 /* TRUE if this user has messages available */
127 gboolean has_messages;
135 /* User chosen language */
138 /* User layout preferences */
141 /* User default session */
147 GObject parent_instance;
154 GObjectClass parent_class;
155 } CommonSessionClass;
157 G_DEFINE_TYPE (CommonUserList, common_user_list, G_TYPE_OBJECT);
158 G_DEFINE_TYPE (CommonUser, common_user, G_TYPE_OBJECT);
159 #define COMMON_SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), common_session_get_type (), CommonSession))
160 GType common_session_get_type (void);
161 G_DEFINE_TYPE (CommonSession, common_session, G_TYPE_OBJECT);
163 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER_LIST, CommonUserListPrivate)
164 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), COMMON_TYPE_USER, CommonUserPrivate)
166 #define PASSWD_FILE "/etc/passwd"
167 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
169 static CommonUserList *singleton = NULL;
172 * common_user_list_get_instance:
176 * Return value: (transfer none): the #CommonUserList
179 common_user_list_get_instance (void)
182 singleton = g_object_new (COMMON_TYPE_USER_LIST, NULL);
187 common_user_list_cleanup (void)
190 g_object_unref (singleton);
195 get_user_by_name (CommonUserList *user_list, const gchar *username)
197 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
200 for (link = priv->users; link; link = link->next)
202 CommonUser *user = link->data;
203 if (g_strcmp0 (common_user_get_name (user), username) == 0)
211 get_user_by_path (CommonUserList *user_list, const gchar *path)
213 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
216 for (link = priv->users; link; link = link->next)
218 CommonUser *user = link->data;
219 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
227 compare_user (gconstpointer a, gconstpointer b)
229 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
230 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
234 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
236 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
238 /* Skip if already set to this */
239 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
240 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
241 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
242 g_strcmp0 (common_user_get_image (user), image) == 0)
245 g_free (priv->real_name);
246 priv->real_name = g_strdup (real_name);
247 g_free (priv->home_directory);
248 priv->home_directory = g_strdup (home_directory);
249 g_free (priv->shell);
250 priv->shell = g_strdup (shell);
251 g_free (priv->image);
252 priv->image = g_strdup (image);
258 user_changed_cb (CommonUser *user)
260 g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
264 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
266 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
267 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
269 gchar *real_name, *image;
271 tokens = g_strsplit (entry->pw_gecos, ",", -1);
272 if (tokens[0] != NULL && tokens[0][0] != '\0')
273 real_name = g_strdup (tokens[0]);
275 real_name = g_strdup ("");
278 image = g_build_filename (entry->pw_dir, ".face", NULL);
279 if (!g_file_test (image, G_FILE_TEST_EXISTS))
282 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
283 if (!g_file_test (image, G_FILE_TEST_EXISTS))
290 priv->user_list = user_list;
291 priv->name = g_strdup (entry->pw_name);
292 priv->real_name = real_name;
293 priv->home_directory = g_strdup (entry->pw_dir);
294 priv->shell = g_strdup (entry->pw_shell);
296 priv->uid = entry->pw_uid;
297 priv->gid = entry->pw_gid;
303 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
305 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
309 gchar **hidden_users, **hidden_shells;
310 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
311 GError *error = NULL;
313 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
315 config = g_key_file_new ();
316 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
317 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
318 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
319 g_clear_error (&error);
321 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
322 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
326 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
328 value = g_strdup ("nobody nobody4 noaccess");
329 hidden_users = g_strsplit (value, " ", -1);
332 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
334 value = g_strdup ("/bin/false /usr/sbin/nologin");
335 hidden_shells = g_strsplit (value, " ", -1);
338 g_key_file_free (config);
344 struct passwd *entry;
353 /* Ignore system users */
354 if (entry->pw_uid < minimum_uid)
357 /* Ignore users disabled by shell */
360 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
361 if (hidden_shells[i])
365 /* Ignore certain users */
366 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
370 user = make_passwd_user (user_list, entry);
372 /* Update existing users if have them */
373 for (link = priv->users; link; link = link->next)
375 CommonUser *info = link->data;
376 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
378 if (update_passwd_user (info, common_user_get_real_name (user), common_user_get_home_directory (user), common_user_get_shell (user), common_user_get_image (user)))
379 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
380 g_object_unref (user);
387 /* Only notify once we have loaded the user list */
388 if (priv->have_users)
389 new_users = g_list_insert_sorted (new_users, user, compare_user);
391 users = g_list_insert_sorted (users, user, compare_user);
393 g_strfreev (hidden_users);
394 g_strfreev (hidden_shells);
397 g_warning ("Failed to read password database: %s", strerror (errno));
401 /* Use new user list */
402 old_users = priv->users;
405 /* Notify of changes */
406 for (link = new_users; link; link = link->next)
408 CommonUser *info = link->data;
409 g_debug ("User %s added", common_user_get_name (info));
410 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), NULL);
412 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
414 g_list_free (new_users);
415 for (link = changed_users; link; link = link->next)
417 CommonUser *info = link->data;
418 g_debug ("User %s changed", common_user_get_name (info));
419 g_signal_emit (info, user_signals[CHANGED], 0);
421 g_list_free (changed_users);
422 for (link = old_users; link; link = link->next)
426 /* See if this user is in the current list */
427 for (new_link = priv->users; new_link; new_link = new_link->next)
429 if (new_link->data == link->data)
435 CommonUser *info = link->data;
436 g_debug ("User %s removed", common_user_get_name (info));
437 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
438 g_object_unref (info);
441 g_list_free (old_users);
445 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
447 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
449 g_debug ("%s changed, reloading user list", g_file_get_path (file));
450 load_passwd_file (user_list, TRUE);
454 static gboolean load_accounts_user (CommonUser *user);
457 accounts_user_changed_cb (GDBusConnection *connection,
458 const gchar *sender_name,
459 const gchar *object_path,
460 const gchar *interface_name,
461 const gchar *signal_name,
462 GVariant *parameters,
465 CommonUser *user = data;
466 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
468 g_debug ("User %s changed", priv->path);
469 if (load_accounts_user (user))
470 g_signal_emit (user, user_signals[CHANGED], 0);
474 load_accounts_user (CommonUser *user)
476 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
477 GVariant *result, *value;
480 gboolean system_account = FALSE;
481 GError *error = NULL;
483 /* Get the properties for this user */
484 if (!priv->changed_signal)
485 priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
486 "org.freedesktop.Accounts",
487 "org.freedesktop.Accounts.User",
491 G_DBUS_SIGNAL_FLAGS_NONE,
492 accounts_user_changed_cb,
495 result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
496 "org.freedesktop.Accounts",
498 "org.freedesktop.DBus.Properties",
500 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
501 G_VARIANT_TYPE ("(a{sv})"),
502 G_DBUS_CALL_FLAGS_NONE,
507 g_warning ("Error updating user %s: %s", priv->path, error->message);
508 g_clear_error (&error);
512 /* Store the properties we need */
513 g_variant_get (result, "(a{sv})", &iter);
514 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
516 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
519 priv->name = g_variant_dup_string (value, NULL);
521 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
523 g_free (priv->real_name);
524 priv->real_name = g_variant_dup_string (value, NULL);
526 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
528 g_free (priv->home_directory);
529 priv->home_directory = g_variant_dup_string (value, NULL);
531 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
533 g_free (priv->shell);
534 priv->shell = g_variant_dup_string (value, NULL);
536 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
537 system_account = g_variant_get_boolean (value);
538 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
541 g_free (priv->language);
542 priv->language = g_variant_dup_string (value, NULL);
544 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
546 g_free (priv->image);
547 priv->image = g_variant_dup_string (value, NULL);
548 if (strcmp (priv->image, "") == 0)
550 g_free (priv->image);
554 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
556 g_free (priv->session);
557 priv->session = g_variant_dup_string (value, NULL);
559 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
561 g_free (priv->background);
562 priv->background = g_variant_dup_string (value, NULL);
563 if (strcmp (priv->background, "") == 0)
565 g_free (priv->background);
566 priv->background = NULL;
569 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
571 g_strfreev (priv->layouts);
572 priv->layouts = g_variant_dup_strv (value, NULL);
575 priv->layouts = g_malloc (sizeof (gchar *) * 1);
576 priv->layouts[0] = NULL;
579 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
580 priv->has_messages = g_variant_get_boolean (value);
581 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
582 priv->uid = g_variant_get_uint64 (value);
584 g_variant_iter_free (iter);
586 g_variant_unref (result);
588 priv->loaded_values = TRUE;
590 return !system_account;
594 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
596 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
598 CommonUserPrivate *priv;
600 user = g_object_new (COMMON_TYPE_USER, NULL);
601 priv = GET_USER_PRIVATE (user);
603 g_debug ("User %s added", path);
604 priv->user_list = user_list;
605 priv->path = g_strdup (path);
606 g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
607 if (load_accounts_user (user))
609 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
611 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
614 g_object_unref (user);
618 accounts_user_added_cb (GDBusConnection *connection,
619 const gchar *sender_name,
620 const gchar *object_path,
621 const gchar *interface_name,
622 const gchar *signal_name,
623 GVariant *parameters,
626 CommonUserList *user_list = data;
630 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
632 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
636 g_variant_get (parameters, "(&o)", &path);
638 /* Add user if we haven't got them */
639 user = get_user_by_path (user_list, path);
641 add_accounts_user (user_list, path, TRUE);
645 accounts_user_deleted_cb (GDBusConnection *connection,
646 const gchar *sender_name,
647 const gchar *object_path,
648 const gchar *interface_name,
649 const gchar *signal_name,
650 GVariant *parameters,
653 CommonUserList *user_list = data;
654 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
658 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
660 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
664 g_variant_get (parameters, "(&o)", &path);
666 /* Delete user if we know of them */
667 user = get_user_by_path (user_list, path);
670 g_debug ("User %s deleted", path);
671 priv->users = g_list_remove (priv->users, user);
673 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
675 g_object_unref (user);
679 static CommonSession *
680 load_session (CommonUserList *user_list, const gchar *path)
682 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
683 CommonSession *session = NULL;
684 GVariant *result, *username;
685 GError *error = NULL;
687 result = g_dbus_connection_call_sync (priv->bus,
688 "org.freedesktop.DisplayManager",
690 "org.freedesktop.DBus.Properties",
692 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
693 G_VARIANT_TYPE ("(v)"),
694 G_DBUS_CALL_FLAGS_NONE,
699 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
700 g_clear_error (&error);
704 g_variant_get (result, "(v)", &username);
705 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
709 g_variant_get (username, "&s", &name);
711 g_debug ("Loaded session %s (%s)", path, name);
712 session = g_object_new (common_session_get_type (), NULL);
713 session->username = g_strdup (name);
714 session->path = g_strdup (path);
715 priv->sessions = g_list_append (priv->sessions, session);
717 g_variant_unref (username);
718 g_variant_unref (result);
724 session_added_cb (GDBusConnection *connection,
725 const gchar *sender_name,
726 const gchar *object_path,
727 const gchar *interface_name,
728 const gchar *signal_name,
729 GVariant *parameters,
732 CommonUserList *user_list = data;
734 CommonSession *session;
735 CommonUser *user = NULL;
737 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
739 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
743 g_variant_get (parameters, "(&o)", &path);
744 session = load_session (user_list, path);
746 user = get_user_by_name (user_list, session->username);
748 g_signal_emit (user, user_signals[CHANGED], 0);
752 session_removed_cb (GDBusConnection *connection,
753 const gchar *sender_name,
754 const gchar *object_path,
755 const gchar *interface_name,
756 const gchar *signal_name,
757 GVariant *parameters,
760 CommonUserList *user_list = data;
761 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
765 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
767 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
771 g_variant_get (parameters, "(&o)", &path);
773 for (link = priv->sessions; link; link = link->next)
775 CommonSession *session = link->data;
776 if (strcmp (session->path, path) == 0)
780 g_debug ("Session %s removed", path);
781 priv->sessions = g_list_delete_link (priv->sessions, link);
782 user = get_user_by_name (user_list, session->username);
784 g_signal_emit (user, user_signals[CHANGED], 0);
785 g_object_unref (session);
792 load_sessions (CommonUserList *user_list)
794 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
796 GError *error = NULL;
798 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
799 "org.freedesktop.DisplayManager",
800 "org.freedesktop.DisplayManager",
802 "/org/freedesktop/DisplayManager",
804 G_DBUS_SIGNAL_FLAGS_NONE,
808 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
809 "org.freedesktop.DisplayManager",
810 "org.freedesktop.DisplayManager",
812 "/org/freedesktop/DisplayManager",
814 G_DBUS_SIGNAL_FLAGS_NONE,
818 result = g_dbus_connection_call_sync (priv->bus,
819 "org.freedesktop.DisplayManager",
820 "/org/freedesktop/DisplayManager",
821 "org.freedesktop.DBus.Properties",
823 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
824 G_VARIANT_TYPE ("(v)"),
825 G_DBUS_CALL_FLAGS_NONE,
830 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
831 g_clear_error (&error);
834 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
840 g_variant_get (result, "(v)", &value);
842 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
843 g_variant_get (value, "ao", &iter);
844 while (g_variant_iter_loop (iter, "&o", &path))
845 load_session (user_list, path);
846 g_variant_iter_free (iter);
848 g_variant_unref (value);
851 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
853 g_variant_unref (result);
858 load_users (CommonUserList *user_list)
860 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
862 GError *error = NULL;
864 if (priv->have_users)
866 priv->have_users = TRUE;
868 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
869 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
870 "org.freedesktop.Accounts",
871 "org.freedesktop.Accounts",
873 "/org/freedesktop/Accounts",
875 G_DBUS_SIGNAL_FLAGS_NONE,
876 accounts_user_added_cb,
879 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
880 "org.freedesktop.Accounts",
881 "org.freedesktop.Accounts",
883 "/org/freedesktop/Accounts",
885 G_DBUS_SIGNAL_FLAGS_NONE,
886 accounts_user_deleted_cb,
889 result = g_dbus_connection_call_sync (priv->bus,
890 "org.freedesktop.Accounts",
891 "/org/freedesktop/Accounts",
892 "org.freedesktop.Accounts",
894 g_variant_new ("()"),
895 G_VARIANT_TYPE ("(ao)"),
896 G_DBUS_CALL_FLAGS_NONE,
901 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
902 g_clear_error (&error);
908 g_debug ("Loading users from org.freedesktop.Accounts");
909 g_variant_get (result, "(ao)", &iter);
910 while (g_variant_iter_loop (iter, "&o", &path))
911 add_accounts_user (user_list, path, FALSE);
912 g_variant_iter_free (iter);
913 g_variant_unref (result);
919 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
920 priv->user_added_signal = 0;
921 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
922 priv->user_removed_signal = 0;
924 load_passwd_file (user_list, FALSE);
926 /* Watch for changes to user list */
928 passwd_file = g_file_new_for_path (PASSWD_FILE);
929 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
930 g_object_unref (passwd_file);
932 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
934 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
935 g_clear_error (&error);
940 * common_user_list_get_length:
941 * @user_list: a #CommonUserList
943 * Return value: The number of users able to log in
946 common_user_list_get_length (CommonUserList *user_list)
948 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
949 load_users (user_list);
950 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
954 * common_user_list_get_users:
955 * @user_list: A #CommonUserList
957 * Get a list of users to present to the user. This list may be a subset of the
958 * available users and may be empty depending on the server configuration.
960 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
963 common_user_list_get_users (CommonUserList *user_list)
965 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
966 load_users (user_list);
967 return GET_LIST_PRIVATE (user_list)->users;
971 * common_user_list_get_user_by_name:
972 * @user_list: A #CommonUserList
973 * @username: Name of user to get.
975 * Get infomation about a given user or #NULL if this user doesn't exist.
976 * Includes hidden and system users, unlike the list from
977 * common_user_list_get_users.
979 * Return value: (transfer full): A #CommonUser entry for the given user.
982 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
984 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
985 g_return_val_if_fail (username != NULL, NULL);
987 load_users (user_list);
989 CommonUser *user = get_user_by_name (user_list, username);
991 return g_object_ref (user);
993 /* Sometimes we need to look up users that aren't in AccountsService.
994 Notably we need to look up the user that the greeter runs as, which
995 is usually 'lightdm'. For such cases, we manually create a one-off
996 CommonUser object and pre-seed with passwd info. */
997 struct passwd *entry = getpwnam (username);
999 return make_passwd_user (user_list, entry);
1005 common_user_list_init (CommonUserList *user_list)
1007 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1009 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1013 common_user_list_set_property (GObject *object,
1015 const GValue *value,
1018 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1022 common_user_list_get_property (GObject *object,
1027 CommonUserList *self;
1029 self = COMMON_USER_LIST (object);
1033 case LIST_PROP_NUM_USERS:
1034 g_value_set_int (value, common_user_list_get_length (self));
1037 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1043 common_user_list_finalize (GObject *object)
1045 CommonUserList *self = COMMON_USER_LIST (object);
1046 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1048 /* Remove children first, they might access us */
1049 g_list_free_full (priv->users, g_object_unref);
1050 g_list_free_full (priv->sessions, g_object_unref);
1052 if (priv->user_added_signal)
1053 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1054 if (priv->user_removed_signal)
1055 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1056 if (priv->session_added_signal)
1057 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1058 if (priv->session_removed_signal)
1059 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1060 g_object_unref (priv->bus);
1061 if (priv->passwd_monitor)
1062 g_object_unref (priv->passwd_monitor);
1064 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1068 common_user_list_class_init (CommonUserListClass *klass)
1070 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1072 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1074 object_class->set_property = common_user_list_set_property;
1075 object_class->get_property = common_user_list_get_property;
1076 object_class->finalize = common_user_list_finalize;
1078 g_object_class_install_property (object_class,
1079 LIST_PROP_NUM_USERS,
1080 g_param_spec_int ("num-users",
1082 "Number of login users",
1086 * CommonUserList::user-added:
1087 * @user_list: A #CommonUserList
1088 * @user: The #CommonUser that has been added.
1090 * The ::user-added signal gets emitted when a user account is created.
1092 list_signals[USER_ADDED] =
1093 g_signal_new ("user-added",
1094 G_TYPE_FROM_CLASS (klass),
1096 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1099 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1102 * CommonUserList::user-changed:
1103 * @user_list: A #CommonUserList
1104 * @user: The #CommonUser that has been changed.
1106 * The ::user-changed signal gets emitted when a user account is modified.
1108 list_signals[USER_CHANGED] =
1109 g_signal_new ("user-changed",
1110 G_TYPE_FROM_CLASS (klass),
1112 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1115 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1118 * CommonUserList::user-removed:
1119 * @user_list: A #CommonUserList
1120 * @user: The #CommonUser that has been removed.
1122 * The ::user-removed signal gets emitted when a user account is removed.
1124 list_signals[USER_REMOVED] =
1125 g_signal_new ("user-removed",
1126 G_TYPE_FROM_CLASS (klass),
1128 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1131 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1135 call_method (CommonUser *user, const gchar *method, GVariant *args,
1136 const gchar *expected, GVariant **result)
1139 GError *error = NULL;
1140 CommonUserPrivate *user_priv = GET_USER_PRIVATE (user);
1141 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_priv->user_list);
1143 answer = g_dbus_connection_call_sync (list_priv->bus,
1144 "org.freedesktop.Accounts",
1146 "org.freedesktop.Accounts.User",
1149 G_VARIANT_TYPE (expected),
1150 G_DBUS_CALL_FLAGS_NONE,
1155 g_warning ("Could not call %s: %s", method, error->message);
1156 g_clear_error (&error);
1164 g_variant_unref (answer);
1170 save_string_to_dmrc (CommonUser *user, const gchar *group,
1171 const gchar *key, const gchar *value)
1175 dmrc = dmrc_load (user);
1176 g_key_file_set_string (dmrc, group, key, value);
1177 dmrc_save (dmrc, user);
1179 g_key_file_free (dmrc);
1183 load_dmrc (CommonUser *user)
1185 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1188 dmrc = dmrc_load (user);
1190 // FIXME: Watch for changes
1192 /* The Language field contains the locale */
1193 g_free (priv->language);
1194 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1196 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1198 g_strfreev (priv->layouts);
1199 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1200 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1201 priv->layouts[1] = NULL;
1204 g_free (priv->session);
1205 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1207 g_key_file_free (dmrc);
1210 /* Loads language/layout/session info for user */
1212 load_user_values (CommonUser *user)
1214 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1216 if (priv->loaded_values)
1218 priv->loaded_values = TRUE;
1223 if (g_strcmp0 (priv->session, "ubuntu-2d") == 0)
1225 g_free(priv->session);
1226 priv->session = g_strdup ("ubuntu");
1231 * common_user_get_name:
1232 * @user: A #CommonUser
1234 * Get the name of a user.
1236 * Return value: The name of the given user
1239 common_user_get_name (CommonUser *user)
1241 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1242 load_user_values (user);
1243 return GET_USER_PRIVATE (user)->name;
1247 * common_user_get_real_name:
1248 * @user: A #CommonUser
1250 * Get the real name of a user.
1252 * Return value: The real name of the given user
1255 common_user_get_real_name (CommonUser *user)
1257 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1258 load_user_values (user);
1259 return GET_USER_PRIVATE (user)->real_name;
1263 * common_user_get_display_name:
1264 * @user: A #CommonUser
1266 * Get the display name of a user.
1268 * Return value: The display name of the given user
1271 common_user_get_display_name (CommonUser *user)
1273 CommonUserPrivate *priv;
1275 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1277 load_user_values (user);
1279 priv = GET_USER_PRIVATE (user);
1280 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1283 return priv->real_name;
1287 * common_user_get_home_directory:
1288 * @user: A #CommonUser
1290 * Get the home directory for a user.
1292 * Return value: The users home directory
1295 common_user_get_home_directory (CommonUser *user)
1297 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1298 load_user_values (user);
1299 return GET_USER_PRIVATE (user)->home_directory;
1303 * common_user_get_shell:
1304 * @user: A #CommonUser
1306 * Get the shell for a user.
1308 * Return value: The user's shell
1311 common_user_get_shell (CommonUser *user)
1313 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1314 load_user_values (user);
1315 return GET_USER_PRIVATE (user)->shell;
1319 * common_user_get_image:
1320 * @user: A #CommonUser
1322 * Get the image URI for a user.
1324 * Return value: The image URI for the given user or #NULL if no URI
1327 common_user_get_image (CommonUser *user)
1329 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1330 load_user_values (user);
1331 return GET_USER_PRIVATE (user)->image;
1335 * common_user_get_background:
1336 * @user: A #CommonUser
1338 * Get the background file path for a user.
1340 * Return value: The background file path for the given user or #NULL if no path
1343 common_user_get_background (CommonUser *user)
1345 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1346 load_user_values (user);
1347 return GET_USER_PRIVATE (user)->background;
1351 * common_user_get_language:
1352 * @user: A #CommonUser
1354 * Get the language for a user.
1356 * 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.
1359 common_user_get_language (CommonUser *user)
1361 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1362 load_user_values (user);
1363 const gchar *language = GET_USER_PRIVATE (user)->language;
1364 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1368 * common_user_set_language:
1369 * @user: A #CommonUser
1370 * @language: The user's new language
1372 * Set the language for a user.
1375 common_user_set_language (CommonUser *user, const gchar *language)
1377 g_return_if_fail (COMMON_IS_USER (user));
1378 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1380 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1381 save_string_to_dmrc (user, "Desktop", "Language", language);
1386 * common_user_get_layout:
1387 * @user: A #CommonUser
1389 * Get the keyboard layout for a user.
1391 * 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.
1394 common_user_get_layout (CommonUser *user)
1396 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1397 load_user_values (user);
1398 return GET_USER_PRIVATE (user)->layouts[0];
1402 * common_user_get_layouts:
1403 * @user: A #CommonUser
1405 * Get the configured keyboard layouts for a user.
1407 * 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.
1409 const gchar * const *
1410 common_user_get_layouts (CommonUser *user)
1412 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1413 load_user_values (user);
1414 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1418 * common_user_get_session:
1419 * @user: A #CommonUser
1421 * Get the session for a user.
1423 * Return value: The session for the given user or #NULL if using system defaults.
1426 common_user_get_session (CommonUser *user)
1428 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1429 load_user_values (user);
1430 const gchar *session = GET_USER_PRIVATE (user)->session;
1431 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1435 * common_user_set_session:
1436 * @user: A #CommonUser
1437 * @language: The user's new session
1439 * Set the session for a user.
1442 common_user_set_session (CommonUser *user, const gchar *session)
1444 g_return_if_fail (COMMON_IS_USER (user));
1445 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1447 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1448 save_string_to_dmrc (user, "Desktop", "Session", session);
1453 * common_user_get_logged_in:
1454 * @user: A #CommonUser
1456 * Check if a user is logged in.
1458 * Return value: #TRUE if the user is currently logged in.
1461 common_user_get_logged_in (CommonUser *user)
1463 CommonUserPrivate *priv;
1464 CommonUserListPrivate *list_priv;
1467 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1469 priv = GET_USER_PRIVATE (user);
1470 list_priv = GET_LIST_PRIVATE (priv->user_list);
1472 // Lazily decide to load/listen to sessions
1473 if (list_priv->session_added_signal == 0)
1474 load_sessions (priv->user_list);
1476 for (link = list_priv->sessions; link; link = link->next)
1478 CommonSession *session = link->data;
1479 if (strcmp (session->username, priv->name) == 0)
1487 * common_user_get_has_messages:
1488 * @user: A #CommonUser
1490 * Check if a user has waiting messages.
1492 * Return value: #TRUE if the user has waiting messages.
1495 common_user_get_has_messages (CommonUser *user)
1497 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1498 load_user_values (user);
1499 return GET_USER_PRIVATE (user)->has_messages;
1503 * common_user_get_uid:
1504 * @user: A #CommonUser
1506 * Get the uid of a user
1508 * Return value: The user's uid
1511 common_user_get_uid (CommonUser *user)
1513 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1514 load_user_values (user);
1515 return GET_USER_PRIVATE (user)->uid;
1519 * common_user_get_gid:
1520 * @user: A #CommonUser
1522 * Get the gid of a user
1524 * Return value: The user's gid
1527 common_user_get_gid (CommonUser *user)
1529 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1530 load_user_values (user);
1531 /* gid is not actually stored in AccountsService, so if our user is from
1532 AccountsService, we have to look up manually in passwd. gid won't
1533 change, so just look up the first time we're asked and never again. */
1534 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1535 if (priv->uid != 0 && priv->gid == 0)
1537 struct passwd *entry = getpwuid (priv->uid);
1539 priv->gid = entry->pw_gid;
1545 common_user_init (CommonUser *user)
1547 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1548 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1549 priv->layouts[0] = NULL;
1553 common_user_set_property (GObject *object,
1555 const GValue *value,
1558 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1562 common_user_get_property (GObject *object,
1569 self = COMMON_USER (object);
1573 case USER_PROP_NAME:
1574 g_value_set_string (value, common_user_get_name (self));
1576 case USER_PROP_REAL_NAME:
1577 g_value_set_string (value, common_user_get_real_name (self));
1579 case USER_PROP_DISPLAY_NAME:
1580 g_value_set_string (value, common_user_get_display_name (self));
1582 case USER_PROP_HOME_DIRECTORY:
1583 g_value_set_string (value, common_user_get_home_directory (self));
1585 case USER_PROP_SHELL:
1586 g_value_set_string (value, common_user_get_shell (self));
1588 case USER_PROP_IMAGE:
1589 g_value_set_string (value, common_user_get_image (self));
1591 case USER_PROP_BACKGROUND:
1592 g_value_set_string (value, common_user_get_background (self));
1594 case USER_PROP_LANGUAGE:
1595 g_value_set_string (value, common_user_get_language (self));
1597 case USER_PROP_LAYOUT:
1598 g_value_set_string (value, common_user_get_layout (self));
1600 case USER_PROP_LAYOUTS:
1601 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1603 case USER_PROP_SESSION:
1604 g_value_set_string (value, common_user_get_session (self));
1606 case USER_PROP_LOGGED_IN:
1607 g_value_set_boolean (value, common_user_get_logged_in (self));
1609 case USER_PROP_HAS_MESSAGES:
1610 g_value_set_boolean (value, common_user_get_has_messages (self));
1613 g_value_set_uint64 (value, common_user_get_uid (self));
1616 g_value_set_uint64 (value, common_user_get_gid (self));
1619 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1625 common_user_finalize (GObject *object)
1627 CommonUser *self = COMMON_USER (object);
1628 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1630 g_free (priv->path);
1631 if (priv->changed_signal)
1632 g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1633 g_free (priv->name);
1634 g_free (priv->real_name);
1635 g_free (priv->home_directory);
1636 g_free (priv->shell);
1637 g_free (priv->image);
1638 g_free (priv->background);
1639 g_free (priv->language);
1640 g_strfreev (priv->layouts);
1641 g_free (priv->session);
1645 common_user_class_init (CommonUserClass *klass)
1647 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1649 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1651 object_class->set_property = common_user_set_property;
1652 object_class->get_property = common_user_get_property;
1653 object_class->finalize = common_user_finalize;
1655 g_object_class_install_property (object_class,
1657 g_param_spec_string ("name",
1661 G_PARAM_READWRITE));
1662 g_object_class_install_property (object_class,
1663 USER_PROP_REAL_NAME,
1664 g_param_spec_string ("real-name",
1668 G_PARAM_READWRITE));
1669 g_object_class_install_property (object_class,
1670 USER_PROP_DISPLAY_NAME,
1671 g_param_spec_string ("display-name",
1673 "Users display name",
1676 g_object_class_install_property (object_class,
1677 USER_PROP_HOME_DIRECTORY,
1678 g_param_spec_string ("home-directory",
1682 G_PARAM_READWRITE));
1683 g_object_class_install_property (object_class,
1685 g_param_spec_string ("shell",
1689 G_PARAM_READWRITE));
1690 g_object_class_install_property (object_class,
1692 g_param_spec_string ("image",
1696 G_PARAM_READWRITE));
1697 g_object_class_install_property (object_class,
1698 USER_PROP_BACKGROUND,
1699 g_param_spec_string ("background",
1703 G_PARAM_READWRITE));
1704 g_object_class_install_property (object_class,
1706 g_param_spec_string ("language",
1708 "Language used by this user",
1711 g_object_class_install_property (object_class,
1713 g_param_spec_string ("layout",
1715 "Keyboard layout used by this user",
1718 g_object_class_install_property (object_class,
1720 g_param_spec_boxed ("layouts",
1722 "Keyboard layouts used by this user",
1725 g_object_class_install_property (object_class,
1727 g_param_spec_string ("session",
1729 "Session used by this user",
1732 g_object_class_install_property (object_class,
1733 USER_PROP_LOGGED_IN,
1734 g_param_spec_boolean ("logged-in",
1736 "TRUE if the user is currently in a session",
1738 G_PARAM_READWRITE));
1739 g_object_class_install_property (object_class,
1740 USER_PROP_LOGGED_IN,
1741 g_param_spec_boolean ("has-messages",
1743 "TRUE if the user is has waiting messages",
1745 G_PARAM_READWRITE));
1746 g_object_class_install_property (object_class,
1748 g_param_spec_uint64 ("uid",
1754 G_PARAM_READWRITE));
1755 g_object_class_install_property (object_class,
1757 g_param_spec_uint64 ("gd",
1763 G_PARAM_READWRITE));
1766 * CommonUser::changed:
1767 * @user: A #CommonUser
1769 * The ::changed signal gets emitted this user account is modified.
1771 user_signals[CHANGED] =
1772 g_signal_new ("changed",
1773 G_TYPE_FROM_CLASS (klass),
1775 G_STRUCT_OFFSET (CommonUserClass, changed),
1782 common_session_init (CommonSession *common_session)
1787 common_session_finalize (GObject *object)
1789 CommonSession *self = COMMON_SESSION (object);
1791 g_free (self->path);
1792 g_free (self->username);
1796 common_session_class_init (CommonSessionClass *klass)
1798 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1799 object_class->finalize = common_session_finalize;