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