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;
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, USER_SIGNAL_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 /* Log message disabled as AccountsService can have arbitrary plugins that
469 * might cause us to log when properties change we don't use. LP: #1376357
471 /*g_debug ("User %s changed", priv->path);*/
472 if (load_accounts_user (user))
473 g_signal_emit (user, user_signals[CHANGED], 0);
477 load_accounts_user (CommonUser *user)
479 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
480 GVariant *result, *value;
483 gboolean system_account = FALSE;
484 GError *error = NULL;
486 /* Get the properties for this user */
487 if (!priv->changed_signal)
488 priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
489 "org.freedesktop.Accounts",
490 "org.freedesktop.Accounts.User",
494 G_DBUS_SIGNAL_FLAGS_NONE,
495 accounts_user_changed_cb,
498 result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
499 "org.freedesktop.Accounts",
501 "org.freedesktop.DBus.Properties",
503 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
504 G_VARIANT_TYPE ("(a{sv})"),
505 G_DBUS_CALL_FLAGS_NONE,
510 g_warning ("Error updating user %s: %s", priv->path, error->message);
511 g_clear_error (&error);
515 /* Store the properties we need */
516 g_variant_get (result, "(a{sv})", &iter);
517 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
519 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
522 priv->name = g_variant_dup_string (value, NULL);
524 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
526 g_free (priv->real_name);
527 priv->real_name = g_variant_dup_string (value, NULL);
529 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
531 g_free (priv->home_directory);
532 priv->home_directory = g_variant_dup_string (value, NULL);
534 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
536 g_free (priv->shell);
537 priv->shell = g_variant_dup_string (value, NULL);
539 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
540 system_account = g_variant_get_boolean (value);
541 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
544 g_free (priv->language);
545 priv->language = g_variant_dup_string (value, NULL);
547 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
549 g_free (priv->image);
550 priv->image = g_variant_dup_string (value, NULL);
551 if (strcmp (priv->image, "") == 0)
553 g_free (priv->image);
557 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
559 g_free (priv->session);
560 priv->session = g_variant_dup_string (value, NULL);
562 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
564 g_free (priv->background);
565 priv->background = g_variant_dup_string (value, NULL);
566 if (strcmp (priv->background, "") == 0)
568 g_free (priv->background);
569 priv->background = NULL;
572 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
574 g_strfreev (priv->layouts);
575 priv->layouts = g_variant_dup_strv (value, NULL);
578 priv->layouts = g_malloc (sizeof (gchar *) * 1);
579 priv->layouts[0] = NULL;
582 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
583 priv->has_messages = g_variant_get_boolean (value);
584 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
585 priv->uid = g_variant_get_uint64 (value);
587 g_variant_iter_free (iter);
589 g_variant_unref (result);
591 return !system_account;
595 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
597 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
599 CommonUserPrivate *priv;
601 user = g_object_new (COMMON_TYPE_USER, NULL);
602 priv = GET_USER_PRIVATE (user);
604 g_debug ("User %s added", path);
605 priv->user_list = user_list;
606 priv->path = g_strdup (path);
607 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), NULL);
608 if (load_accounts_user (user))
610 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
612 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
615 g_object_unref (user);
619 accounts_user_added_cb (GDBusConnection *connection,
620 const gchar *sender_name,
621 const gchar *object_path,
622 const gchar *interface_name,
623 const gchar *signal_name,
624 GVariant *parameters,
627 CommonUserList *user_list = data;
631 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
633 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
637 g_variant_get (parameters, "(&o)", &path);
639 /* Add user if we haven't got them */
640 user = get_user_by_path (user_list, path);
642 add_accounts_user (user_list, path, TRUE);
646 accounts_user_deleted_cb (GDBusConnection *connection,
647 const gchar *sender_name,
648 const gchar *object_path,
649 const gchar *interface_name,
650 const gchar *signal_name,
651 GVariant *parameters,
654 CommonUserList *user_list = data;
655 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
659 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
661 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
665 g_variant_get (parameters, "(&o)", &path);
667 /* Delete user if we know of them */
668 user = get_user_by_path (user_list, path);
671 g_debug ("User %s deleted", path);
672 priv->users = g_list_remove (priv->users, user);
674 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
676 g_object_unref (user);
680 static CommonSession *
681 load_session (CommonUserList *user_list, const gchar *path)
683 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
684 CommonSession *session = NULL;
685 GVariant *result, *username;
686 GError *error = NULL;
688 result = g_dbus_connection_call_sync (priv->bus,
689 "org.freedesktop.DisplayManager",
691 "org.freedesktop.DBus.Properties",
693 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
694 G_VARIANT_TYPE ("(v)"),
695 G_DBUS_CALL_FLAGS_NONE,
700 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
701 g_clear_error (&error);
705 g_variant_get (result, "(v)", &username);
706 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
710 g_variant_get (username, "&s", &name);
712 g_debug ("Loaded session %s (%s)", path, name);
713 session = g_object_new (common_session_get_type (), NULL);
714 session->username = g_strdup (name);
715 session->path = g_strdup (path);
716 priv->sessions = g_list_append (priv->sessions, session);
718 g_variant_unref (username);
719 g_variant_unref (result);
725 session_added_cb (GDBusConnection *connection,
726 const gchar *sender_name,
727 const gchar *object_path,
728 const gchar *interface_name,
729 const gchar *signal_name,
730 GVariant *parameters,
733 CommonUserList *user_list = data;
735 CommonSession *session;
736 CommonUser *user = NULL;
738 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
740 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
744 g_variant_get (parameters, "(&o)", &path);
745 session = load_session (user_list, path);
747 user = get_user_by_name (user_list, session->username);
749 g_signal_emit (user, user_signals[CHANGED], 0);
753 session_removed_cb (GDBusConnection *connection,
754 const gchar *sender_name,
755 const gchar *object_path,
756 const gchar *interface_name,
757 const gchar *signal_name,
758 GVariant *parameters,
761 CommonUserList *user_list = data;
762 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
766 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
768 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
772 g_variant_get (parameters, "(&o)", &path);
774 for (link = priv->sessions; link; link = link->next)
776 CommonSession *session = link->data;
777 if (strcmp (session->path, path) == 0)
781 g_debug ("Session %s removed", path);
782 priv->sessions = g_list_delete_link (priv->sessions, link);
783 user = get_user_by_name (user_list, session->username);
785 g_signal_emit (user, user_signals[CHANGED], 0);
786 g_object_unref (session);
793 load_sessions (CommonUserList *user_list)
795 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
797 GError *error = NULL;
799 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
800 "org.freedesktop.DisplayManager",
801 "org.freedesktop.DisplayManager",
803 "/org/freedesktop/DisplayManager",
805 G_DBUS_SIGNAL_FLAGS_NONE,
809 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
810 "org.freedesktop.DisplayManager",
811 "org.freedesktop.DisplayManager",
813 "/org/freedesktop/DisplayManager",
815 G_DBUS_SIGNAL_FLAGS_NONE,
819 result = g_dbus_connection_call_sync (priv->bus,
820 "org.freedesktop.DisplayManager",
821 "/org/freedesktop/DisplayManager",
822 "org.freedesktop.DBus.Properties",
824 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
825 G_VARIANT_TYPE ("(v)"),
826 G_DBUS_CALL_FLAGS_NONE,
831 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
832 g_clear_error (&error);
835 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
841 g_variant_get (result, "(v)", &value);
843 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
844 g_variant_get (value, "ao", &iter);
845 while (g_variant_iter_loop (iter, "&o", &path))
846 load_session (user_list, path);
847 g_variant_iter_free (iter);
849 g_variant_unref (value);
852 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
854 g_variant_unref (result);
859 load_users (CommonUserList *user_list)
861 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
863 GError *error = NULL;
865 if (priv->have_users)
867 priv->have_users = TRUE;
869 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
870 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
871 "org.freedesktop.Accounts",
872 "org.freedesktop.Accounts",
874 "/org/freedesktop/Accounts",
876 G_DBUS_SIGNAL_FLAGS_NONE,
877 accounts_user_added_cb,
880 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
881 "org.freedesktop.Accounts",
882 "org.freedesktop.Accounts",
884 "/org/freedesktop/Accounts",
886 G_DBUS_SIGNAL_FLAGS_NONE,
887 accounts_user_deleted_cb,
890 result = g_dbus_connection_call_sync (priv->bus,
891 "org.freedesktop.Accounts",
892 "/org/freedesktop/Accounts",
893 "org.freedesktop.Accounts",
895 g_variant_new ("()"),
896 G_VARIANT_TYPE ("(ao)"),
897 G_DBUS_CALL_FLAGS_NONE,
902 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
903 g_clear_error (&error);
909 g_debug ("Loading users from org.freedesktop.Accounts");
910 g_variant_get (result, "(ao)", &iter);
911 while (g_variant_iter_loop (iter, "&o", &path))
912 add_accounts_user (user_list, path, FALSE);
913 g_variant_iter_free (iter);
914 g_variant_unref (result);
920 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
921 priv->user_added_signal = 0;
922 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
923 priv->user_removed_signal = 0;
925 load_passwd_file (user_list, FALSE);
927 /* Watch for changes to user list */
929 passwd_file = g_file_new_for_path (PASSWD_FILE);
930 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
931 g_object_unref (passwd_file);
933 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
935 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
936 g_clear_error (&error);
941 * common_user_list_get_length:
942 * @user_list: a #CommonUserList
944 * Return value: The number of users able to log in
947 common_user_list_get_length (CommonUserList *user_list)
949 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
950 load_users (user_list);
951 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
955 * common_user_list_get_users:
956 * @user_list: A #CommonUserList
958 * Get a list of users to present to the user. This list may be a subset of the
959 * available users and may be empty depending on the server configuration.
961 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
964 common_user_list_get_users (CommonUserList *user_list)
966 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
967 load_users (user_list);
968 return GET_LIST_PRIVATE (user_list)->users;
972 * common_user_list_get_user_by_name:
973 * @user_list: A #CommonUserList
974 * @username: Name of user to get.
976 * Get infomation about a given user or #NULL if this user doesn't exist.
977 * Includes hidden and system users, unlike the list from
978 * common_user_list_get_users.
980 * Return value: (transfer full): A #CommonUser entry for the given user.
983 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
985 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
986 g_return_val_if_fail (username != NULL, NULL);
988 load_users (user_list);
990 CommonUser *user = get_user_by_name (user_list, username);
992 return g_object_ref (user);
994 /* Sometimes we need to look up users that aren't in AccountsService.
995 Notably we need to look up the user that the greeter runs as, which
996 is usually 'lightdm'. For such cases, we manually create a one-off
997 CommonUser object and pre-seed with passwd info. */
998 struct passwd *entry = getpwnam (username);
1000 return make_passwd_user (user_list, entry);
1006 common_user_list_init (CommonUserList *user_list)
1008 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1010 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1014 common_user_list_set_property (GObject *object,
1016 const GValue *value,
1019 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1023 common_user_list_get_property (GObject *object,
1028 CommonUserList *self;
1030 self = COMMON_USER_LIST (object);
1034 case LIST_PROP_NUM_USERS:
1035 g_value_set_int (value, common_user_list_get_length (self));
1038 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1044 common_user_list_finalize (GObject *object)
1046 CommonUserList *self = COMMON_USER_LIST (object);
1047 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1049 /* Remove children first, they might access us */
1050 g_list_free_full (priv->users, g_object_unref);
1051 g_list_free_full (priv->sessions, g_object_unref);
1053 if (priv->user_added_signal)
1054 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1055 if (priv->user_removed_signal)
1056 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1057 if (priv->session_added_signal)
1058 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1059 if (priv->session_removed_signal)
1060 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1061 g_object_unref (priv->bus);
1062 g_clear_object (&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_LIST_SIGNAL_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_LIST_SIGNAL_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_LIST_SIGNAL_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);
1182 /* Loads language/layout/session info for user */
1184 load_dmrc (CommonUser *user)
1186 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1189 /* We're using Accounts service instead */
1193 if (priv->loaded_dmrc)
1195 priv->loaded_dmrc = TRUE;
1196 dmrc = dmrc_load (user);
1198 // FIXME: Watch for changes
1200 /* The Language field contains the locale */
1201 g_free (priv->language);
1202 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1204 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1206 g_strfreev (priv->layouts);
1207 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1208 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1209 priv->layouts[1] = NULL;
1212 g_free (priv->session);
1213 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1215 g_key_file_free (dmrc);
1219 * common_user_get_name:
1220 * @user: A #CommonUser
1222 * Get the name of a user.
1224 * Return value: The name of the given user
1227 common_user_get_name (CommonUser *user)
1229 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1230 return GET_USER_PRIVATE (user)->name;
1234 * common_user_get_real_name:
1235 * @user: A #CommonUser
1237 * Get the real name of a user.
1239 * Return value: The real name of the given user
1242 common_user_get_real_name (CommonUser *user)
1244 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1245 return GET_USER_PRIVATE (user)->real_name;
1249 * common_user_get_display_name:
1250 * @user: A #CommonUser
1252 * Get the display name of a user.
1254 * Return value: The display name of the given user
1257 common_user_get_display_name (CommonUser *user)
1259 CommonUserPrivate *priv;
1261 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1263 priv = GET_USER_PRIVATE (user);
1264 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1267 return priv->real_name;
1271 * common_user_get_home_directory:
1272 * @user: A #CommonUser
1274 * Get the home directory for a user.
1276 * Return value: The users home directory
1279 common_user_get_home_directory (CommonUser *user)
1281 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1282 return GET_USER_PRIVATE (user)->home_directory;
1286 * common_user_get_shell:
1287 * @user: A #CommonUser
1289 * Get the shell for a user.
1291 * Return value: The user's shell
1294 common_user_get_shell (CommonUser *user)
1296 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1297 return GET_USER_PRIVATE (user)->shell;
1301 * common_user_get_image:
1302 * @user: A #CommonUser
1304 * Get the image URI for a user.
1306 * Return value: The image URI for the given user or #NULL if no URI
1309 common_user_get_image (CommonUser *user)
1311 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1312 return GET_USER_PRIVATE (user)->image;
1316 * common_user_get_background:
1317 * @user: A #CommonUser
1319 * Get the background file path for a user.
1321 * Return value: The background file path for the given user or #NULL if no path
1324 common_user_get_background (CommonUser *user)
1326 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1327 return GET_USER_PRIVATE (user)->background;
1331 * common_user_get_language:
1332 * @user: A #CommonUser
1334 * Get the language for a user.
1336 * 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.
1339 common_user_get_language (CommonUser *user)
1341 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1343 const gchar *language = GET_USER_PRIVATE (user)->language;
1344 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1348 * common_user_set_language:
1349 * @user: A #CommonUser
1350 * @language: The user's new language
1352 * Set the language for a user.
1355 common_user_set_language (CommonUser *user, const gchar *language)
1357 g_return_if_fail (COMMON_IS_USER (user));
1358 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1360 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1361 save_string_to_dmrc (user, "Desktop", "Language", language);
1366 * common_user_get_layout:
1367 * @user: A #CommonUser
1369 * Get the keyboard layout for a user.
1371 * 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.
1374 common_user_get_layout (CommonUser *user)
1376 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1378 return GET_USER_PRIVATE (user)->layouts[0];
1382 * common_user_get_layouts:
1383 * @user: A #CommonUser
1385 * Get the configured keyboard layouts for a user.
1387 * 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.
1389 const gchar * const *
1390 common_user_get_layouts (CommonUser *user)
1392 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1394 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1398 * common_user_get_session:
1399 * @user: A #CommonUser
1401 * Get the session for a user.
1403 * Return value: The session for the given user or #NULL if using system defaults.
1406 common_user_get_session (CommonUser *user)
1408 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1410 const gchar *session = GET_USER_PRIVATE (user)->session;
1411 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1415 * common_user_set_session:
1416 * @user: A #CommonUser
1417 * @language: The user's new session
1419 * Set the session for a user.
1422 common_user_set_session (CommonUser *user, const gchar *session)
1424 g_return_if_fail (COMMON_IS_USER (user));
1425 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1427 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1428 save_string_to_dmrc (user, "Desktop", "Session", session);
1433 * common_user_get_logged_in:
1434 * @user: A #CommonUser
1436 * Check if a user is logged in.
1438 * Return value: #TRUE if the user is currently logged in.
1441 common_user_get_logged_in (CommonUser *user)
1443 CommonUserPrivate *priv;
1444 CommonUserListPrivate *list_priv;
1447 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1449 priv = GET_USER_PRIVATE (user);
1450 list_priv = GET_LIST_PRIVATE (priv->user_list);
1452 // Lazily decide to load/listen to sessions
1453 if (list_priv->session_added_signal == 0)
1454 load_sessions (priv->user_list);
1456 for (link = list_priv->sessions; link; link = link->next)
1458 CommonSession *session = link->data;
1459 if (strcmp (session->username, priv->name) == 0)
1467 * common_user_get_has_messages:
1468 * @user: A #CommonUser
1470 * Check if a user has waiting messages.
1472 * Return value: #TRUE if the user has waiting messages.
1475 common_user_get_has_messages (CommonUser *user)
1477 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1478 return GET_USER_PRIVATE (user)->has_messages;
1482 * common_user_get_uid:
1483 * @user: A #CommonUser
1485 * Get the uid of a user
1487 * Return value: The user's uid
1490 common_user_get_uid (CommonUser *user)
1492 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1493 return GET_USER_PRIVATE (user)->uid;
1497 * common_user_get_gid:
1498 * @user: A #CommonUser
1500 * Get the gid of a user
1502 * Return value: The user's gid
1505 common_user_get_gid (CommonUser *user)
1507 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1508 /* gid is not actually stored in AccountsService, so if our user is from
1509 AccountsService, we have to look up manually in passwd. gid won't
1510 change, so just look up the first time we're asked and never again. */
1511 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1512 if (priv->uid != 0 && priv->gid == 0)
1514 struct passwd *entry = getpwuid (priv->uid);
1516 priv->gid = entry->pw_gid;
1522 common_user_init (CommonUser *user)
1524 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1525 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1526 priv->layouts[0] = NULL;
1530 common_user_set_property (GObject *object,
1532 const GValue *value,
1535 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1539 common_user_get_property (GObject *object,
1546 self = COMMON_USER (object);
1550 case USER_PROP_NAME:
1551 g_value_set_string (value, common_user_get_name (self));
1553 case USER_PROP_REAL_NAME:
1554 g_value_set_string (value, common_user_get_real_name (self));
1556 case USER_PROP_DISPLAY_NAME:
1557 g_value_set_string (value, common_user_get_display_name (self));
1559 case USER_PROP_HOME_DIRECTORY:
1560 g_value_set_string (value, common_user_get_home_directory (self));
1562 case USER_PROP_SHELL:
1563 g_value_set_string (value, common_user_get_shell (self));
1565 case USER_PROP_IMAGE:
1566 g_value_set_string (value, common_user_get_image (self));
1568 case USER_PROP_BACKGROUND:
1569 g_value_set_string (value, common_user_get_background (self));
1571 case USER_PROP_LANGUAGE:
1572 g_value_set_string (value, common_user_get_language (self));
1574 case USER_PROP_LAYOUT:
1575 g_value_set_string (value, common_user_get_layout (self));
1577 case USER_PROP_LAYOUTS:
1578 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1580 case USER_PROP_SESSION:
1581 g_value_set_string (value, common_user_get_session (self));
1583 case USER_PROP_LOGGED_IN:
1584 g_value_set_boolean (value, common_user_get_logged_in (self));
1586 case USER_PROP_HAS_MESSAGES:
1587 g_value_set_boolean (value, common_user_get_has_messages (self));
1590 g_value_set_uint64 (value, common_user_get_uid (self));
1593 g_value_set_uint64 (value, common_user_get_gid (self));
1596 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1602 common_user_finalize (GObject *object)
1604 CommonUser *self = COMMON_USER (object);
1605 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1607 g_free (priv->path);
1608 if (priv->changed_signal)
1609 g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1610 g_free (priv->name);
1611 g_free (priv->real_name);
1612 g_free (priv->home_directory);
1613 g_free (priv->shell);
1614 g_free (priv->image);
1615 g_free (priv->background);
1616 g_free (priv->language);
1617 g_strfreev (priv->layouts);
1618 g_free (priv->session);
1622 common_user_class_init (CommonUserClass *klass)
1624 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1626 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1628 object_class->set_property = common_user_set_property;
1629 object_class->get_property = common_user_get_property;
1630 object_class->finalize = common_user_finalize;
1632 g_object_class_install_property (object_class,
1634 g_param_spec_string ("name",
1638 G_PARAM_READWRITE));
1639 g_object_class_install_property (object_class,
1640 USER_PROP_REAL_NAME,
1641 g_param_spec_string ("real-name",
1645 G_PARAM_READWRITE));
1646 g_object_class_install_property (object_class,
1647 USER_PROP_DISPLAY_NAME,
1648 g_param_spec_string ("display-name",
1650 "Users display name",
1653 g_object_class_install_property (object_class,
1654 USER_PROP_HOME_DIRECTORY,
1655 g_param_spec_string ("home-directory",
1659 G_PARAM_READWRITE));
1660 g_object_class_install_property (object_class,
1662 g_param_spec_string ("shell",
1666 G_PARAM_READWRITE));
1667 g_object_class_install_property (object_class,
1669 g_param_spec_string ("image",
1673 G_PARAM_READWRITE));
1674 g_object_class_install_property (object_class,
1675 USER_PROP_BACKGROUND,
1676 g_param_spec_string ("background",
1680 G_PARAM_READWRITE));
1681 g_object_class_install_property (object_class,
1683 g_param_spec_string ("language",
1685 "Language used by this user",
1688 g_object_class_install_property (object_class,
1690 g_param_spec_string ("layout",
1692 "Keyboard layout used by this user",
1695 g_object_class_install_property (object_class,
1697 g_param_spec_boxed ("layouts",
1699 "Keyboard layouts used by this user",
1702 g_object_class_install_property (object_class,
1704 g_param_spec_string ("session",
1706 "Session used by this user",
1709 g_object_class_install_property (object_class,
1710 USER_PROP_LOGGED_IN,
1711 g_param_spec_boolean ("logged-in",
1713 "TRUE if the user is currently in a session",
1715 G_PARAM_READWRITE));
1716 g_object_class_install_property (object_class,
1717 USER_PROP_LOGGED_IN,
1718 g_param_spec_boolean ("has-messages",
1720 "TRUE if the user is has waiting messages",
1722 G_PARAM_READWRITE));
1723 g_object_class_install_property (object_class,
1725 g_param_spec_uint64 ("uid",
1731 G_PARAM_READWRITE));
1732 g_object_class_install_property (object_class,
1734 g_param_spec_uint64 ("gd",
1740 G_PARAM_READWRITE));
1743 * CommonUser::changed:
1744 * @user: A #CommonUser
1746 * The ::changed signal gets emitted this user account is modified.
1748 user_signals[CHANGED] =
1749 g_signal_new (USER_SIGNAL_CHANGED,
1750 G_TYPE_FROM_CLASS (klass),
1752 G_STRUCT_OFFSET (CommonUserClass, changed),
1759 common_session_init (CommonSession *common_session)
1764 common_session_finalize (GObject *object)
1766 CommonSession *self = COMMON_SESSION (object);
1768 g_free (self->path);
1769 g_free (self->username);
1773 common_session_class_init (CommonSessionClass *klass)
1775 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1776 object_class->finalize = common_session_finalize;