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 "x-server-local.h"
22 #include "configuration.h"
26 struct XServerLocalPrivate
28 /* X server process */
29 Process *x_server_process;
34 /* Command to run the X server */
37 /* Config file to use */
40 /* Server layout to use */
43 /* Value for -seat argument */
46 /* TRUE if TCP/IP connections are allowed */
50 gchar *authority_file;
52 /* XDMCP server to connect to */
55 /* XDMCP port to connect to */
58 /* XDMCP key to use */
61 /* ID to report to Mir */
64 /* Filename of socket Mir is listening on */
67 /* TRUE when received ready signal */
74 /* Background to set */
78 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
80 static GList *display_numbers = NULL;
83 display_number_in_use (guint display_number)
90 /* See if we know we are managing a server with that number */
91 for (link = display_numbers; link; link = link->next)
93 guint number = GPOINTER_TO_UINT (link->data);
94 if (number == display_number)
98 /* See if an X server that we don't know of has a lock on that number */
99 path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
100 in_use = g_file_test (path, G_FILE_TEST_EXISTS);
102 /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
103 if (in_use && g_file_get_contents (path, &data, NULL, NULL))
107 pid = atoi (g_strstrip (data));
111 if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
121 x_server_local_get_unused_display_number (void)
125 number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
126 while (display_number_in_use (number))
129 display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
135 x_server_local_release_display_number (guint display_number)
138 for (link = display_numbers; link; link = link->next)
140 guint number = GPOINTER_TO_UINT (link->data);
141 if (number == display_number)
143 display_numbers = g_list_remove_link (display_numbers, link);
150 x_server_local_new (void)
152 XServerLocal *self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
153 gchar hostname[1024], *number, *name;
155 x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
157 gethostname (hostname, 1024);
158 number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (self)));
159 x_server_set_authority (X_SERVER (self), x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number));
162 name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
163 display_server_set_name (DISPLAY_SERVER (self), name);
170 x_server_local_set_command (XServerLocal *server, const gchar *command)
172 g_return_if_fail (server != NULL);
173 g_free (server->priv->command);
174 server->priv->command = g_strdup (command);
178 x_server_local_set_vt (XServerLocal *server, gint vt)
180 g_return_if_fail (server != NULL);
181 if (server->priv->have_vt_ref)
182 vt_unref (server->priv->vt);
183 server->priv->have_vt_ref = FALSE;
184 server->priv->vt = vt;
188 server->priv->have_vt_ref = TRUE;
193 x_server_local_set_config (XServerLocal *server, const gchar *path)
195 g_return_if_fail (server != NULL);
196 g_free (server->priv->config_file);
197 server->priv->config_file = g_strdup (path);
201 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
203 g_return_if_fail (server != NULL);
204 g_free (server->priv->layout);
205 server->priv->layout = g_strdup (layout);
209 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
211 g_return_if_fail (server != NULL);
212 g_free (server->priv->xdg_seat);
213 server->priv->xdg_seat = g_strdup (xdg_seat);
217 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
219 g_return_if_fail (server != NULL);
220 server->priv->allow_tcp = allow_tcp;
224 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
226 g_return_if_fail (server != NULL);
227 g_free (server->priv->xdmcp_server);
228 server->priv->xdmcp_server = g_strdup (hostname);
232 x_server_local_get_xdmcp_server (XServerLocal *server)
234 g_return_val_if_fail (server != NULL, 0);
235 return server->priv->xdmcp_server;
239 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
241 g_return_if_fail (server != NULL);
242 server->priv->xdmcp_port = port;
246 x_server_local_get_xdmcp_port (XServerLocal *server)
248 g_return_val_if_fail (server != NULL, 0);
249 return server->priv->xdmcp_port;
253 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
255 g_return_if_fail (server != NULL);
256 g_free (server->priv->xdmcp_key);
257 server->priv->xdmcp_key = g_strdup (key);
258 x_server_set_authority (X_SERVER (server), NULL);
262 x_server_local_set_background (XServerLocal *server, const gchar *background)
264 g_return_if_fail (server != NULL);
265 g_free (server->priv->background);
266 server->priv->background = g_strdup (background);
270 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
272 g_return_if_fail (server != NULL);
273 g_free (server->priv->mir_id);
274 server->priv->mir_id = g_strdup (id);
277 const gchar *x_server_local_get_mir_id (XServerLocal *server)
279 g_return_val_if_fail (server != NULL, NULL);
280 return server->priv->mir_id;
284 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
286 g_return_if_fail (server != NULL);
287 g_free (server->priv->mir_socket);
288 server->priv->mir_socket = g_strdup (socket);
292 x_server_local_get_vt (DisplayServer *server)
294 g_return_val_if_fail (server != NULL, 0);
295 return X_SERVER_LOCAL (server)->priv->vt;
299 x_server_local_get_authority_file_path (XServerLocal *server)
301 g_return_val_if_fail (server != NULL, 0);
302 return server->priv->authority_file;
306 get_absolute_command (const gchar *command)
309 gchar *absolute_binary, *absolute_command = NULL;
311 tokens = g_strsplit (command, " ", 2);
313 absolute_binary = g_find_program_in_path (tokens[0]);
317 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
319 absolute_command = g_strdup (absolute_binary);
321 g_free (absolute_binary);
325 return absolute_command;
329 run_cb (Process *process, XServerLocal *server)
333 /* Make input non-blocking */
334 fd = open ("/dev/null", O_RDONLY);
335 dup2 (fd, STDIN_FILENO);
338 /* Redirect output to logfile */
339 if (server->priv->log_file)
344 /* Move old file out of the way */
345 old_filename = g_strdup_printf ("%s.old", server->priv->log_file);
346 rename (server->priv->log_file, old_filename);
347 g_free (old_filename);
349 /* Create new file and log to it */
350 fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
352 l_warning (server, "Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
355 dup2 (fd, STDOUT_FILENO);
356 dup2 (fd, STDERR_FILENO);
361 /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
362 signal (SIGUSR1, SIG_IGN);
366 got_signal_cb (Process *process, int signum, XServerLocal *server)
368 if (signum == SIGUSR1 && !server->priv->got_signal)
370 server->priv->got_signal = TRUE;
371 l_debug (server, "Got signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
373 // FIXME: Check return value
374 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
379 stopped_cb (Process *process, XServerLocal *server)
381 l_debug (server, "X server stopped");
383 /* Release VT and display number for re-use */
384 if (server->priv->have_vt_ref)
386 vt_unref (server->priv->vt);
387 server->priv->have_vt_ref = FALSE;
389 x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
391 if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
393 l_debug (server, "Removing X server authority %s", server->priv->authority_file);
395 g_unlink (server->priv->authority_file);
397 g_free (server->priv->authority_file);
398 server->priv->authority_file = NULL;
401 DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
405 write_authority_file (XServerLocal *server)
407 XAuthority *authority;
408 GError *error = NULL;
410 authority = x_server_get_authority (X_SERVER (server));
414 /* Get file to write to if have authority */
415 if (!server->priv->authority_file)
417 gchar *run_dir, *dir;
419 run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
420 dir = g_build_filename (run_dir, "root", NULL);
422 if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
423 l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
425 server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
429 l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
431 x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
433 l_warning (server, "Failed to write authority: %s", error->message);
434 g_clear_error (&error);
438 x_server_local_start (DisplayServer *display_server)
440 XServerLocal *server = X_SERVER_LOCAL (display_server);
442 gchar *filename, *dir, *absolute_command;
445 g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
447 server->priv->got_signal = FALSE;
449 g_return_val_if_fail (server->priv->command != NULL, FALSE);
451 server->priv->x_server_process = process_new ();
452 process_set_clear_environment (server->priv->x_server_process, TRUE);
453 g_signal_connect (server->priv->x_server_process, "run", G_CALLBACK (run_cb), server);
454 g_signal_connect (server->priv->x_server_process, "got-signal", G_CALLBACK (got_signal_cb), server);
455 g_signal_connect (server->priv->x_server_process, "stopped", G_CALLBACK (stopped_cb), server);
458 filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
459 dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
460 server->priv->log_file = g_build_filename (dir, filename, NULL);
461 l_debug (display_server, "Logging to %s", server->priv->log_file);
465 absolute_command = get_absolute_command (server->priv->command);
466 if (!absolute_command)
468 l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
469 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
472 command = g_string_new (absolute_command);
473 g_free (absolute_command);
475 g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
477 if (server->priv->config_file)
478 g_string_append_printf (command, " -config %s", server->priv->config_file);
480 if (server->priv->layout)
481 g_string_append_printf (command, " -layout %s", server->priv->layout);
483 if (server->priv->xdg_seat)
484 g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
486 write_authority_file (server);
487 if (server->priv->authority_file)
488 g_string_append_printf (command, " -auth %s", server->priv->authority_file);
490 /* Setup for running inside Mir */
491 if (server->priv->mir_id)
492 g_string_append_printf (command, " -mir %s", server->priv->mir_id);
494 if (server->priv->mir_socket)
495 g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
497 /* Connect to a remote server using XDMCP */
498 if (server->priv->xdmcp_server != NULL)
500 if (server->priv->xdmcp_port != 0)
501 g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
502 g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
503 if (server->priv->xdmcp_key)
504 g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
506 else if (!server->priv->allow_tcp)
507 g_string_append (command, " -nolisten tcp");
509 if (server->priv->vt >= 0)
510 g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
512 if (server->priv->background)
513 g_string_append_printf (command, " -background %s", server->priv->background);
515 process_set_command (server->priv->x_server_process, command->str);
516 g_string_free (command, TRUE);
518 l_debug (display_server, "Launching X Server");
520 /* If running inside another display then pass through those variables */
521 if (g_getenv ("DISPLAY"))
523 process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
524 if (g_getenv ("XAUTHORITY"))
525 process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
529 path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
530 process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
535 /* Variable required for regression tests */
536 if (g_getenv ("LIGHTDM_TEST_ROOT"))
538 process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
539 process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
540 process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
543 result = process_start (server->priv->x_server_process, FALSE);
546 l_debug (display_server, "Waiting for ready signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
549 stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
555 x_server_local_stop (DisplayServer *server)
557 process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
561 x_server_local_init (XServerLocal *server)
563 server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
564 server->priv->vt = -1;
565 server->priv->command = g_strdup ("X");
569 x_server_local_finalize (GObject *object)
573 self = X_SERVER_LOCAL (object);
575 if (self->priv->x_server_process)
577 g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
578 g_object_unref (self->priv->x_server_process);
580 g_free (self->priv->log_file);
581 g_free (self->priv->command);
582 g_free (self->priv->config_file);
583 g_free (self->priv->layout);
584 g_free (self->priv->xdmcp_server);
585 g_free (self->priv->xdmcp_key);
586 g_free (self->priv->mir_id);
587 g_free (self->priv->mir_socket);
588 g_free (self->priv->authority_file);
589 if (self->priv->have_vt_ref)
590 vt_unref (self->priv->vt);
591 g_free (self->priv->background);
593 G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
597 x_server_local_class_init (XServerLocalClass *klass)
599 GObjectClass *object_class = G_OBJECT_CLASS (klass);
600 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
602 display_server_class->get_vt = x_server_local_get_vt;
603 display_server_class->start = x_server_local_start;
604 display_server_class->stop = x_server_local_stop;
605 object_class->finalize = x_server_local_finalize;
607 g_type_class_add_private (klass, sizeof (XServerLocalPrivate));