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