]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/X.c
Make XDMCP tests more controllable
[sojka/lightdm.git] / tests / src / X.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <sys/stat.h>
7 #include <fcntl.h>
8 #include <errno.h>
9 #include <glib-unix.h>
10
11 #include "status.h"
12 #include "x-server.h"
13 #include "x-authority.h"
14 #include "xdmcp-client.h"
15
16 static GMainLoop *loop;
17 static int exit_status = EXIT_SUCCESS;
18
19 static GKeyFile *config;
20
21 /* Version to pretend to be */
22 static gchar *xorg_version;
23 static gint xorg_version_major, xorg_version_minor;
24
25 /* Path to lock file */
26 static gchar *lock_path = NULL;
27
28 /* TRUE if we allow TCP connections */
29 static gboolean listen_tcp = TRUE;
30
31 /* TRUE if we allow Unix connections */
32 static gboolean listen_unix = TRUE;
33
34 /* Configuration to use */
35 static gchar *config_file = NULL;
36
37 /* Configuration layout to use */
38 static gchar *layout = NULL;
39
40 /* Path to authority database to use */
41 static gchar *auth_path = NULL;
42
43 /* ID to use for test reporting */
44 static gchar *id;
45
46 /* Display number being served */
47 static int display_number = 0;
48
49 /* VT being run on */
50 static int vt_number = -1;
51
52 /* X server */
53 static XServer *xserver = NULL;
54
55 /* XDMCP client */
56 static XDMCPClient *xdmcp_client = NULL;
57
58 /* Session ID provided by XDMCP server */
59 static guint32 xdmcp_session_id = 0;
60
61 /* Authorization provided by XDMCP server */
62 static guint16 xdmcp_cookie_length = 0;
63 static guint8 *xdmcp_cookie = NULL;
64
65 static void
66 cleanup (void)
67 {
68     if (lock_path)
69         unlink (lock_path);
70     if (xserver)
71         g_object_unref (xserver);
72     if (xdmcp_client)
73         g_object_unref (xdmcp_client);
74 }
75
76 static void
77 quit (int status)
78 {
79     exit_status = status;
80     g_main_loop_quit (loop);
81 }
82
83 static gboolean
84 sighup_cb (gpointer user_data)
85 {
86     status_notify ("%s DISCONNECT-CLIENTS", id);
87     return TRUE;
88 }
89
90 static gboolean
91 sigint_cb (gpointer user_data)
92 {
93     status_notify ("%s TERMINATE SIGNAL=%d", id, SIGINT);
94     quit (EXIT_SUCCESS);
95     return TRUE;
96 }
97
98 static gboolean
99 sigterm_cb (gpointer user_data)
100 {
101     status_notify ("%s TERMINATE SIGNAL=%d", id, SIGTERM);
102     quit (EXIT_SUCCESS);
103     return TRUE;
104 }
105
106 static void
107 xdmcp_query_cb (XDMCPClient *client)
108 {
109     static gboolean notified_query = FALSE;
110
111     if (!notified_query)
112     {
113         status_notify ("%s SEND-QUERY", id);
114         notified_query = TRUE;
115     }
116 }
117
118 static void
119 xdmcp_willing_cb (XDMCPClient *client, XDMCPWilling *message)
120 {
121     status_notify ("%s GOT-WILLING AUTHENTICATION-NAME=\"%s\" HOSTNAME=\"%s\" STATUS=\"%s\"", id, message->authentication_name, message->hostname, message->status);
122 }
123
124 static void
125 xdmcp_accept_cb (XDMCPClient *client, XDMCPAccept *message)
126 {
127     status_notify ("%s GOT-ACCEPT SESSION-ID=%d AUTHENTICATION-NAME=\"%s\" AUTHORIZATION-NAME=\"%s\"", id, message->session_id, message->authentication_name, message->authorization_name);
128
129     xdmcp_session_id = message->session_id;
130
131     g_free (xdmcp_cookie);
132     xdmcp_cookie_length = message->authorization_data_length;
133     xdmcp_cookie = g_malloc (message->authorization_data_length);
134     memcpy (xdmcp_cookie, message->authorization_data, message->authorization_data_length);
135 }
136
137 static void
138 xdmcp_decline_cb (XDMCPClient *client, XDMCPDecline *message)
139 {
140     status_notify ("%s GOT-DECLINE STATUS=\"%s\" AUTHENTICATION-NAME=\"%s\"", id, message->status, message->authentication_name);
141 }
142
143 static void
144 xdmcp_failed_cb (XDMCPClient *client, XDMCPFailed *message)
145 {
146     status_notify ("%s GOT-FAILED SESSION-ID=%d STATUS=\"%s\"", id, message->session_id, message->status);
147 }
148
149 static void
150 client_connected_cb (XServer *server, XClient *client)
151 {
152     status_notify ("%s ACCEPT-CONNECT", id);
153     x_client_send_success (client);
154 }
155
156 static void
157 client_disconnected_cb (XServer *server, XClient *client)
158 {
159     g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, NULL);
160 }
161
162 static void
163 request_cb (const gchar *name, GHashTable *params)
164 {
165     if (!name)
166     {
167         g_main_loop_quit (loop);
168         return;
169     }
170
171     if (strcmp (name, "CRASH") == 0)
172     {
173         cleanup ();
174         kill (getpid (), SIGSEGV);
175     }
176
177     else if (strcmp (name, "INDICATE-READY") == 0)
178     {
179         void *handler;
180
181         handler = signal (SIGUSR1, SIG_IGN);
182         if (handler == SIG_IGN)
183         {
184             status_notify ("%s INDICATE-READY", id);
185             kill (getppid (), SIGUSR1);
186         }
187         signal (SIGUSR1, handler);
188     }
189
190     else if (strcmp (name, "START-XDMCP") == 0)
191     {
192         if (!xdmcp_client_start (xdmcp_client))
193             quit (EXIT_FAILURE);
194     }
195
196     else if (strcmp (name, "SEND-REQUEST") == 0)
197     {
198         const gchar *addresses_list, *authorization_names_list, *mfid;
199         gchar **list, **authorization_names;
200         gsize list_length;
201         gint i;
202         GInetAddress **addresses;
203
204         addresses_list = g_hash_table_lookup (params, "ADDRESSES");
205         if (!addresses_list)
206             addresses_list = "";
207         authorization_names_list = g_hash_table_lookup (params, "AUTHORIZATION-NAMES");
208         if (!authorization_names_list)
209             authorization_names_list = "";
210         mfid = g_hash_table_lookup (params, "MFID");
211         if (!mfid)
212             mfid = "";
213
214         list = g_strsplit (addresses_list, " ", -1);
215         list_length = g_strv_length (list);
216         addresses = g_malloc (sizeof (GInetAddress *) * (list_length + 1));
217         for (i = 0; i < list_length; i++)
218             addresses[i] = g_inet_address_new_from_string (list[i]);
219         addresses[i] = NULL;
220         g_strfreev (list);
221
222         authorization_names = g_strsplit (authorization_names_list, " ", -1);
223
224         xdmcp_client_send_request (xdmcp_client, display_number,
225                                    addresses,
226                                    "", NULL, 0,
227                                    authorization_names, mfid);
228         g_strfreev (authorization_names);
229     }
230
231     else if (strcmp (name, "SEND-MANAGE") == 0)
232     {
233         xdmcp_client_send_manage (xdmcp_client, xdmcp_session_id, display_number, "DISPLAY CLASS");
234     }
235 }
236
237 static int
238 version_compare (int major, int minor)
239 {
240     if (major == xorg_version_major)
241         return xorg_version_minor - minor;
242     else
243         return xorg_version_major - major;
244 }
245
246 int
247 main (int argc, char **argv)
248 {
249     int i;
250     gchar **tokens;
251     char *pid_string;
252     gboolean do_xdmcp = FALSE;
253     guint xdmcp_port = 0;
254     gchar *xdmcp_host = NULL;
255     gchar *seat = NULL;
256     gchar *mir_id = NULL;
257     gchar *lock_filename;
258     int lock_file;
259     GString *status_text;
260
261 #if !defined(GLIB_VERSION_2_36)
262     g_type_init ();
263 #endif
264
265     loop = g_main_loop_new (NULL, FALSE);
266
267     g_unix_signal_add (SIGINT, sigint_cb, NULL);
268     g_unix_signal_add (SIGTERM, sigterm_cb, NULL);
269     g_unix_signal_add (SIGHUP, sighup_cb, NULL);
270
271     config = g_key_file_new ();
272     g_key_file_load_from_file (config, g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "script", NULL), G_KEY_FILE_NONE, NULL);
273
274     xorg_version = g_key_file_get_string (config, "test-xserver-config", "version", NULL);
275     if (!xorg_version)
276         xorg_version = g_strdup ("1.17.0");
277     tokens = g_strsplit (xorg_version, ".", -1);
278     xorg_version_major = g_strv_length (tokens) > 0 ? atoi (tokens[0]) : 0;
279     xorg_version_minor = g_strv_length (tokens) > 1 ? atoi (tokens[1]) : 0;
280     g_strfreev (tokens);
281
282     /* TCP listening default changed in 1.17.0 */
283     listen_tcp = version_compare (1, 17) < 0;
284
285     for (i = 1; i < argc; i++)
286     {
287         char *arg = argv[i];
288
289         if (arg[0] == ':')
290         {
291             display_number = atoi (arg + 1);
292         }
293         else if (strcmp (arg, "-config") == 0)
294         {
295             config_file = argv[i+1];
296             i++;
297         }
298         else if (strcmp (arg, "-layout") == 0)
299         {
300             layout = argv[i+1];
301             i++;
302         }
303         else if (strcmp (arg, "-auth") == 0)
304         {
305             auth_path = argv[i+1];
306             i++;
307         }
308         else if (strcmp (arg, "-listen") == 0 && version_compare (1, 17) >= 0)
309         {
310             char *protocol = argv[i+1];
311             i++;
312             if (strcmp (protocol, "tcp") == 0)
313                 listen_tcp = TRUE;
314             else if (strcmp (protocol, "unix") == 0)
315                 listen_unix = TRUE;
316         }
317         else if (strcmp (arg, "-nolisten") == 0)
318         {
319             char *protocol = argv[i+1];
320             i++;
321             if (strcmp (protocol, "tcp") == 0)
322                 listen_tcp = FALSE;
323             else if (strcmp (protocol, "unix") == 0)
324                 listen_unix = FALSE;
325         }
326         else if (strcmp (arg, "-nr") == 0)
327         {
328         }
329         else if (strcmp (arg, "-background") == 0)
330         {
331             /* Ignore arg */
332             i++;
333         }
334         else if (strcmp (arg, "-port") == 0)
335         {
336             xdmcp_port = atoi (argv[i+1]);
337             i++;
338         }
339         else if (strcmp (arg, "-query") == 0)
340         {
341             do_xdmcp = TRUE;
342             xdmcp_host = argv[i+1];
343             listen_tcp = TRUE;
344             i++;
345         }
346         else if (strcmp (arg, "-broadcast") == 0)
347         {
348             do_xdmcp = TRUE;
349             listen_tcp = TRUE;
350         }
351         else if (g_str_has_prefix (arg, "vt"))
352         {
353             vt_number = atoi (arg + 2);
354         }
355         else if (strcmp (arg, "-novtswitch") == 0)
356         {
357             /* Ignore VT args */
358         }
359         else if (strcmp (arg, "-seat") == 0)
360         {
361             seat = argv[i+1];
362             i++;
363         }
364         else if (strcmp (arg, "-mir") == 0)
365         {
366             mir_id = argv[i+1];
367             i++;
368         }
369         else if (strcmp (arg, "-mirSocket") == 0)
370         {
371             /* FIXME */
372             i++;
373         }
374         else if (strcmp (arg, "-version") == 0)
375         {
376             fprintf (stderr, "\nX.Org X Server %s\nBlah blah blah\n", xorg_version);
377             return EXIT_SUCCESS;
378         }
379         else
380         {
381             g_printerr ("Unrecognized option: %s\n"
382                         "Use: %s [:<display>] [option]\n"
383                         "-config file           Specify a configuration file\n"
384                         "-layout name           Specify the ServerLayout section name\n"
385                         "-auth file             Select authorization file\n"
386                         "-nolisten protocol     Don't listen on protocol\n"
387                         "-listen protocol       Listen on protocol\n"
388                         "-background [none]     Create root window with no background\n"
389                         "-nr                    (Ubuntu-specific) Synonym for -background none\n"
390                         "-query host-name       Contact named host for XDMCP\n"
391                         "-broadcast             Broadcast for XDMCP\n"
392                         "-port port-num         UDP port number to send messages to\n"
393                         "-seat string           seat to run on\n"
394                         "-mir id                Mir ID to use\n"
395                         "-mirSocket name        Mir socket to use\n"
396                         "-version               show the server version\n"
397                         "vtxx                   Use virtual terminal xx instead of the next available\n",
398                         arg, argv[0]);
399             return EXIT_FAILURE;
400         }
401     }
402
403     id = g_strdup_printf ("XSERVER-%d", display_number);
404
405     status_connect (request_cb, id);
406
407     xserver = x_server_new (display_number);
408     g_signal_connect (xserver, X_SERVER_SIGNAL_CLIENT_CONNECTED, G_CALLBACK (client_connected_cb), NULL);
409     g_signal_connect (xserver, X_SERVER_SIGNAL_CLIENT_DISCONNECTED, G_CALLBACK (client_disconnected_cb), NULL);
410
411     status_text = g_string_new ("");
412     g_string_printf (status_text, "%s START", id);
413     if (config_file)
414         g_string_append_printf (status_text, " CONFIG=%s", config_file);
415     if (layout)
416         g_string_append_printf (status_text, " LAYOUT=%s", layout);
417     if (vt_number >= 0)
418         g_string_append_printf (status_text, " VT=%d", vt_number);
419     if (listen_tcp)
420         g_string_append (status_text, " LISTEN-TCP");
421     if (!listen_unix)
422         g_string_append (status_text, " NO-LISTEN-UNIX");
423     if (seat != NULL)
424         g_string_append_printf (status_text, " SEAT=%s", seat);
425     if (mir_id != NULL)
426         g_string_append_printf (status_text, " MIR-ID=%s", mir_id);
427     status_notify ("%s", status_text->str);
428     g_string_free (status_text, TRUE);
429
430     if (g_key_file_has_key (config, "test-xserver-config", "return-value", NULL))
431     {
432         int return_value = g_key_file_get_integer (config, "test-xserver-config", "return-value", NULL);
433         status_notify ("%s EXIT CODE=%d", id, return_value);
434         return return_value;
435     }
436
437     lock_filename = g_strdup_printf (".X%d-lock", display_number);
438     lock_path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", lock_filename, NULL);
439     g_free (lock_filename);
440     lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
441     if (lock_file < 0)
442     {
443         char *lock_contents = NULL;
444
445         if (g_file_get_contents (lock_path, &lock_contents, NULL, NULL))
446         {
447             gchar *proc_filename;
448             pid_t pid;
449
450             pid = atol (lock_contents);
451             g_free (lock_contents);
452
453             proc_filename = g_strdup_printf ("/proc/%d", pid);
454             if (!g_file_test (proc_filename, G_FILE_TEST_EXISTS))
455             {
456                 gchar *socket_dir;
457                 gchar *socket_filename;
458                 gchar *socket_path;
459
460                 socket_dir = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "tmp", ".X11-unix", NULL);
461                 g_mkdir_with_parents (socket_dir, 0755);
462
463                 socket_filename = g_strdup_printf ("X%d", display_number);
464                 socket_path = g_build_filename (socket_dir, socket_filename, NULL);
465
466                 g_printerr ("Breaking lock on non-existant process %d\n", pid);
467                 unlink (lock_path);
468                 unlink (socket_path);
469
470                 g_free (socket_dir);
471                 g_free (socket_filename);
472                 g_free (socket_path);
473             }
474             g_free (proc_filename);
475
476             lock_file = open (lock_path, O_CREAT | O_EXCL | O_WRONLY, 0444);
477         }
478     }
479     if (lock_file < 0)
480     {
481         fprintf (stderr,
482                  "Fatal server error:\n"
483                  "Server is already active for display %d\n"
484                  "      If this server is no longer running, remove %s\n"
485                  "      and start again.\n", display_number, lock_path);
486         g_free (lock_path);
487         lock_path = NULL;
488         return EXIT_FAILURE;
489     }
490     pid_string = g_strdup_printf ("%10ld", (long) getpid ());
491     if (write (lock_file, pid_string, strlen (pid_string)) < 0)
492     {
493         g_warning ("Error writing PID file: %s", strerror (errno));
494         return EXIT_FAILURE;
495     }
496     g_free (pid_string);
497
498     if (!x_server_start (xserver))
499         return EXIT_FAILURE;
500
501     /* Enable XDMCP */
502     if (do_xdmcp)
503     {
504         xdmcp_client = xdmcp_client_new ();
505         if (xdmcp_host > 0)
506             xdmcp_client_set_hostname (xdmcp_client, xdmcp_host);
507         if (xdmcp_port > 0)
508             xdmcp_client_set_port (xdmcp_client, xdmcp_port);
509         g_signal_connect (xdmcp_client, XDMCP_CLIENT_SIGNAL_QUERY, G_CALLBACK (xdmcp_query_cb), NULL);
510         g_signal_connect (xdmcp_client, XDMCP_CLIENT_SIGNAL_WILLING, G_CALLBACK (xdmcp_willing_cb), NULL);
511         g_signal_connect (xdmcp_client, XDMCP_CLIENT_SIGNAL_ACCEPT, G_CALLBACK (xdmcp_accept_cb), NULL);
512         g_signal_connect (xdmcp_client, XDMCP_CLIENT_SIGNAL_DECLINE, G_CALLBACK (xdmcp_decline_cb), NULL);
513         g_signal_connect (xdmcp_client, XDMCP_CLIENT_SIGNAL_FAILED, G_CALLBACK (xdmcp_failed_cb), NULL);
514     }
515
516     g_main_loop_run (loop);
517
518     cleanup ();
519
520     return exit_status;
521 }