]> rtime.felk.cvut.cz Git - sojka/lightdm.git/blob - tests/src/test-runner.c
Fix warning when tests complete
[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 <glib/gstdio.h>
8 #include <glib-unix.h>
9 #include <gio/gio.h>
10 #include <gio/gunixsocketaddress.h>
11 #include <unistd.h>
12 #include <pwd.h>
13
14 /* Timeout in ms waiting for the status we expect */
15 static int status_timeout_ms = 4000;
16
17 /* Timeout in ms to wait for SIGTERM to be handled by a child process */
18 #define KILL_TIMEOUT 2000
19
20 static gchar *test_runner_command;
21 static gchar *config_path;
22 static GKeyFile *config;
23 static GSocket *status_socket = NULL;
24 static gchar *status_socket_name = NULL;
25 static GList *statuses = NULL;
26 typedef struct
27 {
28     gchar *text;
29     gboolean done;
30 } ScriptLine;
31 static GList *script = 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 GDBusConnection *accounts_connection = NULL;
45 static GDBusNodeInfo *accounts_info;
46 static GDBusNodeInfo *user_info;
47 typedef struct
48 {
49     guint uid;
50     gchar *user_name;
51     gchar *real_name;
52     gchar *home_directory;
53     gchar *image;
54     gchar *background;
55     gchar *path;
56     guint id;
57     gchar *language;
58     gchar *xsession;
59     gchar **layouts;
60     gboolean has_messages;
61     gboolean hidden;
62 } AccountsUser;
63 static GList *accounts_users = NULL;
64 static void handle_user_call (GDBusConnection       *connection,
65                               const gchar           *sender,
66                               const gchar           *object_path,
67                               const gchar           *interface_name,
68                               const gchar           *method_name,
69                               GVariant              *parameters,
70                               GDBusMethodInvocation *invocation,
71                               gpointer               user_data);
72 static GVariant *handle_user_get_property (GDBusConnection       *connection,
73                                            const gchar           *sender,
74                                            const gchar           *object_path,
75                                            const gchar           *interface_name,
76                                            const gchar           *property_name,
77                                            GError               **error,
78                                            gpointer               user_data);
79 static const GDBusInterfaceVTable user_vtable =
80 {
81     handle_user_call,
82     handle_user_get_property,
83 };
84 static GDBusNodeInfo *ck_session_info;
85 typedef struct
86 {
87     gchar *cookie;
88     gchar *path;
89     guint id;
90     gboolean locked;
91 } CKSession;
92 static GList *ck_sessions = NULL;
93 static gint ck_session_index = 0;
94 static void handle_ck_session_call (GDBusConnection       *connection,
95                                     const gchar           *sender,
96                                     const gchar           *object_path,
97                                     const gchar           *interface_name,
98                                     const gchar           *method_name,
99                                     GVariant              *parameters,
100                                     GDBusMethodInvocation *invocation,
101                                     gpointer               user_data);
102 static const GDBusInterfaceVTable ck_session_vtable =
103 {
104     handle_ck_session_call,
105 };
106
107 typedef struct
108 {
109     gchar *id;
110     gchar *path;
111     gboolean can_graphical;
112     gboolean can_multi_session;
113     gchar *active_session;
114 } Login1Seat;
115
116 static GList *login1_seats = NULL;
117
118 static Login1Seat *add_login1_seat (GDBusConnection *connection, const gchar *id, gboolean emit_signal);
119 static Login1Seat *find_login1_seat (const gchar *id);
120 static void remove_login1_seat (GDBusConnection *connection, const gchar *id);
121
122 typedef struct
123 {
124     gchar *id;
125     gchar *path;
126     guint pid;
127     gboolean locked;
128 } Login1Session;
129
130 static GList *login1_sessions = NULL;
131 static gint login1_session_index = 0;
132
133 typedef struct
134 {
135     GSocket *socket;
136     GSource *source;
137 } StatusClient;
138 static GList *status_clients = NULL;
139
140 static void ready (void);
141 static void quit (int status);
142 static gboolean status_timeout_cb (gpointer data);
143 static void check_status (const gchar *status);
144 static AccountsUser *get_accounts_user_by_uid (guint uid);
145 static AccountsUser *get_accounts_user_by_name (const gchar *username);
146 static void accounts_user_set_hidden (AccountsUser *user, gboolean hidden, gboolean emit_signal);
147 static Login1Session *find_login1_session (const gchar *id);
148
149 static gboolean
150 kill_timeout_cb (gpointer data)
151 {
152     Process *process = data;
153
154     process->kill_timeout = 0;
155
156     if (getenv ("DEBUG"))
157         g_print ("Sending SIGKILL to process %d\n", process->pid);
158     kill (process->pid, SIGKILL);
159
160     return FALSE;
161 }
162
163 static void
164 stop_process (Process *process)
165 {
166     if (process->kill_timeout != 0)
167         return;
168
169     if (getenv ("DEBUG"))
170         g_print ("Sending SIGTERM to process %d\n", process->pid);
171     kill (process->pid, SIGTERM);
172     process->kill_timeout = g_timeout_add (KILL_TIMEOUT, kill_timeout_cb, process);
173 }
174
175 static void
176 process_exit_cb (GPid pid, gint status, gpointer data)
177 {
178     Process *process;
179     gchar *status_text;
180
181     if (getenv ("DEBUG"))
182     {
183         if (WIFEXITED (status))
184             g_print ("Process %d exited with status %d\n", pid, WEXITSTATUS (status));
185         else
186             g_print ("Process %d terminated with signal %d\n", pid, WTERMSIG (status));
187     }
188
189     if (lightdm_process && pid == lightdm_process->pid)
190     {
191         process = lightdm_process;
192         lightdm_process = NULL;
193         if (WIFEXITED (status))
194             status_text = g_strdup_printf ("RUNNER DAEMON-EXIT STATUS=%d", WEXITSTATUS (status));
195         else
196             status_text = g_strdup_printf ("RUNNER DAEMON-TERMINATE SIGNAL=%d", WTERMSIG (status));
197         check_status (status_text);
198     }
199     else
200     {
201         process = g_hash_table_lookup (children, GINT_TO_POINTER (pid));
202         if (!process)
203             return;
204         g_hash_table_remove (children, GINT_TO_POINTER (pid));
205     }
206
207     if (process->kill_timeout)
208         g_source_remove (process->kill_timeout);
209     process->kill_timeout = 0;
210
211     /* Quit once all children have stopped */
212     if (stop)
213         quit (exit_status);
214 }
215
216 static Process *
217 watch_process (pid_t pid)
218 {
219     Process *process;
220
221     process = g_malloc0 (sizeof (Process));
222     process->pid = pid;
223     process->kill_timeout = 0;
224
225     if (getenv ("DEBUG"))
226         g_print ("Watching process %d\n", process->pid);
227     g_child_watch_add (process->pid, process_exit_cb, NULL);
228
229     return process;
230 }
231
232 static void
233 quit (int status)
234 {
235     GHashTableIter iter;
236
237     if (!stop)
238         exit_status = status;
239     stop = TRUE;
240
241     /* Stop all the children */
242     g_hash_table_iter_init (&iter, children);
243     while (TRUE)
244     {
245         gpointer key, value;
246
247         if (!g_hash_table_iter_next (&iter, &key, &value))
248             break;
249
250         stop_process ((Process *)value);
251     }
252
253     /* Don't quit until all children are stopped */
254     if (g_hash_table_size (children) > 0)
255         return;
256
257     /* Stop the daemon */
258     if (lightdm_process)
259     {
260         stop_process (lightdm_process);
261         return;
262     }
263
264     if (status_socket_name)
265         unlink (status_socket_name);
266
267     if (temp_dir && getenv ("DEBUG") == NULL)
268     {
269         gchar *command = g_strdup_printf ("rm -rf %s", temp_dir);
270         if (system (command))
271             perror ("Failed to delete temp directory");
272     }
273
274     exit (status);
275 }
276
277 static void
278 fail (const gchar *event, const gchar *expected)
279 {
280     GList *link;
281
282     if (stop)
283         return;
284
285     g_printerr ("Command line: %s", test_runner_command);
286     g_printerr ("Events:\n");
287     for (link = statuses; link; link = link->next)
288         g_printerr ("    %s\n", (gchar *)link->data);
289     if (event)
290         g_printerr ("    %s\n", event);
291     if (expected)
292         g_printerr ("    ^^^ expected \"%s\"\n", expected);
293     else
294         g_printerr ("^^^ expected nothing\n");
295
296     quit (EXIT_FAILURE);
297 }
298
299 static gchar *
300 get_prefix (const gchar *text)
301 {
302     gchar *prefix;
303     gint i;
304
305     prefix = g_strdup (text);
306     for (i = 0; prefix[i] != '\0' && prefix[i] != ' '; i++);
307     prefix[i] = '\0';
308
309     return prefix;
310 }
311
312 static ScriptLine *
313 get_script_line (const gchar *prefix)
314 {
315     GList *link;
316
317     for (link = script; link; link = link->next)
318     {
319         ScriptLine *line = link->data;
320
321         /* Ignore lines with other prefixes */
322         if (prefix)
323         {
324             gchar *p;
325             gboolean matches;
326
327             p = get_prefix (line->text);
328             matches = strcmp (prefix, p) == 0;
329             g_free (p);
330
331             if (!matches)
332                 continue;
333         }
334
335         if (!line->done)
336             return line;
337     }
338
339     return NULL;
340 }
341
342 static gboolean
343 stop_loop (gpointer user_data)
344 {
345     g_main_loop_quit ((GMainLoop *)user_data);
346     return G_SOURCE_REMOVE;
347 }
348
349 static void
350 switch_to_greeter_done_cb (GObject *bus, GAsyncResult *result, gpointer data)
351 {
352     GVariant *r;
353     GError *error = NULL;
354
355     r = g_dbus_connection_call_finish (G_DBUS_CONNECTION (bus), result, &error);
356     if (error)
357         g_warning ("Failed to switch to greeter: %s\n", error->message);
358     g_clear_error (&error);
359
360     if (r)
361     {
362         check_status ("RUNNER SWITCH-TO-GREETER");
363         g_variant_unref (r);
364     }
365     else
366         check_status ("RUNNER SWITCH-TO-GREETER FAILED");
367 }
368
369 static void
370 switch_to_user_done_cb (GObject *bus, GAsyncResult *result, gpointer data)
371 {
372     GVariant *r;
373     GError *error = NULL;
374     gchar *username = data, *status_text;
375
376     r = g_dbus_connection_call_finish (G_DBUS_CONNECTION (bus), result, &error);
377     if (error)
378         g_warning ("Failed to switch to user: %s\n", error->message);
379     g_clear_error (&error);
380
381     if (r)
382     {
383         status_text = g_strdup_printf ("RUNNER SWITCH-TO-USER USERNAME=%s", username);
384         g_variant_unref (r);
385     }
386     else
387         status_text = g_strdup_printf ("RUNNER SWITCH-TO-USER USERNAME=%s FAILED", username);
388     check_status (status_text);
389
390     g_free (status_text);
391     g_free (username);
392 }
393
394 static void
395 switch_to_guest_done_cb (GObject *bus, GAsyncResult *result, gpointer data)
396 {
397     GVariant *r;
398     GError *error = NULL;
399
400     r = g_dbus_connection_call_finish (G_DBUS_CONNECTION (bus), result, &error);
401     if (error)
402         g_warning ("Failed to switch to guest: %s\n", error->message);
403     g_clear_error (&error);
404
405     if (r)
406     {
407         check_status ("RUNNER SWITCH-TO-GUEST");
408         g_variant_unref (r);
409     }
410     else
411         check_status ("RUNNER SWITCH-TO-GUEST FAILED");
412 }
413
414 static void
415 handle_command (const gchar *command)
416 {
417     const gchar *c;
418     gchar *name = NULL;
419     GHashTable *params;
420
421     c = command;
422     while (*c && !isspace (*c))
423         c++;
424     name = g_strdup_printf ("%.*s", (int) (c - command), command);
425
426     params = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
427     while (TRUE)
428     {
429         const gchar *start;
430         gchar *param_name, *param_value;
431
432         while (isspace (*c))
433             c++;
434         start = c;
435         while (*c && !isspace (*c) && *c != '=')
436             c++;
437         if (*c == '\0')
438             break;
439
440         param_name = g_strdup_printf ("%.*s", (int) (c - start), start);
441
442         if (*c == '=')
443         {
444             c++;
445             while (isspace (*c))
446                 c++;
447             if (*c == '\"')
448             {
449                 gboolean escaped = FALSE;
450                 GString *value;
451
452                 c++;
453                 value = g_string_new ("");
454                 while (*c)
455                 {
456                     if (*c == '\\')
457                     {
458                         if (escaped)
459                         {
460                             g_string_append_c (value, '\\');
461                             escaped = FALSE;
462                         }
463                         else
464                             escaped = TRUE;
465                     }
466                     else if (!escaped && *c == '\"')
467                         break;
468                     if (!escaped)
469                         g_string_append_c (value, *c);
470                     c++;
471                 }
472                 param_value = value->str;
473                 g_string_free (value, FALSE);
474                 if (*c == '\"')
475                     c++;
476             }
477             else
478             {
479                 start = c;
480                 while (*c && !isspace (*c))
481                     c++;
482                 param_value = g_strdup_printf ("%.*s", (int) (c - start), start);
483             }
484         }
485         else
486             param_value = g_strdup ("");
487
488         g_hash_table_insert (params, param_name, param_value);
489     }
490
491     if (strcmp (name, "START-DAEMON") == 0)
492     {
493         GString *command_line;
494         gchar **lightdm_argv;
495         pid_t lightdm_pid;
496         GError *error = NULL;
497
498         command_line = g_string_new ("lightdm");
499         if (getenv ("DEBUG"))
500             g_string_append (command_line, " --debug");
501         g_string_append_printf (command_line, " --cache-dir %s/cache", temp_dir);
502
503         test_runner_command = g_strdup_printf ("PATH=%s LD_PRELOAD=%s LD_LIBRARY_PATH=%s LIGHTDM_TEST_ROOT=%s DBUS_SESSION_BUS_ADDRESS=%s %s\n",
504                                                g_getenv ("PATH"), g_getenv ("LD_PRELOAD"), g_getenv ("LD_LIBRARY_PATH"), g_getenv ("LIGHTDM_TEST_ROOT"), g_getenv ("DBUS_SESSION_BUS_ADDRESS"),
505                                                command_line->str);
506
507         if (!g_shell_parse_argv (command_line->str, NULL, &lightdm_argv, &error))
508         {
509             g_warning ("Error parsing command line: %s", error->message);
510             quit (EXIT_FAILURE);
511         }
512         g_clear_error (&error);
513
514         if (!g_spawn_async (NULL, lightdm_argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL, NULL, &lightdm_pid, &error))
515         {
516             g_warning ("Error launching LightDM: %s", error->message);
517             quit (EXIT_FAILURE);
518         }
519         g_clear_error (&error);
520         lightdm_process = watch_process (lightdm_pid);
521
522         check_status ("RUNNER DAEMON-START");
523     }
524     else if (strcmp (name, "WAIT") == 0)
525     {
526         const gchar *v;
527         int duration;
528
529         /* Stop status timeout */
530         if (status_timeout)
531             g_source_remove (status_timeout);
532         status_timeout = 0;
533
534         /* Use a main loop so that our DBus functions are still responsive */
535         GMainLoop *loop = g_main_loop_new (NULL, FALSE);
536         v = g_hash_table_lookup (params, "DURATION");
537         duration = v ? atoi (v) : 1;
538         if (duration < 1)
539             duration = 1;
540         g_timeout_add_seconds (duration, stop_loop, loop);
541         g_main_loop_run (loop);
542         g_main_loop_unref (loop);
543
544         /* Restart status timeout */
545         status_timeout = g_timeout_add (status_timeout_ms, status_timeout_cb, NULL);
546     }
547     else if (strcmp (name, "ADD-SEAT") == 0)
548     {
549         const gchar *id, *v;
550         Login1Seat *seat;
551
552         id = g_hash_table_lookup (params, "ID");
553         seat = add_login1_seat (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL), id, TRUE);
554         v = g_hash_table_lookup (params, "CAN-GRAPHICAL");
555         if (v)
556             seat->can_graphical = strcmp (v, "TRUE") == 0;
557         v = g_hash_table_lookup (params, "CAN-MULTI-SESSION");
558         if (v)
559             seat->can_multi_session = strcmp (v, "TRUE") == 0;
560     }
561     else if (strcmp (name, "ADD-LOCAL-X-SEAT") == 0)
562     {
563         GVariant *result;
564         const gchar *v;
565
566         v = g_hash_table_lookup (params, "DISPLAY");
567         result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
568                                               "org.freedesktop.DisplayManager",
569                                               "/org/freedesktop/DisplayManager",
570                                               "org.freedesktop.DisplayManager",
571                                               "AddLocalXSeat",
572                                               g_variant_new ("(i)", v ? atoi (v) : -1),
573                                               G_VARIANT_TYPE ("(o)"),
574                                               G_DBUS_CALL_FLAGS_NONE,
575                                               G_MAXINT,
576                                               NULL,
577                                               NULL);
578         g_variant_unref (result);
579     }
580     else if (strcmp (name, "UPDATE-SEAT") == 0)
581     {
582         Login1Seat *seat;
583         const gchar *id;
584
585         id = g_hash_table_lookup (params, "ID");
586         seat = find_login1_seat (id);
587         if (seat)
588         {
589             const gchar *v;
590             GVariantBuilder invalidated_properties;
591             GError *error = NULL;
592
593             g_variant_builder_init (&invalidated_properties, G_VARIANT_TYPE_ARRAY);
594
595             v = g_hash_table_lookup (params, "CAN-GRAPHICAL");
596             if (v)
597             {
598                 seat->can_graphical = strcmp (v, "TRUE") == 0;
599                 g_variant_builder_add (&invalidated_properties, "s", "CanGraphical");
600             }
601             v = g_hash_table_lookup (params, "CAN-MULTI-SESSION");
602             if (v)
603             {
604                 seat->can_multi_session = strcmp (v, "TRUE") == 0;
605                 g_variant_builder_add (&invalidated_properties, "s", "CanMultiSession");
606             }
607             v = g_hash_table_lookup (params, "ACTIVE-SESSION");
608             if (v)
609             {
610                 g_free (seat->active_session);
611                 seat->active_session = g_strdup (v);
612                 g_variant_builder_add (&invalidated_properties, "s", "ActiveSession");
613             }
614
615             g_dbus_connection_emit_signal (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
616                                            NULL,
617                                            seat->path,
618                                            "org.freedesktop.DBus.Properties",
619                                            "PropertiesChanged",
620                                            g_variant_new ("(sa{sv}as)", "org.freedesktop.login1.Seat", NULL, &invalidated_properties),
621                                            &error);
622             if (error)
623                 g_warning ("Failed to emit PropertiesChanged: %s", error->message);
624             g_clear_error (&error);
625         }
626     }
627     else if (strcmp (name, "REMOVE-SEAT") == 0)
628     {
629         const gchar *id;
630         id = g_hash_table_lookup (params, "ID");
631         remove_login1_seat (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL), id);
632     }
633     else if (strcmp (name, "LIST-SEATS") == 0)
634     {
635         GVariant *result, *value;
636         GString *status;
637         GVariantIter *iter;
638         const gchar *path;
639         int i = 0;
640
641         result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
642                                               "org.freedesktop.DisplayManager",
643                                               "/org/freedesktop/DisplayManager",
644                                               "org.freedesktop.DBus.Properties",
645                                               "Get",
646                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Seats"),
647                                               G_VARIANT_TYPE ("(v)"),
648                                               G_DBUS_CALL_FLAGS_NONE,
649                                               G_MAXINT,
650                                               NULL,
651                                               NULL);
652
653         status = g_string_new ("RUNNER LIST-SEATS SEATS=");
654         g_variant_get (result, "(v)", &value);
655         g_variant_get (value, "ao", &iter);
656         while (g_variant_iter_loop (iter, "&o", &path))
657         {
658             if (i != 0)
659                 g_string_append (status, ",");
660             g_string_append (status, path);
661             i++;
662         }
663         g_variant_unref (value);
664         g_variant_unref (result);
665
666         check_status (status->str);
667         g_string_free (status, TRUE);
668     }
669     else if (strcmp (name, "LIST-SESSIONS") == 0)
670     {
671         GVariant *result, *value;
672         GString *status;
673         GVariantIter *iter;
674         const gchar *path;
675         int i = 0;
676
677         result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
678                                               "org.freedesktop.DisplayManager",
679                                               "/org/freedesktop/DisplayManager",
680                                               "org.freedesktop.DBus.Properties",
681                                               "Get",
682                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
683                                               G_VARIANT_TYPE ("(v)"),
684                                               G_DBUS_CALL_FLAGS_NONE,
685                                               G_MAXINT,
686                                               NULL,
687                                               NULL);
688
689         status = g_string_new ("RUNNER LIST-SESSIONS SESSIONS=");
690         g_variant_get (result, "(v)", &value);
691         g_variant_get (value, "ao", &iter);
692         while (g_variant_iter_loop (iter, "&o", &path))
693         {
694             if (i != 0)
695                 g_string_append (status, ",");
696             g_string_append (status, path);
697             i++;
698         }
699         g_variant_unref (value);
700         g_variant_unref (result);
701
702         check_status (status->str);
703         g_string_free (status, TRUE);
704     }
705     else if (strcmp (name, "SEAT-CAN-SWITCH") == 0)
706     {
707         GVariant *result, *value;
708         gchar *status;
709
710         result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
711                                               "org.freedesktop.DisplayManager",
712                                               "/org/freedesktop/DisplayManager/Seat0",
713                                               "org.freedesktop.DBus.Properties",
714                                               "Get",
715                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Seat", "CanSwitch"),
716                                               G_VARIANT_TYPE ("(v)"),
717                                               G_DBUS_CALL_FLAGS_NONE,
718                                               G_MAXINT,
719                                               NULL,
720                                               NULL);
721
722         g_variant_get (result, "(v)", &value);
723         status = g_strdup_printf ("RUNNER SEAT-CAN-SWITCH CAN-SWITCH=%s", g_variant_get_boolean (value) ? "TRUE" : "FALSE");
724         g_variant_unref (value);
725         g_variant_unref (result);
726         check_status (status);
727         g_free (status);
728     }
729     else if (strcmp (name, "SEAT-HAS-GUEST-ACCOUNT") == 0)
730     {
731         GVariant *result, *value;
732         gchar *status;
733
734         result = g_dbus_connection_call_sync (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
735                                               "org.freedesktop.DisplayManager",
736                                               "/org/freedesktop/DisplayManager/Seat0",
737                                               "org.freedesktop.DBus.Properties",
738                                               "Get",
739                                               g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Seat", "HasGuestAccount"),
740                                               G_VARIANT_TYPE ("(v)"),
741                                               G_DBUS_CALL_FLAGS_NONE,
742                                               G_MAXINT,
743                                               NULL,
744                                               NULL);
745
746         g_variant_get (result, "(v)", &value);
747         status = g_strdup_printf ("RUNNER SEAT-HAS-GUEST-ACCOUNT HAS-GUEST-ACCOUNT=%s", g_variant_get_boolean (value) ? "TRUE" : "FALSE");
748         g_variant_unref (value);
749         g_variant_unref (result);
750         check_status (status);
751         g_free (status);
752     }
753     else if (strcmp (name, "SWITCH-TO-GREETER") == 0)
754     {
755         g_dbus_connection_call (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
756                                 "org.freedesktop.DisplayManager",
757                                 "/org/freedesktop/DisplayManager/Seat0",
758                                 "org.freedesktop.DisplayManager.Seat",
759                                 "SwitchToGreeter",
760                                 g_variant_new ("()"),
761                                 G_VARIANT_TYPE ("()"),
762                                 G_DBUS_CALL_FLAGS_NONE,
763                                 G_MAXINT,
764                                 NULL,
765                                 switch_to_greeter_done_cb,
766                                 NULL);
767     }
768     else if (strcmp (name, "SWITCH-TO-USER") == 0)
769     {
770         const gchar *username;
771
772         username = g_hash_table_lookup (params, "USERNAME");
773         g_dbus_connection_call (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
774                                 "org.freedesktop.DisplayManager",
775                                 "/org/freedesktop/DisplayManager/Seat0",
776                                 "org.freedesktop.DisplayManager.Seat",
777                                 "SwitchToUser",
778                                 g_variant_new ("(ss)", username, ""),
779                                 G_VARIANT_TYPE ("()"),
780                                 G_DBUS_CALL_FLAGS_NONE,
781                                 G_MAXINT,
782                                 NULL,
783                                 switch_to_user_done_cb,
784                                 g_strdup (username));
785     }
786     else if (strcmp (name, "SWITCH-TO-GUEST") == 0)
787     {
788         g_dbus_connection_call (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
789                                 "org.freedesktop.DisplayManager",
790                                 "/org/freedesktop/DisplayManager/Seat0",
791                                 "org.freedesktop.DisplayManager.Seat",
792                                 "SwitchToGuest",
793                                 g_variant_new ("(s)", ""),
794                                 G_VARIANT_TYPE ("()"),
795                                 G_DBUS_CALL_FLAGS_NONE,
796                                 G_MAXINT,
797                                 NULL,
798                                 switch_to_guest_done_cb,
799                                 NULL);
800     }
801     else if (strcmp (name, "STOP-DAEMON") == 0)
802         stop_process (lightdm_process);
803     // FIXME: Make generic RUN-COMMAND
804     else if (strcmp (name, "START-XSERVER") == 0)
805     {
806         gchar *xserver_args, *command_line;
807         gchar **argv;
808         GPid pid;
809         Process *process;
810         GError *error = NULL;
811
812         xserver_args = g_hash_table_lookup (params, "ARGS");
813         if (!xserver_args)
814             xserver_args = "";
815         command_line = g_strdup_printf ("%s/tests/src/X %s", BUILDDIR, xserver_args);
816
817         if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
818             !g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error))
819         {
820             g_printerr ("Error starting X server: %s", error->message);
821             quit (EXIT_FAILURE);
822         }
823         else
824         {
825             process = watch_process (pid);
826             g_hash_table_insert (children, GINT_TO_POINTER (process->pid), process);
827         }
828     }
829     else if (strcmp (name, "START-VNC-CLIENT") == 0)
830     {
831         gchar *vnc_client_args, *command_line;
832         gchar **argv;
833         GPid pid;
834         Process *process;
835         GError *error = NULL;
836
837         vnc_client_args = g_hash_table_lookup (params, "ARGS");
838         if (!vnc_client_args)
839             vnc_client_args = "";
840         command_line = g_strdup_printf ("%s/tests/src/vnc-client %s", BUILDDIR, vnc_client_args);
841
842         if (!g_shell_parse_argv (command_line, NULL, &argv, &error) ||
843             !g_spawn_async (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error))
844         {
845             g_printerr ("Error starting VNC client: %s", error->message);
846             quit (EXIT_FAILURE);
847         }
848         else
849         {
850             process = watch_process (pid);
851             g_hash_table_insert (children, GINT_TO_POINTER (process->pid), process);
852         }
853     }
854     else if (strcmp (name, "ADD-USER") == 0)
855     {
856         gchar *status_text, *username;
857         AccountsUser *user;
858
859         username = g_hash_table_lookup (params, "USERNAME");
860         user = get_accounts_user_by_name (username);
861         if (user)
862             accounts_user_set_hidden (user, FALSE, TRUE);
863         else
864             g_warning ("Unknown user %s", username);
865
866         status_text = g_strdup_printf ("RUNNER ADD-USER USERNAME=%s", username);
867         check_status (status_text);
868         g_free (status_text);
869     }
870     else if (strcmp (name, "UPDATE-USER") == 0)
871     {
872         GString *status_text;
873         gchar *username;
874         AccountsUser *user;
875         GError *error = NULL;
876
877         status_text = g_string_new ("RUNNER UPDATE-USER USERNAME=");
878
879         username = g_hash_table_lookup (params, "USERNAME");
880         g_string_append (status_text, username);
881         user = get_accounts_user_by_name (username);
882         if (user)
883         {
884             if (g_hash_table_lookup (params, "NAME"))
885             {
886                 user->user_name = g_strdup (g_hash_table_lookup (params, "NAME"));
887                 g_string_append_printf (status_text, " NAME=%s", user->user_name);
888             }
889             if (g_hash_table_lookup (params, "REAL-NAME"))
890             {
891                 user->real_name = g_strdup (g_hash_table_lookup (params, "REAL-NAME"));
892                 g_string_append_printf (status_text, " REAL-NAME=%s", user->real_name);
893             }
894             if (g_hash_table_lookup (params, "HOME-DIRECTORY"))
895             {
896                 user->home_directory = g_strdup (g_hash_table_lookup (params, "HOME-DIRECTORY"));
897                 g_string_append_printf (status_text, " HOME-DIRECTORY=%s", user->home_directory);
898             }
899             if (g_hash_table_lookup (params, "IMAGE"))
900             {
901                 user->image = g_strdup (g_hash_table_lookup (params, "IMAGE"));
902                 g_string_append_printf (status_text, " IMAGE=%s", user->image);
903             }
904             if (g_hash_table_lookup (params, "BACKGROUND"))
905             {
906                 user->background = g_strdup (g_hash_table_lookup (params, "BACKGROUND"));
907                 g_string_append_printf (status_text, " BACKGROUND=%s", user->background);
908             }
909             if (g_hash_table_lookup (params, "LANGUAGE"))
910             {
911                 user->language = g_strdup (g_hash_table_lookup (params, "LANGUAGE"));
912                 g_string_append_printf (status_text, " LANGUAGE=%s", user->language);
913             }
914             if (g_hash_table_lookup (params, "LAYOUTS"))
915             {
916                 const gchar *value = g_hash_table_lookup (params, "LAYOUTS");
917                 user->layouts = g_strsplit (value, ";", -1);
918                 g_string_append_printf (status_text, " LAYOUTS=%s", value);
919             }
920             if (g_hash_table_lookup (params, "HAS-MESSAGES"))
921             {
922                 user->has_messages = g_strcmp0 (g_hash_table_lookup (params, "HAS-MESSAGES"), "TRUE") == 0;
923                 g_string_append_printf (status_text, " HAS-MESSAGES=%s", user->has_messages ? "TRUE" : "FALSE");
924             }
925             if (g_hash_table_lookup (params, "SESSION"))
926             {
927                 user->xsession = g_strdup (g_hash_table_lookup (params, "SESSION"));
928                 g_string_append_printf (status_text, " SESSION=%s", user->xsession);
929             }
930
931             g_dbus_connection_emit_signal (accounts_connection,
932                                            NULL,
933                                            user->path,
934                                            "org.freedesktop.Accounts.User",
935                                            "Changed",
936                                            g_variant_new ("()"),
937                                            &error);
938         }
939         else
940             g_warning ("Unknown user %s", username);
941
942         if (error)
943             g_warning ("Failed to emit Changed: %s", error->message);
944         g_clear_error (&error);
945
946         check_status (status_text->str);
947         g_string_free (status_text, TRUE);
948     }
949     else if (strcmp (name, "DELETE-USER") == 0)
950     {
951         gchar *status_text, *username;
952         AccountsUser *user;
953
954         username = g_hash_table_lookup (params, "USERNAME");
955         user = get_accounts_user_by_name (username);
956         if (user)
957             accounts_user_set_hidden (user, TRUE, TRUE);
958         else
959             g_warning ("Unknown user %s", username);
960
961         status_text = g_strdup_printf ("RUNNER DELETE-USER USERNAME=%s", username);
962         check_status (status_text);
963         g_free (status_text);
964     }
965     else if (strcmp (name, "UNLOCK-SESSION") == 0)
966     {
967         gchar *status_text, *id;
968         Login1Session *session;
969         
970         id = g_hash_table_lookup (params, "SESSION");
971         session = find_login1_session (id);
972         if (session)
973         {
974             if (!session->locked)
975                 g_warning ("Session %s is not locked", id);
976             session->locked = FALSE;
977         }
978         else
979             g_warning ("Unknown session %s", id);
980
981         status_text = g_strdup_printf ("RUNNER UNLOCK-SESSION SESSION=%s", id);
982         check_status (status_text);
983         g_free (status_text);
984     }
985     /* Forward to external processes */
986     else if (g_str_has_prefix (name, "SESSION-") ||
987              g_str_has_prefix (name, "GREETER-") ||
988              g_str_has_prefix (name, "XSERVER-") ||
989              g_str_has_prefix (name, "XMIR-") ||
990              g_str_has_prefix (name, "XVNC-") ||
991              strcmp (name, "UNITY-SYSTEM-COMPOSITOR") == 0)
992     {
993         GList *link;
994         for (link = status_clients; link; link = link->next)
995         {
996             StatusClient *client = link->data;
997             int length;
998             GError *error = NULL;
999
1000             length = strlen (command);
1001             if (g_socket_send (client->socket, (gchar *) &length, sizeof (length), NULL, &error) < 0 ||
1002                 g_socket_send (client->socket, command, strlen (command), NULL, &error) < 0)
1003                 g_printerr ("Failed to write to client socket: %s\n", error->message);
1004             g_clear_error (&error);
1005         }
1006     }
1007     else
1008     {
1009         g_printerr ("Unknown command '%s'\n", name);
1010         quit (EXIT_FAILURE);
1011     }
1012
1013     g_free (name);
1014     g_hash_table_unref (params);
1015 }
1016
1017 static void
1018 run_commands (void)
1019 {
1020     /* Stop daemon if requested */
1021     while (TRUE)
1022     {
1023         ScriptLine *line;
1024
1025         /* Commands start with an asterisk */
1026         line = get_script_line (NULL);
1027         if (!line || line->text[0] != '*')
1028             break;
1029
1030         statuses = g_list_append (statuses, g_strdup (line->text));
1031         line->done = TRUE;
1032
1033         handle_command (line->text + 1);
1034     }
1035
1036     /* Stop at the end of the script */
1037     if (get_script_line (NULL) == NULL)
1038         quit (EXIT_SUCCESS);
1039 }
1040
1041 static gboolean
1042 status_timeout_cb (gpointer data)
1043 {
1044     ScriptLine *line;
1045
1046     status_timeout = 0;
1047
1048     line = get_script_line (NULL);
1049     fail ("(timeout)", line ? line->text : NULL);
1050
1051     return G_SOURCE_REMOVE;
1052 }
1053
1054 static void
1055 check_status (const gchar *status)
1056 {
1057     ScriptLine *line;
1058     gboolean result = FALSE;
1059     gchar *prefix;
1060
1061     if (stop)
1062         return;
1063
1064     statuses = g_list_append (statuses, g_strdup (status));
1065
1066     if (getenv ("DEBUG"))
1067         g_print ("%s\n", status);
1068
1069     /* Try and match against expected */
1070     prefix = get_prefix (status);
1071     line = get_script_line (prefix);
1072     g_free (prefix);
1073     if (line)
1074     {
1075         gchar *full_pattern = g_strdup_printf ("^%s$", line->text);
1076         result = g_regex_match_simple (full_pattern, status, 0, 0);
1077         g_free (full_pattern);
1078     }
1079
1080     if (!result)
1081     {
1082         if (line == NULL)
1083             line = get_script_line (NULL);
1084         fail (NULL, line ? line->text : NULL);
1085         return;
1086     }
1087
1088     line->done = TRUE;
1089
1090     /* Restart timeout */
1091     if (status_timeout)
1092         g_source_remove (status_timeout);
1093     status_timeout = g_timeout_add (status_timeout_ms, status_timeout_cb, NULL);
1094
1095     run_commands ();
1096 }
1097
1098 static gboolean
1099 status_message_cb (GSocket *socket, GIOCondition condition, StatusClient *client)
1100 {
1101     int length;
1102     gchar buffer[1024];
1103     ssize_t n_read;
1104     GError *error = NULL;
1105
1106     n_read = g_socket_receive (socket, (gchar *)&length, sizeof (length), NULL, &error);
1107     if (n_read > 0)
1108         n_read = g_socket_receive (socket, buffer, length, NULL, &error);
1109     if (error)
1110     {
1111         if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED))
1112             n_read = 0;
1113         else
1114             g_warning ("Error reading from socket: %s", error->message);
1115     }
1116     g_clear_error (&error);
1117     if (n_read == 0)
1118     {
1119         status_clients = g_list_remove (status_clients, client);
1120         g_object_unref (client->socket);
1121         g_free (client);
1122         return FALSE;
1123     }
1124     else if (n_read > 0)
1125     {
1126         buffer[n_read] = '\0';
1127         check_status (buffer);
1128     }
1129
1130     return TRUE;
1131 }
1132
1133 static gboolean
1134 status_connect_cb (gpointer data)
1135 {
1136     GSocket *socket;
1137     GError *error = NULL;
1138
1139     socket = g_socket_accept (status_socket, NULL, &error);
1140     if (error)
1141         g_warning ("Failed to accept status connection: %s", error->message);
1142     g_clear_error (&error);
1143     if (socket)
1144     {
1145         StatusClient *client;
1146
1147         client = g_malloc0 (sizeof (StatusClient));
1148         client->socket = socket;
1149         client->source = g_socket_create_source (socket, G_IO_IN, NULL);
1150         status_clients = g_list_append (status_clients, client);
1151
1152         g_source_set_callback (client->source, (GSourceFunc) status_message_cb, client, NULL);
1153         g_source_attach (client->source, NULL);
1154     }
1155
1156     return TRUE;
1157 }
1158
1159 static void
1160 load_script (const gchar *filename)
1161 {
1162     int i;
1163     gchar *data, **lines;
1164
1165     if (!g_file_get_contents (filename, &data, NULL, NULL))
1166     {
1167         g_printerr ("Unable to load script: %s\n", filename);
1168         quit (EXIT_FAILURE);
1169     }
1170
1171     lines = g_strsplit (data, "\n", -1);
1172     g_free (data);
1173
1174     /* Load lines with #? prefix as expected behaviour */
1175     for (i = 0; lines[i]; i++)
1176     {
1177         gchar *text = g_strstrip (lines[i]);
1178         if (g_str_has_prefix (text, "#?"))
1179         {
1180             ScriptLine *line;
1181             line = g_malloc0 (sizeof (ScriptLine));
1182             line->text = g_strdup (text + 2);
1183             line->done = FALSE;
1184             script = g_list_append (script, line);
1185         }
1186     }
1187     g_strfreev (lines);
1188 }
1189
1190 static void
1191 handle_upower_call (GDBusConnection       *connection,
1192                     const gchar           *sender,
1193                     const gchar           *object_path,
1194                     const gchar           *interface_name,
1195                     const gchar           *method_name,
1196                     GVariant              *parameters,
1197                     GDBusMethodInvocation *invocation,
1198                     gpointer               user_data)
1199 {
1200     if (strcmp (method_name, "SuspendAllowed") == 0)
1201     {
1202         check_status ("UPOWER SUSPEND-ALLOWED");
1203         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
1204     }
1205     else if (strcmp (method_name, "Suspend") == 0)
1206     {
1207         check_status ("UPOWER SUSPEND");
1208         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1209     }
1210     else if (strcmp (method_name, "HibernateAllowed") == 0)
1211     {
1212         check_status ("UPOWER HIBERNATE-ALLOWED");
1213         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
1214     }
1215     else if (strcmp (method_name, "Hibernate") == 0)
1216     {
1217         check_status ("UPOWER HIBERNATE");
1218         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1219     }
1220     else
1221         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1222 }
1223
1224 static void
1225 upower_name_acquired_cb (GDBusConnection *connection,
1226                          const gchar     *name,
1227                          gpointer         user_data)
1228 {
1229     const gchar *upower_interface =
1230         "<node>"
1231         "  <interface name='org.freedesktop.UPower'>"
1232         "    <method name='SuspendAllowed'>"
1233         "      <arg name='allowed' direction='out' type='b'/>"
1234         "    </method>"
1235         "    <method name='Suspend'/>"
1236         "    <method name='HibernateAllowed'>"
1237         "      <arg name='allowed' direction='out' type='b'/>"
1238         "    </method>"
1239         "    <method name='Hibernate'/>"
1240         "  </interface>"
1241         "</node>";
1242     static const GDBusInterfaceVTable upower_vtable =
1243     {
1244         handle_upower_call,
1245     };
1246     GDBusNodeInfo *upower_info;
1247     GError *error = NULL;
1248
1249     upower_info = g_dbus_node_info_new_for_xml (upower_interface, &error);
1250     if (error)
1251         g_warning ("Failed to parse D-Bus interface: %s", error->message);
1252     g_clear_error (&error);
1253     if (!upower_info)
1254         return;
1255     g_dbus_connection_register_object (connection,
1256                                        "/org/freedesktop/UPower",
1257                                        upower_info->interfaces[0],
1258                                        &upower_vtable,
1259                                        NULL, NULL,
1260                                        &error);
1261     if (error)
1262         g_warning ("Failed to register UPower service: %s", error->message);
1263     g_clear_error (&error);
1264     g_dbus_node_info_unref (upower_info);
1265
1266     service_count--;
1267     if (service_count == 0)
1268         ready ();
1269 }
1270
1271 static void
1272 start_upower_daemon (void)
1273 {
1274     service_count++;
1275     g_bus_own_name (G_BUS_TYPE_SYSTEM,
1276                     "org.freedesktop.UPower",
1277                     G_BUS_NAME_OWNER_FLAGS_NONE,
1278                     upower_name_acquired_cb,
1279                     NULL,
1280                     NULL,
1281                     NULL,
1282                     NULL);
1283 }
1284
1285 static CKSession *
1286 open_ck_session (GDBusConnection *connection, GVariant *params)
1287 {
1288     CKSession *session;
1289     GString *cookie;
1290     GVariantIter *iter;
1291     const gchar *name;
1292     GVariant *value;
1293     GError *error = NULL;
1294
1295     session = g_malloc0 (sizeof (CKSession));
1296     ck_sessions = g_list_append (ck_sessions, session);
1297
1298     cookie = g_string_new ("ck-cookie");
1299     g_variant_get (params, "a(sv)", &iter);
1300     while (g_variant_iter_loop (iter, "(&sv)", &name, &value))
1301     {
1302         if (strcmp (name, "x11-display") == 0)
1303         {
1304             const gchar *display;
1305             g_variant_get (value, "&s", &display);
1306             g_string_append_printf (cookie, "-x%s", display);
1307         }
1308     }
1309
1310     session->cookie = cookie->str;
1311     g_string_free (cookie, FALSE);
1312     session->path = g_strdup_printf ("/org/freedesktop/ConsoleKit/Session%d", ck_session_index++);
1313     session->id = g_dbus_connection_register_object (connection,
1314                                                      session->path,
1315                                                      ck_session_info->interfaces[0],
1316                                                      &ck_session_vtable,
1317                                                      session,
1318                                                      NULL,
1319                                                      &error);
1320     if (error)
1321         g_warning ("Failed to register CK Session: %s", error->message);
1322     g_clear_error (&error);
1323
1324     return session;
1325 }
1326
1327 static void
1328 handle_ck_call (GDBusConnection       *connection,
1329                 const gchar           *sender,
1330                 const gchar           *object_path,
1331                 const gchar           *interface_name,
1332                 const gchar           *method_name,
1333                 GVariant              *parameters,
1334                 GDBusMethodInvocation *invocation,
1335                 gpointer               user_data)
1336 {
1337     if (strcmp (method_name, "CanRestart") == 0)
1338     {
1339         check_status ("CONSOLE-KIT CAN-RESTART");
1340         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
1341     }
1342     else if (strcmp (method_name, "CanStop") == 0)
1343     {
1344         check_status ("CONSOLE-KIT CAN-STOP");
1345         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
1346     }
1347     else if (strcmp (method_name, "CloseSession") == 0)
1348         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", TRUE));
1349     else if (strcmp (method_name, "OpenSession") == 0)
1350     {
1351         GVariantBuilder params;
1352         g_variant_builder_init (&params, G_VARIANT_TYPE ("a(sv)"));
1353         CKSession *session = open_ck_session (connection, g_variant_builder_end (&params));
1354         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", session->cookie));
1355     }
1356     else if (strcmp (method_name, "OpenSessionWithParameters") == 0)
1357     {
1358         CKSession *session = open_ck_session (connection, g_variant_get_child_value (parameters, 0));
1359         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", session->cookie));
1360     }
1361     else if (strcmp (method_name, "GetSessionForCookie") == 0)
1362     {
1363         GList *link;
1364         gchar *cookie;
1365
1366         g_variant_get (parameters, "(&s)", &cookie);
1367
1368         for (link = ck_sessions; link; link = link->next)
1369         {
1370             CKSession *session = link->data;
1371             if (strcmp (session->cookie, cookie) == 0)
1372             {
1373                 g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", session->path));
1374                 return;
1375             }
1376         }
1377
1378         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unable to find session for cookie");
1379     }
1380     else if (strcmp (method_name, "Restart") == 0)
1381     {
1382         check_status ("CONSOLE-KIT RESTART");
1383         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1384     }
1385     else if (strcmp (method_name, "Stop") == 0)
1386     {
1387         check_status ("CONSOLE-KIT STOP");
1388         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1389     }
1390     else
1391         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1392 }
1393
1394 static void
1395 handle_ck_session_call (GDBusConnection       *connection,
1396                         const gchar           *sender,
1397                         const gchar           *object_path,
1398                         const gchar           *interface_name,
1399                         const gchar           *method_name,
1400                         GVariant              *parameters,
1401                         GDBusMethodInvocation *invocation,
1402                         gpointer               user_data)
1403 {
1404     CKSession *session = user_data;
1405
1406     if (strcmp (method_name, "GetXDGRuntimeDir") == 0 && !g_key_file_get_boolean (config, "test-runner-config", "ck-no-xdg-runtime", NULL))
1407     {
1408         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "/run/console-kit"));
1409     }
1410     else if (strcmp (method_name, "Lock") == 0)
1411     {
1412         if (!session->locked)
1413             check_status ("CONSOLE-KIT LOCK-SESSION");
1414         session->locked = TRUE;
1415         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1416     }
1417     else if (strcmp (method_name, "Unlock") == 0)
1418     {
1419         if (session->locked)
1420             check_status ("CONSOLE-KIT UNLOCK-SESSION");
1421         session->locked = FALSE;
1422         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1423     }
1424     else if (strcmp (method_name, "Activate") == 0)
1425     {
1426         gchar *status = g_strdup_printf ("CONSOLE-KIT ACTIVATE-SESSION SESSION=%s", session->cookie);
1427         check_status (status);
1428         g_free (status);
1429
1430         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1431     }
1432     else
1433         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1434 }
1435
1436 static void
1437 ck_name_acquired_cb (GDBusConnection *connection,
1438                      const gchar     *name,
1439                      gpointer         user_data)
1440 {
1441     const gchar *ck_interface =
1442         "<node>"
1443         "  <interface name='org.freedesktop.ConsoleKit.Manager'>"
1444         "    <method name='CanRestart'>"
1445         "      <arg name='can_restart' direction='out' type='b'/>"
1446         "    </method>"
1447         "    <method name='CanStop'>"
1448         "      <arg name='can_stop' direction='out' type='b'/>"
1449         "    </method>"
1450         "    <method name='CloseSession'>"
1451         "      <arg name='cookie' direction='in' type='s'/>"
1452         "      <arg name='result' direction='out' type='b'/>"
1453         "    </method>"
1454         "    <method name='OpenSession'>"
1455         "      <arg name='cookie' direction='out' type='s'/>"
1456         "    </method>"
1457         "    <method name='OpenSessionWithParameters'>"
1458         "      <arg name='parameters' direction='in' type='a(sv)'/>"
1459         "      <arg name='cookie' direction='out' type='s'/>"
1460         "    </method>"
1461         "    <method name='GetSessionForCookie'>"
1462         "      <arg name='cookie' direction='in' type='s'/>"
1463         "      <arg name='ssid' direction='out' type='o'/>"
1464         "    </method>"
1465         "    <method name='Restart'/>"
1466         "    <method name='Stop'/>"
1467         "    <signal name='SeatAdded'>"
1468         "      <arg name='seat' type='o'/>"
1469         "    </signal>"
1470         "    <signal name='SeatRemoved'>"
1471         "      <arg name='seat' type='o'/>"
1472         "    </signal>"
1473         "  </interface>"
1474         "</node>";
1475     static const GDBusInterfaceVTable ck_vtable =
1476     {
1477         handle_ck_call,
1478     };
1479     const gchar *ck_session_interface_old =
1480         "<node>"
1481         "  <interface name='org.freedesktop.ConsoleKit.Session'>"
1482         "    <method name='Lock'/>"
1483         "    <method name='Unlock'/>"
1484         "    <method name='Activate'/>"
1485         "  </interface>"
1486         "</node>";
1487     const gchar *ck_session_interface =
1488         "<node>"
1489         "  <interface name='org.freedesktop.ConsoleKit.Session'>"
1490         "    <method name='GetXDGRuntimeDir'>"
1491         "      <arg name='dir' direction='out' type='s'/>"
1492         "    </method>"
1493         "    <method name='Lock'/>"
1494         "    <method name='Unlock'/>"
1495         "    <method name='Activate'/>"
1496         "  </interface>"
1497         "</node>";
1498     GDBusNodeInfo *ck_info;
1499     GError *error = NULL;
1500
1501     ck_info = g_dbus_node_info_new_for_xml (ck_interface, &error);
1502     if (error)
1503         g_warning ("Failed to parse D-Bus interface: %s", error->message);
1504     g_clear_error (&error);
1505     if (!ck_info)
1506         return;
1507     if (g_key_file_get_boolean (config, "test-runner-config", "ck-no-xdg-runtime", NULL))
1508         ck_session_info = g_dbus_node_info_new_for_xml (ck_session_interface_old, &error);
1509     else
1510         ck_session_info = g_dbus_node_info_new_for_xml (ck_session_interface, &error);  
1511     if (error)
1512         g_warning ("Failed to parse D-Bus interface: %s", error->message);
1513     g_clear_error (&error);
1514     if (!ck_session_info)
1515         return;
1516     g_dbus_connection_register_object (connection,
1517                                        "/org/freedesktop/ConsoleKit/Manager",
1518                                        ck_info->interfaces[0],
1519                                        &ck_vtable,
1520                                        NULL, NULL,
1521                                        &error);
1522     if (error)
1523         g_warning ("Failed to register console kit service: %s", error->message);
1524     g_clear_error (&error);
1525     g_dbus_node_info_unref (ck_info);
1526
1527     service_count--;
1528     if (service_count == 0)
1529         ready ();
1530 }
1531
1532 static void
1533 start_console_kit_daemon (void)
1534 {
1535     service_count++;
1536     g_bus_own_name (G_BUS_TYPE_SYSTEM,
1537                     "org.freedesktop.ConsoleKit",
1538                     G_BUS_NAME_OWNER_FLAGS_NONE,
1539                     NULL,
1540                     ck_name_acquired_cb,
1541                     NULL,
1542                     NULL,
1543                     NULL);
1544 }
1545
1546 static void
1547 handle_login1_seat_call (GDBusConnection       *connection,
1548                          const gchar           *sender,
1549                          const gchar           *object_path,
1550                          const gchar           *interface_name,
1551                          const gchar           *method_name,
1552                          GVariant              *parameters,
1553                          GDBusMethodInvocation *invocation,
1554                          gpointer               user_data)
1555 {
1556     g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1557 }
1558
1559 static GVariant *
1560 handle_login1_seat_get_property (GDBusConnection       *connection,
1561                                  const gchar           *sender,
1562                                  const gchar           *object_path,
1563                                  const gchar           *interface_name,
1564                                  const gchar           *property_name,
1565                                  GError               **error,
1566                                  gpointer               user_data)
1567 {
1568     Login1Seat *seat = user_data;
1569
1570     if (strcmp (property_name, "CanGraphical") == 0)
1571         return g_variant_new_boolean (seat->can_graphical);
1572     else if (strcmp (property_name, "CanMultiSession") == 0)
1573         return g_variant_new_boolean (seat->can_multi_session);
1574     else if (strcmp (property_name, "Id") == 0)
1575         return g_variant_new_string (seat->id);
1576     else if (strcmp (property_name, "ActiveSession") == 0)
1577     {
1578         if (seat->active_session)
1579         {
1580             gchar *path;
1581             GVariant *ret;
1582             
1583             path = g_strdup_printf ("/org/freedesktop/login1/session/%s", seat->active_session);
1584             ret = g_variant_new ("(so)", seat->active_session, path);
1585             g_free (path);
1586
1587             return ret;
1588         }
1589         else 
1590             return NULL;
1591     }
1592     else
1593         return NULL;
1594 }
1595
1596 static Login1Seat *
1597 add_login1_seat (GDBusConnection *connection, const gchar *id, gboolean emit_signal)
1598 {
1599     Login1Seat *seat;
1600     GError *error = NULL;
1601     GDBusNodeInfo *login1_seat_info;
1602
1603     const gchar *login1_seat_interface =
1604         "<node>"
1605         "  <interface name='org.freedesktop.login1.Seat'>"
1606         "    <property name='CanGraphical' type='b' access='read'/>"
1607         "    <property name='CanMultiSession' type='b' access='read'/>"
1608         "    <property name='ActiveSession' type='(so)' access='read'/>"
1609         "    <property name='Id' type='s' access='read'/>"
1610         "  </interface>"
1611         "</node>";
1612     static const GDBusInterfaceVTable login1_seat_vtable =
1613     {
1614         handle_login1_seat_call,
1615         handle_login1_seat_get_property,
1616     };
1617
1618     seat = g_malloc0 (sizeof (Login1Seat));
1619     login1_seats = g_list_append (login1_seats, seat);
1620     seat->id = g_strdup (id);
1621     seat->path = g_strdup_printf ("/org/freedesktop/login1/seat/%s", seat->id);
1622     seat->can_graphical = TRUE;
1623     seat->can_multi_session = TRUE;
1624     seat->active_session = NULL;
1625
1626     login1_seat_info = g_dbus_node_info_new_for_xml (login1_seat_interface, &error);
1627     if (error)
1628         g_warning ("Failed to parse login1 seat D-Bus interface: %s", error->message);
1629     g_clear_error (&error);
1630     if (!login1_seat_info)
1631         return NULL;
1632
1633     g_dbus_connection_register_object (connection,
1634                                        seat->path,
1635                                        login1_seat_info->interfaces[0],
1636                                        &login1_seat_vtable,
1637                                        seat,
1638                                        NULL,
1639                                        &error);
1640     if (error)
1641         g_warning ("Failed to register login1 seat: %s", error->message);
1642     g_clear_error (&error);
1643     g_dbus_node_info_unref (login1_seat_info);
1644
1645     if (emit_signal)
1646     {
1647         g_dbus_connection_emit_signal (connection,
1648                                        NULL,
1649                                        "/org/freedesktop/login1",
1650                                        "org.freedesktop.login1.Manager",
1651                                        "SeatNew",
1652                                        g_variant_new ("(so)", seat->id, seat->path),
1653                                        &error);
1654         if (error)
1655             g_warning ("Failed to emit SeatNew: %s", error->message);
1656         g_clear_error (&error);
1657     }
1658
1659     return seat;
1660 }
1661
1662 static Login1Seat *
1663 find_login1_seat (const gchar *id)
1664 {
1665     Login1Seat *seat;
1666     GList *link;
1667
1668     for (link = login1_seats; link; link = link->next)
1669     {
1670         seat = link->data;
1671         if (strcmp (seat->id, id) == 0)
1672             return seat;
1673     }
1674
1675     return NULL;
1676 }
1677
1678 static void
1679 remove_login1_seat (GDBusConnection *connection, const gchar *id)
1680 {
1681     Login1Seat *seat;
1682     GError *error = NULL;
1683
1684     seat = find_login1_seat (id);
1685     if (!seat)
1686         return;
1687
1688     g_dbus_connection_emit_signal (connection,
1689                                    NULL,
1690                                    "/org/freedesktop/login1",
1691                                    "org.freedesktop.login1.Manager",
1692                                    "SeatRemoved",
1693                                    g_variant_new ("(so)", seat->id, seat->path),
1694                                    &error);
1695     if (error)
1696         g_warning ("Failed to emit SeatNew: %s", error->message);
1697     g_clear_error (&error);
1698
1699     login1_seats = g_list_remove (login1_seats, seat);
1700     g_free (seat->id);
1701     g_free (seat->path);
1702     g_free (seat->active_session);
1703     g_free (seat);
1704 }
1705
1706 static void
1707 handle_login1_session_call (GDBusConnection       *connection,
1708                             const gchar           *sender,
1709                             const gchar           *object_path,
1710                             const gchar           *interface_name,
1711                             const gchar           *method_name,
1712                             GVariant              *parameters,
1713                             GDBusMethodInvocation *invocation,
1714                             gpointer               user_data)
1715 {
1716     /*Login1Session *session = user_data;*/
1717     g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1718 }
1719
1720 static Login1Session *
1721 create_login1_session (GDBusConnection *connection)
1722 {
1723     Login1Session *session;
1724     GError *error = NULL;
1725     GDBusNodeInfo *login1_session_info;
1726
1727     const gchar *login1_session_interface =
1728         "<node>"
1729         "  <interface name='org.freedesktop.login1.Session'>"
1730         "  </interface>"
1731         "</node>";
1732     static const GDBusInterfaceVTable login1_session_vtable =
1733     {
1734         handle_login1_session_call,
1735     };
1736
1737     session = g_malloc0 (sizeof (Login1Session));
1738     login1_sessions = g_list_append (login1_sessions, session);
1739
1740     session->id = g_strdup_printf ("c%d", login1_session_index++);
1741     session->path = g_strdup_printf ("/org/freedesktop/login1/Session/%s", session->id);
1742
1743     login1_session_info = g_dbus_node_info_new_for_xml (login1_session_interface, &error);
1744     if (error)
1745         g_warning ("Failed to parse login1 session D-Bus interface: %s", error->message);
1746     g_clear_error (&error);
1747     if (!login1_session_info)
1748         return NULL;
1749
1750     g_dbus_connection_register_object (connection,
1751                                        session->path,
1752                                        login1_session_info->interfaces[0],
1753                                        &login1_session_vtable,
1754                                        session,
1755                                        NULL,
1756                                        &error);
1757     if (error)
1758         g_warning ("Failed to register login1 session: %s", error->message);
1759     g_clear_error (&error);
1760     g_dbus_node_info_unref (login1_session_info);
1761
1762     return session;
1763 }
1764
1765 static Login1Session *
1766 find_login1_session (const gchar *id)
1767 {
1768     GList *link;
1769
1770     for (link = login1_sessions; link; link = link->next)
1771     {
1772         Login1Session *session = link->data;
1773         if (strcmp (session->id, id) == 0)
1774             return session;
1775     }
1776
1777     return NULL;
1778 }
1779
1780 static void
1781 handle_login1_call (GDBusConnection       *connection,
1782                     const gchar           *sender,
1783                     const gchar           *object_path,
1784                     const gchar           *interface_name,
1785                     const gchar           *method_name,
1786                     GVariant              *parameters,
1787                     GDBusMethodInvocation *invocation,
1788                     gpointer               user_data)
1789 {
1790     if (strcmp (method_name, "ListSeats") == 0)
1791     {
1792         GVariantBuilder seats;
1793         GList *link;
1794
1795         g_variant_builder_init (&seats, G_VARIANT_TYPE ("a(so)"));
1796         for (link = login1_seats; link; link = link->next)
1797         {
1798             Login1Seat *seat = link->data;
1799             g_variant_builder_add (&seats, "(so)", seat->id, seat->path);
1800         }
1801         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(so))", &seats));
1802     }
1803     else if (strcmp (method_name, "CreateSession") == 0)
1804     {
1805         /* Note: this is not the full CreateSession as used by logind, we just
1806            need one so our fake PAM stack can communicate with this service */
1807         Login1Session *session = create_login1_session (connection);
1808         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(so)", session->id, session->path));
1809
1810     }
1811     else if (strcmp (method_name, "LockSession") == 0)
1812     {
1813         const gchar *id;
1814         Login1Session *session;
1815
1816         g_variant_get (parameters, "(&s)", &id);
1817         session = find_login1_session (id);
1818         if (!session)
1819         {
1820             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such session: %s", id);
1821             return;
1822         }
1823
1824         if (!session->locked)
1825         {
1826             gchar *status = g_strdup_printf ("LOGIN1 LOCK-SESSION SESSION=%s", id);
1827             check_status (status);
1828             g_free (status);
1829         }
1830         session->locked = TRUE;
1831         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1832     }
1833     else if (strcmp (method_name, "UnlockSession") == 0)
1834     {
1835         const gchar *id;
1836         Login1Session *session;
1837
1838         g_variant_get (parameters, "(&s)", &id);
1839         session = find_login1_session (id);
1840         if (!session)
1841         {
1842             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such session: %s", id);
1843             return;
1844         }
1845
1846         if (session->locked)
1847         {
1848             gchar *status = g_strdup_printf ("LOGIN1 UNLOCK-SESSION SESSION=%s", id);
1849             check_status (status);
1850             g_free (status);
1851         }
1852         session->locked = FALSE;
1853         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1854     }
1855     else if (strcmp (method_name, "ActivateSession") == 0)
1856     {
1857         const gchar *id;
1858         Login1Session *session;
1859
1860         g_variant_get (parameters, "(&s)", &id);
1861         session = find_login1_session (id);
1862         if (!session)
1863         {
1864             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such session: %s", id);
1865             return;
1866         }
1867
1868         gchar *status = g_strdup_printf ("LOGIN1 ACTIVATE-SESSION SESSION=%s", id);
1869         check_status (status);
1870         g_free (status);
1871
1872         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1873     }
1874     else if (strcmp (method_name, "CanReboot") == 0)
1875     {
1876         check_status ("LOGIN1 CAN-REBOOT");
1877         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "yes"));
1878     }
1879     else if (strcmp (method_name, "Reboot") == 0)
1880     {
1881         gboolean interactive;
1882         g_variant_get (parameters, "(b)", &interactive);
1883         check_status ("LOGIN1 REBOOT");
1884         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1885     }
1886     else if (strcmp (method_name, "CanPowerOff") == 0)
1887     {
1888         check_status ("LOGIN1 CAN-POWER-OFF");
1889         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "yes"));
1890     }
1891     else if (strcmp (method_name, "Suspend") == 0)
1892     {
1893         gboolean interactive;
1894         g_variant_get (parameters, "(b)", &interactive);
1895         check_status ("LOGIN1 SUSPEND");
1896         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1897     }
1898     else if (strcmp (method_name, "CanSuspend") == 0)
1899     {
1900         check_status ("LOGIN1 CAN-SUSPEND");
1901         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "yes"));
1902     }
1903     else if (strcmp (method_name, "PowerOff") == 0)
1904     {
1905         gboolean interactive;
1906         g_variant_get (parameters, "(b)", &interactive);
1907         check_status ("LOGIN1 POWER-OFF");
1908         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1909     }
1910     else if (strcmp (method_name, "CanHibernate") == 0)
1911     {
1912         check_status ("LOGIN1 CAN-HIBERNATE");
1913         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", "yes"));
1914     }
1915     else if (strcmp (method_name, "Hibernate") == 0)
1916     {
1917         gboolean interactive;
1918         g_variant_get (parameters, "(b)", &interactive);
1919         check_status ("LOGIN1 HIBERNATE");
1920         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
1921     }
1922     else
1923         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
1924 }
1925
1926 static void
1927 login1_name_acquired_cb (GDBusConnection *connection,
1928                          const gchar     *name,
1929                          gpointer         user_data)
1930 {
1931     const gchar *login1_interface =
1932         "<node>"
1933         "  <interface name='org.freedesktop.login1.Manager'>"
1934         "    <method name='ListSeats'>"
1935         "      <arg name='seats' type='a(so)' direction='out'/>"
1936         "    </method>"
1937         "    <method name='CreateSession'>"
1938         "      <arg name='id' type='s' direction='out'/>"
1939         "      <arg name='path' type='o' direction='out'/>"
1940         "    </method>"
1941         "    <method name='LockSession'>"
1942         "      <arg name='id' type='s' direction='in'/>"
1943         "    </method>"
1944         "    <method name='UnlockSession'>"
1945         "      <arg name='id' type='s' direction='in'/>"
1946         "    </method>"
1947         "    <method name='ActivateSession'>"
1948         "      <arg name='id' type='s' direction='in'/>"
1949         "    </method>"
1950         "    <method name='CanReboot'>"
1951         "      <arg name='result' direction='out' type='s'/>"
1952         "    </method>"
1953         "    <method name='Reboot'>"
1954         "      <arg name='interactive' direction='in' type='b'/>"
1955         "    </method>"
1956         "    <method name='CanPowerOff'>"
1957         "      <arg name='result' direction='out' type='s'/>"
1958         "    </method>"
1959         "    <method name='PowerOff'>"
1960         "      <arg name='interactive' direction='in' type='b'/>"
1961         "    </method>"
1962         "    <method name='CanSuspend'>"
1963         "      <arg name='result' direction='out' type='s'/>"
1964         "    </method>"
1965         "    <method name='Suspend'>"
1966         "      <arg name='interactive' direction='in' type='b'/>"
1967         "    </method>"
1968         "    <method name='CanHibernate'>"
1969         "      <arg name='result' direction='out' type='s'/>"
1970         "    </method>"
1971         "    <method name='Hibernate'>"
1972         "      <arg name='interactive' direction='in' type='b'/>"
1973         "    </method>"
1974         "    <signal name='SeatNew'>"
1975         "      <arg name='seat' type='so'/>"
1976         "    </signal>"
1977         "    <signal name='SeatRemoved'>"
1978         "      <arg name='seat' type='so'/>"
1979         "    </signal>"
1980         "  </interface>"
1981         "</node>";
1982     static const GDBusInterfaceVTable login1_vtable =
1983     {
1984         handle_login1_call,
1985     };
1986     GDBusNodeInfo *login1_info;
1987     Login1Seat *seat0;
1988     GError *error = NULL;
1989
1990     login1_info = g_dbus_node_info_new_for_xml (login1_interface, &error);
1991     if (error)
1992         g_warning ("Failed to parse login1 D-Bus interface: %s", error->message);
1993     g_clear_error (&error);
1994     if (!login1_info)
1995         return;
1996     g_dbus_connection_register_object (connection,
1997                                        "/org/freedesktop/login1",
1998                                        login1_info->interfaces[0],
1999                                        &login1_vtable,
2000                                        NULL, NULL,
2001                                        &error);
2002     if (error)
2003         g_warning ("Failed to register login1 service: %s", error->message);
2004     g_clear_error (&error);
2005     g_dbus_node_info_unref (login1_info);
2006
2007     /* We always have seat0 */
2008     seat0 = add_login1_seat (connection, "seat0", FALSE);
2009     if (g_key_file_has_key (config, "test-runner-config", "seat0-can-graphical", NULL))
2010         seat0->can_graphical = g_key_file_get_boolean (config, "test-runner-config", "seat0-can-graphical", NULL);
2011     if (g_key_file_has_key (config, "test-runner-config", "seat0-can-multi-session", NULL))
2012         seat0->can_multi_session = g_key_file_get_boolean (config, "test-runner-config", "seat0-can-multi-session", NULL);
2013
2014     service_count--;
2015     if (service_count == 0)
2016         ready ();
2017 }
2018
2019 static void
2020 start_login1_daemon (void)
2021 {
2022     service_count++;
2023     g_bus_own_name (G_BUS_TYPE_SYSTEM,
2024                     "org.freedesktop.login1",
2025                     G_BUS_NAME_OWNER_FLAGS_NONE,
2026                     NULL,
2027                     login1_name_acquired_cb,
2028                     NULL,
2029                     NULL,
2030                     NULL);
2031 }
2032
2033 static AccountsUser *
2034 get_accounts_user_by_uid (guint uid)
2035 {
2036     GList *link;
2037
2038     for (link = accounts_users; link; link = link->next)
2039     {
2040         AccountsUser *u = link->data;
2041         if (u->uid == uid)
2042             return u;
2043     }
2044
2045     return NULL;
2046 }
2047
2048 static AccountsUser *
2049 get_accounts_user_by_name (const gchar *username)
2050 {
2051     GList *link;
2052
2053     for (link = accounts_users; link; link = link->next)
2054     {
2055         AccountsUser *u = link->data;
2056         if (strcmp (u->user_name, username) == 0)
2057             return u;
2058     }
2059
2060     return NULL;
2061 }
2062
2063 static void
2064 accounts_user_set_hidden (AccountsUser *user, gboolean hidden, gboolean emit_signal)
2065 {
2066     GError *error = NULL;
2067
2068     user->hidden = hidden;
2069
2070     if (user->hidden && user->id != 0)
2071     {
2072         g_dbus_connection_unregister_object (accounts_connection, user->id);
2073         g_dbus_connection_emit_signal (accounts_connection,
2074                                        NULL,
2075                                        "/org/freedesktop/Accounts",
2076                                        "org.freedesktop.Accounts",
2077                                        "UserDeleted",
2078                                        g_variant_new ("(o)", user->path),
2079                                        &error);
2080         if (error)
2081             g_warning ("Failed to emit UserDeleted: %s", error->message);
2082         g_clear_error (&error);
2083
2084         user->id = 0;
2085     }
2086     if (!user->hidden && user->id == 0)
2087     {
2088         user->id = g_dbus_connection_register_object (accounts_connection,
2089                                                       user->path,
2090                                                       user_info->interfaces[0],
2091                                                       &user_vtable,
2092                                                       user,
2093                                                       NULL,
2094                                                       &error);
2095         if (error)
2096             g_warning ("Failed to register user: %s", error->message);
2097         g_clear_error (&error);
2098
2099         g_dbus_connection_emit_signal (accounts_connection,
2100                                        NULL,
2101                                        "/org/freedesktop/Accounts",
2102                                        "org.freedesktop.Accounts",
2103                                        "UserAdded",
2104                                        g_variant_new ("(o)", user->path),
2105                                        &error);
2106         if (error)
2107             g_warning ("Failed to emit UserAdded: %s", error->message);
2108         g_clear_error (&error);
2109     }
2110 }
2111
2112 static void
2113 load_passwd_file (void)
2114 {
2115     gchar *path, *data, **lines;
2116     gchar **user_filter = NULL;
2117     int i;
2118
2119     if (g_key_file_has_key (config, "test-runner-config", "accounts-service-user-filter", NULL))
2120     {
2121         gchar *filter;
2122
2123         filter = g_key_file_get_string (config, "test-runner-config", "accounts-service-user-filter", NULL);
2124         user_filter = g_strsplit (filter, " ", -1);
2125         g_free (filter);
2126     }
2127
2128     path = g_build_filename (g_getenv ("LIGHTDM_TEST_ROOT"), "etc", "passwd", NULL);
2129     g_file_get_contents (path, &data, NULL, NULL);
2130     g_free (path);
2131     lines = g_strsplit (data, "\n", -1);
2132     g_free (data);
2133
2134     for (i = 0; lines[i]; i++)
2135     {
2136         gchar **fields;
2137         guint uid;
2138         gchar *user_name, *real_name;
2139         AccountsUser *user = NULL;
2140
2141         fields = g_strsplit (lines[i], ":", -1);
2142         if (fields == NULL || g_strv_length (fields) < 7)
2143         {
2144             g_strfreev (fields);
2145             continue;
2146         }
2147
2148         user_name = fields[0];
2149         uid = atoi (fields[2]);
2150         real_name = fields[4];
2151
2152         user = get_accounts_user_by_uid (uid);
2153         if (!user)
2154         {
2155             gchar *path;
2156             GKeyFile *dmrc_file;
2157
2158             user = g_malloc0 (sizeof (AccountsUser));
2159             accounts_users = g_list_append (accounts_users, user);
2160
2161             /* Only allow users in whitelist */
2162             user->hidden = FALSE;
2163             if (user_filter)
2164             {
2165                 int j;
2166
2167                 user->hidden = TRUE;
2168                 for (j = 0; user_filter[j] != NULL; j++)
2169                     if (strcmp (user_name, user_filter[j]) == 0)
2170                         user->hidden = FALSE;
2171             }
2172
2173             dmrc_file = g_key_file_new ();
2174             path = g_build_filename (temp_dir, "home", user_name, ".dmrc", NULL);
2175             g_key_file_load_from_file (dmrc_file, path, G_KEY_FILE_NONE, NULL);
2176             g_free (path);
2177
2178             user->uid = uid;
2179             user->user_name = g_strdup (user_name);
2180             user->real_name = g_strdup (real_name);
2181             user->home_directory = g_build_filename (temp_dir, "home", user_name, NULL);
2182             user->language = g_key_file_get_string (dmrc_file, "Desktop", "Language", NULL);
2183             /* DMRC contains a locale, strip the codeset off it to get the language */
2184             if (user->language)
2185             {
2186                 gchar *c = strchr (user->language, '.');
2187                 if (c)
2188                     *c = '\0';
2189             }
2190             user->xsession = g_key_file_get_string (dmrc_file, "Desktop", "Session", NULL);
2191             user->layouts = g_key_file_get_string_list (dmrc_file, "X-Accounts", "Layouts", NULL, NULL);
2192             if (!user->layouts)
2193             {
2194                 user->layouts = g_malloc (sizeof (gchar *) * 2);
2195                 user->layouts[0] = g_key_file_get_string (dmrc_file, "Desktop", "Layout", NULL);
2196                 user->layouts[1] = NULL;
2197             }
2198             user->has_messages = g_key_file_get_boolean (dmrc_file, "X-Accounts", "HasMessages", NULL);
2199             user->path = g_strdup_printf ("/org/freedesktop/Accounts/User%d", uid);
2200             accounts_user_set_hidden (user, user->hidden, FALSE);
2201
2202             g_key_file_free (dmrc_file);
2203         }
2204
2205         g_strfreev (fields);
2206     }
2207
2208     g_strfreev (lines);
2209 }
2210
2211 static void
2212 handle_accounts_call (GDBusConnection       *connection,
2213                       const gchar           *sender,
2214                       const gchar           *object_path,
2215                       const gchar           *interface_name,
2216                       const gchar           *method_name,
2217                       GVariant              *parameters,
2218                       GDBusMethodInvocation *invocation,
2219                       gpointer               user_data)
2220 {
2221     if (strcmp (method_name, "ListCachedUsers") == 0)
2222     {
2223         GVariantBuilder builder;
2224         GList *link;
2225
2226         g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
2227
2228         load_passwd_file ();
2229         for (link = accounts_users; link; link = link->next)
2230         {
2231             AccountsUser *user = link->data;
2232             if (!user->hidden && user->uid >= 1000)
2233                 g_variant_builder_add_value (&builder, g_variant_new_object_path (user->path));
2234         }
2235
2236         g_dbus_method_invocation_return_value (invocation, g_variant_new ("(ao)", &builder));
2237     }
2238     else if (strcmp (method_name, "FindUserByName") == 0)
2239     {
2240         AccountsUser *user = NULL;
2241         gchar *user_name;
2242
2243         g_variant_get (parameters, "(&s)", &user_name);
2244
2245         load_passwd_file ();
2246         user = get_accounts_user_by_name (user_name);
2247         if (user)
2248         {
2249             if (user->hidden)
2250                 accounts_user_set_hidden (user, FALSE, TRUE);
2251             g_dbus_method_invocation_return_value (invocation, g_variant_new ("(o)", user->path));
2252         }
2253         else
2254             g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such user: %s", user_name);
2255     }
2256     else
2257         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
2258 }
2259
2260 static void
2261 handle_user_call (GDBusConnection       *connection,
2262                   const gchar           *sender,
2263                   const gchar           *object_path,
2264                   const gchar           *interface_name,
2265                   const gchar           *method_name,
2266                   GVariant              *parameters,
2267                   GDBusMethodInvocation *invocation,
2268                   gpointer               user_data)
2269 {
2270     AccountsUser *user = user_data;
2271
2272     if (strcmp (method_name, "SetXSession") == 0)
2273     {
2274         gchar *xsession;
2275
2276         g_variant_get (parameters, "(&s)", &xsession);
2277
2278         g_free (user->xsession);
2279         user->xsession = g_strdup (xsession);
2280
2281         g_dbus_method_invocation_return_value (invocation, g_variant_new ("()"));
2282
2283         /* And notify others that it took */
2284         g_dbus_connection_emit_signal (accounts_connection,
2285                                        NULL,
2286                                        user->path,
2287                                        "org.freedesktop.Accounts.User",
2288                                        "Changed",
2289                                        g_variant_new ("()"),
2290                                        NULL);
2291     }
2292     else
2293         g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No such method: %s", method_name);
2294 }
2295
2296 static GVariant *
2297 handle_user_get_property (GDBusConnection       *connection,
2298                           const gchar           *sender,
2299                           const gchar           *object_path,
2300                           const gchar           *interface_name,
2301                           const gchar           *property_name,
2302                           GError               **error,
2303                           gpointer               user_data)
2304 {
2305     AccountsUser *user = user_data;
2306
2307     if (strcmp (property_name, "UserName") == 0)
2308         return g_variant_new_string (user->user_name);
2309     else if (strcmp (property_name, "RealName") == 0)
2310         return g_variant_new_string (user->real_name);
2311     else if (strcmp (property_name, "HomeDirectory") == 0)
2312         return g_variant_new_string (user->home_directory);
2313     else if (strcmp (property_name, "SystemAccount") == 0)
2314         return g_variant_new_boolean (user->uid < 1000);
2315     else if (strcmp (property_name, "BackgroundFile") == 0)
2316         return g_variant_new_string (user->background ? user->background : "");
2317     else if (strcmp (property_name, "Language") == 0)
2318         return g_variant_new_string (user->language ? user->language : "");
2319     else if (strcmp (property_name, "IconFile") == 0)
2320         return g_variant_new_string (user->image ? user->image : "");
2321     else if (strcmp (property_name, "Shell") == 0)
2322         return g_variant_new_string ("/bin/sh");
2323     else if (strcmp (property_name, "Uid") == 0)
2324         return g_variant_new_uint64 (user->uid);
2325     else if (strcmp (property_name, "XSession") == 0)
2326         return g_variant_new_string (user->xsession ? user->xsession : "");
2327     else if (strcmp (property_name, "XKeyboardLayouts") == 0)
2328     {
2329         if (user->layouts != NULL)
2330             return g_variant_new_strv ((const gchar * const *) user->layouts, -1);
2331         else
2332             return g_variant_new_strv (NULL, 0);
2333     }
2334     else if (strcmp (property_name, "XHasMessages") == 0)
2335         return g_variant_new_boolean (user->has_messages);
2336
2337     return NULL;
2338 }
2339
2340 static void
2341 accounts_name_acquired_cb (GDBusConnection *connection,
2342                            const gchar     *name,
2343                            gpointer         user_data)
2344 {
2345     const gchar *accounts_interface =
2346         "<node>"
2347         "  <interface name='org.freedesktop.Accounts'>"
2348         "    <method name='ListCachedUsers'>"
2349         "      <arg name='user' direction='out' type='ao'/>"
2350         "    </method>"
2351         "    <method name='FindUserByName'>"
2352         "      <arg name='name' direction='in' type='s'/>"
2353         "      <arg name='user' direction='out' type='o'/>"
2354         "    </method>"
2355         "    <signal name='UserAdded'>"
2356         "      <arg name='user' type='o'/>"
2357         "    </signal>"
2358         "    <signal name='UserDeleted'>"
2359         "      <arg name='user' type='o'/>"
2360         "    </signal>"
2361         "  </interface>"
2362         "</node>";
2363     static const GDBusInterfaceVTable accounts_vtable =
2364     {
2365         handle_accounts_call,
2366     };
2367     const gchar *user_interface =
2368         "<node>"
2369         "  <interface name='org.freedesktop.Accounts.User'>"
2370         "    <method name='SetXSession'>"
2371         "      <arg name='x_session' direction='in' type='s'/>"
2372         "    </method>"
2373         "    <property name='UserName' type='s' access='read'/>"
2374         "    <property name='RealName' type='s' access='read'/>"
2375         "    <property name='HomeDirectory' type='s' access='read'/>"
2376         "    <property name='SystemAccount' type='b' access='read'/>"
2377         "    <property name='BackgroundFile' type='s' access='read'/>"
2378         "    <property name='Language' type='s' access='read'/>"
2379         "    <property name='IconFile' type='s' access='read'/>"
2380         "    <property name='Shell' type='s' access='read'/>"
2381         "    <property name='Uid' type='t' access='read'/>"
2382         "    <property name='XSession' type='s' access='read'/>"
2383         "    <property name='XKeyboardLayouts' type='as' access='read'/>"
2384         "    <property name='XHasMessages' type='b' access='read'/>"
2385         "    <signal name='Changed' />"
2386         "  </interface>"
2387         "</node>";
2388     GError *error = NULL;
2389
2390     accounts_connection = connection;
2391
2392     accounts_info = g_dbus_node_info_new_for_xml (accounts_interface, &error);
2393     if (error)
2394         g_warning ("Failed to parse D-Bus interface: %s", error->message);
2395     g_clear_error (&error);
2396     if (!accounts_info)
2397         return;
2398     user_info = g_dbus_node_info_new_for_xml (user_interface, &error);
2399     if (error)
2400         g_warning ("Failed to parse D-Bus interface: %s", error->message);
2401     g_clear_error (&error);
2402     if (!user_info)
2403         return;
2404     g_dbus_connection_register_object (connection,
2405                                        "/org/freedesktop/Accounts",
2406                                        accounts_info->interfaces[0],
2407                                        &accounts_vtable,
2408                                        NULL,
2409                                        NULL,
2410                                        &error);
2411     if (error)
2412         g_warning ("Failed to register accounts service: %s", error->message);
2413     g_clear_error (&error);
2414     g_dbus_node_info_unref (accounts_info);
2415
2416     service_count--;
2417     if (service_count == 0)
2418         ready ();
2419 }
2420
2421 static void
2422 start_accounts_service_daemon (void)
2423 {
2424     service_count++;
2425     g_bus_own_name (G_BUS_TYPE_SYSTEM,
2426                     "org.freedesktop.Accounts",
2427                     G_BUS_NAME_OWNER_FLAGS_NONE,
2428                     accounts_name_acquired_cb,
2429                     NULL,
2430                     NULL,
2431                     NULL,
2432                     NULL);
2433 }
2434
2435 static void
2436 ready (void)
2437 {
2438     run_commands ();
2439 }
2440
2441 static gboolean
2442 signal_cb (gpointer user_data)
2443 {
2444     g_print ("Caught signal, quitting\n");
2445     quit (EXIT_FAILURE);
2446     return FALSE;
2447 }
2448
2449 static void
2450 properties_changed_cb (GDBusConnection *connection,
2451                        const gchar *sender_name,
2452                        const gchar *object_path,
2453                        const gchar *interface_name,
2454                        const gchar *signal_name,
2455                        GVariant *parameters,
2456                        gpointer user_data)
2457 {
2458     const gchar *interface, *name;
2459     GString *status;
2460     GVariant *value;
2461     GVariantIter *changed_properties, *invalidated_properties;
2462     int i;
2463
2464     g_variant_get (parameters, "(&sa{sv}as)", &interface, &changed_properties, &invalidated_properties);
2465
2466     status = g_string_new ("RUNNER DBUS-PROPERTIES-CHANGED");
2467     g_string_append_printf (status, " PATH=%s", object_path);
2468     g_string_append_printf (status, " INTERFACE=%s", interface);
2469     for (i = 0; g_variant_iter_loop (changed_properties, "{&sv}", &name, &value); i++)
2470     {
2471         if (i == 0)
2472             g_string_append (status, " CHANGED=");
2473         else
2474             g_string_append (status, ",");
2475         g_string_append (status, name);
2476         if (g_variant_is_of_type (value, G_VARIANT_TYPE ("ao")))
2477         {
2478             GVariantIter iter;
2479             const gchar *path;
2480
2481             g_variant_iter_init (&iter, value);
2482             while (g_variant_iter_loop (&iter, "&o", &path))
2483                 g_string_append_printf (status, ":%s", path);
2484         }
2485     }
2486     for (i = 0; g_variant_iter_loop (invalidated_properties, "&s", &name); i++)
2487     {
2488         if (i == 0)
2489             g_string_append (status, " INVALIDATED=");
2490         else
2491             g_string_append (status, ",");
2492         g_string_append (status, name);
2493     }
2494
2495     check_status (status->str);
2496     g_string_free (status, TRUE);
2497 }
2498
2499 static void
2500 dbus_signal_cb (GDBusConnection *connection,
2501                 const gchar *sender_name,
2502                 const gchar *object_path,
2503                 const gchar *interface_name,
2504                 const gchar *signal_name,
2505                 GVariant *parameters,
2506                 gpointer user_data)
2507 {
2508     GString *status;
2509
2510     status = g_string_new ("RUNNER DBUS-SIGNAL");
2511     g_string_append_printf (status, " PATH=%s", object_path);
2512     g_string_append_printf (status, " INTERFACE=%s", interface_name);
2513     g_string_append_printf (status, " NAME=%s", signal_name);
2514
2515     check_status (status->str);
2516     g_string_free (status, TRUE);
2517 }
2518
2519 int
2520 main (int argc, char **argv)
2521 {
2522     GMainLoop *loop;
2523     int i;
2524     gchar *greeter = NULL, *script_name, *config_file, *additional_system_config;
2525     gchar *additional_config, *path, *path1, *path2, *ld_preload, *ld_library_path, *home_dir;
2526     GString *passwd_data, *group_data;
2527     GSource *status_source;
2528     gchar cwd[1024];
2529     GError *error = NULL;
2530
2531 #if !defined(GLIB_VERSION_2_36)
2532     g_type_init ();
2533 #endif
2534
2535     loop = g_main_loop_new (NULL, FALSE);
2536
2537     g_unix_signal_add (SIGINT, signal_cb, NULL);
2538     g_unix_signal_add (SIGTERM, signal_cb, NULL);
2539
2540     children = g_hash_table_new (g_direct_hash, g_direct_equal);
2541
2542     if (argc != 3)
2543     {
2544         g_printerr ("Usage %s SCRIPT-NAME GREETER\n", argv[0]);
2545         quit (EXIT_FAILURE);
2546     }
2547     script_name = argv[1];
2548     config_file = g_strdup_printf ("%s.conf", script_name);
2549     config_path = g_build_filename (SRCDIR, "tests", "scripts", config_file, NULL);
2550     g_free (config_file);
2551
2552     config = g_key_file_new ();
2553     g_key_file_load_from_file (config, config_path, G_KEY_FILE_NONE, NULL);
2554
2555     load_script (config_path);
2556
2557     if (!getcwd (cwd, 1024))
2558     {
2559         g_critical ("Error getting current directory: %s", strerror (errno));
2560         quit (EXIT_FAILURE);
2561     }
2562
2563     /* Don't contact our X server */
2564     g_unsetenv ("DISPLAY");
2565
2566     /* Don't let XDG vars from system affect tests */
2567     g_unsetenv ("XDG_CONFIG_DIRS");
2568     g_unsetenv ("XDG_DATA_DIRS");
2569
2570     /* Override system calls */
2571     ld_preload = g_build_filename (BUILDDIR, "tests", "src", ".libs", "libsystem.so", NULL);
2572     g_setenv ("LD_PRELOAD", ld_preload, TRUE);
2573     g_free (ld_preload);
2574
2575     /* Run test programs */
2576     path = g_strdup_printf ("%s/tests/src/.libs:%s/tests/src:%s/tests/src:%s/src:%s", BUILDDIR, BUILDDIR, SRCDIR, BUILDDIR, g_getenv ("PATH"));
2577     g_setenv ("PATH", path, TRUE);
2578     g_free (path);
2579
2580     /* Use locally built libraries */
2581     path1 = g_build_filename (BUILDDIR, "liblightdm-gobject", ".libs", NULL);
2582     path2 = g_build_filename (BUILDDIR, "liblightdm-qt", ".libs", NULL);
2583     ld_library_path = g_strdup_printf ("%s:%s", path1, path2);
2584     g_free (path1);
2585     g_free (path2);
2586     g_setenv ("LD_LIBRARY_PATH", ld_library_path, TRUE);
2587     g_free (ld_library_path);
2588     path1 = g_build_filename (BUILDDIR, "liblightdm-gobject", NULL);
2589     g_setenv ("GI_TYPELIB_PATH", path1, TRUE);
2590     g_free (path1);
2591
2592     /* Run in a temporary directory inside the build directory */
2593     /* Note we have to pick a name that is short since Unix sockets in this directory have a 108 character limit on their paths */
2594     i = 0;
2595     while (TRUE) {
2596         gchar *name;
2597
2598         name = g_strdup_printf (".r%d", i);
2599         g_free (temp_dir);
2600         temp_dir = g_build_filename ("/tmp", name, NULL);
2601         g_free (name);
2602         if (!g_file_test (temp_dir, G_FILE_TEST_EXISTS))
2603             break;
2604         i++;
2605     }
2606     g_mkdir_with_parents (temp_dir, 0755);
2607     g_setenv ("LIGHTDM_TEST_ROOT", temp_dir, TRUE);
2608
2609     /* Open socket for status */
2610     /* Note we have to pick a socket name that is short since there is a 108 character limit on the name */
2611     status_socket_name = g_build_filename (temp_dir, ".s", NULL);
2612     unlink (status_socket_name);
2613     status_socket = g_socket_new (G_SOCKET_FAMILY_UNIX, G_SOCKET_TYPE_STREAM, G_SOCKET_PROTOCOL_DEFAULT, &error);
2614     if (error)
2615         g_warning ("Error creating status socket %s: %s", status_socket_name, error->message);
2616     g_clear_error (&error);
2617     if (status_socket)
2618     {
2619         GSocketAddress *address;
2620         gboolean result;
2621
2622         address = g_unix_socket_address_new (status_socket_name);
2623         result = g_socket_bind (status_socket, address, FALSE, &error);
2624         g_object_unref (address);
2625         if (error)
2626             g_warning ("Error binding status socket %s: %s", status_socket_name, error->message);
2627         g_clear_error (&error);
2628         if (result)
2629         {
2630             result = g_socket_listen (status_socket, &error);
2631             if (error)
2632                 g_warning ("Error listening on status socket %s: %s", status_socket_name, error->message);
2633             g_clear_error (&error);
2634         }
2635         if (!result)
2636         {
2637             g_object_unref (status_socket);
2638             status_socket = NULL;
2639         }
2640     }
2641     if (!status_socket)
2642         quit (EXIT_FAILURE);
2643     status_source = g_socket_create_source (status_socket, G_IO_IN, NULL);
2644     g_source_set_callback (status_source, status_connect_cb, NULL, NULL);
2645     g_source_attach (status_source, NULL);
2646
2647     /* Set up a skeleton file system */
2648     g_mkdir_with_parents (g_strdup_printf ("%s/etc", temp_dir), 0755);
2649     g_mkdir_with_parents (g_strdup_printf ("%s/run", temp_dir), 0755);
2650     g_mkdir_with_parents (g_strdup_printf ("%s/usr/share", temp_dir), 0755);
2651     g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/sessions", temp_dir), 0755);
2652     g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/remote-sessions", temp_dir), 0755);
2653     g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/greeters", temp_dir), 0755);
2654     g_mkdir_with_parents (g_strdup_printf ("%s/tmp", temp_dir), 0755);
2655     g_mkdir_with_parents (g_strdup_printf ("%s/var/lib/lightdm-data", temp_dir), 0755);
2656     g_mkdir_with_parents (g_strdup_printf ("%s/var/run", temp_dir), 0755);
2657     g_mkdir_with_parents (g_strdup_printf ("%s/var/log", temp_dir), 0755);
2658
2659     /* Copy over the configuration */
2660     g_mkdir_with_parents (g_strdup_printf ("%s/etc/lightdm", temp_dir), 0755);
2661     if (!g_key_file_has_key (config, "test-runner-config", "have-config", NULL) || g_key_file_get_boolean (config, "test-runner-config", "have-config", NULL))
2662         if (system (g_strdup_printf ("cp %s %s/etc/lightdm/lightdm.conf", config_path, temp_dir)))
2663             perror ("Failed to copy configuration");
2664     if (system (g_strdup_printf ("cp %s/tests/data/keys.conf %s/etc/lightdm/", SRCDIR, temp_dir)))
2665         perror ("Failed to copy key configuration");
2666
2667     additional_system_config = g_key_file_get_string (config, "test-runner-config", "additional-system-config", NULL);
2668     if (additional_system_config)
2669     {
2670         gchar **files;
2671
2672         g_mkdir_with_parents (g_strdup_printf ("%s/usr/share/lightdm/lightdm.conf.d", temp_dir), 0755);
2673
2674         files = g_strsplit (additional_system_config, " ", -1);
2675         for (i = 0; files[i]; i++)
2676             if (system (g_strdup_printf ("cp %s/tests/scripts/%s %s/usr/share/lightdm/lightdm.conf.d", SRCDIR, files[i], temp_dir)))
2677                 perror ("Failed to copy configuration");
2678         g_strfreev (files);
2679     }
2680
2681     additional_config = g_key_file_get_string (config, "test-runner-config", "additional-config", NULL);
2682     if (additional_config)
2683     {
2684         gchar **files;
2685
2686         g_mkdir_with_parents (g_strdup_printf ("%s/etc/xdg/lightdm/lightdm.conf.d", temp_dir), 0755);
2687
2688         files = g_strsplit (additional_config, " ", -1);
2689         for (i = 0; files[i]; i++)
2690             if (system (g_strdup_printf ("cp %s/tests/scripts/%s %s/etc/xdg/lightdm/lightdm.conf.d", SRCDIR, files[i], temp_dir)))
2691                 perror ("Failed to copy configuration");
2692         g_strfreev (files);
2693     }
2694
2695     if (g_key_file_has_key (config, "test-runner-config", "shared-data-dirs", NULL))
2696     {
2697         gchar *dir_string;
2698         gchar **dirs;
2699         gint i;
2700
2701         dir_string = g_key_file_get_string (config, "test-runner-config", "shared-data-dirs", NULL);
2702         dirs = g_strsplit (dir_string, " ", -1);
2703         g_free (dir_string);
2704
2705         for (i = 0; dirs[i]; i++)
2706         {
2707             gchar **fields = g_strsplit (dirs[i], ":", -1);
2708             if (g_strv_length (fields) == 4)
2709             {
2710                 gchar *path = g_strdup_printf ("%s/var/lib/lightdm-data/%s", temp_dir, fields[0]);
2711                 int uid = g_ascii_strtoll (fields[1], NULL, 10);
2712                 int gid = g_ascii_strtoll (fields[2], NULL, 10);
2713                 int mode = g_ascii_strtoll (fields[3], NULL, 8);
2714                 g_mkdir (path, mode);
2715                 g_chmod (path, mode); /* mkdir filters by umask, so make sure we have what we want */
2716                 if (chown (path, uid, gid) < 0)
2717                   g_warning ("chown (%s) failed: %s", path, strerror (errno));
2718                 g_free (path);
2719             }
2720             g_strfreev (fields);
2721         }
2722
2723         g_strfreev (dirs);
2724     }
2725
2726     /* Always copy the script */
2727     if (system (g_strdup_printf ("cp %s %s/script", config_path, temp_dir)))
2728         perror ("Failed to copy configuration");
2729
2730     /* Copy over the greeter files */
2731     if (system (g_strdup_printf ("cp %s/sessions/* %s/usr/share/lightdm/sessions", DATADIR, temp_dir)))
2732         perror ("Failed to copy sessions");
2733     if (system (g_strdup_printf ("cp %s/remote-sessions/* %s/usr/share/lightdm/remote-sessions", DATADIR, temp_dir)))
2734         perror ("Failed to copy remote sessions");
2735     if (system (g_strdup_printf ("cp %s/greeters/* %s/usr/share/lightdm/greeters", DATADIR, temp_dir)))
2736         perror ("Failed to copy greeters");
2737
2738     /* Set up the default greeter */
2739     path = g_build_filename (temp_dir, "usr", "share", "lightdm", "greeters", "default.desktop", NULL);
2740     greeter = g_strdup_printf ("%s.desktop", argv[2]);
2741     if (symlink (greeter, path) < 0)
2742     {
2743         g_printerr ("Failed to make greeter symlink %s->%s: %s\n", path, greeter, strerror (errno));
2744         quit (EXIT_FAILURE);
2745     }
2746     g_free (path);
2747     g_free (greeter);
2748
2749     home_dir = g_build_filename (temp_dir, "home", NULL);
2750
2751     /* Make fake users */
2752     struct
2753     {
2754         gchar *user_name;
2755         gchar *password;
2756         gchar *real_name;
2757         gint uid;
2758     } users[] =
2759     {
2760         /* Root account */
2761         {"root",             "",          "root",                  0},
2762         /* Unprivileged account for greeters */
2763         {"lightdm",          "",          "",                    100},
2764         /* These accounts have a password */
2765         {"have-password1",   "password",  "Password User 1",    1000},
2766         {"have-password2",   "password",  "Password User 2",    1001},
2767         {"have-password3",   "password",  "Password User 3",    1002},
2768         {"have-password4",   "password",  "Password User 4",    1003},
2769         /* This account always prompts for a password, even if using the lightdm-autologin service */
2770         {"always-password",  "password",  "Password User 4",    1004},
2771         /* These accounts have no password */
2772         {"no-password1",     "",          "No Password User 1", 1005},
2773         {"no-password2",     "",          "No Password User 2", 1006},
2774         {"no-password3",     "",          "No Password User 3", 1007},
2775         {"no-password4",     "",          "No Password User 4", 1008},
2776         /* This account has a keyboard layout */
2777         {"have-layout",      "",          "Layout User",        1009},
2778         /* This account has a set of keyboard layouts */
2779         {"have-layouts",     "",          "Layouts User",       1010},
2780         /* This account has a language set */
2781         {"have-language",    "",          "Language User",      1011},
2782         /* This account has a preconfigured session */
2783         {"have-session",            "",   "Session User",       1012},
2784         /* This account has the home directory mounted on login */
2785         {"mount-home-dir",   "",          "Mounted Home Dir User", 1013},
2786         /* This account is denied access */
2787         {"denied",           "",          "Denied User",        1014},
2788         /* This account has expired */
2789         {"expired",          "",          "Expired User",       1015},
2790         /* This account needs a password change */
2791         {"new-authtok",      "",          "New Token User",     1016},
2792         /* This account is switched to change-user2 when authentication succeeds */
2793         {"change-user1",     "",          "Change User 1",      1017},
2794         {"change-user2",     "",          "Change User 2",      1018},
2795         /* This account switches to invalid-user when authentication succeeds */
2796         {"change-user-invalid", "",       "Invalid Change User", 1019},
2797         /* This account crashes on authentication */
2798         {"crash-authenticate", "",        "Crash Auth User",    1020},
2799         /* This account shows an informational prompt on login */
2800         {"info-prompt",      "password",  "Info Prompt",        1021},
2801         /* This account shows multiple informational prompts on login */
2802         {"multi-info-prompt","password",  "Multi Info Prompt",  1022},
2803         /* This account uses two factor authentication */
2804         {"two-factor",       "password",  "Two Factor",         1023},
2805         /* This account has a special group */
2806         {"group-member",     "password",  "Group Member",       1024},
2807         /* This account has the home directory created when the session starts */
2808         {"make-home-dir",    "",          "Make Home Dir User", 1025},
2809         /* This account fails to open a session */
2810         {"session-error",    "password",  "Session Error",      1026},
2811         /* This account can't establish credentials */
2812         {"cred-error",       "password",  "Cred Error",         1027},
2813         /* This account has expired credentials */
2814         {"cred-expired",     "password",  "Cred Expired",       1028},
2815         /* This account has cannot access their credentials */
2816         {"cred-unavail",     "password",  "Cred Unavail",       1029},
2817         /* This account sends informational messages for each PAM function that is called */
2818         {"log-pam",          "password",  "Log PAM",            1030},
2819         /* This account shows multiple prompts on login */
2820         {"multi-prompt",     "password",  "Multi Prompt",       1031},
2821         /* This account has an existing corrupt X authority */
2822         {"corrupt-xauth",    "password",  "Corrupt Xauthority", 1032},
2823         /* User to test properties */
2824         {"prop-user",        "",          "TEST",               1033},
2825         {NULL,               NULL,        NULL,                    0}
2826     };
2827     passwd_data = g_string_new ("");
2828     group_data = g_string_new ("");
2829     for (i = 0; users[i].user_name; i++)
2830     {
2831         GKeyFile *dmrc_file;
2832         gboolean save_dmrc = FALSE;
2833
2834         if (strcmp (users[i].user_name, "mount-home-dir") != 0 && strcmp (users[i].user_name, "make-home-dir") != 0)
2835         {
2836             path = g_build_filename (home_dir, users[i].user_name, NULL);
2837             g_mkdir_with_parents (path, 0755);
2838             if (chown (path, users[i].uid, users[i].uid) < 0)
2839               g_debug ("chown (%s) failed: %s", path, strerror (errno));
2840             g_free (path);
2841         }
2842
2843         dmrc_file = g_key_file_new ();
2844         if (strcmp (users[i].user_name, "have-session") == 0)
2845         {
2846             g_key_file_set_string (dmrc_file, "Desktop", "Session", "alternative");
2847             save_dmrc = TRUE;
2848         }
2849         if (strcmp (users[i].user_name, "have-layout") == 0)
2850         {
2851             g_key_file_set_string (dmrc_file, "Desktop", "Layout", "us");
2852             save_dmrc = TRUE;
2853         }
2854         if (strcmp (users[i].user_name, "have-layouts") == 0)
2855         {
2856             g_key_file_set_string (dmrc_file, "Desktop", "Layout", "ru");
2857             g_key_file_set_string (dmrc_file, "X-Accounts", "Layouts", "fr\toss;ru;");
2858             save_dmrc = TRUE;
2859         }
2860         if (strcmp (users[i].user_name, "have-language") == 0)
2861         {
2862             g_key_file_set_string (dmrc_file, "Desktop", "Language", "en_AU.utf8");
2863             save_dmrc = TRUE;
2864         }
2865
2866         if (save_dmrc)
2867         {
2868             gchar *data;
2869
2870             path = g_build_filename (home_dir, users[i].user_name, ".dmrc", NULL);
2871             data = g_key_file_to_data (dmrc_file, NULL, NULL);
2872             g_file_set_contents (path, data, -1, NULL);
2873             g_free (data);
2874             g_free (path);
2875         }
2876
2877         g_key_file_free (dmrc_file);
2878
2879         /* Write corrupt X authority file */
2880         if (strcmp (users[i].user_name, "corrupt-xauth") == 0)
2881         {
2882             gchar data[1] = { 0xFF };
2883
2884             path = g_build_filename (home_dir, users[i].user_name, ".Xauthority", NULL);
2885             g_file_set_contents (path, data, 1, NULL);
2886             chmod (path, S_IRUSR | S_IWUSR);
2887             g_free (path);
2888         }
2889
2890         /* Add passwd file entry */
2891         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);
2892
2893         /* Add group file entry */
2894         g_string_append_printf (group_data, "%s:x:%d:%s\n", users[i].user_name, users[i].uid, users[i].user_name);
2895     }
2896     path = g_build_filename (temp_dir, "etc", "passwd", NULL);
2897     g_file_set_contents (path, passwd_data->str, -1, NULL);
2898     g_free (path);
2899     g_string_free (passwd_data, TRUE);
2900
2901     /* Add an extra test group */
2902     g_string_append_printf (group_data, "test-group:x:111:\n");
2903
2904     path = g_build_filename (temp_dir, "etc", "group", NULL);
2905     g_file_set_contents (path, group_data->str, -1, NULL);
2906     g_free (path);
2907     g_string_free (group_data, TRUE);
2908
2909     if (g_key_file_has_key (config, "test-runner-config", "timeout", NULL))
2910         status_timeout_ms = g_key_file_get_integer (config, "test-runner-config", "timeout", NULL) * 1000;
2911
2912     /* Start D-Bus services */
2913     if (!g_key_file_get_boolean (config, "test-runner-config", "disable-upower", NULL))
2914         start_upower_daemon ();
2915     if (!g_key_file_get_boolean (config, "test-runner-config", "disable-console-kit", NULL))
2916         start_console_kit_daemon ();
2917     if (!g_key_file_get_boolean (config, "test-runner-config", "disable-login1", NULL))
2918         start_login1_daemon ();
2919     if (!g_key_file_get_boolean (config, "test-runner-config", "disable-accounts-service", NULL))
2920         start_accounts_service_daemon ();
2921
2922     /* Listen for daemon bus events */
2923     if (g_key_file_get_boolean (config, "test-runner-config", "log-dbus", NULL))
2924     {
2925         g_dbus_connection_signal_subscribe (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
2926                                             "org.freedesktop.DisplayManager",
2927                                             "org.freedesktop.DBus.Properties",
2928                                             "PropertiesChanged",
2929                                             NULL,
2930                                             NULL,
2931                                             G_DBUS_SIGNAL_FLAGS_NONE,
2932                                             properties_changed_cb,
2933                                             NULL,
2934                                             NULL);
2935         g_dbus_connection_signal_subscribe (g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, NULL),
2936                                             "org.freedesktop.DisplayManager",
2937                                             "org.freedesktop.DisplayManager",
2938                                             NULL,
2939                                             NULL,
2940                                             NULL,
2941                                             G_DBUS_SIGNAL_FLAGS_NONE,
2942                                             dbus_signal_cb,
2943                                             NULL,
2944                                             NULL);
2945     }
2946
2947     g_main_loop_run (loop);
2948
2949     return EXIT_FAILURE;
2950 }