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 (¶ms, G_VARIANT_TYPE ("a(sv)"));
1306 CKSession *session = open_ck_session (connection, g_variant_builder_end (¶ms));
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