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