Back to index

lightdm  1.3.2
Xvnc.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 <fcntl.h>
00008 #include <errno.h>
00009 #include <gio/gio.h>
00010 
00011 #include "status.h"
00012 #include "x-server.h"
00013 #include "x-authority.h"
00014 
00015 static GKeyFile *config;
00016 
00017 /* Path to lock file */
00018 static gchar *lock_path = NULL;
00019 
00020 /* Path to authority database to use */
00021 static gchar *auth_path = NULL;
00022 
00023 /* Display number being served */
00024 static int display_number = 0;
00025 
00026 /* X server */
00027 static XServer *xserver = NULL;
00028 
00029 static void
00030 indicate_ready ()
00031 {
00032     void *handler;  
00033     handler = signal (SIGUSR1, SIG_IGN);
00034     if (handler == SIG_IGN)
00035     {
00036         status_notify ("XSERVER :%d INDICATE-READY", display_number);
00037         kill (getppid (), SIGUSR1);
00038     }
00039     signal (SIGUSR1, handler);
00040 }
00041 
00042 static void
00043 cleanup ()
00044 {
00045     if (lock_path)
00046         unlink (lock_path);
00047     if (xserver)
00048         g_object_unref (xserver);
00049 }
00050 
00051 static void
00052 quit (int status)
00053 {
00054     cleanup ();
00055     exit (status);
00056 }
00057 
00058 static void
00059 signal_cb (int signum)
00060 {
00061     if (signum == SIGHUP)
00062     {
00063         status_notify ("XSERVER :%d DISCONNECT-CLIENTS", display_number);
00064         indicate_ready ();
00065     }
00066     else
00067     {
00068         status_notify ("XSERVER :%d TERMINATE SIGNAL=%d", display_number, signum);
00069         quit (EXIT_SUCCESS);
00070     }
00071 }
00072 
00073 static void
00074 x_client_connect_cb (XClient *client, XConnect *message)
00075 {
00076     gchar *auth_error = NULL;
00077 
00078     if (x_client_get_address (client))
00079         status_notify ("XSERVER :%d TCP-ACCEPT-CONNECT", display_number);
00080     else
00081         status_notify ("XSERVER :%d ACCEPT-CONNECT", display_number);
00082 
00083     if (auth_path)
00084     {
00085         XAuthority *authority;
00086         XAuthorityRecord *record = NULL;
00087         GError *error = NULL;
00088 
00089         authority = x_authority_new ();
00090         x_authority_load (authority, auth_path, &error);
00091         if (error)
00092             g_warning ("Error reading auth file: %s", error->message);
00093         g_clear_error (&error);
00094 
00095         if (x_client_get_address (client))
00096             record = x_authority_match_localhost (authority, message->authorization_protocol_name); // FIXME: Should check if remote
00097         else
00098             record = x_authority_match_local (authority, message->authorization_protocol_name);
00099         if (record)
00100         {
00101             if (strcmp (message->authorization_protocol_name, "MIT-MAGIC-COOKIE-1") == 0)
00102             {
00103                 if (!x_authority_record_check_cookie (record, message->authorization_protocol_data, message->authorization_protocol_data_length))
00104                     auth_error = g_strdup_printf ("Invalid MIT-MAGIC-COOKIE key");
00105             }
00106             else
00107                 auth_error = g_strdup_printf ("Unknown authorization: '%s'", message->authorization_protocol_name);
00108         }
00109         else
00110             auth_error = g_strdup ("No authorization record");
00111     }
00112 
00113     if (auth_error)
00114         x_client_send_failed (client, auth_error);
00115     else
00116         x_client_send_success (client);
00117     g_free (auth_error);
00118 }
00119 
00120 static void
00121 client_connected_cb (XServer *server, XClient *client)
00122 {
00123     g_signal_connect (client, "connect", G_CALLBACK (x_client_connect_cb), NULL);
00124 }
00125 
00126 static void
00127 client_disconnected_cb (XServer *server, XClient *client)
00128 {  
00129     g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);  
00130     if (x_server_get_n_clients (server) == 0)
00131         indicate_ready ();
00132 }
00133 
00134 static gboolean
00135 vnc_data_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
00136 {
00137     gchar buffer[1024];
00138     gsize n_read;
00139     GIOStatus status;
00140     GError *error = NULL;
00141 
00142     status = g_io_channel_read_chars (channel, buffer, 1023, &n_read, &error);
00143     if (error)
00144         g_warning ("Error reading from VNC client: %s", error->message);
00145     g_clear_error (&error);
00146 
00147     if (status == G_IO_STATUS_NORMAL)
00148     {
00149         buffer[n_read] = '\0';
00150         if (g_str_has_suffix (buffer, "\n"))
00151             buffer[n_read-1] = '\0';
00152         status_notify ("XSERVER :%d VNC-CLIENT-CONNECT VERSION=\"%s\"", display_number, buffer);
00153     }
00154   
00155     return TRUE;
00156 }
00157 
00158 static void
00159 request_cb (const gchar *message)
00160 {
00161 }
00162 
00163 int
00164 main (int argc, char **argv)
00165 {
00166     GMainLoop *loop;
00167     char *pid_string;
00168     gboolean listen_tcp = TRUE;
00169     gboolean listen_unix = TRUE;
00170     gboolean use_inetd = FALSE;
00171     gchar *geometry = g_strdup ("640x480");
00172     gint depth = 8;
00173     int lock_file;
00174     int i;
00175 
00176     signal (SIGINT, signal_cb);
00177     signal (SIGTERM, signal_cb);
00178     signal (SIGHUP, signal_cb);
00179 
00180     g_type_init ();
00181 
00182     loop = g_main_loop_new (NULL, FALSE);
00183 
00184     status_connect (request_cb);
00185 
00186     for (i = 1; i < argc; i++)
00187     {
00188         char *arg = argv[i];
00189 
00190         if (arg[0] == ':')
00191         {
00192             display_number = atoi (arg + 1);
00193         }
00194         else if (strcmp (arg, "-auth") == 0)
00195         {
00196             auth_path = argv[i+1];
00197             i++;
00198         }
00199         else if (strcmp (arg, "-nolisten") == 0)
00200         {
00201             char *protocol = argv[i+1];
00202             i++;
00203             if (strcmp (protocol, "tcp") == 0)
00204                 listen_tcp = FALSE;
00205             else if (strcmp (protocol, "unix") == 0)
00206                 listen_unix = FALSE;
00207         }
00208         else if (strcmp (arg, "-geometry") == 0)
00209         {
00210             g_free (geometry);
00211             geometry = g_strdup (argv[i+1]);
00212             i++;         
00213         }
00214         else if (strcmp (arg, "-depth") == 0)
00215         {
00216             depth = atoi (argv[i+1]);
00217             i++;
00218         }
00219         else if (strcmp (arg, "-inetd") == 0)
00220         {
00221             use_inetd = TRUE;
00222         }
00223         else
00224         {
00225             g_printerr ("Unrecognized option: %s\n"
00226                         "Use: %s [:<display>] [option]\n"
00227                         "-auth file             Select authorization file\n"
00228                         "-nolisten protocol     Don't listen on protocol\n"
00229                         "-geometry WxH          Set framebuffer width & height\n"
00230                         "-depth D               Set framebuffer depth\n"
00231                         "-inetd                 Xvnc is launched by inetd\n",
00232                         arg, argv[0]);
00233             return EXIT_FAILURE;
00234         }
00235     }
00236   
00237     xserver = x_server_new (display_number);
00238     g_signal_connect (xserver, "client-connected", G_CALLBACK (client_connected_cb), NULL);
00239     g_signal_connect (xserver, "client-disconnected", G_CALLBACK (client_disconnected_cb), NULL);
00240     x_server_set_listen_unix (xserver, listen_unix);
00241     x_server_set_listen_tcp (xserver, listen_tcp);
00242 
00243     status_notify ("XSERVER :%d START GEOMETRY=%s DEPTH=%d", display_number, geometry, depth);
00244 
00245     config = g_key_file_new ();
00246     g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
00247   
00248     if (use_inetd)
00249     {
00250         /* Send server protocol version to client */
00251         g_print ("RFB 003.007\n");
00252 
00253         if (!g_io_add_watch (g_io_channel_unix_new (STDIN_FILENO), G_IO_IN, vnc_data_cb, NULL))
00254             return EXIT_FAILURE;
00255     }
00256     else
00257     {
00258         g_printerr ("Only supported in -inetd mode\n");
00259         return EXIT_FAILURE;
00260     }
00261 
00262     lock_path = g_strdup_printf ("/tmp/.X%d-lock", display_number);
00263     lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
00264     if (lock_file < 0)
00265     {
00266         fprintf (stderr,
00267                  "Fatal server error:\n"
00268                  "Server is already active for display %d\n"
00269                  "   If this server is no longer running, remove %s\n"
00270                  "   and start again.\n", display_number, lock_path);
00271         g_free (lock_path);
00272         lock_path = NULL;
00273         quit (EXIT_FAILURE);
00274     }
00275     pid_string = g_strdup_printf ("%10ld", (long) getpid ());
00276     if (write (lock_file, pid_string, strlen (pid_string)) < 0)
00277     {
00278         g_warning ("Error writing PID file: %s", strerror (errno));
00279         quit (EXIT_FAILURE);
00280     }
00281     g_free (pid_string);
00282 
00283     if (!x_server_start (xserver))
00284         quit (EXIT_FAILURE);
00285 
00286     /* Indicate ready if parent process has requested it */
00287     indicate_ready ();
00288 
00289     g_main_loop_run (loop);
00290 
00291     return EXIT_SUCCESS;
00292 }