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)
189 g_clear_object (&singleton);
193 get_user_by_name (CommonUserList *user_list, const gchar *username)
195 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
198 for (link = priv->users; link; link = link->next)
200 CommonUser *user = link->data;
201 if (g_strcmp0 (common_user_get_name (user), username) == 0)
209 get_user_by_path (CommonUserList *user_list, const gchar *path)
211 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
214 for (link = priv->users; link; link = link->next)
216 CommonUser *user = link->data;
217 if (g_strcmp0 (GET_USER_PRIVATE (user)->path, path) == 0)
225 compare_user (gconstpointer a, gconstpointer b)
227 CommonUser *user_a = (CommonUser *) a, *user_b = (CommonUser *) b;
228 return g_strcmp0 (common_user_get_display_name (user_a), common_user_get_display_name (user_b));
232 update_passwd_user (CommonUser *user, const gchar *real_name, const gchar *home_directory, const gchar *shell, const gchar *image)
234 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
236 /* Skip if already set to this */
237 if (g_strcmp0 (common_user_get_real_name (user), real_name) == 0 &&
238 g_strcmp0 (common_user_get_home_directory (user), home_directory) == 0 &&
239 g_strcmp0 (common_user_get_shell (user), shell) == 0 &&
240 g_strcmp0 (common_user_get_image (user), image) == 0)
243 g_free (priv->real_name);
244 priv->real_name = g_strdup (real_name);
245 g_free (priv->home_directory);
246 priv->home_directory = g_strdup (home_directory);
247 g_free (priv->shell);
248 priv->shell = g_strdup (shell);
249 g_free (priv->image);
250 priv->image = g_strdup (image);
256 user_changed_cb (CommonUser *user)
258 g_signal_emit (GET_USER_PRIVATE (user)->user_list, list_signals[USER_CHANGED], 0, user);
262 make_passwd_user (CommonUserList *user_list, struct passwd *entry)
264 CommonUser *user = g_object_new (COMMON_TYPE_USER, NULL);
265 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
267 gchar *real_name, *image;
269 tokens = g_strsplit (entry->pw_gecos, ",", -1);
270 if (tokens[0] != NULL && tokens[0][0] != '\0')
271 real_name = g_strdup (tokens[0]);
273 real_name = g_strdup ("");
276 image = g_build_filename (entry->pw_dir, ".face", NULL);
277 if (!g_file_test (image, G_FILE_TEST_EXISTS))
280 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
281 if (!g_file_test (image, G_FILE_TEST_EXISTS))
288 priv->user_list = user_list;
289 priv->name = g_strdup (entry->pw_name);
290 priv->real_name = real_name;
291 priv->home_directory = g_strdup (entry->pw_dir);
292 priv->shell = g_strdup (entry->pw_shell);
294 priv->uid = entry->pw_uid;
295 priv->gid = entry->pw_gid;
301 load_passwd_file (CommonUserList *user_list, gboolean emit_add_signal)
303 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
307 gchar **hidden_users, **hidden_shells;
308 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
309 GError *error = NULL;
311 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
313 config = g_key_file_new ();
314 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
315 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
316 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message);
317 g_clear_error (&error);
319 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
320 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
324 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
326 value = g_strdup ("nobody nobody4 noaccess");
327 hidden_users = g_strsplit (value, " ", -1);
330 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
332 value = g_strdup ("/bin/false /usr/sbin/nologin");
333 hidden_shells = g_strsplit (value, " ", -1);
336 g_key_file_free (config);
342 struct passwd *entry;
351 /* Ignore system users */
352 if (entry->pw_uid < minimum_uid)
355 /* Ignore users disabled by shell */
358 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
359 if (hidden_shells[i])
363 /* Ignore certain users */
364 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
368 user = make_passwd_user (user_list, entry);
370 /* Update existing users if have them */
371 for (link = priv->users; link; link = link->next)
373 CommonUser *info = link->data;
374 if (strcmp (common_user_get_name (info), common_user_get_name (user)) == 0)
376 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)))
377 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
378 g_object_unref (user);
385 /* Only notify once we have loaded the user list */
386 if (priv->have_users)
387 new_users = g_list_insert_sorted (new_users, user, compare_user);
389 users = g_list_insert_sorted (users, user, compare_user);
391 g_strfreev (hidden_users);
392 g_strfreev (hidden_shells);
395 g_warning ("Failed to read password database: %s", strerror (errno));
399 /* Use new user list */
400 old_users = priv->users;
403 /* Notify of changes */
404 for (link = new_users; link; link = link->next)
406 CommonUser *info = link->data;
407 g_debug ("User %s added", common_user_get_name (info));
408 g_signal_connect (info, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), NULL);
410 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
412 g_list_free (new_users);
413 for (link = changed_users; link; link = link->next)
415 CommonUser *info = link->data;
416 g_debug ("User %s changed", common_user_get_name (info));
417 g_signal_emit (info, user_signals[CHANGED], 0);
419 g_list_free (changed_users);
420 for (link = old_users; link; link = link->next)
424 /* See if this user is in the current list */
425 for (new_link = priv->users; new_link; new_link = new_link->next)
427 if (new_link->data == link->data)
433 CommonUser *info = link->data;
434 g_debug ("User %s removed", common_user_get_name (info));
435 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
436 g_object_unref (info);
439 g_list_free (old_users);
443 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, CommonUserList *user_list)
445 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
447 g_debug ("%s changed, reloading user list", g_file_get_path (file));
448 load_passwd_file (user_list, TRUE);
452 static gboolean load_accounts_user (CommonUser *user);
455 accounts_user_changed_cb (GDBusConnection *connection,
456 const gchar *sender_name,
457 const gchar *object_path,
458 const gchar *interface_name,
459 const gchar *signal_name,
460 GVariant *parameters,
463 CommonUser *user = data;
464 /*CommonUserPrivate *priv = GET_USER_PRIVATE (user);*/
466 /* Log message disabled as AccountsService can have arbitrary plugins that
467 * might cause us to log when properties change we don't use. LP: #1376357
469 /*g_debug ("User %s changed", priv->path);*/
470 if (load_accounts_user (user))
471 g_signal_emit (user, user_signals[CHANGED], 0);
475 load_accounts_user (CommonUser *user)
477 CommonUserPrivate *priv = GET_USER_PRIVATE (user);
478 GVariant *result, *value;
481 gboolean system_account = FALSE;
482 GError *error = NULL;
484 /* Get the properties for this user */
485 if (!priv->changed_signal)
486 priv->changed_signal = g_dbus_connection_signal_subscribe (GET_LIST_PRIVATE (priv->user_list)->bus,
487 "org.freedesktop.Accounts",
488 "org.freedesktop.Accounts.User",
492 G_DBUS_SIGNAL_FLAGS_NONE,
493 accounts_user_changed_cb,
496 result = g_dbus_connection_call_sync (GET_LIST_PRIVATE (priv->user_list)->bus,
497 "org.freedesktop.Accounts",
499 "org.freedesktop.DBus.Properties",
501 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
502 G_VARIANT_TYPE ("(a{sv})"),
503 G_DBUS_CALL_FLAGS_NONE,
508 g_warning ("Error updating user %s: %s", priv->path, error->message);
509 g_clear_error (&error);
513 /* Store the properties we need */
514 g_variant_get (result, "(a{sv})", &iter);
515 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
517 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
520 priv->name = g_variant_dup_string (value, NULL);
522 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
524 g_free (priv->real_name);
525 priv->real_name = g_variant_dup_string (value, NULL);
527 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
529 g_free (priv->home_directory);
530 priv->home_directory = g_variant_dup_string (value, NULL);
532 else if (strcmp (name, "Shell") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
534 g_free (priv->shell);
535 priv->shell = g_variant_dup_string (value, NULL);
537 else if (strcmp (name, "SystemAccount") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
538 system_account = g_variant_get_boolean (value);
539 else if (strcmp (name, "Language") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
542 g_free (priv->language);
543 priv->language = g_variant_dup_string (value, NULL);
545 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
547 g_free (priv->image);
548 priv->image = g_variant_dup_string (value, NULL);
549 if (strcmp (priv->image, "") == 0)
551 g_free (priv->image);
555 else if (strcmp (name, "XSession") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
557 g_free (priv->session);
558 priv->session = g_variant_dup_string (value, NULL);
560 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
562 g_free (priv->background);
563 priv->background = g_variant_dup_string (value, NULL);
564 if (strcmp (priv->background, "") == 0)
566 g_free (priv->background);
567 priv->background = NULL;
570 else if (strcmp (name, "XKeyboardLayouts") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING_ARRAY))
572 g_strfreev (priv->layouts);
573 priv->layouts = g_variant_dup_strv (value, NULL);
576 priv->layouts = g_malloc (sizeof (gchar *) * 1);
577 priv->layouts[0] = NULL;
580 else if (strcmp (name, "XHasMessages") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN))
581 priv->has_messages = g_variant_get_boolean (value);
582 else if (strcmp (name, "Uid") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_UINT64))
583 priv->uid = g_variant_get_uint64 (value);
585 g_variant_iter_free (iter);
587 g_variant_unref (result);
589 return !system_account;
593 add_accounts_user (CommonUserList *user_list, const gchar *path, gboolean emit_signal)
595 CommonUserListPrivate *list_priv = GET_LIST_PRIVATE (user_list);
597 CommonUserPrivate *priv;
599 user = g_object_new (COMMON_TYPE_USER, NULL);
600 priv = GET_USER_PRIVATE (user);
602 g_debug ("User %s added", path);
603 priv->user_list = user_list;
604 priv->path = g_strdup (path);
605 g_signal_connect (user, USER_SIGNAL_CHANGED, G_CALLBACK (user_changed_cb), NULL);
606 if (load_accounts_user (user))
608 list_priv->users = g_list_insert_sorted (list_priv->users, user, compare_user);
610 g_signal_emit (user_list, list_signals[USER_ADDED], 0, user);
613 g_object_unref (user);
617 accounts_user_added_cb (GDBusConnection *connection,
618 const gchar *sender_name,
619 const gchar *object_path,
620 const gchar *interface_name,
621 const gchar *signal_name,
622 GVariant *parameters,
625 CommonUserList *user_list = data;
629 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
631 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
635 g_variant_get (parameters, "(&o)", &path);
637 /* Add user if we haven't got them */
638 user = get_user_by_path (user_list, path);
640 add_accounts_user (user_list, path, TRUE);
644 accounts_user_deleted_cb (GDBusConnection *connection,
645 const gchar *sender_name,
646 const gchar *object_path,
647 const gchar *interface_name,
648 const gchar *signal_name,
649 GVariant *parameters,
652 CommonUserList *user_list = data;
653 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
657 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
659 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
663 g_variant_get (parameters, "(&o)", &path);
665 /* Delete user if we know of them */
666 user = get_user_by_path (user_list, path);
669 g_debug ("User %s deleted", path);
670 priv->users = g_list_remove (priv->users, user);
672 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, user);
674 g_object_unref (user);
678 static CommonSession *
679 load_session (CommonUserList *user_list, const gchar *path)
681 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
682 CommonSession *session = NULL;
683 GVariant *result, *username;
684 GError *error = NULL;
686 result = g_dbus_connection_call_sync (priv->bus,
687 "org.freedesktop.DisplayManager",
689 "org.freedesktop.DBus.Properties",
691 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
692 G_VARIANT_TYPE ("(v)"),
693 G_DBUS_CALL_FLAGS_NONE,
698 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
699 g_clear_error (&error);
703 g_variant_get (result, "(v)", &username);
704 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
708 g_variant_get (username, "&s", &name);
710 g_debug ("Loaded session %s (%s)", path, name);
711 session = g_object_new (common_session_get_type (), NULL);
712 session->username = g_strdup (name);
713 session->path = g_strdup (path);
714 priv->sessions = g_list_append (priv->sessions, session);
716 g_variant_unref (username);
717 g_variant_unref (result);
723 session_added_cb (GDBusConnection *connection,
724 const gchar *sender_name,
725 const gchar *object_path,
726 const gchar *interface_name,
727 const gchar *signal_name,
728 GVariant *parameters,
731 CommonUserList *user_list = data;
733 CommonSession *session;
734 CommonUser *user = NULL;
736 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
738 g_warning ("Got DisplayManager signal SessionAdded with unknown parameters %s", g_variant_get_type_string (parameters));
742 g_variant_get (parameters, "(&o)", &path);
743 session = load_session (user_list, path);
745 user = get_user_by_name (user_list, session->username);
747 g_signal_emit (user, user_signals[CHANGED], 0);
751 session_removed_cb (GDBusConnection *connection,
752 const gchar *sender_name,
753 const gchar *object_path,
754 const gchar *interface_name,
755 const gchar *signal_name,
756 GVariant *parameters,
759 CommonUserList *user_list = data;
760 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
764 if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
766 g_warning ("Got DisplayManager signal SessionRemoved with unknown parameters %s", g_variant_get_type_string (parameters));
770 g_variant_get (parameters, "(&o)", &path);
772 for (link = priv->sessions; link; link = link->next)
774 CommonSession *session = link->data;
775 if (strcmp (session->path, path) == 0)
779 g_debug ("Session %s removed", path);
780 priv->sessions = g_list_delete_link (priv->sessions, link);
781 user = get_user_by_name (user_list, session->username);
783 g_signal_emit (user, user_signals[CHANGED], 0);
784 g_object_unref (session);
791 load_sessions (CommonUserList *user_list)
793 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
795 GError *error = NULL;
797 priv->session_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
798 "org.freedesktop.DisplayManager",
799 "org.freedesktop.DisplayManager",
801 "/org/freedesktop/DisplayManager",
803 G_DBUS_SIGNAL_FLAGS_NONE,
807 priv->session_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
808 "org.freedesktop.DisplayManager",
809 "org.freedesktop.DisplayManager",
811 "/org/freedesktop/DisplayManager",
813 G_DBUS_SIGNAL_FLAGS_NONE,
817 result = g_dbus_connection_call_sync (priv->bus,
818 "org.freedesktop.DisplayManager",
819 "/org/freedesktop/DisplayManager",
820 "org.freedesktop.DBus.Properties",
822 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
823 G_VARIANT_TYPE ("(v)"),
824 G_DBUS_CALL_FLAGS_NONE,
829 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
830 g_clear_error (&error);
833 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
839 g_variant_get (result, "(v)", &value);
841 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
842 g_variant_get (value, "ao", &iter);
843 while (g_variant_iter_loop (iter, "&o", &path))
844 load_session (user_list, path);
845 g_variant_iter_free (iter);
847 g_variant_unref (value);
850 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
852 g_variant_unref (result);
857 load_users (CommonUserList *user_list)
859 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
861 GError *error = NULL;
863 if (priv->have_users)
865 priv->have_users = TRUE;
867 /* Get user list from accounts service and fall back to /etc/passwd if that fails */
868 priv->user_added_signal = g_dbus_connection_signal_subscribe (priv->bus,
869 "org.freedesktop.Accounts",
870 "org.freedesktop.Accounts",
872 "/org/freedesktop/Accounts",
874 G_DBUS_SIGNAL_FLAGS_NONE,
875 accounts_user_added_cb,
878 priv->user_removed_signal = g_dbus_connection_signal_subscribe (priv->bus,
879 "org.freedesktop.Accounts",
880 "org.freedesktop.Accounts",
882 "/org/freedesktop/Accounts",
884 G_DBUS_SIGNAL_FLAGS_NONE,
885 accounts_user_deleted_cb,
888 result = g_dbus_connection_call_sync (priv->bus,
889 "org.freedesktop.Accounts",
890 "/org/freedesktop/Accounts",
891 "org.freedesktop.Accounts",
893 g_variant_new ("()"),
894 G_VARIANT_TYPE ("(ao)"),
895 G_DBUS_CALL_FLAGS_NONE,
900 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
901 g_clear_error (&error);
907 g_debug ("Loading users from org.freedesktop.Accounts");
908 g_variant_get (result, "(ao)", &iter);
909 while (g_variant_iter_loop (iter, "&o", &path))
910 add_accounts_user (user_list, path, FALSE);
911 g_variant_iter_free (iter);
912 g_variant_unref (result);
918 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
919 priv->user_added_signal = 0;
920 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
921 priv->user_removed_signal = 0;
923 load_passwd_file (user_list, FALSE);
925 /* Watch for changes to user list */
927 passwd_file = g_file_new_for_path (PASSWD_FILE);
928 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
929 g_object_unref (passwd_file);
931 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
933 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
934 g_clear_error (&error);
939 * common_user_list_get_length:
940 * @user_list: a #CommonUserList
942 * Return value: The number of users able to log in
945 common_user_list_get_length (CommonUserList *user_list)
947 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), 0);
948 load_users (user_list);
949 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
953 * common_user_list_get_users:
954 * @user_list: A #CommonUserList
956 * Get a list of users to present to the user. This list may be a subset of the
957 * available users and may be empty depending on the server configuration.
959 * Return value: (element-type CommonUser) (transfer none): A list of #CommonUser that should be presented to the user.
962 common_user_list_get_users (CommonUserList *user_list)
964 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
965 load_users (user_list);
966 return GET_LIST_PRIVATE (user_list)->users;
970 * common_user_list_get_user_by_name:
971 * @user_list: A #CommonUserList
972 * @username: Name of user to get.
974 * Get infomation about a given user or #NULL if this user doesn't exist.
975 * Includes hidden and system users, unlike the list from
976 * common_user_list_get_users.
978 * Return value: (transfer full): A #CommonUser entry for the given user.
981 common_user_list_get_user_by_name (CommonUserList *user_list, const gchar *username)
983 g_return_val_if_fail (COMMON_IS_USER_LIST (user_list), NULL);
984 g_return_val_if_fail (username != NULL, NULL);
986 load_users (user_list);
988 CommonUser *user = get_user_by_name (user_list, username);
990 return g_object_ref (user);
992 /* Sometimes we need to look up users that aren't in AccountsService.
993 Notably we need to look up the user that the greeter runs as, which
994 is usually 'lightdm'. For such cases, we manually create a one-off
995 CommonUser object and pre-seed with passwd info. */
996 struct passwd *entry = getpwnam (username);
998 return make_passwd_user (user_list, entry);
1004 common_user_list_init (CommonUserList *user_list)
1006 CommonUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
1008 priv->bus = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL);
1012 common_user_list_set_property (GObject *object,
1014 const GValue *value,
1017 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1021 common_user_list_get_property (GObject *object,
1026 CommonUserList *self;
1028 self = COMMON_USER_LIST (object);
1032 case LIST_PROP_NUM_USERS:
1033 g_value_set_int (value, common_user_list_get_length (self));
1036 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1042 common_user_list_finalize (GObject *object)
1044 CommonUserList *self = COMMON_USER_LIST (object);
1045 CommonUserListPrivate *priv = GET_LIST_PRIVATE (self);
1047 /* Remove children first, they might access us */
1048 g_list_free_full (priv->users, g_object_unref);
1049 g_list_free_full (priv->sessions, g_object_unref);
1051 if (priv->user_added_signal)
1052 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_added_signal);
1053 if (priv->user_removed_signal)
1054 g_dbus_connection_signal_unsubscribe (priv->bus, priv->user_removed_signal);
1055 if (priv->session_added_signal)
1056 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_added_signal);
1057 if (priv->session_removed_signal)
1058 g_dbus_connection_signal_unsubscribe (priv->bus, priv->session_removed_signal);
1059 g_object_unref (priv->bus);
1060 g_clear_object (&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_LIST_SIGNAL_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_LIST_SIGNAL_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_LIST_SIGNAL_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 (USER_SIGNAL_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;