]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
Make x_authority_new_local_cookie to de-duplicate code
[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     /* Config file to use */
34     gchar *config_file;
35
36     /* Server layout to use */
37     gchar *layout;
38
39     /* Value for -seat argument */
40     gchar *xdg_seat;
41
42     /* TRUE if TCP/IP connections are allowed */
43     gboolean allow_tcp;
44
45     /* Authority file */
46     gchar *authority_file;
47
48     /* XDMCP server to connect to */
49     gchar *xdmcp_server;
50
51     /* XDMCP port to connect to */
52     guint xdmcp_port;
53
54     /* XDMCP key to use */
55     gchar *xdmcp_key;
56
57     /* ID to report to Mir */
58     gchar *mir_id;
59
60     /* Filename of socket Mir is listening on */
61     gchar *mir_socket;
62
63     /* TRUE when received ready signal */
64     gboolean got_signal;
65
66     /* VT to run on */
67     gint vt;
68     gboolean have_vt_ref;
69
70     /* Background to set */
71     gchar *background;
72 };
73
74 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
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     XServerLocal *self;
206     gchar *number, *name;
207     XAuthority *cookie;
208
209     self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
210
211     x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
212
213     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (self)));
214     cookie = x_authority_new_local_cookie (number);
215     x_server_set_authority (X_SERVER (self), cookie);
216     g_free (number);
217     g_object_unref (cookie);
218
219     name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
220     display_server_set_name (DISPLAY_SERVER (self), name);
221     g_free (name);
222
223     return self;
224 }
225
226 void
227 x_server_local_set_command (XServerLocal *server, const gchar *command)
228 {
229     g_return_if_fail (server != NULL);
230     g_free (server->priv->command);
231     server->priv->command = g_strdup (command);
232 }
233
234 void
235 x_server_local_set_vt (XServerLocal *server, gint vt)
236 {
237     g_return_if_fail (server != NULL);
238     if (server->priv->have_vt_ref)
239         vt_unref (server->priv->vt);
240     server->priv->have_vt_ref = FALSE;
241     server->priv->vt = vt;
242     if (vt > 0)
243     {
244         vt_ref (vt);
245         server->priv->have_vt_ref = TRUE;
246     }
247 }
248
249 void
250 x_server_local_set_config (XServerLocal *server, const gchar *path)
251 {
252     g_return_if_fail (server != NULL);
253     g_free (server->priv->config_file);
254     server->priv->config_file = g_strdup (path);
255 }
256
257 void
258 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
259 {
260     g_return_if_fail (server != NULL);
261     g_free (server->priv->layout);
262     server->priv->layout = g_strdup (layout);
263 }
264
265 void
266 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
267 {
268     g_return_if_fail (server != NULL);
269     g_free (server->priv->xdg_seat);
270     server->priv->xdg_seat = g_strdup (xdg_seat);
271 }
272
273 void
274 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
275 {
276     g_return_if_fail (server != NULL);
277     server->priv->allow_tcp = allow_tcp;
278 }
279
280 void
281 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
282 {
283     g_return_if_fail (server != NULL);
284     g_free (server->priv->xdmcp_server);
285     server->priv->xdmcp_server = g_strdup (hostname);
286 }
287
288 const gchar *
289 x_server_local_get_xdmcp_server (XServerLocal *server)
290 {
291     g_return_val_if_fail (server != NULL, 0);
292     return server->priv->xdmcp_server;
293 }
294
295 void
296 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
297 {
298     g_return_if_fail (server != NULL);
299     server->priv->xdmcp_port = port;
300 }
301
302 guint
303 x_server_local_get_xdmcp_port (XServerLocal *server)
304 {
305     g_return_val_if_fail (server != NULL, 0);
306     return server->priv->xdmcp_port;
307 }
308
309 void
310 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
311 {
312     g_return_if_fail (server != NULL);
313     g_free (server->priv->xdmcp_key);
314     server->priv->xdmcp_key = g_strdup (key);
315     x_server_set_authority (X_SERVER (server), NULL);
316 }
317
318 void
319 x_server_local_set_background (XServerLocal *server, const gchar *background)
320 {
321     g_return_if_fail (server != NULL);
322     g_free (server->priv->background);
323     server->priv->background = g_strdup (background);
324 }
325
326 void
327 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
328 {
329     g_return_if_fail (server != NULL);
330     g_free (server->priv->mir_id);
331     server->priv->mir_id = g_strdup (id);
332 }
333
334 const gchar *x_server_local_get_mir_id (XServerLocal *server)
335 {
336     g_return_val_if_fail (server != NULL, NULL);
337     return server->priv->mir_id;
338 }
339
340 void
341 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
342 {
343     g_return_if_fail (server != NULL);
344     g_free (server->priv->mir_socket);
345     server->priv->mir_socket = g_strdup (socket);
346 }
347
348 static gint
349 x_server_local_get_vt (DisplayServer *server)
350 {
351     return X_SERVER_LOCAL (server)->priv->vt;
352 }
353
354 const gchar *
355 x_server_local_get_authority_file_path (XServerLocal *server)
356 {
357     g_return_val_if_fail (server != NULL, 0);
358     return server->priv->authority_file;
359 }
360
361 static gchar *
362 get_absolute_command (const gchar *command)
363 {
364     gchar **tokens;
365     gchar *absolute_binary, *absolute_command = NULL;
366
367     tokens = g_strsplit (command, " ", 2);
368
369     absolute_binary = g_find_program_in_path (tokens[0]);
370     if (absolute_binary)
371     {
372         if (tokens[1])
373             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
374         else
375             absolute_command = g_strdup (absolute_binary);
376     }
377     g_free (absolute_binary);
378
379     g_strfreev (tokens);
380
381     return absolute_command;
382 }
383
384 static void
385 run_cb (Process *process, gpointer user_data)
386 {
387     int fd;
388
389     /* Make input non-blocking */
390     fd = open ("/dev/null", O_RDONLY);
391     dup2 (fd, STDIN_FILENO);
392     close (fd);
393
394     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
395     signal (SIGUSR1, SIG_IGN);
396 }
397
398 static void
399 got_signal_cb (Process *process, int signum, XServerLocal *server)
400 {
401     if (signum == SIGUSR1 && !server->priv->got_signal)
402     {
403         server->priv->got_signal = TRUE;
404         l_debug (server, "Got signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
405
406         // FIXME: Check return value
407         DISPLAY_SERVER_CLASS (x_server_local_parent_class)->start (DISPLAY_SERVER (server));
408     }
409 }
410
411 static void
412 stopped_cb (Process *process, XServerLocal *server)
413 {
414     l_debug (server, "X server stopped");
415
416     /* Release VT and display number for re-use */
417     if (server->priv->have_vt_ref)
418     {
419         vt_unref (server->priv->vt);
420         server->priv->have_vt_ref = FALSE;
421     }
422     x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
423
424     if (x_server_get_authority (X_SERVER (server)) && server->priv->authority_file)
425     {
426         l_debug (server, "Removing X server authority %s", server->priv->authority_file);
427
428         g_unlink (server->priv->authority_file);
429
430         g_free (server->priv->authority_file);
431         server->priv->authority_file = NULL;
432     }
433
434     DISPLAY_SERVER_CLASS (x_server_local_parent_class)->stop (DISPLAY_SERVER (server));
435 }
436
437 static void
438 write_authority_file (XServerLocal *server)
439 {
440     XAuthority *authority;
441     GError *error = NULL;
442
443     authority = x_server_get_authority (X_SERVER (server));
444     if (!authority)
445         return;
446
447     /* Get file to write to if have authority */
448     if (!server->priv->authority_file)
449     {
450         gchar *run_dir, *dir;
451
452         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
453         dir = g_build_filename (run_dir, "root", NULL);
454         g_free (run_dir);
455         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
456             l_warning (server, "Failed to make authority directory %s: %s", dir, strerror (errno));
457
458         server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
459         g_free (dir);
460     }
461
462     l_debug (server, "Writing X server authority to %s", server->priv->authority_file);
463
464     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
465     if (error)
466         l_warning (server, "Failed to write authority: %s", error->message);
467     g_clear_error (&error);
468 }
469
470 static gboolean
471 x_server_local_start (DisplayServer *display_server)
472 {
473     XServerLocal *server = X_SERVER_LOCAL (display_server);
474     gboolean result, backup_logs;
475     gchar *filename, *dir, *log_file, *absolute_command;
476     GString *command;
477
478     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
479
480     server->priv->got_signal = FALSE;
481
482     g_return_val_if_fail (server->priv->command != NULL, FALSE);
483
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);
488
489     /* Setup logging */
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);
496     g_free (log_file);
497     g_free (filename);
498     g_free (dir);
499
500     absolute_command = get_absolute_command (server->priv->command);
501     if (!absolute_command)
502     {
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));
505         return FALSE;
506     }
507     command = g_string_new (absolute_command);
508     g_free (absolute_command);
509
510     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
511
512     if (server->priv->config_file)
513         g_string_append_printf (command, " -config %s", server->priv->config_file);
514
515     if (server->priv->layout)
516         g_string_append_printf (command, " -layout %s", server->priv->layout);
517
518     if (server->priv->xdg_seat)
519         g_string_append_printf (command, " -seat %s", server->priv->xdg_seat);
520
521     write_authority_file (server);
522     if (server->priv->authority_file)
523         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
524
525     /* Setup for running inside Mir */
526     if (server->priv->mir_id)
527         g_string_append_printf (command, " -mir %s", server->priv->mir_id);
528
529     if (server->priv->mir_socket)
530         g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
531
532     /* Connect to a remote server using XDMCP */
533     if (server->priv->xdmcp_server != NULL)
534     {
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);
540     }
541     else if (server->priv->allow_tcp)
542     {
543         if (x_server_local_version_compare (1, 17) >= 0)
544             g_string_append (command, " -listen tcp");
545     }
546     else
547         g_string_append (command, " -nolisten tcp");
548
549     if (server->priv->vt >= 0)
550         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
551
552     if (server->priv->background)
553         g_string_append_printf (command, " -background %s", server->priv->background);
554
555     process_set_command (server->priv->x_server_process, command->str);
556     g_string_free (command, TRUE);
557
558     l_debug (display_server, "Launching X Server");
559
560     /* If running inside another display then pass through those variables */
561     if (g_getenv ("DISPLAY"))
562     {
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"));
566         else
567         {
568             gchar *path;
569             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
570             process_set_env (server->priv->x_server_process, "XAUTHORITY", path);
571             g_free (path);
572         }
573     }
574
575     /* Variable required for regression tests */
576     if (g_getenv ("LIGHTDM_TEST_ROOT"))
577     {
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"));
581     }
582
583     result = process_start (server->priv->x_server_process, FALSE);
584
585     if (result)
586         l_debug (display_server, "Waiting for ready signal from X server :%d", x_server_get_display_number (X_SERVER (server)));
587
588     if (!result)
589         stopped_cb (server->priv->x_server_process, X_SERVER_LOCAL (server));
590
591     return result;
592 }
593
594 static void
595 x_server_local_stop (DisplayServer *server)
596 {
597     process_stop (X_SERVER_LOCAL (server)->priv->x_server_process);
598 }
599
600 static void
601 x_server_local_init (XServerLocal *server)
602 {
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 }
607
608 static void
609 x_server_local_finalize (GObject *object)
610 {
611     XServerLocal *self = X_SERVER_LOCAL (object);
612
613     if (self->priv->x_server_process)
614     {
615         g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
616         g_object_unref (self->priv->x_server_process);
617     }
618     g_free (self->priv->command);
619     g_free (self->priv->config_file);
620     g_free (self->priv->layout);
621     g_free (self->priv->xdg_seat);
622     g_free (self->priv->xdmcp_server);
623     g_free (self->priv->xdmcp_key);
624     g_free (self->priv->mir_id);
625     g_free (self->priv->mir_socket);
626     g_free (self->priv->authority_file);
627     if (self->priv->have_vt_ref)
628         vt_unref (self->priv->vt);
629     g_free (self->priv->background);
630
631     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
632 }
633
634 static void
635 x_server_local_class_init (XServerLocalClass *klass)
636 {
637     GObjectClass *object_class = G_OBJECT_CLASS (klass);
638     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
639
640     display_server_class->get_vt = x_server_local_get_vt;
641     display_server_class->start = x_server_local_start;
642     display_server_class->stop = x_server_local_stop;
643     object_class->finalize = x_server_local_finalize;
644
645     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
646 }