1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
27 #include <systemd/sd-id128.h>
28 #include <systemd/sd-messages.h>
31 #include "dbus-common.h"
34 #include "path-util.h"
36 #include "sleep-config.h"
37 #include "fileio-label.h"
40 #include "unit-name.h"
41 #include "bus-errors.h"
44 #define BUS_MANAGER_INTERFACE \
45 " <interface name=\"org.freedesktop.machine1.Manager\">\n" \
46 " <method name=\"GetMachine\">\n" \
47 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
48 " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
50 " <method name=\"GetMachineByPID\">\n" \
51 " <arg name=\"pid\" type=\"u\" direction=\"in\"/>\n" \
52 " <arg name=\"machine\" type=\"o\" direction=\"out\"/>\n" \
54 " <method name=\"ListMachines\">\n" \
55 " <arg name=\"machines\" type=\"a(ssso)\" direction=\"out\"/>\n" \
57 " <method name=\"CreateMachine\">\n" \
58 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
59 " <arg name=\"id\" type=\"ay\" direction=\"in\"/>\n" \
60 " <arg name=\"service\" type=\"s\" direction=\"in\"/>\n" \
61 " <arg name=\"class\" type=\"s\" direction=\"in\"/>\n" \
62 " <arg name=\"leader\" type=\"u\" direction=\"in\"/>\n" \
63 " <arg name=\"root_directory\" type=\"s\" direction=\"in\"/>\n" \
64 " <arg name=\"scope_properties\" type=\"a(sv)\" direction=\"in\"/>\n" \
65 " <arg name=\"path\" type=\"o\" direction=\"out\"/>\n" \
67 " <method name=\"KillMachine\">\n" \
68 " <arg name=\"name\" type=\"s\" direction=\"in\"/>\n" \
69 " <arg name=\"who\" type=\"s\" direction=\"in\"/>\n" \
70 " <arg name=\"signal\" type=\"s\" direction=\"in\"/>\n" \
72 " <method name=\"TerminateMachine\">\n" \
73 " <arg name=\"id\" type=\"s\" direction=\"in\"/>\n" \
75 " <signal name=\"MachineNew\">\n" \
76 " <arg name=\"machine\" type=\"s\"/>\n" \
77 " <arg name=\"path\" type=\"o\"/>\n" \
79 " <signal name=\"MachineRemoved\">\n" \
80 " <arg name=\"machine\" type=\"s\"/>\n" \
81 " <arg name=\"path\" type=\"o\"/>\n" \
85 #define INTROSPECTION_BEGIN \
86 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
88 BUS_MANAGER_INTERFACE \
89 BUS_PROPERTIES_INTERFACE \
91 BUS_INTROSPECTABLE_INTERFACE
93 #define INTROSPECTION_END \
96 #define INTERFACES_LIST \
97 BUS_GENERIC_INTERFACES_LIST \
98 "org.freedesktop.machine1.Manager\0"
100 static bool valid_machine_name(const char *p) {
103 if (!filename_is_safe(p))
106 if (!ascii_is_valid(p))
117 static int bus_manager_create_machine(Manager *manager, DBusMessage *message) {
119 const char *name, *service, *class, *root_directory;
120 _cleanup_free_ char *p = NULL;
121 DBusMessageIter iter, sub;
132 if (!dbus_message_iter_init(message, &iter) ||
133 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
136 dbus_message_iter_get_basic(&iter, &name);
138 if (!valid_machine_name(name) ||
139 !dbus_message_iter_next(&iter) ||
140 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
141 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
144 dbus_message_iter_recurse(&iter, &sub);
145 dbus_message_iter_get_fixed_array(&sub, &v, &n);
154 if (!dbus_message_iter_next(&iter) ||
155 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
158 dbus_message_iter_get_basic(&iter, &service);
160 if (!dbus_message_iter_next(&iter) ||
161 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
164 dbus_message_iter_get_basic(&iter, &class);
167 c = _MACHINE_CLASS_INVALID;
169 c = machine_class_from_string(class);
174 if (!dbus_message_iter_next(&iter) ||
175 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
178 dbus_message_iter_get_basic(&iter, &leader);
179 if (!dbus_message_iter_next(&iter) ||
180 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
183 dbus_message_iter_get_basic(&iter, &root_directory);
185 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
188 if (!dbus_message_iter_next(&iter) ||
189 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
190 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)
193 dbus_message_iter_recurse(&iter, &sub);
195 if (hashmap_get(manager->machines, name))
199 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
204 r = manager_add_machine(manager, name, &m);
212 if (!isempty(service)) {
213 m->service = strdup(service);
220 if (!isempty(root_directory)) {
221 m->root_directory = strdup(root_directory);
222 if (!m->root_directory) {
228 r = machine_start(m, &sub);
232 m->create_message = dbus_message_ref(message);
238 machine_add_to_gc_queue(m);
243 static DBusHandlerResult manager_message_handler(
244 DBusConnection *connection,
245 DBusMessage *message,
248 Manager *m = userdata;
251 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
258 dbus_error_init(&error);
260 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
266 if (!dbus_message_get_args(
269 DBUS_TYPE_STRING, &name,
271 return bus_send_error_reply(connection, message, &error, -EINVAL);
273 machine = hashmap_get(m->machines, name);
275 return bus_send_error_reply(connection, message, &error, -ENOENT);
277 reply = dbus_message_new_method_return(message);
281 p = machine_bus_path(machine);
285 b = dbus_message_append_args(
287 DBUS_TYPE_OBJECT_PATH, &p,
294 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
300 if (!dbus_message_get_args(
303 DBUS_TYPE_UINT32, &pid,
305 return bus_send_error_reply(connection, message, &error, -EINVAL);
307 r = manager_get_machine_by_pid(m, pid, &machine);
309 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
311 reply = dbus_message_new_method_return(message);
315 p = machine_bus_path(machine);
319 b = dbus_message_append_args(
321 DBUS_TYPE_OBJECT_PATH, &p,
328 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
331 DBusMessageIter iter, sub;
333 reply = dbus_message_new_method_return(message);
337 dbus_message_iter_init_append(reply, &iter);
339 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
342 HASHMAP_FOREACH(machine, m->machines, i) {
343 _cleanup_free_ char *p = NULL;
344 DBusMessageIter sub2;
347 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
350 p = machine_bus_path(machine);
354 class = strempty(machine_class_to_string(machine->class));
356 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
357 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
358 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
359 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
364 if (!dbus_message_iter_close_container(&sub, &sub2))
368 if (!dbus_message_iter_close_container(&iter, &sub))
371 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
373 r = bus_manager_create_machine(m, message);
375 return bus_send_error_reply(connection, message, NULL, r);
377 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
384 if (!dbus_message_get_args(
387 DBUS_TYPE_STRING, &name,
388 DBUS_TYPE_STRING, &swho,
389 DBUS_TYPE_INT32, &signo,
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
396 who = kill_who_from_string(swho);
398 return bus_send_error_reply(connection, message, &error, -EINVAL);
401 if (signo <= 0 || signo >= _NSIG)
402 return bus_send_error_reply(connection, message, &error, -EINVAL);
404 machine = hashmap_get(m->machines, name);
406 return bus_send_error_reply(connection, message, &error, -ENOENT);
408 r = machine_kill(machine, who, signo);
410 return bus_send_error_reply(connection, message, NULL, r);
412 reply = dbus_message_new_method_return(message);
416 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
420 if (!dbus_message_get_args(
423 DBUS_TYPE_STRING, &name,
425 return bus_send_error_reply(connection, message, &error, -EINVAL);
427 machine = hashmap_get(m->machines, name);
429 return bus_send_error_reply(connection, message, &error, -ENOENT);
431 r = machine_stop(machine);
433 return bus_send_error_reply(connection, message, NULL, r);
435 reply = dbus_message_new_method_return(message);
439 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
440 char *introspection = NULL;
447 reply = dbus_message_new_method_return(message);
451 /* We roll our own introspection code here, instead of
452 * relying on bus_default_message_handler() because we
453 * need to generate our introspection string
456 f = open_memstream(&introspection, &size);
460 fputs(INTROSPECTION_BEGIN, f);
462 HASHMAP_FOREACH(machine, m->machines, i) {
463 p = bus_path_escape(machine->name);
466 fprintf(f, "<node name=\"machine/%s\"/>", p);
471 fputs(INTROSPECTION_END, f);
484 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
491 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
494 if (!bus_maybe_send_reply(connection, message, reply))
498 return DBUS_HANDLER_RESULT_HANDLED;
501 dbus_error_free(&error);
503 return DBUS_HANDLER_RESULT_NEED_MEMORY;
506 const DBusObjectPathVTable bus_manager_vtable = {
507 .message_function = manager_message_handler
510 DBusHandlerResult bus_message_filter(
511 DBusConnection *connection,
512 DBusMessage *message,
515 Manager *m = userdata;
522 dbus_error_init(&error);
524 log_debug("Got message: %s %s %s", strna(dbus_message_get_sender(message)), strna(dbus_message_get_interface(message)), strna(dbus_message_get_member(message)));
526 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
527 const char *path, *result, *unit;
531 if (!dbus_message_get_args(message, &error,
532 DBUS_TYPE_UINT32, &id,
533 DBUS_TYPE_OBJECT_PATH, &path,
534 DBUS_TYPE_STRING, &unit,
535 DBUS_TYPE_STRING, &result,
536 DBUS_TYPE_INVALID)) {
537 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
541 mm = hashmap_get(m->machine_units, unit);
543 if (streq_ptr(path, mm->scope_job)) {
545 mm->scope_job = NULL;
548 if (streq(result, "done"))
549 machine_send_create_reply(mm, NULL);
551 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
552 machine_send_create_reply(mm, &error);
559 machine_add_to_gc_queue(mm);
562 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
564 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
565 _cleanup_free_ char *unit = NULL;
568 path = dbus_message_get_path(message);
572 unit_name_from_dbus_path(path, &unit);
576 mm = hashmap_get(m->machine_units, unit);
578 machine_add_to_gc_queue(mm);
581 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
582 const char *path, *unit;
585 if (!dbus_message_get_args(message, &error,
586 DBUS_TYPE_STRING, &unit,
587 DBUS_TYPE_OBJECT_PATH, &path,
588 DBUS_TYPE_INVALID)) {
589 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
593 mm = hashmap_get(m->machine_units, unit);
595 machine_add_to_gc_queue(mm);
597 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
600 if (!dbus_message_get_args(message, &error,
601 DBUS_TYPE_BOOLEAN, &b,
602 DBUS_TYPE_INVALID)) {
603 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
607 /* systemd finished reloading, let's recheck all our machines */
612 log_debug("System manager has been reloaded, rechecking machines...");
614 HASHMAP_FOREACH(mm, m->machines, i)
615 machine_add_to_gc_queue(mm);
620 dbus_error_free(&error);
622 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
625 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
627 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
630 type = dbus_message_iter_get_arg_type(src);
634 case DBUS_TYPE_STRUCT: {
635 DBusMessageIter dest_sub, src_sub;
637 dbus_message_iter_recurse(src, &src_sub);
639 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
642 r = copy_many_fields(&dest_sub, &src_sub);
646 if (!dbus_message_iter_close_container(dest, &dest_sub))
652 case DBUS_TYPE_ARRAY: {
653 DBusMessageIter dest_sub, src_sub;
655 dbus_message_iter_recurse(src, &src_sub);
657 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
660 r = copy_many_fields(&dest_sub, &src_sub);
664 if (!dbus_message_iter_close_container(dest, &dest_sub))
670 case DBUS_TYPE_VARIANT: {
671 DBusMessageIter dest_sub, src_sub;
673 dbus_message_iter_recurse(src, &src_sub);
675 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
678 r = copy_one_field(&dest_sub, &src_sub);
682 if (!dbus_message_iter_close_container(dest, &dest_sub))
688 case DBUS_TYPE_STRING:
689 case DBUS_TYPE_OBJECT_PATH:
691 case DBUS_TYPE_BOOLEAN:
692 case DBUS_TYPE_UINT16:
693 case DBUS_TYPE_INT16:
694 case DBUS_TYPE_UINT32:
695 case DBUS_TYPE_INT32:
696 case DBUS_TYPE_UINT64:
697 case DBUS_TYPE_INT64:
698 case DBUS_TYPE_DOUBLE:
699 case DBUS_TYPE_SIGNATURE: {
702 dbus_message_iter_get_basic(src, &p);
703 dbus_message_iter_append_basic(dest, type, &p);
712 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
718 while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
720 r = copy_one_field(dest, src);
724 dbus_message_iter_next(src);
730 int manager_start_scope(
735 const char *description,
736 DBusMessageIter *more_properties,
740 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
741 DBusMessageIter iter, sub, sub2, sub3, sub4;
742 const char *timeout_stop_property = "TimeoutStopUSec";
743 const char *pids_property = "PIDs";
744 uint64_t timeout = 500 * USEC_PER_MSEC;
745 const char *fail = "fail";
756 m = dbus_message_new_method_call(
757 "org.freedesktop.systemd1",
758 "/org/freedesktop/systemd1",
759 "org.freedesktop.systemd1.Manager",
760 "StartTransientUnit");
764 dbus_message_iter_init_append(m, &iter);
766 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
767 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
768 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
771 if (!isempty(slice)) {
772 const char *slice_property = "Slice";
774 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
775 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
776 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
777 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
778 !dbus_message_iter_close_container(&sub2, &sub3) ||
779 !dbus_message_iter_close_container(&sub, &sub2))
783 if (!isempty(description)) {
784 const char *description_property = "Description";
786 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
787 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
788 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
789 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
790 !dbus_message_iter_close_container(&sub2, &sub3) ||
791 !dbus_message_iter_close_container(&sub, &sub2))
795 /* cgroup empty notification is not available in containers
796 * currently. To make this less problematic, let's shorten the
797 * stop timeout for sessions, so that we don't wait
800 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
801 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
802 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
803 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
804 !dbus_message_iter_close_container(&sub2, &sub3) ||
805 !dbus_message_iter_close_container(&sub, &sub2))
809 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
810 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
811 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
812 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
813 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
814 !dbus_message_iter_close_container(&sub3, &sub4) ||
815 !dbus_message_iter_close_container(&sub2, &sub3) ||
816 !dbus_message_iter_close_container(&sub, &sub2))
819 if (more_properties) {
820 r = copy_many_fields(&sub, more_properties);
825 if (!dbus_message_iter_close_container(&iter, &sub))
828 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
836 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
849 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
850 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
851 const char *fail = "fail";
857 r = bus_method_call_with_reply(
859 "org.freedesktop.systemd1",
860 "/org/freedesktop/systemd1",
861 "org.freedesktop.systemd1.Manager",
865 DBUS_TYPE_STRING, &unit,
866 DBUS_TYPE_STRING, &fail,
869 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
870 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
875 dbus_error_free(error);
879 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
887 if (!dbus_message_get_args(reply, error,
888 DBUS_TYPE_OBJECT_PATH, &j,
889 DBUS_TYPE_INVALID)) {
890 log_error("Failed to parse reply.");
904 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
905 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
912 w = who == KILL_LEADER ? "process" : "cgroup";
913 assert_cc(sizeof(signo) == sizeof(int32_t));
915 r = bus_method_call_with_reply(
917 "org.freedesktop.systemd1",
918 "/org/freedesktop/systemd1",
919 "org.freedesktop.systemd1.Manager",
923 DBUS_TYPE_STRING, &unit,
924 DBUS_TYPE_STRING, &w,
925 DBUS_TYPE_INT32, &signo,
928 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
935 int manager_unit_is_active(Manager *manager, const char *unit) {
937 const char *interface = "org.freedesktop.systemd1.Unit";
938 const char *property = "ActiveState";
939 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
940 _cleanup_free_ char *path = NULL;
941 DBusMessageIter iter, sub;
949 dbus_error_init(&error);
951 path = unit_dbus_path_from_name(unit);
955 r = bus_method_call_with_reply(
957 "org.freedesktop.systemd1",
959 "org.freedesktop.DBus.Properties",
963 DBUS_TYPE_STRING, &interface,
964 DBUS_TYPE_STRING, &property,
967 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
968 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
969 dbus_error_free(&error);
973 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
974 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
975 dbus_error_free(&error);
979 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
980 dbus_error_free(&error);
984 if (!dbus_message_iter_init(reply, &iter) ||
985 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
986 log_error("Failed to parse reply.");
990 dbus_message_iter_recurse(&iter, &sub);
991 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
992 log_error("Failed to parse reply.");
996 dbus_message_iter_get_basic(&sub, &state);
998 return !streq(state, "inactive") && !streq(state, "failed");