Back to index

lightdm  1.3.2
xserver-xvnc.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2010-2011 Robert Ancell.
00003  * Author: Robert Ancell <robert.ancell@canonical.com>
00004  * 
00005  * This program is free software: you can redistribute it and/or modify it under
00006  * the terms of the GNU General Public License as published by the Free Software
00007  * Foundation, either version 3 of the License, or (at your option) any later
00008  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
00009  * license.
00010  */
00011 
00012 #include <config.h>
00013 #include <string.h>
00014 #include <fcntl.h>
00015 #include <sys/stat.h>
00016 #include <errno.h>
00017 #include <glib/gstdio.h>
00018 
00019 #include "xserver-xvnc.h"
00020 #include "configuration.h"
00021 #include "xserver-local.h"
00022 #include "process.h"
00023 
00024 struct XServerXVNCPrivate
00025 {
00026     /* X server process */
00027     Process *xserver_process;
00028   
00029     /* File to log to */
00030     gchar *log_file;  
00031 
00032     /* Authority file */
00033     GFile *authority_file;
00034 
00035     /* File descriptor to use for standard input */
00036     gint socket_fd;
00037   
00038     /* Geometry and colour depth */
00039     gint width, height, depth;
00040 
00041     /* TRUE when received ready signal */
00042     gboolean got_signal;
00043 };
00044 
00045 G_DEFINE_TYPE (XServerXVNC, xserver_xvnc, XSERVER_TYPE);
00046 
00047 XServerXVNC *
00048 xserver_xvnc_new (void)
00049 {
00050     XServerXVNC *self = g_object_new (XSERVER_XVNC_TYPE, NULL);
00051     gchar *name;
00052 
00053     xserver_set_display_number (XSERVER (self), xserver_local_get_unused_display_number ());
00054 
00055     name = g_strdup_printf ("xvnc-%d", xserver_get_display_number (XSERVER (self)));
00056     display_server_set_name (DISPLAY_SERVER (self), name);
00057     g_free (name);
00058 
00059     return self;
00060 }
00061 
00062 void
00063 xserver_xvnc_set_socket (XServerXVNC *server, int fd)
00064 {
00065     g_return_if_fail (server != NULL);
00066     server->priv->socket_fd = fd;
00067 }
00068 
00069 int
00070 xserver_xvnc_get_socket (XServerXVNC *server)
00071 {
00072     g_return_val_if_fail (server != NULL, 0);
00073     return server->priv->socket_fd;
00074 }
00075 
00076 void
00077 xserver_xvnc_set_geometry (XServerXVNC *server, gint width, gint height)
00078 {
00079     g_return_if_fail (server != NULL);
00080     server->priv->width = width;
00081     server->priv->height = height;
00082 }
00083 
00084 void
00085 xserver_xvnc_set_depth (XServerXVNC *server, gint depth)
00086 {
00087     g_return_if_fail (server != NULL);
00088     server->priv->depth = depth;
00089 }
00090 
00091 gchar *
00092 xserver_xvnc_get_authority_file_path (XServerXVNC *server)
00093 {
00094     g_return_val_if_fail (server != NULL, 0);
00095     if (server->priv->authority_file)
00096         return g_file_get_path (server->priv->authority_file);
00097     return NULL;
00098 }
00099 
00100 static gchar *
00101 get_absolute_command (const gchar *command)
00102 {
00103     gchar **tokens;
00104     gchar *absolute_binary, *absolute_command = NULL;
00105 
00106     tokens = g_strsplit (command, " ", 2);
00107 
00108     absolute_binary = g_find_program_in_path (tokens[0]);
00109     if (absolute_binary)
00110     {
00111         if (tokens[1])
00112             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
00113         else
00114             absolute_command = g_strdup (absolute_binary);
00115     }
00116 
00117     g_strfreev (tokens);
00118 
00119     return absolute_command;
00120 }
00121 
00122 static void
00123 run_cb (Process *process, XServerXVNC *server)
00124 {
00125     /* Connect input */
00126     dup2 (server->priv->socket_fd, STDIN_FILENO);
00127     dup2 (server->priv->socket_fd, STDOUT_FILENO);
00128     close (server->priv->socket_fd);
00129 
00130     /* Redirect output to logfile */
00131     if (server->priv->log_file)
00132     {
00133          int fd;
00134 
00135          fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
00136          if (fd < 0)
00137              g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
00138          else
00139          {
00140              dup2 (fd, STDERR_FILENO);
00141              close (fd);
00142          }
00143     }
00144 
00145     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
00146     signal (SIGUSR1, SIG_IGN);
00147 }
00148 
00149 static void
00150 got_signal_cb (Process *process, int signum, XServerXVNC *server)
00151 {
00152     if (signum == SIGUSR1 && !server->priv->got_signal)
00153     {
00154         server->priv->got_signal = TRUE;
00155         g_debug ("Got signal from Xvnc server :%d", xserver_get_display_number (XSERVER (server)));
00156 
00157         // FIXME: Check return value
00158         DISPLAY_SERVER_CLASS (xserver_xvnc_parent_class)->start (DISPLAY_SERVER (server));
00159     }
00160 }
00161 
00162 static void
00163 stopped_cb (Process *process, XServerXVNC *server)
00164 {
00165     GError *error = NULL;
00166     gchar *path;
00167   
00168     g_debug ("Xvnc server stopped");
00169 
00170     g_object_unref (server->priv->xserver_process);
00171     server->priv->xserver_process = NULL;
00172 
00173     xserver_local_release_display_number (xserver_get_display_number (XSERVER (server)));
00174 
00175     path = g_file_get_path (server->priv->authority_file);
00176     g_debug ("Removing X server authority %s", path);
00177     g_free (path);
00178 
00179     g_file_delete (server->priv->authority_file, NULL, &error);
00180     if (error)
00181         g_debug ("Error removing authority: %s", error->message);
00182     g_clear_error (&error);
00183     g_object_unref (server->priv->authority_file);
00184     server->priv->authority_file = NULL;
00185 
00186     DISPLAY_SERVER_CLASS (xserver_xvnc_parent_class)->stop (DISPLAY_SERVER (server));
00187 }
00188 
00189 static gboolean
00190 xserver_xvnc_start (DisplayServer *display_server)
00191 {
00192     XServerXVNC *server = XSERVER_XVNC (display_server);
00193     XAuthority *authority;
00194     gboolean result;
00195     gchar *filename, *run_dir, *dir, *path, *absolute_command;
00196     GString *command;
00197     gchar hostname[1024], *number;
00198     GError *error = NULL;
00199 
00200     g_return_val_if_fail (server->priv->xserver_process == NULL, FALSE);
00201 
00202     server->priv->got_signal = FALSE;
00203 
00204     server->priv->xserver_process = process_new ();
00205     process_set_clear_environment (server->priv->xserver_process, TRUE);
00206     g_signal_connect (server->priv->xserver_process, "run", G_CALLBACK (run_cb), server);  
00207     g_signal_connect (server->priv->xserver_process, "got-signal", G_CALLBACK (got_signal_cb), server);
00208     g_signal_connect (server->priv->xserver_process, "stopped", G_CALLBACK (stopped_cb), server);
00209 
00210     /* Setup logging */
00211     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
00212     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
00213     server->priv->log_file = g_build_filename (dir, filename, NULL);
00214     g_debug ("Logging to %s", server->priv->log_file);
00215     g_free (filename);
00216     g_free (dir);
00217 
00218     absolute_command = get_absolute_command ("Xvnc");
00219     if (!absolute_command)
00220     {
00221         g_debug ("Can't launch Xvnc, not found in path");
00222         stopped_cb (server->priv->xserver_process, XSERVER_XVNC (server));
00223         return FALSE;
00224     }
00225 
00226     gethostname (hostname, 1024);
00227     number = g_strdup_printf ("%d", xserver_get_display_number (XSERVER (server)));
00228     authority = xauth_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number);
00229 
00230     xserver_set_authority (XSERVER (server), authority);
00231 
00232     run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
00233     dir = g_build_filename (run_dir, "root", NULL);
00234     g_free (run_dir);
00235     g_mkdir_with_parents (dir, S_IRWXU);
00236 
00237     path = g_build_filename (dir, xserver_get_address (XSERVER (server)), NULL);
00238     g_free (dir);
00239     server->priv->authority_file = g_file_new_for_path (path);
00240     g_free (path);
00241 
00242     path = g_file_get_path (server->priv->authority_file);
00243     g_debug ("Writing X server authority to %s", path);
00244 
00245     xauth_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
00246     if (error)
00247         g_warning ("Failed to write authority: %s", error->message);
00248     g_clear_error (&error);
00249   
00250     command = g_string_new (absolute_command);
00251     g_free (absolute_command);
00252   
00253     g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server)));
00254     g_string_append_printf (command, " -auth %s", path);
00255     g_free (path);
00256     g_string_append (command, " -inetd -nolisten tcp");
00257     if (server->priv->width > 0 && server->priv->height > 0)
00258         g_string_append_printf (command, " -geometry %dx%d", server->priv->width, server->priv->height);
00259     if (server->priv->depth > 0)
00260         g_string_append_printf (command, " -depth %d", server->priv->depth);
00261 
00262     process_set_command (server->priv->xserver_process, command->str);
00263     g_string_free (command, TRUE);
00264 
00265     g_debug ("Launching Xvnc server");
00266 
00267     /* Variable required for regression tests */
00268     if (g_getenv ("LIGHTDM_TEST_ROOT"))
00269     {
00270         process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
00271         process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
00272     }
00273 
00274     result = process_start (server->priv->xserver_process);
00275 
00276     if (result)
00277         g_debug ("Waiting for ready signal from Xvnc server :%d", xserver_get_display_number (XSERVER (server)));
00278 
00279     if (!result)
00280         stopped_cb (server->priv->xserver_process, XSERVER_XVNC (server));
00281 
00282     return result;
00283 }
00284  
00285 static void
00286 xserver_xvnc_stop (DisplayServer *server)
00287 {
00288     process_stop (XSERVER_XVNC (server)->priv->xserver_process);
00289 }
00290 
00291 static gboolean
00292 xserver_xvnc_get_is_stopped (DisplayServer *server)
00293 {
00294     return process_get_pid (XSERVER_XVNC (server)->priv->xserver_process) == 0;
00295 }
00296 
00297 static void
00298 xserver_xvnc_init (XServerXVNC *server)
00299 {
00300     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XSERVER_XVNC_TYPE, XServerXVNCPrivate);
00301     server->priv->width = 1024;
00302     server->priv->height = 768;
00303     server->priv->depth = 8;
00304 }
00305 
00306 static void
00307 xserver_xvnc_finalize (GObject *object)
00308 {
00309     XServerXVNC *self;
00310 
00311     self = XSERVER_XVNC (object);
00312 
00313     if (self->priv->xserver_process)
00314         g_object_unref (self->priv->xserver_process);
00315     if (self->priv->authority_file)
00316         g_object_unref (self->priv->authority_file);
00317     g_free (self->priv->log_file);
00318 
00319     G_OBJECT_CLASS (xserver_xvnc_parent_class)->finalize (object);
00320 }
00321 
00322 static void
00323 xserver_xvnc_class_init (XServerXVNCClass *klass)
00324 {
00325     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00326     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
00327 
00328     display_server_class->start = xserver_xvnc_start;
00329     display_server_class->stop = xserver_xvnc_stop;
00330     display_server_class->get_is_stopped = xserver_xvnc_get_is_stopped;
00331     object_class->finalize = xserver_xvnc_finalize;
00332 
00333     g_type_class_add_private (klass, sizeof (XServerXVNCPrivate));
00334 }