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, *slice, *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, &slice);
184 if (!(isempty(slice) || (unit_name_is_valid(slice, false) && endswith(slice, ".slice"))) ||
185 !dbus_message_iter_next(&iter) ||
186 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
189 dbus_message_iter_get_basic(&iter, &root_directory);
191 if (!(isempty(root_directory) || path_is_absolute(root_directory)))
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);
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));
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);
557 machine_add_to_gc_queue(mm);
560 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
562 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
563 _cleanup_free_ char *unit = NULL;
566 path = dbus_message_get_path(message);
570 unit_name_from_dbus_path(path, &unit);
574 mm = hashmap_get(m->machine_units, unit);
576 machine_add_to_gc_queue(mm);
581 dbus_error_free(&error);
583 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
586 int manager_start_scope(
591 const char *description,
595 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
596 DBusMessageIter iter, sub, sub2, sub3, sub4;
597 const char *timeout_stop_property = "TimeoutStopUSec";
598 const char *pids_property = "PIDs";
599 uint64_t timeout = 500 * USEC_PER_MSEC;
600 const char *fail = "fail";
610 m = dbus_message_new_method_call(
611 "org.freedesktop.systemd1",
612 "/org/freedesktop/systemd1",
613 "org.freedesktop.systemd1.Manager",
614 "StartTransientUnit");
618 dbus_message_iter_init_append(m, &iter);
620 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
621 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
622 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
625 if (!isempty(slice)) {
626 const char *slice_property = "Slice";
628 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
629 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
630 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
631 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
632 !dbus_message_iter_close_container(&sub2, &sub3) ||
633 !dbus_message_iter_close_container(&sub, &sub2))
637 if (!isempty(description)) {
638 const char *description_property = "Description";
640 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
641 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
642 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
643 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
644 !dbus_message_iter_close_container(&sub2, &sub3) ||
645 !dbus_message_iter_close_container(&sub, &sub2))
649 /* cgroup empty notification is not available in containers
650 * currently. To make this less problematic, let's shorten the
651 * stop timeout for sessions, so that we don't wait
654 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
655 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
656 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
657 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
658 !dbus_message_iter_close_container(&sub2, &sub3) ||
659 !dbus_message_iter_close_container(&sub, &sub2))
663 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
664 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
665 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
666 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
667 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
668 !dbus_message_iter_close_container(&sub3, &sub4) ||
669 !dbus_message_iter_close_container(&sub2, &sub3) ||
670 !dbus_message_iter_close_container(&sub, &sub2) ||
671 !dbus_message_iter_close_container(&iter, &sub))
674 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
682 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
695 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
696 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
697 const char *fail = "fail";
703 r = bus_method_call_with_reply(
705 "org.freedesktop.systemd1",
706 "/org/freedesktop/systemd1",
707 "org.freedesktop.systemd1.Manager",
711 DBUS_TYPE_STRING, &unit,
712 DBUS_TYPE_STRING, &fail,
715 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
723 if (!dbus_message_get_args(reply, error,
724 DBUS_TYPE_OBJECT_PATH, &j,
725 DBUS_TYPE_INVALID)) {
726 log_error("Failed to parse reply.");
740 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
741 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
748 w = who == KILL_LEADER ? "process" : "cgroup";
749 assert_cc(sizeof(signo) == sizeof(int32_t));
751 r = bus_method_call_with_reply(
753 "org.freedesktop.systemd1",
754 "/org/freedesktop/systemd1",
755 "org.freedesktop.systemd1.Manager",
759 DBUS_TYPE_STRING, &unit,
760 DBUS_TYPE_STRING, &w,
761 DBUS_TYPE_INT32, &signo,
764 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
771 int manager_unit_is_active(Manager *manager, const char *unit) {
773 const char *interface = "org.freedesktop.systemd1.Unit";
774 const char *property = "ActiveState";
775 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
776 _cleanup_free_ char *path = NULL;
777 DBusMessageIter iter, sub;
785 dbus_error_init(&error);
787 path = unit_dbus_path_from_name(unit);
791 r = bus_method_call_with_reply(
793 "org.freedesktop.systemd1",
795 "org.freedesktop.DBus.Properties",
799 DBUS_TYPE_STRING, &interface,
800 DBUS_TYPE_STRING, &property,
804 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
805 dbus_error_free(&error);
809 if (!dbus_message_iter_init(reply, &iter) ||
810 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
811 log_error("Failed to parse reply.");
815 dbus_message_iter_recurse(&iter, &sub);
816 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
817 log_error("Failed to parse reply.");
821 dbus_message_iter_get_basic(&sub, &state);
823 return !streq(state, "inactive") && !streq(state, "failed");