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, &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;
542 if (streq(result, "done"))
543 machine_send_create_reply(mm, NULL);
545 dbus_set_error(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
546 machine_send_create_reply(mm, &error);
551 machine_add_to_gc_queue(mm);
554 } else if (dbus_message_is_signal(message, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
556 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
557 _cleanup_free_ char *unit = NULL;
560 path = dbus_message_get_path(message);
564 unit_name_from_dbus_path(path, &unit);
568 mm = hashmap_get(m->machine_units, unit);
570 machine_add_to_gc_queue(mm);
575 dbus_error_free(&error);
577 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
580 int manager_start_scope(
585 const char *description,
589 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
590 DBusMessageIter iter, sub, sub2, sub3, sub4;
591 const char *timeout_stop_property = "TimeoutStopUSec";
592 const char *pids_property = "PIDs";
593 uint64_t timeout = 500 * USEC_PER_MSEC;
594 const char *fail = "fail";
604 m = dbus_message_new_method_call(
605 "org.freedesktop.systemd1",
606 "/org/freedesktop/systemd1",
607 "org.freedesktop.systemd1.Manager",
608 "StartTransientUnit");
612 dbus_message_iter_init_append(m, &iter);
614 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
615 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
616 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
619 if (!isempty(slice)) {
620 const char *slice_property = "Slice";
622 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
623 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
624 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
625 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
626 !dbus_message_iter_close_container(&sub2, &sub3) ||
627 !dbus_message_iter_close_container(&sub, &sub2))
631 if (!isempty(description)) {
632 const char *description_property = "Description";
634 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
635 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
636 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
637 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
638 !dbus_message_iter_close_container(&sub2, &sub3) ||
639 !dbus_message_iter_close_container(&sub, &sub2))
643 /* cgroup empty notification is not available in containers
644 * currently. To make this less problematic, let's shorten the
645 * stop timeout for sessions, so that we don't wait
648 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
649 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
650 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
651 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
652 !dbus_message_iter_close_container(&sub2, &sub3) ||
653 !dbus_message_iter_close_container(&sub, &sub2))
657 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
658 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
659 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
660 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
661 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
662 !dbus_message_iter_close_container(&sub3, &sub4) ||
663 !dbus_message_iter_close_container(&sub2, &sub3) ||
664 !dbus_message_iter_close_container(&sub, &sub2) ||
665 !dbus_message_iter_close_container(&iter, &sub))
668 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
676 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
689 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
690 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
691 const char *fail = "fail";
697 r = bus_method_call_with_reply(
699 "org.freedesktop.systemd1",
700 "/org/freedesktop/systemd1",
701 "org.freedesktop.systemd1.Manager",
705 DBUS_TYPE_STRING, &unit,
706 DBUS_TYPE_STRING, &fail,
709 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
717 if (!dbus_message_get_args(reply, error,
718 DBUS_TYPE_OBJECT_PATH, &j,
719 DBUS_TYPE_INVALID)) {
720 log_error("Failed to parse reply.");
734 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
735 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
742 w = who == KILL_LEADER ? "process" : "cgroup";
743 assert_cc(sizeof(signo) == sizeof(int32_t));
745 r = bus_method_call_with_reply(
747 "org.freedesktop.systemd1",
748 "/org/freedesktop/systemd1",
749 "org.freedesktop.systemd1.Manager",
753 DBUS_TYPE_STRING, &unit,
754 DBUS_TYPE_STRING, &w,
755 DBUS_TYPE_INT32, &signo,
758 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
765 int manager_unit_is_active(Manager *manager, const char *unit) {
767 const char *interface = "org.freedesktop.systemd1.Unit";
768 const char *property = "ActiveState";
769 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
770 _cleanup_free_ char *path = NULL;
771 DBusMessageIter iter, sub;
779 dbus_error_init(&error);
781 path = unit_dbus_path_from_name(unit);
785 r = bus_method_call_with_reply(
787 "org.freedesktop.systemd1",
789 "org.freedesktop.DBus.Properties",
793 DBUS_TYPE_STRING, &interface,
794 DBUS_TYPE_STRING, &property,
798 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
799 dbus_error_free(&error);
803 if (!dbus_message_iter_init(reply, &iter) ||
804 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
805 log_error("Failed to parse reply.");
809 dbus_message_iter_recurse(&iter, &sub);
810 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
811 log_error("Failed to parse reply.");
815 dbus_message_iter_get_basic(&sub, &state);
817 return !streq(state, "inactive") && !streq(state, "failed");