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