]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/xserver-local.c
Merge with trunk
[sojka/lightdm.git] / src / xserver-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 "xserver-local.h"
22 #include "configuration.h"
23 #include "process.h"
24 #include "vt.h"
25 #include "plymouth.h"
26
27 struct XServerLocalPrivate
28 {
29     /* X server process */
30     Process *xserver_process;
31   
32     /* File to log to */
33     gchar *log_file;    
34
35     /* Command to run the X server */
36     gchar *command;
37
38     /* Config file to use */
39     gchar *config_file;
40
41     /* Server layout to use */
42     gchar *layout;
43
44     /* TRUE if TCP/IP connections are allowed */
45     gboolean allow_tcp;
46
47     /* Authority file */
48     gchar *authority_file;
49
50     /* XDMCP server to connect to */
51     gchar *xdmcp_server;
52
53     /* XDMCP port to connect to */
54     guint xdmcp_port;
55
56     /* XDMCP key to use */
57     gchar *xdmcp_key;
58
59     /* ID to report to Mir */
60     gchar *mir_id;
61
62     /* Filename of socket Mir is listening on */
63     gchar *mir_socket;
64
65     /* TRUE when received ready signal */
66     gboolean got_signal;
67
68     /* VT to run on */
69     gint vt;
70   
71     /* TRUE if holding a reference to the VT */
72     gboolean have_vt_ref;
73   
74     /* TRUE if replacing Plymouth */
75     gboolean replacing_plymouth;
76 };
77
78 G_DEFINE_TYPE (XServerLocal, xserver_local, XSERVER_TYPE);
79
80 static GList *display_numbers = NULL;
81
82 static gboolean
83 display_number_in_use (guint display_number)
84 {
85     GList *link;
86     gchar *path;
87     gboolean exists;
88     gboolean stale = TRUE;
89     gchar *data;
90
91     /* See if we know we are managing a server with that number */
92     for (link = display_numbers; link; link = link->next)
93     {
94         guint number = GPOINTER_TO_UINT (link->data);
95         if (number == display_number)
96             return TRUE;
97     }
98
99     /* See if an X server that we don't know of has a lock on that number */
100     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
101     exists = g_file_test (path, G_FILE_TEST_EXISTS);
102     g_free (path);
103     if (!exists)
104         return FALSE;
105
106     /* See if that lock file is valid, ignore it if the contents are invalid or the process doesn't exist */
107     if (g_file_get_contents (path, &data, NULL, NULL))
108     {
109         int pid;
110
111         pid = atoi (g_strstrip (data));
112         if (pid <= 0)
113             return FALSE;
114
115         errno = 0;
116         if (kill (pid, 0) < 0 && errno == ESRCH)
117             return FALSE;
118     }
119   
120     return TRUE;
121 }
122
123 guint
124 xserver_local_get_unused_display_number (void)
125 {
126     guint number;
127
128     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
129     while (display_number_in_use (number))
130         number++;
131
132     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
133
134     return number;
135 }
136
137 void
138 xserver_local_release_display_number (guint display_number)
139 {
140     GList *link;
141     for (link = display_numbers; link; link = link->next)
142     {
143         guint number = GPOINTER_TO_UINT (link->data);
144         if (number == display_number)
145         {
146             display_numbers = g_list_remove_link (display_numbers, link);
147             return;
148         }
149     }
150 }
151
152 XServerLocal *
153 xserver_local_new (void)
154 {
155     XServerLocal *self = g_object_new (XSERVER_LOCAL_TYPE, NULL);
156     gchar *name;
157
158     xserver_set_display_number (XSERVER (self), xserver_local_get_unused_display_number ());
159
160     name = g_strdup_printf ("x-%d", xserver_get_display_number (XSERVER (self)));
161     display_server_set_name (DISPLAY_SERVER (self), name);
162     g_free (name);
163
164     /* Replace Plymouth if it is running */
165     if (plymouth_get_is_active () && plymouth_has_active_vt ())
166     {
167         gint active_vt = vt_get_active ();
168         if (active_vt >= vt_get_min ())
169         {
170             g_debug ("X server %s will replace Plymouth", xserver_get_address (XSERVER (self)));
171             self->priv->replacing_plymouth = TRUE;
172             self->priv->vt = active_vt;
173             plymouth_deactivate ();
174         }
175         else
176             g_debug ("Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
177     }
178     if (self->priv->vt < 0)
179         self->priv->vt = vt_get_unused ();
180     if (self->priv->vt >= 0)
181     {
182         vt_ref (self->priv->vt);
183         self->priv->have_vt_ref = TRUE;
184     }
185
186     return self;
187 }
188
189 void
190 xserver_local_set_command (XServerLocal *server, const gchar *command)
191 {
192     g_return_if_fail (server != NULL);
193     g_free (server->priv->command);
194     server->priv->command = g_strdup (command);
195 }
196
197 void
198 xserver_local_set_config (XServerLocal *server, const gchar *path)
199 {
200     g_return_if_fail (server != NULL);
201     g_free (server->priv->config_file);
202     server->priv->config_file = g_strdup (path);
203 }
204
205 void
206 xserver_local_set_layout (XServerLocal *server, const gchar *layout)
207 {
208     g_return_if_fail (server != NULL);
209     g_free (server->priv->layout);
210     server->priv->layout = g_strdup (layout);
211 }
212
213 void
214 xserver_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
215 {
216     g_return_if_fail (server != NULL);
217     server->priv->allow_tcp = allow_tcp;
218 }
219
220 void
221 xserver_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
222 {
223     g_return_if_fail (server != NULL);
224     g_free (server->priv->xdmcp_server);
225     server->priv->xdmcp_server = g_strdup (hostname);
226     display_server_set_start_local_sessions (DISPLAY_SERVER (server), hostname == NULL);
227 }
228
229 const gchar *
230 xserver_local_get_xdmcp_server (XServerLocal *server)
231 {
232     g_return_val_if_fail (server != NULL, 0);
233     return server->priv->xdmcp_server;
234 }
235
236 void
237 xserver_local_set_xdmcp_port (XServerLocal *server, guint port)
238 {
239     g_return_if_fail (server != NULL);
240     server->priv->xdmcp_port = port;
241 }
242
243 guint
244 xserver_local_get_xdmcp_port (XServerLocal *server)
245 {
246     g_return_val_if_fail (server != NULL, 0);
247     return server->priv->xdmcp_port;
248 }
249
250 void
251 xserver_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
252 {
253     g_return_if_fail (server != NULL);
254     g_free (server->priv->xdmcp_key);
255     server->priv->xdmcp_key = g_strdup (key);
256 }
257
258 void
259 xserver_local_set_mir_id (XServerLocal *server, const gchar *id)
260 {
261     g_return_if_fail (server != NULL);
262     g_free (server->priv->mir_id);
263     server->priv->mir_id = g_strdup (id);
264
265     if (server->priv->have_vt_ref)
266     {
267         vt_unref (server->priv->vt);
268         server->priv->have_vt_ref = FALSE;
269     }
270     server->priv->vt = -1;
271 }
272
273 const gchar *xserver_local_get_mir_id (XServerLocal *server)
274 {
275     g_return_val_if_fail (server != NULL, NULL);
276     return server->priv->mir_id; 
277 }
278
279 void
280 xserver_local_set_mir_socket (XServerLocal *server, const gchar *socket)
281 {
282     g_return_if_fail (server != NULL);
283     g_free (server->priv->mir_socket);
284     server->priv->mir_socket = g_strdup (socket);
285 }
286
287 gint
288 xserver_local_get_vt (XServerLocal *server)
289 {
290     g_return_val_if_fail (server != NULL, 0);
291     return server->priv->vt;
292 }
293
294 const gchar *
295 xserver_local_get_authority_file_path (XServerLocal *server)
296 {
297     g_return_val_if_fail (server != NULL, 0);
298     return server->priv->authority_file;
299 }
300
301 static gchar *
302 get_absolute_command (const gchar *command)
303 {
304     gchar **tokens;
305     gchar *absolute_binary, *absolute_command = NULL;
306
307     tokens = g_strsplit (command, " ", 2);
308
309     absolute_binary = g_find_program_in_path (tokens[0]);
310     if (absolute_binary)
311     {
312         if (tokens[1])
313             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
314         else
315             absolute_command = g_strdup (absolute_binary);
316     }
317     g_free (absolute_binary);
318
319     g_strfreev (tokens);
320
321     return absolute_command;
322 }
323
324 static void
325 run_cb (Process *process, XServerLocal *server)
326 {
327     int fd;
328
329     /* Make input non-blocking */
330     fd = open ("/dev/null", O_RDONLY);
331     dup2 (fd, STDIN_FILENO);
332     close (fd);
333
334     /* Redirect output to logfile */
335     if (server->priv->log_file)
336     {
337          int fd;
338
339          fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
340          if (fd < 0)
341              g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
342          else
343          {
344              dup2 (fd, STDOUT_FILENO);
345              dup2 (fd, STDERR_FILENO);
346              close (fd);
347          }
348     }
349
350     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
351     signal (SIGUSR1, SIG_IGN);
352 }
353
354 static void
355 got_signal_cb (Process *process, int signum, XServerLocal *server)
356 {
357     if (signum == SIGUSR1 && !server->priv->got_signal)
358     {
359         server->priv->got_signal = TRUE;
360         g_debug ("Got signal from X server :%d", xserver_get_display_number (XSERVER (server)));
361
362         if (server->priv->replacing_plymouth)
363         {
364             g_debug ("Stopping Plymouth, X server is ready");
365             server->priv->replacing_plymouth = FALSE;
366             plymouth_quit (TRUE);
367         }
368
369         // FIXME: Check return value
370         DISPLAY_SERVER_CLASS (xserver_local_parent_class)->start (DISPLAY_SERVER (server));
371     }
372 }
373
374 static void
375 stopped_cb (Process *process, XServerLocal *server)
376 {
377     g_debug ("X server stopped");
378
379     xserver_local_release_display_number (xserver_get_display_number (XSERVER (server)));
380   
381     if (xserver_get_authority (XSERVER (server)) && server->priv->authority_file)
382     {
383         g_debug ("Removing X server authority %s", server->priv->authority_file);
384
385         g_unlink (server->priv->authority_file);
386
387         g_free (server->priv->authority_file);
388         server->priv->authority_file = NULL;
389     }
390
391     if (server->priv->have_vt_ref)
392     {
393         vt_unref (server->priv->vt);
394         server->priv->have_vt_ref = FALSE;
395     }  
396
397     if (server->priv->replacing_plymouth && plymouth_get_is_running ())
398     {
399         g_debug ("Stopping Plymouth, X server failed to start");
400         server->priv->replacing_plymouth = FALSE;
401         plymouth_quit (FALSE);
402     }
403
404     DISPLAY_SERVER_CLASS (xserver_local_parent_class)->stop (DISPLAY_SERVER (server));
405 }
406
407 static void
408 write_authority_file (XServerLocal *server)
409 {
410     XAuthority *authority;
411     GError *error = NULL;
412
413     authority = xserver_get_authority (XSERVER (server));
414     if (!authority)
415         return;
416
417     /* Get file to write to if have authority */
418     if (!server->priv->authority_file)
419     {
420         gchar *run_dir, *dir;
421       
422         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
423         dir = g_build_filename (run_dir, "root", NULL);
424         g_free (run_dir);
425         if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
426             g_warning ("Failed to make authority directory %s: %s", dir, strerror (errno));
427
428         server->priv->authority_file = g_build_filename (dir, xserver_get_address (XSERVER (server)), NULL);
429         g_free (dir);
430     }
431
432     g_debug ("Writing X server authority to %s", server->priv->authority_file);
433
434     xauth_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
435     if (error)
436         g_warning ("Failed to write authority: %s", error->message);
437     g_clear_error (&error);
438 }
439
440 static gboolean
441 xserver_local_start (DisplayServer *display_server)
442 {
443     XServerLocal *server = XSERVER_LOCAL (display_server);
444     gboolean result;
445     gchar *filename, *dir, *absolute_command;
446     gchar hostname[1024], *number;
447     GString *command;
448
449     g_return_val_if_fail (server->priv->xserver_process == NULL, FALSE);
450
451     server->priv->got_signal = FALSE;
452
453     g_return_val_if_fail (server->priv->command != NULL, FALSE);
454
455     server->priv->xserver_process = process_new ();
456     process_set_clear_environment (server->priv->xserver_process, TRUE);
457     g_signal_connect (server->priv->xserver_process, "run", G_CALLBACK (run_cb), server);  
458     g_signal_connect (server->priv->xserver_process, "got-signal", G_CALLBACK (got_signal_cb), server);
459     g_signal_connect (server->priv->xserver_process, "stopped", G_CALLBACK (stopped_cb), server);
460
461     /* Setup logging */
462     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
463     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
464     server->priv->log_file = g_build_filename (dir, filename, NULL);
465     g_debug ("Logging to %s", server->priv->log_file);
466     g_free (filename);
467     g_free (dir);
468
469     absolute_command = get_absolute_command (server->priv->command);
470     if (!absolute_command)
471     {
472         g_debug ("Can't launch X server %s, not found in path", server->priv->command);
473         stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
474         return FALSE;
475     }
476     command = g_string_new (absolute_command);
477     g_free (absolute_command);
478
479     g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server)));
480
481     if (server->priv->config_file)
482         g_string_append_printf (command, " -config %s", server->priv->config_file);
483
484     if (server->priv->layout)
485         g_string_append_printf (command, " -layout %s", server->priv->layout);
486
487     gethostname (hostname, 1024);
488     number = g_strdup_printf ("%d", xserver_get_display_number (XSERVER (server)));
489     if (!server->priv->xdmcp_key)
490         xserver_set_authority (XSERVER (server), xauth_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number));
491     g_free (number);
492     write_authority_file (server);
493     if (server->priv->authority_file)
494         g_string_append_printf (command, " -auth %s", server->priv->authority_file);
495
496     /* Setup for running inside Mir */
497     if (server->priv->mir_id)
498         g_string_append_printf (command, " -mir %s", server->priv->mir_id);
499
500     if (server->priv->mir_socket)
501         g_string_append_printf (command, " -mirSocket %s", server->priv->mir_socket);
502
503     /* Connect to a remote server using XDMCP */
504     if (server->priv->xdmcp_server != NULL)
505     {
506         if (server->priv->xdmcp_port != 0)
507             g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
508         g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
509         if (server->priv->xdmcp_key)
510             g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
511     }
512     else if (!server->priv->allow_tcp)
513         g_string_append (command, " -nolisten tcp");
514
515     if (server->priv->vt >= 0)
516         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
517
518     if (server->priv->replacing_plymouth)
519         g_string_append (command, " -background none");
520     process_set_command (server->priv->xserver_process, command->str);
521     g_string_free (command, TRUE);
522
523     g_debug ("Launching X Server");
524
525     /* If running inside another display then pass through those variables */
526     if (g_getenv ("DISPLAY"))
527     {
528         process_set_env (server->priv->xserver_process, "DISPLAY", g_getenv ("DISPLAY"));
529         if (g_getenv ("XAUTHORITY"))
530             process_set_env (server->priv->xserver_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
531         else
532         {
533             gchar *path;
534             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
535             process_set_env (server->priv->xserver_process, "XAUTHORITY", path);
536             g_free (path);
537         }
538     }
539
540     /* Variable required for regression tests */
541     if (g_getenv ("LIGHTDM_TEST_ROOT"))
542     {
543         process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
544         process_set_env (server->priv->xserver_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
545         process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
546     }
547
548     result = process_start (server->priv->xserver_process, FALSE);
549
550     if (result)
551         g_debug ("Waiting for ready signal from X server :%d", xserver_get_display_number (XSERVER (server)));
552
553     if (!result)
554         stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
555
556     return result;
557 }
558  
559 static void
560 xserver_local_stop (DisplayServer *server)
561 {
562     process_stop (XSERVER_LOCAL (server)->priv->xserver_process);
563 }
564
565 static void
566 xserver_local_init (XServerLocal *server)
567 {
568     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XSERVER_LOCAL_TYPE, XServerLocalPrivate);
569     server->priv->vt = -1;
570     server->priv->command = g_strdup ("X");
571 }
572
573 static void
574 xserver_local_finalize (GObject *object)
575 {
576     XServerLocal *self;
577
578     self = XSERVER_LOCAL (object);  
579
580     if (self->priv->xserver_process)
581         g_object_unref (self->priv->xserver_process);
582     g_free (self->priv->log_file);
583     g_free (self->priv->command);
584     g_free (self->priv->config_file);
585     g_free (self->priv->layout);
586     g_free (self->priv->xdmcp_server);
587     g_free (self->priv->xdmcp_key);
588     g_free (self->priv->mir_id);
589     g_free (self->priv->mir_socket);
590     g_free (self->priv->authority_file);
591     if (self->priv->have_vt_ref)
592         vt_unref (self->priv->vt);
593
594     G_OBJECT_CLASS (xserver_local_parent_class)->finalize (object);
595 }
596
597 static void
598 xserver_local_class_init (XServerLocalClass *klass)
599 {
600     GObjectClass *object_class = G_OBJECT_CLASS (klass);
601     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
602
603     display_server_class->start = xserver_local_start;
604     display_server_class->stop = xserver_local_stop;
605     object_class->finalize = xserver_local_finalize;
606
607     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
608 }