]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
* Generate login and logout events that can be used by the Linux Audit tools
[sojka/lightdm.git] / src / x-server-local.c
1 /*
2  * Copyright (C) 2010-2011 Robert Ancell.
3  * Author: Robert Ancell <robert.ancell@canonical.com>
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 3 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11
12 #include <config.h>
13 #include <string.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/stat.h>
17 #include <errno.h>
18 #include <glib/gstdio.h>
19 #include <stdlib.h>
20
21 #include "x-server-local.h"
22 #include "configuration.h"
23 #include "process.h"
24 #include "vt.h"
25
26 struct XServerLocalPrivate
27 {
28     /* X server process */
29     Process *x_server_process;
30
31     /* Command to run the X server */
32     gchar *command;
33
34     /* Config file to use */
35     gchar *config_file;
36
37     /* Server layout to use */
38     gchar *layout;
39
40     /* Value for -seat argument */
41     gchar *xdg_seat;
42
43     /* TRUE if TCP/IP connections are allowed */
44     gboolean allow_tcp;
45
46     /* Authority file */
47     gchar *authority_file;
48
49     /* XDMCP server to connect to */
50     gchar *xdmcp_server;
51
52     /* XDMCP port to connect to */
53     guint xdmcp_port;
54
55     /* XDMCP key to use */
56     gchar *xdmcp_key;
57
58     /* ID to report to Mir */
59     gchar *mir_id;
60
61     /* Filename of socket Mir is listening on */
62     gchar *mir_socket;
63
64     /* TRUE when received ready signal */
65     gboolean got_signal;
66
67     /* VT to run on */
68     gint vt;
69     gboolean have_vt_ref;
70
71     /* Background to set */
72     gchar *background;
73 };
74
75 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
76
77 static GList *display_numbers = NULL;
78
79 static gboolean
80 display_number_in_use (guint display_number)
81 {
82     GList *link;
83     gchar *path;
84     gboolean in_use;
85     gchar *data;
86
87     /* See if we know we are managing a server with that number */
88     for (link = display_numbers; link; link = link->next)
89     {
90         guint number = GPOINTER_TO_UINT (link->data);
91         if (number == display_number)
92             return TRUE;
93     }
94
95     /* See if an X server that we don't know of has a lock on that number */
96     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
97     in_use = g_file_test (path, G_FILE_TEST_EXISTS);
98
99     /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
100     if (in_use && g_file_get_contents (path, &data, NULL, NULL))
101     {
102         int pid;
103
104         pid = atoi (g_strstrip (data));
105         g_free (data);
106
107         errno = 0;
108         if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
109             in_use = FALSE;
110     }
111
112     g_free (path);
113
114     return in_use;
115 }
116
117 guint
118 x_server_local_get_unused_display_number (void)
119 {
120     guint number;
121
122     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
123     while (display_number_in_use (number))
124         number++;
125
126     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
127
128     return number;
129 }
130
131 void
132 x_server_local_release_display_number (guint display_number)
133 {
134     GList *link;
135     for (link = display_numbers; link; link = link->next)
136     {
137         guint number = GPOINTER_TO_UINT (link->data);
138         if (number == display_number)
139         {
140             display_numbers = g_list_delete_link (display_numbers, link);
141             return;
142         }
143     }
144 }
145
146 XServerLocal *
147 x_server_local_new (void)
148 {
149     XServerLocal *self;
150     gchar hostname[1024], *number, *name;
151     XAuthority *cookie;
152
153     self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
154
155     x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
156
157     gethostname (hostname, 1024);
158     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (self)));
159     cookie = x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
160     x_server_set_authority (X_SERVER (self), cookie);
161     g_free (number);
162     g_object_unref (cookie);
163
164     name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
165     display_server_set_name (DISPLAY_SERVER (self), name);
166     g_free (name);
167
168     return self;
169 }
170
171 void
172 x_server_local_set_command (XServerLocal *server, const gchar *command)
173 {
174     g_return_if_fail (server != NULL);
175     g_free (server->priv->command);
176     server->priv->command = g_strdup (command);
177 }
178
179 void
180 x_server_local_set_vt (XServerLocal *server, gint vt)
181 {
182     g_return_if_fail (server != NULL);
183     if (server->priv->have_vt_ref)
184         vt_unref (server->priv->vt);
185     server->priv->have_vt_ref = FALSE;
186     server->priv->vt = vt;
187     if (vt > 0)
188     {
189         vt_ref (vt);
190         server->priv->have_vt_ref = TRUE;
191     }
192 }
193
194 void
195 x_server_local_set_config (XServerLocal *server, const gchar *path)
196 {
197     g_return_if_fail (server != NULL);
198     g_free (server->priv->config_file);
199     server->priv->config_file = g_strdup (path);
200 }
201
202 void
203 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
204 {
205     g_return_if_fail (server != NULL);
206     g_free (server->priv->layout);
207     server->priv->layout = g_strdup (layout);
208 }
209
210 void
211 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
212 {
213     g_return_if_fail (server != NULL);
214     g_free (server->priv->xdg_seat);
215     server->priv->xdg_seat = g_strdup (xdg_seat);
216 }
217
218 void
219 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
220 {
221     g_return_if_fail (server != NULL);
222     server->priv->allow_tcp = allow_tcp;
223 }
224
225 void
226 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
227 {
228     g_return_if_fail (server != NULL);
229     g_free (server->priv->xdmcp_server);
230     server->priv->xdmcp_server = g_strdup (hostname);
231 }
232
233 const gchar *
234 x_server_local_get_xdmcp_server (XServerLocal *server)
235 {
236     g_return_val_if_fail (server != NULL, 0);
237     return server->priv->xdmcp_server;
238 }
239
240 void
241 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
242 {
243     g_return_if_fail (server != NULL);
244     server->priv->xdmcp_port = port;
245 }
246
247 guint
248 x_server_local_get_xdmcp_port (XServerLocal *server)
249 {
250     g_return_val_if_fail (server != NULL, 0);
251     return server->priv->xdmcp_port;
252 }
253
254 void
255 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
256 {
257     g_return_if_fail (server != NULL);
258     g_free (server->priv->xdmcp_key);
259     server->priv->xdmcp_key = g_strdup (key);
260     x_server_set_authority (X_SERVER (server), NULL);
261 }
262
263 void
264 x_server_local_set_background (XServerLocal *server, const gchar *background)
265 {
266     g_return_if_fail (server != NULL);
267     g_free (server->priv->background);
268     server->priv->background = g_strdup (background);
269 }
270
271 void
272 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
273 {
274     g_return_if_fail (server != NULL);
275     g_free (server->priv->mir_id);
276     server->priv->mir_id = g_strdup (id);
277 }
278
279 const gchar *x_server_local_get_mir_id (XServerLocal *server)
280 {
281     g_return_val_if_fail (server != NULL, NULL);
282     return server->priv->mir_id;
283 }
284
285 void
286 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
287 {
288     g_return_if_fail (server != NULL);
289     g_free (server->priv->mir_socket);
290     server->priv->mir_socket = g_strdup (socket);
291 }
292
293 static gint
294 x_server_local_get_vt (DisplayServer *server)
295 {
296     g_return_val_if_fail (server != NULL, 0);
297     return X_SERVER_LOCAL (server)->priv->vt;
298 }
299
300 const gchar *
301 x_server_local_get_authority_file_path (XServerLocal *server)
302 {
303     g_return_val_if_fail (server != NULL, 0);
304     return server->priv->authority_file;
305 }
306
307 static gchar *
308 get_absolute_command (const gchar *command)
309 {
310     gchar **tokens;
311     gchar *absolute_binary, *absolute_command = NULL;
312
313     tokens = g_strsplit (command, " ", 2);
314
315     absolute_binary = g_find_program_in_path (tokens[0]);
316     if (absolute_binary)
317     {
318         if (tokens[1])
319             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
320         else
321             absolute_command = g_strdup (absolute_binary);
322     }
323     g_free (absolute_binary);
324
325     g_strfreev (tokens);
326
327     return absolute_command;
328 }
329
330 static void
331 run_cb (Process *process, gpointer user_data)
332 {
333     int fd;
334
335     /* Make input non-blocking */
336     fd = open ("/dev/null", O_RDONLY);
337     dup2 (fd, STDIN_FILENO);
338     close (fd);
339
340     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
341     signal (SIGUSR1, SIG_IGN);
342 }
343
344 static void
345 got_signal_cb (Process *process, int signum, XServerLocal *server)
346 {
347     if (signum == SIGUSR1 && !server->priv->got_signal)
348     {
349         server->priv->got_signal = TRUE;
350         l_debug (server, "Got signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
351
352         // FIXME: Check return value
353         DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
354     }
355 }
356
357 static void
358 stopped_cb (Process *process, XServerLocal *server)
359 {
360     l_debug (server, "X server stopped");
361
362     /* Release VT and display number for re-use */
363     if (server->priv->have_vt_ref)
364     {
365         vt_unref (server->priv->vt);
366         server->priv->have_vt_ref = FALSE;
367     }
368     x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
369
370     if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
371     {
372         l_debug (server, "Removing X server authority %s", server->priv->authority_file);
373
374         g_unlink (server->priv->authority_file);
375
376         g_free (server->priv->authority_file);
377         server->priv->authority_file = NULL;
378     }
379
380     DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
381 }
382
383 static void
384 write_authority_file (XServerLocal *server)
385 {
386     XAuthority *authority;
387     GError *error = NULL;
388
389     authority = x_server_get_authority (X_SERVER (server));
390     if (!authority)
391         return;
392
393     /* Get file to write to if have authority */
394     if (!server->priv->authority_file)
395     {
396         gchar *run_dir, *dir;
397
398         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
399         dir = g_build_filename (run_dir, "root", NULL);
400         g_free (run_dir);
401         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
402             l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
403
404         server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
405         g_free (dir);
406     }
407
408     l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
409
410     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
411     if (error)
412         l_warning (server, "Failed to write authority: %s", error->message);
413     g_clear_error (&error);
414 }
415
416 static gboolean
417 x_server_local_start (DisplayServer *display_server)
418 {
419     XServerLocal *server = X_SERVER_LOCAL (display_server);
420     gboolean result;
421     gchar *filename, *dir, *log_file, *absolute_command;
422     GString *command;
423
424     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
425
426     server->priv->got_signal = FALSE;
427
428     g_return_val_if_fail (server->priv->command != NULL, FALSE);
429
430     server->priv->x_server_process = process_new (run_cb, server);
431     process_set_clear_environment (server->priv->x_server_process, TRUE);
432     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
433     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
434
435     /* Setup logging */
436     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
437     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
438     log_file = g_build_filename (dir, filename, NULL);
439     process_set_log_file (server->priv->x_server_process, log_file, TRUE);
440     l_debug (display_server, "Logging to %s", log_file);
441     g_free (log_file);
442     g_free (filename);
443     g_free (dir);
444
445     absolute_command = get_absolute_command (server->priv->command);
446     if (!absolute_command)
447     {
448         l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
449         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
450         return FALSE;
451     }
452     command = g_string_new (absolute_command);
453     g_free (absolute_command);
454
455     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
456
457     if (server->priv->config_file)
458         g_string_append_printf (command, " -config %s", server->priv->config_file);
459
460     if (server->priv->layout)
461         g_string_append_printf (command, " -layout %s", server->priv->layout);
462
463     if (server->priv->xdg_seat)
464         g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
465
466     write_authority_file (server);
467     if (server->priv->authority_file)
468         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
469
470     /* Setup for running inside Mir */
471     if (server->priv->mir_id)
472         g_string_append_printf (command, " -mir %s", server->priv->mir_id);
473
474     if (server->priv->mir_socket)
475         g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
476
477     /* Connect to a remote server using XDMCP */
478     if (server->priv->xdmcp_server != NULL)
479     {
480         if (server->priv->xdmcp_port != 0)
481             g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
482         g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
483         if (server->priv->xdmcp_key)
484             g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
485     }
486     else if (!server->priv->allow_tcp)
487         g_string_append (command, " -nolisten tcp");
488
489     if (server->priv->vt >= 0)
490         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
491
492     if (server->priv->background)
493         g_string_append_printf (command, " -background %s", server->priv->background);
494
495     process_set_command (server->priv->x_server_process, command->str);
496     g_string_free (command, TRUE);
497
498     l_debug (display_server, "Launching X Server");
499
500     /* If running inside another display then pass through those variables */
501     if (g_getenv ("DISPLAY"))
502     {
503         process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
504         if (g_getenv ("XAUTHORITY"))
505             process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
506         else
507         {
508             gchar *path;
509             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
510             process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
511             g_free (path);
512         }
513     }
514
515     /* Variable required for regression tests */
516     if (g_getenv ("LIGHTDM_TEST_ROOT"))
517     {
518         process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
519         process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
520         process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
521     }
522
523     result = process_start (server->priv->x_server_process, FALSE);
524
525     if (result)
526         l_debug (display_server, "Waiting for ready signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
527
528     if (!result)
529         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
530
531     return result;
532 }
533
534 static void
535 x_server_local_stop (DisplayServer *server)
536 {
537     process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
538 }
539
540 static void
541 x_server_local_init (XServerLocal *server)
542 {
543     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
544     server->priv->vt = -1;
545     server->priv->command = g_strdup ("X");
546 }
547
548 static void
549 x_server_local_finalize (GObject *object)
550 {
551     XServerLocal *self;
552
553     self = X_SERVER_LOCAL (object);
554
555     if (self->priv->x_server_process)
556     {
557         g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
558         g_object_unref (self->priv->x_server_process);
559     }
560     g_free (self->priv->command);
561     g_free (self->priv->config_file);
562     g_free (self->priv->layout);
563     g_free (self->priv->xdg_seat);
564     g_free (self->priv->xdmcp_server);
565     g_free (self->priv->xdmcp_key);
566     g_free (self->priv->mir_id);
567     g_free (self->priv->mir_socket);
568     g_free (self->priv->authority_file);
569     if (self->priv->have_vt_ref)
570         vt_unref (self->priv->vt);
571     g_free (self->priv->background);
572
573     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
574 }
575
576 static void
577 x_server_local_class_init (XServerLocalClass *klass)
578 {
579     GObjectClass *object_class = G_OBJECT_CLASS (klass);
580     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
581
582     display_server_class->get_vt = x_server_local_get_vt;
583     display_server_class->start = x_server_local_start;
584     display_server_class->stop = x_server_local_stop;
585     object_class->finalize = x_server_local_finalize;
586
587     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
588 }