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