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