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
18 #include <glib/gstdio.h>
21 #include "xserver-local.h"
22 #include "configuration.h"
27 struct XServerLocalPrivate
29 /* X server process */
30 Process *xserver_process;
35 /* Command to run the X server */
38 /* Config file to use */
41 /* Server layout to use */
44 /* TRUE if TCP/IP connections are allowed */
48 gchar *authority_file;
50 /* XDMCP server to connect to */
53 /* XDMCP port to connect to */
56 /* XDMCP key to use */
59 /* ID to report to Mir */
62 /* Filename of socket Mir is listening on */
65 /* TRUE when received ready signal */
71 /* TRUE if holding a reference to the VT */
74 /* TRUE if replacing Plymouth */
75 gboolean replacing_plymouth;
78 G_DEFINE_TYPE (XServerLocal, xserver_local, XSERVER_TYPE);
80 static GList *display_numbers = NULL;
83 display_number_in_use (guint display_number)
88 gboolean stale = TRUE;
91 /* See if we know we are managing a server with that number */
92 for (link = display_numbers; link; link = link->next)
94 guint number = GPOINTER_TO_UINT (link->data);
95 if (number == display_number)
99 /* See if an X server that we don't know of has a lock on that number */
100 path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
101 exists = g_file_test (path, G_FILE_TEST_EXISTS);
106 /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
107 if (g_file_get_contents (path, &data, NULL, NULL))
111 pid = atoi (g_strstrip (data));
116 if (kill (pid, 0) < 0 && errno == ESRCH)
124 xserver_local_get_unused_display_number (void)
128 number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
129 while (display_number_in_use (number))
132 display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
138 xserver_local_release_display_number (guint display_number)
141 for (link = display_numbers; link; link = link->next)
143 guint number = GPOINTER_TO_UINT (link->data);
144 if (number == display_number)
146 display_numbers = g_list_remove_link (display_numbers, link);
153 xserver_local_new (void)
155 XServerLocal *self = g_object_new (XSERVER_LOCAL_TYPE, NULL);
158 xserver_set_display_number (XSERVER (self), xserver_local_get_unused_display_number ());
160 name = g_strdup_printf ("x-%d", xserver_get_display_number (XSERVER (self)));
161 display_server_set_name (DISPLAY_SERVER (self), name);
164 /* Replace Plymouth if it is running */
165 if (plymouth_get_is_active () && plymouth_has_active_vt ())
167 gint active_vt = vt_get_active ();
168 if (active_vt >= vt_get_min ())
170 g_debug ("X server %s will replace Plymouth", xserver_get_address (XSERVER (self)));
171 self->priv->replacing_plymouth = TRUE;
172 self->priv->vt = active_vt;
173 plymouth_deactivate ();
176 g_debug ("Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
178 if (self->priv->vt < 0)
179 self->priv->vt = vt_get_unused ();
180 if (self->priv->vt >= 0)
182 vt_ref (self->priv->vt);
183 self->priv->have_vt_ref = TRUE;
190 xserver_local_set_command (XServerLocal *server, const gchar *command)
192 g_return_if_fail (server != NULL);
193 g_free (server->priv->command);
194 server->priv->command = g_strdup (command);
198 xserver_local_set_config (XServerLocal *server, const gchar *path)
200 g_return_if_fail (server != NULL);
201 g_free (server->priv->config_file);
202 server->priv->config_file = g_strdup (path);
206 xserver_local_set_layout (XServerLocal *server, const gchar *layout)
208 g_return_if_fail (server != NULL);
209 g_free (server->priv->layout);
210 server->priv->layout = g_strdup (layout);
214 xserver_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
216 g_return_if_fail (server != NULL);
217 server->priv->allow_tcp = allow_tcp;
221 xserver_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
223 g_return_if_fail (server != NULL);
224 g_free (server->priv->xdmcp_server);
225 server->priv->xdmcp_server = g_strdup (hostname);
226 display_server_set_start_local_sessions (DISPLAY_SERVER (server), hostname == NULL);
230 xserver_local_get_xdmcp_server (XServerLocal *server)
232 g_return_val_if_fail (server != NULL, 0);
233 return server->priv->xdmcp_server;
237 xserver_local_set_xdmcp_port (XServerLocal *server, guint port)
239 g_return_if_fail (server != NULL);
240 server->priv->xdmcp_port = port;
244 xserver_local_get_xdmcp_port (XServerLocal *server)
246 g_return_val_if_fail (server != NULL, 0);
247 return server->priv->xdmcp_port;
251 xserver_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
253 g_return_if_fail (server != NULL);
254 g_free (server->priv->xdmcp_key);
255 server->priv->xdmcp_key = g_strdup (key);
259 xserver_local_set_mir_id (XServerLocal *server, const gchar *id)
261 g_return_if_fail (server != NULL);
262 g_free (server->priv->mir_id);
263 server->priv->mir_id = g_strdup (id);
265 if (server->priv->have_vt_ref)
267 vt_unref (server->priv->vt);
268 server->priv->have_vt_ref = FALSE;
270 server->priv->vt = -1;
273 const gchar *xserver_local_get_mir_id (XServerLocal *server)
275 g_return_val_if_fail (server != NULL, NULL);
276 return server->priv->mir_id;
280 xserver_local_set_mir_socket (XServerLocal *server, const gchar *socket)
282 g_return_if_fail (server != NULL);
283 g_free (server->priv->mir_socket);
284 server->priv->mir_socket = g_strdup (socket);
288 xserver_local_get_vt (XServerLocal *server)
290 g_return_val_if_fail (server != NULL, 0);
291 return server->priv->vt;
295 xserver_local_get_authority_file_path (XServerLocal *server)
297 g_return_val_if_fail (server != NULL, 0);
298 return server->priv->authority_file;
302 get_absolute_command (const gchar *command)
305 gchar *absolute_binary, *absolute_command = NULL;
307 tokens = g_strsplit (command, " ", 2);
309 absolute_binary = g_find_program_in_path (tokens[0]);
313 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
315 absolute_command = g_strdup (absolute_binary);
317 g_free (absolute_binary);
321 return absolute_command;
325 run_cb (Process *process, XServerLocal *server)
329 /* Make input non-blocking */
330 fd = open ("/dev/null", O_RDONLY);
331 dup2 (fd, STDIN_FILENO);
334 /* Redirect output to logfile */
335 if (server->priv->log_file)
339 fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
341 g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
344 dup2 (fd, STDOUT_FILENO);
345 dup2 (fd, STDERR_FILENO);
350 /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
351 signal (SIGUSR1, SIG_IGN);
355 got_signal_cb (Process *process, int signum, XServerLocal *server)
357 if (signum == SIGUSR1 && !server->priv->got_signal)
359 server->priv->got_signal = TRUE;
360 g_debug ("Got signal from X server :%d", xserver_get_display_number (XSERVER (server)));
362 if (server->priv->replacing_plymouth)
364 g_debug ("Stopping Plymouth, X server is ready");
365 server->priv->replacing_plymouth = FALSE;
366 plymouth_quit (TRUE);
369 // FIXME: Check return value
370 DISPLAY_SERVER_CLASS (xserver_local_parent_class)->start (DISPLAY_SERVER (server));
375 stopped_cb (Process *process, XServerLocal *server)
377 g_debug ("X server stopped");
379 xserver_local_release_display_number (xserver_get_display_number (XSERVER (server)));
381 if (xserver_get_authority (XSERVER (server)) && server->priv->authority_file)
383 g_debug ("Removing X server authority %s", server->priv->authority_file);
385 g_unlink (server->priv->authority_file);
387 g_free (server->priv->authority_file);
388 server->priv->authority_file = NULL;
391 if (server->priv->have_vt_ref)
393 vt_unref (server->priv->vt);
394 server->priv->have_vt_ref = FALSE;
397 if (server->priv->replacing_plymouth && plymouth_get_is_running ())
399 g_debug ("Stopping Plymouth, X server failed to start");
400 server->priv->replacing_plymouth = FALSE;
401 plymouth_quit (FALSE);
404 DISPLAY_SERVER_CLASS (xserver_local_parent_class)->stop (DISPLAY_SERVER (server));
408 write_authority_file (XServerLocal *server)
410 XAuthority *authority;
411 GError *error = NULL;
413 authority = xserver_get_authority (XSERVER (server));
417 /* Get file to write to if have authority */
418 if (!server->priv->authority_file)
420 gchar *run_dir, *dir;
422 run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
423 dir = g_build_filename (run_dir, "root", NULL);
425 if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
426 g_warning ("Failed to make authority directory %s: %s", dir, strerror (errno));
428 server->priv->authority_file = g_build_filename (dir, xserver_get_address (XSERVER (server)), NULL);
432 g_debug ("Writing X server authority to %s", server->priv->authority_file);
434 xauth_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
436 g_warning ("Failed to write authority: %s", error->message);
437 g_clear_error (&error);
441 xserver_local_start (DisplayServer *display_server)
443 XServerLocal *server = XSERVER_LOCAL (display_server);
445 gchar *filename, *dir, *absolute_command;
446 gchar hostname[1024], *number;
449 g_return_val_if_fail (server->priv->xserver_process == NULL, FALSE);
451 server->priv->got_signal = FALSE;
453 g_return_val_if_fail (server->priv->command != NULL, FALSE);
455 server->priv->xserver_process = process_new ();
456 process_set_clear_environment (server->priv->xserver_process, TRUE);
457 g_signal_connect (server->priv->xserver_process, "run", G_CALLBACK (run_cb), server);
458 g_signal_connect (server->priv->xserver_process, "got-signal", G_CALLBACK (got_signal_cb), server);
459 g_signal_connect (server->priv->xserver_process, "stopped", G_CALLBACK (stopped_cb), server);
462 filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
463 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
464 server->priv->log_file = g_build_filename (dir, filename, NULL);
465 g_debug ("Logging to %s", server->priv->log_file);
469 absolute_command = get_absolute_command (server->priv->command);
470 if (!absolute_command)
472 g_debug ("Can't launch X server %s, not found in path", server->priv->command);
473 stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
476 command = g_string_new (absolute_command);
477 g_free (absolute_command);
479 g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server)));
481 if (server->priv->config_file)
482 g_string_append_printf (command, " -config %s", server->priv->config_file);
484 if (server->priv->layout)
485 g_string_append_printf (command, " -layout %s", server->priv->layout);
487 gethostname (hostname, 1024);
488 number = g_strdup_printf ("%d", xserver_get_display_number (XSERVER (server)));
489 if (!server->priv->xdmcp_key)
490 xserver_set_authority (XSERVER (server), xauth_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number));
492 write_authority_file (server);
493 if (server->priv->authority_file)
494 g_string_append_printf (command, " -auth %s", server->priv->authority_file);
496 /* Setup for running inside Mir */
497 if (server->priv->mir_id)
498 g_string_append_printf (command, " -mir %s", server->priv->mir_id);
500 if (server->priv->mir_socket)
501 g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
503 /* Connect to a remote server using XDMCP */
504 if (server->priv->xdmcp_server != NULL)
506 if (server->priv->xdmcp_port != 0)
507 g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
508 g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
509 if (server->priv->xdmcp_key)
510 g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
512 else if (!server->priv->allow_tcp)
513 g_string_append (command, " -nolisten tcp");
515 if (server->priv->vt >= 0)
516 g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
518 if (server->priv->replacing_plymouth)
519 g_string_append (command, " -background none");
520 process_set_command (server->priv->xserver_process, command->str);
521 g_string_free (command, TRUE);
523 g_debug ("Launching X Server");
525 /* If running inside another display then pass through those variables */
526 if (g_getenv ("DISPLAY"))
528 process_set_env (server->priv->xserver_process, "DISPLAY", g_getenv ("DISPLAY"));
529 if (g_getenv ("XAUTHORITY"))
530 process_set_env (server->priv->xserver_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
534 path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
535 process_set_env (server->priv->xserver_process, "XAUTHORITY", path);
540 /* Variable required for regression tests */
541 if (g_getenv ("LIGHTDM_TEST_ROOT"))
543 process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
544 process_set_env (server->priv->xserver_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
545 process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
548 result = process_start (server->priv->xserver_process, FALSE);
551 g_debug ("Waiting for ready signal from X server :%d", xserver_get_display_number (XSERVER (server)));
554 stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
560 xserver_local_stop (DisplayServer *server)
562 process_stop (XSERVER_LOCAL (server)->priv->xserver_process);
566 xserver_local_init (XServerLocal *server)
568 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XSERVER_LOCAL_TYPE, XServerLocalPrivate);
569 server->priv->vt = -1;
570 server->priv->command = g_strdup ("X");
574 xserver_local_finalize (GObject *object)
578 self = XSERVER_LOCAL (object);
580 if (self->priv->xserver_process)
581 g_object_unref (self->priv->xserver_process);
582 g_free (self->priv->log_file);
583 g_free (self->priv->command);
584 g_free (self->priv->config_file);
585 g_free (self->priv->layout);
586 g_free (self->priv->xdmcp_server);
587 g_free (self->priv->xdmcp_key);
588 g_free (self->priv->mir_id);
589 g_free (self->priv->mir_socket);
590 g_free (self->priv->authority_file);
591 if (self->priv->have_vt_ref)
592 vt_unref (self->priv->vt);
594 G_OBJECT_CLASS (xserver_local_parent_class)->finalize (object);
598 xserver_local_class_init (XServerLocalClass *klass)
600 GObjectClass *object_class = G_OBJECT_CLASS (klass);
601 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
603 display_server_class->start = xserver_local_start;
604 display_server_class->stop = xserver_local_stop;
605 object_class->finalize = xserver_local_finalize;
607 g_type_class_add_private (klass, sizeof (XServerLocalPrivate));