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