]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-local.c
Remove unused vfunc
[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     /* ID to report to Mir */
61     gchar *mir_id;
62
63     /* Filename of socket Mir is listening on */
64     gchar *mir_socket;
65
66     /* TRUE when received ready signal */
67     gboolean got_signal;
68
69     /* VT to run on */
70     gint vt;
71     gboolean have_vt_ref;
72
73     /* Background to set */
74     gchar *background;
75 };
76
77 G_DEFINE_TYPE (XServerLocal, x_server_local, X_SERVER_TYPE);
78
79 static gchar *version = NULL;
80 static guint version_major = 0, version_minor = 0;
81 static GList *display_numbers = NULL;
82
83 #define XORG_VERSION_PREFIX "X.Org X Server "
84
85 static gchar *
86 find_version (const gchar *line)
87 {
88     if (!g_str_has_prefix (line, XORG_VERSION_PREFIX))
89         return NULL;
90
91     return g_strdup (line + strlen (XORG_VERSION_PREFIX));
92 }
93
94 const gchar *
95 x_server_local_get_version (void)
96 {
97     gchar *stderr_text;
98     gint exit_status;
99     gchar **tokens;
100     guint n_tokens;
101
102     if (version)
103         return version;
104
105     if (!g_spawn_command_line_sync ("X -version", NULL, &stderr_text, &exit_status, NULL))
106         return NULL;
107     if (exit_status == EXIT_SUCCESS)
108     {
109         gchar **lines;
110         int i;
111
112         lines = g_strsplit (stderr_text, "\n", -1);
113         for (i = 0; lines[i] && !version; i++)
114             version = find_version (lines[i]);
115         g_strfreev (lines);
116     }
117     g_free (stderr_text);
118
119     tokens = g_strsplit (version, ".", 3);
120     n_tokens = g_strv_length (tokens);
121     version_major = n_tokens > 0 ? atoi (tokens[0]) : 0;
122     version_minor = n_tokens > 1 ? atoi (tokens[1]) : 0;
123     g_strfreev (tokens);
124
125     return version;
126 }
127
128 gint
129 x_server_local_version_compare (guint major, guint minor)
130 {
131     x_server_local_get_version ();
132     if (major == version_major)
133         return version_minor - minor;
134     else
135         return version_major - major;
136 }
137
138 static gboolean
139 display_number_in_use (guint display_number)
140 {
141     GList *link;
142     gchar *path;
143     gboolean in_use;
144     gchar *data;
145
146     /* See if we know we are managing a server with that number */
147     for (link = display_numbers; link; link = link->next)
148     {
149         guint number = GPOINTER_TO_UINT (link->data);
150         if (number == display_number)
151             return TRUE;
152     }
153
154     /* See if an X server that we don't know of has a lock on that number */
155     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
156     in_use = g_file_test (path, G_FILE_TEST_EXISTS);
157
158     /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
159     if (in_use && g_file_get_contents (path, &data, NULL, NULL))
160     {
161         int pid;
162
163         pid = atoi (g_strstrip (data));
164         g_free (data);
165
166         errno = 0;
167         if (pid < 0 || (kill (pid, 0) < 0 && errno == ESRCH))
168             in_use = FALSE;
169     }
170
171     g_free (path);
172
173     return in_use;
174 }
175
176 guint
177 x_server_local_get_unused_display_number (void)
178 {
179     guint number;
180
181     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
182     while (display_number_in_use (number))
183         number++;
184
185     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
186
187     return number;
188 }
189
190 void
191 x_server_local_release_display_number (guint display_number)
192 {
193     GList *link;
194     for (link = display_numbers; link; link = link->next)
195     {
196         guint number = GPOINTER_TO_UINT (link->data);
197         if (number == display_number)
198         {
199             display_numbers = g_list_delete_link (display_numbers, link);
200             return;
201         }
202     }
203 }
204
205 XServerLocal *
206 x_server_local_new (void)
207 {
208     XServerLocal *self;
209     gchar *name;
210
211     self = g_object_new (X_SERVER_LOCAL_TYPE, NULL);
212
213     name = g_strdup_printf ("x-%d", x_server_get_display_number (X_SERVER (self)));
214     display_server_set_name (DISPLAY_SERVER (self), name);
215     g_free (name);
216
217     return self;
218 }
219
220 void
221 x_server_local_set_command (XServerLocal *server, const gchar *command)
222 {
223     g_return_if_fail (server != NULL);
224     g_free (server->priv->command);
225     server->priv->command = g_strdup (command);
226 }
227
228 void
229 x_server_local_set_vt (XServerLocal *server, gint vt)
230 {
231     g_return_if_fail (server != NULL);
232     if (server->priv->have_vt_ref)
233         vt_unref (server->priv->vt);
234     server->priv->have_vt_ref = FALSE;
235     server->priv->vt = vt;
236     if (vt > 0)
237     {
238         vt_ref (vt);
239         server->priv->have_vt_ref = TRUE;
240     }
241 }
242
243 void
244 x_server_local_set_config (XServerLocal *server, const gchar *path)
245 {
246     g_return_if_fail (server != NULL);
247     g_free (server->priv->config_file);
248     server->priv->config_file = g_strdup (path);
249 }
250
251 void
252 x_server_local_set_layout (XServerLocal *server, const gchar *layout)
253 {
254     g_return_if_fail (server != NULL);
255     g_free (server->priv->layout);
256     server->priv->layout = g_strdup (layout);
257 }
258
259 void
260 x_server_local_set_xdg_seat (XServerLocal *server, const gchar *xdg_seat)
261 {
262     g_return_if_fail (server != NULL);
263     g_free (server->priv->xdg_seat);
264     server->priv->xdg_seat = g_strdup (xdg_seat);
265 }
266
267 void
268 x_server_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
269 {
270     g_return_if_fail (server != NULL);
271     server->priv->allow_tcp = allow_tcp;
272 }
273
274 void
275 x_server_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
276 {
277     g_return_if_fail (server != NULL);
278     g_free (server->priv->xdmcp_server);
279     server->priv->xdmcp_server = g_strdup (hostname);
280 }
281
282 const gchar *
283 x_server_local_get_xdmcp_server (XServerLocal *server)
284 {
285     g_return_val_if_fail (server != NULL, 0);
286     return server->priv->xdmcp_server;
287 }
288
289 void
290 x_server_local_set_xdmcp_port (XServerLocal *server, guint port)
291 {
292     g_return_if_fail (server != NULL);
293     server->priv->xdmcp_port = port;
294 }
295
296 guint
297 x_server_local_get_xdmcp_port (XServerLocal *server)
298 {
299     g_return_val_if_fail (server != NULL, 0);
300     return server->priv->xdmcp_port;
301 }
302
303 void
304 x_server_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
305 {
306     g_return_if_fail (server != NULL);
307     g_free (server->priv->xdmcp_key);
308     server->priv->xdmcp_key = g_strdup (key);
309     x_server_set_authority (X_SERVER (server), NULL);
310 }
311
312 void
313 x_server_local_set_background (XServerLocal *server, const gchar *background)
314 {
315     g_return_if_fail (server != NULL);
316     g_free (server->priv->background);
317     server->priv->background = g_strdup (background);
318 }
319
320 void
321 x_server_local_set_mir_id (XServerLocal *server, const gchar *id)
322 {
323     g_return_if_fail (server != NULL);
324     g_free (server->priv->mir_id);
325     server->priv->mir_id = g_strdup (id);
326 }
327
328 const gchar *x_server_local_get_mir_id (XServerLocal *server)
329 {
330     g_return_val_if_fail (server != NULL, NULL);
331     return server->priv->mir_id;
332 }
333
334 void
335 x_server_local_set_mir_socket (XServerLocal *server, const gchar *socket)
336 {
337     g_return_if_fail (server != NULL);
338     g_free (server->priv->mir_socket);
339     server->priv->mir_socket = g_strdup (socket);
340 }
341
342 static guint
343 x_server_local_get_display_number (XServer *server)
344 {
345     return X_SERVER_LOCAL (server)->priv->display_number;
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", server->priv->display_number);
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 (server->priv->display_number);
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", server->priv->display_number);
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", server->priv->display_number);
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     server->priv->display_number = x_server_local_get_unused_display_number ();
607 }
608
609 static void
610 x_server_local_finalize (GObject *object)
611 {
612     XServerLocal *self = X_SERVER_LOCAL (object);
613
614     if (self->priv->x_server_process)
615     {
616         g_signal_handlers_disconnect_matched (self->priv->x_server_process, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, self);
617         g_object_unref (self->priv->x_server_process);
618     }
619     g_free (self->priv->command);
620     g_free (self->priv->config_file);
621     g_free (self->priv->layout);
622     g_free (self->priv->xdg_seat);
623     g_free (self->priv->xdmcp_server);
624     g_free (self->priv->xdmcp_key);
625     g_free (self->priv->mir_id);
626     g_free (self->priv->mir_socket);
627     g_free (self->priv->authority_file);
628     if (self->priv->have_vt_ref)
629         vt_unref (self->priv->vt);
630     g_free (self->priv->background);
631
632     G_OBJECT_CLASS (x_server_local_parent_class)->finalize (object);
633 }
634
635 static void
636 x_server_local_class_init (XServerLocalClass *klass)
637 {
638     GObjectClass *object_class = G_OBJECT_CLASS (klass);
639     XServerClass *x_server_class = X_SERVER_CLASS (klass);
640     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
641
642     x_server_class->get_display_number = x_server_local_get_display_number;
643     display_server_class->get_vt = x_server_local_get_vt;
644     display_server_class->start = x_server_local_start;
645     display_server_class->stop = x_server_local_stop;
646     object_class->finalize = x_server_local_finalize;
647
648     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
649 }