]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/display-manager.c
Change language/layout/session when user selected in GTK+ greeter
[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
326     display = display_new (g_list_length (manager->priv->displays));
327     g_signal_connect (display, "start-greeter", G_CALLBACK (start_greeter_cb), manager);
328     g_signal_connect (display, "start-session", G_CALLBACK (start_session_cb), manager);
329     g_signal_connect (display, "end-session", G_CALLBACK (end_session_cb), manager);
330
331     if (manager->priv->test_mode)
332         display_set_greeter_user (display, NULL);
333
334     manager->priv->displays = g_list_append (manager->priv->displays, display);
335
336     g_signal_emit (manager, signals[DISPLAY_ADDED], 0, display);
337
338     return display;
339 }
340
341 gboolean
342 display_manager_add_display (DisplayManager *manager, GError *error)
343 {
344     Display *display;
345     XServer *xserver;
346
347     g_debug ("Starting new display");
348     display = add_display (manager);
349     xserver = make_xserver (manager, NULL);
350     display_set_xserver (display, xserver);
351     display_start (display);
352     g_object_unref (xserver);
353
354     return TRUE;
355 }
356
357 gboolean
358 display_manager_switch_to_user (DisplayManager *manager, char *username, GError *error)
359 {
360     GList *link;
361     Display *display;
362     XServer *xserver;
363
364     for (link = manager->priv->displays; link; link = link->next)
365     {
366         display = link->data;
367         const gchar *session_user;
368
369         session_user = display_get_session_user (display);
370         if (session_user && strcmp (session_user, username) == 0)
371         {
372             g_debug ("Switching to user %s session on display %s", username, xserver_get_address (display_get_xserver (display)));
373             //display_focus (display);
374             return TRUE;
375         }
376     }
377
378     g_debug ("Starting new display for user %s", username);
379     display = add_display (manager);
380     xserver = make_xserver (manager, NULL);
381     display_set_xserver (display, xserver);
382     display_start (display);
383     g_object_unref (xserver);
384
385     return TRUE;
386 }
387
388 static gboolean
389 xdmcp_session_cb (XDMCPServer *server, XDMCPSession *session, DisplayManager *manager)
390 {
391     Display *display;
392     gchar *address;
393     XServer *xserver;
394     gboolean result;
395   
396     // FIXME: Try IPv6 then fallback to IPv4
397
398     display = add_display (manager);
399     address = g_inet_address_to_string (G_INET_ADDRESS (xdmcp_session_get_address (session)));
400     xserver = xserver_new (XSERVER_TYPE_REMOTE, address, xdmcp_session_get_display_number (session));
401     if (strcmp (xdmcp_session_get_authorization_name (session), "") != 0)
402     {
403         XAuthorization *authorization = NULL;
404         gchar *path;
405
406         authorization = xauth_new (xdmcp_session_get_authorization_name (session),
407                                    xdmcp_session_get_authorization_data (session),
408                                    xdmcp_session_get_authorization_data_length (session));
409         path = get_authorization_path (manager);
410
411         xserver_set_authorization (xserver, authorization, path);
412
413         g_object_unref (authorization);
414         g_free (path);
415     }
416
417     display_set_xserver (display, xserver);
418     result = display_start (display);
419     g_object_unref (xserver);
420     g_free (address);
421     if (!result)
422        g_object_unref (display);
423
424     return result;
425 }
426
427 static void
428 setup_auth_dir (DisplayManager *manager)
429 {
430     GDir *dir;
431     GError *error = NULL;
432
433     g_mkdir_with_parents (manager->priv->auth_dir, S_IRWXU | S_IXGRP | S_IXOTH);
434     dir = g_dir_open (manager->priv->auth_dir, 0, &error);
435     if (!dir)
436     {
437         g_warning ("Authorization dir not created: %s", error->message);
438         g_clear_error (&error);
439         return;
440     }
441
442     /* Clear out the directory */
443     while (TRUE)
444     {
445         const gchar *filename;
446         gchar *path;
447         GFile *file;
448
449         filename = g_dir_read_name (dir);
450         if (!filename)
451             break;
452
453         path = g_build_filename (manager->priv->auth_dir, filename, NULL);
454         file = g_file_new_for_path (filename);
455         g_file_delete (file, NULL, NULL);
456
457         g_free (path);
458         g_object_unref (file);
459     }
460
461     g_dir_close (dir);
462 }
463
464 void
465 display_manager_start (DisplayManager *manager)
466 {
467     gchar *displays;
468     gchar **tokens, **i;
469
470     /* Make an empty authorization directory */
471     setup_auth_dir (manager);
472   
473     /* Start the first display */
474     displays = g_key_file_get_string (manager->priv->config, "LightDM", "displays", NULL);
475     if (!displays)
476         displays = g_strdup ("");
477     tokens = g_strsplit (displays, " ", -1);
478     g_free (displays);
479
480     for (i = tokens; *i; i++)
481     {
482         Display *display;
483         gchar *value, *default_user, *display_name;
484         gint user_timeout;
485         XServer *xserver;
486
487         display_name = *i;
488         g_debug ("Loading display %s", display_name);
489
490         display = add_display (manager);
491
492         value = g_key_file_get_string (manager->priv->config, display_name, "language", NULL);
493         if (value)
494             display_set_default_language (display, value);
495         g_free (value);
496         value = g_key_file_get_string (manager->priv->config, display_name, "layout", NULL);
497         if (value)
498             display_set_default_layout (display, value);
499         g_free (value);
500         value = g_key_file_get_string (manager->priv->config, display_name, "session", NULL);
501         if (value)
502             display_set_default_session (display, value);
503         g_free (value);
504         value = g_key_file_get_string (manager->priv->config, display_name, "user", NULL);
505         if (value)
506             display_set_greeter_user (display, value);
507         g_free (value);
508         value = g_key_file_get_string (manager->priv->config, display_name, "theme", NULL);
509         if (value)
510             display_set_greeter_theme (display, value);
511         g_free (value);
512       
513         /* Automatically log in or start a greeter session */
514         default_user = g_key_file_get_string (manager->priv->config, display_name, "default-user", NULL);
515         user_timeout = g_key_file_get_integer (manager->priv->config, display_name, "default-user-timeout", NULL);
516         if (user_timeout < 0)
517             user_timeout = 0;
518
519         if (default_user)
520         {
521             display_set_default_user (display, default_user);
522             display_set_default_user_timeout (display, user_timeout);
523             if (user_timeout == 0)
524                 g_debug ("Starting session for user %s", default_user);
525             else
526                 g_debug ("Starting session for user %s in %d seconds", default_user, user_timeout);
527         }
528
529         xserver = make_xserver (manager, display_name);
530
531         display_set_xserver (display, xserver);
532         display_start (display);
533         g_object_unref (xserver);
534         g_free (default_user);
535     }
536     g_strfreev (tokens);
537
538     if (g_key_file_get_boolean (manager->priv->config, "xdmcp", "enabled", NULL))
539     {
540         gchar *key;
541
542         manager->priv->xdmcp_server = xdmcp_server_new ();
543         if (g_key_file_has_key (manager->priv->config, "xdmcp", "port", NULL))
544         {
545             gint port;
546             port = g_key_file_get_integer (manager->priv->config, "xdmcp", "port", NULL);
547             if (port > 0)
548                 xdmcp_server_set_port (manager->priv->xdmcp_server, port);
549         }
550         g_signal_connect (manager->priv->xdmcp_server, "new-session", G_CALLBACK (xdmcp_session_cb), manager);
551
552         key = g_key_file_get_string (manager->priv->config, "xdmcp", "key", NULL);
553         if (key)
554         {
555             guchar data[8];
556             string_to_xdm_auth_key (key, data);
557             xdmcp_server_set_authentication (manager->priv->xdmcp_server, "XDM-AUTHENTICATION-1", data, 8);
558             xdmcp_server_set_authorization (manager->priv->xdmcp_server, "XDM-AUTHORIZATION-1", data, 8);
559         }
560         g_free (key);
561
562         g_debug ("Starting XDMCP server on UDP/IP port %d", xdmcp_server_get_port (manager->priv->xdmcp_server));
563         xdmcp_server_start (manager->priv->xdmcp_server); 
564     }
565 }
566
567 static void
568 display_manager_init (DisplayManager *manager)
569 {
570     manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, DISPLAY_MANAGER_TYPE, DisplayManagerPrivate);
571 }
572
573 static void
574 display_manager_finalize (GObject *object)
575 {
576     DisplayManager *self;
577     GList *link;
578
579     self = DISPLAY_MANAGER (object);
580
581     if (self->priv->xdmcp_server)
582         g_object_unref (self->priv->xdmcp_server);
583     for (link = self->priv->displays; link; link = link->next)
584         g_object_unref (link->data);
585     g_list_free (self->priv->displays);
586 }
587
588 static void
589 display_manager_class_init (DisplayManagerClass *klass)
590 {
591     GObjectClass *object_class = G_OBJECT_CLASS (klass);
592
593     object_class->finalize = display_manager_finalize;
594
595     g_type_class_add_private (klass, sizeof (DisplayManagerPrivate));
596
597     signals[DISPLAY_ADDED] =
598         g_signal_new ("display-added",
599                       G_TYPE_FROM_CLASS (klass),
600                       G_SIGNAL_RUN_LAST,
601                       G_STRUCT_OFFSET (DisplayManagerClass, display_added),
602                       NULL, NULL,
603                       g_cclosure_marshal_VOID__OBJECT,
604                       G_TYPE_NONE, 1, DISPLAY_TYPE);
605
606     dbus_g_object_type_install_info (DISPLAY_MANAGER_TYPE, &dbus_glib_display_manager_object_info);
607 }