]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/seat.c
Remove some FIXMEs
[sojka/lightdm.git] / src / seat.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  * 
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
9  * license.
10  */
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/wait.h>
15
16 #include "seat.h"
17 #include "configuration.h"
18 #include "guest-account.h"
19 #include "greeter.h"
20
21 enum {
22     SESSION_ADDED,
23     SESSION_REMOVED,
24     STOPPED,
25     LAST_SIGNAL
26 };
27 static guint signals[LAST_SIGNAL] = { 0 };
28
29 struct SeatPrivate
30 {
31     /* Configuration for this seat */
32     GHashTable *properties;
33
34     /* TRUE if able to switch users */
35     gboolean can_switch;
36
37     /* TRUE if display server can be shared for sessions */
38     gboolean share_display_server;
39
40     /* The display servers on this seat */
41     GList *display_servers;
42
43     /* The sessions on this seat */
44     GList *sessions;
45
46     /* The session to set active when it starts */
47     Session *session_to_activate;
48
49     /* TRUE if stopping this seat (waiting for displays to stop) */
50     gboolean stopping;
51
52     /* TRUE if stopped */
53     gboolean stopped;
54 };
55
56 /* PAM services to use */
57 #define GREETER_SERVICE   "lightdm-greeter"
58 #define USER_SERVICE      "lightdm"
59 #define AUTOLOGIN_SERVICE "lightdm-autologin"
60
61 G_DEFINE_TYPE (Seat, seat, G_TYPE_OBJECT);
62
63 typedef struct
64 {
65     const gchar *name;
66     GType type;
67 } SeatModule;
68 static GHashTable *seat_modules = NULL;
69
70 // FIXME: Make a get_display_server() that re-uses display servers if supported
71 static DisplayServer *create_display_server (Seat *seat);
72 static Greeter *create_greeter_session (Seat *seat);
73
74 void
75 seat_register_module (const gchar *name, GType type)
76 {
77     SeatModule *module;
78
79     if (!seat_modules)
80         seat_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
81
82     g_debug ("Registered seat module %s", name);
83
84     module = g_malloc0 (sizeof (SeatModule));
85     module->name = g_strdup (name);
86     module->type = type;
87     g_hash_table_insert (seat_modules, g_strdup (name), module);
88 }
89
90 Seat *
91 seat_new (const gchar *module_name)
92 {
93     Seat *seat;
94     SeatModule *m = NULL;
95   
96     g_return_val_if_fail (module_name != NULL, NULL);
97
98     if (seat_modules)
99         m = g_hash_table_lookup (seat_modules, module_name);
100     if (!m)
101         return NULL;
102
103     seat = g_object_new (m->type, NULL);
104
105     return seat;
106 }
107
108 void
109 seat_set_property (Seat *seat, const gchar *name, const gchar *value)
110 {
111     g_return_if_fail (seat != NULL);
112     g_hash_table_insert (seat->priv->properties, g_strdup (name), g_strdup (value));
113 }
114
115 const gchar *
116 seat_get_string_property (Seat *seat, const gchar *name)
117 {
118     g_return_val_if_fail (seat != NULL, NULL);
119     return g_hash_table_lookup (seat->priv->properties, name);
120 }
121
122 gboolean
123 seat_get_boolean_property (Seat *seat, const gchar *name)
124 {
125     return g_strcmp0 (seat_get_string_property (seat, name), "true") == 0;
126 }
127
128 gint
129 seat_get_integer_property (Seat *seat, const gchar *name)
130 {
131     const gchar *value;
132
133     value = seat_get_string_property (seat, name);
134     return value ? atoi (value) : 0;
135 }
136
137 void
138 seat_set_can_switch (Seat *seat, gboolean can_switch)
139 {
140     g_return_if_fail (seat != NULL);
141
142     seat->priv->can_switch = can_switch;
143 }
144
145 void
146 seat_set_share_display_server (Seat *seat, gboolean share_display_server)
147 {
148     g_return_if_fail (seat != NULL);
149
150     seat->priv->share_display_server = share_display_server;
151 }
152
153 gboolean
154 seat_start (Seat *seat)
155 {
156     g_return_val_if_fail (seat != NULL, FALSE);
157   
158     SEAT_GET_CLASS (seat)->setup (seat);
159     return SEAT_GET_CLASS (seat)->start (seat);
160 }
161
162 GList *
163 seat_get_sessions (Seat *seat)
164 {
165     g_return_val_if_fail (seat != NULL, NULL);
166     return seat->priv->sessions;
167 }
168
169 void
170 seat_set_active_session (Seat *seat, Session *session)
171 {
172     GList *link;
173
174     g_return_if_fail (seat != NULL);
175
176     SEAT_GET_CLASS (seat)->set_active_session (seat, session);
177   
178     /* Stop any greeters */
179     for (link = seat->priv->sessions; link; link = link->next)
180     {
181         Session *s = link->data;
182
183         if (s == session || session_get_is_stopping (s))
184             continue;
185
186         if (IS_GREETER (s))
187         {
188             g_debug ("Stopping greeter");
189             session_stop (s);
190         }
191     }
192 }
193
194 Session *
195 seat_get_active_session (Seat *seat)
196 {
197     g_return_val_if_fail (seat != NULL, NULL);
198     return SEAT_GET_CLASS (seat)->get_active_session (seat);
199 }
200
201 gboolean
202 seat_get_can_switch (Seat *seat)
203 {
204     g_return_val_if_fail (seat != NULL, FALSE);
205     return seat->priv->can_switch;
206 }
207
208 gboolean
209 seat_get_allow_guest (Seat *seat)
210 {
211     g_return_val_if_fail (seat != NULL, FALSE);
212     return seat_get_boolean_property (seat, "allow-guest") && guest_account_is_installed ();
213 }
214
215 gboolean
216 seat_get_greeter_allow_guest (Seat *seat)
217 {
218     g_return_val_if_fail (seat != NULL, FALSE);  
219     return seat_get_allow_guest (seat) && seat_get_boolean_property (seat, "greeter-allow-guest");
220 }
221
222 static gboolean
223 run_script (Seat *seat, DisplayServer *display_server, const gchar *script_name, User *user)
224 {
225     Process *script;
226     gboolean result = FALSE;
227   
228     script = process_new ();
229
230     process_set_command (script, script_name);
231
232     /* Set POSIX variables */
233     process_set_clear_environment (script, TRUE);
234     process_set_env (script, "SHELL", "/bin/sh");
235
236     /* Variables required for regression tests */
237     if (g_getenv ("LIGHTDM_TEST_ROOT"))
238     {
239         process_set_env (script, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
240         process_set_env (script, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
241         process_set_env (script, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
242         process_set_env (script, "PATH", g_getenv ("PATH"));
243     }
244     else
245         process_set_env (script, "PATH", "/usr/local/bin:/usr/bin:/bin");
246
247     if (user)
248     {
249         process_set_env (script, "USER", user_get_name (user));
250         process_set_env (script, "LOGNAME", user_get_name (user));
251         process_set_env (script, "HOME", user_get_home_directory (user));
252     }
253     else
254         process_set_env (script, "HOME", "/");
255
256     SEAT_GET_CLASS (seat)->run_script (seat, display_server, script);
257
258     if (process_start (script, TRUE))
259     {
260         int exit_status;
261
262         exit_status = process_get_exit_status (script);
263         if (WIFEXITED (exit_status))
264         {
265             g_debug ("Exit status of %s: %d", script_name, WEXITSTATUS (exit_status));
266             result = WEXITSTATUS (exit_status) == EXIT_SUCCESS;
267         }
268     }
269
270     g_object_unref (script);
271
272     return result;
273 }
274
275 static void
276 seat_real_run_script (Seat *seat, DisplayServer *display_server, Process *process)
277 {  
278 }
279
280 static void
281 emit_upstart_signal (const gchar *signal)
282 {
283     g_return_if_fail (signal != NULL);
284     g_return_if_fail (signal[0] != 0);
285
286     if (getuid () != 0)
287         return;
288
289     gchar *cmd = g_strdup_printf ("initctl -q emit %s DISPLAY_MANAGER=lightdm", signal);
290     g_spawn_command_line_async (cmd, NULL); /* OK if it fails, probably not installed */
291     g_free (cmd);
292 }
293
294 static void
295 check_stopped (Seat *seat)
296 {
297     if (seat->priv->stopping &&
298         !seat->priv->stopped &&
299         g_list_length (seat->priv->display_servers) == 0 &&
300         g_list_length (seat->priv->sessions) == 0)
301     {
302         seat->priv->stopped = TRUE;
303         g_debug ("Seat stopped");
304         g_signal_emit (seat, signals[STOPPED], 0);
305     }
306 }
307
308 static void
309 display_server_stopped_cb (DisplayServer *display_server, Seat *seat)
310 {
311     GList *link;
312     Session *active_session;
313
314     g_debug ("Display server stopped");
315
316     g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
317     seat->priv->display_servers = g_list_remove (seat->priv->display_servers, display_server);
318
319     if (seat->priv->stopping)
320     {
321         check_stopped (seat);
322         g_object_unref (display_server);
323         return;
324     }
325
326     /* Stop all sessions on this display server */
327     for (link = seat->priv->sessions; link; link = link->next)
328     {
329         Session *session = link->data;
330
331         if (session_get_display_server (session) != display_server || session_get_is_stopping (session))
332             continue;
333
334         /* Stop seat if this is the only display server and it failed to start a greeter */
335         if (IS_GREETER (session) &&
336             !session_get_is_started (session) &&
337             g_list_length (seat->priv->display_servers) == 0)
338         {
339             g_debug ("Stopping seat, greeter display server failed to start");
340             seat_stop (seat);
341         }
342
343         g_debug ("Stopping session");
344         session_stop (session);
345     }
346
347     /* If we were the active session, switch to a greeter */
348     active_session = seat_get_active_session (seat);
349     if (!active_session || session_get_display_server (active_session) == display_server)
350     {
351         g_debug ("Active display server stopped, starting greeter");
352         seat_switch_to_greeter (seat);
353     }
354
355     g_object_unref (display_server);
356 }
357
358 static void
359 start_session (Seat *seat, Session *session)
360 {
361     Greeter *greeter_session;
362
363     if (session_start (session))
364         return;
365
366     if (IS_GREETER (session))
367     {
368         g_debug ("Failed to start greeter");
369         display_server_stop (session_get_display_server (session));
370         return;
371     }
372
373     g_debug ("Failed to start session, starting greeter");
374     greeter_session = create_greeter_session (seat);
375     if (seat->priv->session_to_activate)
376         g_object_unref (seat->priv->session_to_activate);
377     seat->priv->session_to_activate = g_object_ref (greeter_session);
378
379     if (seat->priv->share_display_server)
380         session_set_display_server (SESSION (greeter_session), session_get_display_server (session));
381     else
382     {
383         DisplayServer *display_server;
384
385         display_server = create_display_server (seat);
386         if (!display_server_start (display_server))
387         {
388             g_debug ("Failed to start display server for greeter");
389             seat_stop (seat);
390         }
391
392         session_set_display_server (session, display_server);
393     }
394
395     start_session (seat, SESSION (greeter_session));
396 }
397
398 static void
399 run_session (Seat *seat, Session *session)
400 {
401     const gchar *script;
402
403     if (IS_GREETER (session))
404         script = seat_get_string_property (seat, "greeter-setup-script");
405     else
406         script = seat_get_string_property (seat, "session-setup-script");
407     if (script && !run_script (seat, session_get_display_server (session), script, NULL))
408     {
409         Greeter *greeter_session;
410
411         g_debug ("Switching to greeter due to failed setup script");
412
413         greeter_session = create_greeter_session (seat);
414         if (seat->priv->session_to_activate)
415             g_object_unref (seat->priv->session_to_activate);
416         seat->priv->session_to_activate = g_object_ref (greeter_session);
417
418         if (seat->priv->share_display_server)
419             session_set_display_server (SESSION (greeter_session), session_get_display_server (session));
420         else
421         {
422             DisplayServer *display_server;
423
424             display_server = create_display_server (seat);
425             if (!display_server_start (display_server))
426             {
427                 g_debug ("Failed to start display server for greeter");
428                 seat_stop (seat);
429             }
430
431             session_set_display_server (SESSION (greeter_session), display_server);         
432         }
433
434         start_session (seat, SESSION (greeter_session));
435
436         /* Stop failed session */
437         session_stop (session);
438       
439         return;
440     }
441
442     session_run (session);
443
444     // FIXME: Wait until the session is ready
445
446     if (session == seat->priv->session_to_activate)
447     {
448         seat_set_active_session (seat, session);
449         g_object_unref (seat->priv->session_to_activate);
450         seat->priv->session_to_activate = NULL;
451     }
452 }
453
454 static void
455 session_authentication_complete_cb (Session *session, Seat *seat)
456 {
457     if (session_get_is_authenticated (session))
458     {
459         g_debug ("Session authenticated, running command");
460         run_session (seat, session);
461     }
462     else if (!IS_GREETER (session))
463     {
464         Greeter *greeter_session;
465
466         g_debug ("Switching to greeter due to failed authentication");
467
468         greeter_session = create_greeter_session (seat);
469         if (seat->priv->session_to_activate)
470             g_object_unref (seat->priv->session_to_activate);
471         seat->priv->session_to_activate = g_object_ref (greeter_session);
472         if (session_get_is_guest (session))
473             greeter_set_hint (greeter_session, "select-guest", "true");
474         else
475             greeter_set_hint (greeter_session, "select-user", session_get_username (session));
476
477         if (seat->priv->share_display_server)
478             session_set_display_server (SESSION (greeter_session), session_get_display_server (session));
479         else
480         {
481             DisplayServer *display_server;
482
483             display_server = create_display_server (seat);
484             if (!display_server_start (display_server))
485             {
486                 g_debug ("Failed to start display server for greeter");
487                 seat_stop (seat);
488             }
489
490             session_set_display_server (SESSION (greeter_session), display_server);         
491         }
492
493         start_session (seat, SESSION (greeter_session));
494
495         /* Stop failed session */
496         session_stop (session);
497     }
498     else
499     {
500         g_debug ("Stopping session that failed authentication");
501         session_stop (session);
502     }
503 }
504
505 static void
506 session_stopped_cb (Session *session, Seat *seat)
507 {
508     DisplayServer *display_server;
509
510     g_debug ("Session stopped");
511
512     display_server = session_get_display_server (session);
513
514     /* Cleanup */
515     if (!IS_GREETER (session))
516     {
517         const gchar *script;
518         script = seat_get_string_property (seat, "session-cleanup-script");
519         if (script)
520             run_script (seat, display_server, script, session_get_user (session));
521     }
522
523     g_signal_handlers_disconnect_matched (session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, seat);
524     seat->priv->sessions = g_list_remove (seat->priv->sessions, session);
525
526     /* We were waiting for this session, but it didn't start :( */
527     // FIXME: Start a greeter on this?
528     if (session == seat->priv->session_to_activate)
529     {
530         g_object_unref (seat->priv->session_to_activate);
531         seat->priv->session_to_activate = NULL;
532     }
533
534     if (seat->priv->stopping)
535     {
536         check_stopped (seat);
537         g_object_unref (session);
538         return;
539     }
540
541     /* If this is the greeter session then re-use this display server */
542     if (IS_GREETER (session) && seat->priv->share_display_server &&
543         greeter_get_start_session (GREETER (session)))
544     {
545         GList *link;
546
547         for (link = seat->priv->sessions; link; link = link->next)
548         {
549             Session *s = link->data;
550
551             /* Skip this session and sessions on other display servers */
552             if (s == session || session_get_display_server (s) != display_server || session_get_is_stopping (s))
553                 continue;
554
555             if (session_get_is_authenticated (s))
556             {
557                 g_debug ("Greeter stopped, running session");
558                 run_session (seat, s);
559             }
560             else
561             {
562                 g_debug ("Greeter stopped, starting session authentication");
563                 start_session (seat, s);
564             }
565             break;
566         }
567     }
568     /* If this is the greeter and nothing else is running then stop the seat */
569     else if (IS_GREETER (session) &&
570         !greeter_get_start_session (GREETER (session)) &&
571         g_list_length (seat->priv->display_servers) == 1 &&
572         g_list_nth_data (seat->priv->display_servers, 0) == display_server)
573     {
574         g_debug ("Stopping seat, failed to start a greeter");
575         seat_stop (seat);
576     }
577     /* If we were the active session, switch to a greeter */
578     else if (!IS_GREETER (session) && session == seat_get_active_session (seat))
579     {
580         g_debug ("Active session stopped, starting greeter");
581         seat_switch_to_greeter (seat);
582     }
583
584     /* Stop the display server if no-longer required */
585     if (display_server && !display_server_get_is_stopping (display_server))
586     {
587         GList *link;
588         int n_sessions = 0;
589
590         for (link = seat->priv->sessions; link; link = link->next)
591         {
592             Session *s = link->data;
593             if (s == session)
594                 continue;
595             if (session_get_display_server (s) == display_server)
596                 n_sessions++;
597         }
598         if (n_sessions == 0)
599         {
600             g_debug ("Stopping display server, no sessions require it");
601             display_server_stop (display_server);
602         }
603     }
604
605     g_object_unref (session);
606 }
607
608 static void
609 set_session_env (Session *session)
610 {
611     /* Variables required for regression tests */
612     if (g_getenv ("LIGHTDM_TEST_ROOT"))
613     {
614         session_set_env (session, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
615         session_set_env (session, "DBUS_SYSTEM_BUS_ADDRESS", g_getenv ("DBUS_SYSTEM_BUS_ADDRESS"));
616         session_set_env (session, "DBUS_SESSION_BUS_ADDRESS", g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
617         session_set_env (session, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
618         session_set_env (session, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
619         session_set_env (session, "GI_TYPELIB_PATH", g_getenv ("GI_TYPELIB_PATH"));
620     }
621 }
622
623 static Session *
624 create_session (Seat *seat, gboolean autostart)
625 {
626     Session *session;
627
628     session = SEAT_GET_CLASS (seat)->create_session (seat);
629     seat->priv->sessions = g_list_append (seat->priv->sessions, session);
630     if (autostart)
631         g_signal_connect (session, "authentication-complete", G_CALLBACK (session_authentication_complete_cb), seat);
632     g_signal_connect (session, "stopped", G_CALLBACK (session_stopped_cb), seat);
633
634     set_session_env (session);
635
636     return session;
637 }
638
639 static gchar **
640 get_session_argv_from_filename (const gchar *filename, const gchar *session_wrapper)
641 {
642     GKeyFile *session_desktop_file;
643     gboolean result;
644     int argc;
645     gchar *command = NULL, **argv, *path;
646     GError *error = NULL;
647
648     /* Read the command from the .desktop file */
649     session_desktop_file = g_key_file_new ();
650     result = g_key_file_load_from_file (session_desktop_file, filename, G_KEY_FILE_NONE, &error);
651     if (error)
652         g_debug ("Failed to load session file %s: %s", filename, error->message);
653     g_clear_error (&error);
654     if (result)
655     {
656         command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
657         if (!command)
658             g_debug ("No command in session file %s", filename);
659     }
660     g_key_file_free (session_desktop_file);
661
662     if (!command)
663         return NULL;
664
665     /* If configured, run sessions through a wrapper */
666     if (session_wrapper)
667     {
668         argv = g_malloc (sizeof (gchar *) * 3);
669         path = g_find_program_in_path (session_wrapper);
670         argv[0] = path ? path : g_strdup (session_wrapper);
671         argv[1] = command;
672         argv[2] = NULL;
673         return argv;
674     }
675
676     /* Split command into an array listing and make command absolute */
677     result = g_shell_parse_argv (command, &argc, &argv, &error);
678     if (error)
679         g_debug ("Invalid session command '%s': %s", command, error->message);
680     g_clear_error (&error);
681     g_free (command);
682     if (!result)
683         return NULL;
684     path = g_find_program_in_path (argv[0]);
685     if (path)
686     {
687         g_free (argv[0]);
688         argv[0] = path;
689     }
690   
691     return argv;
692 }
693
694 static gchar **
695 get_session_argv (const gchar *sessions_dir, const gchar *session_name, const gchar *session_wrapper)
696 {
697     gchar **dirs, **argv;
698     int i;
699
700     g_return_val_if_fail (sessions_dir != NULL, NULL);
701     g_return_val_if_fail (session_name != NULL, NULL);
702
703     dirs = g_strsplit (sessions_dir, ":", -1);
704     for (i = 0; dirs[i]; i++)
705     {
706         gchar *filename, *path;
707
708         filename = g_strdup_printf ("%s.desktop", session_name);
709         path = g_build_filename (dirs[i], filename, NULL);
710         g_free (filename);
711         argv = get_session_argv_from_filename (path, session_wrapper);
712         g_free (path);
713         if (argv)
714             break;
715     }
716     g_strfreev (dirs);
717
718     return argv;
719 }
720
721 static Session *
722 create_user_session (Seat *seat, const gchar *username)
723 {
724     User *user;
725     gchar *sessions_dir, **argv;
726     const gchar *session_name;
727     Session *session;
728   
729     user = accounts_get_user_by_name (username);
730     if (!user)
731     {
732         g_debug ("Can't login unknown user '%s'", username);
733         return NULL;
734     }
735
736     session_name = user_get_xsession (user);
737     g_object_unref (user);
738     if (!session_name)
739         session_name = seat_get_string_property (seat, "user-session");
740     sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
741     argv = get_session_argv (sessions_dir, session_name, seat_get_string_property (seat, "session-wrapper"));
742     g_free (sessions_dir);
743     if (!argv)
744     {
745         g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
746         return NULL;
747     }
748
749     session = create_session (seat, TRUE);
750     session_set_pam_service (session, AUTOLOGIN_SERVICE);
751     session_set_username (session, username);
752     session_set_do_authenticate (session, TRUE);
753     session_set_argv (session, argv);
754
755     return session;
756 }
757
758 static Session *
759 create_guest_session (Seat *seat)
760 {
761     gchar *sessions_dir, **argv;
762     Session *session;
763
764     sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
765     argv = get_session_argv (sessions_dir,
766                              seat_get_string_property (seat, "user-session"),
767                              seat_get_string_property (seat, "session-wrapper"));
768     g_free (sessions_dir);
769     if (!argv)
770     {
771         g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
772         return NULL;
773     }
774
775     session = create_session (seat, TRUE);
776     session_set_do_authenticate (session, TRUE);
777     session_set_is_guest (session, TRUE);
778     session_set_argv (session, argv);
779   
780     return session;
781 }
782
783 static Session *
784 greeter_create_session_cb (Greeter *greeter, Seat *seat)
785 {
786     Session *session;
787
788     session = create_session (seat, FALSE);
789     session_set_display_server (session, session_get_display_server (SESSION (greeter)));
790
791     return g_object_ref (session);
792 }
793
794 static void
795 prepend_argv (gchar ***argv, const gchar *value)
796 {
797     gchar **old_argv, **new_argv;
798     gint i;
799
800     old_argv = *argv;
801     new_argv = g_malloc (sizeof (gchar *) * (g_strv_length (*argv) + 2));
802     new_argv[0] = g_strdup (value);
803     for (i = 0; old_argv[i]; i++)
804         new_argv[i + 1] = old_argv[i];
805     new_argv[i + 1] = NULL;
806
807     g_free (*argv);
808     *argv = new_argv;
809 }
810
811 static Session *
812 find_user_session (Seat *seat, const gchar *username)
813 {
814     GList *link;
815
816     if (!username)
817         return NULL;
818
819     for (link = seat->priv->sessions; link; link = link->next)
820     {
821         Session *session = link->data;
822
823         if (!session_get_is_stopping (session) && strcmp (session_get_username (session), username) == 0)
824             return session;
825     }
826
827     return NULL;
828 }
829
830 static gboolean
831 greeter_start_session_cb (Greeter *greeter, SessionType type, const gchar *session_name, Seat *seat)
832 {
833     Session *session, *existing_session;
834     const gchar *username;
835     User *user;
836     gchar *sessions_dir = NULL;
837     gchar **argv;
838
839     /* Get the session to use */
840     if (greeter_get_guest_authenticated (greeter))
841     {
842         session = create_guest_session (seat);
843         session_set_pam_service (session, AUTOLOGIN_SERVICE);
844     }
845     else
846         session = greeter_get_authentication_session (greeter);
847
848     /* Switch to this session when it is ready */
849     if (seat->priv->session_to_activate)
850         g_object_unref (seat->priv->session_to_activate);
851     seat->priv->session_to_activate = g_object_ref (session);
852
853     /* Return to existing session if it is open */
854     username = session_get_username (session);
855     existing_session = find_user_session (seat, username);
856     if (existing_session && session != existing_session)
857     {
858         g_debug ("Returning to existing user session %s", username);
859         session_stop (session);
860         seat_set_active_session (seat, existing_session);
861         return TRUE;
862     }
863
864     /* Get session command to run */
865     switch (type)
866     {
867     case SESSION_TYPE_LOCAL:
868         sessions_dir = config_get_string (config_get_instance (), "LightDM", "sessions-directory");
869         break;
870     case SESSION_TYPE_REMOTE:
871         sessions_dir = config_get_string (config_get_instance (), "LightDM", "remote-sessions-directory");
872         break;
873     }
874     user = session_get_user (session);
875     if (!session_name && user)
876         session_name = user_get_xsession (user);
877     if (!session_name)
878         session_name = seat_get_string_property (seat, "user-session");
879     if (user)
880         user_set_xsession (session_get_user (session), session_name);
881     argv = get_session_argv (sessions_dir, session_name, seat_get_string_property (seat, "session-wrapper"));
882     g_free (sessions_dir);
883   
884     if (!argv)
885     {
886         g_debug ("Can't find session '%s'", seat_get_string_property (seat, "user-session"));
887         return FALSE;
888     }
889
890     session_set_argv (session, argv);
891     g_strfreev (argv);
892
893     /* If no session information found, then can't start the session */
894     if (!argv)
895     {
896         g_debug ("Can't find greeter session '%s'", session_name);
897         return FALSE;
898     }
899
900     /* If can re-use the display server, stop the greeter first */
901     if (seat->priv->share_display_server)
902     {
903         /* Run on the same display server after the greeter has stopped */
904         session_set_display_server (session, session_get_display_server (SESSION (greeter)));
905
906         g_debug ("Stopping greeter");
907         session_stop (SESSION (greeter));
908
909         return TRUE;
910     }
911     /* Otherwise start a new display server for this session */
912     else
913     {
914         DisplayServer *display_server;
915
916         display_server = create_display_server (seat);
917         if (!display_server_start (display_server))
918             return FALSE;
919
920         session_set_display_server (session, display_server);
921
922         return TRUE;
923     }
924 }
925
926 static Greeter *
927 create_greeter_session (Seat *seat)
928 {
929     gchar *sessions_dir, **argv;
930     Greeter *greeter_session;
931     gchar *greeter_user;
932     const gchar *greeter_wrapper;
933
934     sessions_dir = config_get_string (config_get_instance (), "LightDM", "greeters-directory");
935     argv = get_session_argv (sessions_dir,
936                              seat_get_string_property (seat, "greeter-session"),
937                              seat_get_string_property (seat, "session-wrapper"));
938     g_free (sessions_dir);
939     if (!argv)
940         return NULL;
941
942     greeter_wrapper = seat_get_string_property (seat, "greeter-wrapper");
943     if (greeter_wrapper)
944     {
945         gchar *path;
946         path = g_find_program_in_path (greeter_wrapper);
947         prepend_argv (&argv, path ? path : greeter_wrapper);
948         g_free (path);
949     }
950
951     greeter_session = SEAT_GET_CLASS (seat)->create_greeter_session (seat);
952     seat->priv->sessions = g_list_append (seat->priv->sessions, SESSION (greeter_session));
953     g_signal_connect (greeter_session, "authentication-complete", G_CALLBACK (session_authentication_complete_cb), seat);
954     g_signal_connect (greeter_session, "stopped", G_CALLBACK (session_stopped_cb), seat);
955   
956     set_session_env (SESSION (greeter_session));
957
958     session_set_pam_service (SESSION (greeter_session), GREETER_SERVICE);
959     greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
960     session_set_username (SESSION (greeter_session), greeter_user);
961     g_free (greeter_user);
962     session_set_argv (SESSION (greeter_session), argv);
963
964     greeter_set_pam_services (greeter_session, USER_SERVICE, AUTOLOGIN_SERVICE);
965     greeter_set_allow_guest (greeter_session, seat_get_allow_guest (seat));
966     g_signal_connect (greeter_session, "create-session", G_CALLBACK (greeter_create_session_cb), seat);
967     g_signal_connect (greeter_session, "start-session", G_CALLBACK (greeter_start_session_cb), seat);
968
969     return greeter_session;
970 }
971
972 static Session *
973 find_session_for_display_server (Seat *seat, DisplayServer *display_server)
974 {
975     GList *link;
976
977     for (link = seat->priv->sessions; link; link = link->next)
978     {
979         Session *session = link->data;
980         if (session_get_display_server (session) == display_server && !session_get_is_stopping (session))
981             return session;
982     }
983
984     return NULL;
985 }
986
987 static void
988 display_server_ready_cb (DisplayServer *display_server, Seat *seat)
989 {
990     const gchar *script;
991     Session *session;
992
993     /* Run setup script */
994     script = seat_get_string_property (seat, "display-setup-script");
995     if (script && !run_script (seat, display_server, script, NULL))
996     {
997         g_debug ("Stopping display server due to failed setup script");
998         display_server_stop (display_server);
999         return;
1000     }
1001
1002     emit_upstart_signal ("login-session-start");
1003
1004     /* Start the session waiting for this display server */
1005     session = find_session_for_display_server (seat, display_server);
1006     if (session)
1007     {
1008         if (session_get_is_authenticated (session))
1009         {
1010             g_debug ("Display server ready, running session");
1011             run_session (seat, session);
1012         }
1013         else
1014         {
1015             g_debug ("Display server ready, starting session authentication");
1016             start_session (seat, session);
1017         }
1018     }
1019     else
1020     {
1021         g_debug ("Stopping not required display server");
1022         display_server_stop (display_server);
1023     }
1024 }
1025
1026 static DisplayServer *
1027 create_display_server (Seat *seat)
1028 {
1029     DisplayServer *display_server;
1030
1031     display_server = SEAT_GET_CLASS (seat)->create_display_server (seat);
1032     seat->priv->display_servers = g_list_append (seat->priv->display_servers, display_server);
1033     g_signal_connect (display_server, "ready", G_CALLBACK (display_server_ready_cb), seat);
1034     g_signal_connect (display_server, "stopped", G_CALLBACK (display_server_stopped_cb), seat);
1035
1036     return display_server;
1037 }
1038
1039 static Greeter *
1040 find_greeter_session (Seat *seat)
1041 {
1042     GList *link;
1043
1044     for (link = seat->priv->sessions; link; link = link->next)
1045     {
1046         Session *session = link->data;
1047         if (!session_get_is_stopping (session) && IS_GREETER (session))
1048             return GREETER (session);
1049     }
1050
1051     return NULL;
1052 }
1053
1054 gboolean
1055 seat_switch_to_greeter (Seat *seat)
1056 {
1057     Greeter *greeter_session;
1058     DisplayServer *display_server;
1059
1060     g_return_val_if_fail (seat != NULL, FALSE);
1061
1062     if (!seat->priv->can_switch)
1063         return FALSE;
1064
1065     /* Switch to greeter if one open (shouldn't be though) */
1066     greeter_session = find_greeter_session (seat);
1067     if (greeter_session)
1068     {
1069         g_debug ("Switching to existing greeter");
1070         seat_set_active_session (seat, SESSION (greeter_session));
1071         return TRUE;
1072     }
1073
1074     greeter_session = create_greeter_session (seat);
1075     if (seat->priv->session_to_activate)
1076         g_object_unref (seat->priv->session_to_activate);
1077     seat->priv->session_to_activate = g_object_ref (greeter_session);
1078
1079     display_server = create_display_server (seat);
1080     session_set_display_server (SESSION (greeter_session), display_server);
1081     if (!display_server_start (display_server))
1082         return FALSE;
1083
1084     return TRUE;
1085 }
1086
1087 gboolean
1088 seat_switch_to_user (Seat *seat, const gchar *username, const gchar *session_name)
1089 {
1090     Session *session;
1091     DisplayServer *display_server;
1092
1093     g_return_val_if_fail (seat != NULL, FALSE);
1094     g_return_val_if_fail (username != NULL, FALSE);
1095
1096     if (!seat->priv->can_switch)
1097         return FALSE;
1098
1099     g_debug ("Switching to user %s", username);
1100
1101     session = find_user_session (seat, username);
1102     if (session)
1103     {
1104         g_debug ("Switching to existing user session %s", username);
1105         seat_set_active_session (seat, session);
1106         return TRUE;
1107     }
1108
1109     session = create_user_session (seat, username);
1110     if (seat->priv->session_to_activate)
1111         g_object_unref (seat->priv->session_to_activate);
1112     seat->priv->session_to_activate = g_object_ref (session);
1113     session_set_pam_service (session, USER_SERVICE);
1114
1115     display_server = create_display_server (seat);
1116     session_set_display_server (session, display_server);
1117     if (!display_server_start (display_server))
1118         return FALSE;
1119
1120     return FALSE;
1121 }
1122
1123 static Session *
1124 find_guest_session (Seat *seat)
1125 {
1126     GList *link;
1127
1128     for (link = seat->priv->sessions; link; link = link->next)
1129     {
1130         Session *session = link->data;
1131         if (!session_get_is_stopping (session) && session_get_is_guest (session))
1132             return session;
1133     }
1134
1135     return NULL;
1136 }
1137
1138 gboolean
1139 seat_switch_to_guest (Seat *seat, const gchar *session_name)
1140 {
1141     Session *session;
1142     DisplayServer *display_server;
1143     GList *link;
1144
1145     g_return_val_if_fail (seat != NULL, FALSE);
1146
1147     if (!seat->priv->can_switch || !seat_get_allow_guest (seat))
1148         return FALSE;
1149
1150     /* Switch to session if one open */
1151     session = find_guest_session (seat);
1152     if (session)
1153     {
1154         g_debug ("Switching to existing guest account %s", session_get_username (session));
1155         seat_set_active_session (seat, session);
1156         return TRUE;
1157     }
1158
1159     display_server = create_display_server (seat);
1160     if (!display_server_start (display_server))
1161         return FALSE;
1162
1163     session = create_guest_session (seat);
1164     if (seat->priv->session_to_activate)
1165         g_object_unref (seat->priv->session_to_activate);
1166     seat->priv->session_to_activate = g_object_ref (session);
1167     session_set_pam_service (session, AUTOLOGIN_SERVICE);
1168     session_set_display_server (session, display_server);
1169
1170     return TRUE;
1171 }
1172
1173 gboolean
1174 seat_lock (Seat *seat, const gchar *username)
1175 {
1176     Greeter *greeter_session;
1177     DisplayServer *display_server;
1178
1179     g_return_val_if_fail (seat != NULL, FALSE);
1180
1181     if (!seat->priv->can_switch)
1182         return FALSE;
1183
1184     g_debug ("Locking seat");
1185
1186     /* Switch to greeter if one open (shouldn't be though) */
1187     greeter_session = find_greeter_session (seat);
1188     if (greeter_session)
1189     {
1190         g_debug ("Switching to existing greeter");
1191         seat_set_active_session (seat, SESSION (greeter_session));
1192         return TRUE;
1193     }
1194
1195     display_server = create_display_server (seat);
1196     if (!display_server_start (display_server))
1197         return FALSE;
1198
1199     greeter_session = create_greeter_session (seat);
1200     if (seat->priv->session_to_activate)
1201         g_object_unref (seat->priv->session_to_activate);
1202     seat->priv->session_to_activate = g_object_ref (greeter_session);
1203     greeter_set_hint (greeter_session, "lock-screen", "true");
1204     if (username)
1205         greeter_set_hint (greeter_session, "select-user", username);
1206     session_set_display_server (SESSION (greeter_session), display_server);
1207
1208     return TRUE;
1209 }
1210
1211 void
1212 seat_stop (Seat *seat)
1213 {
1214     g_return_if_fail (seat != NULL);
1215
1216     if (seat->priv->stopping)
1217         return;
1218
1219     g_debug ("Stopping seat");
1220     seat->priv->stopping = TRUE;
1221     SEAT_GET_CLASS (seat)->stop (seat);
1222 }
1223
1224 gboolean
1225 seat_get_is_stopping (Seat *seat)
1226 {
1227     g_return_val_if_fail (seat != NULL, FALSE);
1228     return seat->priv->stopping;
1229 }
1230
1231 static void
1232 seat_real_setup (Seat *seat)
1233 {
1234 }
1235
1236 static gboolean
1237 seat_real_start (Seat *seat)
1238 {
1239     const gchar *autologin_username;
1240     int autologin_timeout;
1241     gboolean autologin_guest;
1242     gboolean autologin_in_background;
1243     const gchar *user_session;
1244     Session *session = NULL;
1245     DisplayServer *display_server;
1246     User *user;
1247     gchar *sessions_dir;
1248     const gchar *wrapper = NULL;
1249     const gchar *session_name = NULL;
1250
1251     g_debug ("Starting seat");
1252
1253     display_server = create_display_server (seat);
1254
1255     /* Get autologin settings */
1256     autologin_username = seat_get_string_property (seat, "autologin-user");
1257     if (g_strcmp0 (autologin_username, "") == 0)
1258         autologin_username = NULL;
1259     autologin_timeout = seat_get_integer_property (seat, "autologin-user-timeout");
1260     autologin_guest = seat_get_boolean_property (seat, "autologin-guest");
1261     autologin_in_background = seat_get_boolean_property (seat, "autologin-in-background");
1262
1263     /* Autologin if configured */
1264     if (autologin_timeout == 0 || autologin_in_background)
1265     {
1266         if (autologin_guest)
1267         {
1268             session = create_guest_session (seat);
1269             if (seat->priv->session_to_activate)
1270                 g_object_unref (seat->priv->session_to_activate);
1271             seat->priv->session_to_activate = g_object_ref (session);
1272             session_set_pam_service (session, AUTOLOGIN_SERVICE);
1273         }
1274         else if (autologin_username != NULL)
1275         {
1276             session = create_user_session (seat, autologin_username);
1277             if (seat->priv->session_to_activate)
1278                 g_object_unref (seat->priv->session_to_activate);
1279             seat->priv->session_to_activate = g_object_ref (session);
1280             session_set_pam_service (session, AUTOLOGIN_SERVICE);
1281         }
1282
1283         /* Load in background if required */
1284         if (autologin_in_background && session)
1285         {
1286             DisplayServer *background_display_server;
1287
1288             background_display_server = create_display_server (seat);
1289             session_set_display_server (session, background_display_server);
1290             if (!display_server_start (background_display_server))
1291                 return FALSE;
1292
1293             /* Start a greeter as well */
1294             session = NULL;
1295         }
1296     }
1297
1298     /* Fallback to a greeter */
1299     if (!session)
1300     {
1301         Greeter *greeter_session;
1302
1303         greeter_session = create_greeter_session (seat);
1304         if (seat->priv->session_to_activate)
1305             g_object_unref (seat->priv->session_to_activate);
1306         seat->priv->session_to_activate = g_object_ref (greeter_session);
1307         session = SESSION (greeter_session);
1308
1309         if (autologin_timeout)
1310         {
1311             gchar *value;
1312
1313             value = g_strdup_printf ("%d", autologin_timeout);
1314             greeter_set_hint (greeter_session, "autologin-timeout", value);
1315             g_free (value);
1316             if (autologin_username)
1317                 greeter_set_hint (greeter_session, "autologin-user", autologin_username);
1318             if (autologin_guest)
1319                 greeter_set_hint (greeter_session, "autologin-guest", "true");
1320         }
1321     }
1322
1323     /* Fail if can't start a session */
1324     if (!session)
1325     {
1326         seat_stop (seat);
1327         return FALSE;
1328     }
1329
1330     /* Start display server to show session on */
1331     session_set_display_server (session, display_server);
1332     if (!display_server_start (display_server))
1333         return FALSE;
1334
1335     return TRUE;
1336 }
1337
1338 static void
1339 seat_real_set_active_session (Seat *seat, Session *session)
1340 {
1341 }
1342
1343 static Session *
1344 seat_real_get_active_session (Seat *seat)
1345 {
1346     return NULL;
1347 }
1348
1349 static void
1350 seat_real_stop (Seat *seat)
1351 {
1352     GList *list, *link;
1353
1354     check_stopped (seat);
1355     if (seat->priv->stopped)
1356         return;
1357
1358     /* Stop all the display servers and sessions on the seat. Copy the list as
1359      * it might be modified if a display server / session stops during this loop */
1360     list = g_list_copy (seat->priv->display_servers);
1361     for (link = list; link; link = link->next)
1362     {
1363         DisplayServer *display_server = link->data;
1364         if (!display_server_get_is_stopping (display_server))
1365         {
1366             g_debug ("Stopping display server");
1367             display_server_stop (display_server);
1368         }
1369     }
1370     g_list_free (list);
1371     list = g_list_copy (seat->priv->sessions);
1372     for (link = list; link; link = link->next)
1373     {
1374         Session *session = link->data;
1375         if (!session_get_is_stopping (session))
1376         {
1377             g_debug ("Stopping session");
1378             session_stop (session);
1379         }
1380     }
1381     g_list_free (list);
1382 }
1383
1384 static void
1385 seat_init (Seat *seat)
1386 {
1387     seat->priv = G_TYPE_INSTANCE_GET_PRIVATE (seat, SEAT_TYPE, SeatPrivate);
1388     seat->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1389     seat->priv->share_display_server = TRUE;
1390 }
1391
1392 static void
1393 seat_finalize (GObject *object)
1394 {
1395     Seat *self;
1396     GList *link;
1397
1398     self = SEAT (object);
1399
1400     g_hash_table_unref (self->priv->properties);
1401     for (link = self->priv->display_servers; link; link = link->next)
1402     {
1403         DisplayServer *display_server = link->data;
1404         g_signal_handlers_disconnect_matched (display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
1405     }  
1406     g_list_free_full (self->priv->display_servers, g_object_unref);
1407     for (link = self->priv->sessions; link; link = link->next)
1408     {
1409         Session *session = link->data;
1410         g_signal_handlers_disconnect_matched (session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
1411     }
1412     g_list_free_full (self->priv->sessions, g_object_unref);
1413     if (self->priv->session_to_activate)
1414         g_object_unref (self->priv->session_to_activate);
1415
1416     G_OBJECT_CLASS (seat_parent_class)->finalize (object);
1417 }
1418
1419 static void
1420 seat_class_init (SeatClass *klass)
1421 {
1422     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1423
1424     klass->setup = seat_real_setup;
1425     klass->start = seat_real_start;
1426     klass->set_active_session = seat_real_set_active_session;
1427     klass->get_active_session = seat_real_get_active_session;
1428     klass->run_script = seat_real_run_script;
1429     klass->stop = seat_real_stop;
1430
1431     object_class->finalize = seat_finalize;
1432
1433     g_type_class_add_private (klass, sizeof (SeatPrivate));
1434
1435     signals[SESSION_ADDED] =
1436         g_signal_new ("session-added",
1437                       G_TYPE_FROM_CLASS (klass),
1438                       G_SIGNAL_RUN_LAST,
1439                       G_STRUCT_OFFSET (SeatClass, session_added),
1440                       NULL, NULL,
1441                       g_cclosure_marshal_VOID__OBJECT,
1442                       G_TYPE_NONE, 1, SESSION_TYPE);
1443     signals[SESSION_REMOVED] =
1444         g_signal_new ("session-removed",
1445                       G_TYPE_FROM_CLASS (klass),
1446                       G_SIGNAL_RUN_LAST,
1447                       G_STRUCT_OFFSET (SeatClass, session_removed),
1448                       NULL, NULL,
1449                       g_cclosure_marshal_VOID__OBJECT,
1450                       G_TYPE_NONE, 1, SESSION_TYPE);
1451     signals[STOPPED] =
1452         g_signal_new ("stopped",
1453                       G_TYPE_FROM_CLASS (klass),
1454                       G_SIGNAL_RUN_LAST,
1455                       G_STRUCT_OFFSET (SeatClass, stopped),
1456                       NULL, NULL,
1457                       g_cclosure_marshal_VOID__VOID,
1458                       G_TYPE_NONE, 0);
1459 }