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