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