Back to index

lightdm  1.3.2
X.c
Go to the documentation of this file.
00001 #include <stdlib.h>
00002 #include <stdio.h>
00003 #include <string.h>
00004 #include <sys/types.h>
00005 #include <unistd.h>
00006 #include <signal.h>
00007 #include <sys/stat.h>
00008 #include <fcntl.h>
00009 #include <errno.h>
00010 
00011 #include "status.h"
00012 #include "x-server.h"
00013 #include "x-authority.h"
00014 #include "xdmcp-client.h"
00015 
00016 static GKeyFile *config;
00017 
00018 /* Path to lock file */
00019 static gchar *lock_path = NULL;
00020 
00021 /* Path to authority database to use */
00022 static gchar *auth_path = NULL;
00023 
00024 /* Display number being served */
00025 static int display_number = 0;
00026 
00027 /* X server */
00028 static XServer *xserver = NULL;
00029 
00030 /* XDMCP client */
00031 static XDMCPClient *xdmcp_client = NULL;
00032 
00033 /* Authorization provided by XDMCP server */
00034 static guint16 xdmcp_cookie_length = 0;
00035 static guint8 *xdmcp_cookie = NULL;
00036 
00037 static void
00038 cleanup ()
00039 {
00040     if (lock_path)
00041         unlink (lock_path);
00042     if (xserver)
00043         g_object_unref (xserver);
00044     if (xdmcp_client)
00045         g_object_unref (xdmcp_client);
00046 }
00047 
00048 static void
00049 quit (int status)
00050 {
00051     cleanup ();
00052     exit (status);
00053 }
00054 
00055 static void
00056 indicate_ready ()
00057 {
00058     void *handler;  
00059     handler = signal (SIGUSR1, SIG_IGN);
00060     if (handler == SIG_IGN)
00061     {
00062         status_notify ("XSERVER :%d INDICATE-READY", display_number);
00063         kill (getppid (), SIGUSR1);
00064     }
00065     signal (SIGUSR1, handler);
00066 }
00067 
00068 static void
00069 signal_cb (int signum)
00070 {
00071     if (signum == SIGHUP)
00072     {
00073         status_notify ("XSERVER :%d DISCONNECT-CLIENTS", display_number);
00074         indicate_ready ();
00075     }
00076     else
00077     {
00078         status_notify ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum);
00079         quit (EXIT_SUCCESS);
00080     }
00081 }
00082 
00083 static void
00084 xdmcp_query_cb (XDMCPClient *client)
00085 {
00086     static gboolean notified_query = FALSE;
00087 
00088     if (!notified_query)
00089     {
00090         status_notify ("XSERVER :%d SEND-QUERY", display_number);
00091         notified_query = TRUE;
00092     }
00093 }
00094 
00095 static void
00096 xdmcp_willing_cb (XDMCPClient *client, XDMCPWilling *message)
00097 {
00098     gchar **authorization_names;
00099     GInetAddress *addresses[2];
00100 
00101     status_notify ("XSERVER :%d GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", display_number, message->authentication_name, message->hostname, message->status);
00102 
00103     status_notify ("XSERVER :%d SEND-REQUEST DISPLAY-NUMBER=%d AUTHORIZATION-NAME=\"%s\" MFID=\"%s\"", display_number, display_number, "MIT-MAGIC-COOKIE-1", "TEST XSERVER");
00104 
00105     authorization_names = g_strsplit ("MIT-MAGIC-COOKIE-1", " ", -1);
00106     addresses[0] = xdmcp_client_get_local_address (client);
00107     addresses[1] = NULL;
00108     xdmcp_client_send_request (client, display_number,
00109                                addresses,
00110                                "", NULL, 0,
00111                                authorization_names, "TEST XSERVER");
00112     g_strfreev (authorization_names);
00113 }
00114 
00115 static void
00116 xdmcp_accept_cb (XDMCPClient *client, XDMCPAccept *message)
00117 {
00118     status_notify ("XSERVER :%d GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", display_number, message->session_id, message->authentication_name, message->authorization_name);
00119 
00120     /* Ignore if haven't picked a valid authorization */
00121     if (strcmp (message->authorization_name, "MIT-MAGIC-COOKIE-1") != 0)
00122         return;
00123 
00124     g_free (xdmcp_cookie);
00125     xdmcp_cookie_length = message->authorization_data_length;
00126     xdmcp_cookie = g_malloc (message->authorization_data_length);
00127     memcpy (xdmcp_cookie, message->authorization_data, message->authorization_data_length);
00128 
00129     status_notify ("XSERVER :%d SEND-MANAGE SESSION-ID=%d DISPLAY-NUMBER=%d DISPLAY-CLASS=\"%s\"", display_number, message->session_id, display_number, "DISPLAY CLASS");
00130     xdmcp_client_send_manage (client, message->session_id, display_number, "DISPLAY CLASS");
00131 }
00132 
00133 static void
00134 xdmcp_decline_cb (XDMCPClient *client, XDMCPDecline *message)
00135 {
00136     status_notify ("XSERVER :%d GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", display_number, message->status, message->authentication_name);  
00137 }
00138 
00139 static void
00140 xdmcp_failed_cb (XDMCPClient *client, XDMCPFailed *message)
00141 {
00142     status_notify ("XSERVER :%d GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", display_number, message->session_id, message->status);
00143 }
00144 
00145 static void
00146 x_client_connect_cb (XClient *client, XConnect *message)
00147 {
00148     gchar *auth_error = NULL;
00149 
00150     if (x_client_get_address (client))
00151         status_notify ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number);
00152     else
00153         status_notify ("XSERVER :%d ACCEPT-CONNECT", display_number);
00154 
00155     if (xdmcp_client)
00156     {
00157         if (!xdmcp_cookie)
00158             auth_error = g_strdup ("Need to authenticate with XDMCP");
00159         else
00160         {
00161             gboolean matches = TRUE;
00162             if (message->authorization_protocol_data_length == xdmcp_cookie_length)
00163             {
00164                 guint16 i;
00165                 for (i = 0; i < xdmcp_cookie_length && message->authorization_protocol_data[i] == xdmcp_cookie[i]; i++);
00166                 matches = i == xdmcp_cookie_length;
00167             }
00168             else
00169                 matches = FALSE;
00170 
00171             if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") != 0)
00172                 auth_error = g_strdup ("Authorization required");
00173             else if (!matches)
00174                 auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
00175         }
00176     }
00177     else if (auth_path)
00178     {
00179         XAuthority *authority;
00180         XAuthorityRecord *record = NULL;
00181         GError *error = NULL;
00182 
00183         authority = x_authority_new ();
00184         x_authority_load (authority, auth_path, &error);
00185         if (error)
00186             g_warning ("Error reading auth file: %s", error->message);
00187         g_clear_error (&error);
00188 
00189         if (x_client_get_address (client))
00190             record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote
00191         else
00192             record = x_authority_match_local (authority, message->authorization_protocol_name);
00193         if (record)
00194         {
00195             if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0)
00196             {
00197                 if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length))
00198                     auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
00199             }
00200             else
00201                 auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name);
00202         }
00203         else
00204             auth_error = g_strdup ("No authorization record");
00205     }
00206 
00207     if (auth_error)
00208         x_client_send_failed (client, auth_error);
00209     else
00210         x_client_send_success (client);
00211     g_free (auth_error);
00212 }
00213 
00214 static void
00215 client_connected_cb (XServer *server, XClient *client)
00216 {
00217     g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL);
00218 }
00219 
00220 static void
00221 client_disconnected_cb (XServer *server, XClient *client)
00222 {  
00223     g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);  
00224     if (x_server_get_n_clients (server) == 0)
00225         indicate_ready ();
00226 }
00227 
00228 static void
00229 request_cb (const gchar *request)
00230 {
00231     gchar *r;
00232   
00233     r = g_strdup_printf ("XSERVER :%d CRASH", display_number);
00234     if (strcmp (request, r) == 0)
00235     {
00236         cleanup ();
00237         kill (getpid (), SIGSEGV);
00238     }
00239     g_free (r);
00240 }
00241 
00242 int
00243 main (int argc, char **argv)
00244 {
00245     int i;
00246     char *pid_string;
00247     GMainLoop *loop;
00248     gboolean listen_tcp = TRUE;
00249     gboolean listen_unix = TRUE;
00250     gboolean do_xdmcp = FALSE;
00251     guint xdmcp_port = 0;
00252     gchar *xdmcp_host = NULL;
00253     int lock_file;
00254 
00255     signal (SIGINT, signal_cb);
00256     signal (SIGTERM, signal_cb);
00257     signal (SIGHUP, signal_cb);
00258   
00259     g_type_init ();
00260 
00261     loop = g_main_loop_new (NULL, FALSE);
00262 
00263     status_connect (request_cb);
00264 
00265     for (i = 1; i < argc; i++)
00266     {
00267         char *arg = argv[i];
00268 
00269         if (arg[0] == ':')
00270         {
00271             display_number = atoi (arg + 1);
00272         }
00273         else if (strcmp (arg, "-auth") == 0)
00274         {
00275             auth_path = argv[i+1];
00276             i++;
00277         }
00278         else if (strcmp (arg, "-nolisten") == 0)
00279         {
00280             char *protocol = argv[i+1];
00281             i++;
00282             if (strcmp (protocol, "tcp") == 0)
00283                 listen_tcp = FALSE;
00284             else if (strcmp (protocol, "unix") == 0)
00285                 listen_unix = FALSE;
00286         }
00287         else if (strcmp (arg, "-nr") == 0)
00288         {
00289         }
00290         else if (strcmp (arg, "-background") == 0)
00291         {
00292             /* Ignore arg */
00293             i++;
00294         }
00295         else if (strcmp (arg, "-port") == 0)
00296         {
00297             xdmcp_port = atoi (argv[i+1]);
00298             i++;
00299         }
00300         else if (strcmp (arg, "-query") == 0)
00301         {
00302             do_xdmcp = TRUE;
00303             xdmcp_host = argv[i+1];
00304             i++;
00305         }
00306         else if (strcmp (arg, "-broadcast") == 0)
00307         {
00308             do_xdmcp = TRUE;
00309         }
00310         else if (g_str_has_prefix (arg, "vt"))
00311         {
00312             /* Ignore VT args */
00313         }
00314         else if (g_str_has_prefix (arg, "-novtswitch"))
00315         {
00316             /* Ignore VT args */
00317         }
00318         else
00319         {
00320             g_printerr ("Unrecognized option: %s\n"
00321                         "Use: %s [:<display>] [option]\n"
00322                         "-auth file             Select authorization file\n"
00323                         "-nolisten protocol     Don't listen on protocol\n"
00324                         "-background [none]     Create root window with no background\n"
00325                         "-nr                    (Ubuntu-specific) Synonym for -background none\n"
00326                         "-query host-name       Contact named host for XDMCP\n"
00327                         "-broadcast             Broadcast for XDMCP\n"
00328                         "-port port-num         UDP port number to send messages to\n"
00329                         "vtxx                   Use virtual terminal xx instead of the next available\n",
00330                         arg, argv[0]);
00331             return EXIT_FAILURE;
00332         }
00333     }
00334 
00335     xserver = x_server_new (display_number);
00336     g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL);
00337     g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL);
00338     x_server_set_listen_unix (xserver, listen_unix);
00339     x_server_set_listen_tcp (xserver, listen_tcp);
00340 
00341     /* Add fake screen so that libx11 calls don't freak out when they can't find a screen */
00342     x_server_add_screen (xserver, 0xffffff, 0x000000, 0, 1024, 768, 1000, 1000);
00343 
00344     status_notify ("XSERVER :%d START", display_number);
00345 
00346     config = g_key_file_new ();
00347     g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
00348 
00349     if (g_key_file_has_key (config, "test-xserver-config", "return-value", NULL))
00350     {
00351         int return_value = g_key_file_get_integer (config, "test-xserver-config", "return-value", NULL);
00352         status_notify ("XSERVER :%d EXIT CODE=%d", display_number, return_value);
00353         return return_value;
00354     }
00355 
00356     lock_path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
00357     lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
00358     if (lock_file < 0)
00359     {
00360         char *lock_contents = NULL;
00361 
00362         if (g_file_get_contents (lock_path, &lock_contents, NULL, NULL))
00363         {
00364             gchar *proc_filename;
00365             pid_t pid;
00366 
00367             pid = atol (lock_contents);
00368             g_free (lock_contents);
00369 
00370             proc_filename = g_strdup_printf ("/proc/%d", pid);
00371             if (!g_file_test (proc_filename, G_FILE_TEST_EXISTS))
00372             {
00373                 gchar *socket_path;
00374 
00375                 socket_path = g_strdup_printf ("/tmp/.X11-unix/X%d", display_number);
00376 
00377                 g_printerr ("Breaking lock on non-existant process %d\n", pid);
00378                 unlink (lock_path);
00379                 unlink (socket_path);
00380 
00381                 g_free (socket_path);
00382             }
00383             g_free (proc_filename);
00384 
00385             lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
00386         }
00387     }
00388     if (lock_file < 0)
00389     {
00390         fprintf (stderr,
00391                  "Fatal server error:\n"
00392                  "Server is already active for display %d\n"
00393                  "   If this server is no longer running, remove %s\n"
00394                  "   and start again.\n", display_number, lock_path);
00395         g_free (lock_path);
00396         lock_path = NULL;
00397         quit (EXIT_FAILURE);
00398     }
00399     pid_string = g_strdup_printf ("%10ld", (long) getpid ());
00400     if (write (lock_file, pid_string, strlen (pid_string)) < 0)
00401     {
00402         g_warning ("Error writing PID file: %s", strerror (errno));
00403         quit (EXIT_FAILURE);
00404     }
00405     g_free (pid_string);
00406 
00407     if (!x_server_start (xserver))
00408         quit (EXIT_FAILURE);
00409 
00410     /* Enable XDMCP */
00411     if (do_xdmcp)
00412     {
00413         xdmcp_client = xdmcp_client_new ();
00414         if (xdmcp_host > 0)
00415             xdmcp_client_set_hostname (xdmcp_client, xdmcp_host);
00416         if (xdmcp_port > 0)
00417             xdmcp_client_set_port (xdmcp_client, xdmcp_port);
00418         g_signal_connect (xdmcp_client, "query", G_CALLBACK (xdmcp_query_cb), NULL);      
00419         g_signal_connect (xdmcp_client, "willing", G_CALLBACK (xdmcp_willing_cb), NULL);
00420         g_signal_connect (xdmcp_client, "accept", G_CALLBACK (xdmcp_accept_cb), NULL);
00421         g_signal_connect (xdmcp_client, "decline", G_CALLBACK (xdmcp_decline_cb), NULL);
00422         g_signal_connect (xdmcp_client, "failed", G_CALLBACK (xdmcp_failed_cb), NULL);
00423 
00424         if (!xdmcp_client_start (xdmcp_client))
00425             quit (EXIT_FAILURE);
00426     }
00427 
00428     /* Indicate ready if parent process has requested it */
00429     indicate_ready ();
00430 
00431     g_main_loop_run (loop);
00432 
00433     return EXIT_SUCCESS;
00434 }