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