Back to index

lightdm  1.3.2
xserver-local.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-local.h"
00020 #include "configuration.h"
00021 #include "process.h"
00022 #include "vt.h"
00023 #include "plymouth.h"
00024 
00025 struct XServerLocalPrivate
00026 {
00027     /* X server process */
00028     Process *xserver_process;
00029   
00030     /* File to log to */
00031     gchar *log_file;    
00032 
00033     /* Command to run the X server */
00034     gchar *command;
00035 
00036     /* Config file to use */
00037     gchar *config_file;
00038 
00039     /* Server layout to use */
00040     gchar *layout;
00041 
00042     /* TRUE if TCP/IP connections are allowed */
00043     gboolean allow_tcp;
00044 
00045     /* Authority file */
00046     GFile *authority_file;
00047 
00048     /* XDMCP server to connect to */
00049     gchar *xdmcp_server;
00050 
00051     /* XDMCP port to connect to */
00052     guint xdmcp_port;
00053 
00054     /* XDMCP key to use */
00055     gchar *xdmcp_key;
00056 
00057     /* TRUE when received ready signal */
00058     gboolean got_signal;
00059 
00060     /* VT to run on */
00061     gint vt;
00062   
00063     /* TRUE if replacing Plymouth */
00064     gboolean replacing_plymouth;
00065 };
00066 
00067 G_DEFINE_TYPE (XServerLocal, xserver_local, XSERVER_TYPE);
00068 
00069 static GList *display_numbers = NULL;
00070 
00071 static gboolean
00072 display_number_in_use (guint display_number)
00073 {
00074     GList *link;
00075     gchar *path;
00076     gboolean result;
00077 
00078     for (link = display_numbers; link; link = link->next)
00079     {
00080         guint number = GPOINTER_TO_UINT (link->data);
00081         if (number == display_number)
00082             return TRUE;
00083     }
00084 
00085     path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
00086     result = g_file_test (path, G_FILE_TEST_EXISTS);
00087     g_free (path);
00088 
00089     return result;
00090 }
00091 
00092 guint
00093 xserver_local_get_unused_display_number (void)
00094 {
00095     guint number;
00096 
00097     number = config_get_integer (config_get_instance (), "LightDM", "minimum-display-number");
00098     while (display_number_in_use (number))
00099         number++;
00100 
00101     display_numbers = g_list_append (display_numbers, GUINT_TO_POINTER (number));
00102 
00103     return number;
00104 }
00105 
00106 void
00107 xserver_local_release_display_number (guint display_number)
00108 {
00109     GList *link;
00110     for (link = display_numbers; link; link = link->next)
00111     {
00112         guint number = GPOINTER_TO_UINT (link->data);
00113         if (number == display_number)
00114         {
00115             display_numbers = g_list_remove_link (display_numbers, link);
00116             return;
00117         }
00118     }
00119 }
00120 
00121 XServerLocal *
00122 xserver_local_new (void)
00123 {
00124     XServerLocal *self = g_object_new (XSERVER_LOCAL_TYPE, NULL);
00125     gchar *name;
00126 
00127     xserver_set_display_number (XSERVER (self), xserver_local_get_unused_display_number ());
00128 
00129     name = g_strdup_printf ("x-%d", xserver_get_display_number (XSERVER (self)));
00130     display_server_set_name (DISPLAY_SERVER (self), name);
00131     g_free (name);
00132 
00133     /* Replace Plymouth if it is running */
00134     if (plymouth_get_is_active () && plymouth_has_active_vt ())
00135     {
00136         gint active_vt = vt_get_active ();
00137         if (active_vt >= vt_get_min ())
00138         {
00139             g_debug ("X server %s will replace Plymouth", xserver_get_address (XSERVER (self)));
00140             self->priv->replacing_plymouth = TRUE;
00141             self->priv->vt = active_vt;
00142             plymouth_deactivate ();
00143         }
00144         else
00145             g_debug ("Plymouth is running on VT %d, but this is less than the configured minimum of %d so not replacing it", active_vt, vt_get_min ());
00146     }
00147     if (self->priv->vt < 0)
00148         self->priv->vt = vt_get_unused ();
00149     if (self->priv->vt >= 0)
00150         vt_ref (self->priv->vt);
00151 
00152     return self;
00153 }
00154 
00155 void
00156 xserver_local_set_command (XServerLocal *server, const gchar *command)
00157 {
00158     g_return_if_fail (server != NULL);
00159     g_free (server->priv->command);
00160     server->priv->command = g_strdup (command);
00161 }
00162 
00163 void
00164 xserver_local_set_config (XServerLocal *server, const gchar *path)
00165 {
00166     g_return_if_fail (server != NULL);
00167     g_free (server->priv->config_file);
00168     server->priv->config_file = g_strdup (path);
00169 }
00170 
00171 void
00172 xserver_local_set_layout (XServerLocal *server, const gchar *layout)
00173 {
00174     g_return_if_fail (server != NULL);
00175     g_free (server->priv->layout);
00176     server->priv->layout = g_strdup (layout);
00177 }
00178 
00179 void
00180 xserver_local_set_allow_tcp (XServerLocal *server, gboolean allow_tcp)
00181 {
00182     g_return_if_fail (server != NULL);
00183     server->priv->allow_tcp = allow_tcp;
00184 }
00185 
00186 void
00187 xserver_local_set_xdmcp_server (XServerLocal *server, const gchar *hostname)
00188 {
00189     g_return_if_fail (server != NULL);
00190     g_free (server->priv->xdmcp_server);
00191     server->priv->xdmcp_server = g_strdup (hostname);
00192     display_server_set_start_local_sessions (DISPLAY_SERVER (server), hostname == NULL);
00193 }
00194 
00195 const gchar *
00196 xserver_local_get_xdmcp_server (XServerLocal *server)
00197 {
00198     g_return_val_if_fail (server != NULL, 0);
00199     return server->priv->xdmcp_server;
00200 }
00201 
00202 void
00203 xserver_local_set_xdmcp_port (XServerLocal *server, guint port)
00204 {
00205     g_return_if_fail (server != NULL);
00206     server->priv->xdmcp_port = port;
00207 }
00208 
00209 guint
00210 xserver_local_get_xdmcp_port (XServerLocal *server)
00211 {
00212     g_return_val_if_fail (server != NULL, 0);
00213     return server->priv->xdmcp_port;
00214 }
00215 
00216 void
00217 xserver_local_set_xdmcp_key (XServerLocal *server, const gchar *key)
00218 {
00219     g_return_if_fail (server != NULL);
00220     g_free (server->priv->xdmcp_key);
00221     server->priv->xdmcp_key = g_strdup (key);
00222 }
00223 
00224 gint
00225 xserver_local_get_vt (XServerLocal *server)
00226 {
00227     g_return_val_if_fail (server != NULL, 0);
00228     return server->priv->vt;
00229 }
00230 
00231 gchar *
00232 xserver_local_get_authority_file_path (XServerLocal *server)
00233 {
00234     g_return_val_if_fail (server != NULL, 0);
00235     if (server->priv->authority_file)
00236         return g_file_get_path (server->priv->authority_file);
00237     return NULL;
00238 }
00239 
00240 static gchar *
00241 get_absolute_command (const gchar *command)
00242 {
00243     gchar **tokens;
00244     gchar *absolute_binary, *absolute_command = NULL;
00245 
00246     tokens = g_strsplit (command, " ", 2);
00247 
00248     absolute_binary = g_find_program_in_path (tokens[0]);
00249     if (absolute_binary)
00250     {
00251         if (tokens[1])
00252             absolute_command = g_strjoin (" ", absolute_binary, tokens[1], NULL);
00253         else
00254             absolute_command = g_strdup (absolute_binary);
00255     }
00256     g_free (absolute_binary);
00257 
00258     g_strfreev (tokens);
00259 
00260     return absolute_command;
00261 }
00262 
00263 static void
00264 run_cb (Process *process, XServerLocal *server)
00265 {
00266     int fd;
00267 
00268     /* Make input non-blocking */
00269     fd = open ("/dev/null", O_RDONLY);
00270     dup2 (fd, STDIN_FILENO);
00271     close (fd);
00272 
00273     /* Redirect output to logfile */
00274     if (server->priv->log_file)
00275     {
00276          int fd;
00277 
00278          fd = g_open (server->priv->log_file, O_WRONLY | O_CREAT | O_TRUNC, 0600);
00279          if (fd < 0)
00280              g_warning ("Failed to open log file %s: %s", server->priv->log_file, g_strerror (errno));
00281          else
00282          {
00283              dup2 (fd, STDOUT_FILENO);
00284              dup2 (fd, STDERR_FILENO);
00285              close (fd);
00286          }
00287     }
00288 
00289     /* Set SIGUSR1 to ignore so the X server can indicate it when it is ready */
00290     signal (SIGUSR1, SIG_IGN);
00291 }
00292 
00293 static void
00294 got_signal_cb (Process *process, int signum, XServerLocal *server)
00295 {
00296     if (signum == SIGUSR1 && !server->priv->got_signal)
00297     {
00298         server->priv->got_signal = TRUE;
00299         g_debug ("Got signal from X server :%d", xserver_get_display_number (XSERVER (server)));
00300 
00301         if (server->priv->replacing_plymouth)
00302         {
00303             g_debug ("Stopping Plymouth, X server is ready");
00304             server->priv->replacing_plymouth = FALSE;
00305             plymouth_quit (TRUE);
00306         }
00307 
00308         // FIXME: Check return value
00309         DISPLAY_SERVER_CLASS (xserver_local_parent_class)->start (DISPLAY_SERVER (server));
00310     }
00311 }
00312 
00313 static void
00314 stopped_cb (Process *process, XServerLocal *server)
00315 {
00316     g_debug ("X server stopped");
00317 
00318     g_object_unref (server->priv->xserver_process);
00319     server->priv->xserver_process = NULL;
00320 
00321     xserver_local_release_display_number (xserver_get_display_number (XSERVER (server)));
00322   
00323     if (xserver_get_authority (XSERVER (server)) && server->priv->authority_file)
00324     {
00325         GError *error = NULL;
00326         gchar *path;
00327 
00328         path = g_file_get_path (server->priv->authority_file);      
00329         g_debug ("Removing X server authority %s", path);
00330         g_free (path);
00331 
00332         g_file_delete (server->priv->authority_file, NULL, &error);
00333         if (error)
00334             g_debug ("Error removing authority: %s", error->message);
00335         g_clear_error (&error);
00336 
00337         g_object_unref (server->priv->authority_file);
00338         server->priv->authority_file = NULL;
00339     }
00340 
00341     if (server->priv->vt >= 0)
00342     {
00343         vt_unref (server->priv->vt);
00344         server->priv->vt = -1;
00345     }
00346 
00347     if (server->priv->replacing_plymouth && plymouth_get_is_running ())
00348     {
00349         g_debug ("Stopping Plymouth, X server failed to start");
00350         server->priv->replacing_plymouth = FALSE;
00351         plymouth_quit (FALSE);
00352     }
00353 
00354     DISPLAY_SERVER_CLASS (xserver_local_parent_class)->stop (DISPLAY_SERVER (server));
00355 }
00356 
00357 static void
00358 write_authority_file (XServerLocal *server)
00359 {
00360     XAuthority *authority;
00361     gchar *path;
00362     GError *error = NULL;
00363 
00364     authority = xserver_get_authority (XSERVER (server));
00365     if (!authority)
00366         return;
00367 
00368     /* Get file to write to if have authority */
00369     if (!server->priv->authority_file)
00370     {
00371         gchar *run_dir, *dir;
00372       
00373         run_dir = config_get_string (config_get_instance (), "LightDM", "run-directory");
00374         dir = g_build_filename (run_dir, "root", NULL);
00375         g_free (run_dir);
00376         g_mkdir_with_parents (dir, S_IRWXU);
00377 
00378         path = g_build_filename (dir, xserver_get_address (XSERVER (server)), NULL);
00379         g_free (dir);
00380         server->priv->authority_file = g_file_new_for_path (path);
00381         g_free (path);
00382     }
00383 
00384     path = g_file_get_path (server->priv->authority_file);
00385     g_debug ("Writing X server authority to %s", path);
00386     g_free (path);
00387 
00388     xauth_write (authority, XAUTH_WRITE_MODE_REPLACE, server->priv->authority_file, &error);
00389     if (error)
00390         g_warning ("Failed to write authority: %s", error->message);
00391     g_clear_error (&error);
00392 }
00393 
00394 static gboolean
00395 xserver_local_start (DisplayServer *display_server)
00396 {
00397     XServerLocal *server = XSERVER_LOCAL (display_server);
00398     gboolean result;
00399     gchar *filename, *dir, *absolute_command;
00400     gchar hostname[1024], *number;
00401     GString *command;
00402 
00403     g_return_val_if_fail (server->priv->xserver_process == NULL, FALSE);
00404 
00405     server->priv->got_signal = FALSE;
00406 
00407     g_return_val_if_fail (server->priv->command != NULL, FALSE);
00408 
00409     server->priv->xserver_process = process_new ();
00410     process_set_clear_environment (server->priv->xserver_process, TRUE);
00411     g_signal_connect (server->priv->xserver_process, "run", G_CALLBACK (run_cb), server);  
00412     g_signal_connect (server->priv->xserver_process, "got-signal", G_CALLBACK (got_signal_cb), server);
00413     g_signal_connect (server->priv->xserver_process, "stopped", G_CALLBACK (stopped_cb), server);
00414 
00415     /* Setup logging */
00416     filename = g_strdup_printf ("%s.log", display_server_get_name (display_server));
00417     dir = config_get_string (config_get_instance (), "LightDM", "log-directory");
00418     server->priv->log_file = g_build_filename (dir, filename, NULL);
00419     g_debug ("Logging to %s", server->priv->log_file);
00420     g_free (filename);
00421     g_free (dir);
00422 
00423     absolute_command = get_absolute_command (server->priv->command);
00424     if (!absolute_command)
00425     {
00426         g_debug ("Can't launch X server %s, not found in path", server->priv->command);
00427         stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
00428         return FALSE;
00429     }
00430     command = g_string_new (absolute_command);
00431     g_free (absolute_command);
00432 
00433     g_string_append_printf (command, " :%d", xserver_get_display_number (XSERVER (server)));
00434 
00435     if (server->priv->config_file)
00436         g_string_append_printf (command, " -config %s", server->priv->config_file);
00437 
00438     if (server->priv->layout)
00439         g_string_append_printf (command, " -layout %s", server->priv->layout);
00440 
00441     gethostname (hostname, 1024);
00442     number = g_strdup_printf ("%d", xserver_get_display_number (XSERVER (server)));
00443     if (!server->priv->xdmcp_key)
00444         xserver_set_authority (XSERVER (server), xauth_new_cookie (XAUTH_FAMILY_LOCAL, (guint8*) hostname, strlen (hostname), number));
00445     g_free (number);
00446     write_authority_file (server);
00447     if (server->priv->authority_file)
00448     {
00449         gchar *path = g_file_get_path (server->priv->authority_file);
00450         g_string_append_printf (command, " -auth %s", path);
00451         g_free (path);
00452     }
00453 
00454     /* Connect to a remote server using XDMCP */
00455     if (server->priv->xdmcp_server != NULL)
00456     {
00457         if (server->priv->xdmcp_port != 0)
00458             g_string_append_printf (command, " -port %d", server->priv->xdmcp_port);
00459         g_string_append_printf (command, " -query %s", server->priv->xdmcp_server);
00460         if (server->priv->xdmcp_key)
00461             g_string_append_printf (command, " -cookie %s", server->priv->xdmcp_key);
00462     }
00463     else if (!server->priv->allow_tcp)
00464         g_string_append (command, " -nolisten tcp");
00465 
00466     if (server->priv->vt >= 0)
00467         g_string_append_printf (command, " vt%d -novtswitch", server->priv->vt);
00468 
00469     if (server->priv->replacing_plymouth)
00470         g_string_append (command, " -background none");
00471     process_set_command (server->priv->xserver_process, command->str);
00472     g_string_free (command, TRUE);
00473 
00474     g_debug ("Launching X Server");
00475 
00476     /* If running inside another display then pass through those variables */
00477     if (g_getenv ("DISPLAY"))
00478     {
00479         process_set_env (server->priv->xserver_process, "DISPLAY", g_getenv ("DISPLAY"));
00480         if (g_getenv ("XAUTHORITY"))
00481             process_set_env (server->priv->xserver_process, "XAUTHORITY", g_getenv ("XAUTHORITY"));
00482         else
00483         {
00484             gchar *path;
00485             path = g_build_filename (g_get_home_dir (), ".Xauthority", NULL);
00486             process_set_env (server->priv->xserver_process, "XAUTHORITY", path);
00487         }
00488     }
00489 
00490     /* Variable required for regression tests */
00491     if (g_getenv ("LIGHTDM_TEST_ROOT"))
00492     {
00493         process_set_env (server->priv->xserver_process, "LIGHTDM_TEST_ROOT", g_getenv ("LIGHTDM_TEST_ROOT"));
00494         process_set_env (server->priv->xserver_process, "LD_PRELOAD", g_getenv ("LD_PRELOAD"));
00495         process_set_env (server->priv->xserver_process, "LD_LIBRARY_PATH", g_getenv ("LD_LIBRARY_PATH"));
00496     }
00497 
00498     result = process_start (server->priv->xserver_process);
00499 
00500     if (result)
00501         g_debug ("Waiting for ready signal from X server :%d", xserver_get_display_number (XSERVER (server)));
00502 
00503     if (!result)
00504         stopped_cb (server->priv->xserver_process, XSERVER_LOCAL (server));
00505 
00506     return result;
00507 }
00508  
00509 static void
00510 xserver_local_stop (DisplayServer *server)
00511 {
00512     process_stop (XSERVER_LOCAL (server)->priv->xserver_process);
00513 }
00514 
00515 static gboolean
00516 xserver_local_get_is_stopped (DisplayServer *server)
00517 {
00518     return process_get_pid (XSERVER_LOCAL (server)->priv->xserver_process) == 0;
00519 }
00520 
00521 static void
00522 xserver_local_init (XServerLocal *server)
00523 {
00524     server->priv = G_TYPE_INSTANCE_GET_PRIVATE (server, XSERVER_LOCAL_TYPE, XServerLocalPrivate);
00525     server->priv->vt = -1;
00526     server->priv->command = g_strdup ("X");
00527 }
00528 
00529 static void
00530 xserver_local_finalize (GObject *object)
00531 {
00532     XServerLocal *self;
00533 
00534     self = XSERVER_LOCAL (object);  
00535 
00536     if (self->priv->xserver_process)
00537         g_object_unref (self->priv->xserver_process);
00538     g_free (self->priv->log_file);
00539     g_free (self->priv->command);
00540     g_free (self->priv->config_file);
00541     g_free (self->priv->layout);
00542     g_free (self->priv->xdmcp_server);
00543     g_free (self->priv->xdmcp_key);
00544     if (self->priv->authority_file)
00545         g_object_unref (self->priv->authority_file);
00546     if (self->priv->vt >= 0)
00547         vt_unref (self->priv->vt);
00548 
00549     G_OBJECT_CLASS (xserver_local_parent_class)->finalize (object);
00550 }
00551 
00552 static void
00553 xserver_local_class_init (XServerLocalClass *klass)
00554 {
00555     GObjectClass *object_class = G_OBJECT_CLASS (klass);
00556     DisplayServerClass *display_server_class = DISPLAY_SERVER_CLASS (klass);
00557 
00558     display_server_class->start = xserver_local_start;
00559     display_server_class->stop = xserver_local_stop;
00560     display_server_class->get_is_stopped = xserver_local_get_is_stopped;
00561     object_class->finalize = xserver_local_finalize;
00562 
00563     g_type_class_add_private (klass, sizeof (XServerLocalPrivate));
00564 }