2 * Copyright (C) 2010-2011 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
5 * This program is free software: you can redistribute it and/or modify it under
6 * the terms of the GNU General Public License as published by the Free Software
7 * Foundation, either version 3 of the License, or (at your option) any later
8 * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
17 #include "guest-account.h"
25 static guint signals[LAST_SIGNAL] = { 0 };
29 /* Configuration for this seat */
30 GHashTable *properties;
32 /* TRUE if able to switch users */
35 /* TRUE if display server can be shared for sessions */
36 gboolean share_display_server;
38 /* Name of guest account */
39 gchar *guest_username;
41 /* The displays for this seat */
44 /* TRUE if stopping this seat (waiting for displays to stop) */
51 G_DEFINE_TYPE (Seat, seat, G_TYPE_OBJECT);
58 static GHashTable *seat_modules = NULL;
60 static Display *create_display (Seat *seat);
63 seat_register_module (const gchar *name, GType type)
68 seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
70 g_debug ("Registered seat module %s", name);
72 module = g_malloc0 (sizeof (SeatModule));
73 module->name = g_strdup (name);
75 g_hash_table_insert (seat_modules, g_strdup (name), module);
79 seat_new (const gchar *module_name)
84 g_return_val_if_fail (module_name != NULL, NULL);
87 m = g_hash_table_lookup (seat_modules, module_name);
91 seat = g_object_new (m->type, NULL);
97 seat_set_property (Seat *seat, const gchar *name, const gchar *value)
99 g_return_if_fail (seat != NULL);
100 g_hash_table_insert (seat->priv->properties, g_strdup (name), g_strdup (value));
104 seat_has_property (Seat *seat, const gchar *name)
106 g_return_val_if_fail (seat != NULL, FALSE);
107 return g_hash_table_lookup (seat->priv->properties, name) != NULL;
111 seat_get_string_property (Seat *seat, const gchar *name)
113 g_return_val_if_fail (seat != NULL, NULL);
114 return g_hash_table_lookup (seat->priv->properties, name);
118 seat_get_boolean_property (Seat *seat, const gchar *name)
120 return g_strcmp0 (seat_get_string_property (seat, name), "true") == 0;
124 seat_get_integer_property (Seat *seat, const gchar *name)
128 value = seat_get_string_property (seat, name);
129 return value ? atoi (value) : 0;
133 seat_set_can_switch (Seat *seat, gboolean can_switch)
135 g_return_if_fail (seat != NULL);
137 seat->priv->can_switch = can_switch;
141 seat_set_share_display_server (Seat *seat, gboolean share_display_server)
143 g_return_if_fail (seat != NULL);
145 seat->priv->share_display_server = share_display_server;
149 seat_start (Seat *seat)
151 g_return_val_if_fail (seat != NULL, FALSE);
153 SEAT_GET_CLASS (seat)->setup (seat);
154 return SEAT_GET_CLASS (seat)->start (seat);
158 seat_get_displays (Seat *seat)
160 g_return_val_if_fail (seat != NULL, NULL);
161 return seat->priv->displays;
165 seat_set_active_display (Seat *seat, Display *display)
167 g_return_if_fail (seat != NULL);
168 SEAT_GET_CLASS (seat)->set_active_display (seat, display);
172 seat_get_active_display (Seat *seat)
174 g_return_val_if_fail (seat != NULL, NULL);
175 return SEAT_GET_CLASS (seat)->get_active_display (seat);
179 seat_get_can_switch (Seat *seat)
181 g_return_val_if_fail (seat != NULL, FALSE);
182 return seat->priv->can_switch;
186 seat_get_allow_guest (Seat *seat)
188 g_return_val_if_fail (seat != NULL, FALSE);
189 return seat_get_boolean_property (seat, "allow-guest") && guest_account_is_installed ();
193 seat_get_greeter_allow_guest (Seat *seat)
195 g_return_val_if_fail (seat != NULL, FALSE);
196 return seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest");
200 switch_to_user (Seat *seat, const gchar *username, gboolean unlock)
204 /* Switch to active display if it exists */
205 for (link = seat->priv->displays; link; link = link->next)
207 Display *display = link->data;
209 if (display_get_is_stopped (display))
212 /* If already logged in, then switch to that display */
213 if (g_strcmp0 (display_get_username (display), username) == 0)
216 g_debug ("Switching to existing session for user %s", username);
218 g_debug ("Switching to existing greeter");
220 display_unlock (display);
221 seat_set_active_display (seat, display);
230 display_switch_to_user_cb (Display *display, User *user, Seat *seat)
232 return switch_to_user (seat, user_get_name (user), TRUE);
236 display_switch_to_guest_cb (Display *display, Seat *seat)
238 /* No guest account */
239 if (!seat->priv->guest_username)
242 return switch_to_user (seat, seat->priv->guest_username, TRUE);
246 display_get_guest_username_cb (Display *display, Seat *seat)
248 if (seat->priv->guest_username)
249 return seat->priv->guest_username;
251 seat->priv->guest_username = guest_account_setup ();
252 return g_strdup (seat->priv->guest_username);
256 run_script (Seat *seat, Display *display, const gchar *script_name, User *user)
259 gboolean result = FALSE;
261 script = process_new ();
263 process_set_command (script, script_name);
265 /* Set POSIX variables */
266 process_set_clear_environment (script, TRUE);
267 process_set_env (script, "SHELL", "/bin/sh");
269 /* Variables required for regression tests */
270 if (g_getenv ("LIGHTDM_TEST_ROOT"))
272 process_set_env (script, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
273 process_set_env (script, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
274 process_set_env (script, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
275 process_set_env (script, "PATH", g_getenv ("PATH"));
278 process_set_env (script, "PATH", "/usr/local/bin:/usr/bin:/bin");
282 process_set_env (script, "USER", user_get_name (user));
283 process_set_env (script, "LOGNAME", user_get_name (user));
284 process_set_env (script, "HOME", user_get_home_directory (user));
287 process_set_env (script, "HOME", "/");
289 SEAT_GET_CLASS (seat)->run_script (seat, display, script);
291 if (process_start (script, TRUE))
295 exit_status = process_get_exit_status (script);
296 if (WIFEXITED (exit_status))
298 g_debug ("Exit status of %s: %d", script_name, WEXITSTATUS (exit_status));
299 result = WEXITSTATUS (exit_status) == EXIT_SUCCESS;
303 g_object_unref (script);
309 seat_real_run_script (Seat *seat, Display *display, Process *process)
314 emit_upstart_signal (const gchar *signal)
316 g_return_if_fail (signal != NULL);
317 g_return_if_fail (signal[0] != 0);
322 gchar *cmd = g_strdup_printf ("initctl -q emit %s DISPLAY_MANAGER=lightdm", signal);
323 g_spawn_command_line_async (cmd, NULL); /* OK if it fails, probably not installed */
328 display_display_server_ready_cb (Display *display, Seat *seat)
332 /* Run setup script */
333 script = seat_get_string_property (seat, "display-setup-script");
334 if (script && !run_script (seat, display, script, NULL))
337 emit_upstart_signal ("login-session-start");
343 display_create_session_cb (Display *display, Seat *seat)
345 return SEAT_GET_CLASS (seat)->create_session (seat, display);
349 display_start_greeter_cb (Display *display, Seat *seat)
354 session = display_get_session (display);
356 script = seat_get_string_property (seat, "greeter-setup-script");
358 return !run_script (seat, display, script, session_get_user (session));
364 display_start_session_cb (Display *display, Seat *seat)
369 session = display_get_session (display);
371 script = seat_get_string_property (seat, "session-setup-script");
373 return !run_script (seat, display, script, session_get_user (session));
379 session_stopped_cb (Session *session, Seat *seat)
381 Display *display = NULL;
385 /* Work out what display this session is on, it's a bit hacky because we really should know already... */
386 for (link = seat->priv->displays; link; link = link->next)
388 Display *d = link->data;
389 if (display_get_session (d) == session)
395 g_return_if_fail (display != NULL);
398 script = seat_get_string_property (seat, "session-cleanup-script");
400 run_script (seat, display, script, session_get_user (session));
402 if (seat->priv->guest_username && strcmp (session_get_username (session), seat->priv->guest_username) == 0)
404 g_free (seat->priv->guest_username);
405 seat->priv->guest_username = NULL;
410 display_session_started_cb (Display *display, Seat *seat)
412 g_signal_connect (display_get_session (display), "stopped", G_CALLBACK (session_stopped_cb), seat);
413 emit_upstart_signal ("desktop-session-start");
418 display_ready_cb (Display *display, Seat *seat)
420 /* Switch to this new display */
421 g_debug ("New display ready, switching to it");
422 SEAT_GET_CLASS (seat)->set_active_display (seat, display);
426 display_create_display_cb (Display *display, Session *session, Seat *seat)
430 d = create_display (seat);
431 g_signal_connect (d, "ready", G_CALLBACK (display_ready_cb), seat);
432 g_signal_emit (seat, signals[DISPLAY_ADDED], 0, d);
434 display_start_with_session (d, session);
436 return g_object_ref (d);
440 check_stopped (Seat *seat)
442 if (seat->priv->stopping &&
443 !seat->priv->stopped &&
444 g_list_length (seat->priv->displays) == 0)
446 seat->priv->stopped = TRUE;
447 g_debug ("Seat stopped");
448 g_signal_emit (seat, signals[STOPPED], 0);
453 display_stopped_cb (Display *display, Seat *seat)
457 is_active = display == seat_get_active_display (seat);
459 g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
460 seat->priv->displays = g_list_remove (seat->priv->displays, display);
462 g_signal_emit (seat, signals[DISPLAY_REMOVED], 0, display);
464 /* If no more displays running either start a greeter or stop the seat */
465 if (!seat->priv->stopping)
467 if (g_list_length (seat->priv->displays) == 0)
469 /* If failed to start then stop this seat */
470 if (!display_get_is_ready (display))
472 g_debug ("Stopping seat, failed to start a display");
475 /* Attempt to start a greeter */
476 else if (!seat->priv->can_switch)
478 g_debug ("Stopping seat, display stopped");
482 else if (!seat_switch_to_greeter (seat))
484 g_debug ("Stopping seat, unable to start greeter");
490 g_debug ("Active display stopped, switching to greeter");
491 if (!seat_switch_to_greeter (seat))
493 g_debug ("Stopping seat, unable to start greeter");
499 g_object_unref (display);
501 check_stopped (seat);
505 create_display (Seat *seat)
508 DisplayServer *display_server;
510 display_server = SEAT_GET_CLASS (seat)->create_display_server (seat);
511 display = display_new (display_server);
512 g_object_unref (display_server);
514 g_signal_connect (display, "display-server-ready", G_CALLBACK (display_display_server_ready_cb), seat);
515 g_signal_connect (display, "switch-to-user", G_CALLBACK (display_switch_to_user_cb), seat);
516 g_signal_connect (display, "switch-to-guest", G_CALLBACK (display_switch_to_guest_cb), seat);
517 g_signal_connect (display, "get-guest-username", G_CALLBACK (display_get_guest_username_cb), seat);
518 g_signal_connect (display, "create-session", G_CALLBACK (display_create_session_cb), seat);
519 g_signal_connect (display, "start-greeter", G_CALLBACK (display_start_greeter_cb), seat);
520 g_signal_connect (display, "start-session", G_CALLBACK (display_start_session_cb), seat);
521 g_signal_connect_after (display, "start-session", G_CALLBACK (display_session_started_cb), seat);
522 g_signal_connect (display, "create-display", G_CALLBACK (display_create_display_cb), seat);
523 g_signal_connect (display, "stopped", G_CALLBACK (display_stopped_cb), seat);
524 display_set_greeter_session (display, seat_get_string_property (seat, "greeter-session"));
525 display_set_session_wrapper (display, seat_get_string_property (seat, "session-wrapper"));
526 display_set_greeter_wrapper (display, seat_get_string_property (seat, "greeter-wrapper"));
527 display_set_hide_users_hint (display, seat_get_boolean_property (seat, "greeter-hide-users"));
528 display_set_show_manual_login_hint (display, seat_get_boolean_property (seat, "greeter-show-manual-login"));
529 display_set_show_remote_login_hint (display, seat_get_boolean_property (seat, "greeter-show-remote-login"));
530 display_set_allow_guest (display, seat_get_allow_guest (seat));
531 display_set_greeter_allow_guest (display, seat_get_greeter_allow_guest (seat));
532 display_set_user_session (display, SESSION_TYPE_LOCAL, seat_get_string_property (seat, "user-session"));
533 display_set_share_display_server (display, seat->priv->share_display_server);
535 seat->priv->displays = g_list_append (seat->priv->displays, display);
541 start_display (Seat *seat, Display *display)
543 g_signal_emit (seat, signals[DISPLAY_ADDED], 0, display);
545 /* Switch to this display if currently not looking at anything */
546 if (seat_get_active_display (seat) == NULL)
547 seat_set_active_display (seat, display);
549 return display_start (display);
553 seat_switch_to_greeter (Seat *seat)
557 g_return_val_if_fail (seat != NULL, FALSE);
559 if (!seat->priv->can_switch)
562 g_debug ("Switching to greeter");
564 /* Switch to greeter if one open (shouldn't be though) */
565 if (switch_to_user (seat, NULL, FALSE))
568 g_debug ("Starting new display for greeter");
570 display = create_display (seat);
571 g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
573 return start_display (seat, display);
577 seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_name)
581 g_return_val_if_fail (seat != NULL, FALSE);
582 g_return_val_if_fail (username != NULL, FALSE);
584 if (!seat->priv->can_switch)
587 g_debug ("Switching to user %s", username);
589 /* Switch to session if one open */
590 if (switch_to_user (seat, username, FALSE))
594 g_debug ("Starting new display for greeter with user %s selected", username);
596 g_debug ("Starting new display for greeter");
598 display = create_display (seat);
599 g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
600 display_set_select_user_hint (display, username, FALSE, TRUE);
601 if (session_name != NULL)
602 display_set_user_session (display, SESSION_TYPE_LOCAL, session_name);
604 return start_display (seat, display);
608 seat_switch_to_guest (Seat *seat, const gchar *session_name)
612 g_return_val_if_fail (seat != NULL, FALSE);
614 if (!seat->priv->can_switch || !seat_get_allow_guest (seat))
617 /* Switch to session if one open */
618 if (seat->priv->guest_username)
620 g_debug ("Switching to existing guest account %s", seat->priv->guest_username);
621 return switch_to_user (seat, seat->priv->guest_username, FALSE);
624 g_debug ("Starting new display for automatic guest login");
626 display = create_display (seat);
627 g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
628 display_set_autologin_user (display, NULL, TRUE, 0);
629 if (session_name != NULL)
630 display_set_user_session (display, SESSION_TYPE_LOCAL, session_name);
632 return start_display (seat, display);
636 seat_lock (Seat *seat, const gchar *username)
640 g_return_val_if_fail (seat != NULL, FALSE);
642 if (!seat->priv->can_switch)
645 g_debug ("Locking seat");
647 /* Switch to greeter if one open (shouldn't be though) */
648 if (switch_to_user (seat, NULL, FALSE))
651 g_debug ("Starting new display for greeter (lock screen)");
653 display = create_display (seat);
654 g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
655 display_set_lock_hint (display, TRUE);
656 display_set_select_user_hint (display, username, FALSE, FALSE);
658 return start_display (seat, display);
662 seat_stop (Seat *seat)
664 g_return_if_fail (seat != NULL);
666 if (seat->priv->stopping)
669 g_debug ("Stopping seat");
670 seat->priv->stopping = TRUE;
671 SEAT_GET_CLASS (seat)->stop (seat);
675 seat_get_is_stopping (Seat *seat)
677 g_return_val_if_fail (seat != NULL, FALSE);
678 return seat->priv->stopping;
682 seat_real_setup (Seat *seat)
687 autologin_greeter_ready_cb (Display *display, Seat *seat)
689 const gchar *autologin_username;
690 gboolean autologin_guest;
691 Display *autologin_display;
693 g_debug ("Greeter ready, starting autologin session");
695 /* Get autologin settings */
696 autologin_username = seat_get_string_property (seat, "autologin-user");
697 if (g_strcmp0 (autologin_username, "") == 0)
698 autologin_username = NULL;
699 autologin_guest = seat_get_boolean_property (seat, "autologin-guest");
702 g_debug ("Starting new display for automatic guest login");
703 else if (autologin_username)
704 g_debug ("Starting new display for automatic login as user %s", autologin_username);
706 autologin_display = create_display (seat);
707 display_set_autologin_user (autologin_display, autologin_username, autologin_guest, 0);
709 start_display (seat, autologin_display);
713 seat_real_start (Seat *seat)
715 const gchar *autologin_username;
716 int autologin_timeout;
717 gboolean autologin_guest;
718 gboolean do_autologin;
719 gboolean autologin_in_background;
722 g_debug ("Starting seat");
724 /* Get autologin settings */
725 autologin_username = seat_get_string_property (seat, "autologin-user");
726 if (g_strcmp0 (autologin_username, "") == 0)
727 autologin_username = NULL;
728 autologin_timeout = seat_get_integer_property (seat, "autologin-user-timeout");
729 autologin_guest = seat_get_boolean_property (seat, "autologin-guest");
730 autologin_in_background = seat_get_boolean_property (seat, "autologin-in-background");
731 do_autologin = autologin_username != NULL || autologin_guest;
733 /* Background automatic logins need the greeter showing first */
734 if (do_autologin && autologin_in_background && !switch_to_user (seat, NULL, FALSE))
736 g_debug ("Autologin in background, opening greeter first");
737 display = create_display (seat);
738 g_signal_connect (display, "ready", G_CALLBACK (autologin_greeter_ready_cb), seat);
739 if (autologin_timeout > 0)
740 display_set_autologin_user (display, autologin_username, autologin_guest, autologin_timeout);
741 return start_display (seat, display);
745 g_debug ("Starting new display for automatic guest login");
746 else if (autologin_username)
747 g_debug ("Starting new display for automatic login as user %s", autologin_username);
749 g_debug ("Starting new display for greeter");
751 display = create_display (seat);
752 g_signal_connect (display, "ready", G_CALLBACK (display_ready_cb), seat);
753 display_set_autologin_user (display, autologin_username, autologin_guest, autologin_timeout);
755 return start_display (seat, display);
759 seat_real_set_active_display (Seat *seat, Display *display)
763 for (link = seat->priv->displays; link; link = link->next)
765 Display *d = link->data;
770 /* Stop any greeters and lock any other sessions */
771 if (!display_get_username (d))
773 g_debug ("Stopping background greeter");
782 seat_real_get_active_display (Seat *seat)
788 seat_real_stop (Seat *seat)
790 GList *displays, *link;
792 check_stopped (seat);
793 if (seat->priv->stopped)
796 /* Stop all the displays on the seat. Copy the list as it might be modified if a display stops during this loop */
797 displays = g_list_copy (seat->priv->displays);
798 for (link = displays; link; link = link->next)
800 Display *display = link->data;
801 display_stop (display);
803 g_list_free (displays);
807 seat_init (Seat *seat)
809 seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_TYPE, SeatPrivate);
810 seat->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
811 seat->priv->share_display_server = TRUE;
815 seat_finalize (GObject *object)
820 self = SEAT (object);
822 g_hash_table_unref (self->priv->properties);
823 g_free (self->priv->guest_username);
824 for (link = self->priv->displays; link; link = link->next)
826 Display *display = link->data;
827 g_signal_handlers_disconnect_matched (display, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
829 g_list_free_full (self->priv->displays, g_object_unref);
831 G_OBJECT_CLASS (seat_parent_class)->finalize (object);
835 seat_class_init (SeatClass *klass)
837 GObjectClass *object_class = G_OBJECT_CLASS (klass);
839 klass->setup = seat_real_setup;
840 klass->start = seat_real_start;
841 klass->set_active_display = seat_real_set_active_display;
842 klass->get_active_display = seat_real_get_active_display;
843 klass->run_script = seat_real_run_script;
844 klass->stop = seat_real_stop;
846 object_class->finalize = seat_finalize;
848 g_type_class_add_private (klass, sizeof (SeatPrivate));
850 signals[DISPLAY_ADDED] =
851 g_signal_new ("display-added",
852 G_TYPE_FROM_CLASS (klass),
854 G_STRUCT_OFFSET (SeatClass, display_added),
857 G_TYPE_NONE, 1, DISPLAY_TYPE);
858 signals[DISPLAY_REMOVED] =
859 g_signal_new ("display-removed",
860 G_TYPE_FROM_CLASS (klass),
862 G_STRUCT_OFFSET (SeatClass, display_removed),
865 G_TYPE_NONE, 1, DISPLAY_TYPE);
867 g_signal_new ("stopped",
868 G_TYPE_FROM_CLASS (klass),
870 G_STRUCT_OFFSET (SeatClass, stopped),