2 * Copyright (C) 2010-2011 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
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
17 #include <glib/gstdio.h>
20 #include "x-server-local.h"
21 #include "configuration.h"
25 struct XServerLocalPrivate
27 /* X server process */
28 Process *x_server_process;
30 /* Command to run the X server */
33 /* Display number to use */
36 /* Config file to use */
39 /* Server layout to use */
42 /* Value for -seat argument */
45 /* TRUE if TCP/IP connections are allowed */
49 gchar *authority_file;
51 /* XDMCP server to connect to */
54 /* XDMCP port to connect to */
57 /* XDMCP key to use */
60 /* TRUE when received ready signal */
67 /* Background to set */
71 static void x_server_local_logger_iface_init (LoggerInterface *iface);
73 G_DEFINE_TYPE_WITH_CODE (XServerLocal, x_server_local, X_SERVER_TYPE,
74 G_IMPLEMENT_INTERFACE (LOGGER_TYPE, x_server_local_logger_iface_init));
76 static gchar *version = NULL;
77 static guint version_major = 0, version_minor = 0;
78 static GList *display_numbers = NULL;
80 #define XORG_VERSION_PREFIX "X.Org X Server "
83 find_version (const gchar *line)
85 if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
88 return g_strdup (line + strlen (XORG_VERSION_PREFIX));
92 x_server_local_get_version (void)
102 if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
104 if (exit_status == EXIT_SUCCESS)
109 lines = g_strsplit (stderr_text, "\n", -1);
110 for (i = 0; lines[i] && !version; i++)
111 version = find_version (lines[i]);
114 g_free (stderr_text);
116 tokens = g_strsplit (version, ".", 3);
117 n_tokens = g_strv_length (tokens);
118 version_major = n_tokens > 0 ? atoi (tokens[0]) : 0;
119 version_minor = n_tokens > 1 ? atoi (tokens[1]) : 0;
126 x_server_local_version_compare (guint major, guint minor)
128 x_server_local_get_version ();
129 if (major == version_major)
130 return version_minor - minor;
132 return version_major - major;
136 display_number_in_use (guint display_number)
143 /* See if we know we are managing a server with that number */
144 for (link = display_numbers; link; link = link->next)
146 guint number = GPOINTER_TO_UINT (link->data);
147 if (number == display_number)
151 /* See if an X server that we don't know of has a lock on that number */
152 path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
153 in_use = g_file_test (path, G_FILE_TEST_EXISTS);
155 /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
156 if (in_use && g_file_get_contents (path, &data, NULL, NULL))
160 pid = atoi (g_strstrip (data));
164 if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
174 x_server_local_get_unused_display_number (void)
178 number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
179 while (display_number_in_use (number))
182 display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
188 x_server_local_release_display_number (guint display_number)
191 for (link = display_numbers; link; link = link->next)
193 guint number = GPOINTER_TO_UINT (link->data);
194 if (number == display_number)
196 display_numbers = g_list_delete_link (display_numbers, link);
203 x_server_local_new (void)
205 return g_object_new (X_SERVER_LOCAL_TYPE, NULL);
209 x_server_local_set_command (XServerLocal *server, const gchar *command)
211 g_return_if_fail (server != NULL);
212 g_free (server->priv->command);
213 server->priv->command = g_strdup (command);
217 x_server_local_set_vt (XServerLocal *server, gint vt)
219 g_return_if_fail (server != NULL);
220 if (server->priv->have_vt_ref)
221 vt_unref (server->priv->vt);
222 server->priv->have_vt_ref = FALSE;
223 server->priv->vt = vt;
227 server->priv->have_vt_ref = TRUE;
232 x_server_local_set_config (XServerLocal *server, const gchar *path)
234 g_return_if_fail (server != NULL);
235 g_free (server->priv->config_file);
236 server->priv->config_file = g_strdup (path);
240 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
242 g_return_if_fail (server != NULL);
243 g_free (server->priv->layout);
244 server->priv->layout = g_strdup (layout);
248 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
250 g_return_if_fail (server != NULL);
251 g_free (server->priv->xdg_seat);
252 server->priv->xdg_seat = g_strdup (xdg_seat);
256 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
258 g_return_if_fail (server != NULL);
259 server->priv->allow_tcp = allow_tcp;
263 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
265 g_return_if_fail (server != NULL);
266 g_free (server->priv->xdmcp_server);
267 server->priv->xdmcp_server = g_strdup (hostname);
271 x_server_local_get_xdmcp_server (XServerLocal *server)
273 g_return_val_if_fail (server != NULL, 0);
274 return server->priv->xdmcp_server;
278 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
280 g_return_if_fail (server != NULL);
281 server->priv->xdmcp_port = port;
285 x_server_local_get_xdmcp_port (XServerLocal *server)
287 g_return_val_if_fail (server != NULL, 0);
288 return server->priv->xdmcp_port;
292 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
294 g_return_if_fail (server != NULL);
295 g_free (server->priv->xdmcp_key);
296 server->priv->xdmcp_key = g_strdup (key);
297 x_server_set_authority (X_SERVER (server), NULL);
301 x_server_local_set_background (XServerLocal *server, const gchar *background)
303 g_return_if_fail (server != NULL);
304 g_free (server->priv->background);
305 server->priv->background = g_strdup (background);
309 x_server_local_get_display_number (XServer *server)
311 return X_SERVER_LOCAL (server)->priv->display_number;
315 x_server_local_get_vt (DisplayServer *server)
317 return X_SERVER_LOCAL (server)->priv->vt;
321 x_server_local_get_authority_file_path (XServerLocal *server)
323 g_return_val_if_fail (server != NULL, 0);
324 return server->priv->authority_file;
328 get_absolute_command (const gchar *command)
331 gchar *absolute_binary, *absolute_command = NULL;
333 tokens = g_strsplit (command, " ", 2);
335 absolute_binary = g_find_program_in_path (tokens[0]);
339 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
341 absolute_command = g_strdup (absolute_binary);
343 g_free (absolute_binary);
347 return absolute_command;
351 x_server_local_run (Process *process, gpointer user_data)
355 /* Make input non-blocking */
356 fd = open ("/dev/null", O_RDONLY);
357 dup2 (fd, STDIN_FILENO);
360 /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
361 signal (SIGUSR1, SIG_IGN);
364 static ProcessRunFunc
365 x_server_local_get_run_function (XServerLocal *server)
367 return x_server_local_run;
371 x_server_local_get_log_stdout (XServerLocal *server)
377 got_signal_cb (Process *process, int signum, XServerLocal *server)
379 if (signum == SIGUSR1 && !server->priv->got_signal)
381 server->priv->got_signal = TRUE;
382 l_debug (server, "Got signal from X server :%d", server->priv->display_number);
384 // FIXME: Check return value
385 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
390 stopped_cb (Process *process, XServerLocal *server)
392 l_debug (server, "X server stopped");
394 /* Release VT and display number for re-use */
395 if (server->priv->have_vt_ref)
397 vt_unref (server->priv->vt);
398 server->priv->have_vt_ref = FALSE;
400 x_server_local_release_display_number (server->priv->display_number);
402 if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
404 l_debug (server, "Removing X server authority %s", server->priv->authority_file);
406 g_unlink (server->priv->authority_file);
408 g_free (server->priv->authority_file);
409 server->priv->authority_file = NULL;
412 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
416 write_authority_file (XServerLocal *server)
418 XAuthority *authority;
419 GError *error = NULL;
421 authority = x_server_get_authority (X_SERVER (server));
425 /* Get file to write to if have authority */
426 if (!server->priv->authority_file)
428 gchar *run_dir, *dir;
430 run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
431 dir = g_build_filename (run_dir, "root", NULL);
433 if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
434 l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
436 server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
440 l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
442 x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
444 l_warning (server, "Failed to write authority: %s", error->message);
445 g_clear_error (&error);
449 x_server_local_start (DisplayServer *display_server)
451 XServerLocal *server = X_SERVER_LOCAL (display_server);
452 ProcessRunFunc run_cb;
453 gboolean result, backup_logs;
454 gchar *filename, *dir, *log_file, *absolute_command;
457 g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
459 server->priv->got_signal = FALSE;
461 g_return_val_if_fail (server->priv->command != NULL, FALSE);
463 run_cb = X_SERVER_LOCAL_GET_CLASS (server)->get_run_function (server);
464 server->priv->x_server_process = process_new (run_cb, server);
465 process_set_clear_environment (server->priv->x_server_process, TRUE);
466 g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
467 g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
470 filename = g_strdup_printf ("x-%d.log", x_server_get_display_number (X_SERVER (server)));
471 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
472 log_file = g_build_filename (dir, filename, NULL);
473 backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
474 process_set_log_file (server->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);
475 l_debug (display_server, "Logging to %s", log_file);
480 absolute_command = get_absolute_command (server->priv->command);
481 if (!absolute_command)
483 l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
484 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
487 command = g_string_new (absolute_command);
488 g_free (absolute_command);
490 g_string_append_printf (command, " :%d", server->priv->display_number);
492 if (server->priv->config_file)
493 g_string_append_printf (command, " -config %s", server->priv->config_file);
495 if (server->priv->layout)
496 g_string_append_printf (command, " -layout %s", server->priv->layout);
498 if (server->priv->xdg_seat)
499 g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
501 write_authority_file (server);
502 if (server->priv->authority_file)
503 g_string_append_printf (command, " -auth %s", server->priv->authority_file);
505 /* Connect to a remote server using XDMCP */
506 if (server->priv->xdmcp_server != NULL)
508 if (server->priv->xdmcp_port != 0)
509 g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
510 g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
511 if (server->priv->xdmcp_key)
512 g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
514 else if (server->priv->allow_tcp)
516 if (x_server_local_version_compare (1, 17) >= 0)
517 g_string_append (command, " -listen tcp");
520 g_string_append (command, " -nolisten tcp");
522 if (server->priv->vt >= 0)
523 g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
525 if (server->priv->background)
526 g_string_append_printf (command, " -background %s", server->priv->background);
528 /* Allow sub-classes to add arguments */
529 if (X_SERVER_LOCAL_GET_CLASS (server)->add_args)
530 X_SERVER_LOCAL_GET_CLASS (server)->add_args (server, command);
532 process_set_command (server->priv->x_server_process, command->str);
533 g_string_free (command, TRUE);
535 l_debug (display_server, "Launching X Server");
537 /* If running inside another display then pass through those variables */
538 if (g_getenv ("DISPLAY"))
540 process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
541 if (g_getenv ("XAUTHORITY"))
542 process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
546 path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
547 process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
552 /* Variable required for regression tests */
553 if (g_getenv ("LIGHTDM_TEST_ROOT"))
555 process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
556 process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
557 process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
560 result = process_start (server->priv->x_server_process, FALSE);
563 l_debug (display_server, "Waiting for ready signal from X server :%d", server->priv->display_number);
566 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
572 x_server_local_stop (DisplayServer *server)
574 process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
578 x_server_local_init (XServerLocal *server)
580 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
581 server->priv->vt = -1;
582 server->priv->command = g_strdup ("X");
583 server->priv->display_number = x_server_local_get_unused_display_number ();
587 x_server_local_finalize (GObject *object)
589 XServerLocal *self = X_SERVER_LOCAL (object);
591 if (self->priv->x_server_process)
593 g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
594 g_object_unref (self->priv->x_server_process);
596 g_free (self->priv->command);
597 g_free (self->priv->config_file);
598 g_free (self->priv->layout);
599 g_free (self->priv->xdg_seat);
600 g_free (self->priv->xdmcp_server);
601 g_free (self->priv->xdmcp_key);
602 g_free (self->priv->authority_file);
603 if (self->priv->have_vt_ref)
604 vt_unref (self->priv->vt);
605 g_free (self->priv->background);
607 G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
611 x_server_local_class_init (XServerLocalClass *klass)
613 GObjectClass *object_class = G_OBJECT_CLASS (klass);
614 XServerClass *x_server_class = X_SERVER_CLASS (klass);
615 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
617 klass->get_run_function = x_server_local_get_run_function;
618 klass->get_log_stdout = x_server_local_get_log_stdout;
619 x_server_class->get_display_number = x_server_local_get_display_number;
620 display_server_class->get_vt = x_server_local_get_vt;
621 display_server_class->start = klass->start = x_server_local_start;
622 display_server_class->stop = x_server_local_stop;
623 object_class->finalize = x_server_local_finalize;
625 g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
629 x_server_local_real_logprefix (Logger *self, gchar *buf, gulong buflen)
631 XServerLocal *server = X_SERVER_LOCAL (self);
632 return g_snprintf (buf, buflen, "XServer %d: ", server->priv->display_number);
636 x_server_local_logger_iface_init (LoggerInterface *iface)
638 iface->logprefix = &x_server_local_real_logprefix;