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 (hashmap_get(manager->machines, name))
192 leader = bus_get_unix_process_id(manager->bus, dbus_message_get_sender(message), NULL);
197 r = manager_add_machine(manager, name, &m);
205 if (!isempty(service)) {
206 m->service = strdup(service);
213 if (!isempty(root_directory)) {
214 m->root_directory = strdup(root_directory);
215 if (!m->root_directory) {
221 r = machine_start(m);
225 m->create_message = dbus_message_ref(message);
231 machine_add_to_gc_queue(m);
236 static DBusHandlerResult manager_message_handler(
237 DBusConnection *connection,
238 DBusMessage *message,
241 Manager *m = userdata;
244 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
251 dbus_error_init(&error);
253 if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachine")) {
259 if (!dbus_message_get_args(
262 DBUS_TYPE_STRING, &name,
264 return bus_send_error_reply(connection, message, &error, -EINVAL);
266 machine = hashmap_get(m->machines, name);
268 return bus_send_error_reply(connection, message, &error, -ENOENT);
270 reply = dbus_message_new_method_return(message);
274 p = machine_bus_path(machine);
278 b = dbus_message_append_args(
280 DBUS_TYPE_OBJECT_PATH, &p,
287 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "GetMachineByPID")) {
293 if (!dbus_message_get_args(
296 DBUS_TYPE_UINT32, &pid,
298 return bus_send_error_reply(connection, message, &error, -EINVAL);
300 r = manager_get_machine_by_pid(m, pid, &machine);
302 return bus_send_error_reply(connection, message, NULL, r < 0 ? r : -ENOENT);
304 reply = dbus_message_new_method_return(message);
308 p = machine_bus_path(machine);
312 b = dbus_message_append_args(
314 DBUS_TYPE_OBJECT_PATH, &p,
321 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "ListMachines")) {
324 DBusMessageIter iter, sub;
326 reply = dbus_message_new_method_return(message);
330 dbus_message_iter_init_append(reply, &iter);
332 if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(ssso)", &sub))
335 HASHMAP_FOREACH(machine, m->machines, i) {
336 _cleanup_free_ char *p = NULL;
337 DBusMessageIter sub2;
340 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2))
343 p = machine_bus_path(machine);
347 class = strempty(machine_class_to_string(machine->class));
349 if (!dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->name) ||
350 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &class) ||
351 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &machine->service) ||
352 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_OBJECT_PATH, &p)) {
357 if (!dbus_message_iter_close_container(&sub, &sub2))
361 if (!dbus_message_iter_close_container(&iter, &sub))
364 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "CreateMachine")) {
366 r = bus_manager_create_machine(m, message);
368 return bus_send_error_reply(connection, message, NULL, r);
370 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "KillMachine")) {
377 if (!dbus_message_get_args(
380 DBUS_TYPE_STRING, &name,
381 DBUS_TYPE_STRING, &swho,
382 DBUS_TYPE_INT32, &signo,
384 return bus_send_error_reply(connection, message, &error, -EINVAL);
389 who = kill_who_from_string(swho);
391 return bus_send_error_reply(connection, message, &error, -EINVAL);
394 if (signo <= 0 || signo >= _NSIG)
395 return bus_send_error_reply(connection, message, &error, -EINVAL);
397 machine = hashmap_get(m->machines, name);
399 return bus_send_error_reply(connection, message, &error, -ENOENT);
401 r = machine_kill(machine, who, signo);
403 return bus_send_error_reply(connection, message, NULL, r);
405 reply = dbus_message_new_method_return(message);
409 } else if (dbus_message_is_method_call(message, "org.freedesktop.machine1.Manager", "TerminateMachine")) {
413 if (!dbus_message_get_args(
416 DBUS_TYPE_STRING, &name,
418 return bus_send_error_reply(connection, message, &error, -EINVAL);
420 machine = hashmap_get(m->machines, name);
422 return bus_send_error_reply(connection, message, &error, -ENOENT);
424 r = machine_stop(machine);
426 return bus_send_error_reply(connection, message, NULL, r);
428 reply = dbus_message_new_method_return(message);
432 } else if (dbus_message_is_method_call(message, "org.freedesktop.DBus.Introspectable", "Introspect")) {
433 char *introspection = NULL;
440 reply = dbus_message_new_method_return(message);
444 /* We roll our own introspection code here, instead of
445 * relying on bus_default_message_handler() because we
446 * need to generate our introspection string
449 f = open_memstream(&introspection, &size);
453 fputs(INTROSPECTION_BEGIN, f);
455 HASHMAP_FOREACH(machine, m->machines, i) {
456 p = bus_path_escape(machine->name);
459 fprintf(f, "<node name=\"machine/%s\"/>", p);
464 fputs(INTROSPECTION_END, f);
477 if (!dbus_message_append_args(reply, DBUS_TYPE_STRING, &introspection, DBUS_TYPE_INVALID)) {
484 return bus_default_message_handler(connection, message, NULL, INTERFACES_LIST, NULL);
487 if (!bus_maybe_send_reply(connection, message, reply))
491 return DBUS_HANDLER_RESULT_HANDLED;
494 dbus_error_free(&error);
496 return DBUS_HANDLER_RESULT_NEED_MEMORY;
499 const DBusObjectPathVTable bus_manager_vtable = {
500 .message_function = manager_message_handler
503 DBusHandlerResult bus_message_filter(
504 DBusConnection *connection,
505 DBusMessage *message,
508 Manager *m = userdata;
515 dbus_error_init(&error);
517 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)));
519 if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "JobRemoved")) {
520 const char *path, *result, *unit;
524 if (!dbus_message_get_args(message, &error,
525 DBUS_TYPE_UINT32, &id,
526 DBUS_TYPE_OBJECT_PATH, &path,
527 DBUS_TYPE_STRING, &unit,
528 DBUS_TYPE_STRING, &result,
529 DBUS_TYPE_INVALID)) {
530 log_error("Failed to parse JobRemoved message: %s", bus_error_message(&error));
535 mm = hashmap_get(m->machine_units, unit);
537 if (streq_ptr(path, mm->scope_job)) {
539 mm->scope_job = NULL;
543 if (streq(result, "done"))
544 machine_send_create_reply(mm, NULL);
546 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
547 machine_send_create_reply(mm, &error);
552 machine_add_to_gc_queue(mm);
555 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
557 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
558 _cleanup_free_ char *unit = NULL;
561 path = dbus_message_get_path(message);
565 unit_name_from_dbus_path(path, &unit);
569 mm = hashmap_get(m->machine_units, unit);
571 machine_add_to_gc_queue(mm);
576 dbus_error_free(&error);
578 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
581 int manager_start_scope(
586 const char *description,
590 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
591 DBusMessageIter iter, sub, sub2, sub3, sub4;
592 const char *timeout_stop_property = "TimeoutStopUSec";
593 const char *pids_property = "PIDs";
594 uint64_t timeout = 500 * USEC_PER_MSEC;
595 const char *fail = "fail";
605 m = dbus_message_new_method_call(
606 "org.freedesktop.systemd1",
607 "/org/freedesktop/systemd1",
608 "org.freedesktop.systemd1.Manager",
609 "StartTransientUnit");
613 dbus_message_iter_init_append(m, &iter);
615 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
616 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
617 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
620 if (!isempty(slice)) {
621 const char *slice_property = "Slice";
623 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
624 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
625 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
626 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
627 !dbus_message_iter_close_container(&sub2, &sub3) ||
628 !dbus_message_iter_close_container(&sub, &sub2))
632 if (!isempty(description)) {
633 const char *description_property = "Description";
635 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
636 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
637 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
638 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
639 !dbus_message_iter_close_container(&sub2, &sub3) ||
640 !dbus_message_iter_close_container(&sub, &sub2))
644 /* cgroup empty notification is not available in containers
645 * currently. To make this less problematic, let's shorten the
646 * stop timeout for sessions, so that we don't wait
649 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
650 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
651 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
652 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
653 !dbus_message_iter_close_container(&sub2, &sub3) ||
654 !dbus_message_iter_close_container(&sub, &sub2))
658 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
659 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
660 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
661 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
662 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
663 !dbus_message_iter_close_container(&sub3, &sub4) ||
664 !dbus_message_iter_close_container(&sub2, &sub3) ||
665 !dbus_message_iter_close_container(&sub, &sub2) ||
666 !dbus_message_iter_close_container(&iter, &sub))
669 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
677 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
690 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
691 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
692 const char *fail = "fail";
698 r = bus_method_call_with_reply(
700 "org.freedesktop.systemd1",
701 "/org/freedesktop/systemd1",
702 "org.freedesktop.systemd1.Manager",
706 DBUS_TYPE_STRING, &unit,
707 DBUS_TYPE_STRING, &fail,
710 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
718 if (!dbus_message_get_args(reply, error,
719 DBUS_TYPE_OBJECT_PATH, &j,
720 DBUS_TYPE_INVALID)) {
721 log_error("Failed to parse reply.");
735 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
736 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
743 w = who == KILL_LEADER ? "process" : "cgroup";
744 assert_cc(sizeof(signo) == sizeof(int32_t));
746 r = bus_method_call_with_reply(
748 "org.freedesktop.systemd1",
749 "/org/freedesktop/systemd1",
750 "org.freedesktop.systemd1.Manager",
754 DBUS_TYPE_STRING, &unit,
755 DBUS_TYPE_STRING, &w,
756 DBUS_TYPE_INT32, &signo,
759 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
766 int manager_unit_is_active(Manager *manager, const char *unit) {
768 const char *interface = "org.freedesktop.systemd1.Unit";
769 const char *property = "ActiveState";
770 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
771 _cleanup_free_ char *path = NULL;
772 DBusMessageIter iter, sub;
780 dbus_error_init(&error);
782 path = unit_dbus_path_from_name(unit);
786 r = bus_method_call_with_reply(
788 "org.freedesktop.systemd1",
790 "org.freedesktop.DBus.Properties",
794 DBUS_TYPE_STRING, &interface,
795 DBUS_TYPE_STRING, &property,
799 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
800 dbus_error_free(&error);
804 if (!dbus_message_iter_init(reply, &iter) ||
805 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
806 log_error("Failed to parse reply.");
810 dbus_message_iter_recurse(&iter, &sub);
811 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
812 log_error("Failed to parse reply.");
816 dbus_message_iter_get_basic(&sub, &state);
818 return !streq(state, "inactive") && !streq(state, "failed");