]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display.c
More guest account work, change liblightdm API
[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
14 #include <stdlib.h>
15 #include <string.h>
16 #include <errno.h>
17 #include <pwd.h>
18 #include <gio/gdesktopappinfo.h>
19
20 #include "display.h"
21 #include "pam-session.h"
22 #include "theme.h"
23 #include "ldm-marshal.h"
24 #include "greeter.h"
25
26 /* Length of time in milliseconds to wait for a session to load */
27 #define USER_SESSION_TIMEOUT 5000
28
29 enum {
30     START_GREETER,
31     END_GREETER,
32     START_SESSION,
33     END_SESSION,
34     EXITED,
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     gint index;
51
52     /* X server */
53     XServer *xserver;
54   
55     /* Number of times have logged in */
56     gint login_count;
57
58     /* Layout to use in greeter/sessions */
59     gchar *default_layout;
60
61     /* User to run greeter as */
62     gchar *greeter_user;
63
64     /* Theme to use */
65     gchar *greeter_theme;
66
67     /* Program to run sessions through */
68     gchar *session_wrapper;
69
70     /* PAM service to authenticate against */
71     gchar *pam_service;
72
73     /* Greeter session process */
74     Greeter *greeter_session;
75     PAMSession *greeter_pam_session;
76     gchar *greeter_ck_cookie;
77
78     /* TRUE if the greeter can stay active during the session */
79     gboolean supports_transitions;
80
81     /* User session process */
82     Session *user_session;
83     guint user_session_timer;
84     PAMSession *user_pam_session;
85     gchar *user_ck_cookie;
86
87     /* Default login hint */
88     gchar *default_user;
89     gint timeout;
90
91     /* Default session */
92     gchar *default_session;
93 };
94
95 G_DEFINE_TYPE (Display, display, G_TYPE_OBJECT);
96
97 static void start_greeter (Display *display);
98
99 // FIXME: Remove the index, it is an external property
100 Display *
101 display_new (gint index)
102 {
103     Display *self = g_object_new (DISPLAY_TYPE, NULL);
104
105     self->priv->index = index;
106     self->priv->pam_service = g_strdup (DEFAULT_PAM_SERVICE);
107
108     return self;
109 }
110
111 gint
112 display_get_index (Display *display)
113 {
114     return display->priv->index;
115 }
116
117 void
118 display_set_session_wrapper (Display *display, const gchar *session_wrapper)
119 {
120     g_free (display->priv->session_wrapper);
121     display->priv->session_wrapper = g_strdup (session_wrapper);  
122 }
123
124 const gchar *
125 display_get_session_wrapper (Display *display)
126 {
127     return display->priv->session_wrapper;
128 }
129
130 void
131 display_set_default_user (Display *display, const gchar *username)
132 {
133     g_free (display->priv->default_user);
134     display->priv->default_user = g_strdup (username);
135 }
136
137 const gchar *
138 display_get_default_user (Display *display)
139 {
140     return display->priv->default_user;
141 }
142
143 void
144 display_set_default_user_timeout (Display *display, gint timeout)
145 {
146     display->priv->timeout = timeout;  
147 }
148
149 gint
150 display_get_default_user_timeout (Display *display)
151 {
152     return display->priv->timeout;
153 }
154
155 void
156 display_set_greeter_user (Display *display, const gchar *username)
157 {
158     g_free (display->priv->greeter_user);
159     if (username && username[0] != '\0')
160         display->priv->greeter_user = g_strdup (username);
161     else
162         display->priv->greeter_user = NULL;
163 }
164
165 const gchar *
166 display_get_greeter_user (Display *display)
167 {
168     return display->priv->greeter_user;  
169 }
170
171 const gchar *
172 display_get_session_user (Display *display)
173 {
174     if (display->priv->user_session)
175         return pam_session_get_username (display->priv->user_pam_session);
176     else
177         return NULL;
178 }
179
180 void
181 display_set_greeter_theme (Display *display, const gchar *greeter_theme)
182 {
183     g_free (display->priv->greeter_theme);
184     display->priv->greeter_theme = g_strdup (greeter_theme);
185 }
186
187 const gchar *
188 display_get_greeter_theme (Display *display)
189 {
190     return display->priv->greeter_theme;
191 }
192
193 void
194 display_set_default_layout (Display *display, const gchar *layout)
195 {
196     g_free (display->priv->default_layout);
197     display->priv->default_layout = g_strdup (layout);
198 }
199
200 const gchar *
201 display_get_default_layout (Display *display)
202 {
203     return display->priv->default_layout;
204 }
205
206 void
207 display_set_default_session (Display *display, const gchar *session)
208 {
209     g_free (display->priv->default_session);
210     display->priv->default_session = g_strdup (session);
211 }
212
213 const gchar *
214 display_get_default_session (Display *display)
215 {
216     return display->priv->default_session;
217 }
218
219 void
220 display_set_pam_service (Display *display, const gchar *service)
221 {
222     g_free (display->priv->pam_service);
223     display->priv->pam_service = g_strdup (service);
224 }
225
226 const gchar *
227 display_get_pam_service (Display *display)
228 {
229     return display->priv->pam_service;
230 }
231
232 void
233 display_set_xserver (Display *display, XServer *xserver)
234 {
235     if (display->priv->xserver)
236         g_object_unref (display->priv->xserver);
237     display->priv->xserver = g_object_ref (xserver);
238 }
239
240 XServer *
241 display_get_xserver (Display *display)
242 {
243     return display->priv->xserver;
244 }
245
246 static struct passwd *
247 get_user_info (const gchar *username)
248 {
249     struct passwd *user_info;
250
251     errno = 0;
252     user_info = getpwnam (username);
253     if (!user_info)
254     {
255         if (errno == 0)
256             g_warning ("Unable to get information on user %s: User does not exist", username);
257         else
258             g_warning ("Unable to get information on user %s: %s", username, strerror (errno));
259     }
260
261     return user_info;
262 }
263
264 static gchar *
265 start_ck_session (Display *display, const gchar *session_type, const gchar *username)
266 {
267     GDBusProxy *proxy;
268     char *display_device = NULL;
269     const gchar *address, *hostname = "";
270     struct passwd *user_info;
271     GVariantBuilder arg_builder;
272     GVariant *result;
273     gchar *cookie = NULL;
274     GError *error = NULL;
275
276     user_info = get_user_info (username);
277     if (!user_info)
278         return NULL;
279
280     if (xserver_get_vt (display->priv->xserver) >= 0)
281         display_device = g_strdup_printf ("/dev/tty%d", xserver_get_vt (display->priv->xserver));
282     address = xserver_get_address (display->priv->xserver);
283
284     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
285                                            G_DBUS_PROXY_FLAGS_NONE,
286                                            NULL,
287                                            "org.freedesktop.ConsoleKit",
288                                            "/org/freedesktop/ConsoleKit/Manager",
289                                            "org.freedesktop.ConsoleKit.Manager", 
290                                            NULL, NULL);
291     g_variant_builder_init (&arg_builder, G_VARIANT_TYPE ("(a(sv))"));
292     g_variant_builder_open (&arg_builder, G_VARIANT_TYPE ("a(sv)"));
293     g_variant_builder_add (&arg_builder, "(sv)", "unix-user", g_variant_new_int32 (user_info->pw_uid));
294     g_variant_builder_add (&arg_builder, "(sv)", "session-type", g_variant_new_string (session_type));
295     g_variant_builder_add (&arg_builder, "(sv)", "x11-display", g_variant_new_string (address));
296     if (display_device)
297         g_variant_builder_add (&arg_builder, "(sv)", "x11-display-device", g_variant_new_string (display_device));
298     g_variant_builder_add (&arg_builder, "(sv)", "remote-host-name", g_variant_new_string (hostname));
299     g_variant_builder_add (&arg_builder, "(sv)", "is-local", g_variant_new_boolean (TRUE));
300     g_variant_builder_close (&arg_builder);
301     result = g_dbus_proxy_call_sync (proxy,
302                                      "OpenSessionWithParameters",
303                                      g_variant_builder_end (&arg_builder),
304                                      G_DBUS_CALL_FLAGS_NONE,
305                                      -1,
306                                      NULL,
307                                      &error);
308     g_object_unref (proxy);
309     g_free (display_device);
310     if (!result)
311         g_warning ("Failed to open CK session: %s", error->message);
312     g_clear_error (&error);
313     if (!result)
314         return NULL;
315
316     if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(s)")))
317         g_variant_get (result, "(s)", &cookie);
318     else
319         g_warning ("Unexpected response from OpenSessionWithParameters: %s", g_variant_get_type_string (result));
320     g_variant_unref (result);
321
322     if (cookie)
323         g_debug ("Opened ConsoleKit session %s", cookie);
324
325     return cookie;
326 }
327
328 static void
329 end_ck_session (const gchar *cookie)
330 {
331     GDBusProxy *proxy;
332     GVariant *result;
333     GError *error = NULL;
334
335     if (!cookie)
336         return;
337
338     g_debug ("Ending ConsoleKit session %s", cookie);
339
340     proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
341                                            G_DBUS_PROXY_FLAGS_NONE,
342                                            NULL,
343                                            "org.freedesktop.ConsoleKit",
344                                            "/org/freedesktop/ConsoleKit/Manager",
345                                            "org.freedesktop.ConsoleKit.Manager", 
346                                            NULL, NULL);
347     result = g_dbus_proxy_call_sync (proxy,
348                                      "CloseSession",
349                                      g_variant_new ("(s)", cookie),
350                                      G_DBUS_CALL_FLAGS_NONE,
351                                      -1,
352                                      NULL,
353                                      &error);
354     g_object_unref (proxy);
355
356     if (!result)
357         g_warning ("Error ending ConsoleKit session: %s", error->message);
358     g_clear_error (&error);
359     if (!result)
360         return;
361
362     if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
363     {
364         gboolean is_closed;
365         g_variant_get (result, "(b)", &is_closed);
366         if (!is_closed)
367             g_warning ("ConsoleKit.Manager.CloseSession() returned false");
368     }
369     else
370         g_warning ("Unexpected response from CloseSession: %s", g_variant_get_type_string (result));
371
372     g_variant_unref (result);
373 }
374
375 static void
376 run_script (const gchar *script)
377 {
378     // FIXME
379 }
380
381 static void
382 end_user_session (Display *display, gboolean clean_exit)
383 {
384     run_script ("PostSession");
385
386     g_signal_emit (display, signals[END_SESSION], 0, display->priv->user_session);
387   
388     if (display->priv->user_session_timer)
389     {
390         g_source_remove (display->priv->user_session_timer);
391         display->priv->user_session_timer = 0;
392     }
393
394     g_object_unref (display->priv->user_session);
395     display->priv->user_session = NULL;
396
397     pam_session_end (display->priv->user_pam_session);
398     g_object_unref (display->priv->user_pam_session);
399     display->priv->user_pam_session = NULL;
400
401     end_ck_session (display->priv->user_ck_cookie);
402     g_free (display->priv->user_ck_cookie);
403     display->priv->user_ck_cookie = NULL;
404
405     if (!clean_exit)
406         g_warning ("Session exited unexpectedly");
407
408     xserver_disconnect_clients (display->priv->xserver);
409 }
410
411 static void
412 user_session_exited_cb (Session *session, gint status, Display *display)
413 {
414     end_user_session (display, status == 0);
415 }
416
417 static void
418 user_session_terminated_cb (Session *session, gint signum, Display *display)
419 {
420     end_user_session (display, FALSE);
421 }
422
423 static void
424 set_env_from_pam_session (Session *session, PAMSession *pam_session)
425 {
426     gchar **pam_env;
427
428     pam_env = pam_session_get_envlist (pam_session);
429     if (pam_env)
430     {
431         gchar *env_string;      
432         int i;
433
434         env_string = g_strjoinv (" ", pam_env);
435         g_debug ("PAM returns environment %s", env_string);
436         g_free (env_string);
437
438         for (i = 0; pam_env[i]; i++)
439         {
440             gchar **pam_env_vars = g_strsplit (pam_env[i], "=", 2);
441             if (pam_env_vars && pam_env_vars[0] && pam_env_vars[1])
442                 child_process_set_env (CHILD_PROCESS (session), pam_env_vars[0], pam_env_vars[1]);
443             else
444                 g_warning ("Can't parse PAM environment variable %s", pam_env[i]);
445             g_strfreev (pam_env_vars);
446         }
447         g_strfreev (pam_env);
448     }
449 }
450
451 static void
452 set_env_from_keyfile (Session *session, const gchar *name, GKeyFile *key_file, const gchar *section, const gchar *key)
453 {
454     char *value;
455
456     value = g_key_file_get_string (key_file, section, key, NULL);
457     if (!value)
458         return;
459
460     child_process_set_env (CHILD_PROCESS (session), name, value);
461     g_free (value);
462 }
463
464 static void
465 start_user_session (Display *display, const gchar *session, const gchar *language)
466 {
467     gchar *filename, *path, *old_language;
468     struct passwd *user_info;
469     GKeyFile *dmrc_file, *session_desktop_file;
470     gboolean have_dmrc = FALSE, result;
471     GError *error = NULL;
472
473     run_script ("PreSession");
474
475     g_debug ("Launching '%s' session for user %s", session, pam_session_get_username (display->priv->user_pam_session));
476     display->priv->login_count++;
477
478     /* Load the users login settings (~/.dmrc) */
479     dmrc_file = g_key_file_new ();
480     user_info = get_user_info (pam_session_get_username (display->priv->user_pam_session));
481     if (user_info)
482     {
483         /* Load from the user directory, if this fails (e.g. the user directory
484          * is not yet mounted) then load from the cache */
485         path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
486         have_dmrc = g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
487         g_free (path);
488     }
489
490     /* If no .dmrc, then load from the cache */
491     if (!have_dmrc)
492     {
493         filename = g_strdup_printf ("%s.dmrc", user_info->pw_name);
494         path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
495         g_free (filename);
496         if (!g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, &error))
497             g_warning ("Failed to load .dmrc file %s: %s", path, error->message);
498         g_clear_error (&error);
499         g_free (path);
500     }
501
502     /* Update the .dmrc with changed settings */
503     g_key_file_set_string (dmrc_file, "Desktop", "Session", session);
504     old_language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
505     if (language && (!old_language || !g_str_equal(language, old_language)))
506     {
507         g_key_file_set_string (dmrc_file, "Desktop", "Language", language);
508         /* We don't have advanced language checking, so reset these variables */
509         g_key_file_remove_key (dmrc_file, "Desktop", "Langlist", NULL);
510         g_key_file_remove_key (dmrc_file, "Desktop", "LCMess", NULL);
511     }
512     g_free (old_language);
513     if (!g_key_file_has_key (dmrc_file, "Desktop", "Layout", NULL))
514         g_key_file_set_string (dmrc_file, "Desktop", "Layout", display->priv->default_layout);
515
516     filename = g_strdup_printf ("%s.desktop", session);
517     path = g_build_filename (XSESSIONS_DIR, filename, NULL);
518     g_free (filename);
519
520     session_desktop_file = g_key_file_new ();
521     result = g_key_file_load_from_file (session_desktop_file, path, G_KEY_FILE_NONE, &error);
522     g_free (path);
523
524     if (!result)
525         g_warning ("Failed to load session file %s: %s:", path, error->message);
526     g_clear_error (&error);
527
528     if (result)
529     {
530         gchar *session_command;
531
532         session_command = g_key_file_get_string (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
533         if (!session_command)
534             g_warning ("No command in session file %s", path);
535
536         display->priv->supports_transitions = g_key_file_get_boolean (session_desktop_file, G_KEY_FILE_DESKTOP_GROUP, "X-LightDM-Supports-Transitions", NULL);
537
538         if (session_command)
539         {
540             gchar *data;
541             gsize length;
542
543             if (display->priv->session_wrapper)
544             {
545                 gchar *old_command = session_command;
546                 session_command = g_strdup_printf ("%s '%s'", display->priv->session_wrapper, session_command);
547                 g_free (old_command);
548             }
549             display->priv->user_session = session_new ();
550             session_set_username (display->priv->user_session, pam_session_get_username (display->priv->user_pam_session));
551             session_set_command (display->priv->user_session, session_command);
552
553             g_signal_connect (G_OBJECT (display->priv->user_session), "exited", G_CALLBACK (user_session_exited_cb), display);
554             g_signal_connect (G_OBJECT (display->priv->user_session), "terminated", G_CALLBACK (user_session_terminated_cb), display);
555             child_process_set_env (CHILD_PROCESS (display->priv->user_session), "DISPLAY", xserver_get_address (display->priv->xserver));
556             if (display->priv->user_ck_cookie)
557                 child_process_set_env (CHILD_PROCESS (display->priv->user_session), "XDG_SESSION_COOKIE", display->priv->user_ck_cookie);
558             child_process_set_env (CHILD_PROCESS (display->priv->user_session), "DESKTOP_SESSION", session); // FIXME: Apparently deprecated?
559             child_process_set_env (CHILD_PROCESS (display->priv->user_session), "GDMSESSION", session); // FIXME: Not cross-desktop
560             set_env_from_keyfile (display->priv->user_session, "LANG", dmrc_file, "Desktop", "Language");
561             set_env_from_keyfile (display->priv->user_session, "LANGUAGE", dmrc_file, "Desktop", "Langlist");
562             set_env_from_keyfile (display->priv->user_session, "LC_MESSAGES", dmrc_file, "Desktop", "LCMess");
563             //child_process_set_env (CHILD_PROCESS (display->priv->user_session), "GDM_LANG", session_language); // FIXME: Not cross-desktop
564             set_env_from_keyfile (display->priv->user_session, "GDM_KEYBOARD_LAYOUT", dmrc_file, "Desktop", "Layout"); // FIXME: Not cross-desktop
565             set_env_from_pam_session (display->priv->user_session, display->priv->user_pam_session);
566
567             g_signal_emit (display, signals[START_SESSION], 0, display->priv->user_session);
568
569             if (display->priv->greeter_session == NULL || display->priv->supports_transitions)
570                 session_start (display->priv->user_session, FALSE);
571             else
572                 g_debug ("Waiting for greeter to quit before starting user session process");
573
574             data = g_key_file_to_data (dmrc_file, &length, NULL);
575
576             /* Update the users .dmrc */
577             if (user_info)
578             {
579                 path = g_build_filename (user_info->pw_dir, ".dmrc", NULL);
580                 g_file_set_contents (path, data, length, NULL);
581                 if (chown (path, user_info->pw_uid, user_info->pw_gid) < 0)
582                     g_warning ("Error setting ownership on %s: %s", path, strerror (errno));
583                 g_free (path);
584             }
585
586             /* Update the .dmrc cache */
587             path = g_build_filename (CACHE_DIR, "dmrc", NULL);          
588             g_mkdir_with_parents (path, 0700);
589             g_free (path);
590             filename = g_strdup_printf ("%s.dmrc", pam_session_get_username (display->priv->user_pam_session));
591             path = g_build_filename (CACHE_DIR, "dmrc", filename, NULL);
592             g_file_set_contents (path, data, length, NULL);
593             g_free (path);
594
595             g_free (data);
596         }
597
598         g_free (session_command);
599     }
600
601     g_key_file_free (session_desktop_file);
602     g_key_file_free (dmrc_file);  
603 }
604
605 static void
606 start_default_session (Display *display, const gchar *session, const gchar *language)
607 {
608     /* Don't need to check authentication, just authorize */
609     // FIXME: Not correct, should use lightdm-autologin pam session
610     if (display->priv->user_pam_session)
611         pam_session_end (display->priv->user_pam_session);    
612     display->priv->user_pam_session = pam_session_new (display->priv->pam_service, display->priv->default_user);
613     pam_session_authorize (display->priv->user_pam_session);
614
615     display->priv->user_ck_cookie = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
616     start_user_session (display, session, language);  
617 }
618
619 static gboolean
620 session_timeout_cb (Display *display)
621 {
622     g_warning ("Session has not indicated it is ready, stopping greeter anyway");
623
624     /* Stop the greeter */
625     greeter_quit (display->priv->greeter_session);
626
627     display->priv->user_session_timer = 0;
628     return FALSE;
629 }
630
631 static void
632 greeter_start_session_cb (Greeter *greeter, const gchar *session, const gchar *language, Display *display)
633 {
634     /* Default session requested */
635     if (strcmp (session, "") == 0)
636         session = display->priv->default_session;
637
638     /* Default language requested */
639     if (strcmp (language, "") == 0)
640         language = NULL;
641   
642     display->priv->user_pam_session = greeter_get_pam_session (greeter);
643     display->priv->user_ck_cookie = start_ck_session (display, "", pam_session_get_username (display->priv->user_pam_session));
644
645     if (!display->priv->user_pam_session ||
646         !pam_session_get_in_session (display->priv->user_pam_session))
647     {
648         g_warning ("Ignoring request for login with unauthenticated user");
649         return;
650     }
651
652     start_user_session (display, session, language);
653
654     /* Stop session, waiting for user session to indicate it is ready (if supported) */
655     // FIXME: Hard-coded timeout
656     // FIXME: Greeter quit timeout
657     if (display->priv->supports_transitions)
658         display->priv->user_session_timer = g_timeout_add (USER_SESSION_TIMEOUT, (GSourceFunc) session_timeout_cb, display);
659     else
660         greeter_quit (display->priv->greeter_session);
661 }
662
663 static void
664 greeter_quit_cb (Greeter *greeter, Display *display)
665 {
666     g_debug ("Greeter quit");
667
668     g_signal_emit (display, signals[END_GREETER], 0, display->priv->greeter_session);
669
670     pam_session_end (display->priv->greeter_pam_session);
671     g_object_unref (display->priv->greeter_pam_session);
672     display->priv->greeter_pam_session = NULL;
673
674     g_object_unref (display->priv->greeter_session);
675     display->priv->greeter_session = NULL;
676
677     end_ck_session (display->priv->greeter_ck_cookie);
678     g_free (display->priv->greeter_ck_cookie);
679     display->priv->greeter_ck_cookie = NULL;   
680
681     /* Start session if waiting for greeter to quit */
682     if (display->priv->user_session && child_process_get_pid (CHILD_PROCESS (display->priv->user_session)) == 0)
683     {
684         g_debug ("Starting user session");
685         session_start (display->priv->user_session, FALSE);
686     }
687 }
688
689 static void
690 start_greeter (Display *display)
691 {
692     GKeyFile *theme;
693     GError *error = NULL;
694   
695     theme = load_theme (display->priv->greeter_theme, &error);
696     if (!theme)
697         g_warning ("Failed to find theme %s: %s", display->priv->greeter_theme, error->message);
698     g_clear_error (&error);
699
700     if (theme)
701     {
702         gchar *command;
703         gchar *username = NULL;
704
705         g_debug ("Starting greeter %s as user %s", display->priv->greeter_theme,
706                  display->priv->greeter_user ? display->priv->greeter_user : "<current>");
707
708         command = theme_get_command (theme);
709       
710         if (display->priv->greeter_user)
711             username = display->priv->greeter_user;
712         else
713         {
714             struct passwd *user_info;
715             user_info = getpwuid (getuid ());
716             if (!user_info)
717             {
718                 g_warning ("Unable to determine current username: %s", strerror (errno));
719                 return;
720             }
721             username = user_info->pw_name;
722         }
723
724         display->priv->greeter_pam_session = pam_session_new (display->priv->pam_service, username);
725         pam_session_authorize (display->priv->greeter_pam_session);
726
727         display->priv->greeter_ck_cookie = start_ck_session (display,
728                                                              "LoginWindow",
729                                                              username);
730
731         display->priv->greeter_session = greeter_new ();
732         greeter_set_theme (display->priv->greeter_session, display->priv->greeter_theme);
733         greeter_set_default_user (display->priv->greeter_session, display->priv->default_user, display->priv->timeout);
734         greeter_set_layout (display->priv->greeter_session, display->priv->default_layout);
735         greeter_set_session (display->priv->greeter_session, display->priv->default_session);
736         g_signal_connect (G_OBJECT (display->priv->greeter_session), "start-session", G_CALLBACK (greeter_start_session_cb), display);
737         g_signal_connect (G_OBJECT (display->priv->greeter_session), "quit", G_CALLBACK (greeter_quit_cb), display);
738         session_set_username (SESSION (display->priv->greeter_session), username);
739         session_set_command (SESSION (display->priv->greeter_session), command);
740         child_process_set_env (CHILD_PROCESS (display->priv->greeter_session), "DISPLAY", xserver_get_address (display->priv->xserver));
741         if (display->priv->greeter_ck_cookie)
742             child_process_set_env (CHILD_PROCESS (display->priv->greeter_session), "XDG_SESSION_COOKIE", display->priv->greeter_ck_cookie);
743         set_env_from_pam_session (SESSION (display->priv->greeter_session), display->priv->greeter_pam_session);
744
745         g_signal_emit (display, signals[START_GREETER], 0, display->priv->greeter_session);
746
747         session_start (SESSION (display->priv->greeter_session), TRUE);
748
749         g_free (command);
750         g_key_file_free (theme);
751     }
752 }
753
754 static void
755 end_display (Display *display)
756 {
757     g_object_unref (display->priv->xserver);
758     display->priv->xserver = NULL;
759     g_signal_emit (display, signals[EXITED], 0);
760 }
761
762 static void
763 xserver_exit_cb (XServer *server, int status, Display *display)
764 {
765     if (status != 0)
766         g_warning ("X server exited with value %d", status);
767     end_display (display);
768 }
769
770 static void
771 xserver_terminate_cb (XServer *server, int signum, Display *display)
772 {
773     g_warning ("X server terminated with signal %d", signum);
774     end_display (display);
775 }
776
777 static void
778 xserver_ready_cb (XServer *xserver, Display *display)
779 {
780     run_script ("Init"); // FIXME: Async
781
782     /* Don't run any sessions on local terminals */
783     if (xserver_get_server_type (xserver) == XSERVER_TYPE_LOCAL_TERMINAL)
784         return;
785
786     /* If have user then automatically login the first time */
787     if (display->priv->default_user && display->priv->timeout == 0 && display->priv->login_count == 0)
788         start_default_session (display, display->priv->default_session, NULL);
789     else
790         start_greeter (display);
791 }
792
793 gboolean
794 display_start (Display *display)
795 {
796     g_return_val_if_fail (display->priv->xserver != NULL, FALSE);
797     g_signal_connect (G_OBJECT (display->priv->xserver), "ready", G_CALLBACK (xserver_ready_cb), display);
798     g_signal_connect (G_OBJECT (display->priv->xserver), "exited", G_CALLBACK (xserver_exit_cb), display);
799     g_signal_connect (G_OBJECT (display->priv->xserver), "terminated", G_CALLBACK (xserver_terminate_cb), display);
800     return xserver_start (display->priv->xserver);
801 }
802
803 static void
804 display_init (Display *display)
805 {
806     display->priv = G_TYPE_INSTANCE_GET_PRIVATE (display, DISPLAY_TYPE, DisplayPrivate);
807     if (strcmp (GREETER_USER, "") != 0)
808         display->priv->greeter_user = g_strdup (GREETER_USER);
809     display->priv->greeter_theme = g_strdup (GREETER_THEME);
810     display->priv->default_layout = g_strdup ("us"); // FIXME: Is there a better default to get?
811     display->priv->default_session = g_strdup (DEFAULT_SESSION);
812 }
813
814 static void
815 display_finalize (GObject *object)
816 {
817     Display *self;
818
819     self = DISPLAY (object);
820
821     if (self->priv->greeter_session)
822         g_object_unref (self->priv->greeter_session);
823     if (self->priv->user_session_timer)
824         g_source_remove (self->priv->user_session_timer);
825     if (self->priv->user_session)
826         g_object_unref (self->priv->user_session);
827     if (self->priv->user_pam_session)
828         g_object_unref (self->priv->user_pam_session);
829     end_ck_session (self->priv->greeter_ck_cookie);
830     g_free (self->priv->greeter_ck_cookie);
831     end_ck_session (self->priv->user_ck_cookie);
832     g_free (self->priv->user_ck_cookie);
833     if (self->priv->xserver)
834         g_object_unref (self->priv->xserver);
835     g_free (self->priv->greeter_user);
836     g_free (self->priv->greeter_theme);
837     g_free (self->priv->default_user);
838     g_free (self->priv->default_layout);
839     g_free (self->priv->default_session);
840
841     G_OBJECT_CLASS (display_parent_class)->finalize (object);
842 }
843
844 static void
845 display_class_init (DisplayClass *klass)
846 {
847     GObjectClass *object_class = G_OBJECT_CLASS (klass);
848
849     object_class->finalize = display_finalize;
850
851     g_type_class_add_private (klass, sizeof (DisplayPrivate));
852
853     signals[START_GREETER] =
854         g_signal_new ("start-greeter",
855                       G_TYPE_FROM_CLASS (klass),
856                       G_SIGNAL_RUN_LAST,
857                       G_STRUCT_OFFSET (DisplayClass, start_greeter),
858                       NULL, NULL,
859                       g_cclosure_marshal_VOID__OBJECT,
860                       G_TYPE_NONE, 1, SESSION_TYPE);
861
862     signals[END_GREETER] =
863         g_signal_new ("end-greeter",
864                       G_TYPE_FROM_CLASS (klass),
865                       G_SIGNAL_RUN_LAST,
866                       G_STRUCT_OFFSET (DisplayClass, end_greeter),
867                       NULL, NULL,
868                       g_cclosure_marshal_VOID__OBJECT,
869                       G_TYPE_NONE, 1, SESSION_TYPE);
870   
871     signals[START_SESSION] =
872         g_signal_new ("start-session",
873                       G_TYPE_FROM_CLASS (klass),
874                       G_SIGNAL_RUN_LAST,
875                       G_STRUCT_OFFSET (DisplayClass, start_session),
876                       NULL, NULL,
877                       g_cclosure_marshal_VOID__OBJECT,
878                       G_TYPE_NONE, 1, SESSION_TYPE);
879
880     signals[END_SESSION] =
881         g_signal_new ("end-session",
882                       G_TYPE_FROM_CLASS (klass),
883                       G_SIGNAL_RUN_LAST,
884                       G_STRUCT_OFFSET (DisplayClass, end_session),
885                       NULL, NULL,
886                       g_cclosure_marshal_VOID__OBJECT,
887                       G_TYPE_NONE, 1, SESSION_TYPE);
888
889     signals[EXITED] =
890         g_signal_new ("exited",
891                       G_TYPE_FROM_CLASS (klass),
892                       G_SIGNAL_RUN_LAST,
893                       G_STRUCT_OFFSET (DisplayClass, exited),
894                       NULL, NULL,
895                       g_cclosure_marshal_VOID__VOID,
896                       G_TYPE_NONE, 0);
897 }