]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display-manager.c
41453eb93ac528694293929126ad677f64dc4a98
[sojka/lightdm.git] / src / display-manager.c
1 /*
2  * Copyright (C) 2010 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 <sys/stat.h>
17 #include <dbus/dbus-glib.h>
18 #include <xcb/xcb.h>
19 #include <pwd.h>
20
21 #include "display-manager.h"
22 #include "display-manager-glue.h"
23 #include "xdmcp-server.h"
24 #include "xserver.h"
25 #include "theme.h"
26
27 enum {
28     DISPLAY_ADDED,
29     LAST_SIGNAL
30 };
31 static guint signals[LAST_SIGNAL] = { 0 };
32
33 struct DisplayManagerPrivate
34 {
35     /* Configuration */
36     GKeyFile *config;
37
38     /* Directory to store authorization files */
39     gchar *auth_dir;
40
41     /* Counter to generate unique authorization file names */
42     guint auth_counter;
43
44     /* Directory to write log files to */
45     gchar *log_dir;
46
47     /* TRUE if running in test mode (i.e. as non-root for testing) */
48     gboolean test_mode;
49
50     /* The displays being managed */
51     GList *displays;
52
53     /* XDMCP server */
54     XDMCPServer *xdmcp_server;
55 };
56
57 G_DEFINE_TYPE (DisplayManager, display_manager, G_TYPE_OBJECT);
58
59 DisplayManager *
60 display_manager_new (GKeyFile *config)
61 {
62     DisplayManager *self = g_object_new (DISPLAY_MANAGER_TYPE, NULL);
63
64     self->priv->config = config;
65     self->priv->test_mode = g_key_file_get_boolean (self->priv->config, "LightDM", "test-mode", NULL);
66     self->priv->auth_dir = g_key_file_get_string (self->priv->config, "LightDM", "authorization-directory", NULL);
67     if (!self->priv->auth_dir)
68         self->priv->auth_dir = g_strdup (XAUTH_DIR);
69     self->priv->log_dir = g_key_file_get_string (self->priv->config, "LightDM", "log-directory", NULL);
70     if (!self->priv->log_dir)
71         self->priv->log_dir = g_strdup (LOG_DIR);
72
73     return self;
74 }
75
76 static gboolean
77 display_number_used (DisplayManager *manager, guint display_number)
78 {
79     GList *link;
80
81     for (link = manager->priv->displays; link; link = link->next)
82     {
83         Display *display = link->data;      
84         XServer *xserver = display_get_xserver (display);
85         if (xserver && xserver_get_hostname (xserver) == NULL && xserver_get_display_number (xserver) == display_number)
86             return TRUE;
87     }
88
89     /* In test mode there is probably another display manager running so see if the server exists */
90     if (manager->priv->test_mode)
91     {
92         xcb_connection_t *connection;
93         gchar *address;
94         gboolean is_used;
95
96         address = g_strdup_printf (":%d", display_number);
97         connection = xcb_connect_to_display_with_auth_info (address, NULL, NULL);
98         g_free (address);
99         is_used = !xcb_connection_has_error (connection);
100         xcb_disconnect (connection);
101
102         return is_used;
103     }
104
105     return FALSE;
106 }
107
108 static guint
109 get_free_display_number (DisplayManager *manager)
110 {
111     guint display_number = 0;
112
113     while (display_number_used (manager, display_number))
114         display_number++;
115   
116     return display_number;
117 }
118
119 static gchar *
120 get_authorization_path (DisplayManager *manager)
121 {
122     gchar *path;
123
124     path = g_strdup_printf ("%s/%d", manager->priv->auth_dir, manager->priv->auth_counter);
125     manager->priv->auth_counter++;
126
127     return path;
128 }
129
130 static void
131 start_session (Display *display, Session *session, gboolean is_greeter, DisplayManager *manager)
132 {
133     gchar *string;
134     XAuthorization *authorization;
135
136     /* Connect using the session bus */
137     if (manager->priv->test_mode)
138     {
139         session_set_env (session, "DBUS_SESSION_BUS_ADDRESS", getenv ("DBUS_SESSION_BUS_ADDRESS"));
140         session_set_env (session, "XDG_SESSION_COOKIE", getenv ("XDG_SESSION_COOKIE"));
141         session_set_env (session, "LDM_BUS", "SESSION");
142     }
143
144     /* Address for greeter to connect to */
145     string = g_strdup_printf ("/org/lightdm/LightDisplayManager/Display%d", display_get_index (display));
146     session_set_env (session, "LDM_DISPLAY", string);
147     g_free (string);
148
149     authorization = xserver_get_authorization (display_get_xserver (display));
150     if (authorization)
151     {
152         gchar *path;
153
154         path = get_authorization_path (manager);
155         session_set_authorization (session, authorization, path);
156         g_free (path);
157     }
158
159     if (is_greeter)
160     {
161         gchar *filename;
162         filename = g_strdup_printf ("%s-greeter.log", xserver_get_address (display_get_xserver (display)));
163         string = g_build_filename (manager->priv->log_dir, filename, NULL);
164         g_free (filename);
165     }
166     else
167     {
168         // FIXME: Copy old error file
169         if (manager->priv->test_mode)
170             string = g_strdup (".xsession-errors");
171         else
172         {
173             struct passwd *user_info = getpwnam (session_get_username (session));
174             if (user_info)
175                 string = g_build_filename (user_info->pw_dir, ".xsession-errors", NULL);
176             else
177                 g_warning ("Failed to get user info for user '%s'", session_get_username (session));
178         }
179     }
180     g_debug ("Logging to %s", string);
181     session_set_log_file (session, string);
182     g_free (string);
183 }
184
185 static void
186 start_greeter_cb (Display *display, Session *session, DisplayManager *manager)
187 {
188     start_session (display, session, TRUE, manager);
189 }
190
191 static void
192 start_session_cb (Display *display, Session *session, DisplayManager *manager)
193 {
194     start_session (display, session, FALSE, manager);
195 }
196
197 static void
198 end_session_cb (Display *display, Session *session, DisplayManager *manager)
199 {
200     XServer *xserver;
201
202     /* Change authorization for next session */
203     xserver = display_get_xserver (display);
204     if (xserver_get_server_type (xserver) == XSERVER_TYPE_LOCAL)
205     {
206         XAuthorization *authorization;
207
208         g_debug ("Generating new authorization cookie for %s", xserver_get_address (xserver));
209         authorization = xauth_new_cookie ();
210         xserver_set_authorization (xserver, authorization, NULL);
211         g_object_unref (authorization);
212     }
213 }
214
215 static guchar
216 atox (char c)
217 {
218     if (c >= '0' && c <= '9')
219         return c - '0';
220     if (c >= 'a' && c <= 'f')
221         return c - 'a' + 10;
222     if (c >= 'A' && c <= 'F')
223         return c - 'A' + 10;
224     return 0;
225 }
226
227 static void
228 string_to_xdm_auth_key (const gchar *key, guchar *data)
229 {
230     gint i;
231
232     memset (data, 0, sizeof (data));
233     if (strncmp (key, "0x", 2) == 0 || strncmp (key, "0X", 2) == 0)
234     {
235         for (i = 0; i < 8; i++)
236         {
237             if (key[i*2] == '\0')
238                 break;
239             data[i] |= atox (key[i*2]) << 8;
240             if (key[i*2+1] == '\0')
241                 break;
242             data[i] |= atox (key[i*2+1]);
243         }
244     }
245     else
246     {
247         for (i = 1; i < 8 && key[i-1]; i++)
248            data[i] = key[i-1];
249     }
250 }
251
252 static XServer *
253 make_xserver (DisplayManager *manager, gchar *config_section)
254 {
255     gint display_number;
256     XServer *xserver;
257     XAuthorization *authorization = NULL;
258     gchar *xdmcp_manager, *filename, *path, *xserver_command;
259
260     if (config_section && g_key_file_has_key (manager->priv->config, config_section, "display-number", NULL))
261         display_number = g_key_file_get_integer (manager->priv->config, config_section, "display-number", NULL);
262     else
263         display_number = get_free_display_number (manager);
264
265     xdmcp_manager = config_section ? g_key_file_get_string (manager->priv->config, config_section, "xdmcp-manager", NULL) : NULL;
266     if (xdmcp_manager)
267     {
268         gint port;
269         gchar *key;
270
271         xserver = xserver_new (XSERVER_TYPE_LOCAL_TERMINAL, xdmcp_manager, display_number);
272
273         port = g_key_file_get_integer (manager->priv->config, config_section, "xdmcp-port", NULL);
274         if (port > 0)
275             xserver_set_port (xserver, port);
276         key = g_key_file_get_string (manager->priv->config, config_section, "key", NULL);
277         if (key)
278         {
279             guchar data[8];
280
281             string_to_xdm_auth_key (key, data);
282             xserver_set_authentication (xserver, "XDM-AUTHENTICATION-1", data, 8);
283             authorization = xauth_new ("XDM-AUTHORIZATION-1", data, 8);
284         }
285     }
286     else
287     {
288         xserver = xserver_new (XSERVER_TYPE_LOCAL, NULL, display_number);
289         authorization = xauth_new_cookie ();
290     }
291     g_free (xdmcp_manager);
292
293     path = get_authorization_path (manager);
294     xserver_set_authorization (xserver, authorization, path);
295     g_object_unref (authorization);
296     g_free (path);
297
298     filename = g_strdup_printf ("%s.log", xserver_get_address (xserver));
299     path = g_build_filename (manager->priv->log_dir, filename, NULL);
300     g_debug ("Logging to %s", path);
301     xserver_set_log_file (xserver, path);
302     g_free (filename);
303     g_free (path);
304
305     /* Allow X server to be Xephyr */
306     if (getenv ("DISPLAY"))
307         xserver_set_env (xserver, "DISPLAY", getenv ("DISPLAY"));
308     if (getenv ("XAUTHORITY"))
309         xserver_set_env (xserver, "XAUTHORITY", getenv ("XAUTHORITY"));
310     xserver_command = g_key_file_get_string (manager->priv->config, "LightDM", "xserver", NULL);
311     if (xserver_command)
312         xserver_set_command (xserver, xserver_command);
313     g_free (xserver_command);
314
315     if (manager->priv->test_mode)
316         xserver_set_command (xserver, "Xephyr");
317   
318     return xserver;
319 }
320
321 static Display *
322 add_display (DisplayManager *manager)
323 {
324     Display *display;
325     gchar *value;
326
327     display = display_new (g_list_length (manager->priv->displays));
328     g_signal_connect (display, "start-greeter", G_CALLBACK (start_greeter_cb), manager);
329     g_signal_connect (display, "start-session", G_CALLBACK (start_session_cb), manager);
330     g_signal_connect (display, "end-session", G_CALLBACK (end_session_cb), manager);
331
332     if (manager->priv->test_mode)
333         display_set_greeter_user (display, NULL);
334
335     value = g_key_file_get_string (manager->priv->config, "Greeter", "language", NULL);
336     if (value)
337         display_set_default_language (display, value);
338     g_free (value);
339     value = g_key_file_get_string (manager->priv->config, "Greeter", "layout", NULL);
340     if (value)
341         display_set_default_layout (display, value);
342     g_free (value);
343     value = g_key_file_get_string (manager->priv->config, "Greeter", "session", NULL);
344     if (value)
345         display_set_default_session (display, value);
346     g_free (value);
347     value = g_key_file_get_string (manager->priv->config, "Greeter", "user", NULL);
348     if (value)
349         display_set_greeter_user (display, value);
350     g_free (value);
351     value = g_key_file_get_string (manager->priv->config, "Greeter", "theme", NULL);
352     if (value)
353         display_set_greeter_theme (display, value);
354     g_free (value);
355
356     manager->priv->displays = g_list_append (manager->priv->displays, display);
357
358     g_signal_emit (manager, signals[DISPLAY_ADDED], 0, display);
359
360     return display;
361 }
362
363 gboolean
364 display_manager_add_display (DisplayManager *manager, GError *error)
365 {
366     Display *display;
367     XServer *xserver;
368
369     g_debug ("Starting new display");
370     display = add_display (manager);
371     xserver = make_xserver (manager, NULL);
372     display_set_xserver (display, xserver);
373     display_start (display);
374     g_object_unref (xserver);
375
376     return TRUE;
377 }
378
379 gboolean
380 display_manager_switch_to_user (DisplayManager *manager, char *username, GError *error)
381 {
382     GList *link;
383     Display *display;
384     XServer *xserver;
385
386     for (link = manager->priv->displays; link; link = link->next)
387     {
388         display = link->data;
389         const gchar *session_user;
390
391         session_user = display_get_session_user (display);
392         if (session_user && strcmp (session_user, username) == 0)
393         {
394             g_debug ("Switching to user %s session on display %s", username, xserver_get_address (display_get_xserver (display)));
395             //display_focus (display);
396             return TRUE;
397         }
398     }
399
400     g_debug ("Starting new display for user %s", username);
401     display = add_display (manager);
402     xserver = make_xserver (manager, NULL);
403     display_set_xserver (display, xserver);
404     display_start (display);
405     g_object_unref (xserver);
406
407     return TRUE;
408 }
409
410 static gboolean
411 xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session, DisplayManager *manager)
412 {
413     Display *display;
414     gchar *address;
415     XServer *xserver;
416     gboolean result;
417   
418     // FIXME: Try IPv6 then fallback to IPv4
419
420     display = add_display (manager);
421     address = g_inet_address_to_string (G_INET_ADDRESS (xdmcp_session_get_address (session)));
422     xserver = xserver_new (XSERVER_TYPE_REMOTE, address, xdmcp_session_get_display_number (session));
423     if (strcmp (xdmcp_session_get_authorization_name (session), "") != 0)
424     {
425         XAuthorization *authorization = NULL;
426         gchar *path;
427
428         authorization = xauth_new (xdmcp_session_get_authorization_name (session),
429                                    xdmcp_session_get_authorization_data (session),
430                                    xdmcp_session_get_authorization_data_length (session));
431         path = get_authorization_path (manager);
432
433         xserver_set_authorization (xserver, authorization, path);
434
435         g_object_unref (authorization);
436         g_free (path);
437     }
438
439     display_set_xserver (display, xserver);
440     result = display_start (display);
441     g_object_unref (xserver);
442     g_free (address);
443     if (!result)
444        g_object_unref (display);
445
446     return result;
447 }
448
449 static void
450 setup_auth_dir (DisplayManager *manager)
451 {
452     GDir *dir;
453     GError *error = NULL;
454
455     g_mkdir_with_parents (manager->priv->auth_dir, S_IRWXU | S_IXGRP | S_IXOTH);
456     dir = g_dir_open (manager->priv->auth_dir, 0, &error);
457     if (!dir)
458     {
459         g_warning ("Authorization dir not created: %s", error->message);
460         g_clear_error (&error);
461         return;
462     }
463
464     /* Clear out the directory */
465     while (TRUE)
466     {
467         const gchar *filename;
468         gchar *path;
469         GFile *file;
470
471         filename = g_dir_read_name (dir);
472         if (!filename)
473             break;
474
475         path = g_build_filename (manager->priv->auth_dir, filename, NULL);
476         file = g_file_new_for_path (filename);
477         g_file_delete (file, NULL, NULL);
478
479         g_free (path);
480         g_object_unref (file);
481     }
482
483     g_dir_close (dir);
484 }
485
486 void
487 display_manager_start (DisplayManager *manager)
488 {
489     gchar *displays;
490     gchar **tokens, **i;
491
492     /* Make an empty authorization directory */
493     setup_auth_dir (manager);
494   
495     /* Start the first display */
496     displays = g_key_file_get_string (manager->priv->config, "LightDM", "displays", NULL);
497     if (!displays)
498         displays = g_strdup ("");
499     tokens = g_strsplit (displays, " ", -1);
500     g_free (displays);
501
502     for (i = tokens; *i; i++)
503     {
504         Display *display;
505         gchar *default_user, *display_name;
506         gint user_timeout;
507         XServer *xserver;
508
509         display_name = *i;
510         g_debug ("Loading display %s", display_name);
511
512         display = add_display (manager);
513
514         /* Automatically log in or start a greeter session */
515         default_user = g_key_file_get_string (manager->priv->config, display_name, "default-user", NULL);
516         user_timeout = g_key_file_get_integer (manager->priv->config, display_name, "default-user-timeout", NULL);
517         if (user_timeout < 0)
518             user_timeout = 0;
519
520         if (default_user)
521         {
522             display_set_default_user (display, default_user);
523             display_set_default_user_timeout (display, user_timeout);
524             if (user_timeout == 0)
525                 g_debug ("Starting session for user %s", default_user);
526             else
527                 g_debug ("Starting session for user %s in %d seconds", default_user, user_timeout);
528         }
529
530         xserver = make_xserver (manager, display_name);
531
532         display_set_xserver (display, xserver);
533         display_start (display);
534         g_object_unref (xserver);
535         g_free (default_user);
536     }
537     g_strfreev (tokens);
538
539     if (g_key_file_get_boolean (manager->priv->config, "xdmcp", "enabled", NULL))
540     {
541         gchar *key;
542
543         manager->priv->xdmcp_server = xdmcp_server_new ();
544         if (g_key_file_has_key (manager->priv->config, "xdmcp", "port", NULL))
545         {
546             gint port;
547             port = g_key_file_get_integer (manager->priv->config, "xdmcp", "port", NULL);
548             if (port > 0)
549                 xdmcp_server_set_port (manager->priv->xdmcp_server, port);
550         }
551         g_signal_connect (manager->priv->xdmcp_server, "new-session", G_CALLBACK (xdmcp_session_cb), manager);
552
553         key = g_key_file_get_string (manager->priv->config, "xdmcp", "key", NULL);
554         if (key)
555         {
556             guchar data[8];
557             string_to_xdm_auth_key (key, data);
558             xdmcp_server_set_authentication (manager->priv->xdmcp_server, "XDM-AUTHENTICATION-1", data, 8);
559             xdmcp_server_set_authorization (manager->priv->xdmcp_server, "XDM-AUTHORIZATION-1", data, 8);
560         }
561         g_free (key);
562
563         g_debug ("Starting XDMCP server on UDP/IP port %d", xdmcp_server_get_port (manager->priv->xdmcp_server));
564         xdmcp_server_start (manager->priv->xdmcp_server); 
565     }
566 }
567
568 static void
569 display_manager_init (DisplayManager *manager)
570 {
571     manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, DISPLAY_MANAGER_TYPE, DisplayManagerPrivate);
572 }
573
574 static void
575 display_manager_finalize (GObject *object)
576 {
577     DisplayManager *self;
578     GList *link;
579
580     self = DISPLAY_MANAGER (object);
581
582     if (self->priv->xdmcp_server)
583         g_object_unref (self->priv->xdmcp_server);
584     for (link = self->priv->displays; link; link = link->next)
585         g_object_unref (link->data);
586     g_list_free (self->priv->displays);
587 }
588
589 static void
590 display_manager_class_init (DisplayManagerClass *klass)
591 {
592     GObjectClass *object_class = G_OBJECT_CLASS (klass);
593
594     object_class->finalize = display_manager_finalize;
595
596     g_type_class_add_private (klass, sizeof (DisplayManagerPrivate));
597
598     signals[DISPLAY_ADDED] =
599         g_signal_new ("display-added",
600                       G_TYPE_FROM_CLASS (klass),
601                       G_SIGNAL_RUN_LAST,
602                       G_STRUCT_OFFSET (DisplayManagerClass, display_added),
603                       NULL, NULL,
604                       g_cclosure_marshal_VOID__OBJECT,
605                       G_TYPE_NONE, 1, DISPLAY_TYPE);
606
607     dbus_g_object_type_install_info (DISPLAY_MANAGER_TYPE, &dbus_glib_display_manager_object_info);
608 }