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