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);
558 machine_add_to_gc_queue(mm);
561 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
563 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
564 _cleanup_free_ char *unit = NULL;
567 path = dbus_message_get_path(message);
571 unit_name_from_dbus_path(path, &unit);
575 mm = hashmap_get(m->machine_units, unit);
577 machine_add_to_gc_queue(mm);
580 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
581 const char *path, *unit;
584 if (!dbus_message_get_args(message, &error,
585 DBUS_TYPE_STRING, &unit,
586 DBUS_TYPE_OBJECT_PATH, &path,
587 DBUS_TYPE_INVALID)) {
588 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
592 mm = hashmap_get(m->machine_units, unit);
594 machine_add_to_gc_queue(mm);
596 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
599 if (!dbus_message_get_args(message, &error,
600 DBUS_TYPE_BOOLEAN, &b,
601 DBUS_TYPE_INVALID)) {
602 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
606 /* systemd finished reloading, let's recheck all our machines */
611 log_debug("System manager has been reloaded, rechecking machines...");
613 HASHMAP_FOREACH(mm, m->machines, i)
614 machine_add_to_gc_queue(mm);
619 dbus_error_free(&error);
621 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
624 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
626 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
629 type = dbus_message_iter_get_arg_type(src);
633 case DBUS_TYPE_STRUCT: {
634 DBusMessageIter dest_sub, src_sub;
636 dbus_message_iter_recurse(src, &src_sub);
638 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
641 r = copy_many_fields(&dest_sub, &src_sub);
645 if (!dbus_message_iter_close_container(dest, &dest_sub))
651 case DBUS_TYPE_ARRAY: {
652 DBusMessageIter dest_sub, src_sub;
654 dbus_message_iter_recurse(src, &src_sub);
656 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
659 r = copy_many_fields(&dest_sub, &src_sub);
663 if (!dbus_message_iter_close_container(dest, &dest_sub))
669 case DBUS_TYPE_VARIANT: {
670 DBusMessageIter dest_sub, src_sub;
672 dbus_message_iter_recurse(src, &src_sub);
674 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
677 r = copy_one_field(&dest_sub, &src_sub);
681 if (!dbus_message_iter_close_container(dest, &dest_sub))
687 case DBUS_TYPE_STRING:
688 case DBUS_TYPE_OBJECT_PATH:
690 case DBUS_TYPE_BOOLEAN:
691 case DBUS_TYPE_UINT16:
692 case DBUS_TYPE_INT16:
693 case DBUS_TYPE_UINT32:
694 case DBUS_TYPE_INT32:
695 case DBUS_TYPE_UINT64:
696 case DBUS_TYPE_INT64:
697 case DBUS_TYPE_DOUBLE:
698 case DBUS_TYPE_SIGNATURE: {
701 dbus_message_iter_get_basic(src, &p);
702 dbus_message_iter_append_basic(dest, type, &p);
711 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
717 while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
719 r = copy_one_field(dest, src);
723 dbus_message_iter_next(src);
729 int manager_start_scope(
734 const char *description,
735 DBusMessageIter *more_properties,
739 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
740 DBusMessageIter iter, sub, sub2, sub3, sub4;
741 const char *timeout_stop_property = "TimeoutStopUSec";
742 const char *pids_property = "PIDs";
743 uint64_t timeout = 500 * USEC_PER_MSEC;
744 const char *fail = "fail";
755 m = dbus_message_new_method_call(
756 "org.freedesktop.systemd1",
757 "/org/freedesktop/systemd1",
758 "org.freedesktop.systemd1.Manager",
759 "StartTransientUnit");
763 dbus_message_iter_init_append(m, &iter);
765 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
766 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
767 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
770 if (!isempty(slice)) {
771 const char *slice_property = "Slice";
773 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
774 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
775 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
776 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
777 !dbus_message_iter_close_container(&sub2, &sub3) ||
778 !dbus_message_iter_close_container(&sub, &sub2))
782 if (!isempty(description)) {
783 const char *description_property = "Description";
785 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
786 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
787 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
788 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
789 !dbus_message_iter_close_container(&sub2, &sub3) ||
790 !dbus_message_iter_close_container(&sub, &sub2))
794 /* cgroup empty notification is not available in containers
795 * currently. To make this less problematic, let's shorten the
796 * stop timeout for sessions, so that we don't wait
799 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
800 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
801 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
802 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
803 !dbus_message_iter_close_container(&sub2, &sub3) ||
804 !dbus_message_iter_close_container(&sub, &sub2))
808 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
809 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
810 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
811 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
812 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
813 !dbus_message_iter_close_container(&sub3, &sub4) ||
814 !dbus_message_iter_close_container(&sub2, &sub3) ||
815 !dbus_message_iter_close_container(&sub, &sub2))
818 if (more_properties) {
819 r = copy_many_fields(&sub, more_properties);
824 if (!dbus_message_iter_close_container(&iter, &sub))
827 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
835 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
848 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
849 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
850 const char *fail = "fail";
856 r = bus_method_call_with_reply(
858 "org.freedesktop.systemd1",
859 "/org/freedesktop/systemd1",
860 "org.freedesktop.systemd1.Manager",
864 DBUS_TYPE_STRING, &unit,
865 DBUS_TYPE_STRING, &fail,
868 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
869 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
874 dbus_error_free(error);
878 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
886 if (!dbus_message_get_args(reply, error,
887 DBUS_TYPE_OBJECT_PATH, &j,
888 DBUS_TYPE_INVALID)) {
889 log_error("Failed to parse reply.");
903 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
904 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
911 w = who == KILL_LEADER ? "process" : "cgroup";
912 assert_cc(sizeof(signo) == sizeof(int32_t));
914 r = bus_method_call_with_reply(
916 "org.freedesktop.systemd1",
917 "/org/freedesktop/systemd1",
918 "org.freedesktop.systemd1.Manager",
922 DBUS_TYPE_STRING, &unit,
923 DBUS_TYPE_STRING, &w,
924 DBUS_TYPE_INT32, &signo,
927 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
934 int manager_unit_is_active(Manager *manager, const char *unit) {
936 const char *interface = "org.freedesktop.systemd1.Unit";
937 const char *property = "ActiveState";
938 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
939 _cleanup_free_ char *path = NULL;
940 DBusMessageIter iter, sub;
948 dbus_error_init(&error);
950 path = unit_dbus_path_from_name(unit);
954 r = bus_method_call_with_reply(
956 "org.freedesktop.systemd1",
958 "org.freedesktop.DBus.Properties",
962 DBUS_TYPE_STRING, &interface,
963 DBUS_TYPE_STRING, &property,
966 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
967 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
968 dbus_error_free(&error);
972 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
973 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
974 dbus_error_free(&error);
978 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
979 dbus_error_free(&error);
983 if (!dbus_message_iter_init(reply, &iter) ||
984 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
985 log_error("Failed to parse reply.");
989 dbus_message_iter_recurse(&iter, &sub);
990 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
991 log_error("Failed to parse reply.");
995 dbus_message_iter_get_basic(&sub, &state);
997 return !streq(state, "inactive") && !streq(state, "failed");