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 DBusMessageIter iter, sub;
131 if (!dbus_message_iter_init(message, &iter) ||
132 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
135 dbus_message_iter_get_basic(&iter, &name);
137 if (!valid_machine_name(name) ||
138 !dbus_message_iter_next(&iter) ||
139 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
140 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_BYTE)
143 dbus_message_iter_recurse(&iter, &sub);
144 dbus_message_iter_get_fixed_array(&sub, &v, &n);
153 if (!dbus_message_iter_next(&iter) ||
154 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
157 dbus_message_iter_get_basic(&iter, &service);
159 if (!dbus_message_iter_next(&iter) ||
160 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
163 dbus_message_iter_get_basic(&iter, &class);
166 c = _MACHINE_CLASS_INVALID;
168 c = machine_class_from_string(class);
173 if (!dbus_message_iter_next(&iter) ||
174 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
177 dbus_message_iter_get_basic(&iter, &leader);
178 if (!dbus_message_iter_next(&iter) ||
179 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
182 dbus_message_iter_get_basic(&iter, &root_directory);
184 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
187 if (!dbus_message_iter_next(&iter) ||
188 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
189 dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRUCT)
192 dbus_message_iter_recurse(&iter, &sub);
194 if (hashmap_get(manager->machines, name))
198 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
203 r = manager_add_machine(manager, name, &m);
211 if (!isempty(service)) {
212 m->service = strdup(service);
219 if (!isempty(root_directory)) {
220 m->root_directory = strdup(root_directory);
221 if (!m->root_directory) {
227 r = machine_start(m, &sub);
231 m->create_message = dbus_message_ref(message);
237 machine_add_to_gc_queue(m);
242 static DBusHandlerResult manager_message_handler(
243 DBusConnection *connection,
244 DBusMessage *message,
247 Manager *m = userdata;
250 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
257 dbus_error_init(&error);
259 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
265 if (!dbus_message_get_args(
268 DBUS_TYPE_STRING, &name,
270 return bus_send_error_reply(connection, message, &error, -EINVAL);
272 machine = hashmap_get(m->machines, name);
274 return bus_send_error_reply(connection, message, &error, -ENOENT);
276 reply = dbus_message_new_method_return(message);
280 p = machine_bus_path(machine);
284 b = dbus_message_append_args(
286 DBUS_TYPE_OBJECT_PATH, &p,
293 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
299 if (!dbus_message_get_args(
302 DBUS_TYPE_UINT32, &pid,
304 return bus_send_error_reply(connection, message, &error, -EINVAL);
306 r = manager_get_machine_by_pid(m, pid, &machine);
308 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
310 reply = dbus_message_new_method_return(message);
314 p = machine_bus_path(machine);
318 b = dbus_message_append_args(
320 DBUS_TYPE_OBJECT_PATH, &p,
327 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
330 DBusMessageIter iter, sub;
332 reply = dbus_message_new_method_return(message);
336 dbus_message_iter_init_append(reply, &iter);
338 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
341 HASHMAP_FOREACH(machine, m->machines, i) {
342 _cleanup_free_ char *p = NULL;
343 DBusMessageIter sub2;
346 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
349 p = machine_bus_path(machine);
353 class = strempty(machine_class_to_string(machine->class));
355 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
356 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
357 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
358 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
363 if (!dbus_message_iter_close_container(&sub, &sub2))
367 if (!dbus_message_iter_close_container(&iter, &sub))
370 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
372 r = bus_manager_create_machine(m, message);
374 return bus_send_error_reply(connection, message, NULL, r);
376 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
383 if (!dbus_message_get_args(
386 DBUS_TYPE_STRING, &name,
387 DBUS_TYPE_STRING, &swho,
388 DBUS_TYPE_INT32, &signo,
390 return bus_send_error_reply(connection, message, &error, -EINVAL);
395 who = kill_who_from_string(swho);
397 return bus_send_error_reply(connection, message, &error, -EINVAL);
400 if (signo <= 0 || signo >= _NSIG)
401 return bus_send_error_reply(connection, message, &error, -EINVAL);
403 machine = hashmap_get(m->machines, name);
405 return bus_send_error_reply(connection, message, &error, -ENOENT);
407 r = machine_kill(machine, who, signo);
409 return bus_send_error_reply(connection, message, NULL, r);
411 reply = dbus_message_new_method_return(message);
415 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
419 if (!dbus_message_get_args(
422 DBUS_TYPE_STRING, &name,
424 return bus_send_error_reply(connection, message, &error, -EINVAL);
426 machine = hashmap_get(m->machines, name);
428 return bus_send_error_reply(connection, message, &error, -ENOENT);
430 r = machine_stop(machine);
432 return bus_send_error_reply(connection, message, NULL, r);
434 reply = dbus_message_new_method_return(message);
438 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
439 char *introspection = NULL;
446 reply = dbus_message_new_method_return(message);
450 /* We roll our own introspection code here, instead of
451 * relying on bus_default_message_handler() because we
452 * need to generate our introspection string
455 f = open_memstream(&introspection, &size);
459 fputs(INTROSPECTION_BEGIN, f);
461 HASHMAP_FOREACH(machine, m->machines, i) {
462 p = bus_path_escape(machine->name);
465 fprintf(f, "<node name=\"machine/%s\"/>", p);
470 fputs(INTROSPECTION_END, f);
483 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
490 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
493 if (!bus_maybe_send_reply(connection, message, reply))
497 return DBUS_HANDLER_RESULT_HANDLED;
500 dbus_error_free(&error);
502 return DBUS_HANDLER_RESULT_NEED_MEMORY;
505 const DBusObjectPathVTable bus_manager_vtable = {
506 .message_function = manager_message_handler
509 DBusHandlerResult bus_message_filter(
510 DBusConnection *connection,
511 DBusMessage *message,
514 Manager *m = userdata;
521 dbus_error_init(&error);
523 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)));
525 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
526 const char *path, *result, *unit;
530 if (!dbus_message_get_args(message, &error,
531 DBUS_TYPE_UINT32, &id,
532 DBUS_TYPE_OBJECT_PATH, &path,
533 DBUS_TYPE_STRING, &unit,
534 DBUS_TYPE_STRING, &result,
535 DBUS_TYPE_INVALID)) {
536 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
540 mm = hashmap_get(m->machine_units, unit);
542 if (streq_ptr(path, mm->scope_job)) {
544 mm->scope_job = NULL;
547 if (streq(result, "done"))
548 machine_send_create_reply(mm, NULL);
550 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
551 machine_send_create_reply(mm, &error);
557 machine_add_to_gc_queue(mm);
560 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
562 _cleanup_free_ char *unit = NULL;
565 path = dbus_message_get_path(message);
569 unit_name_from_dbus_path(path, &unit);
573 mm = hashmap_get(m->machine_units, unit);
575 machine_add_to_gc_queue(mm);
578 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
579 const char *path, *unit;
582 if (!dbus_message_get_args(message, &error,
583 DBUS_TYPE_STRING, &unit,
584 DBUS_TYPE_OBJECT_PATH, &path,
585 DBUS_TYPE_INVALID)) {
586 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
590 mm = hashmap_get(m->machine_units, unit);
592 machine_add_to_gc_queue(mm);
594 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
597 if (!dbus_message_get_args(message, &error,
598 DBUS_TYPE_BOOLEAN, &b,
599 DBUS_TYPE_INVALID)) {
600 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
604 /* systemd finished reloading, let's recheck all our machines */
609 log_debug("System manager has been reloaded, rechecking machines...");
611 HASHMAP_FOREACH(mm, m->machines, i)
612 machine_add_to_gc_queue(mm);
617 dbus_error_free(&error);
619 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
622 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src);
624 static int copy_one_field(DBusMessageIter *dest, DBusMessageIter *src) {
627 type = dbus_message_iter_get_arg_type(src);
631 case DBUS_TYPE_STRUCT: {
632 DBusMessageIter dest_sub, src_sub;
634 dbus_message_iter_recurse(src, &src_sub);
636 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_STRUCT, NULL, &dest_sub))
639 r = copy_many_fields(&dest_sub, &src_sub);
643 if (!dbus_message_iter_close_container(dest, &dest_sub))
649 case DBUS_TYPE_ARRAY: {
650 DBusMessageIter dest_sub, src_sub;
652 dbus_message_iter_recurse(src, &src_sub);
654 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_ARRAY, dbus_message_iter_get_signature(&src_sub), &dest_sub))
657 r = copy_many_fields(&dest_sub, &src_sub);
661 if (!dbus_message_iter_close_container(dest, &dest_sub))
667 case DBUS_TYPE_VARIANT: {
668 DBusMessageIter dest_sub, src_sub;
670 dbus_message_iter_recurse(src, &src_sub);
672 if (!dbus_message_iter_open_container(dest, DBUS_TYPE_VARIANT, dbus_message_iter_get_signature(&src_sub), &dest_sub))
675 r = copy_one_field(&dest_sub, &src_sub);
679 if (!dbus_message_iter_close_container(dest, &dest_sub))
685 case DBUS_TYPE_STRING:
686 case DBUS_TYPE_OBJECT_PATH:
688 case DBUS_TYPE_BOOLEAN:
689 case DBUS_TYPE_UINT16:
690 case DBUS_TYPE_INT16:
691 case DBUS_TYPE_UINT32:
692 case DBUS_TYPE_INT32:
693 case DBUS_TYPE_UINT64:
694 case DBUS_TYPE_INT64:
695 case DBUS_TYPE_DOUBLE:
696 case DBUS_TYPE_SIGNATURE: {
699 dbus_message_iter_get_basic(src, &p);
700 dbus_message_iter_append_basic(dest, type, &p);
709 static int copy_many_fields(DBusMessageIter *dest, DBusMessageIter *src) {
715 while (dbus_message_iter_get_arg_type(src) != DBUS_TYPE_INVALID) {
717 r = copy_one_field(dest, src);
721 dbus_message_iter_next(src);
727 int manager_start_scope(
732 const char *description,
733 DBusMessageIter *more_properties,
737 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
738 DBusMessageIter iter, sub, sub2, sub3, sub4;
739 const char *timeout_stop_property = "TimeoutStopUSec";
740 const char *pids_property = "PIDs";
741 uint64_t timeout = 500 * USEC_PER_MSEC;
742 const char *fail = "fail";
753 m = dbus_message_new_method_call(
754 "org.freedesktop.systemd1",
755 "/org/freedesktop/systemd1",
756 "org.freedesktop.systemd1.Manager",
757 "StartTransientUnit");
761 dbus_message_iter_init_append(m, &iter);
763 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
764 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
765 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
768 if (!isempty(slice)) {
769 const char *slice_property = "Slice";
771 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
772 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
773 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
774 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
775 !dbus_message_iter_close_container(&sub2, &sub3) ||
776 !dbus_message_iter_close_container(&sub, &sub2))
780 if (!isempty(description)) {
781 const char *description_property = "Description";
783 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
784 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
785 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
786 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
787 !dbus_message_iter_close_container(&sub2, &sub3) ||
788 !dbus_message_iter_close_container(&sub, &sub2))
792 /* cgroup empty notification is not available in containers
793 * currently. To make this less problematic, let's shorten the
794 * stop timeout for sessions, so that we don't wait
797 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
798 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
799 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
800 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
801 !dbus_message_iter_close_container(&sub2, &sub3) ||
802 !dbus_message_iter_close_container(&sub, &sub2))
806 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
807 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
808 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
809 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
810 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
811 !dbus_message_iter_close_container(&sub3, &sub4) ||
812 !dbus_message_iter_close_container(&sub2, &sub3) ||
813 !dbus_message_iter_close_container(&sub, &sub2))
816 if (more_properties) {
817 r = copy_many_fields(&sub, more_properties);
822 if (!dbus_message_iter_close_container(&iter, &sub))
825 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
833 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
846 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
847 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
848 const char *fail = "fail";
854 r = bus_method_call_with_reply(
856 "org.freedesktop.systemd1",
857 "/org/freedesktop/systemd1",
858 "org.freedesktop.systemd1.Manager",
862 DBUS_TYPE_STRING, &unit,
863 DBUS_TYPE_STRING, &fail,
866 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
867 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
872 dbus_error_free(error);
876 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
884 if (!dbus_message_get_args(reply, error,
885 DBUS_TYPE_OBJECT_PATH, &j,
886 DBUS_TYPE_INVALID)) {
887 log_error("Failed to parse reply.");
901 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
902 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
909 w = who == KILL_LEADER ? "process" : "cgroup";
910 assert_cc(sizeof(signo) == sizeof(int32_t));
912 r = bus_method_call_with_reply(
914 "org.freedesktop.systemd1",
915 "/org/freedesktop/systemd1",
916 "org.freedesktop.systemd1.Manager",
920 DBUS_TYPE_STRING, &unit,
921 DBUS_TYPE_STRING, &w,
922 DBUS_TYPE_INT32, &signo,
925 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
932 int manager_unit_is_active(Manager *manager, const char *unit) {
934 const char *interface = "org.freedesktop.systemd1.Unit";
935 const char *property = "ActiveState";
936 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
937 _cleanup_free_ char *path = NULL;
938 DBusMessageIter iter, sub;
946 dbus_error_init(&error);
948 path = unit_dbus_path_from_name(unit);
952 r = bus_method_call_with_reply(
954 "org.freedesktop.systemd1",
956 "org.freedesktop.DBus.Properties",
960 DBUS_TYPE_STRING, &interface,
961 DBUS_TYPE_STRING, &property,
964 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
965 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
966 dbus_error_free(&error);
970 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
971 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
972 dbus_error_free(&error);
976 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
977 dbus_error_free(&error);
981 if (!dbus_message_iter_init(reply, &iter) ||
982 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
983 log_error("Failed to parse reply.");
987 dbus_message_iter_recurse(&iter, &sub);
988 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
989 log_error("Failed to parse reply.");
993 dbus_message_iter_get_basic(&sub, &state);
995 return !streq(state, "inactive") && !streq(state, "failed");