]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display.c
insert our private path into user's PATH
[sojka/lightdm.git] / src / display.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 <config.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <gio/gdesktopappinfo.h>
16
17 #include "display.h"
18 #include "configuration.h"
19 #include "user.h"
20 #include "pam-session.h"
21 #include "dmrc.h"
22 #include "ldm-marshal.h"
23 #include "greeter.h"
24 #include "xserver-local.h" // FIXME: Shouldn't know if it's an xserver
25
26 enum {
27     STARTED,
28     READY,
29     SWITCH_TO_USER,
30     SWITCH_TO_GUEST,
31     GET_GUEST_USERNAME,
32     SESSION_STARTED,
33     SESSION_STOPPED,
34     STOPPED,
35     LAST_SIGNAL
36 };
37 static guint signals[LAST_SIGNAL] = { 0 };
38
39 typedef enum
40 {
41     SESSION_NONE = 0,
42     SESSION_GREETER_PRE_CONNECT,
43     SESSION_GREETER,
44     SESSION_GREETER_AUTHENTICATED,
45     SESSION_USER
46 } SessionType;
47
48 struct DisplayPrivate
49 {
50     /* Display server */
51     DisplayServer *display_server;
52
53     /* User to run greeter as */
54     gchar *greeter_user;
55
56     /* Greeter session */
57     gchar *greeter_session;
58
59     /* TRUE if the user list should be shown */
60     gboolean greeter_hide_users;
61
62     /* Session requested to log into */
63     gchar *user_session;
64   
65     /* Directory to load X sessions from */
66     gchar *xsessions_dir;
67
68     /* Directory to load X greeters from */
69     gchar *xgreeters_dir;
70
71     /* Program to run sessions through */
72     gchar *session_wrapper;
73
74     /* PAM service to authenticate against */
75     gchar *pam_service;
76
77     /* PAM service to authenticate against for automatic logins */
78     gchar *pam_autologin_service;
79
80     /* TRUE if in a user session */
81     gboolean in_user_session;
82   
83     /* TRUE if have emitted ready signal */
84     gboolean indicated_ready;
85   
86     /* Session process */
87     Session *session;
88
89     /* Communication link to greeter */
90     Greeter *greeter;
91
92     /* Current PAM session */
93     PAMSession *pam_session;
94
95     /* User that should be automatically logged in */
96     gchar *autologin_user;
97     gboolean autologin_guest;
98     gint autologin_timeout;
99
100     /* TRUE if start greeter if fail to login */
101     gboolean start_greeter_if_fail;
102   
103     /* Hint to select user in greeter */
104     gchar *select_user_hint;
105     gboolean select_guest_hint;
106   
107     /* TRUE if allowed to log into guest account */
108     gboolean allow_guest;
109
110     /* TRUE if stopping the display (waiting for dispaly server, greeter and session to stop) */
111     gboolean stopping;    
112 };
113
114 G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
115
116 static gboolean start_greeter_session (Display *display);
117 static gboolean start_user_session (Display *display);
118
119 // FIXME: Should be a construct property
120 // FIXME: Move into seat.c
121 void
122 display_load_config (Display *display, const gchar *config_section)
123 {
124     g_return_if_fail (display != NULL);
125     
126     display->priv->greeter_user = config_get_string (config_get_instance (), "LightDM", "greeter-user");
127
128     if (config_section)
129         display->priv->greeter_session = config_get_string (config_get_instance (), config_section, "greeter-session");
130     if (!display->priv->greeter_session)
131         display->priv->greeter_session = config_get_string (config_get_instance (), "SeatDefaults", "greeter-session");
132     if (config_section && config_has_key (config_get_instance (), config_section, "greeter-hide-users"))
133         display->priv->greeter_hide_users = config_get_boolean (config_get_instance (), config_section, "greeter-hide-users");
134     else if (config_has_key (config_get_instance (), "SeatDefaults", "greeter-hide-users"))
135         display->priv->greeter_hide_users = config_get_boolean (config_get_instance (), "SeatDefaults", "greeter-hide-users");
136     if (config_section)
137         display->priv->user_session = config_get_string (config_get_instance (), config_section, "user-session");
138     if (!display->priv->user_session)
139         display->priv->user_session = config_get_string (config_get_instance (), "SeatDefaults", "user-session");
140     if (config_section)
141         display->priv->xsessions_dir = config_get_string (config_get_instance (), config_section, "xsessions-directory");
142     if (!display->priv->xsessions_dir)
143         display->priv->xsessions_dir = config_get_string (config_get_instance (), "SeatDefaults", "xsessions-directory");
144     if (config_section)
145         display->priv->xgreeters_dir = config_get_string (config_get_instance (), config_section, "xgreeters-directory");
146     if (!display->priv->xgreeters_dir)
147         display->priv->xgreeters_dir = config_get_string (config_get_instance (), "SeatDefaults", "xgreeters-directory");
148     if (config_section)
149         display->priv->session_wrapper = config_get_string (config_get_instance (), config_section, "session-wrapper");
150     if (!display->priv->session_wrapper)
151         display->priv->session_wrapper = config_get_string (config_get_instance (), "SeatDefaults", "session-wrapper");
152 }
153
154 // FIXME: Should be a construct property
155 void
156 display_set_display_server (Display *display, DisplayServer *display_server)
157 {
158     g_return_if_fail (display != NULL);
159     g_return_if_fail (display->priv->display_server == NULL);
160     display->priv->display_server = g_object_ref (display_server);
161 }
162
163 DisplayServer *
164 display_get_display_server (Display *display)
165 {
166     g_return_val_if_fail (display != NULL, NULL);
167     return display->priv->display_server;
168 }
169
170 const gchar *
171 display_get_username (Display *display)
172 {
173     g_return_val_if_fail (display != NULL, NULL);
174
175     if (display->priv->pam_session && display->priv->in_user_session)
176         return pam_session_get_username (display->priv->pam_session);
177     else
178         return NULL;
179 }
180
181 Session *
182 display_get_session (Display *display)
183 {
184     g_return_val_if_fail (display != NULL, NULL);
185     return display->priv->session;
186 }
187
188 void
189 display_set_allow_guest (Display *display, gboolean allow_guest)
190 {
191     g_return_if_fail (display != NULL);
192     display->priv->allow_guest = allow_guest;
193 }
194
195 void
196 display_set_autologin_user (Display *display, const gchar *username, gboolean is_guest, gint timeout)
197 {
198     g_return_if_fail (display != NULL);
199     g_free (display->priv->autologin_user);
200     display->priv->autologin_user = g_strdup (username);
201     display->priv->autologin_guest = is_guest;
202     display->priv->autologin_timeout = timeout;
203 }
204
205 void
206 display_set_select_user_hint (Display *display, const gchar *username, gboolean is_guest)
207 {
208     g_return_if_fail (display != NULL);
209     g_free (display->priv->select_user_hint);
210     display->priv->select_user_hint = g_strdup (username);
211     display->priv->select_guest_hint = is_guest;
212 }
213
214 void
215 display_set_user_session (Display *display, const gchar *session_name)
216 {
217     g_return_if_fail (display != NULL);
218     if (session_name)
219     {
220         g_free (display->priv->user_session);
221         display->priv->user_session = g_strdup (session_name);
222     }
223 }
224
225 static gboolean
226 switch_to_user (Display *display, const gchar *username)
227 {
228     gboolean result;
229     g_signal_emit (display, signals[SWITCH_TO_USER], 0, username, &result);
230     return result;
231 }
232
233 static gboolean
234 switch_to_guest (Display *display)
235 {
236     gboolean result;
237     g_signal_emit (display, signals[SWITCH_TO_GUEST], 0, &result);
238     return result;
239 }
240
241 static gchar *
242 get_guest_username (Display *display)
243 {
244     gchar *username;
245     g_signal_emit (display, signals[GET_GUEST_USERNAME], 0, &username);
246     return username;
247 }
248
249 static gchar *
250 start_ck_session (Display *display, const gchar *session_type, User *user)
251 {
252     GDBusProxy *proxy;
253     const gchar *hostname = "";
254     GVariantBuilder arg_builder;
255     GVariant *result;
256     gchar *cookie = NULL;
257     GError *error = NULL;
258
259     /* Only start ConsoleKit sessions when running as root */
260     if (getuid () != 0)
261         return NULL;
262
263     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
264                                            G_DBUS_PROXY_FLAGS_NONE,
265                                            NULL,
266                                            "org.freedesktop.ConsoleKit",
267                                            "/org/freedesktop/ConsoleKit/Manager",
268                                            "org.freedesktop.ConsoleKit.Manager", 
269                                            NULL, &error);
270     if (!proxy)
271         g_warning ("Unable to get connection to ConsoleKit: %s", error->message);
272     g_clear_error (&error);
273     if (!proxy)
274         return NULL;
275
276     g_variant_builder_init (&arg_builder, G_VARIANT_TYPE ("(a(sv))"));
277     g_variant_builder_open (&arg_builder, G_VARIANT_TYPE ("a(sv)"));
278     g_variant_builder_add (&arg_builder, "(sv)", "unix-user", g_variant_new_int32 (user_get_uid (user)));
279     g_variant_builder_add (&arg_builder, "(sv)", "session-type", g_variant_new_string (session_type));
280     if (IS_XSERVER (display->priv->display_server))
281     {
282         g_variant_builder_add (&arg_builder, "(sv)", "x11-display",
283                                g_variant_new_string (xserver_get_address (XSERVER (display->priv->display_server))));
284
285         if (IS_XSERVER_LOCAL (display->priv->display_server) && xserver_local_get_vt (XSERVER_LOCAL (display->priv->display_server)) >= 0)
286         {
287             gchar *display_device;
288             display_device = g_strdup_printf ("/dev/tty%d", xserver_local_get_vt (XSERVER_LOCAL (display->priv->display_server)));
289             g_variant_builder_add (&arg_builder, "(sv)", "x11-display-device", g_variant_new_string (display_device));
290             g_free (display_device);
291         }
292     }
293
294     g_variant_builder_add (&arg_builder, "(sv)", "remote-host-name", g_variant_new_string (hostname));
295     g_variant_builder_add (&arg_builder, "(sv)", "is-local", g_variant_new_boolean (TRUE));
296     g_variant_builder_close (&arg_builder);
297
298     result = g_dbus_proxy_call_sync (proxy,
299                                      "OpenSessionWithParameters",
300                                      g_variant_builder_end (&arg_builder),
301                                      G_DBUS_CALL_FLAGS_NONE,
302                                      -1,
303                                      NULL,
304                                      &error);
305     g_object_unref (proxy);
306
307     if (!result)
308         g_warning ("Failed to open CK session: %s", error->message);
309     g_clear_error (&error);
310     if (!result)
311         return NULL;
312
313     if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
314         g_variant_get (result, "(s)", &cookie);
315     else
316         g_warning ("Unexpected response from OpenSessionWithParameters: %s", g_variant_get_type_string (result));
317     g_variant_unref (result);
318
319     if (cookie)
320         g_debug ("Opened ConsoleKit session %s", cookie);
321
322     return cookie;
323 }
324
325 static void
326 end_ck_session (const gchar *cookie)
327 {
328     GDBusProxy *proxy;
329     GVariant *result;
330     GError *error = NULL;
331
332     if (!cookie)
333         return;
334
335     g_debug ("Ending ConsoleKit session %s", cookie);
336
337     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
338                                            G_DBUS_PROXY_FLAGS_NONE,
339                                            NULL,
340                                            "org.freedesktop.ConsoleKit",
341                                            "/org/freedesktop/ConsoleKit/Manager",
342                                            "org.freedesktop.ConsoleKit.Manager", 
343                                            NULL, NULL);
344     result = g_dbus_proxy_call_sync (proxy,
345                                      "CloseSession",
346                                      g_variant_new ("(s)", cookie),
347                                      G_DBUS_CALL_FLAGS_NONE,
348                                      -1,
349                                      NULL,
350                                      &error);
351     g_object_unref (proxy);
352
353     if (!result)
354         g_warning ("Error ending ConsoleKit session: %s", error->message);
355     g_clear_error (&error);
356     if (!result)
357         return;
358
359     if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
360     {
361         gboolean is_closed;
362         g_variant_get (result, "(b)", &is_closed);
363         if (!is_closed)
364             g_warning ("ConsoleKit.Manager.CloseSession() returned false");
365     }
366     else
367         g_warning ("Unexpected response from CloseSession: %s", g_variant_get_type_string (result));
368
369     g_variant_unref (result);
370 }
371
372 static void
373 set_env_from_pam_session (Session *session, PAMSession *pam_session)
374 {
375     gchar **pam_env;
376
377     pam_env = pam_session_get_envlist (pam_session);
378     if (pam_env)
379     {
380         gchar *env_string;      
381         int i;
382
383         env_string = g_strjoinv (" ", pam_env);
384         g_debug ("PAM returns environment '%s'", env_string);
385         g_free (env_string);
386
387         for (i = 0; pam_env[i]; i++)
388         {
389             gchar **pam_env_vars = g_strsplit (pam_env[i], "=", 2);
390             if (pam_env_vars && pam_env_vars[0] && pam_env_vars[1])
391                 process_set_env (PROCESS (session), pam_env_vars[0], pam_env_vars[1]);
392             else
393                 g_warning ("Can't parse PAM environment variable %s", pam_env[i]);
394             g_strfreev (pam_env_vars);
395         }
396         g_strfreev (pam_env);
397     }
398 }
399
400 static void
401 session_exited_cb (Session *session, gint status, Display *display)
402 {
403     if (status != 0)
404         g_debug ("Session exited with value %d", status);
405 }
406
407 static void
408 session_terminated_cb (Session *session, gint signum, Display *display)
409 {
410     g_debug ("Session terminated with signal %d", signum);
411 }
412
413 static void
414 check_stopped (Display *display)
415 {
416     if (display->priv->stopping &&
417         display->priv->display_server == NULL &&
418         display->priv->session == NULL)
419     {
420         g_debug ("Display stopped");
421         g_signal_emit (display, signals[STOPPED], 0);
422     }
423 }
424
425 static void
426 autologin_pam_message_cb (PAMSession *session, int num_msg, const struct pam_message **msg, Display *display)
427 {
428     g_debug ("Aborting automatic login as PAM requests input");
429     pam_session_cancel (session);
430 }
431
432 static void
433 autologin_authentication_result_cb (PAMSession *session, int result, Display *display)
434 {
435     g_signal_handlers_disconnect_matched (session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
436     if (display->priv->stopping)
437         return;
438
439     gboolean started_session = FALSE;
440
441     if (result == PAM_SUCCESS)
442     {
443         g_debug ("User %s authorized", pam_session_get_username (display->priv->pam_session));
444         pam_session_open (display->priv->pam_session);
445         started_session = start_user_session (display);
446         if (!started_session)
447             g_debug ("Failed to start autologin session");
448     }
449     else
450         g_debug ("Autologin failed authentication");
451
452     if (!started_session && display->priv->start_greeter_if_fail)
453     {
454         display_set_autologin_user (display, NULL, FALSE, 0);
455         if (display->priv->autologin_user)
456             display_set_select_user_hint (display, display->priv->autologin_user, FALSE);
457         started_session = start_greeter_session (display);
458     }
459
460     if (!started_session)
461        display_stop (display);
462 }
463
464 static gboolean
465 autologin (Display *display, const gchar *username, gboolean start_greeter_if_fail)
466 {
467     gboolean result;
468     GError *error = NULL;
469
470     display->priv->start_greeter_if_fail = start_greeter_if_fail;
471
472     display->priv->in_user_session = TRUE;
473     display->priv->pam_session = pam_session_new (display->priv->pam_autologin_service, username);
474     g_signal_connect (display->priv->pam_session, "got-messages", G_CALLBACK (autologin_pam_message_cb), display);
475     g_signal_connect (display->priv->pam_session, "authentication-result", G_CALLBACK (autologin_authentication_result_cb), display);
476
477     result = pam_session_authenticate (display->priv->pam_session, &error);
478     if (!result)
479     {
480         g_debug ("Failed to start autologin session for %s: %s", username, error->message);
481         g_signal_handlers_disconnect_matched (display->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
482         g_object_unref (display->priv->pam_session);
483         display->priv->pam_session = NULL;
484     }
485     g_clear_error (&error);
486
487     return result;
488 }
489
490 static gboolean
491 autologin_guest (Display *display, gboolean start_greeter_if_fail)
492 {
493     gchar *username;
494     gboolean result;
495
496     username = get_guest_username (display);
497     if (!username)
498         return FALSE;
499     result = autologin (display, username, start_greeter_if_fail);
500     g_free (username);
501
502     return result;
503 }
504
505 static gboolean
506 cleanup_after_session (Display *display)
507 {
508     /* Close ConsoleKit session */
509     if (getuid () == 0)
510         end_ck_session (session_get_cookie (display->priv->session));
511
512     /* Close PAM session */
513     g_signal_handlers_disconnect_matched (display->priv->pam_session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
514     pam_session_close (display->priv->pam_session);
515     g_object_unref (display->priv->pam_session);
516     display->priv->pam_session = NULL;
517
518     g_signal_handlers_disconnect_matched (display->priv->session, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
519     g_object_unref (display->priv->session);
520     display->priv->session = NULL;
521
522     if (display->priv->stopping)
523     {
524         check_stopped (display);
525         return TRUE;
526     }
527
528     return FALSE;
529 }
530
531 static void
532 greeter_session_stopped_cb (Session *session, Display *display)
533 {
534     gboolean started_session = FALSE;
535
536     g_debug ("Greeter quit");
537
538     if (cleanup_after_session (display))
539         return;
540
541     if (!display->priv->display_server)
542         return;
543
544     /* Start the session for the authenticated user */  
545     if (greeter_get_guest_authenticated (display->priv->greeter))
546     {
547         started_session = autologin_guest (display, FALSE);
548         if (!started_session)
549             g_debug ("Failed to start guest session");
550     }
551     else
552     {
553         display->priv->in_user_session = TRUE;
554         display->priv->pam_session = g_object_ref (greeter_get_pam_session (display->priv->greeter));
555         pam_session_open (display->priv->pam_session);
556         started_session = start_user_session (display);
557         if (!started_session)
558             g_debug ("Failed to start user session");
559     }
560
561     g_signal_handlers_disconnect_matched (display->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
562     g_object_unref (display->priv->greeter);
563     display->priv->greeter = NULL;
564
565     if (!started_session)
566         display_stop (display);
567 }
568
569 static void
570 user_session_stopped_cb (Session *session, Display *display)
571 {
572     g_debug ("User session quit");
573
574     g_signal_emit (display, signals[SESSION_STOPPED], 0);
575
576     if (cleanup_after_session (display))
577         return;
578
579     /* This display has ended */
580     display_stop (display);
581 }
582
583 static Session *
584 create_session (Display *display, PAMSession *pam_session, const gchar *session_name, gboolean is_greeter, const gchar *log_filename)
585 {
586     User *user;
587     gchar *sessions_dir, *filename, *path, *orig_path, *command = NULL;
588     GKeyFile *session_desktop_file;
589     Session *session;
590     gchar *cookie;
591     gboolean result;
592     GError *error = NULL;
593
594     user = user_get_by_name (pam_session_get_username (pam_session));
595     g_return_val_if_fail (user != NULL, FALSE);
596
597     g_debug ("Starting session %s as user %s logging to %s", session_name, user_get_name (user), log_filename);
598
599     // FIXME: This is X specific, move into xsession.c
600     if (is_greeter)
601         sessions_dir = display->priv->xgreeters_dir;
602     else
603         sessions_dir = display->priv->xsessions_dir;    
604
605     filename = g_strdup_printf ("%s.desktop", session_name);
606     path = g_build_filename (sessions_dir, filename, NULL);
607     g_free (filename);
608
609     session_desktop_file = g_key_file_new ();
610     result = g_key_file_load_from_file (session_desktop_file, path, G_KEY_FILE_NONE, &error);
611     if (!result)
612         g_debug ("Failed to load session file %s: %s:", path, error->message);
613     g_clear_error (&error);
614     if (result)
615     {
616         command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
617         if (!command)
618             g_debug ("No command in session file %s", path);
619     }
620     g_key_file_free (session_desktop_file);
621     g_free (path);
622     if (!command)
623         return NULL;
624     if (display->priv->session_wrapper && !is_greeter)
625     {
626         gchar *wrapper;
627
628         wrapper = g_find_program_in_path (display->priv->session_wrapper);
629         if (wrapper)
630         {
631             gchar *t = command;
632             command = g_strdup_printf ("%s '%s'", wrapper, command);
633             g_free (t);
634             g_free (wrapper);
635         }
636     }
637
638     session = DISPLAY_GET_CLASS (display)->create_session (display);
639     g_return_val_if_fail (session != NULL, NULL);
640     g_signal_connect (session, "exited", G_CALLBACK (session_exited_cb), display);
641     g_signal_connect (session, "terminated", G_CALLBACK (session_terminated_cb), display);
642     if (is_greeter)
643         g_signal_connect (session, "stopped", G_CALLBACK (greeter_session_stopped_cb), display);
644     else
645         g_signal_connect (session, "stopped", G_CALLBACK (user_session_stopped_cb), display);
646     session_set_is_greeter (session, is_greeter);
647     session_set_user (session, user);
648     session_set_command (session, command);
649
650     process_set_env (PROCESS (session), "DESKTOP_SESSION", session_name); // FIXME: Apparently deprecated?
651     process_set_env (PROCESS (session), "GDMSESSION", session_name); // FIXME: Not cross-desktop
652
653     set_env_from_pam_session (session, pam_session);
654
655     /* Insert our own utility directory to PATH */
656     orig_path = process_get_env (PROCESS (session), "PATH");
657     path = g_strdup_printf ("%s:%s", PKGLIBEXEC_DIR, orig_path);
658     process_set_env (PROCESS (session), "PATH", path);
659     g_free (path);
660     g_free (orig_path);
661
662     process_set_log_file (PROCESS (session), log_filename);
663
664     /* Open ConsoleKit session */
665     if (getuid () == 0)
666     {
667         cookie = start_ck_session (display, is_greeter ? "LoginWindow" : "", user);
668         session_set_cookie (session, cookie);
669         g_free (cookie);
670     }
671     else
672         session_set_cookie (session, g_getenv ("XDG_SESSION_COOKIE"));
673
674     /* Connect using the session bus */
675     if (getuid () != 0)
676     {
677         process_set_env (PROCESS (session), "DBUS_SESSION_BUS_ADDRESS", g_getenv ("DBUS_SESSION_BUS_ADDRESS"));
678         process_set_env (PROCESS (session), "LDM_BUS", "SESSION");
679         process_set_env (PROCESS (session), "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
680         process_set_env (PROCESS (session), "PATH", g_getenv ("PATH"));
681     }
682
683     /* Variables required for regression tests */
684     if (g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"))
685     {
686         process_set_env (PROCESS (session), "LIGHTDM_TEST_STATUS_SOCKET", g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"));
687         process_set_env (PROCESS (session), "LIGHTDM_TEST_CONFIG", g_getenv ("LIGHTDM_TEST_CONFIG"));
688         process_set_env (PROCESS (session), "LIGHTDM_TEST_HOME_DIR", g_getenv ("LIGHTDM_TEST_HOME_DIR"));
689         process_set_env (PROCESS (session), "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
690     }
691  
692     return session;
693 }
694
695 static PAMSession *
696 greeter_start_authentication_cb (Greeter *greeter, const gchar *username, Display *display)
697 {
698     return pam_session_new (display->priv->pam_service, username);
699 }
700
701 static gboolean
702 greeter_start_session_cb (Greeter *greeter, const gchar *session_name, Display *display)
703 {
704     /* Store the session to use, use the default if none was requested */
705     if (session_name)
706     {
707         g_free (display->priv->user_session);
708         display->priv->user_session = g_strdup (session_name);
709     }
710
711     /* Stop this display if that session already exists and can switch to it */
712     if (greeter_get_guest_authenticated (greeter))
713     {
714         if (switch_to_guest (display))
715             return TRUE;
716
717         /* Set to login as guest */
718         display_set_autologin_user (display, NULL, TRUE, 0);
719     }
720     else
721     {
722        if (switch_to_user (display, pam_session_get_username (greeter_get_pam_session (display->priv->greeter))))
723            return TRUE;
724     }
725
726     /* Stop the greeter, the session will start when the greeter has quit */
727     g_debug ("Stopping greeter");
728     session_stop (display->priv->session);  
729
730     return TRUE;
731 }
732
733 static gboolean
734 start_greeter_session (Display *display)
735 {
736     User *user;
737     gchar *log_dir, *filename, *log_filename;
738     gboolean result;
739
740     g_debug ("Starting greeter session");
741
742     if (getuid () != 0)
743         user = user_get_current ();
744     else if (display->priv->greeter_user)
745     {
746         user = user_get_by_name (display->priv->greeter_user);
747         if (!user)
748         {
749             g_debug ("Unable to start greeter, user %s does not exist", display->priv->greeter_user);
750             return FALSE;
751         }
752     }
753     else
754     {
755         g_warning ("Greeter must not be run as root");
756         return FALSE;
757     }
758     display->priv->in_user_session = FALSE;
759     display->priv->pam_session = pam_session_new (display->priv->pam_service, user_get_name (user));
760     pam_session_open (display->priv->pam_session);
761     g_object_unref (user);
762
763     log_dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
764     // FIXME: May not be an X server
765     filename = g_strdup_printf ("%s-greeter.log", xserver_get_address (XSERVER (display->priv->display_server)));
766     log_filename = g_build_filename (log_dir, filename, NULL);
767     g_free (log_dir);
768     g_free (filename);
769
770     display->priv->session = create_session (display, display->priv->pam_session, display->priv->greeter_session, TRUE, log_filename);
771     g_free (log_filename);
772     if (!display->priv->session)
773         return FALSE;
774
775     display->priv->greeter = greeter_new (display->priv->session);
776     g_signal_connect (G_OBJECT (display->priv->greeter), "start-authentication", G_CALLBACK (greeter_start_authentication_cb), display);
777     g_signal_connect (G_OBJECT (display->priv->greeter), "start-session", G_CALLBACK (greeter_start_session_cb), display);
778     if (display->priv->autologin_timeout)
779     {
780         gchar *value = g_strdup_printf ("%d", display->priv->autologin_timeout);
781         greeter_set_hint (display->priv->greeter, "autologin-timeout", value);
782         g_free (value);
783         if (display->priv->autologin_user)
784             greeter_set_hint (display->priv->greeter, "autologin-user", display->priv->autologin_user);
785         else if (display->priv->autologin_guest)
786             greeter_set_hint (display->priv->greeter, "autologin-guest", "true");        
787     }
788     if (display->priv->select_user_hint)
789         greeter_set_hint (display->priv->greeter, "select-user", display->priv->select_user_hint);
790     else if (display->priv->select_guest_hint)
791         greeter_set_hint (display->priv->greeter, "select-guest", "true");
792     greeter_set_hint (display->priv->greeter, "default-session", display->priv->user_session);
793     greeter_set_allow_guest (display->priv->greeter, display->priv->allow_guest);
794     greeter_set_hint (display->priv->greeter, "has-guest-account", display->priv->allow_guest ? "true" : "false");
795     greeter_set_hint (display->priv->greeter, "hide-users", display->priv->greeter_hide_users ? "true" : "false");
796
797     result = greeter_start (display->priv->greeter);
798     if (result)
799     {
800         result = session_start (SESSION (display->priv->session));
801         if (!result)
802             g_debug ("Failed to start greeter session");
803     }
804     else
805     {
806         g_debug ("Failed to start greeter protocol");
807
808         g_signal_handlers_disconnect_matched (display->priv->greeter, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
809         g_object_unref (display->priv->greeter);
810         display->priv->greeter = NULL;
811     }
812
813     if (result)
814     {
815         display->priv->indicated_ready = TRUE;
816         g_signal_emit (display, signals[READY], 0);
817     }
818
819     return result;
820 }
821
822 static gboolean
823 start_user_session (Display *display)
824 {
825     GKeyFile *dmrc_file;
826     User *user;
827     gchar *log_filename;
828     gboolean result = FALSE;
829
830     g_debug ("Starting user session");
831
832     user = user_get_by_name (pam_session_get_username (display->priv->pam_session));
833     if (!user)
834     {
835         g_debug ("Unable to start session, user %s does not exist", pam_session_get_username (display->priv->pam_session));
836         return FALSE;
837     }
838
839     /* Load the users login settings (~/.dmrc) */
840     dmrc_file = dmrc_load (user_get_name (user));
841
842     /* Update the .dmrc with changed settings */
843     g_key_file_set_string (dmrc_file, "Desktop", "Session", display->priv->user_session);
844     dmrc_save (dmrc_file, pam_session_get_username (display->priv->pam_session));
845     g_key_file_free (dmrc_file);
846
847     // FIXME: Copy old error file  
848     log_filename = g_build_filename (user_get_home_directory (user), ".xsession-errors", NULL);
849     g_object_unref (user);
850
851     display->priv->session = create_session (display, display->priv->pam_session, display->priv->user_session, FALSE, log_filename);
852     g_free (log_filename);
853
854     if (display->priv->session)
855         result = session_start (SESSION (display->priv->session));
856
857     if (result)
858     {
859         if (!display->priv->indicated_ready)
860             g_signal_emit (display, signals[READY], 0);
861         g_signal_emit (display, signals[SESSION_STARTED], 0);
862     }
863
864     return result;
865 }
866
867 static void
868 display_server_stopped_cb (DisplayServer *server, Display *display)
869 {
870     g_debug ("Display server stopped");
871
872     g_signal_handlers_disconnect_matched (display->priv->display_server, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, display);
873     g_object_unref (display->priv->display_server);
874     display->priv->display_server = NULL;
875
876     /* Stop this display, it will be restarted by the seat if necessary */
877     display_stop (display);
878 }
879
880 static void
881 display_server_ready_cb (DisplayServer *display_server, Display *display)
882 {
883     gboolean started_session = FALSE;
884
885     /* Don't run any sessions on local terminals */
886     // FIXME: Make display_server_get_has_local_session
887     if (IS_XSERVER_LOCAL (display_server) && xserver_local_get_xdmcp_server (XSERVER_LOCAL (display_server)))
888         return;
889
890     /* Automatically log in */
891     if (display->priv->autologin_guest)
892     {
893         g_debug ("Automatically logging in as guest");
894         started_session = autologin_guest (display, TRUE);
895         if (!started_session)
896             g_debug ("Failed to autologin as guest");
897     }
898     else if (display->priv->autologin_user)
899     {
900         g_debug ("Automatically logging in user %s", display->priv->autologin_user);
901         started_session = autologin (display, display->priv->autologin_user, TRUE);
902         if (!started_session)
903             g_debug ("Failed to autologin user %s", display->priv->autologin_user);
904     }
905
906     /* Finally start a greeter */
907     if (!started_session)
908     {
909         started_session = start_greeter_session (display);
910         if (!started_session)
911             g_debug ("Failed to start greeter");
912     }
913
914     if (!started_session)
915         display_stop (display);
916 }
917
918 gboolean
919 display_start (Display *display)
920 {
921     gboolean result;
922
923     g_return_val_if_fail (display != NULL, FALSE);
924
925     g_signal_connect (G_OBJECT (display->priv->display_server), "ready", G_CALLBACK (display_server_ready_cb), display);
926     g_signal_connect (G_OBJECT (display->priv->display_server), "stopped", G_CALLBACK (display_server_stopped_cb), display);
927     result = display_server_start (display->priv->display_server);
928
929     g_signal_emit (display, signals[STARTED], 0);
930
931     return result;
932 }
933
934 void
935 display_stop (Display *display)
936 {
937     g_return_if_fail (display != NULL);
938
939     if (!display->priv->stopping)
940     {
941         g_debug ("Stopping display");
942
943         display->priv->stopping = TRUE;
944
945         if (display->priv->display_server)
946             display_server_stop (display->priv->display_server);
947         if (display->priv->session)
948             session_stop (display->priv->session);
949     }
950
951     check_stopped (display);
952 }
953
954 static gboolean
955 display_real_switch_to_user (Display *display, const gchar *username)
956 {
957     return FALSE;
958 }
959
960 static gboolean
961 display_real_switch_to_guest (Display *display)
962 {
963     return FALSE;
964 }
965
966 static gchar *
967 display_real_get_guest_username (Display *display)
968 {
969     return NULL;
970 }
971
972 static void
973 display_init (Display *display)
974 {
975     display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, DISPLAY_TYPE, DisplayPrivate);
976     display->priv->pam_service = g_strdup ("lightdm");
977     display->priv->pam_autologin_service = g_strdup ("lightdm-autologin");
978 }
979
980 static Session *
981 display_create_session (Display *display)
982 {
983     return NULL;
984 }
985
986 static void
987 display_finalize (GObject *object)
988 {
989     Display *self;
990
991     self = DISPLAY (object);
992
993     if (self->priv->display_server)
994         g_object_unref (self->priv->display_server);
995     g_free (self->priv->greeter_user);
996     g_free (self->priv->greeter_session);
997     if (self->priv->greeter)
998         g_object_unref (self->priv->greeter);
999     g_free (self->priv->xsessions_dir);
1000     g_free (self->priv->xgreeters_dir);
1001     g_free (self->priv->session_wrapper);
1002     g_free (self->priv->pam_service);
1003     g_free (self->priv->pam_autologin_service);
1004     if (self->priv->session)
1005     {
1006         if (session_get_cookie (self->priv->session))
1007             end_ck_session (session_get_cookie (self->priv->session));
1008         g_object_unref (self->priv->session);
1009     }
1010     if (self->priv->pam_session)
1011         g_object_unref (self->priv->pam_session);
1012     g_free (self->priv->autologin_user);
1013     g_free (self->priv->select_user_hint);
1014     g_free (self->priv->user_session);
1015
1016     G_OBJECT_CLASS (display_parent_class)->finalize (object);
1017 }
1018
1019 static void
1020 display_class_init (DisplayClass *klass)
1021 {
1022     GObjectClass *object_class = G_OBJECT_CLASS (klass);
1023
1024     klass->switch_to_user = display_real_switch_to_user;
1025     klass->switch_to_guest = display_real_switch_to_guest;
1026     klass->get_guest_username = display_real_get_guest_username;
1027     klass->create_session = display_create_session;
1028     object_class->finalize = display_finalize;
1029
1030     g_type_class_add_private (klass, sizeof (DisplayPrivate));
1031
1032     signals[STARTED] =
1033         g_signal_new ("started",
1034                       G_TYPE_FROM_CLASS (klass),
1035                       G_SIGNAL_RUN_LAST,
1036                       G_STRUCT_OFFSET (DisplayClass, started),
1037                       NULL, NULL,
1038                       g_cclosure_marshal_VOID__VOID,
1039                       G_TYPE_NONE, 0);
1040     signals[READY] =
1041         g_signal_new ("ready",
1042                       G_TYPE_FROM_CLASS (klass),
1043                       G_SIGNAL_RUN_LAST,
1044                       G_STRUCT_OFFSET (DisplayClass, ready),
1045                       NULL, NULL,
1046                       g_cclosure_marshal_VOID__VOID,
1047                       G_TYPE_NONE, 0);
1048     signals[SWITCH_TO_USER] =
1049         g_signal_new ("switch-to-user",
1050                       G_TYPE_FROM_CLASS (klass),
1051                       G_SIGNAL_RUN_LAST,
1052                       G_STRUCT_OFFSET (DisplayClass, switch_to_user),
1053                       g_signal_accumulator_true_handled,
1054                       NULL,
1055                       ldm_marshal_BOOLEAN__STRING,
1056                       G_TYPE_BOOLEAN, 1, G_TYPE_STRING);
1057     signals[SWITCH_TO_GUEST] =
1058         g_signal_new ("switch-to-guest",
1059                       G_TYPE_FROM_CLASS (klass),
1060                       G_SIGNAL_RUN_LAST,
1061                       G_STRUCT_OFFSET (DisplayClass, switch_to_guest),
1062                       g_signal_accumulator_true_handled,
1063                       NULL,
1064                       ldm_marshal_BOOLEAN__VOID,
1065                       G_TYPE_BOOLEAN, 0);
1066     signals[GET_GUEST_USERNAME] =
1067         g_signal_new ("get-guest-username",
1068                       G_TYPE_FROM_CLASS (klass),
1069                       G_SIGNAL_RUN_LAST,
1070                       G_STRUCT_OFFSET (DisplayClass, get_guest_username),
1071                       g_signal_accumulator_first_wins,
1072                       NULL,
1073                       ldm_marshal_STRING__VOID,
1074                       G_TYPE_STRING, 0);
1075     signals[SESSION_STARTED] =
1076         g_signal_new ("session-started",
1077                       G_TYPE_FROM_CLASS (klass),
1078                       G_SIGNAL_RUN_LAST,
1079                       G_STRUCT_OFFSET (DisplayClass, session_started),
1080                       NULL, NULL,
1081                       g_cclosure_marshal_VOID__VOID,
1082                       G_TYPE_NONE, 0);
1083     signals[SESSION_STOPPED] =
1084         g_signal_new ("session-stopped",
1085                       G_TYPE_FROM_CLASS (klass),
1086                       G_SIGNAL_RUN_LAST,
1087                       G_STRUCT_OFFSET (DisplayClass, session_stopped),
1088                       NULL, NULL,
1089                       g_cclosure_marshal_VOID__VOID,
1090                       G_TYPE_NONE, 0);
1091     signals[STOPPED] =
1092         g_signal_new ("stopped",
1093                       G_TYPE_FROM_CLASS (klass),
1094                       G_SIGNAL_RUN_LAST,
1095                       G_STRUCT_OFFSET (DisplayClass, stopped),
1096                       NULL, NULL,
1097                       g_cclosure_marshal_VOID__VOID,
1098                       G_TYPE_NONE, 0);
1099 }