]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-xvnc.c
Add a backup-logs option to re-enable the old log backup mode
[sojka/lightdm.git] / src / x-server-xvnc.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
20 #include "x-server-xvnc.h"
21 #include "configuration.h"
22 #include "x-server-local.h"
23 #include "process.h"
24
25 struct XServerXVNCPrivate
26 {
27     /* X server process */
28     Process *x_server_process;
29
30     /* Command to run the X server */
31     gchar *command;
32
33     /* Authority file */
34     gchar *authority_file;
35
36     /* File descriptor to use for standard input */
37     gint socket_fd;
38
39     /* Geometry and colour depth */
40     gint width, height, depth;
41
42     /* TRUE when received ready signal */
43     gboolean got_signal;
44 };
45
46 G_DEFINE_TYPE (XServerXVNC, x_server_xvnc, X_SERVER_TYPE);
47
48 XServerXVNC *
49 x_server_xvnc_new (void)
50 {
51     XServerXVNC *self = g_object_new (X_SERVER_XVNC_TYPE, NULL);
52     gchar *name;
53
54     x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
55
56     name = g_strdup_printf ("xvnc-%d", x_server_get_display_number (X_SERVER (self)));
57     display_server_set_name (DISPLAY_SERVER (self), name);
58     g_free (name);
59
60     return self;
61 }
62
63 void
64 x_server_xvnc_set_command (XServerXVNC *server, const gchar *command)
65 {
66     g_return_if_fail (server != NULL);
67     g_free (server->priv->command);
68     server->priv->command = g_strdup (command);
69 }
70
71 void
72 x_server_xvnc_set_socket (XServerXVNC *server, int fd)
73 {
74     g_return_if_fail (server != NULL);
75     server->priv->socket_fd = fd;
76 }
77
78 int
79 x_server_xvnc_get_socket (XServerXVNC *server)
80 {
81     g_return_val_if_fail (server != NULL, 0);
82     return server->priv->socket_fd;
83 }
84
85 void
86 x_server_xvnc_set_geometry (XServerXVNC *server, gint width, gint height)
87 {
88     g_return_if_fail (server != NULL);
89     server->priv->width = width;
90     server->priv->height = height;
91 }
92
93 void
94 x_server_xvnc_set_depth (XServerXVNC *server, gint depth)
95 {
96     g_return_if_fail (server != NULL);
97     server->priv->depth = depth;
98 }
99
100 const gchar *
101 x_server_xvnc_get_authority_file_path (XServerXVNC *server)
102 {
103     g_return_val_if_fail (server != NULL, 0);
104     return server->priv->authority_file;
105 }
106
107 static gchar *
108 get_absolute_command (const gchar *command)
109 {
110     gchar **tokens;
111     gchar *absolute_binary, *absolute_command = NULL;
112
113     tokens = g_strsplit (command, " ", 2);
114
115     absolute_binary = g_find_program_in_path (tokens[0]);
116     if (absolute_binary)
117     {
118         if (tokens[1])
119             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
120         else
121             absolute_command = g_strdup (absolute_binary);
122     }
123
124     g_strfreev (tokens);
125
126     return absolute_command;
127 }
128
129 static void
130 run_cb (Process *process, gpointer user_data)
131 {
132     XServerXVNC *server = user_data;
133
134     /* Connect input */
135     dup2 (server->priv->socket_fd, STDIN_FILENO);
136     dup2 (server->priv->socket_fd, STDOUT_FILENO);
137     close (server->priv->socket_fd);
138
139     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
140     signal (SIGUSR1, SIG_IGN);
141 }
142
143 static void
144 got_signal_cb (Process *process, int signum, XServerXVNC *server)
145 {
146     if (signum == SIGUSR1 && !server->priv->got_signal)
147     {
148         server->priv->got_signal = TRUE;
149         l_debug (server, "Got signal from Xvnc server :%d", x_server_get_display_number (X_SERVER (server)));
150
151         // FIXME: Check return value
152         DISPLAY_SERVER_CLASS (x_server_xvnc_parent_class)->start (DISPLAY_SERVER (server));
153     }
154 }
155
156 static void
157 stopped_cb (Process *process, XServerXVNC *server)
158 {
159     l_debug (server, "Xvnc server stopped");
160
161     g_clear_object (&server->priv->x_server_process);
162
163     x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
164
165     l_debug (server, "Removing X server authority %s", server->priv->authority_file);
166
167     g_unlink (server->priv->authority_file);
168     g_free (server->priv->authority_file);
169     server->priv->authority_file = NULL;
170
171     DISPLAY_SERVER_CLASS (x_server_xvnc_parent_class)->stop (DISPLAY_SERVER (server));
172 }
173
174 static gboolean
175 x_server_xvnc_get_can_share (DisplayServer *server)
176 {
177     return TRUE;
178 }
179
180 static gboolean
181 x_server_xvnc_start (DisplayServer *display_server)
182 {
183     XServerXVNC *server = X_SERVER_XVNC (display_server);
184     XAuthority *authority;
185     gboolean result, backup_logs;
186     gchar *filename, *run_dir, *dir, *log_file, *absolute_command;
187     GString *command;
188     gchar hostname[1024], *number;
189     GError *error = NULL;
190
191     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
192
193     server->priv->got_signal = FALSE;
194
195     server->priv->x_server_process = process_new (run_cb, server);
196     process_set_clear_environment (server->priv->x_server_process, TRUE);
197     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_GOT_SIGNAL, G_CALLBACK (got_signal_cb), server);
198     g_signal_connect (server->priv->x_server_process, PROCESS_SIGNAL_STOPPED, G_CALLBACK (stopped_cb), server);
199
200     /* Setup logging */
201     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
202     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
203     log_file = g_build_filename (dir, filename, NULL);
204     backup_logs = config_get_boolean (config_get_instance (), "LightDM", "backup-logs");  
205     process_set_log_file (server->priv->x_server_process, log_file, FALSE, backup_logs ? LOG_MODE_BACKUP_AND_TRUNCATE : LOG_MODE_APPEND);
206     l_debug (display_server, "Logging to %s", log_file);
207     g_free (log_file);
208     g_free (filename);
209     g_free (dir);
210
211     absolute_command = get_absolute_command (server->priv->command);
212     if (!absolute_command)
213     {
214         l_debug (display_server, "Can't launch X server %s, not found in path", server->priv->command);
215         stopped_cb (server->priv->x_server_process, X_SERVER_XVNC (server));
216         return FALSE;
217     }
218
219     gethostname (hostname, 1024);
220     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (server)));
221     authority = x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
222
223     x_server_set_authority (X_SERVER (server), authority);
224
225     run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
226     dir = g_build_filename (run_dir, "root", NULL);
227     g_free (run_dir);
228     if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
229         l_warning (display_server, "Failed to make authority directory %s: %s", dir, strerror (errno));
230
231     server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
232     g_free (dir);
233
234     l_debug (display_server, "Writing X server authority to %s", server->priv->authority_file);
235
236     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
237     if (error)
238         l_warning (display_server, "Failed to write authority: %s", error->message);
239     g_clear_error (&error);
240
241     command = g_string_new (absolute_command);
242     g_free (absolute_command);
243
244     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
245     g_string_append_printf (command, " -auth %s", server->priv->authority_file);
246     g_string_append (command, " -inetd -nolisten tcp");
247     if (server->priv->width > 0 && server->priv->height > 0)
248         g_string_append_printf (command, " -geometry %dx%d", server->priv->width, server->priv->height);
249     if (server->priv->depth > 0)
250         g_string_append_printf (command, " -depth %d", server->priv->depth);
251
252     process_set_command (server->priv->x_server_process, command->str);
253     g_string_free (command, TRUE);
254
255     l_debug (display_server, "Launching Xvnc server");
256
257     /* Variable required for regression tests */
258     if (g_getenv ("LIGHTDM_TEST_ROOT"))
259     {
260         process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
261         process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
262     }
263
264     result = process_start (server->priv->x_server_process, FALSE);
265
266     if (result)
267         l_debug (display_server, "Waiting for ready signal from Xvnc server :%d", x_server_get_display_number (X_SERVER (server)));
268
269     if (!result)
270         stopped_cb (server->priv->x_server_process, X_SERVER_XVNC (server));
271
272     return result;
273 }
274
275 static void
276 x_server_xvnc_stop (DisplayServer *server)
277 {
278     process_stop (X_SERVER_XVNC (server)->priv->x_server_process);
279 }
280
281 static void
282 x_server_xvnc_init (XServerXVNC *server)
283 {
284     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_XVNC_TYPE, XServerXVNCPrivate);
285     server->priv->command = g_strdup ("Xvnc");
286     server->priv->width = 1024;
287     server->priv->height = 768;
288     server->priv->depth = 8;
289 }
290
291 static void
292 x_server_xvnc_finalize (GObject *object)
293 {
294     XServerXVNC *self = X_SERVER_XVNC (object);
295
296     g_clear_object (&self->priv->x_server_process);
297     g_free (self->priv->command);
298     g_free (self->priv->authority_file);
299
300     G_OBJECT_CLASS (x_server_xvnc_parent_class)->finalize (object);
301 }
302
303 static void
304 x_server_xvnc_class_init (XServerXVNCClass *klass)
305 {
306     GObjectClass *object_class = G_OBJECT_CLASS (klass);
307     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
308
309     display_server_class->get_can_share = x_server_xvnc_get_can_share;
310     display_server_class->start = x_server_xvnc_start;
311     display_server_class->stop = x_server_xvnc_stop;
312     object_class->finalize = x_server_xvnc_finalize;
313
314     g_type_class_add_private (klass, sizeof (XServerXVNCPrivate));
315 }