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