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 /* ID to report to Mir */
63 /* Filename of socket Mir is listening on */
66 /* TRUE when received ready signal */
73 /* Background to set */
77 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
79 static gchar *version = NULL;
80 static guint version_major = 0, version_minor = 0;
81 static GList *display_numbers = NULL;
83 #define XORG_VERSION_PREFIX "X.Org X Server "
86 find_version (const gchar *line)
88 if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
91 return g_strdup (line + strlen (XORG_VERSION_PREFIX));
95 x_server_local_get_version (void)
105 if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
107 if (exit_status == EXIT_SUCCESS)
112 lines = g_strsplit (stderr_text, "\n", -1);
113 for (i = 0; lines[i] && !version; i++)
114 version = find_version (lines[i]);
117 g_free (stderr_text);
119 tokens = g_strsplit (version, ".", 3);
120 n_tokens = g_strv_length (tokens);
121 version_major = n_tokens > 0 ? atoi (tokens[0]) : 0;
122 version_minor = n_tokens > 1 ? atoi (tokens[1]) : 0;
129 x_server_local_version_compare (guint major, guint minor)
131 x_server_local_get_version ();
132 if (major == version_major)
133 return version_minor - minor;
135 return version_major - major;
139 display_number_in_use (guint display_number)
146 /* See if we know we are managing a server with that number */
147 for (link = display_numbers; link; link = link->next)
149 guint number = GPOINTER_TO_UINT (link->data);
150 if (number == display_number)
154 /* See if an X server that we don't know of has a lock on that number */
155 path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
156 in_use = g_file_test (path, G_FILE_TEST_EXISTS);
158 /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
159 if (in_use && g_file_get_contents (path, &data, NULL, NULL))
163 pid = atoi (g_strstrip (data));
167 if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
177 x_server_local_get_unused_display_number (void)
181 number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
182 while (display_number_in_use (number))
185 display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
191 x_server_local_release_display_number (guint display_number)
194 for (link = display_numbers; link; link = link->next)
196 guint number = GPOINTER_TO_UINT (link->data);
197 if (number == display_number)
199 display_numbers = g_list_delete_link (display_numbers, link);
206 x_server_local_new (void)
211 self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
213 name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
214 display_server_set_name (DISPLAY_SERVER (self), name);
221 x_server_local_set_command (XServerLocal *server, const gchar *command)
223 g_return_if_fail (server != NULL);
224 g_free (server->priv->command);
225 server->priv->command = g_strdup (command);
229 x_server_local_set_vt (XServerLocal *server, gint vt)
231 g_return_if_fail (server != NULL);
232 if (server->priv->have_vt_ref)
233 vt_unref (server->priv->vt);
234 server->priv->have_vt_ref = FALSE;
235 server->priv->vt = vt;
239 server->priv->have_vt_ref = TRUE;
244 x_server_local_set_config (XServerLocal *server, const gchar *path)
246 g_return_if_fail (server != NULL);
247 g_free (server->priv->config_file);
248 server->priv->config_file = g_strdup (path);
252 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
254 g_return_if_fail (server != NULL);
255 g_free (server->priv->layout);
256 server->priv->layout = g_strdup (layout);
260 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
262 g_return_if_fail (server != NULL);
263 g_free (server->priv->xdg_seat);
264 server->priv->xdg_seat = g_strdup (xdg_seat);
268 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
270 g_return_if_fail (server != NULL);
271 server->priv->allow_tcp = allow_tcp;
275 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
277 g_return_if_fail (server != NULL);
278 g_free (server->priv->xdmcp_server);
279 server->priv->xdmcp_server = g_strdup (hostname);
283 x_server_local_get_xdmcp_server (XServerLocal *server)
285 g_return_val_if_fail (server != NULL, 0);
286 return server->priv->xdmcp_server;
290 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
292 g_return_if_fail (server != NULL);
293 server->priv->xdmcp_port = port;
297 x_server_local_get_xdmcp_port (XServerLocal *server)
299 g_return_val_if_fail (server != NULL, 0);
300 return server->priv->xdmcp_port;
304 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
306 g_return_if_fail (server != NULL);
307 g_free (server->priv->xdmcp_key);
308 server->priv->xdmcp_key = g_strdup (key);
309 x_server_set_authority (X_SERVER (server), NULL);
313 x_server_local_set_background (XServerLocal *server, const gchar *background)
315 g_return_if_fail (server != NULL);
316 g_free (server->priv->background);
317 server->priv->background = g_strdup (background);
321 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
323 g_return_if_fail (server != NULL);
324 g_free (server->priv->mir_id);
325 server->priv->mir_id = g_strdup (id);
328 const gchar *x_server_local_get_mir_id (XServerLocal *server)
330 g_return_val_if_fail (server != NULL, NULL);
331 return server->priv->mir_id;
335 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
337 g_return_if_fail (server != NULL);
338 g_free (server->priv->mir_socket);
339 server->priv->mir_socket = g_strdup (socket);
343 x_server_local_get_display_number (XServer *server)
345 return X_SERVER_LOCAL (server)->priv->display_number;
349 x_server_local_get_vt (DisplayServer *server)
351 return X_SERVER_LOCAL (server)->priv->vt;
355 x_server_local_get_authority_file_path (XServerLocal *server)
357 g_return_val_if_fail (server != NULL, 0);
358 return server->priv->authority_file;
362 get_absolute_command (const gchar *command)
365 gchar *absolute_binary, *absolute_command = NULL;
367 tokens = g_strsplit (command, " ", 2);
369 absolute_binary = g_find_program_in_path (tokens[0]);
373 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
375 absolute_command = g_strdup (absolute_binary);
377 g_free (absolute_binary);
381 return absolute_command;
385 run_cb (Process *process, gpointer user_data)
389 /* Make input non-blocking */
390 fd = open ("/dev/null", O_RDONLY);
391 dup2 (fd, STDIN_FILENO);
394 /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
395 signal (SIGUSR1, SIG_IGN);
399 got_signal_cb (Process *process, int signum, XServerLocal *server)
401 if (signum == SIGUSR1 && !server->priv->got_signal)
403 server->priv->got_signal = TRUE;
404 l_debug (server, "Got signal from X server :%d", server->priv->display_number);
406 // FIXME: Check return value
407 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
412 stopped_cb (Process *process, XServerLocal *server)
414 l_debug (server, "X server stopped");
416 /* Release VT and display number for re-use */
417 if (server->priv->have_vt_ref)
419 vt_unref (server->priv->vt);
420 server->priv->have_vt_ref = FALSE;
422 x_server_local_release_display_number (server->priv->display_number);
424 if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
426 l_debug (server, "Removing X server authority %s", server->priv->authority_file);
428 g_unlink (server->priv->authority_file);
430 g_free (server->priv->authority_file);
431 server->priv->authority_file = NULL;
434 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
438 write_authority_file (XServerLocal *server)
440 XAuthority *authority;
441 GError *error = NULL;
443 authority = x_server_get_authority (X_SERVER (server));
447 /* Get file to write to if have authority */
448 if (!server->priv->authority_file)
450 gchar *run_dir, *dir;
452 run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
453 dir = g_build_filename (run_dir, "root", NULL);
455 if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
456 l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
458 server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
462 l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
464 x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
466 l_warning (server, "Failed to write authority: %s", error->message);
467 g_clear_error (&error);
471 x_server_local_start (DisplayServer *display_server)
473 XServerLocal *server = X_SERVER_LOCAL (display_server);
474 gboolean result, backup_logs;
475 gchar *filename, *dir, *log_file, *absolute_command;
478 g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
480 server->priv->got_signal = FALSE;
482 g_return_val_if_fail (server->priv->command != NULL, FALSE);
484 server->priv->x_server_process = process_new (run_cb, server);
485 process_set_clear_environment (server->priv->x_server_process, TRUE);
486 g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
487 g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
490 filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
491 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
492 log_file = g_build_filename (dir, filename, NULL);
493 backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
494 process_set_log_file (server->priv->x_server_process, log_file, TRUE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
495 l_debug (display_server, "Logging to %s", log_file);
500 absolute_command = get_absolute_command (server->priv->command);
501 if (!absolute_command)
503 l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
504 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
507 command = g_string_new (absolute_command);
508 g_free (absolute_command);
510 g_string_append_printf (command, " :%d", server->priv->display_number);
512 if (server->priv->config_file)
513 g_string_append_printf (command, " -config %s", server->priv->config_file);
515 if (server->priv->layout)
516 g_string_append_printf (command, " -layout %s", server->priv->layout);
518 if (server->priv->xdg_seat)
519 g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
521 write_authority_file (server);
522 if (server->priv->authority_file)
523 g_string_append_printf (command, " -auth %s", server->priv->authority_file);
525 /* Setup for running inside Mir */
526 if (server->priv->mir_id)
527 g_string_append_printf (command, " -mir %s", server->priv->mir_id);
529 if (server->priv->mir_socket)
530 g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
532 /* Connect to a remote server using XDMCP */
533 if (server->priv->xdmcp_server != NULL)
535 if (server->priv->xdmcp_port != 0)
536 g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
537 g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
538 if (server->priv->xdmcp_key)
539 g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
541 else if (server->priv->allow_tcp)
543 if (x_server_local_version_compare (1, 17) >= 0)
544 g_string_append (command, " -listen tcp");
547 g_string_append (command, " -nolisten tcp");
549 if (server->priv->vt >= 0)
550 g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
552 if (server->priv->background)
553 g_string_append_printf (command, " -background %s", server->priv->background);
555 process_set_command (server->priv->x_server_process, command->str);
556 g_string_free (command, TRUE);
558 l_debug (display_server, "Launching X Server");
560 /* If running inside another display then pass through those variables */
561 if (g_getenv ("DISPLAY"))
563 process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
564 if (g_getenv ("XAUTHORITY"))
565 process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
569 path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
570 process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
575 /* Variable required for regression tests */
576 if (g_getenv ("LIGHTDM_TEST_ROOT"))
578 process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
579 process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
580 process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
583 result = process_start (server->priv->x_server_process, FALSE);
586 l_debug (display_server, "Waiting for ready signal from X server :%d", server->priv->display_number);
589 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
595 x_server_local_stop (DisplayServer *server)
597 process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
601 x_server_local_init (XServerLocal *server)
603 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
604 server->priv->vt = -1;
605 server->priv->command = g_strdup ("X");
606 server->priv->display_number = x_server_local_get_unused_display_number ();
610 x_server_local_finalize (GObject *object)
612 XServerLocal *self = X_SERVER_LOCAL (object);
614 if (self->priv->x_server_process)
616 g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
617 g_object_unref (self->priv->x_server_process);
619 g_free (self->priv->command);
620 g_free (self->priv->config_file);
621 g_free (self->priv->layout);
622 g_free (self->priv->xdg_seat);
623 g_free (self->priv->xdmcp_server);
624 g_free (self->priv->xdmcp_key);
625 g_free (self->priv->mir_id);
626 g_free (self->priv->mir_socket);
627 g_free (self->priv->authority_file);
628 if (self->priv->have_vt_ref)
629 vt_unref (self->priv->vt);
630 g_free (self->priv->background);
632 G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
636 x_server_local_class_init (XServerLocalClass *klass)
638 GObjectClass *object_class = G_OBJECT_CLASS (klass);
639 XServerClass *x_server_class = X_SERVER_CLASS (klass);
640 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
642 x_server_class->get_display_number = x_server_local_get_display_number;
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;
648 g_type_class_add_private (klass, sizeof (XServerLocalPrivate));