]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
Remove warning about Mir IDs when not using Mir
[sojka/lightdm.git] / src / x-server-local.c
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 struct XServerLocalPrivate
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 };
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_IMPLEMENT_INTERFACE (LOGGER_TYPE, x_server_local_logger_iface_init));
75
76 static gchar *version = NULL;
77 static guint version_major = 0, version_minor = 0;
78 static GList *display_numbers = NULL;
79
80 #define XORG_VERSION_PREFIX "X.Org X Server "
81
82 static gchar *
83 find_version (const gchar *line)
84 {
85     if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
86         return NULL;
87
88     return g_strdup (line + strlen (XORG_VERSION_PREFIX));
89 }
90
91 const gchar *
92 x_server_local_get_version (void)
93 {
94     gchar *stderr_text;
95     gint exit_status;
96     gchar **tokens;
97     guint n_tokens;
98
99     if (version)
100         return version;
101
102     if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
103         return NULL;
104     if (exit_status == EXIT_SUCCESS)
105     {
106         gchar **lines;
107         int i;
108
109         lines = g_strsplit (stderr_text, "\n", -1);
110         for (i = 0; lines[i] && !version; i++)
111             version = find_version (lines[i]);
112         g_strfreev (lines);
113     }
114     g_free (stderr_text);
115
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;
120     g_strfreev (tokens);
121
122     return version;
123 }
124
125 gint
126 x_server_local_version_compare (guint major, guint minor)
127 {
128     x_server_local_get_version ();
129     if (major == version_major)
130         return version_minor - minor;
131     else
132         return version_major - major;
133 }
134
135 static gboolean
136 display_number_in_use (guint display_number)
137 {
138     GList *link;
139     gchar *path;
140     gboolean in_use;
141     gchar *data;
142
143     /* See if we know we are managing a server with that number */
144     for (link = display_numbers; link; link = link->next)
145     {
146         guint number = GPOINTER_TO_UINT (link->data);
147         if (number == display_number)
148             return TRUE;
149     }
150
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);
154
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))
157     {
158         int pid;
159
160         pid = atoi (g_strstrip (data));
161         g_free (data);
162
163         errno = 0;
164         if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
165             in_use = FALSE;
166     }
167
168     g_free (path);
169
170     return in_use;
171 }
172
173 guint
174 x_server_local_get_unused_display_number (void)
175 {
176     guint number;
177
178     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
179     while (display_number_in_use (number))
180         number++;
181
182     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
183
184     return number;
185 }
186
187 void
188 x_server_local_release_display_number (guint display_number)
189 {
190     GList *link;
191     for (link = display_numbers; link; link = link->next)
192     {
193         guint number = GPOINTER_TO_UINT (link->data);
194         if (number == display_number)
195         {
196             display_numbers = g_list_delete_link (display_numbers, link);
197             return;
198         }
199     }
200 }
201
202 XServerLocal *
203 x_server_local_new (void)
204 {
205     return g_object_new (X_SERVER_LOCAL_TYPE, NULL);
206 }
207
208 void
209 x_server_local_set_command (XServerLocal *server, const gchar *command)
210 {
211     g_return_if_fail (server != NULL);
212     g_free (server->priv->command);
213     server->priv->command = g_strdup (command);
214 }
215
216 void
217 x_server_local_set_vt (XServerLocal *server, gint vt)
218 {
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;
224     if (vt > 0)
225     {
226         vt_ref (vt);
227         server->priv->have_vt_ref = TRUE;
228     }
229 }
230
231 void
232 x_server_local_set_config (XServerLocal *server, const gchar *path)
233 {
234     g_return_if_fail (server != NULL);
235     g_free (server->priv->config_file);
236     server->priv->config_file = g_strdup (path);
237 }
238
239 void
240 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
241 {
242     g_return_if_fail (server != NULL);
243     g_free (server->priv->layout);
244     server->priv->layout = g_strdup (layout);
245 }
246
247 void
248 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
249 {
250     g_return_if_fail (server != NULL);
251     g_free (server->priv->xdg_seat);
252     server->priv->xdg_seat = g_strdup (xdg_seat);
253 }
254
255 void
256 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
257 {
258     g_return_if_fail (server != NULL);
259     server->priv->allow_tcp = allow_tcp;
260 }
261
262 void
263 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
264 {
265     g_return_if_fail (server != NULL);
266     g_free (server->priv->xdmcp_server);
267     server->priv->xdmcp_server = g_strdup (hostname);
268 }
269
270 const gchar *
271 x_server_local_get_xdmcp_server (XServerLocal *server)
272 {
273     g_return_val_if_fail (server != NULL, 0);
274     return server->priv->xdmcp_server;
275 }
276
277 void
278 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
279 {
280     g_return_if_fail (server != NULL);
281     server->priv->xdmcp_port = port;
282 }
283
284 guint
285 x_server_local_get_xdmcp_port (XServerLocal *server)
286 {
287     g_return_val_if_fail (server != NULL, 0);
288     return server->priv->xdmcp_port;
289 }
290
291 void
292 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
293 {
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);
298 }
299
300 void
301 x_server_local_set_background (XServerLocal *server, const gchar *background)
302 {
303     g_return_if_fail (server != NULL);
304     g_free (server->priv->background);
305     server->priv->background = g_strdup (background);
306 }
307
308 static guint
309 x_server_local_get_display_number (XServer *server)
310 {
311     return X_SERVER_LOCAL (server)->priv->display_number;
312 }
313
314 static gint
315 x_server_local_get_vt (DisplayServer *server)
316 {
317     return X_SERVER_LOCAL (server)->priv->vt;
318 }
319
320 const gchar *
321 x_server_local_get_authority_file_path (XServerLocal *server)
322 {
323     g_return_val_if_fail (server != NULL, 0);
324     return server->priv->authority_file;
325 }
326
327 static gchar *
328 get_absolute_command (const gchar *command)
329 {
330     gchar **tokens;
331     gchar *absolute_binary, *absolute_command = NULL;
332
333     tokens = g_strsplit (command, " ", 2);
334
335     absolute_binary = g_find_program_in_path (tokens[0]);
336     if (absolute_binary)
337     {
338         if (tokens[1])
339             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
340         else
341             absolute_command = g_strdup (absolute_binary);
342     }
343     g_free (absolute_binary);
344
345     g_strfreev (tokens);
346
347     return absolute_command;
348 }
349
350 static void
351 x_server_local_run (Process *process, gpointer user_data)
352 {
353     int fd;
354
355     /* Make input non-blocking */
356     fd = open ("/dev/null", O_RDONLY);
357     dup2 (fd, STDIN_FILENO);
358     close (fd);
359
360     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
361     signal (SIGUSR1, SIG_IGN);
362 }
363
364 static ProcessRunFunc
365 x_server_local_get_run_function (XServerLocal *server)
366 {
367     return x_server_local_run;
368 }
369
370 static gboolean
371 x_server_local_get_log_stdout (XServerLocal *server)
372 {
373     return TRUE;
374 }
375
376 static void
377 got_signal_cb (Process *process, int signum, XServerLocal *server)
378 {
379     if (signum == SIGUSR1 && !server->priv->got_signal)
380     {
381         server->priv->got_signal = TRUE;
382         l_debug (server, "Got signal from X server :%d", server->priv->display_number);
383
384         // FIXME: Check return value
385         DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
386     }
387 }
388
389 static void
390 stopped_cb (Process *process, XServerLocal *server)
391 {
392     l_debug (server, "X server stopped");
393
394     /* Release VT and display number for re-use */
395     if (server->priv->have_vt_ref)
396     {
397         vt_unref (server->priv->vt);
398         server->priv->have_vt_ref = FALSE;
399     }
400     x_server_local_release_display_number (server->priv->display_number);
401
402     if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
403     {
404         l_debug (server, "Removing X server authority %s", server->priv->authority_file);
405
406         g_unlink (server->priv->authority_file);
407
408         g_free (server->priv->authority_file);
409         server->priv->authority_file = NULL;
410     }
411
412     DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
413 }
414
415 static void
416 write_authority_file (XServerLocal *server)
417 {
418     XAuthority *authority;
419     GError *error = NULL;
420
421     authority = x_server_get_authority (X_SERVER (server));
422     if (!authority)
423         return;
424
425     /* Get file to write to if have authority */
426     if (!server->priv->authority_file)
427     {
428         gchar *run_dir, *dir;
429
430         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
431         dir = g_build_filename (run_dir, "root", NULL);
432         g_free (run_dir);
433         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
434             l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
435
436         server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
437         g_free (dir);
438     }
439
440     l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
441
442     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
443     if (error)
444         l_warning (server, "Failed to write authority: %s", error->message);
445     g_clear_error (&error);
446 }
447
448 static gboolean
449 x_server_local_start (DisplayServer *display_server)
450 {
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;
455     GString *command;
456
457     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
458
459     server->priv->got_signal = FALSE;
460
461     g_return_val_if_fail (server->priv->command != NULL, FALSE);
462
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);
468
469     /* Setup logging */
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);
476     g_free (log_file);
477     g_free (filename);
478     g_free (dir);
479
480     absolute_command = get_absolute_command (server->priv->command);
481     if (!absolute_command)
482     {
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));
485         return FALSE;
486     }
487     command = g_string_new (absolute_command);
488     g_free (absolute_command);
489
490     g_string_append_printf (command, " :%d", server->priv->display_number);
491
492     if (server->priv->config_file)
493         g_string_append_printf (command, " -config %s", server->priv->config_file);
494
495     if (server->priv->layout)
496         g_string_append_printf (command, " -layout %s", server->priv->layout);
497
498     if (server->priv->xdg_seat)
499         g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
500
501     write_authority_file (server);
502     if (server->priv->authority_file)
503         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
504
505     /* Connect to a remote server using XDMCP */
506     if (server->priv->xdmcp_server != NULL)
507     {
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);
513     }
514     else if (server->priv->allow_tcp)
515     {
516         if (x_server_local_version_compare (1, 17) >= 0)
517             g_string_append (command, " -listen tcp");
518     }
519     else
520         g_string_append (command, " -nolisten tcp");
521
522     if (server->priv->vt >= 0)
523         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
524
525     if (server->priv->background)
526         g_string_append_printf (command, " -background %s", server->priv->background);
527
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);
531
532     process_set_command (server->priv->x_server_process, command->str);
533     g_string_free (command, TRUE);
534
535     l_debug (display_server, "Launching X Server");
536
537     /* If running inside another display then pass through those variables */
538     if (g_getenv ("DISPLAY"))
539     {
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"));
543         else
544         {
545             gchar *path;
546             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
547             process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
548             g_free (path);
549         }
550     }
551
552     /* Variable required for regression tests */
553     if (g_getenv ("LIGHTDM_TEST_ROOT"))
554     {
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"));
558     }
559
560     result = process_start (server->priv->x_server_process, FALSE);
561
562     if (result)
563         l_debug (display_server, "Waiting for ready signal from X server :%d", server->priv->display_number);
564
565     if (!result)
566         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
567
568     return result;
569 }
570
571 static void
572 x_server_local_stop (DisplayServer *server)
573 {
574     process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
575 }
576
577 static void
578 x_server_local_init (XServerLocal *server)
579 {
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 ();
584 }
585
586 static void
587 x_server_local_finalize (GObject *object)
588 {
589     XServerLocal *self = X_SERVER_LOCAL (object);
590
591     if (self->priv->x_server_process)
592     {
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);
595     }
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);
606
607     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
608 }
609
610 static void
611 x_server_local_class_init (XServerLocalClass *klass)
612 {
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);
616
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;
624
625     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
626 }
627
628 static gint
629 x_server_local_real_logprefix (Logger *self, gchar *buf, gulong buflen)
630 {
631     XServerLocal *server = X_SERVER_LOCAL (self);
632     return g_snprintf (buf, buflen, "XServer %d: ", server->priv->display_number);
633 }
634
635 static void
636 x_server_local_logger_iface_init (LoggerInterface *iface)
637 {
638     iface->logprefix = &x_server_local_real_logprefix;
639 }