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