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, "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 return !system_account;
592 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
594 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
596 CommonUserPrivate *priv;
598 user = g_object_new (COMMON_TYPE_USER, NULL);
599 priv = GET_USER_PRIVATE (user);
601 g_debug ("User %s added", path);
602 priv->user_list = user_list;
603 priv->path = g_strdup (path);
604 g_signal_connect (user, "changed", G_CALLBACK (user_changed_cb), NULL);
605 if (load_accounts_user (user))
607 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
609 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
612 g_object_unref (user);
616 accounts_user_added_cb (GDBusConnection *connection,
617 const gchar *sender_name,
618 const gchar *object_path,
619 const gchar *interface_name,
620 const gchar *signal_name,
621 GVariant *parameters,
624 CommonUserList *user_list = data;
628 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
630 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
634 g_variant_get (parameters, "(&o)", &path);
636 /* Add user if we haven't got them */
637 user = get_user_by_path (user_list, path);
639 add_accounts_user (user_list, path, TRUE);
643 accounts_user_deleted_cb (GDBusConnection *connection,
644 const gchar *sender_name,
645 const gchar *object_path,
646 const gchar *interface_name,
647 const gchar *signal_name,
648 GVariant *parameters,
651 CommonUserList *user_list = data;
652 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
656 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
658 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
662 g_variant_get (parameters, "(&o)", &path);
664 /* Delete user if we know of them */
665 user = get_user_by_path (user_list, path);
668 g_debug ("User %s deleted", path);
669 priv->users = g_list_remove (priv->users, user);
671 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
673 g_object_unref (user);
677 static CommonSession *
678 load_session (CommonUserList *user_list, const gchar *path)
680 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
681 CommonSession *session = NULL;
682 GVariant *result, *username;
683 GError *error = NULL;
685 result = g_dbus_connection_call_sync (priv->bus,
686 "org.freedesktop.DisplayManager",
688 "org.freedesktop.DBus.Properties",
690 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
691 G_VARIANT_TYPE ("(v)"),
692 G_DBUS_CALL_FLAGS_NONE,
697 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
698 g_clear_error (&error);
702 g_variant_get (result, "(v)", &username);
703 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
707 g_variant_get (username, "&s", &name);
709 g_debug ("Loaded session %s (%s)", path, name);
710 session = g_object_new (common_session_get_type (), NULL);
711 session->username = g_strdup (name);
712 session->path = g_strdup (path);
713 priv->sessions = g_list_append (priv->sessions, session);
715 g_variant_unref (username);
716 g_variant_unref (result);
722 session_added_cb (GDBusConnection *connection,
723 const gchar *sender_name,
724 const gchar *object_path,
725 const gchar *interface_name,
726 const gchar *signal_name,
727 GVariant *parameters,
730 CommonUserList *user_list = data;
732 CommonSession *session;
733 CommonUser *user = NULL;
735 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
737 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
741 g_variant_get (parameters, "(&o)", &path);
742 session = load_session (user_list, path);
744 user = get_user_by_name (user_list, session->username);
746 g_signal_emit (user, user_signals[CHANGED], 0);
750 session_removed_cb (GDBusConnection *connection,
751 const gchar *sender_name,
752 const gchar *object_path,
753 const gchar *interface_name,
754 const gchar *signal_name,
755 GVariant *parameters,
758 CommonUserList *user_list = data;
759 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
763 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
765 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
769 g_variant_get (parameters, "(&o)", &path);
771 for (link = priv->sessions; link; link = link->next)
773 CommonSession *session = link->data;
774 if (strcmp (session->path, path) == 0)
778 g_debug ("Session %s removed", path);
779 priv->sessions = g_list_delete_link (priv->sessions, link);
780 user = get_user_by_name (user_list, session->username);
782 g_signal_emit (user, user_signals[CHANGED], 0);
783 g_object_unref (session);
790 load_sessions (CommonUserList *user_list)
792 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
794 GError *error = NULL;
796 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
797 "org.freedesktop.DisplayManager",
798 "org.freedesktop.DisplayManager",
800 "/org/freedesktop/DisplayManager",
802 G_DBUS_SIGNAL_FLAGS_NONE,
806 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
807 "org.freedesktop.DisplayManager",
808 "org.freedesktop.DisplayManager",
810 "/org/freedesktop/DisplayManager",
812 G_DBUS_SIGNAL_FLAGS_NONE,
816 result = g_dbus_connection_call_sync (priv->bus,
817 "org.freedesktop.DisplayManager",
818 "/org/freedesktop/DisplayManager",
819 "org.freedesktop.DBus.Properties",
821 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
822 G_VARIANT_TYPE ("(v)"),
823 G_DBUS_CALL_FLAGS_NONE,
828 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
829 g_clear_error (&error);
832 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
838 g_variant_get (result, "(v)", &value);
840 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
841 g_variant_get (value, "ao", &iter);
842 while (g_variant_iter_loop (iter, "&o", &path))
843 load_session (user_list, path);
844 g_variant_iter_free (iter);
846 g_variant_unref (value);
849 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
851 g_variant_unref (result);
856 load_users (CommonUserList *user_list)
858 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
860 GError *error = NULL;
862 if (priv->have_users)
864 priv->have_users = TRUE;
866 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
867 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
868 "org.freedesktop.Accounts",
869 "org.freedesktop.Accounts",
871 "/org/freedesktop/Accounts",
873 G_DBUS_SIGNAL_FLAGS_NONE,
874 accounts_user_added_cb,
877 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
878 "org.freedesktop.Accounts",
879 "org.freedesktop.Accounts",
881 "/org/freedesktop/Accounts",
883 G_DBUS_SIGNAL_FLAGS_NONE,
884 accounts_user_deleted_cb,
887 result = g_dbus_connection_call_sync (priv->bus,
888 "org.freedesktop.Accounts",
889 "/org/freedesktop/Accounts",
890 "org.freedesktop.Accounts",
892 g_variant_new ("()"),
893 G_VARIANT_TYPE ("(ao)"),
894 G_DBUS_CALL_FLAGS_NONE,
899 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
900 g_clear_error (&error);
906 g_debug ("Loading users from org.freedesktop.Accounts");
907 g_variant_get (result, "(ao)", &iter);
908 while (g_variant_iter_loop (iter, "&o", &path))
909 add_accounts_user (user_list, path, FALSE);
910 g_variant_iter_free (iter);
911 g_variant_unref (result);
917 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
918 priv->user_added_signal = 0;
919 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
920 priv->user_removed_signal = 0;
922 load_passwd_file (user_list, FALSE);
924 /* Watch for changes to user list */
926 passwd_file = g_file_new_for_path (PASSWD_FILE);
927 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
928 g_object_unref (passwd_file);
930 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
932 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
933 g_clear_error (&error);
938 * common_user_list_get_length:
939 * @user_list: a #CommonUserList
941 * Return value: The number of users able to log in
944 common_user_list_get_length (CommonUserList *user_list)
946 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
947 load_users (user_list);
948 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
952 * common_user_list_get_users:
953 * @user_list: A #CommonUserList
955 * Get a list of users to present to the user. This list may be a subset of the
956 * available users and may be empty depending on the server configuration.
958 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
961 common_user_list_get_users (CommonUserList *user_list)
963 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
964 load_users (user_list);
965 return GET_LIST_PRIVATE (user_list)->users;
969 * common_user_list_get_user_by_name:
970 * @user_list: A #CommonUserList
971 * @username: Name of user to get.
973 * Get infomation about a given user or #NULL if this user doesn't exist.
974 * Includes hidden and system users, unlike the list from
975 * common_user_list_get_users.
977 * Return value: (transfer full): A #CommonUser entry for the given user.
980 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
982 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
983 g_return_val_if_fail (username != NULL, NULL);
985 load_users (user_list);
987 CommonUser *user = get_user_by_name (user_list, username);
989 return g_object_ref (user);
991 /* Sometimes we need to look up users that aren't in AccountsService.
992 Notably we need to look up the user that the greeter runs as, which
993 is usually 'lightdm'. For such cases, we manually create a one-off
994 CommonUser object and pre-seed with passwd info. */
995 struct passwd *entry = getpwnam (username);
997 return make_passwd_user (user_list, entry);
1003 common_user_list_init (CommonUserList *user_list)
1005 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1007 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1011 common_user_list_set_property (GObject *object,
1013 const GValue *value,
1016 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1020 common_user_list_get_property (GObject *object,
1025 CommonUserList *self;
1027 self = COMMON_USER_LIST (object);
1031 case LIST_PROP_NUM_USERS:
1032 g_value_set_int (value, common_user_list_get_length (self));
1035 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1041 common_user_list_finalize (GObject *object)
1043 CommonUserList *self = COMMON_USER_LIST (object);
1044 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1046 /* Remove children first, they might access us */
1047 g_list_free_full (priv->users, g_object_unref);
1048 g_list_free_full (priv->sessions, g_object_unref);
1050 if (priv->user_added_signal)
1051 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1052 if (priv->user_removed_signal)
1053 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1054 if (priv->session_added_signal)
1055 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1056 if (priv->session_removed_signal)
1057 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1058 g_object_unref (priv->bus);
1059 if (priv->passwd_monitor)
1060 g_object_unref (priv->passwd_monitor);
1062 G_OBJECT_CLASS (common_user_list_parent_class)->finalize (object);
1066 common_user_list_class_init (CommonUserListClass *klass)
1068 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1070 g_type_class_add_private (klass, sizeof (CommonUserListPrivate));
1072 object_class->set_property = common_user_list_set_property;
1073 object_class->get_property = common_user_list_get_property;
1074 object_class->finalize = common_user_list_finalize;
1076 g_object_class_install_property (object_class,
1077 LIST_PROP_NUM_USERS,
1078 g_param_spec_int ("num-users",
1080 "Number of login users",
1084 * CommonUserList::user-added:
1085 * @user_list: A #CommonUserList
1086 * @user: The #CommonUser that has been added.
1088 * The ::user-added signal gets emitted when a user account is created.
1090 list_signals[USER_ADDED] =
1091 g_signal_new ("user-added",
1092 G_TYPE_FROM_CLASS (klass),
1094 G_STRUCT_OFFSET (CommonUserListClass, user_added),
1097 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1100 * CommonUserList::user-changed:
1101 * @user_list: A #CommonUserList
1102 * @user: The #CommonUser that has been changed.
1104 * The ::user-changed signal gets emitted when a user account is modified.
1106 list_signals[USER_CHANGED] =
1107 g_signal_new ("user-changed",
1108 G_TYPE_FROM_CLASS (klass),
1110 G_STRUCT_OFFSET (CommonUserListClass, user_changed),
1113 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1116 * CommonUserList::user-removed:
1117 * @user_list: A #CommonUserList
1118 * @user: The #CommonUser that has been removed.
1120 * The ::user-removed signal gets emitted when a user account is removed.
1122 list_signals[USER_REMOVED] =
1123 g_signal_new ("user-removed",
1124 G_TYPE_FROM_CLASS (klass),
1126 G_STRUCT_OFFSET (CommonUserListClass, user_removed),
1129 G_TYPE_NONE, 1, COMMON_TYPE_USER);
1133 call_method (CommonUser *user, const gchar *method, GVariant *args,
1134 const gchar *expected, GVariant **result)
1137 GError *error = NULL;
1138 CommonUserPrivate *user_priv = GET_USER_PRIVATE (user);
1139 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_priv->user_list);
1141 answer = g_dbus_connection_call_sync (list_priv->bus,
1142 "org.freedesktop.Accounts",
1144 "org.freedesktop.Accounts.User",
1147 G_VARIANT_TYPE (expected),
1148 G_DBUS_CALL_FLAGS_NONE,
1153 g_warning ("Could not call %s: %s", method, error->message);
1154 g_clear_error (&error);
1162 g_variant_unref (answer);
1168 save_string_to_dmrc (CommonUser *user, const gchar *group,
1169 const gchar *key, const gchar *value)
1173 dmrc = dmrc_load (user);
1174 g_key_file_set_string (dmrc, group, key, value);
1175 dmrc_save (dmrc, user);
1177 g_key_file_free (dmrc);
1180 /* Loads language/layout/session info for user */
1182 load_dmrc (CommonUser *user)
1184 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1187 /* We're using Accounts service instead */
1191 if (priv->loaded_dmrc)
1193 priv->loaded_dmrc = TRUE;
1194 dmrc = dmrc_load (user);
1196 // FIXME: Watch for changes
1198 /* The Language field contains the locale */
1199 g_free (priv->language);
1200 priv->language = g_key_file_get_string (dmrc, "Desktop", "Language", NULL);
1202 if (g_key_file_has_key (dmrc, "Desktop", "Layout", NULL))
1204 g_strfreev (priv->layouts);
1205 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1206 priv->layouts[0] = g_key_file_get_string (dmrc, "Desktop", "Layout", NULL);
1207 priv->layouts[1] = NULL;
1210 g_free (priv->session);
1211 priv->session = g_key_file_get_string (dmrc, "Desktop", "Session", NULL);
1213 g_key_file_free (dmrc);
1217 * common_user_get_name:
1218 * @user: A #CommonUser
1220 * Get the name of a user.
1222 * Return value: The name of the given user
1225 common_user_get_name (CommonUser *user)
1227 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1228 return GET_USER_PRIVATE (user)->name;
1232 * common_user_get_real_name:
1233 * @user: A #CommonUser
1235 * Get the real name of a user.
1237 * Return value: The real name of the given user
1240 common_user_get_real_name (CommonUser *user)
1242 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1243 return GET_USER_PRIVATE (user)->real_name;
1247 * common_user_get_display_name:
1248 * @user: A #CommonUser
1250 * Get the display name of a user.
1252 * Return value: The display name of the given user
1255 common_user_get_display_name (CommonUser *user)
1257 CommonUserPrivate *priv;
1259 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1261 priv = GET_USER_PRIVATE (user);
1262 if (!priv->real_name || strcmp (priv->real_name, "") == 0)
1265 return priv->real_name;
1269 * common_user_get_home_directory:
1270 * @user: A #CommonUser
1272 * Get the home directory for a user.
1274 * Return value: The users home directory
1277 common_user_get_home_directory (CommonUser *user)
1279 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1280 return GET_USER_PRIVATE (user)->home_directory;
1284 * common_user_get_shell:
1285 * @user: A #CommonUser
1287 * Get the shell for a user.
1289 * Return value: The user's shell
1292 common_user_get_shell (CommonUser *user)
1294 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1295 return GET_USER_PRIVATE (user)->shell;
1299 * common_user_get_image:
1300 * @user: A #CommonUser
1302 * Get the image URI for a user.
1304 * Return value: The image URI for the given user or #NULL if no URI
1307 common_user_get_image (CommonUser *user)
1309 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1310 return GET_USER_PRIVATE (user)->image;
1314 * common_user_get_background:
1315 * @user: A #CommonUser
1317 * Get the background file path for a user.
1319 * Return value: The background file path for the given user or #NULL if no path
1322 common_user_get_background (CommonUser *user)
1324 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1325 return GET_USER_PRIVATE (user)->background;
1329 * common_user_get_language:
1330 * @user: A #CommonUser
1332 * Get the language for a user.
1334 * 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.
1337 common_user_get_language (CommonUser *user)
1339 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1341 const gchar *language = GET_USER_PRIVATE (user)->language;
1342 return (language && language[0] == 0) ? NULL : language; /* Treat "" as NULL */
1346 * common_user_set_language:
1347 * @user: A #CommonUser
1348 * @language: The user's new language
1350 * Set the language for a user.
1353 common_user_set_language (CommonUser *user, const gchar *language)
1355 g_return_if_fail (COMMON_IS_USER (user));
1356 if (g_strcmp0 (common_user_get_language (user), language) != 0)
1358 call_method (user, "SetLanguage", g_variant_new ("(s)", language), "()", NULL);
1359 save_string_to_dmrc (user, "Desktop", "Language", language);
1364 * common_user_get_layout:
1365 * @user: A #CommonUser
1367 * Get the keyboard layout for a user.
1369 * 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.
1372 common_user_get_layout (CommonUser *user)
1374 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1376 return GET_USER_PRIVATE (user)->layouts[0];
1380 * common_user_get_layouts:
1381 * @user: A #CommonUser
1383 * Get the configured keyboard layouts for a user.
1385 * 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.
1387 const gchar * const *
1388 common_user_get_layouts (CommonUser *user)
1390 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1392 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1396 * common_user_get_session:
1397 * @user: A #CommonUser
1399 * Get the session for a user.
1401 * Return value: The session for the given user or #NULL if using system defaults.
1404 common_user_get_session (CommonUser *user)
1406 g_return_val_if_fail (COMMON_IS_USER (user), NULL);
1408 const gchar *session = GET_USER_PRIVATE (user)->session;
1409 return (session && session[0] == 0) ? NULL : session; /* Treat "" as NULL */
1413 * common_user_set_session:
1414 * @user: A #CommonUser
1415 * @language: The user's new session
1417 * Set the session for a user.
1420 common_user_set_session (CommonUser *user, const gchar *session)
1422 g_return_if_fail (COMMON_IS_USER (user));
1423 if (g_strcmp0 (common_user_get_session (user), session) != 0)
1425 call_method (user, "SetXSession", g_variant_new ("(s)", session), "()", NULL);
1426 save_string_to_dmrc (user, "Desktop", "Session", session);
1431 * common_user_get_logged_in:
1432 * @user: A #CommonUser
1434 * Check if a user is logged in.
1436 * Return value: #TRUE if the user is currently logged in.
1439 common_user_get_logged_in (CommonUser *user)
1441 CommonUserPrivate *priv;
1442 CommonUserListPrivate *list_priv;
1445 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1447 priv = GET_USER_PRIVATE (user);
1448 list_priv = GET_LIST_PRIVATE (priv->user_list);
1450 // Lazily decide to load/listen to sessions
1451 if (list_priv->session_added_signal == 0)
1452 load_sessions (priv->user_list);
1454 for (link = list_priv->sessions; link; link = link->next)
1456 CommonSession *session = link->data;
1457 if (strcmp (session->username, priv->name) == 0)
1465 * common_user_get_has_messages:
1466 * @user: A #CommonUser
1468 * Check if a user has waiting messages.
1470 * Return value: #TRUE if the user has waiting messages.
1473 common_user_get_has_messages (CommonUser *user)
1475 g_return_val_if_fail (COMMON_IS_USER (user), FALSE);
1476 return GET_USER_PRIVATE (user)->has_messages;
1480 * common_user_get_uid:
1481 * @user: A #CommonUser
1483 * Get the uid of a user
1485 * Return value: The user's uid
1488 common_user_get_uid (CommonUser *user)
1490 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1491 return GET_USER_PRIVATE (user)->uid;
1495 * common_user_get_gid:
1496 * @user: A #CommonUser
1498 * Get the gid of a user
1500 * Return value: The user's gid
1503 common_user_get_gid (CommonUser *user)
1505 g_return_val_if_fail (COMMON_IS_USER (user), 0);
1506 /* gid is not actually stored in AccountsService, so if our user is from
1507 AccountsService, we have to look up manually in passwd. gid won't
1508 change, so just look up the first time we're asked and never again. */
1509 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1510 if (priv->uid != 0 && priv->gid == 0)
1512 struct passwd *entry = getpwuid (priv->uid);
1514 priv->gid = entry->pw_gid;
1520 common_user_init (CommonUser *user)
1522 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
1523 priv->layouts = g_malloc (sizeof (gchar *) * 1);
1524 priv->layouts[0] = NULL;
1528 common_user_set_property (GObject *object,
1530 const GValue *value,
1533 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1537 common_user_get_property (GObject *object,
1544 self = COMMON_USER (object);
1548 case USER_PROP_NAME:
1549 g_value_set_string (value, common_user_get_name (self));
1551 case USER_PROP_REAL_NAME:
1552 g_value_set_string (value, common_user_get_real_name (self));
1554 case USER_PROP_DISPLAY_NAME:
1555 g_value_set_string (value, common_user_get_display_name (self));
1557 case USER_PROP_HOME_DIRECTORY:
1558 g_value_set_string (value, common_user_get_home_directory (self));
1560 case USER_PROP_SHELL:
1561 g_value_set_string (value, common_user_get_shell (self));
1563 case USER_PROP_IMAGE:
1564 g_value_set_string (value, common_user_get_image (self));
1566 case USER_PROP_BACKGROUND:
1567 g_value_set_string (value, common_user_get_background (self));
1569 case USER_PROP_LANGUAGE:
1570 g_value_set_string (value, common_user_get_language (self));
1572 case USER_PROP_LAYOUT:
1573 g_value_set_string (value, common_user_get_layout (self));
1575 case USER_PROP_LAYOUTS:
1576 g_value_set_boxed (value, g_strdupv ((gchar **) common_user_get_layouts (self)));
1578 case USER_PROP_SESSION:
1579 g_value_set_string (value, common_user_get_session (self));
1581 case USER_PROP_LOGGED_IN:
1582 g_value_set_boolean (value, common_user_get_logged_in (self));
1584 case USER_PROP_HAS_MESSAGES:
1585 g_value_set_boolean (value, common_user_get_has_messages (self));
1588 g_value_set_uint64 (value, common_user_get_uid (self));
1591 g_value_set_uint64 (value, common_user_get_gid (self));
1594 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1600 common_user_finalize (GObject *object)
1602 CommonUser *self = COMMON_USER (object);
1603 CommonUserPrivate *priv = GET_USER_PRIVATE (self);
1605 g_free (priv->path);
1606 if (priv->changed_signal)
1607 g_dbus_connection_signal_unsubscribe (GET_LIST_PRIVATE (priv->user_list)->bus, priv->changed_signal);
1608 g_free (priv->name);
1609 g_free (priv->real_name);
1610 g_free (priv->home_directory);
1611 g_free (priv->shell);
1612 g_free (priv->image);
1613 g_free (priv->background);
1614 g_free (priv->language);
1615 g_strfreev (priv->layouts);
1616 g_free (priv->session);
1620 common_user_class_init (CommonUserClass *klass)
1622 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1624 g_type_class_add_private (klass, sizeof (CommonUserPrivate));
1626 object_class->set_property = common_user_set_property;
1627 object_class->get_property = common_user_get_property;
1628 object_class->finalize = common_user_finalize;
1630 g_object_class_install_property (object_class,
1632 g_param_spec_string ("name",
1636 G_PARAM_READWRITE));
1637 g_object_class_install_property (object_class,
1638 USER_PROP_REAL_NAME,
1639 g_param_spec_string ("real-name",
1643 G_PARAM_READWRITE));
1644 g_object_class_install_property (object_class,
1645 USER_PROP_DISPLAY_NAME,
1646 g_param_spec_string ("display-name",
1648 "Users display name",
1651 g_object_class_install_property (object_class,
1652 USER_PROP_HOME_DIRECTORY,
1653 g_param_spec_string ("home-directory",
1657 G_PARAM_READWRITE));
1658 g_object_class_install_property (object_class,
1660 g_param_spec_string ("shell",
1664 G_PARAM_READWRITE));
1665 g_object_class_install_property (object_class,
1667 g_param_spec_string ("image",
1671 G_PARAM_READWRITE));
1672 g_object_class_install_property (object_class,
1673 USER_PROP_BACKGROUND,
1674 g_param_spec_string ("background",
1678 G_PARAM_READWRITE));
1679 g_object_class_install_property (object_class,
1681 g_param_spec_string ("language",
1683 "Language used by this user",
1686 g_object_class_install_property (object_class,
1688 g_param_spec_string ("layout",
1690 "Keyboard layout used by this user",
1693 g_object_class_install_property (object_class,
1695 g_param_spec_boxed ("layouts",
1697 "Keyboard layouts used by this user",
1700 g_object_class_install_property (object_class,
1702 g_param_spec_string ("session",
1704 "Session used by this user",
1707 g_object_class_install_property (object_class,
1708 USER_PROP_LOGGED_IN,
1709 g_param_spec_boolean ("logged-in",
1711 "TRUE if the user is currently in a session",
1713 G_PARAM_READWRITE));
1714 g_object_class_install_property (object_class,
1715 USER_PROP_LOGGED_IN,
1716 g_param_spec_boolean ("has-messages",
1718 "TRUE if the user is has waiting messages",
1720 G_PARAM_READWRITE));
1721 g_object_class_install_property (object_class,
1723 g_param_spec_uint64 ("uid",
1729 G_PARAM_READWRITE));
1730 g_object_class_install_property (object_class,
1732 g_param_spec_uint64 ("gd",
1738 G_PARAM_READWRITE));
1741 * CommonUser::changed:
1742 * @user: A #CommonUser
1744 * The ::changed signal gets emitted this user account is modified.
1746 user_signals[CHANGED] =
1747 g_signal_new ("changed",
1748 G_TYPE_FROM_CLASS (klass),
1750 G_STRUCT_OFFSET (CommonUserClass, changed),
1757 common_session_init (CommonSession *common_session)
1762 common_session_finalize (GObject *object)
1764 CommonSession *self = COMMON_SESSION (object);
1766 g_free (self->path);
1767 g_free (self->username);
1771 common_session_class_init (CommonSessionClass *klass)
1773 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1774 object_class->finalize = common_session_finalize;