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