2 * Copyright (C) 2010-2011 Robert Ancell.
3 * Author: Robert Ancell <robert.ancell@canonical.com>
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
18 #include <glib/gstdio.h>
20 #include "x-server-xvnc.h"
21 #include "configuration.h"
22 #include "x-server-local.h"
25 struct XServerXVNCPrivate
27 /* X server process */
28 Process *x_server_process;
33 /* Command to run the X server */
37 gchar *authority_file;
39 /* File descriptor to use for standard input */
42 /* Geometry and colour depth */
43 gint width, height, depth;
45 /* TRUE when received ready signal */
49 G_DEFINE_TYPE (XServerXVNC, x_server_xvnc, X_SERVER_TYPE);
52 x_server_xvnc_new (void)
54 XServerXVNC *self = g_object_new (X_SERVER_XVNC_TYPE, NULL);
57 x_server_set_display_number (X_SERVER (self), x_server_local_get_unused_display_number ());
59 name = g_strdup_printf ("xvnc-%d", x_server_get_display_number (X_SERVER (self)));
60 display_server_set_name (DISPLAY_SERVER (self), name);
67 x_server_xvnc_set_command (XServerXVNC *server, const gchar *command)
69 g_return_if_fail (server != NULL);
70 g_free (server->priv->command);
71 server->priv->command = g_strdup (command);
75 x_server_xvnc_set_socket (XServerXVNC *server, int fd)
77 g_return_if_fail (server != NULL);
78 server->priv->socket_fd = fd;
82 x_server_xvnc_get_socket (XServerXVNC *server)
84 g_return_val_if_fail (server != NULL, 0);
85 return server->priv->socket_fd;
89 x_server_xvnc_set_geometry (XServerXVNC *server, gint width, gint height)
91 g_return_if_fail (server != NULL);
92 server->priv->width = width;
93 server->priv->height = height;
97 x_server_xvnc_set_depth (XServerXVNC *server, gint depth)
99 g_return_if_fail (server != NULL);
100 server->priv->depth = depth;
104 x_server_xvnc_get_authority_file_path (XServerXVNC *server)
106 g_return_val_if_fail (server != NULL, 0);
107 return server->priv->authority_file;
111 get_absolute_command (const gchar *command)
114 gchar *absolute_binary, *absolute_command = NULL;
116 tokens = g_strsplit (command, " ", 2);
118 absolute_binary = g_find_program_in_path (tokens[0]);
122 absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
124 absolute_command = g_strdup (absolute_binary);
129 return absolute_command;
133 run_cb (Process *process, XServerXVNC *server)
136 dup2 (server->priv->socket_fd, STDIN_FILENO);
137 dup2 (server->priv->socket_fd, STDOUT_FILENO);
138 close (server->priv->socket_fd);
140 /* Redirect output to logfile */
141 if (server->priv->log_file)
145 fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
147 g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
150 dup2 (fd, STDERR_FILENO);
155 /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
156 signal (SIGUSR1, SIG_IGN);
160 got_signal_cb (Process *process, int signum, XServerXVNC *server)
162 if (signum == SIGUSR1 && !server->priv->got_signal)
164 server->priv->got_signal = TRUE;
165 g_debug ("Got signal from Xvnc server :%d", x_server_get_display_number (X_SERVER (server)));
167 // FIXME: Check return value
168 DISPLAY_SERVER_CLASS (x_server_xvnc_parent_class)->start (DISPLAY_SERVER (server));
173 stopped_cb (Process *process, XServerXVNC *server)
175 g_debug ("Xvnc server stopped");
177 g_object_unref (server->priv->x_server_process);
178 server->priv->x_server_process = NULL;
180 x_server_local_release_display_number (x_server_get_display_number (X_SERVER (server)));
182 g_debug ("Removing X server authority %s", server->priv->authority_file);
184 g_unlink (server->priv->authority_file);
185 g_free (server->priv->authority_file);
186 server->priv->authority_file = NULL;
188 DISPLAY_SERVER_CLASS (x_server_xvnc_parent_class)->stop (DISPLAY_SERVER (server));
192 x_server_xvnc_start (DisplayServer *display_server)
194 XServerXVNC *server = X_SERVER_XVNC (display_server);
195 XAuthority *authority;
197 gchar *filename, *run_dir, *dir, *absolute_command;
199 gchar hostname[1024], *number;
200 GError *error = NULL;
202 g_return_val_if_fail (server->priv->x_server_process == NULL, FALSE);
204 server->priv->got_signal = FALSE;
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);
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);
220 absolute_command = get_absolute_command (server->priv->command);
221 if (!absolute_command)
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));
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);
232 x_server_set_authority (X_SERVER (server), authority);
234 run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
235 dir = g_build_filename (run_dir, "root", NULL);
237 if (g_mkdir_with_parents (dir, S_IRWXU) < 0)
238 g_warning ("Failed to make authority directory %s: %s", dir, strerror (errno));
240 server->priv->authority_file = g_build_filename (dir, x_server_get_address (X_SERVER (server)), NULL);
243 g_debug ("Writing X server authority to %s", server->priv->authority_file);
245 x_authority_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
247 g_warning ("Failed to write authority: %s", error->message);
248 g_clear_error (&error);
250 command = g_string_new (absolute_command);
251 g_free (absolute_command);
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);
261 process_set_command (server->priv->x_server_process, command->str);
262 g_string_free (command, TRUE);
264 g_debug ("Launching Xvnc server");
266 /* Variable required for regression tests */
267 if (g_getenv ("LIGHTDM_TEST_ROOT"))
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"));
273 result = process_start (server->priv->x_server_process, FALSE);
276 g_debug ("Waiting for ready signal from Xvnc server :%d", x_server_get_display_number (X_SERVER (server)));
279 stopped_cb (server->priv->x_server_process, X_SERVER_XVNC (server));
285 x_server_xvnc_stop (DisplayServer *server)
287 process_stop (X_SERVER_XVNC (server)->priv->x_server_process);
291 x_server_xvnc_init (XServerXVNC *server)
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;
301 x_server_xvnc_finalize (GObject *object)
305 self = X_SERVER_XVNC (object);
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);
313 G_OBJECT_CLASS (x_server_xvnc_parent_class)->finalize (object);
317 x_server_xvnc_class_init (XServerXVNCClass *klass)
319 GObjectClass *object_class = G_OBJECT_CLASS (klass);
320 DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
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;
326 g_type_class_add_private (klass, sizeof (XServerXVNCPrivate));