]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
0f5cc204998897919804e177864d15227ac09e51
[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 <unistd.h>
16 #include <sys/stat.h>
17 #include <errno.h>
18 #include <glib/gstdio.h>
19 #include <stdlib.h>
20
21 #include "x-server-local.h"
22 #include "configuration.h"
23 #include "process.h"
24 #include "vt.h"
25
26 struct XServerLocalPrivate
27 {
28     /* X server process */
29     Process *x_server_process;
30
31     /* Command to run the X server */
32     gchar *command;
33
34     /* Config file to use */
35     gchar *config_file;
36
37     /* Server layout to use */
38     gchar *layout;
39
40     /* Value for -seat argument */
41     gchar *xdg_seat;
42
43     /* TRUE if TCP/IP connections are allowed */
44     gboolean allow_tcp;
45
46     /* Authority file */
47     gchar *authority_file;
48
49     /* XDMCP server to connect to */
50     gchar *xdmcp_server;
51
52     /* XDMCP port to connect to */
53     guint xdmcp_port;
54
55     /* XDMCP key to use */
56     gchar *xdmcp_key;
57
58     /* ID to report to Mir */
59     gchar *mir_id;
60
61     /* Filename of socket Mir is listening on */
62     gchar *mir_socket;
63
64     /* TRUE when received ready signal */
65     gboolean got_signal;
66
67     /* VT to run on */
68     gint vt;
69     gboolean have_vt_ref;
70
71     /* Background to set */
72     gchar *background;
73 };
74
75 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
76
77 static gchar *version = NULL;
78 static guint version_major = 0, version_minor = 0;
79 static GList *display_numbers = NULL;
80
81 #define XORG_VERSION_PREFIX "X.Org X Server "
82
83 static gchar *
84 find_version (const gchar *line)
85 {
86     if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
87         return NULL;
88
89     return g_strdup (line + strlen (XORG_VERSION_PREFIX));
90 }
91
92 const gchar *
93 x_server_local_get_version (void)
94 {
95     gchar *stderr_text;
96     gint exit_status;
97     gchar **tokens;
98     guint n_tokens;
99
100     if (version)
101         return version;
102
103     if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
104         return NULL;
105     if (exit_status == EXIT_SUCCESS)
106     {
107         gchar **lines;
108         int i;
109
110         lines = g_strsplit (stderr_text, "\n", -1);
111         for (i = 0; lines[i] && !version; i++)
112             version = find_version (lines[i]);
113         g_strfreev (lines);
114     }
115     g_free (stderr_text);
116
117     tokens = g_strsplit (version, ".", 3);
118     n_tokens = g_strv_length (tokens);
119     version_major = n_tokens > 0 ? atoi (tokens[0]) : 0;
120     version_minor = n_tokens > 1 ? atoi (tokens[1]) : 0;
121     g_strfreev (tokens);
122
123     return version;
124 }
125
126 gint
127 x_server_local_version_compare (guint major, guint minor)
128 {
129     x_server_local_get_version ();
130     if (major == version_major)
131         return version_minor - minor;
132     else
133         return version_major - major;
134 }
135
136 static gboolean
137 display_number_in_use (guint display_number)
138 {
139     GList *link;
140     gchar *path;
141     gboolean in_use;
142     gchar *data;
143
144     /* See if we know we are managing a server with that number */
145     for (link = display_numbers; link; link = link->next)
146     {
147         guint number = GPOINTER_TO_UINT (link->data);
148         if (number == display_number)
149             return TRUE;
150     }
151
152     /* See if an X server that we don't know of has a lock on that number */
153     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
154     in_use = g_file_test (path, G_FILE_TEST_EXISTS);
155
156     /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
157     if (in_use && g_file_get_contents (path, &data, NULL, NULL))
158     {
159         int pid;
160
161         pid = atoi (g_strstrip (data));
162         g_free (data);
163
164         errno = 0;
165         if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
166             in_use = FALSE;
167     }
168
169     g_free (path);
170
171     return in_use;
172 }
173
174 guint
175 x_server_local_get_unused_display_number (void)
176 {
177     guint number;
178
179     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
180     while (display_number_in_use (number))
181         number++;
182
183     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
184
185     return number;
186 }
187
188 void
189 x_server_local_release_display_number (guint display_number)
190 {
191     GList *link;
192     for (link = display_numbers; link; link = link->next)
193     {
194         guint number = GPOINTER_TO_UINT (link->data);
195         if (number == display_number)
196         {
197             display_numbers = g_list_delete_link (display_numbers, link);
198             return;
199         }
200     }
201 }
202
203 XServerLocal *
204 x_server_local_new (void)
205 {
206     XServerLocal *self;
207     gchar hostname[1024], *number, *name;
208     XAuthority *cookie;
209
210     self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
211
212     x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
213
214     gethostname (hostname, 1024);
215     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (self)));
216     cookie = x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
217     x_server_set_authority (X_SERVER (self), cookie);
218     g_free (number);
219     g_object_unref (cookie);
220
221     name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
222     display_server_set_name (DISPLAY_SERVER (self), name);
223     g_free (name);
224
225     return self;
226 }
227
228 void
229 x_server_local_set_command (XServerLocal *server, const gchar *command)
230 {
231     g_return_if_fail (server != NULL);
232     g_free (server->priv->command);
233     server->priv->command = g_strdup (command);
234 }
235
236 void
237 x_server_local_set_vt (XServerLocal *server, gint vt)
238 {
239     g_return_if_fail (server != NULL);
240     if (server->priv->have_vt_ref)
241         vt_unref (server->priv->vt);
242     server->priv->have_vt_ref = FALSE;
243     server->priv->vt = vt;
244     if (vt > 0)
245     {
246         vt_ref (vt);
247         server->priv->have_vt_ref = TRUE;
248     }
249 }
250
251 void
252 x_server_local_set_config (XServerLocal *server, const gchar *path)
253 {
254     g_return_if_fail (server != NULL);
255     g_free (server->priv->config_file);
256     server->priv->config_file = g_strdup (path);
257 }
258
259 void
260 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
261 {
262     g_return_if_fail (server != NULL);
263     g_free (server->priv->layout);
264     server->priv->layout = g_strdup (layout);
265 }
266
267 void
268 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
269 {
270     g_return_if_fail (server != NULL);
271     g_free (server->priv->xdg_seat);
272     server->priv->xdg_seat = g_strdup (xdg_seat);
273 }
274
275 void
276 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
277 {
278     g_return_if_fail (server != NULL);
279     server->priv->allow_tcp = allow_tcp;
280 }
281
282 void
283 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
284 {
285     g_return_if_fail (server != NULL);
286     g_free (server->priv->xdmcp_server);
287     server->priv->xdmcp_server = g_strdup (hostname);
288 }
289
290 const gchar *
291 x_server_local_get_xdmcp_server (XServerLocal *server)
292 {
293     g_return_val_if_fail (server != NULL, 0);
294     return server->priv->xdmcp_server;
295 }
296
297 void
298 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
299 {
300     g_return_if_fail (server != NULL);
301     server->priv->xdmcp_port = port;
302 }
303
304 guint
305 x_server_local_get_xdmcp_port (XServerLocal *server)
306 {
307     g_return_val_if_fail (server != NULL, 0);
308     return server->priv->xdmcp_port;
309 }
310
311 void
312 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
313 {
314     g_return_if_fail (server != NULL);
315     g_free (server->priv->xdmcp_key);
316     server->priv->xdmcp_key = g_strdup (key);
317     x_server_set_authority (X_SERVER (server), NULL);
318 }
319
320 void
321 x_server_local_set_background (XServerLocal *server, const gchar *background)
322 {
323     g_return_if_fail (server != NULL);
324     g_free (server->priv->background);
325     server->priv->background = g_strdup (background);
326 }
327
328 void
329 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
330 {
331     g_return_if_fail (server != NULL);
332     g_free (server->priv->mir_id);
333     server->priv->mir_id = g_strdup (id);
334 }
335
336 const gchar *x_server_local_get_mir_id (XServerLocal *server)
337 {
338     g_return_val_if_fail (server != NULL, NULL);
339     return server->priv->mir_id;
340 }
341
342 void
343 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
344 {
345     g_return_if_fail (server != NULL);
346     g_free (server->priv->mir_socket);
347     server->priv->mir_socket = g_strdup (socket);
348 }
349
350 static gint
351 x_server_local_get_vt (DisplayServer *server)
352 {
353     return X_SERVER_LOCAL (server)->priv->vt;
354 }
355
356 const gchar *
357 x_server_local_get_authority_file_path (XServerLocal *server)
358 {
359     g_return_val_if_fail (server != NULL, 0);
360     return server->priv->authority_file;
361 }
362
363 static gchar *
364 get_absolute_command (const gchar *command)
365 {
366     gchar **tokens;
367     gchar *absolute_binary, *absolute_command = NULL;
368
369     tokens = g_strsplit (command, " ", 2);
370
371     absolute_binary = g_find_program_in_path (tokens[0]);
372     if (absolute_binary)
373     {
374         if (tokens[1])
375             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
376         else
377             absolute_command = g_strdup (absolute_binary);
378     }
379     g_free (absolute_binary);
380
381     g_strfreev (tokens);
382
383     return absolute_command;
384 }
385
386 static void
387 run_cb (Process *process, gpointer user_data)
388 {
389     int fd;
390
391     /* Make input non-blocking */
392     fd = open ("/dev/null", O_RDONLY);
393     dup2 (fd, STDIN_FILENO);
394     close (fd);
395
396     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
397     signal (SIGUSR1, SIG_IGN);
398 }
399
400 static void
401 got_signal_cb (Process *process, int signum, XServerLocal *server)
402 {
403     if (signum == SIGUSR1 && !server->priv->got_signal)
404     {
405         server->priv->got_signal = TRUE;
406         l_debug (server, "Got signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
407
408         // FIXME: Check return value
409         DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
410     }
411 }
412
413 static void
414 stopped_cb (Process *process, XServerLocal *server)
415 {
416     l_debug (server, "X server stopped");
417
418     /* Release VT and display number for re-use */
419     if (server->priv->have_vt_ref)
420     {
421         vt_unref (server->priv->vt);
422         server->priv->have_vt_ref = FALSE;
423     }
424     x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
425
426     if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
427     {
428         l_debug (server, "Removing X server authority %s", server->priv->authority_file);
429
430         g_unlink (server->priv->authority_file);
431
432         g_free (server->priv->authority_file);
433         server->priv->authority_file = NULL;
434     }
435
436     DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
437 }
438
439 static void
440 write_authority_file (XServerLocal *server)
441 {
442     XAuthority *authority;
443     GError *error = NULL;
444
445     authority = x_server_get_authority (X_SERVER (server));
446     if (!authority)
447         return;
448
449     /* Get file to write to if have authority */
450     if (!server->priv->authority_file)
451     {
452         gchar *run_dir, *dir;
453
454         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
455         dir = g_build_filename (run_dir, "root", NULL);
456         g_free (run_dir);
457         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
458             l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
459
460         server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
461         g_free (dir);
462     }
463
464     l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
465
466     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
467     if (error)
468         l_warning (server, "Failed to write authority: %s", error->message);
469     g_clear_error (&error);
470 }
471
472 static gboolean
473 x_server_local_start (DisplayServer *display_server)
474 {
475     XServerLocal *server = X_SERVER_LOCAL (display_server);
476     gboolean result, backup_logs;
477     gchar *filename, *dir, *log_file, *absolute_command;
478     GString *command;
479
480     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
481
482     server->priv->got_signal = FALSE;
483
484     g_return_val_if_fail (server->priv->command != NULL, FALSE);
485
486     server->priv->x_server_process = process_new (run_cb, server);
487     process_set_clear_environment (server->priv->x_server_process, TRUE);
488     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
489     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
490
491     /* Setup logging */
492     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
493     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
494     log_file = g_build_filename (dir, filename, NULL);
495     backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");
496     process_set_log_file (server->priv->x_server_process, log_file, TRUE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
497     l_debug (display_server, "Logging to %s", log_file);
498     g_free (log_file);
499     g_free (filename);
500     g_free (dir);
501
502     absolute_command = get_absolute_command (server->priv->command);
503     if (!absolute_command)
504     {
505         l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
506         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
507         return FALSE;
508     }
509     command = g_string_new (absolute_command);
510     g_free (absolute_command);
511
512     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
513
514     if (server->priv->config_file)
515         g_string_append_printf (command, " -config %s", server->priv->config_file);
516
517     if (server->priv->layout)
518         g_string_append_printf (command, " -layout %s", server->priv->layout);
519
520     if (server->priv->xdg_seat)
521         g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
522
523     write_authority_file (server);
524     if (server->priv->authority_file)
525         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
526
527     /* Setup for running inside Mir */
528     if (server->priv->mir_id)
529         g_string_append_printf (command, " -mir %s", server->priv->mir_id);
530
531     if (server->priv->mir_socket)
532         g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
533
534     /* Connect to a remote server using XDMCP */
535     if (server->priv->xdmcp_server != NULL)
536     {
537         if (server->priv->xdmcp_port != 0)
538             g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
539         g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
540         if (server->priv->xdmcp_key)
541             g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
542     }
543     else if (server->priv->allow_tcp)
544     {
545         if (x_server_local_version_compare (1, 17) >= 0)
546             g_string_append (command, " -listen tcp");
547     }
548     else
549         g_string_append (command, " -nolisten tcp");
550
551     if (server->priv->vt >= 0)
552         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
553
554     if (server->priv->background)
555         g_string_append_printf (command, " -background %s", server->priv->background);
556
557     process_set_command (server->priv->x_server_process, command->str);
558     g_string_free (command, TRUE);
559
560     l_debug (display_server, "Launching X Server");
561
562     /* If running inside another display then pass through those variables */
563     if (g_getenv ("DISPLAY"))
564     {
565         process_set_env (server->priv->x_server_process, "DISPLAY", g_getenv ("DISPLAY"));
566         if (g_getenv ("XAUTHORITY"))
567             process_set_env (server->priv->x_server_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
568         else
569         {
570             gchar *path;
571             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
572             process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
573             g_free (path);
574         }
575     }
576
577     /* Variable required for regression tests */
578     if (g_getenv ("LIGHTDM_TEST_ROOT"))
579     {
580         process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
581         process_set_env (server->priv->x_server_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
582         process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
583     }
584
585     result = process_start (server->priv->x_server_process, FALSE);
586
587     if (result)
588         l_debug (display_server, "Waiting for ready signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
589
590     if (!result)
591         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
592
593     return result;
594 }
595
596 static void
597 x_server_local_stop (DisplayServer *server)
598 {
599     process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
600 }
601
602 static void
603 x_server_local_init (XServerLocal *server)
604 {
605     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_LOCAL_TYPE, XServerLocalPrivate);
606     server->priv->vt = -1;
607     server->priv->command = g_strdup ("X");
608 }
609
610 static void
611 x_server_local_finalize (GObject *object)
612 {
613     XServerLocal *self = X_SERVER_LOCAL (object);
614
615     if (self->priv->x_server_process)
616     {
617         g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
618         g_object_unref (self->priv->x_server_process);
619     }
620     g_free (self->priv->command);
621     g_free (self->priv->config_file);
622     g_free (self->priv->layout);
623     g_free (self->priv->xdg_seat);
624     g_free (self->priv->xdmcp_server);
625     g_free (self->priv->xdmcp_key);
626     g_free (self->priv->mir_id);
627     g_free (self->priv->mir_socket);
628     g_free (self->priv->authority_file);
629     if (self->priv->have_vt_ref)
630         vt_unref (self->priv->vt);
631     g_free (self->priv->background);
632
633     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
634 }
635
636 static void
637 x_server_local_class_init (XServerLocalClass *klass)
638 {
639     GObjectClass *object_class = G_OBJECT_CLASS (klass);
640     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
641
642     display_server_class->get_vt = x_server_local_get_vt;
643     display_server_class->start = x_server_local_start;
644     display_server_class->stop = x_server_local_stop;
645     object_class->finalize = x_server_local_finalize;
646
647     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
648 }