]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - src/x-server-xvnc.c
Merge in X module renaming changes
[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_start (DisplayServer *display_server)
193 {
194     XServerXVNC *server = X_SERVER_XVNC (display_server);
195     XAuthority *authority;
196     gboolean result;
197     gchar *filename, *run_dir, *dir, *absolute_command;
198     GString *command;
199     gchar hostname[1024], *number;
200     GError *error = NULL;
201
202     g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
203
204     server->priv->got_signal = FALSE;
205
206     server->priv->x_server_process = process_new ();
207     process_set_clear_environment (server->priv->x_server_process, TRUE);
208     g_signal_connect (server->priv->x_server_process, "run", G_CALLBACK (run_cb), server);  
209     g_signal_connect (server->priv->x_server_process, "got-signal", G_CALLBACK (got_signal_cb), server);
210     g_signal_connect (server->priv->x_server_process, "stopped", G_CALLBACK (stopped_cb), server);
211
212     /* Setup logging */
213     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
214     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
215     server->priv->log_file = g_build_filename (dir, filename, NULL);
216     g_debug ("Logging to %s", server->priv->log_file);
217     g_free (filename);
218     g_free (dir);
219
220     absolute_command = get_absolute_command (server->priv->command);
221     if (!absolute_command)
222     {
223         g_debug ("Can't launch X server %s, not found in path", server->priv->command);
224         stopped_cb (server->priv->x_server_process, X_SERVER_XVNC (server));
225         return FALSE;
226     }
227
228     gethostname (hostname, 1024);
229     number = g_strdup_printf ("%d", x_server_get_display_number (X_SERVER (server)));
230     authority = x_authority_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
231
232     x_server_set_authority (X_SERVER (server), authority);
233
234     run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
235     dir = g_build_filename (run_dir, "root", NULL);
236     g_free (run_dir);
237     if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
238         g_warning ("Failed to make authority directory %s: %s", dir, strerror (errno));
239
240     server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
241     g_free (dir);
242
243     g_debug ("Writing X server authority to %s", server->priv->authority_file);
244
245     x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
246     if (error)
247         g_warning ("Failed to write authority: %s", error->message);
248     g_clear_error (&error);
249   
250     command = g_string_new (absolute_command);
251     g_free (absolute_command);
252   
253     g_string_append_printf (command, " :%d", x_server_get_display_number (X_SERVER (server)));
254     g_string_append_printf (command, " -auth %s", server->priv->authority_file);
255     g_string_append (command, " -inetd -nolisten tcp");
256     if (server->priv->width > 0 && server->priv->height > 0)
257         g_string_append_printf (command, " -geometry %dx%d", server->priv->width, server->priv->height);
258     if (server->priv->depth > 0)
259         g_string_append_printf (command, " -depth %d", server->priv->depth);
260
261     process_set_command (server->priv->x_server_process, command->str);
262     g_string_free (command, TRUE);
263
264     g_debug ("Launching Xvnc server");
265
266     /* Variable required for regression tests */
267     if (g_getenv ("LIGHTDM_TEST_ROOT"))
268     {
269         process_set_env (server->priv->x_server_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
270         process_set_env (server->priv->x_server_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
271     }
272
273     result = process_start (server->priv->x_server_process, FALSE);
274
275     if (result)
276         g_debug ("Waiting for ready signal from Xvnc server :%d", x_server_get_display_number (X_SERVER (server)));
277
278     if (!result)
279         stopped_cb (server->priv->x_server_process, X_SERVER_XVNC (server));
280
281     return result;
282 }
283  
284 static void
285 x_server_xvnc_stop (DisplayServer *server)
286 {
287     process_stop (X_SERVER_XVNC (server)->priv->x_server_process);
288 }
289
290 static void
291 x_server_xvnc_init (XServerXVNC *server)
292 {
293     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, X_SERVER_XVNC_TYPE, XServerXVNCPrivate);
294     server->priv->command = g_strdup ("Xvnc");
295     server->priv->width = 1024;
296     server->priv->height = 768;
297     server->priv->depth = 8;
298 }
299
300 static void
301 x_server_xvnc_finalize (GObject *object)
302 {
303     XServerXVNC *self;
304
305     self = X_SERVER_XVNC (object);
306
307     if (self->priv->x_server_process)
308         g_object_unref (self->priv->x_server_process);
309     g_free (self->priv->command);
310     g_free (self->priv->authority_file);
311     g_free (self->priv->log_file);
312
313     G_OBJECT_CLASS (x_server_xvnc_parent_class)->finalize (object);
314 }
315
316 static void
317 x_server_xvnc_class_init (XServerXVNCClass *klass)
318 {
319     GObjectClass *object_class = G_OBJECT_CLASS (klass);
320     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
321
322     display_server_class->start = x_server_xvnc_start;
323     display_server_class->stop = x_server_xvnc_stop;
324     object_class->finalize = x_server_xvnc_finalize;
325
326     g_type_class_add_private (klass, sizeof (XServerXVNCPrivate));
327 }