]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
Add a backup-logs option to re-enable the old log backup mode
[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 gchar *version = NULL;
78 static guint version_major = 0, version_minor = 0;
79 static GList *display_numbers = NULL;
80
81 #define XORG_VERSION_PREFIX "X.Org X Server "
82
83 static gchar *
84 find_version (const gchar *line)
85 {
86     if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
87         return NULL;
88
89     return g_strdup (line + strlen (XORG_VERSION_PREFIX));
90 }
91
92 const gchar *
93 x_server_local_get_version (void)
94 {
95     gchar *stderr_text;
96     gint exit_status;
97     gchar **tokens;
98     guint n_tokens;
99
100     if (version)
101         return version;
102
103     if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
104         return NULL;
105     if (exit_status == EXIT_SUCCESS)
106     {
107         gchar **lines;
108         int i;
109
110         lines = g_strsplit (stderr_text, "\n", -1);
111         for (i = 0; lines[i] && !version; i++)
112             version = find_version (lines[i]);
113         g_strfreev (lines);
114     }
115     g_free (stderr_text);
116
117     tokens = g_strsplit (version, ".", 3);
118     n_tokens = g_strv_length (tokens);
119     version_major = n_tokens > 0 ? atoi (tokens[0]) : 0;
120     version_minor = n_tokens > 1 ? atoi (tokens[1]) : 0;
121     g_strfreev (tokens);
122
123     return version;
124 }
125
126 gint
127 x_server_local_version_compare (guint major, guint minor)
128 {
129     x_server_local_get_version ();
130     if (major == version_major)
131         return version_minor - minor;
132     else
133         return version_major - major;
134 }
135
136 static gboolean
137 display_number_in_use (guint display_number)
138 {
139     GList *link;
140     gchar *path;
141     gboolean in_use;
142     gchar *data;
143
144     /* See if we know we are managing a server with that number */
145     for (link = display_numbers; link; link = link->next)
146     {
147         guint number = GPOINTER_TO_UINT (link->data);
148         if (number == display_number)
149             return TRUE;
150     }
151
152     /* See if an X server that we don't know of has a lock on that number */
153     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
154     in_use = g_file_test (path, G_FILE_TEST_EXISTS);
155
156     /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
157     if (in_use && g_file_get_contents (path, &data, NULL, NULL))
158     {
159         int pid;
160
161         pid = atoi (g_strstrip (data));
162         g_free (data);
163
164         errno = 0;
165         if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
166             in_use = FALSE;
167     }
168
169     g_free (path);
170
171     return in_use;
172 }
173
174 guint
175 x_server_local_get_unused_display_number (void)
176 {
177     guint number;
178
179     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
180     while (display_number_in_use (number))
181         number++;
182
183     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
184
185     return number;
186 }
187
188 void
189 x_server_local_release_display_number (guint display_number)
190 {
191     GList *link;
192     for (link = display_numbers; link; link = link->next)
193     {
194         guint number = GPOINTER_TO_UINT (link->data);
195         if (number == display_number)
196         {
197             display_numbers = g_list_delete_link (display_numbers, link);
198             return;
199         }
200     }
201 }
202
203 XServerLocal *
204 x_server_local_new (void)
205 {
206     XServerLocal *self;
207     gchar hostname[1024], *number, *name;
208     XAuthority *cookie;
209
210     self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
211
212     x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
213
214     gethostname (hostname, 1024);
215     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (self)));
216     cookie = x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
217     x_server_set_authority (X_SERVER (self), cookie);
218     g_free (number);
219     g_object_unref (cookie);
220
221     name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
222     display_server_set_name (DISPLAY_SERVER (self), name);
223     g_free (name);
224
225     return self;
226 }
227
228 void
229 x_server_local_set_command (XServerLocal *server, const gchar *command)
230 {
231     g_return_if_fail (server != NULL);
232     g_free (server->priv->command);
233     server->priv->command = g_strdup (command);
234 }
235
236 void
237 x_server_local_set_vt (XServerLocal *server, gint vt)
238 {
239     g_return_if_fail (server != NULL);
240     if (server->priv->have_vt_ref)
241         vt_unref (server->priv->vt);
242     server->priv->have_vt_ref = FALSE;
243     server->priv->vt = vt;
244     if (vt > 0)
245     {
246         vt_ref (vt);
247         server->priv->have_vt_ref = TRUE;
248     }
249 }
250
251 void
252 x_server_local_set_config (XServerLocal *server, const gchar *path)
253 {
254     g_return_if_fail (server != NULL);
255     g_free (server->priv->config_file);
256     server->priv->config_file = g_strdup (path);
257 }
258
259 void
260 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
261 {
262     g_return_if_fail (server != NULL);
263     g_free (server->priv->layout);
264     server->priv->layout = g_strdup (layout);
265 }
266
267 void
268 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
269 {
270     g_return_if_fail (server != NULL);
271     g_free (server->priv->xdg_seat);
272     server->priv->xdg_seat = g_strdup (xdg_seat);
273 }
274
275 void
276 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
277 {
278     g_return_if_fail (server != NULL);
279     server->priv->allow_tcp = allow_tcp;
280 }
281
282 void
283 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
284 {
285     g_return_if_fail (server != NULL);
286     g_free (server->priv->xdmcp_server);
287     server->priv->xdmcp_server = g_strdup (hostname);
288 }
289
290 const gchar *
291 x_server_local_get_xdmcp_server (XServerLocal *server)
292 {
293     g_return_val_if_fail (server != NULL, 0);
294     return server->priv->xdmcp_server;
295 }
296
297 void
298 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
299 {
300     g_return_if_fail (server != NULL);
301     server->priv->xdmcp_port = port;
302 }
303
304 guint
305 x_server_local_get_xdmcp_port (XServerLocal *server)
306 {
307     g_return_val_if_fail (server != NULL, 0);
308     return server->priv->xdmcp_port;
309 }
310
311 void
312 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
313 {
314     g_return_if_fail (server != NULL);
315     g_free (server->priv->xdmcp_key);
316     server->priv->xdmcp_key = g_strdup (key);
317     x_server_set_authority (X_SERVER (server), NULL);
318 }
319
320 void
321 x_server_local_set_background (XServerLocal *server, const gchar *background)
322 {
323     g_return_if_fail (server != NULL);
324     g_free (server->priv->background);
325     server->priv->background = g_strdup (background);
326 }
327
328 void
329 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
330 {
331     g_return_if_fail (server != NULL);
332     g_free (server->priv->mir_id);
333     server->priv->mir_id = g_strdup (id);
334 }
335
336 const gchar *x_server_local_get_mir_id (XServerLocal *server)
337 {
338     g_return_val_if_fail (server != NULL, NULL);
339     return server->priv->mir_id;
340 }
341
342 void
343 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
344 {
345     g_return_if_fail (server != NULL);
346     g_free (server->priv->mir_socket);
347     server->priv->mir_socket = g_strdup (socket);
348 }
349
350 static gint
351 x_server_local_get_vt (DisplayServer *server)
352 {
353     g_return_val_if_fail (server != NULL, 0);
354     return X_SERVER_LOCAL (server)->priv->vt;
355 }
356
357 const gchar *
358 x_server_local_get_authority_file_path (XServerLocal *server)
359 {
360     g_return_val_if_fail (server != NULL, 0);
361     return server->priv->authority_file;
362 }
363
364 static gchar *
365 get_absolute_command (const gchar *command)
366 {
367     gchar **tokens;
368     gchar *absolute_binary, *absolute_command = NULL;
369
370     tokens = g_strsplit (command, " ", 2);
371
372     absolute_binary = g_find_program_in_path (tokens[0]);
373     if (absolute_binary)
374     {
375         if (tokens[1])
376             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
377         else
378             absolute_command = g_strdup (absolute_binary);
379     }
380     g_free (absolute_binary);
381
382     g_strfreev (tokens);
383
384     return absolute_command;
385 }
386
387 static void
388 run_cb (Process *process, gpointer user_data)
389 {
390     int fd;
391
392     /* Make input non-blocking */
393     fd = open ("/dev/null", O_RDONLY);
394     dup2 (fd, STDIN_FILENO);
395     close (fd);
396
397     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
398     signal (SIGUSR1, SIG_IGN);
399 }
400
401 static void
402 got_signal_cb (Process *process, int signum, XServerLocal *server)
403 {
404     if (signum == SIGUSR1 && !server->priv->got_signal)
405     {
406         server->priv->got_signal = TRUE;
407         l_debug (server, "Got signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
408
409         // FIXME: Check return value
410         DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
411     }
412 }
413
414 static void
415 stopped_cb (Process *process, XServerLocal *server)
416 {
417     l_debug (server, "X server stopped");
418
419     /* Release VT and display number for re-use */
420     if (server->priv->have_vt_ref)
421     {
422         vt_unref (server->priv->vt);
423         server->priv->have_vt_ref = FALSE;
424     }
425     x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
426
427     if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
428     {
429         l_debug (server, "Removing X server authority %s", server->priv->authority_file);
430
431         g_unlink (server->priv->authority_file);
432
433         g_free (server->priv->authority_file);
434         server->priv->authority_file = NULL;
435     }
436
437     DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
438 }
439
440 static void
441 write_authority_file (XServerLocal *server)
442 {
443     XAuthority *authority;
444     GError *error = NULL;
445
446     authority = x_server_get_authority (X_SERVER (server));
447     if (!authority)
448         return;
449
450     /* Get file to write to if have authority */
451     if (!server->priv->authority_file)
452     {
453         gchar *run_dir, *dir;
454
455         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
456         dir = g_build_filename (run_dir, "root", NULL);
457         g_free (run_dir);
458         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
459             l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
460
461         server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
462         g_free (dir);
463     }
464
465     l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
466
467     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
468     if (error)
469         l_warning (server, "Failed to write authority: %s", error->message);
470     g_clear_error (&error);
471 }
472
473 static gboolean
474 x_server_local_start (DisplayServer *display_server)
475 {
476     XServerLocal *server = X_SERVER_LOCAL (display_server);
477     gboolean result, backup_logs;
478     gchar *filename, *dir, *log_file, *absolute_command;
479     GString *command;
480
481     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
482
483     server->priv->got_signal = FALSE;
484
485     g_return_val_if_fail (server->priv->command != NULL, FALSE);
486
487     server->priv->x_server_process = process_new (run_cb, server);
488     process_set_clear_environment (server->priv->x_server_process, TRUE);
489     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
490     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
491
492     /* Setup logging */
493     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
494     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
495     log_file = g_build_filename (dir, filename, NULL);
496     backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
497     process_set_log_file (server->priv->x_server_process, log_file, TRUE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
498     l_debug (display_server, "Logging to %s", log_file);
499     g_free (log_file);
500     g_free (filename);
501     g_free (dir);
502
503     absolute_command = get_absolute_command (server->priv->command);
504     if (!absolute_command)
505     {
506         l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
507         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
508         return FALSE;
509     }
510     command = g_string_new (absolute_command);
511     g_free (absolute_command);
512
513     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
514
515     if (server->priv->config_file)
516         g_string_append_printf (command, " -config %s", server->priv->config_file);
517
518     if (server->priv->layout)
519         g_string_append_printf (command, " -layout %s", server->priv->layout);
520
521     if (server->priv->xdg_seat)
522         g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
523
524     write_authority_file (server);
525     if (server->priv->authority_file)
526         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
527
528     /* Setup for running inside Mir */
529     if (server->priv->mir_id)
530         g_string_append_printf (command, " -mir %s", server->priv->mir_id);
531
532     if (server->priv->mir_socket)
533         g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
534
535     /* Connect to a remote server using XDMCP */
536     if (server->priv->xdmcp_server != NULL)
537     {
538         if (server->priv->xdmcp_port != 0)
539             g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
540         g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
541         if (server->priv->xdmcp_key)
542             g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
543     }
544     else if (server->priv->allow_tcp)
545     {
546         if (x_server_local_version_compare (1, 17) >= 0)
547             g_string_append (command, " -listen tcp");
548     }
549     else
550         g_string_append (command, " -nolisten tcp");
551
552     if (server->priv->vt >= 0)
553         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
554
555     if (server->priv->background)
556         g_string_append_printf (command, " -background %s", server->priv->background);
557
558     process_set_command (server->priv->x_server_process, command->str);
559     g_string_free (command, TRUE);
560
561     l_debug (display_server, "Launching X Server");
562
563     /* If running inside another display then pass through those variables */
564     if (g_getenv ("DISPLAY"))
565     {
566         process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
567         if (g_getenv ("XAUTHORITY"))
568             process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
569         else
570         {
571             gchar *path;
572             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
573             process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
574             g_free (path);
575         }
576     }
577
578     /* Variable required for regression tests */
579     if (g_getenv ("LIGHTDM_TEST_ROOT"))
580     {
581         process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
582         process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
583         process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
584     }
585
586     result = process_start (server->priv->x_server_process, FALSE);
587
588     if (result)
589         l_debug (display_server, "Waiting for ready signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
590
591     if (!result)
592         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
593
594     return result;
595 }
596
597 static void
598 x_server_local_stop (DisplayServer *server)
599 {
600     process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
601 }
602
603 static void
604 x_server_local_init (XServerLocal *server)
605 {
606     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
607     server->priv->vt = -1;
608     server->priv->command = g_strdup ("X");
609 }
610
611 static void
612 x_server_local_finalize (GObject *object)
613 {
614     XServerLocal *self = X_SERVER_LOCAL (object);
615
616     if (self->priv->x_server_process)
617     {
618         g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
619         g_object_unref (self->priv->x_server_process);
620     }
621     g_free (self->priv->command);
622     g_free (self->priv->config_file);
623     g_free (self->priv->layout);
624     g_free (self->priv->xdg_seat);
625     g_free (self->priv->xdmcp_server);
626     g_free (self->priv->xdmcp_key);
627     g_free (self->priv->mir_id);
628     g_free (self->priv->mir_socket);
629     g_free (self->priv->authority_file);
630     if (self->priv->have_vt_ref)
631         vt_unref (self->priv->vt);
632     g_free (self->priv->background);
633
634     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
635 }
636
637 static void
638 x_server_local_class_init (XServerLocalClass *klass)
639 {
640     GObjectClass *object_class = G_OBJECT_CLASS (klass);
641     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
642
643     display_server_class->get_vt = x_server_local_get_vt;
644     display_server_class->start = x_server_local_start;
645     display_server_class->stop = x_server_local_stop;
646     object_class->finalize = x_server_local_finalize;
647
648     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
649 }