]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/test-runner.c
Control more of D-Bus in the regression tests
[sojka/lightdm.git] / tests / src / test-runner.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <errno.h>
6 #include <glib.h>
7 #include <gio/gio.h>
8 #include <unistd.h>
9 #include <pwd.h>
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <sys/un.h>
13 #include <sys/wait.h>
14
15 /* For some reason sys/un.h doesn't define this */
16 #ifndef UNIX_PATH_MAX
17 #define UNIX_PATH_MAX 108
18 #endif
19
20 /* Timeout in ms waiting for the status we expect */
21 #define STATUS_TIMEOUT 2000
22
23 /* Timeout in ms to wait for SIGTERM to be handled by a child process */
24 #define KILL_TIMEOUT 2000
25
26 static gchar *config_path;
27 static GKeyFile *config;
28 static gchar *status_socket_name = NULL;
29 static GList *statuses = NULL;
30 static GList *script = NULL;
31 static GList *script_iter = NULL;
32 static guint status_timeout = 0;
33 static gchar *temp_dir = NULL;
34 static int service_count;
35 typedef struct
36 {
37     pid_t pid;
38     guint kill_timeout;
39 } Process;
40 static Process *lightdm_process = NULL;
41 static GHashTable *children = NULL;
42 static gboolean stop = FALSE;
43 static gint exit_status = 0;
44 static gchar *carols_xsession = NULL;
45 static GDBusConnection *accounts_connection = NULL;
46 static GDBusNodeInfo *accounts_info;
47 static GDBusNodeInfo *user_info;
48 static void handle_user_call (GDBusConnection       *connection,
49                               const gchar           *sender,
50                               const gchar           *object_path,
51                               const gchar           *interface_name,
52                               const gchar           *method_name,
53                               GVariant              *parameters,
54                               GDBusMethodInvocation *invocation,
55                               gpointer               user_data);
56 static GVariant *handle_user_get_property (GDBusConnection       *connection,
57                                            const gchar           *sender,
58                                            const gchar           *object_path,
59                                            const gchar           *interface_name,
60                                            const gchar           *property_name,
61                                            GError               **error,
62                                            gpointer               user_data);
63 static const GDBusInterfaceVTable user_vtable =
64 {
65     handle_user_call,
66     handle_user_get_property,
67 };
68
69 static void run_lightdm (void);
70 static void quit (int status);
71 static void check_status (const gchar *status);
72
73 static gboolean
74 kill_timeout_cb (gpointer data)
75 {
76     Process *process = data;
77
78     if (getenv ("DEBUG"))
79         g_print ("Sending SIGKILL to process %d\n", process->pid);
80     kill (process->pid, SIGKILL);
81     return FALSE;
82 }
83
84 static void
85 stop_process (Process *process)
86 {
87     if (process->kill_timeout != 0)
88         return;
89
90     if (getenv ("DEBUG"))
91         g_print ("Sending SIGTERM to process %d\n", process->pid);
92     kill (process->pid, SIGTERM);
93     process->kill_timeout = g_timeout_add (KILL_TIMEOUT, kill_timeout_cb, process);
94 }
95
96 static void
97 process_exit_cb (GPid pid, gint status, gpointer data)
98 {
99     Process *process;
100     gchar *status_text;
101   
102     if (getenv ("DEBUG"))
103     {
104         if (WIFEXITED (status))
105             g_print ("Process %d exited with status %d\n", pid, WEXITSTATUS (status));
106         else
107             g_print ("Process %d terminated with signal %d\n", pid, WTERMSIG (status));
108     }
109
110     if (lightdm_process && pid == lightdm_process->pid)
111     {
112         process = lightdm_process;
113         lightdm_process = NULL;
114         if (WIFEXITED (status))
115             status_text = g_strdup_printf ("RUNNER DAEMON-EXIT STATUS=%d", WEXITSTATUS (status));
116         else
117             status_text = g_strdup_printf ("RUNNER DAEMON-TERMINATE SIGNAL=%d", WTERMSIG (status));
118         check_status (status_text);
119     }
120     else
121     {
122         process = g_hash_table_lookup (children, GINT_TO_POINTER (pid));
123         if (!process)
124             return;
125         g_hash_table_remove (children, GINT_TO_POINTER (pid));
126     }
127
128     if (process->kill_timeout)
129         g_source_remove (process->kill_timeout);
130     process->kill_timeout = 0;
131
132     /* Quit once all children have stopped */
133     if (stop)
134         quit (exit_status);
135 }
136
137 static Process *
138 watch_process (pid_t pid)
139 {
140     Process *process;  
141
142     process = g_malloc0 (sizeof (Process));
143     process->pid = pid;
144     process->kill_timeout = 0;
145
146     if (getenv ("DEBUG"))
147         g_print ("Watching process %d\n", process->pid);
148     g_child_watch_add (process->pid, process_exit_cb, NULL);
149
150     return process;
151 }
152
153 static void
154 quit (int status)
155 {
156     GHashTableIter iter;
157
158     if (!stop)
159         exit_status = status;
160     stop = TRUE;
161
162     /* Stop all the children */
163     g_hash_table_iter_init (&iter, children);
164     while (TRUE)
165     {
166         gpointer key, value;
167
168         if (!g_hash_table_iter_next (&iter, &key, &value))
169             break;
170
171         stop_process ((Process *)value);
172     }
173
174     /* Don't quit until all children are stopped */
175     if (g_hash_table_size (children) > 0)
176         return;
177
178     /* Stop the daemon */
179     if (lightdm_process)
180     {
181         stop_process (lightdm_process);
182         return;
183     }
184
185     if (status_socket_name)
186         unlink (status_socket_name);
187
188     if (temp_dir)
189     {
190         gchar *command = g_strdup_printf ("rm -r %s", temp_dir);
191         if (system (command))
192             perror ("Failed to delete temp directory");
193     }
194
195     exit (status);
196 }
197
198 static void
199 fail (const gchar *event, const gchar *expected)
200 {
201     GList *link;
202
203     if (stop)
204         return;
205
206     g_printerr ("Test failed, got the following events:\n");
207     for (link = statuses; link; link = link->next)
208         g_printerr ("    %s\n", (gchar *)link->data);
209     if (event)
210         g_printerr ("    %s\n", event);
211     if (expected)
212         g_printerr ("    ^^^ expected \"%s\"\n", expected);
213     else
214         g_printerr ("^^^ expected nothing\n");
215
216     quit (EXIT_FAILURE);
217 }
218
219 static gchar *
220 get_script_line ()
221 {
222     if (!script_iter)
223         return NULL;
224     return script_iter->data;
225 }
226
227 static int
228 open_unix_socket (const gchar *name)
229 {
230     int s;
231     struct sockaddr_un address;
232
233     s = socket (AF_UNIX, SOCK_DGRAM, 0);
234     if (s < 0)
235         return -1;
236     address.sun_family = AF_UNIX;
237     strncpy (address.sun_path, name, UNIX_PATH_MAX);
238     if (bind (s, (struct sockaddr *) &address, sizeof (address)) < 0)
239         return -1;
240     return s;
241 }
242
243 static void
244 run_commands ()
245 {
246     /* Stop daemon if requested */
247     while (TRUE)
248     {
249         gchar *command, *name = NULL, *c;
250         GHashTable *params;
251
252         command = get_script_line ();
253         if (!command)
254             break;
255
256         /* Commands start with an asterisk */
257         if (command[0] != '*')
258             break;
259         statuses = g_list_append (statuses, g_strdup (command));
260         script_iter = script_iter->next;
261
262         c = command + 1;
263         while (*c && !isspace (*c))
264             c++;
265         name = g_strdup_printf ("%.*s", (int) (c - command - 1), command + 1);
266
267         params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
268         while (TRUE)
269         {
270             gchar *start, *param_name, *param_value;
271           
272             while (isspace (*c))
273                 c++;
274             start = c;
275             while (*c && !isspace (*c) && *c != '=')
276                 c++;
277             if (*c == '\0')
278                 break;
279
280             param_name = g_strdup_printf ("%.*s", (int) (c - start), start);
281
282             if (*c == '=')
283             {
284                 c++;
285                 while (isspace (*c))
286                     c++;
287                 if (*c == '\"')
288                 {
289                     gboolean escaped = FALSE;
290                     GString *value;
291
292                     c++;
293                     value = g_string_new ("");
294                     while (*c)
295                     {
296                         if (*c == '\\')
297                         {
298                             if (escaped)
299                             {
300                                 g_string_append_c (value, '\\');
301                                 escaped = FALSE;
302                             }
303                             else
304                                 escaped = TRUE;
305                         }
306                         else if (!escaped && *c == '\"')
307                             break;
308                         if (!escaped)
309                             g_string_append_c (value, *c);
310                         c++;
311                     }
312                     param_value = value->str;
313                     g_string_free (value, FALSE);
314                     if (*c == '\"')
315                         c++;
316                 }
317                 else
318                 {
319                     start = c;
320                     while (*c && !isspace (*c))
321                         c++;
322                     param_value = g_strdup_printf ("%.*s", (int) (c - start), start);
323                 }
324             }
325             else
326                 param_value = g_strdup ("");
327
328             g_hash_table_insert (params, param_name, param_value);
329         }
330
331         if (strcmp (name, "WAIT") == 0)
332         {
333             sleep (1);
334         }
335         else if (strcmp (name, "SWITCH-TO-GREETER") == 0)
336         {
337             g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
338                                          "org.freedesktop.DisplayManager",
339                                          "/org/freedesktop/DisplayManager/Seat0",
340                                          "org.freedesktop.DisplayManager.Seat",
341                                          "SwitchToGreeter",
342                                          g_variant_new ("()"),
343                                          G_VARIANT_TYPE ("()"),
344                                          G_DBUS_CALL_FLAGS_NONE,
345                                          1000,
346                                          NULL,
347                                          NULL);
348             check_status ("RUNNER SWITCH-TO-GREETER");
349         }
350         else if (strcmp (name, "SWITCH-TO-USER") == 0)
351         {
352             gchar *status_text, *username;
353           
354             username = g_hash_table_lookup (params, "USERNAME");
355             g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
356                                          "org.freedesktop.DisplayManager",
357                                          "/org/freedesktop/DisplayManager/Seat0",
358                                          "org.freedesktop.DisplayManager.Seat",
359                                          "SwitchToUser",
360                                          g_variant_new ("(ss)", username, ""),
361                                          G_VARIANT_TYPE ("()"),
362                                          G_DBUS_CALL_FLAGS_NONE,
363                                          1000,
364                                          NULL,
365                                          NULL);
366             status_text = g_strdup_printf ("RUNNER SWITCH-TO-USER USERNAME=%s", username);
367             check_status (status_text);
368             g_free (status_text);
369         }
370         else if (strcmp (name, "SWITCH-TO-GUEST") == 0)
371         {
372             g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL),
373                                          "org.freedesktop.DisplayManager",
374                                          "/org/freedesktop/DisplayManager/Seat0",
375                                          "org.freedesktop.DisplayManager.Seat",
376                                          "SwitchToGuest",
377                                          g_variant_new ("(s)", ""),
378                                          G_VARIANT_TYPE ("()"),
379                                          G_DBUS_CALL_FLAGS_NONE,
380                                          1000,
381                                          NULL,
382                                          NULL);
383             check_status ("RUNNER SWITCH-TO-GUEST");
384         }
385         else if (strcmp (name, "STOP-DAEMON") == 0)
386             stop_process (lightdm_process);
387         // FIXME: Make generic RUN-COMMAND
388         else if (strcmp (name, "START-XSERVER") == 0)
389         {
390             gchar *xserver_args, *command_line;
391             gchar **argv;
392             GPid pid;
393             Process *process;
394             GError *error = NULL;
395
396             xserver_args = g_hash_table_lookup (params, "ARGS");
397             if (!xserver_args)
398                 xserver_args = "";
399             command_line = g_strdup_printf ("%s/tests/src/X %s", BUILDDIR, xserver_args);
400
401             if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
402                 !g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error))
403             {
404                 g_printerr ("Error starting X server: %s", error->message);
405                 quit (EXIT_FAILURE);
406                 return;
407             }
408             process = watch_process (pid);
409             g_hash_table_insert (children, GINT_TO_POINTER (process->pid), process);
410         }
411         else if (strcmp (name, "START-VNC-CLIENT") == 0)
412         {
413             gchar *vnc_client_args, *command_line;
414             gchar **argv;
415             GPid pid;
416             Process *process;
417             GError *error = NULL;
418
419             vnc_client_args = g_hash_table_lookup (params, "ARGS");
420             if (!vnc_client_args)
421                 vnc_client_args = "";
422             command_line = g_strdup_printf ("%s/tests/src/vnc-client %s", BUILDDIR, vnc_client_args);
423
424             if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
425                 !g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error))
426             {
427                 g_printerr ("Error starting VNC client: %s", error->message);
428                 quit (EXIT_FAILURE);
429                 return;
430             }
431             process = watch_process (pid);
432             g_hash_table_insert (children, GINT_TO_POINTER (process->pid), process);
433         }
434         else
435         {
436             g_printerr ("Unknown command '%s'\n", name);
437             quit (EXIT_FAILURE);
438             return;
439         }
440
441         g_free (name);
442         g_hash_table_unref (params);
443     }
444
445     /* Stop at the end of the script */
446     if (get_script_line () == NULL)
447         quit (EXIT_SUCCESS);
448 }
449
450 static gboolean
451 status_timeout_cb (gpointer data)
452 {
453     fail ("(timeout)", get_script_line ());
454     return FALSE;
455 }
456
457 static void
458 check_status (const gchar *status)
459 {
460     gchar *pattern;
461
462     if (stop)
463         return;
464   
465     statuses = g_list_append (statuses, g_strdup (status));
466   
467     if (getenv ("DEBUG"))
468         g_print ("%s\n", status);
469
470     /* Try and match against expected */
471     pattern = get_script_line ();
472     if (!pattern || !g_regex_match_simple (pattern, status, 0, 0))
473     {
474         fail (NULL, pattern);
475         return;
476     }
477     script_iter = script_iter->next;
478
479     /* Restart timeout */
480     g_source_remove (status_timeout);
481     status_timeout = g_timeout_add (STATUS_TIMEOUT, status_timeout_cb, NULL);
482
483     run_commands ();
484 }
485
486 static gboolean
487 status_message_cb (GIOChannel *channel, GIOCondition condition, gpointer data)
488 {
489     int s;
490     guint8 buffer[1024];
491     ssize_t n_read;
492
493     s = g_io_channel_unix_get_fd (channel);
494     n_read = recv (s, buffer, 1023, 0);
495     if (n_read < 0)
496         g_warning ("Error reading from socket: %s", strerror (errno));
497     else if (n_read == 0)
498         return FALSE;
499     else
500     {
501         buffer[n_read] = '\0';
502         check_status ((gchar *) buffer);
503     }
504
505     return TRUE;
506 }
507
508 static void
509 signal_cb (int signum)
510 {
511     g_print ("Caught signal %d, quitting\n", signum);
512     quit (EXIT_FAILURE);
513 }
514
515 static void
516 load_script (const gchar *filename)
517 {
518     int i;
519     gchar *data, **lines;
520
521     if (!g_file_get_contents (filename, &data, NULL, NULL))
522     {
523         g_printerr ("Unable to load script: %s\n", filename);
524         quit (EXIT_FAILURE);
525     }
526
527     lines = g_strsplit (data, "\n", -1);
528     g_free (data);
529
530     /* Load lines with #? prefix as expected behaviour */
531     for (i = 0; lines[i]; i++)
532     {
533         gchar *line = g_strstrip (lines[i]);
534         if (g_str_has_prefix (line, "#?"))
535             script = g_list_append (script, g_strdup (line+2));
536     }
537     script_iter = script;
538     g_strfreev (lines);
539 }
540
541 static void
542 handle_ck_call (GDBusConnection       *connection,
543                 const gchar           *sender,
544                 const gchar           *object_path,
545                 const gchar           *interface_name,
546                 const gchar           *method_name,
547                 GVariant              *parameters,
548                 GDBusMethodInvocation *invocation,
549                 gpointer               user_data)
550 {
551     if (strcmp (method_name, "CanRestart") == 0)
552     {
553         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
554     }
555     else if (strcmp (method_name, "CanStop") == 0)
556     {
557         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
558     }
559     else if (strcmp (method_name, "CloseSession") == 0)
560     {
561         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
562     }
563     else if (strcmp (method_name, "OpenSession") == 0)
564     {
565         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "deadbeef"));      
566     }
567     else if (strcmp (method_name, "OpenSessionWithParameters") == 0)
568     {
569         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "deadbeef"));      
570     }
571     else if (strcmp (method_name, "Restart") == 0)
572     {
573         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
574     }
575     else if (strcmp (method_name, "Stop") == 0)
576     {
577         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
578     }
579 }
580
581 static void
582 ck_name_acquired_cb (GDBusConnection *connection,
583                      const gchar     *name,
584                      gpointer         user_data)
585 {
586     const gchar *ck_interface =
587         "<node>"
588         "  <interface name='org.freedesktop.ConsoleKit.Manager'>"
589         "    <method name='CanRestart'>"
590         "      <arg name='can_restart' direction='out' type='b'/>"
591         "    </method>"
592         "    <method name='CanStop'>"
593         "      <arg name='can_stop' direction='out' type='b'/>"
594         "    </method>"
595         "    <method name='CloseSession'>"
596         "      <arg name='cookie' direction='in' type='s'/>"
597         "      <arg name='result' direction='out' type='b'/>"
598         "    </method>"
599         "    <method name='OpenSession'>"
600         "      <arg name='cookie' direction='out' type='s'/>"
601         "    </method>"
602         "    <method name='OpenSessionWithParameters'>"
603         "      <arg name='parameters' direction='in' type='a(sv)'/>"
604         "      <arg name='cookie' direction='out' type='s'/>"
605         "    </method>"
606         "    <method name='Restart'/>"
607         "    <method name='Stop'/>"
608         "    <signal name='SeatAdded'>"
609         "      <arg name='seat' type='o'/>"
610         "    </signal>"
611         "    <signal name='SeatRemoved'>"
612         "      <arg name='seat' type='o'/>"
613         "    </signal>"
614         "  </interface>"
615         "</node>";
616     static const GDBusInterfaceVTable ck_vtable =
617     {
618         handle_ck_call,
619     };
620     GDBusNodeInfo *ck_info;
621     GError *error = NULL;
622
623     ck_info = g_dbus_node_info_new_for_xml (ck_interface, &error);
624     if (error)
625         g_warning ("Failed to parse D-Bus interface: %s", error->message);  
626     g_clear_error (&error);
627     if (!ck_info)
628         return;
629     g_dbus_connection_register_object (connection,
630                                        "/org/freedesktop/ConsoleKit/Manager",
631                                        ck_info->interfaces[0],
632                                        &ck_vtable,
633                                        NULL, NULL,
634                                        &error);
635     if (error)
636         g_warning ("Failed to register console kit service: %s", error->message);
637     g_clear_error (&error);
638     g_dbus_node_info_unref (ck_info);
639
640     service_count--;
641     if (service_count == 0)
642         run_lightdm ();
643 }
644
645 static void
646 start_console_kit_daemon ()
647 {  
648     g_bus_own_name (G_BUS_TYPE_SYSTEM,
649                     "org.freedesktop.ConsoleKit",
650                     G_BUS_NAME_OWNER_FLAGS_NONE,
651                     ck_name_acquired_cb,
652                     NULL,
653                     NULL,
654                     NULL,
655                     NULL);
656 }
657
658 static void
659 handle_accounts_call (GDBusConnection       *connection,
660                       const gchar           *sender,
661                       const gchar           *object_path,
662                       const gchar           *interface_name,
663                       const gchar           *method_name,
664                       GVariant              *parameters,
665                       GDBusMethodInvocation *invocation,
666                       gpointer               user_data)
667 {
668     if (strcmp (method_name, "FindUserByName") == 0)
669     {
670         gchar *name, *data, **lines;
671         int i;
672
673         g_variant_get (parameters, "(&s)", &name);
674
675         g_file_get_contents (g_getenv ("LIGHTDM_TEST_PASSWD_FILE"), &data, NULL, NULL);
676         lines = g_strsplit (data, "\n", -1);
677         g_free (data);
678
679         for (i = 0; lines[i]; i++)
680         {
681             gchar **fields;
682             fields = g_strsplit (lines[i], ":", -1);
683             if (strcmp (fields[0], name) == 0)
684             {
685                 gchar *path;
686                 GError *error = NULL;
687
688                 path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", atoi (fields[2]));
689
690                 g_dbus_connection_register_object (accounts_connection,
691                                                    path,
692                                                    user_info->interfaces[0],
693                                                    &user_vtable,
694                                                    NULL,
695                                                    NULL,
696                                                    &error);
697                 if (error)
698                     g_warning ("Failed to register user: %s", error->message);
699                 g_clear_error (&error);
700
701                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", path));
702                 g_free (path);
703                 return;
704             }
705         }
706       
707         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such user: %s", name);
708     }
709     else
710         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);    
711 }
712
713 static void
714 handle_user_call (GDBusConnection       *connection,
715                   const gchar           *sender,
716                   const gchar           *object_path,
717                   const gchar           *interface_name,
718                   const gchar           *method_name,
719                   GVariant              *parameters,
720                   GDBusMethodInvocation *invocation,
721                   gpointer               user_data)
722 {
723     if (strcmp (method_name, "SetXSession") == 0)
724     {
725         gchar *xsession;
726
727         g_variant_get (parameters, "(&s)", &xsession);
728
729         g_free (carols_xsession);
730         carols_xsession = g_strdup (xsession);
731
732         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
733     }
734     else
735         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
736 }
737
738 static GVariant *
739 handle_user_get_property (GDBusConnection       *connection,
740                           const gchar           *sender,
741                           const gchar           *object_path,
742                           const gchar           *interface_name,
743                           const gchar           *property_name,
744                           GError               **error,
745                           gpointer               user_data)
746 {
747     if (strcmp (property_name, "BackgroundFile") == 0)
748         return g_variant_new_string ("");
749     else if (strcmp (property_name, "Language") == 0)
750         return g_variant_new_string ("en_US");
751     else if (strcmp (property_name, "XSession") == 0)
752         return g_variant_new_string (carols_xsession ? carols_xsession : "");
753
754     return NULL;
755 }
756
757 static void
758 accounts_name_acquired_cb (GDBusConnection *connection,
759                            const gchar     *name,
760                            gpointer         user_data)
761 {
762     const gchar *accounts_interface =
763         "<node>"
764         "  <interface name='org.freedesktop.Accounts'>"
765         "    <method name='FindUserByName'>"
766         "      <arg name='name' direction='in' type='s'/>"
767         "      <arg name='user' direction='out' type='o'/>"
768         "    </method>"
769         "  </interface>"
770         "</node>";
771     static const GDBusInterfaceVTable accounts_vtable =
772     {
773         handle_accounts_call,
774     };
775     const gchar *user_interface =
776         "<node>"
777         "  <interface name='org.freedesktop.Accounts.User'>"
778         "    <method name='SetXSession'>"
779         "      <arg name='x_session' direction='in' type='s'/>"
780         "    </method>"
781         "    <property name='BackgroundFile' type='s' access='read'/>"
782         "    <property name='Language' type='s' access='read'/>"
783         "    <property name='XSession' type='s' access='read'/>"
784         "  </interface>"
785         "</node>";
786     GError *error = NULL;
787
788     accounts_connection = connection;
789
790     accounts_info = g_dbus_node_info_new_for_xml (accounts_interface, &error);
791     if (error)
792         g_warning ("Failed to parse D-Bus interface: %s", error->message);  
793     g_clear_error (&error);
794     if (!accounts_info)
795         return;
796     user_info = g_dbus_node_info_new_for_xml (user_interface, &error);
797     if (error)
798         g_warning ("Failed to parse D-Bus interface: %s", error->message);  
799     g_clear_error (&error);
800     if (!user_info)
801         return;
802     g_dbus_connection_register_object (connection,
803                                        "/org/freedesktop/Accounts",
804                                        accounts_info->interfaces[0],
805                                        &accounts_vtable,
806                                        NULL,
807                                        NULL,
808                                        &error);
809     if (error)
810         g_warning ("Failed to register accounts service: %s", error->message);
811     g_clear_error (&error);
812     g_dbus_node_info_unref (accounts_info);
813
814     service_count--;
815     if (service_count == 0)
816         run_lightdm ();
817 }
818
819 static void
820 start_accounts_service_daemon ()
821 {
822     g_bus_own_name (G_BUS_TYPE_SYSTEM,
823                     "org.freedesktop.Accounts",
824                     G_BUS_NAME_OWNER_FLAGS_NONE,
825                     accounts_name_acquired_cb,
826                     NULL,
827                     NULL,
828                     NULL,
829                     NULL);
830 }
831
832 static void
833 run_lightdm ()
834 {
835     GString *command_line;
836     gchar **lightdm_argv;
837     pid_t lightdm_pid;
838     GError *error = NULL;
839
840     run_commands ();
841
842     status_timeout = g_timeout_add (STATUS_TIMEOUT, status_timeout_cb, NULL);
843
844     command_line = g_string_new ("../src/lightdm");
845     if (getenv ("DEBUG"))
846         g_string_append (command_line, " --debug");
847     if (!g_key_file_has_key (config, "test-runner-config", "have-config", NULL) ||
848         g_key_file_get_boolean (config, "test-runner-config", "have-config", NULL))
849     {
850         g_string_append_printf (command_line, " --config %s", config_path);
851         g_setenv ("LIGHTDM_TEST_CONFIG", config_path, TRUE);
852     }
853     g_string_append_printf (command_line, " --cache-dir %s/cache", temp_dir);
854     g_string_append_printf (command_line, " --xsessions-dir=%s/tests/data/xsessions", SRCDIR);
855     g_string_append_printf (command_line, " --xgreeters-dir=%s/tests", BUILDDIR);
856
857     g_print ("Start daemon with command: PATH=%s LD_PRELOAD=%s LD_LIBRARY_PATH=%s LIGHTDM_TEST_STATUS_SOCKET=%s DBUS_SESSION_BUS_ADDRESS=%s %s\n",
858              g_getenv ("PATH"), g_getenv ("LD_PRELOAD"), g_getenv ("LD_LIBRARY_PATH"), g_getenv ("LIGHTDM_TEST_STATUS_SOCKET"), g_getenv ("DBUS_SESSION_BUS_ADDRESS"),
859              command_line->str);
860
861     if (!g_shell_parse_argv (command_line->str, NULL, &lightdm_argv, &error))
862     {
863         g_warning ("Error parsing command line: %s", error->message);
864         quit (EXIT_FAILURE);
865     }
866     g_clear_error (&error);
867
868     if (!g_spawn_async (NULL, lightdm_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &lightdm_pid, &error))
869     {
870         g_warning ("Error launching LightDM: %s", error->message);
871         quit (EXIT_FAILURE);
872     }
873     g_clear_error (&error);
874     lightdm_process = watch_process (lightdm_pid);
875
876     check_status ("RUNNER DAEMON-START");
877 }
878
879 int
880 main (int argc, char **argv)
881 {
882     GMainLoop *loop;
883     gchar *greeter = NULL, *script_name, *config_file, *path, *path1, *path2, *ld_preload, *ld_library_path, *home_dir;
884     GString *passwd_data;
885     int status_socket;
886     gchar cwd[1024];
887
888     signal (SIGINT, signal_cb);
889     signal (SIGTERM, signal_cb);
890
891     g_type_init ();
892
893     /* Use the session bus as the system bus */
894     g_setenv ("DBUS_SYSTEM_BUS_ADDRESS", g_getenv ("DBUS_SESSION_BUS_ADDRESS"), TRUE);
895
896     children = g_hash_table_new (g_direct_hash, g_direct_equal);
897
898     loop = g_main_loop_new (NULL, FALSE);
899
900     if (argc != 3)
901     {
902         g_printerr ("Usage %s SCRIPT-NAME GREETER\n", argv[0]);
903         quit (EXIT_FAILURE);
904     }
905     script_name = argv[1];
906     config_file = g_strdup_printf ("%s.conf", script_name);
907     config_path = g_build_filename (SRCDIR, "tests", "scripts", config_file, NULL);
908     g_free (config_file);
909
910     /* Link to the correct greeter */
911     path = g_build_filename (BUILDDIR, "tests", "default.desktop", NULL);
912     if (unlink (path) < 0 && errno != ENOENT)
913     {
914         g_printerr ("Failed to rm greeter symlink %s: %s\n", path, strerror (errno));
915         quit (EXIT_FAILURE);
916     }
917
918     greeter = g_strdup_printf ("%s.desktop", argv[2]);
919     path1 = g_build_filename (SRCDIR, "tests", "data", "xgreeters", greeter, NULL);
920     g_free(greeter);
921     if (symlink (path1, path) < 0)
922     {
923         g_printerr ("Failed to make greeter symlink %s->%s: %s\n", path, path1, strerror (errno));
924         quit (EXIT_FAILURE);
925     }
926     g_free (path);
927     g_free (path1);
928
929     config = g_key_file_new ();
930     g_key_file_load_from_file (config, config_path, G_KEY_FILE_NONE, NULL);
931
932     load_script (config_path);
933
934     g_print ("----------------------------------------\n");
935     g_print ("Running script %s\n", script_name);
936
937     if (!getcwd (cwd, 1024))
938     {
939         g_critical ("Error getting current directory: %s", strerror (errno));
940         quit (EXIT_FAILURE);
941     }
942   
943     /* Don't contact our X server */
944     g_unsetenv ("DISPLAY");
945
946     /* Start D-Bus services */
947     service_count = 2;
948     start_console_kit_daemon ();
949     start_accounts_service_daemon ();
950
951     /* Override system calls */
952     ld_preload = g_build_filename (BUILDDIR, "tests", "src", ".libs", "libsystem.so", NULL);
953     g_setenv ("LD_PRELOAD", ld_preload, TRUE);
954     g_free (ld_preload);
955
956     /* Run test programs */
957     path = g_strdup_printf ("%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s", BUILDDIR, BUILDDIR, SRCDIR, g_getenv ("PATH"));
958     g_setenv ("PATH", path, TRUE);
959     g_free (path);
960
961     /* Use locally built libraries */
962     path1 = g_build_filename (BUILDDIR, "liblightdm-gobject", ".libs", NULL);  
963     path2 = g_build_filename (BUILDDIR, "liblightdm-qt", ".libs", NULL);
964     ld_library_path = g_strdup_printf ("%s:%s", path1, path2);
965     g_free (path1);
966     g_free (path2);
967     g_setenv ("LD_LIBRARY_PATH", ld_library_path, TRUE);
968     g_free (ld_library_path);
969
970     /* Open socket for status */
971     status_socket_name = g_build_filename (cwd, ".status-socket", NULL);
972     g_setenv ("LIGHTDM_TEST_STATUS_SOCKET", status_socket_name, TRUE);
973     unlink (status_socket_name);  
974     status_socket = open_unix_socket (status_socket_name);
975     if (status_socket < 0)
976     {
977         g_warning ("Error opening status socket: %s", strerror (errno));
978         quit (EXIT_FAILURE);
979     }
980     g_io_add_watch (g_io_channel_unix_new (status_socket), G_IO_IN, status_message_cb, NULL);
981
982     /* Run from a temporary directory */
983     temp_dir = g_build_filename (cwd, "lightdm-test-XXXXXX", NULL);
984     if (!mkdtemp (temp_dir))
985     {
986         g_warning ("Error creating temporary directory: %s", strerror (errno));
987         quit (EXIT_FAILURE);
988     }
989     home_dir = g_build_filename (temp_dir, "home", NULL);
990     g_setenv ("LIGHTDM_TEST_HOME_DIR", home_dir, TRUE);
991     passwd_data = g_string_new ("");
992
993     /* Make fake users */
994     struct
995     {
996         gchar *user_name;
997         gchar *password;
998         gchar *real_name;
999         gchar *xsession;
1000         gint uid;
1001     } users[] =
1002     {
1003         {"root",    "",         "root",       NULL,             0},
1004         {"lightdm", "",         "",           NULL,           100},
1005         {"alice",   "password", "Alice User", NULL,          1000},
1006         {"bob",     "",         "Bob User",   NULL,          1001},
1007         {"carol",   "",         "Carol User", "alternative", 1002},
1008         {NULL,      NULL,       NULL,         NULL,             0}
1009     };
1010     int i;
1011     for (i = 0; users[i].user_name; i++)
1012     {
1013         path = g_build_filename (home_dir, users[i].user_name, NULL);
1014         g_mkdir_with_parents (path, 0755);
1015         g_free (path);
1016
1017         if (users[i].xsession)
1018         {
1019             path = g_build_filename (home_dir, users[i].user_name, ".dmrc", NULL);
1020             g_file_set_contents (path, g_strdup_printf ("[Desktop]\nSession=%s", users[i].xsession), -1, NULL);
1021             g_free (path);
1022         }
1023
1024         g_string_append_printf (passwd_data, "%s:%s:%d:%d:%s:%s/home/%s:/bin/sh\n", users[i].user_name, users[i].password, users[i].uid, users[i].uid, users[i].real_name, temp_dir, users[i].user_name);
1025     }
1026     path = g_build_filename (temp_dir, "passwd", NULL);
1027     g_setenv ("LIGHTDM_TEST_PASSWD_FILE", path, TRUE);
1028     g_file_set_contents (path, passwd_data->str, -1, NULL);
1029     g_free (path);
1030     g_string_free (passwd_data, TRUE);
1031
1032     g_main_loop_run (loop);
1033
1034     return EXIT_FAILURE;
1035 }