]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/lightdm.c
Rename test-xserver to X and remove --xserver-command option
[sojka/lightdm.git] / src / lightdm.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 <stdio.h>
16 #include <sys/stat.h>
17 #include <glib.h>
18 #include <glib/gi18n.h>
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <sys/stat.h>
22
23 #include "configuration.h"
24 #include "display-manager.h"
25 #include "xdmcp-server.h"
26 #include "vnc-server.h"
27 #include "seat-xdmcp-session.h"
28 #include "seat-xvnc.h"
29 #include "xserver.h"
30 #include "pam-session.h"
31 #include "process.h"
32
33 static gchar *config_path = NULL;
34 static GMainLoop *loop = NULL;
35 static GTimer *log_timer;
36 static int log_fd = -1;
37 static gboolean debug = FALSE;
38
39 static DisplayManager *display_manager = NULL;
40 static XDMCPServer *xdmcp_server = NULL;
41 static VNCServer *vnc_server = NULL;
42 static GDBusConnection *bus = NULL;
43 static guint bus_id;
44 static GDBusNodeInfo *seat_info;
45 static GHashTable *seat_bus_entries;
46 static guint seat_index = 0;
47 static GDBusNodeInfo *session_info;
48 static GHashTable *session_bus_entries;
49 static guint session_index = 0;
50 static gint exit_code = EXIT_SUCCESS;
51
52 typedef struct
53 {
54     gchar *path;
55     gchar *parent_path;
56     gchar *removed_signal;
57     guint bus_id;
58 } BusEntry;
59
60 #define LIGHTDM_BUS_NAME "org.freedesktop.DisplayManager"
61
62 static void
63 log_cb (const gchar *log_domain, GLogLevelFlags log_level,
64         const gchar *message, gpointer data)
65 {
66     /* Log everything to a file */
67     if (log_fd >= 0)
68     {
69         const gchar *prefix;
70         gchar *text;
71         ssize_t n_written;
72
73         switch (log_level & G_LOG_LEVEL_MASK)
74         {
75         case G_LOG_LEVEL_ERROR:
76             prefix = "ERROR:";
77             break;
78         case G_LOG_LEVEL_CRITICAL:
79             prefix = "CRITICAL:";
80             break;
81         case G_LOG_LEVEL_WARNING:
82             prefix = "WARNING:";
83             break;
84         case G_LOG_LEVEL_MESSAGE:
85             prefix = "MESSAGE:";
86             break;
87         case G_LOG_LEVEL_INFO:
88             prefix = "INFO:";
89             break;
90         case G_LOG_LEVEL_DEBUG:
91             prefix = "DEBUG:";
92             break;
93         default:
94             prefix = "LOG:";
95             break;
96         }
97
98         text = g_strdup_printf ("[%+.2fs] %s %s\n", g_timer_elapsed (log_timer, NULL), prefix, message);
99         n_written = write (log_fd, text, strlen (text));
100         if (n_written < 0)
101             ; /* Check result so compiler doesn't warn about it */
102         g_free (text);
103     }
104
105     /* Only show debug if requested */
106     if (log_level & G_LOG_LEVEL_DEBUG) {
107         if (debug)
108             g_log_default_handler (log_domain, log_level, message, data);
109     }
110     else
111         g_log_default_handler (log_domain, log_level, message, data);    
112 }
113
114 static void
115 log_init (void)
116 {
117     gchar *log_dir, *path;
118
119     log_timer = g_timer_new ();
120
121     /* Log to a file */
122     log_dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
123     path = g_build_filename (log_dir, "lightdm.log", NULL);
124     g_free (log_dir);
125
126     log_fd = open (path, O_WRONLY | O_CREAT | O_TRUNC, 0600);
127     g_log_set_default_handler (log_cb, NULL);
128
129     g_debug ("Logging to %s", path);
130     g_free (path);
131 }
132
133 static void
134 signal_cb (Process *process, int signum)
135 {
136     g_debug ("Caught %s signal, shutting down", g_strsignal (signum));
137     display_manager_stop (display_manager);
138     // FIXME: Stop XDMCP server
139 }
140
141 static void
142 display_manager_stopped_cb (DisplayManager *display_manager)
143 {
144     g_debug ("Stopping Light Display Manager");
145     exit (exit_code);
146 }
147
148 static GVariant *
149 handle_display_manager_get_property (GDBusConnection       *connection,
150                                      const gchar           *sender,
151                                      const gchar           *object_path,
152                                      const gchar           *interface_name,
153                                      const gchar           *property_name,
154                                      GError               **error,
155                                      gpointer               user_data)
156 {
157     GVariant *result = NULL;
158
159     if (g_strcmp0 (property_name, "Seats") == 0)
160     {
161         GVariantBuilder *builder;
162         GHashTableIter iter;
163         gpointer value;
164
165         builder = g_variant_builder_new (G_VARIANT_TYPE ("ao"));
166         g_hash_table_iter_init (&iter, seat_bus_entries);
167         while (g_hash_table_iter_next (&iter, NULL, &value))
168         {
169             BusEntry *entry = value;
170             g_variant_builder_add_value (builder, g_variant_new_object_path (entry->path));
171         }
172         result = g_variant_builder_end (builder);
173         g_variant_builder_unref (builder);
174     }
175     else if (g_strcmp0 (property_name, "Sessions") == 0)
176     {
177         GVariantBuilder *builder;
178         GHashTableIter iter;
179         gpointer value;
180
181         builder = g_variant_builder_new (G_VARIANT_TYPE ("ao"));
182         g_hash_table_iter_init (&iter, session_bus_entries);
183         while (g_hash_table_iter_next (&iter, NULL, &value))
184         {
185             BusEntry *entry = value;
186             g_variant_builder_add_value (builder, g_variant_new_object_path (entry->path));
187         }
188         result = g_variant_builder_end (builder);
189         g_variant_builder_unref (builder);
190     }
191
192     return result;
193 }
194
195 static void
196 set_seat_properties (Seat *seat, const gchar *config_section)
197 {
198     gchar **keys;
199     gint i;
200
201     keys = config_get_keys (config_get_instance (), "SeatDefaults");
202     for (i = 0; keys[i]; i++)
203     {
204         gchar *value = config_get_string (config_get_instance (), "SeatDefaults", keys[i]);
205         seat_set_property (seat, keys[i], value);
206         g_free (value);
207     }
208     g_strfreev (keys);
209
210     if (config_section)
211     {
212         keys = config_get_keys (config_get_instance (), config_section);
213         for (i = 0; keys[i]; i++)
214         {
215             gchar *value = config_get_string (config_get_instance (), config_section, keys[i]);
216             seat_set_property (seat, keys[i], value);
217             g_free (value);
218         }
219         g_strfreev (keys);
220     }
221 }
222
223 static void
224 handle_display_manager_call (GDBusConnection       *connection,
225                              const gchar           *sender,
226                              const gchar           *object_path,
227                              const gchar           *interface_name,
228                              const gchar           *method_name,
229                              GVariant              *parameters,
230                              GDBusMethodInvocation *invocation,
231                              gpointer               user_data)
232 {
233     if (g_strcmp0 (method_name, "AddSeat") == 0)
234     {
235         gchar *type;
236         GVariantIter *property_iter;
237         gchar *name, *value;
238         Seat *seat;
239
240         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(sa(ss))")))
241             return;
242
243         g_variant_get (parameters, "(&sa(ss))", &type, &property_iter);
244
245         g_debug ("Adding seat of type %s", type);
246
247         seat = seat_new (type);
248         if (seat)
249         {
250             set_seat_properties (seat, NULL);
251             while (g_variant_iter_loop (property_iter, "(&s&s)", &name, &value))
252                 seat_set_property (seat, name, value);
253         }
254         g_variant_iter_free (property_iter);
255
256         if (!seat)
257         {
258             // FIXME: Need to make proper error
259             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unable to create seat of type %s", type);
260             return;
261         }
262
263         if (display_manager_add_seat (display_manager, seat))
264         {
265             BusEntry *entry;
266
267             entry = g_hash_table_lookup (seat_bus_entries, seat);
268             g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", entry->path));
269         }
270         else// FIXME: Need to make proper error
271             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to start seat");
272     }
273     else if (g_strcmp0 (method_name, "AddLocalXSeat") == 0)
274     {
275         gint display_number;
276         Seat *seat;
277
278         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(i)")))
279             return;
280
281         g_variant_get (parameters, "(i)", &display_number);
282
283         g_debug ("Adding local X seat :%d", display_number);
284
285         seat = seat_new ("xremote");
286         if (seat)
287         {
288             gchar *display_number_string;
289
290             set_seat_properties (seat, NULL);
291             display_number_string = g_strdup_printf ("%d", display_number);
292             seat_set_property (seat, "xserver-display-number", display_number_string);
293             g_free (display_number_string);
294         }
295
296         if (!seat)
297         {
298             // FIXME: Need to make proper error
299             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unable to create local X seat");
300             return;
301         }
302
303         if (display_manager_add_seat (display_manager, seat))
304         {
305             BusEntry *entry;
306
307             entry = g_hash_table_lookup (seat_bus_entries, seat);
308             g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", entry->path));
309         }
310         else// FIXME: Need to make proper error
311             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to start seat");
312     }
313 }
314
315 static GVariant *
316 handle_seat_get_property (GDBusConnection       *connection,
317                           const gchar           *sender,
318                           const gchar           *object_path,
319                           const gchar           *interface_name,
320                           const gchar           *property_name,
321                           GError               **error,
322                           gpointer               user_data)
323 {
324     Seat *seat = user_data;
325     GVariant *result = NULL;
326
327     if (g_strcmp0 (property_name, "CanSwitch") == 0)
328         result = g_variant_new_boolean (seat_get_can_switch (seat));
329     if (g_strcmp0 (property_name, "HasGuestAccount") == 0)
330         result = g_variant_new_boolean (seat_get_allow_guest (seat));
331     else if (g_strcmp0 (property_name, "Sessions") == 0)
332     {
333         GVariantBuilder *builder;
334         GList *link;
335
336         builder = g_variant_builder_new (G_VARIANT_TYPE ("ao"));
337         for (link = seat_get_displays (seat); link; link = link->next)
338         {
339             Display *display = link->data;
340             BusEntry *entry;
341             entry = g_hash_table_lookup (session_bus_entries, display_get_session (display));
342             if (entry)
343                 g_variant_builder_add_value (builder, g_variant_new_object_path (entry->path));
344         }
345         result = g_variant_builder_end (builder);
346         g_variant_builder_unref (builder);
347     }
348   
349     return result;
350 }
351
352 static void
353 handle_seat_call (GDBusConnection       *connection,
354                   const gchar           *sender,
355                   const gchar           *object_path,
356                   const gchar           *interface_name,
357                   const gchar           *method_name,
358                   GVariant              *parameters,
359                   GDBusMethodInvocation *invocation,
360                   gpointer               user_data)
361 {
362     Seat *seat = user_data;
363   
364     if (g_strcmp0 (method_name, "SwitchToGreeter") == 0)
365     {
366         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
367             return;
368
369         seat_switch_to_greeter (seat);
370         g_dbus_method_invocation_return_value (invocation, NULL);
371     }
372     else if (g_strcmp0 (method_name, "SwitchToUser") == 0)
373     {
374         const gchar *username, *session_name;
375
376         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(ss)")))
377             return;
378
379         g_variant_get (parameters, "(&s&s)", &username, &session_name);
380         if (strcmp (session_name, "") == 0)
381             session_name = NULL;
382
383         seat_switch_to_user (seat, username, session_name);
384         g_dbus_method_invocation_return_value (invocation, NULL);
385     }
386     else if (g_strcmp0 (method_name, "SwitchToGuest") == 0)
387     {
388         const gchar *session_name;
389
390         if (!g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(s)")))
391             return;
392
393         g_variant_get (parameters, "(&s)", &session_name);
394         if (strcmp (session_name, "") == 0)
395             session_name = NULL;
396
397         seat_switch_to_guest (seat, session_name);
398         g_dbus_method_invocation_return_value (invocation, NULL);
399     }
400 }
401
402 static GVariant *
403 handle_session_get_property (GDBusConnection       *connection,
404                              const gchar           *sender,
405                              const gchar           *object_path,
406                              const gchar           *interface_name,
407                              const gchar           *property_name,
408                              GError               **error,
409                              gpointer               user_data)
410 {
411     Session *session = user_data;
412     BusEntry *entry;
413
414     entry = g_hash_table_lookup (session_bus_entries, session);
415     if (g_strcmp0 (property_name, "Seat") == 0)
416         return g_variant_new_object_path (entry ? entry->parent_path : "");
417     else if (g_strcmp0 (property_name, "UserName") == 0)
418         return g_variant_new_string (user_get_name (session_get_user (session)));
419
420     return NULL;
421 }
422
423 static void
424 handle_session_call (GDBusConnection       *connection,
425                      const gchar           *sender,
426                      const gchar           *object_path,
427                      const gchar           *interface_name,
428                      const gchar           *method_name,
429                      GVariant              *parameters,
430                      GDBusMethodInvocation *invocation,
431                      gpointer               user_data)
432 {
433 }
434
435 static BusEntry *
436 bus_entry_new (const gchar *path, const gchar *parent_path, const gchar *removed_signal)
437 {
438     BusEntry *entry;
439
440     entry = g_malloc0 (sizeof (BusEntry));
441     entry->path = g_strdup (path);
442     entry->parent_path = g_strdup (parent_path);
443     entry->removed_signal = g_strdup (removed_signal);
444
445     return entry;
446 }
447
448 static void
449 bus_entry_free (gpointer data)
450 {
451     BusEntry *entry = data;
452
453     g_dbus_connection_unregister_object (bus, entry->bus_id);
454
455     g_dbus_connection_emit_signal (bus,
456                                    NULL,
457                                    "/org/freedesktop/DisplayManager",
458                                    "org.freedesktop.DisplayManager",
459                                    entry->removed_signal,
460                                    g_variant_new ("(o)", entry->path),
461                                    NULL);
462
463     g_free (entry->path);
464     g_free (entry->parent_path);
465     g_free (entry->removed_signal);
466     g_free (entry);
467 }
468
469 static gboolean
470 start_session_cb (Display *display, Seat *seat)
471 {
472     Session *session;
473     BusEntry *seat_entry;
474     gchar *path;
475
476     session = display_get_session (display);
477
478     seat_entry = g_hash_table_lookup (seat_bus_entries, seat);
479     session_set_env (session, "XDG_SEAT_PATH", seat_entry->path);
480
481     path = g_strdup_printf ("/org/freedesktop/DisplayManager/Session%d", session_index);
482     session_index++;
483     session_set_env (session, "XDG_SESSION_PATH", path);
484     g_free (path);
485
486     return FALSE;
487 }
488
489 static void
490 session_stopped_cb (Session *session, Seat *seat)
491 {
492     g_hash_table_remove (session_bus_entries, session);
493 }
494
495 static gboolean
496 session_started_cb (Display *display, Seat *seat)
497 {
498     static const GDBusInterfaceVTable session_vtable =
499     {
500         handle_session_call,
501         handle_session_get_property
502     };
503     Session *session;
504     BusEntry *seat_entry, *entry;
505
506     session = display_get_session (display);
507
508     g_signal_connect (session, "stopped", G_CALLBACK (session_stopped_cb), seat);
509
510     seat_entry = g_hash_table_lookup (seat_bus_entries, seat);
511     entry = bus_entry_new (session_get_env (session, "XDG_SESSION_PATH"), seat_entry ? seat_entry->path : NULL, "SessionRemoved");
512     g_hash_table_insert (session_bus_entries, g_object_ref (session), entry);
513
514     g_debug ("Registering session with bus path %s", entry->path);
515
516     entry->bus_id = g_dbus_connection_register_object (bus,
517                                                        entry->path,
518                                                        session_info->interfaces[0],
519                                                        &session_vtable,
520                                                        g_object_ref (session), g_object_unref,
521                                                        NULL);
522     g_dbus_connection_emit_signal (bus,
523                                    NULL,
524                                    "/org/freedesktop/DisplayManager",
525                                    "org.freedesktop.DisplayManager",
526                                    "SessionAdded",
527                                    g_variant_new ("(o)", entry->path),
528                                    NULL);
529
530     return FALSE;
531 }
532
533 static void
534 display_added_cb (Seat *seat, Display *display)
535 {
536     g_signal_connect (display, "start-session", G_CALLBACK (start_session_cb), seat);
537     g_signal_connect_after (display, "start-session", G_CALLBACK (session_started_cb), seat);
538 }
539
540 static void
541 seat_added_cb (DisplayManager *display_manager, Seat *seat)
542 {
543     static const GDBusInterfaceVTable seat_vtable =
544     {
545         handle_seat_call,
546         handle_seat_get_property
547     };
548     GList *link;
549     gchar *path;
550     BusEntry *entry;
551
552     g_signal_connect (seat, "display-added", G_CALLBACK (display_added_cb), NULL);
553     for (link = seat_get_displays (seat); link; link = link->next)
554         display_added_cb (seat, (Display *) link->data);
555
556     path = g_strdup_printf ("/org/freedesktop/DisplayManager/Seat%d", seat_index);
557     seat_index++;
558
559     entry = bus_entry_new (path, NULL, "SeatRemoved");
560     g_free (path);
561     g_hash_table_insert (seat_bus_entries, g_object_ref (seat), entry);
562
563     g_debug ("Registering seat with bus path %s", entry->path);
564
565     entry->bus_id = g_dbus_connection_register_object (bus,
566                                                        entry->path,
567                                                        seat_info->interfaces[0],
568                                                        &seat_vtable,
569                                                        g_object_ref (seat), g_object_unref,
570                                                        NULL);
571     g_dbus_connection_emit_signal (bus,
572                                    NULL,
573                                    "/org/freedesktop/DisplayManager",
574                                    "org.freedesktop.DisplayManager",
575                                    "SeatAdded",
576                                    g_variant_new ("(o)", entry->path),
577                                    NULL);
578 }
579
580 static void
581 seat_removed_cb (DisplayManager *display_manager, Seat *seat)
582 {
583     g_hash_table_remove (seat_bus_entries, seat);
584
585     if (seat_get_boolean_property (seat, "exit-on-failure"))
586     {
587         g_debug ("Stopping lightdm, required seat has stopped");
588         exit_code = EXIT_FAILURE;
589         display_manager_stop (display_manager);
590     }
591 }
592
593 static void
594 bus_acquired_cb (GDBusConnection *connection,
595                  const gchar     *name,
596                  gpointer         user_data)
597 {
598     const gchar *display_manager_interface =
599         "<node>"
600         "  <interface name='org.freedesktop.DisplayManager'>"
601         "    <property name='Seats' type='ao' access='read'/>"
602         "    <property name='Sessions' type='ao' access='read'/>"
603         "    <method name='AddSeat'>"
604         "      <arg name='type' direction='in' type='s'/>"
605         "      <arg name='properties' direction='in' type='a(ss)'/>"
606         "      <arg name='seat' direction='out' type='o'/>"
607         "    </method>"
608         "    <method name='AddLocalXSeat'>"
609         "      <arg name='display-number' direction='in' type='i'/>"
610         "      <arg name='seat' direction='out' type='o'/>"
611         "    </method>"
612         "    <signal name='SeatAdded'>"
613         "      <arg name='seat' type='o'/>"
614         "    </signal>"
615         "    <signal name='SeatRemoved'>"
616         "      <arg name='seat' type='o'/>"
617         "    </signal>"
618         "    <signal name='SessionAdded'>"
619         "      <arg name='session' type='o'/>"
620         "    </signal>"
621         "    <signal name='SessionRemoved'>"
622         "      <arg name='session' type='o'/>"
623         "    </signal>"
624         "  </interface>"
625         "</node>";
626     static const GDBusInterfaceVTable display_manager_vtable =
627     {
628         handle_display_manager_call,
629         handle_display_manager_get_property
630     };
631     const gchar *seat_interface =
632         "<node>"
633         "  <interface name='org.freedesktop.DisplayManager.Seat'>"
634         "    <property name='CanSwitch' type='b' access='read'/>"
635         "    <property name='HasGuestAccount' type='b' access='read'/>"
636         "    <property name='Sessions' type='ao' access='read'/>"
637         "    <method name='SwitchToGreeter'/>"
638         "    <method name='SwitchToUser'>"
639         "      <arg name='username' direction='in' type='s'/>"
640         "      <arg name='session-name' direction='in' type='s'/>"
641         "    </method>"
642         "    <method name='SwitchToGuest'>"
643         "      <arg name='session-name' direction='in' type='s'/>"
644         "    </method>"
645         "  </interface>"
646         "</node>";
647     const gchar *session_interface =
648         "<node>"
649         "  <interface name='org.freedesktop.DisplayManager.Session'>"
650         "    <property name='Seat' type='o' access='read'/>"
651         "    <property name='UserName' type='s' access='read'/>"
652         "  </interface>"
653         "</node>";
654     GDBusNodeInfo *display_manager_info;
655     GList *link;
656   
657     g_debug ("Acquired bus name");
658
659     bus = connection;
660
661     display_manager_info = g_dbus_node_info_new_for_xml (display_manager_interface, NULL);
662     g_assert (display_manager_info != NULL);
663     seat_info = g_dbus_node_info_new_for_xml (seat_interface, NULL);
664     g_assert (seat_info != NULL);
665     session_info = g_dbus_node_info_new_for_xml (session_interface, NULL);
666     g_assert (session_info != NULL);
667
668     bus_id = g_dbus_connection_register_object (connection,
669                                                 "/org/freedesktop/DisplayManager",
670                                                 display_manager_info->interfaces[0],
671                                                 &display_manager_vtable,
672                                                 NULL, NULL,
673                                                 NULL);
674
675     seat_bus_entries = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, bus_entry_free);
676     session_bus_entries = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, bus_entry_free);
677
678     g_signal_connect (display_manager, "seat-added", G_CALLBACK (seat_added_cb), NULL);
679     g_signal_connect (display_manager, "seat-removed", G_CALLBACK (seat_removed_cb), NULL);
680     for (link = display_manager_get_seats (display_manager); link; link = link->next)
681         seat_added_cb (display_manager, (Seat *) link->data);
682 }
683
684 static void
685 name_lost_cb (GDBusConnection *connection,
686               const gchar *name,
687               gpointer user_data)
688 {
689     if (connection)
690         g_printerr ("Failed to use bus name " LIGHTDM_BUS_NAME ", do you have appropriate permissions?\n");
691     else
692         g_printerr ("Failed to get D-Bus connection\n");
693
694     exit (EXIT_FAILURE);
695 }
696
697 static gchar *
698 path_make_absolute (gchar *path)
699 {
700     gchar *cwd, *abs_path;
701   
702     if (!path)
703         return NULL;
704
705     if (g_path_is_absolute (path))
706         return path;
707
708     cwd = g_get_current_dir ();
709     abs_path = g_build_filename (cwd, path, NULL);
710     g_free (path);
711
712     return abs_path;
713 }
714
715 static gboolean
716 xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session)
717 {
718     SeatXDMCPSession *seat;
719     gboolean result;
720
721     seat = seat_xdmcp_session_new (session);
722     set_seat_properties (SEAT (seat), NULL);
723     result = display_manager_add_seat (display_manager, SEAT (seat));
724     g_object_unref (seat);
725
726     return result;
727 }
728
729 static void
730 vnc_connection_cb (VNCServer *server, GSocket *connection)
731 {
732     SeatXVNC *seat;
733
734     seat = seat_xvnc_new (connection);
735     set_seat_properties (SEAT (seat), NULL);
736     display_manager_add_seat (display_manager, SEAT (seat));
737     g_object_unref (seat);
738 }
739
740 int
741 main (int argc, char **argv)
742 {
743     FILE *pid_file;
744     GOptionContext *option_context;
745     gchar **groups, **i;
746     gint n_seats = 0;
747     gboolean explicit_config = FALSE;
748     gboolean test_mode = FALSE;
749     gchar *pid_path = "/var/run/lightdm.pid";
750     gchar *xsessions_dir = NULL;
751     gchar *xgreeters_dir = NULL;
752     gchar *greeter_session = NULL;
753     gchar *user_session = NULL;
754     gchar *session_wrapper = NULL;
755     gchar *config_dir;
756     gchar *log_dir = NULL;
757     gchar *run_dir = NULL;
758     gchar *cache_dir = NULL;
759     gchar *default_log_dir = g_strdup (LOG_DIR);
760     gchar *default_run_dir = g_strdup (RUN_DIR);
761     gchar *default_cache_dir = g_strdup (CACHE_DIR);
762     gchar *minimum_vt = NULL;
763     gchar *minimum_display_number = NULL;
764     gboolean show_version = FALSE;
765     GOptionEntry options[] = 
766     {
767         { "config", 'c', 0, G_OPTION_ARG_STRING, &config_path,
768           /* Help string for command line --config flag */
769           N_("Use configuration file"), "FILE" },
770         { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug,
771           /* Help string for command line --debug flag */
772           N_("Print debugging messages"), NULL },
773         { "test-mode", 0, 0, G_OPTION_ARG_NONE, &test_mode,
774           /* Help string for command line --test-mode flag */
775           N_("Run as unprivileged user, skipping things that require root access"), NULL },
776         { "pid-file", 0, 0, G_OPTION_ARG_STRING, &pid_path,
777           /* Help string for command line --pid-file flag */
778           N_("File to write PID into"), "FILE" },
779         { "greeter-session", 0, 0, G_OPTION_ARG_STRING, &greeter_session,
780           /* Help string for command line --greeter-session flag */
781           N_("Greeter session"), "SESSION" },
782         { "user-session", 0, 0, G_OPTION_ARG_STRING, &user_session,
783           /* Help string for command line --user-session flag */
784           N_("User session"), "SESSION" },
785         { "session-wrapper", 0, 0, G_OPTION_ARG_STRING, &session_wrapper,
786           /* Help string for command line --session-wrapper flag */
787           N_("Session wrapper"), "SESSION" },
788         { "minimum-vt", 0, 0, G_OPTION_ARG_STRING, &minimum_vt,
789           /* Help string for command line --minimum-vt flag */
790           N_("Minimum VT to use for X servers"), "NUMBER" },
791         { "minimum-display-number", 0, 0, G_OPTION_ARG_STRING, &minimum_display_number,
792           /* Help string for command line --minimum-display-number flag */
793           N_("Minimum display number to use for X servers"), "NUMBER" },
794         { "xsessions-dir", 0, 0, G_OPTION_ARG_STRING, &xsessions_dir,
795           /* Help string for command line --xsessions-dir flag */
796           N_("Directory to load X sessions from"), "DIRECTORY" },
797         { "xgreeters-dir", 0, 0, G_OPTION_ARG_STRING, &xgreeters_dir,
798           /* Help string for command line --xgreeters-dir flag */
799           N_("Directory to load X greeters from"), "DIRECTORY" },
800         { "log-dir", 0, 0, G_OPTION_ARG_STRING, &log_dir,
801           /* Help string for command line --log-dir flag */
802           N_("Directory to write logs to"), "DIRECTORY" },
803         { "run-dir", 0, 0, G_OPTION_ARG_STRING, &run_dir,
804           /* Help string for command line --run-dir flag */
805           N_("Directory to store running state"), "DIRECTORY" },
806         { "cache-dir", 0, 0, G_OPTION_ARG_STRING, &cache_dir,
807           /* Help string for command line --cache-dir flag */
808           N_("Directory to cached information"), "DIRECTORY" },
809         { "version", 'v', 0, G_OPTION_ARG_NONE, &show_version,
810           /* Help string for command line --version flag */
811           N_("Show release version"), NULL },
812         { NULL }
813     };
814     GError *error = NULL;
815
816     g_thread_init (NULL);
817     g_type_init ();
818
819     g_signal_connect (process_get_current (), "got-signal", G_CALLBACK (signal_cb), NULL);
820
821     option_context = g_option_context_new (/* Arguments and description for --help test */
822                                            _("- Display Manager"));
823     g_option_context_add_main_entries (option_context, options, GETTEXT_PACKAGE);
824     if (!g_option_context_parse (option_context, &argc, &argv, &error))
825     {
826         if (error)
827             fprintf (stderr, "%s\n", error->message);
828         fprintf (stderr, /* Text printed out when an unknown command-line argument provided */
829                  _("Run '%s --help' to see a full list of available command line options."), argv[0]);
830         fprintf (stderr, "\n");
831         return EXIT_FAILURE;
832     }
833     g_clear_error (&error);
834
835     if (show_version)
836     {
837         /* NOTE: Is not translated so can be easily parsed */
838         g_printerr ("lightdm %s\n", VERSION);
839         return EXIT_SUCCESS;
840     }
841
842     if (config_path)
843     {
844         config_dir = g_path_get_basename (config_path);
845         config_dir = path_make_absolute (config_dir);
846         explicit_config = TRUE;
847     }
848     else
849     {
850         config_dir = g_strdup (CONFIG_DIR);
851         config_path = g_build_filename (config_dir, "lightdm.conf", NULL);
852     }
853     config_set_string (config_get_instance (), "LightDM", "config-directory", config_dir);
854     g_free (config_dir);
855
856     if (!test_mode && getuid () != 0)
857     {
858         g_printerr ("Only root can run Light Display Manager.  To run as a regular user for testing run with the --test-mode flag.\n");
859         return EXIT_FAILURE;
860     }
861
862     /* If running inside an X server use Xephyr for display */
863     if (getenv ("DISPLAY") && getuid () != 0)
864     {
865         gchar *xserver_path;
866
867         xserver_path = g_find_program_in_path ("Xephyr");
868         if (!xserver_path)
869         {
870             g_printerr ("Running inside an X server requires Xephyr to be installed but it cannot be found.  Please install it or update your PATH environment variable.\n");
871             return EXIT_FAILURE;
872         }
873         g_free (xserver_path);
874     }
875
876     /* Make sure the system binary directory (where the greeters are installed) is in the path */
877     if (test_mode)
878     {
879         const gchar *path = g_getenv ("PATH");
880         gchar *new_path;
881
882         if (path)
883             new_path = g_strdup_printf ("%s:%s", path, SBIN_DIR);
884         else
885             new_path = g_strdup (SBIN_DIR);
886         g_setenv ("PATH", new_path, TRUE);
887         g_free (new_path);
888     }
889
890     /* Write PID file */
891     pid_file = fopen (pid_path, "w");
892     if (pid_file)
893     {
894         fprintf (pid_file, "%d\n", getpid ());
895         fclose (pid_file);
896     }
897
898     /* Always use absolute directories as child processes may run from different locations */
899     xsessions_dir = path_make_absolute (xsessions_dir);
900     xgreeters_dir = path_make_absolute (xgreeters_dir);
901
902     /* If not running as root write output to directories we control */
903     if (getuid () != 0)
904     {
905         g_free (default_log_dir);
906         default_log_dir = g_build_filename (g_get_user_cache_dir (), "lightdm", "log", NULL);
907         g_free (default_run_dir);
908         default_run_dir = g_build_filename (g_get_user_cache_dir (), "lightdm", "run", NULL);
909         g_free (default_cache_dir);
910         default_cache_dir = g_build_filename (g_get_user_cache_dir (), "lightdm", "cache", NULL);
911     }
912
913     /* Load config file */
914     if (!config_load_from_file (config_get_instance (), config_path, &error))
915     {
916         gboolean is_empty;
917
918         is_empty = error && g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT);
919       
920         if (explicit_config || !is_empty)      
921         {
922             if (error)
923                 g_printerr ("Failed to load configuration from %s: %s\n", config_path, error->message);
924             exit (EXIT_FAILURE);
925         }
926     }
927     g_clear_error (&error);
928
929     /* Set default values */
930     if (!config_has_key (config_get_instance (), "LightDM", "start-default-seat"))
931         config_set_boolean (config_get_instance (), "LightDM", "start-default-seat", TRUE);
932     if (!config_has_key (config_get_instance (), "LightDM", "minimum-vt"))
933         config_set_integer (config_get_instance (), "LightDM", "minimum-vt", 7);
934     if (!config_has_key (config_get_instance (), "LightDM", "guest-account-script"))
935         config_set_string (config_get_instance (), "LightDM", "guest-account-script", "guest-account");
936     if (!config_has_key (config_get_instance (), "LightDM", "greeter-user"))
937         config_set_string (config_get_instance (), "LightDM", "greeter-user", GREETER_USER);
938     if (!config_has_key (config_get_instance (), "SeatDefaults", "type"))
939         config_set_string (config_get_instance (), "SeatDefaults", "type", "xlocal");
940     if (!config_has_key (config_get_instance (), "SeatDefaults", "xserver-command"))
941         config_set_string (config_get_instance (), "SeatDefaults", "xserver-command", "X");
942     if (!config_has_key (config_get_instance (), "SeatDefaults", "start-session"))
943         config_set_boolean (config_get_instance (), "SeatDefaults", "start-session", TRUE);
944     if (!config_has_key (config_get_instance (), "SeatDefaults", "allow-guest"))
945         config_set_boolean (config_get_instance (), "SeatDefaults", "allow-guest", TRUE);
946     if (!config_has_key (config_get_instance (), "SeatDefaults", "greeter-session"))
947         config_set_string (config_get_instance (), "SeatDefaults", "greeter-session", GREETER_SESSION);
948     if (!config_has_key (config_get_instance (), "SeatDefaults", "user-session"))
949         config_set_string (config_get_instance (), "SeatDefaults", "user-session", USER_SESSION);
950     if (!config_has_key (config_get_instance (), "SeatDefaults", "session-wrapper"))
951         config_set_string (config_get_instance (), "SeatDefaults", "session-wrapper", "lightdm-session");
952     if (!config_has_key (config_get_instance (), "LightDM", "log-directory"))
953         config_set_string (config_get_instance (), "LightDM", "log-directory", default_log_dir);
954     g_free (default_log_dir);
955     if (!config_has_key (config_get_instance (), "LightDM", "run-directory"))
956         config_set_string (config_get_instance (), "LightDM", "run-directory", default_run_dir);
957     g_free (default_run_dir);
958     if (!config_has_key (config_get_instance (), "LightDM", "cache-directory"))
959         config_set_string (config_get_instance (), "LightDM", "cache-directory", default_cache_dir);
960     g_free (default_cache_dir);
961     if (!config_has_key (config_get_instance (), "LightDM", "xsessions-directory"))
962         config_set_string (config_get_instance (), "LightDM", "xsessions-directory", XSESSIONS_DIR);
963     if (!config_has_key (config_get_instance (), "LightDM", "xgreeters-directory"))
964         config_set_string (config_get_instance (), "LightDM", "xgreeters-directory", XGREETERS_DIR);
965
966     /* Override defaults */
967     if (minimum_vt)
968         config_set_integer (config_get_instance (), "LightDM", "minimum-vt", atoi (minimum_vt));
969     g_free (minimum_vt);
970     if (minimum_display_number)
971         config_set_integer (config_get_instance (), "LightDM", "minimum-display-number", atoi (minimum_display_number));
972     g_free (minimum_display_number);
973     if (log_dir)
974         config_set_string (config_get_instance (), "LightDM", "log-directory", log_dir);
975     g_free (log_dir);
976     if (run_dir)
977         config_set_string (config_get_instance (), "LightDM", "run-directory", run_dir);
978     g_free (run_dir);
979     if (cache_dir)
980         config_set_string (config_get_instance (), "LightDM", "cache-directory", cache_dir);
981     g_free (cache_dir);
982     if (xsessions_dir)
983         config_set_string (config_get_instance (), "LightDM", "xsessions-directory", xsessions_dir);
984     g_free (xsessions_dir);
985     if (xgreeters_dir)
986         config_set_string (config_get_instance (), "LightDM", "xgreeters-directory", xgreeters_dir);
987     g_free (xgreeters_dir);
988     if (greeter_session)
989         config_set_string (config_get_instance (), "SeatDefaults", "greeter-session", greeter_session);
990     g_free (greeter_session);
991     if (user_session)
992         config_set_string (config_get_instance (), "SeatDefaults", "user-session", user_session);
993     g_free (user_session);
994     if (session_wrapper)
995         config_set_string (config_get_instance (), "SeatDefaults", "session-wrapper", session_wrapper);
996     g_free (session_wrapper);
997
998     /* Create run and cache directories */
999     g_mkdir_with_parents (config_get_string (config_get_instance (), "LightDM", "log-directory"), S_IRWXU | S_IXGRP | S_IXOTH);  
1000     g_mkdir_with_parents (config_get_string (config_get_instance (), "LightDM", "run-directory"), S_IRWXU | S_IXGRP | S_IXOTH);
1001     g_mkdir_with_parents (config_get_string (config_get_instance (), "LightDM", "cache-directory"), S_IRWXU | S_IXGRP | S_IXOTH);
1002
1003     loop = g_main_loop_new (NULL, FALSE);
1004
1005     log_init ();
1006
1007     g_debug ("Starting Light Display Manager %s, UID=%i PID=%i", VERSION, getuid (), getpid ());
1008
1009     g_debug ("Loaded configuration from %s", config_path);
1010     g_free (config_path);
1011
1012     g_debug ("Using D-Bus name %s", LIGHTDM_BUS_NAME);
1013     g_bus_own_name (getuid () == 0 ? G_BUS_TYPE_SYSTEM : G_BUS_TYPE_SESSION,
1014                     LIGHTDM_BUS_NAME,
1015                     G_BUS_NAME_OWNER_FLAGS_NONE,
1016                     bus_acquired_cb,
1017                     NULL,
1018                     name_lost_cb,
1019                     NULL,
1020                     NULL);
1021
1022     if (getuid () != 0)
1023         g_debug ("Running in user mode");
1024     if (getenv ("DISPLAY"))
1025         g_debug ("Using Xephyr for X servers");
1026
1027     display_manager = display_manager_new ();
1028     g_signal_connect (display_manager, "stopped", G_CALLBACK (display_manager_stopped_cb), NULL);
1029
1030     /* Load the static display entries */
1031     groups = config_get_groups (config_get_instance ());
1032     for (i = groups; *i; i++)
1033     {
1034         gchar *config_section = *i;
1035         gchar *type;
1036         Seat *seat;
1037
1038         if (!g_str_has_prefix (config_section, "Seat:"))
1039             continue;
1040
1041         g_debug ("Loading seat %s", config_section);
1042         type = config_get_string (config_get_instance (), config_section, "type");
1043         if (!type)
1044             type = config_get_string (config_get_instance (), "SeatDefaults", "type");
1045         seat = seat_new (type);
1046         g_free (type);
1047         if (seat)
1048         {
1049             set_seat_properties (seat, config_section);
1050             display_manager_add_seat (display_manager, seat);
1051             n_seats++;
1052         }
1053         else
1054             g_warning ("Failed to create seat %s", config_section);
1055     }
1056     g_strfreev (groups);
1057
1058     /* If no seats start a default one */
1059     if (n_seats == 0 && config_get_boolean (config_get_instance (), "LightDM", "start-default-seat"))
1060     {
1061         gchar *type;
1062         Seat *seat;
1063
1064         g_debug ("Adding default seat");
1065
1066         type = config_get_string (config_get_instance (), "SeatDefaults", "type");
1067         seat = seat_new (type);
1068         g_free (type);
1069         if (seat)
1070         {
1071             set_seat_properties (seat, NULL);
1072             seat_set_property (seat, "exit-on-failure", "true");
1073             display_manager_add_seat (display_manager, seat);
1074         }
1075         else
1076             g_warning ("Failed to create default seat");
1077     }
1078
1079     display_manager_start (display_manager);
1080
1081     /* Start the XDMCP server */
1082     if (config_get_boolean (config_get_instance (), "XDMCPServer", "enabled"))
1083     {
1084         gchar *key_name, *key = NULL;
1085
1086         xdmcp_server = xdmcp_server_new ();
1087         if (config_has_key (config_get_instance (), "XDMCPServer", "port"))
1088         {
1089             gint port;
1090             port = config_get_integer (config_get_instance (), "XDMCPServer", "port");
1091             if (port > 0)
1092                 xdmcp_server_set_port (xdmcp_server, port);
1093         }
1094         g_signal_connect (xdmcp_server, "new-session", G_CALLBACK (xdmcp_session_cb), NULL);
1095
1096         key_name = config_get_string (config_get_instance (), "XDMCPServer", "key");
1097         if (key_name)
1098         {
1099             gchar *dir, *path;
1100             GKeyFile *keys;
1101             gboolean result;
1102             GError *error = NULL;
1103
1104             dir = config_get_string (config_get_instance (), "LightDM", "config-directory");
1105             path = g_build_filename (dir, "keys.conf", NULL);
1106             g_free (dir);
1107
1108             keys = g_key_file_new ();
1109             result = g_key_file_load_from_file (keys, path, G_KEY_FILE_NONE, &error);
1110             if (error)
1111                 g_debug ("Error getting key %s", error->message);
1112             g_clear_error (&error);
1113
1114             if (result)
1115             {
1116                 if (g_key_file_has_key (keys, "keyring", key_name, NULL))
1117                     key = g_key_file_get_string (keys, "keyring", key_name, NULL);
1118                 else
1119                     g_debug ("Key %s not defined", key_name);
1120             }
1121             g_free (path);
1122             g_key_file_free (keys);
1123         }
1124         if (key)
1125             xdmcp_server_set_key (xdmcp_server, key);
1126         g_free (key_name);
1127         g_free (key);
1128
1129         g_debug ("Starting XDMCP server on UDP/IP port %d", xdmcp_server_get_port (xdmcp_server));
1130         xdmcp_server_start (xdmcp_server);
1131     }
1132
1133     /* Start the VNC server */
1134     if (config_get_boolean (config_get_instance (), "VNCServer", "enabled"))
1135     {
1136         gchar *path;
1137
1138         path = g_find_program_in_path ("Xvnc");
1139         if (path)
1140         {
1141             vnc_server = vnc_server_new ();
1142             if (config_has_key (config_get_instance (), "VNCServer", "port"))
1143             {
1144                 gint port;
1145                 port = config_get_integer (config_get_instance (), "VNCServer", "port");
1146                 if (port > 0)
1147                     vnc_server_set_port (vnc_server, port);
1148             }
1149             g_signal_connect (vnc_server, "new-connection", G_CALLBACK (vnc_connection_cb), NULL);
1150
1151             g_debug ("Starting VNC server on TCP/IP port %d", vnc_server_get_port (vnc_server));
1152             vnc_server_start (vnc_server);
1153
1154             g_free (path);
1155         }
1156         else
1157             g_warning ("Can't start VNC server, Xvn is not in the path");
1158     }
1159
1160     g_main_loop_run (loop);
1161
1162     return EXIT_SUCCESS;
1163 }