1 /* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
3 * Copyright (C) 2010 Robert Ancell.
4 * Author: Robert Ancell <robert.ancell@canonical.com>
6 * This library is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License as published by the Free
8 * Software Foundation; either version 3 of the License, or (at your option) any
9 * later version. See http://www.gnu.org/copyleft/lgpl.html the full text of the
17 #include <sys/utsname.h>
21 #include "lightdm/user.h"
35 USER_PROP_DISPLAY_NAME,
36 USER_PROP_HOME_DIRECTORY,
53 static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
60 static guint user_signals[LAST_USER_SIGNAL] = { 0 };
64 /* Connection to AccountsService */
65 GDBusProxy *accounts_service_proxy;
66 GList *user_account_objects;
68 /* Connection to DisplayManager */
69 GDBusProxy *display_manager_proxy;
71 /* File monitor for password file */
72 GFileMonitor *passwd_monitor;
74 /* TRUE if have scanned users */
80 /* List of sessions */
82 } LightDMUserListPrivate;
92 LightDMUserList *user_list;
96 gchar *home_directory;
104 } LightDMUserPrivate;
108 GObject parent_instance;
115 GObjectClass parent_class;
118 G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
119 G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
120 #define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
121 GType session_get_type (void);
122 G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
124 #define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
125 #define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
127 #define PASSWD_FILE "/etc/passwd"
128 #define USER_CONFIG_FILE "/etc/lightdm/users.conf"
130 static LightDMUserList *singleton = NULL;
133 * lightdm_user_list_get_instance:
137 * Return value: (transfer none): the #LightDMUserList
140 lightdm_user_list_get_instance (void)
143 singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
148 get_user_by_name (LightDMUserList *user_list, const gchar *username)
150 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
153 for (link = priv->users; link; link = link->next)
155 LightDMUser *user = link->data;
156 if (strcmp (lightdm_user_get_name (user), username) == 0)
164 compare_user (gconstpointer a, gconstpointer b)
166 LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
167 return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
171 update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
173 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
175 if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
176 g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
177 g_strcmp0 (lightdm_user_get_image (user), image) == 0)
180 g_free (priv->real_name);
181 priv->real_name = g_strdup (real_name);
182 g_free (priv->home_directory);
183 priv->home_directory = g_strdup (home_directory);
184 g_free (priv->image);
185 priv->image = g_strdup (image);
191 user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
193 g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
197 load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
199 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
203 gchar **hidden_users, **hidden_shells;
204 GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
205 GError *error = NULL;
207 g_debug ("Loading user config from %s", USER_CONFIG_FILE);
209 config = g_key_file_new ();
210 g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
211 if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
212 g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
213 g_clear_error (&error);
215 if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
216 minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
220 value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
222 value = g_strdup ("nobody nobody4 noaccess");
223 hidden_users = g_strsplit (value, " ", -1);
226 value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
228 value = g_strdup ("/bin/false /usr/sbin/nologin");
229 hidden_shells = g_strsplit (value, " ", -1);
232 g_key_file_free (config);
238 struct passwd *entry;
240 LightDMUserPrivate *user_priv;
242 gchar *real_name, *image;
250 /* Ignore system users */
251 if (entry->pw_uid < minimum_uid)
254 /* Ignore users disabled by shell */
257 for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
258 if (hidden_shells[i])
262 /* Ignore certain users */
263 for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
267 tokens = g_strsplit (entry->pw_gecos, ",", -1);
268 if (tokens[0] != NULL && tokens[0][0] != '\0')
269 real_name = g_strdup (tokens[0]);
271 real_name = g_strdup ("");
274 image = g_build_filename (entry->pw_dir, ".face", NULL);
275 if (!g_file_test (image, G_FILE_TEST_EXISTS))
278 image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
279 if (!g_file_test (image, G_FILE_TEST_EXISTS))
286 user = g_object_new (LIGHTDM_TYPE_USER, NULL);
287 user_priv = GET_USER_PRIVATE (user);
288 user_priv->user_list = user_list;
289 g_free (user_priv->name);
290 user_priv->name = g_strdup (entry->pw_name);
291 g_free (user_priv->real_name);
292 user_priv->real_name = real_name;
293 g_free (user_priv->home_directory);
294 user_priv->home_directory = g_strdup (entry->pw_dir);
295 g_free (user_priv->image);
296 user_priv->image = image;
298 /* Update existing users if have them */
299 for (link = priv->users; link; link = link->next)
301 LightDMUser *info = link->data;
302 if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
304 if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
305 changed_users = g_list_insert_sorted (changed_users, info, compare_user);
306 g_object_unref (user);
313 /* Only notify once we have loaded the user list */
314 if (priv->have_users)
315 new_users = g_list_insert_sorted (new_users, user, compare_user);
317 users = g_list_insert_sorted (users, user, compare_user);
319 g_strfreev (hidden_users);
320 g_strfreev (hidden_shells);
323 g_warning ("Failed to read password database: %s", strerror (errno));
327 /* Use new user list */
328 old_users = priv->users;
331 /* Notify of changes */
332 for (link = new_users; link; link = link->next)
334 LightDMUser *info = link->data;
335 g_debug ("User %s added", lightdm_user_get_name (info));
336 g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
338 g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
340 g_list_free (new_users);
341 for (link = changed_users; link; link = link->next)
343 LightDMUser *info = link->data;
344 g_debug ("User %s changed", lightdm_user_get_name (info));
345 g_signal_emit (info, user_signals[CHANGED], 0);
347 g_list_free (changed_users);
348 for (link = old_users; link; link = link->next)
352 /* See if this user is in the current list */
353 for (new_link = priv->users; new_link; new_link = new_link->next)
355 if (new_link->data == link->data)
361 LightDMUser *info = link->data;
362 g_debug ("User %s removed", lightdm_user_get_name (info));
363 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
364 g_object_unref (info);
367 g_list_free (old_users);
371 passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
373 if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
375 g_debug ("%s changed, reloading user list", g_file_get_path (file));
376 load_passwd_file (user_list, TRUE);
381 update_user (UserAccountObject *object)
383 LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
384 GVariant *result, *value;
387 GError *error = NULL;
389 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
390 "org.freedesktop.Accounts",
391 g_dbus_proxy_get_object_path (object->proxy),
392 "org.freedesktop.DBus.Properties",
394 g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
395 G_VARIANT_TYPE ("(a{sv})"),
396 G_DBUS_CALL_FLAGS_NONE,
401 g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
402 g_clear_error (&error);
406 g_variant_get (result, "(a{sv})", &iter);
407 while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
409 if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
412 g_variant_get (value, "&s", &user_name);
414 priv->name = g_strdup (user_name);
416 else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
419 g_variant_get (value, "&s", &real_name);
420 g_free (priv->real_name);
421 priv->real_name = g_strdup (real_name);
423 else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
425 gchar *home_directory;
426 g_variant_get (value, "&s", &home_directory);
427 g_free (priv->home_directory);
428 priv->home_directory = g_strdup (home_directory);
430 else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
433 g_variant_get (value, "&s", &icon_file);
434 g_free (priv->image);
435 if (strcmp (icon_file, "") == 0)
438 priv->image = g_strdup (icon_file);
440 else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
442 gchar *background_file;
443 g_variant_get (value, "&s", &background_file);
444 g_free (priv->background);
445 if (strcmp (background_file, "") == 0)
446 priv->background = NULL;
448 priv->background = g_strdup (background_file);
451 g_variant_iter_free (iter);
453 g_variant_unref (result);
459 user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
461 if (strcmp (signal_name, "Changed") == 0)
463 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
465 g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
466 update_user (object);
467 g_signal_emit (object->user, user_signals[CHANGED], 0);
470 g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
474 static UserAccountObject *
475 user_account_object_new (LightDMUserList *user_list, const gchar *path)
478 UserAccountObject *object;
479 GError *error = NULL;
481 proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
482 G_DBUS_PROXY_FLAGS_NONE,
484 "org.freedesktop.Accounts",
486 "org.freedesktop.Accounts.User",
490 g_warning ("Error getting user %s: %s", path, error->message);
491 g_clear_error (&error);
495 object = g_malloc0 (sizeof (UserAccountObject));
496 object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
497 GET_USER_PRIVATE (object->user)->user_list = user_list;
498 object->proxy = proxy;
499 g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
505 user_account_object_free (UserAccountObject *object)
509 g_object_unref (object->user);
510 g_object_unref (object->proxy);
514 static UserAccountObject *
515 find_user_account_object (LightDMUserList *user_list, const gchar *path)
517 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
520 for (link = priv->user_account_objects; link; link = link->next)
522 UserAccountObject *object = link->data;
523 if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
531 user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
533 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
535 if (strcmp (signal_name, "UserAdded") == 0)
537 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
540 UserAccountObject *object;
542 g_variant_get (parameters, "(&o)", &path);
544 /* Ignore duplicate requests */
545 object = find_user_account_object (user_list, path);
549 object = user_account_object_new (user_list, path);
550 if (object && update_user (object))
552 g_debug ("User %s added", path);
553 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
554 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
555 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
556 g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
559 user_account_object_free (object);
562 g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
564 else if (strcmp (signal_name, "UserDeleted") == 0)
566 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
569 UserAccountObject *object;
571 g_variant_get (parameters, "(&o)", &path);
573 object = find_user_account_object (user_list, path);
577 g_debug ("User %s deleted", path);
578 priv->users = g_list_remove (priv->users, object->user);
579 g_object_unref (object->user);
581 g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
583 priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
584 user_account_object_free (object);
587 g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
592 load_session (LightDMUserList *user_list, const gchar *path)
594 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
595 Session *session = NULL;
596 GVariant *result, *username;
597 GError *error = NULL;
599 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
600 "org.freedesktop.DisplayManager",
602 "org.freedesktop.DBus.Properties",
604 g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
605 G_VARIANT_TYPE ("(v)"),
606 G_DBUS_CALL_FLAGS_NONE,
611 g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
612 g_clear_error (&error);
616 g_variant_get (result, "(v)", &username);
617 if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
621 g_variant_get (username, "&s", &name);
623 g_debug ("Loaded session %s (%s)", path, name);
624 session = g_object_new (session_get_type (), NULL);
625 session->username = g_strdup (name);
626 session->path = g_strdup (path);
627 priv->sessions = g_list_append (priv->sessions, session);
629 g_variant_unref (username);
630 g_variant_unref (result);
636 display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
638 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
640 if (strcmp (signal_name, "SessionAdded") == 0)
642 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
646 LightDMUser *user = NULL;
648 g_variant_get (parameters, "(&o)", &path);
649 session = load_session (user_list, path);
651 user = get_user_by_name (user_list, session->username);
653 g_signal_emit (user, user_signals[CHANGED], 0);
656 else if (strcmp (signal_name, "SessionRemoved") == 0)
658 if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
663 g_variant_get (parameters, "(&o)", &path);
665 for (link = priv->sessions; link; link = link->next)
667 Session *session = link->data;
668 if (strcmp (session->path, path) == 0)
672 g_debug ("Session %s removed", path);
673 priv->sessions = g_list_remove_link (priv->sessions, link);
674 user = get_user_by_name (user_list, session->username);
676 g_signal_emit (user, user_signals[CHANGED], 0);
677 g_object_unref (session);
686 update_users (LightDMUserList *user_list)
688 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
689 GError *error = NULL;
691 if (priv->have_users)
693 priv->have_users = TRUE;
695 priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
696 G_DBUS_PROXY_FLAGS_NONE,
698 "org.freedesktop.Accounts",
699 "/org/freedesktop/Accounts",
700 "org.freedesktop.Accounts",
704 g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
705 g_clear_error (&error);
707 /* Check if the service exists */
708 if (priv->accounts_service_proxy)
712 name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
715 g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
716 g_object_unref (priv->accounts_service_proxy);
717 priv->accounts_service_proxy = NULL;
722 if (priv->accounts_service_proxy)
726 g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
728 result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
730 g_variant_new ("()"),
731 G_DBUS_CALL_FLAGS_NONE,
736 g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
737 g_clear_error (&error);
741 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
746 g_debug ("Loading users from org.freedesktop.Accounts");
747 g_variant_get (result, "(ao)", &iter);
748 while (g_variant_iter_loop (iter, "&o", &path))
750 UserAccountObject *object;
752 g_debug ("Loading user %s", path);
754 object = user_account_object_new (user_list, path);
755 if (object && update_user (object))
757 priv->user_account_objects = g_list_append (priv->user_account_objects, object);
758 priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
759 g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
762 user_account_object_free (object);
764 g_variant_iter_free (iter);
767 g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
769 g_variant_unref (result);
773 const gchar *passwd_filename;
776 load_passwd_file (user_list, FALSE);
778 /* Watch for changes to user list */
780 passwd_filename = g_getenv ("LIGHTDM_TEST_PASSWD_FILE");
781 if (!passwd_filename)
782 passwd_filename = PASSWD_FILE;
783 passwd_file = g_file_new_for_path (PASSWD_FILE);
784 priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
785 g_object_unref (passwd_file);
787 g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
789 g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
790 g_clear_error (&error);
793 priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
794 G_DBUS_PROXY_FLAGS_NONE,
796 "org.freedesktop.DisplayManager",
797 "/org/freedesktop/DisplayManager",
798 "org.freedesktop.DisplayManager",
802 g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
803 g_clear_error (&error);
805 if (priv->display_manager_proxy)
809 g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
811 result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
812 "org.freedesktop.DisplayManager",
813 "/org/freedesktop/DisplayManager",
814 "org.freedesktop.DBus.Properties",
816 g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
817 G_VARIANT_TYPE ("(v)"),
818 G_DBUS_CALL_FLAGS_NONE,
823 g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
824 g_clear_error (&error);
828 if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
834 g_variant_get (result, "(v)", &value);
836 g_debug ("Loading sessions from org.freedesktop.DisplayManager");
837 g_variant_get (value, "ao", &iter);
838 while (g_variant_iter_loop (iter, "&o", &path))
839 load_session (user_list, path);
840 g_variant_iter_free (iter);
842 g_variant_unref (value);
845 g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
847 g_variant_unref (result);
852 * lightdm_user_list_get_length:
853 * @user_list: a #LightDMUserList
855 * Return value: The number of users able to log in
858 lightdm_user_list_get_length (LightDMUserList *user_list)
860 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
861 update_users (user_list);
862 return g_list_length (GET_LIST_PRIVATE (user_list)->users);
866 * lightdm_user_list_get_users:
867 * @user_list: A #LightDMUserList
869 * Get a list of users to present to the user. This list may be a subset of the
870 * available users and may be empty depending on the server configuration.
872 * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
875 lightdm_user_list_get_users (LightDMUserList *user_list)
877 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
878 update_users (user_list);
879 return GET_LIST_PRIVATE (user_list)->users;
883 * lightdm_user_list_get_user_by_name:
884 * @user_list: A #LightDMUserList
885 * @username: Name of user to get.
887 * Get infomation about a given user or #NULL if this user doesn't exist.
889 * Return value: (transfer none): A #LightDMUser entry for the given user.
892 lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
894 g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
895 g_return_val_if_fail (username != NULL, NULL);
897 update_users (user_list);
899 return get_user_by_name (user_list, username);
903 lightdm_user_list_init (LightDMUserList *user_list)
908 lightdm_user_list_set_property (GObject *object,
913 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
917 lightdm_user_list_get_property (GObject *object,
922 LightDMUserList *self;
924 self = LIGHTDM_USER_LIST (object);
928 case LIST_PROP_NUM_USERS:
929 g_value_set_int (value, lightdm_user_list_get_length (self));
932 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
938 lightdm_user_list_finalize (GObject *object)
940 LightDMUserList *self = LIGHTDM_USER_LIST (object);
941 LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
943 if (priv->accounts_service_proxy)
944 g_object_unref (priv->accounts_service_proxy);
945 g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
946 if (priv->passwd_monitor)
947 g_object_unref (priv->passwd_monitor);
948 g_list_free_full (priv->users, g_object_unref);
949 g_list_free_full (priv->sessions, g_object_unref);
951 G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
955 lightdm_user_list_class_init (LightDMUserListClass *klass)
957 GObjectClass *object_class = G_OBJECT_CLASS (klass);
959 g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
961 object_class->set_property = lightdm_user_list_set_property;
962 object_class->get_property = lightdm_user_list_get_property;
963 object_class->finalize = lightdm_user_list_finalize;
965 g_object_class_install_property (object_class,
967 g_param_spec_int ("num-users",
969 "Number of login users",
973 * LightDMUserList::user-added:
974 * @user_list: A #LightDMUserList
975 * @user: The #LightDM user that has been added.
977 * The ::user-added signal gets emitted when a user account is created.
979 list_signals[USER_ADDED] =
980 g_signal_new ("user-added",
981 G_TYPE_FROM_CLASS (klass),
983 G_STRUCT_OFFSET (LightDMUserListClass, user_added),
985 g_cclosure_marshal_VOID__OBJECT,
986 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
989 * LightDMUserList::user-changed:
990 * @user_list: A #LightDMUserList
991 * @user: The #LightDM user that has been changed.
993 * The ::user-changed signal gets emitted when a user account is modified.
995 list_signals[USER_CHANGED] =
996 g_signal_new ("user-changed",
997 G_TYPE_FROM_CLASS (klass),
999 G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
1001 g_cclosure_marshal_VOID__OBJECT,
1002 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1005 * LightDMUserList::user-removed:
1006 * @user_list: A #LightDMUserList
1007 * @user: The #LightDM user that has been removed.
1009 * The ::user-removed signal gets emitted when a user account is removed.
1011 list_signals[USER_REMOVED] =
1012 g_signal_new ("user-removed",
1013 G_TYPE_FROM_CLASS (klass),
1015 G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
1017 g_cclosure_marshal_VOID__OBJECT,
1018 G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
1022 * lightdm_user_get_name:
1023 * @user: A #LightDMUser
1025 * Get the name of a user.
1027 * Return value: The name of the given user
1030 lightdm_user_get_name (LightDMUser *user)
1032 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1033 return GET_USER_PRIVATE (user)->name;
1037 * lightdm_user_get_real_name:
1038 * @user: A #LightDMUser
1040 * Get the real name of a user.
1042 * Return value: The real name of the given user
1045 lightdm_user_get_real_name (LightDMUser *user)
1047 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1048 return GET_USER_PRIVATE (user)->real_name;
1052 * lightdm_user_get_display_name:
1053 * @user: A #LightDMUser
1055 * Get the display name of a user.
1057 * Return value: The display name of the given user
1060 lightdm_user_get_display_name (LightDMUser *user)
1062 LightDMUserPrivate *priv;
1064 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1066 priv = GET_USER_PRIVATE (user);
1067 if (strcmp (priv->real_name, ""))
1068 return priv->real_name;
1074 * lightdm_user_get_home_directory:
1075 * @user: A #LightDMUser
1077 * Get the home directory for a user.
1079 * Return value: The users home directory
1082 lightdm_user_get_home_directory (LightDMUser *user)
1084 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1085 return GET_USER_PRIVATE (user)->home_directory;
1089 * lightdm_user_get_image:
1090 * @user: A #LightDMUser
1092 * Get the image URI for a user.
1094 * Return value: The image URI for the given user or #NULL if no URI
1097 lightdm_user_get_image (LightDMUser *user)
1099 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1100 return GET_USER_PRIVATE (user)->image;
1104 * lightdm_user_get_background:
1105 * @user: A #LightDMUser
1107 * Get the background file path for a user.
1109 * Return value: The background file path for the given user or #NULL if no path
1112 lightdm_user_get_background (LightDMUser *user)
1114 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1115 return GET_USER_PRIVATE (user)->background;
1119 load_dmrc (LightDMUser *user)
1121 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1123 //gboolean have_dmrc;
1125 priv->dmrc_file = g_key_file_new ();
1127 /* Load from the user directory */
1128 path = g_build_filename (priv->home_directory, ".dmrc", NULL);
1129 /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
1132 /* If no ~/.dmrc, then load from the cache */
1135 // FIXME: Watch for changes
1137 /* The Language field is actually a locale, strip the codeset off it to get the language */
1139 g_free (priv->language);
1140 priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
1143 gchar *codeset = strchr (priv->language, '.');
1150 g_strfreev (priv->layouts);
1151 priv->layouts = NULL;
1153 if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
1155 priv->layouts = g_malloc (sizeof (gchar *) * 2);
1156 priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
1157 priv->layouts[1] = NULL;
1161 g_free (priv->session);
1162 priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
1166 get_string_property (GDBusProxy *proxy, const gchar *property)
1174 answer = g_dbus_proxy_get_cached_property (proxy, property);
1178 g_warning ("Could not get accounts property %s", property);
1182 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("s")))
1184 g_warning ("Unexpected accounts property type for %s: %s",
1185 property, g_variant_get_type_string (answer));
1186 g_variant_unref (answer);
1190 g_variant_get (answer, "s", &rv);
1192 if (strcmp (rv, "") == 0)
1198 g_variant_unref (answer);
1203 get_string_array_property (GDBusProxy *proxy, const gchar *property)
1211 answer = g_dbus_proxy_get_cached_property (proxy, property);
1215 g_warning ("Could not get accounts property %s", property);
1219 if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
1221 g_warning ("Unexpected accounts property type for %s: %s",
1222 property, g_variant_get_type_string (answer));
1223 g_variant_unref (answer);
1227 rv = g_variant_dup_strv (answer, NULL);
1229 g_variant_unref (answer);
1234 load_accounts_service (LightDMUser *user)
1236 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1237 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1238 UserAccountObject *account = NULL;
1242 /* First, find AccountObject proxy */
1243 for (iter = list_priv->user_account_objects; iter; iter = iter->next)
1245 UserAccountObject *a = iter->data;
1246 if (a->user == user)
1255 /* We have proxy, let's grab some properties */
1257 g_free (priv->language);
1258 priv->language = get_string_property (account->proxy, "Language");
1260 g_free (priv->session);
1261 priv->session = get_string_property (account->proxy, "XSession");
1263 value = get_string_array_property (account->proxy, "XKeyboardLayouts");
1266 g_strfreev (priv->layouts);
1267 priv->layouts = value;
1273 /* Loads language/layout/session info for user */
1275 load_user_values (LightDMUser *user)
1278 load_accounts_service (user); // overrides dmrc values
1280 /* Ensure a few guarantees */
1281 if (GET_USER_PRIVATE (user)->layouts == NULL)
1283 GET_USER_PRIVATE (user)->layouts = g_malloc (sizeof (gchar));
1284 GET_USER_PRIVATE (user)->layouts[0] = NULL;
1289 * lightdm_user_get_language
1290 * @user: A #LightDMUser
1292 * Get the language for a user.
1294 * Return value: The language for the given user or #NULL if using system defaults.
1297 lightdm_user_get_language (LightDMUser *user)
1299 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1300 load_user_values (user);
1301 return GET_USER_PRIVATE (user)->language;
1305 * lightdm_user_get_layout
1306 * @user: A #LightDMUser
1308 * Get the keyboard layout for a user.
1310 * 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.
1313 lightdm_user_get_layout (LightDMUser *user)
1315 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1316 load_user_values (user);
1317 return GET_USER_PRIVATE (user)->layouts[0];
1321 * lightdm_user_get_layouts
1322 * @user: A #LightDMUser
1324 * Get the configured keyboard layouts for a user.
1326 * 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.
1328 const gchar * const *
1329 lightdm_user_get_layouts (LightDMUser *user)
1331 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1332 load_user_values (user);
1333 return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
1337 * lightdm_user_get_session
1338 * @user: A #LightDMUser
1340 * Get the session for a user.
1342 * Return value: The session for the given user or #NULL if using system defaults.
1345 lightdm_user_get_session (LightDMUser *user)
1347 g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
1348 load_user_values (user);
1349 return GET_USER_PRIVATE (user)->session;
1353 * lightdm_user_get_logged_in:
1354 * @user: A #LightDMUser
1356 * Check if a user is logged in.
1358 * Return value: #TRUE if the user is currently logged in.
1361 lightdm_user_get_logged_in (LightDMUser *user)
1363 LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
1364 LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
1367 g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
1369 for (link = list_priv->sessions; link; link = link->next)
1371 Session *session = link->data;
1372 if (strcmp (session->username, priv->name) == 0)
1380 lightdm_user_init (LightDMUser *user)
1385 lightdm_user_set_property (GObject *object,
1387 const GValue *value,
1390 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1394 lightdm_user_get_property (GObject *object,
1401 self = LIGHTDM_USER (object);
1405 case USER_PROP_NAME:
1406 g_value_set_string (value, lightdm_user_get_name (self));
1408 case USER_PROP_REAL_NAME:
1409 g_value_set_string (value, lightdm_user_get_real_name (self));
1411 case USER_PROP_DISPLAY_NAME:
1412 g_value_set_string (value, lightdm_user_get_display_name (self));
1414 case USER_PROP_HOME_DIRECTORY:
1415 g_value_set_string (value, lightdm_user_get_home_directory (self));
1417 case USER_PROP_IMAGE:
1418 g_value_set_string (value, lightdm_user_get_image (self));
1420 case USER_PROP_BACKGROUND:
1421 g_value_set_string (value, lightdm_user_get_background (self));
1423 case USER_PROP_LANGUAGE:
1424 g_value_set_string (value, lightdm_user_get_language (self));
1426 case USER_PROP_LAYOUT:
1427 g_value_set_string (value, lightdm_user_get_layout (self));
1429 case USER_PROP_LAYOUTS:
1430 g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
1432 case USER_PROP_SESSION:
1433 g_value_set_string (value, lightdm_user_get_session (self));
1435 case USER_PROP_LOGGED_IN:
1436 g_value_set_boolean (value, lightdm_user_get_logged_in (self));
1439 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1445 lightdm_user_finalize (GObject *object)
1447 LightDMUser *self = LIGHTDM_USER (object);
1448 LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
1450 g_free (priv->name);
1451 g_free (priv->real_name);
1452 g_free (priv->home_directory);
1453 g_free (priv->image);
1454 g_free (priv->background);
1455 g_strfreev (priv->layouts);
1456 if (priv->dmrc_file)
1457 g_key_file_free (priv->dmrc_file);
1461 lightdm_user_class_init (LightDMUserClass *klass)
1463 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1465 g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
1467 object_class->set_property = lightdm_user_set_property;
1468 object_class->get_property = lightdm_user_get_property;
1469 object_class->finalize = lightdm_user_finalize;
1471 g_object_class_install_property (object_class,
1473 g_param_spec_string ("name",
1477 G_PARAM_READWRITE));
1478 g_object_class_install_property (object_class,
1479 USER_PROP_REAL_NAME,
1480 g_param_spec_string ("real-name",
1484 G_PARAM_READWRITE));
1485 g_object_class_install_property (object_class,
1486 USER_PROP_DISPLAY_NAME,
1487 g_param_spec_string ("display-name",
1489 "Users display name",
1492 g_object_class_install_property (object_class,
1493 USER_PROP_HOME_DIRECTORY,
1494 g_param_spec_string ("home-directory",
1498 G_PARAM_READWRITE));
1499 g_object_class_install_property (object_class,
1501 g_param_spec_string ("image",
1505 G_PARAM_READWRITE));
1506 g_object_class_install_property (object_class,
1507 USER_PROP_BACKGROUND,
1508 g_param_spec_string ("background",
1512 G_PARAM_READWRITE));
1513 g_object_class_install_property (object_class,
1515 g_param_spec_string ("language",
1517 "Language used by this user",
1520 g_object_class_install_property (object_class,
1522 g_param_spec_string ("layout",
1524 "Keyboard layout used by this user",
1527 g_object_class_install_property (object_class,
1529 g_param_spec_boxed ("layouts",
1531 "Keyboard layouts used by this user",
1534 g_object_class_install_property (object_class,
1536 g_param_spec_string ("session",
1538 "Session used by this user",
1541 g_object_class_install_property (object_class,
1542 USER_PROP_LOGGED_IN,
1543 g_param_spec_boolean ("logged-in",
1545 "TRUE if the user is currently in a session",
1547 G_PARAM_READWRITE));
1550 * LightDMUser::changed:
1551 * @user: A #LightDMUser
1553 * The ::changed signal gets emitted this user account is modified.
1555 user_signals[CHANGED] =
1556 g_signal_new ("changed",
1557 G_TYPE_FROM_CLASS (klass),
1559 G_STRUCT_OFFSET (LightDMUserClass, changed),
1561 g_cclosure_marshal_VOID__VOID,
1566 session_init (Session *session)
1571 session_finalize (GObject *object)
1573 Session *self = SESSION (object);
1575 g_free (self->path);
1576 g_free (self->username);
1580 session_class_init (SessionClass *klass)
1582 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1583 object_class->finalize = session_finalize;