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