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));
534 mm = hashmap_get(m->machine_units, unit);
536 if (streq_ptr(path, mm->scope_job)) {
538 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);
573 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "UnitRemoved")) {
574 const char *path, *unit;
577 if (!dbus_message_get_args(message, &error,
578 DBUS_TYPE_STRING, &unit,
579 DBUS_TYPE_OBJECT_PATH, &path,
580 DBUS_TYPE_INVALID)) {
581 log_error("Failed to parse UnitRemoved message: %s", bus_error_message(&error));
585 mm = hashmap_get(m->machine_units, unit);
587 machine_add_to_gc_queue(mm);
589 } else if (dbus_message_is_signal(message, "org.freedesktop.systemd1.Manager", "Reloading")) {
592 if (!dbus_message_get_args(message, &error,
593 DBUS_TYPE_BOOLEAN, &b,
594 DBUS_TYPE_INVALID)) {
595 log_error("Failed to parse Reloading message: %s", bus_error_message(&error));
599 /* systemd finished reloading, let's recheck all our machines */
604 log_debug("System manager has been reloaded, rechecking machines...");
606 HASHMAP_FOREACH(mm, m->machines, i)
607 machine_add_to_gc_queue(mm);
612 dbus_error_free(&error);
614 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
617 int manager_start_scope(
622 const char *description,
626 _cleanup_dbus_message_unref_ DBusMessage *m = NULL, *reply = NULL;
627 DBusMessageIter iter, sub, sub2, sub3, sub4;
628 const char *timeout_stop_property = "TimeoutStopUSec";
629 const char *pids_property = "PIDs";
630 uint64_t timeout = 500 * USEC_PER_MSEC;
631 const char *fail = "fail";
641 m = dbus_message_new_method_call(
642 "org.freedesktop.systemd1",
643 "/org/freedesktop/systemd1",
644 "org.freedesktop.systemd1.Manager",
645 "StartTransientUnit");
649 dbus_message_iter_init_append(m, &iter);
651 if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &scope) ||
652 !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &fail) ||
653 !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(sv)", &sub))
656 if (!isempty(slice)) {
657 const char *slice_property = "Slice";
659 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
660 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &slice_property) ||
661 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
662 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &slice) ||
663 !dbus_message_iter_close_container(&sub2, &sub3) ||
664 !dbus_message_iter_close_container(&sub, &sub2))
668 if (!isempty(description)) {
669 const char *description_property = "Description";
671 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
672 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &description_property) ||
673 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "s", &sub3) ||
674 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_STRING, &description) ||
675 !dbus_message_iter_close_container(&sub2, &sub3) ||
676 !dbus_message_iter_close_container(&sub, &sub2))
680 /* cgroup empty notification is not available in containers
681 * currently. To make this less problematic, let's shorten the
682 * stop timeout for sessions, so that we don't wait
685 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
686 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &timeout_stop_property) ||
687 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "t", &sub3) ||
688 !dbus_message_iter_append_basic(&sub3, DBUS_TYPE_UINT64, &timeout) ||
689 !dbus_message_iter_close_container(&sub2, &sub3) ||
690 !dbus_message_iter_close_container(&sub, &sub2))
694 if (!dbus_message_iter_open_container(&sub, DBUS_TYPE_STRUCT, NULL, &sub2) ||
695 !dbus_message_iter_append_basic(&sub2, DBUS_TYPE_STRING, &pids_property) ||
696 !dbus_message_iter_open_container(&sub2, DBUS_TYPE_VARIANT, "au", &sub3) ||
697 !dbus_message_iter_open_container(&sub3, DBUS_TYPE_ARRAY, "u", &sub4) ||
698 !dbus_message_iter_append_basic(&sub4, DBUS_TYPE_UINT32, &u) ||
699 !dbus_message_iter_close_container(&sub3, &sub4) ||
700 !dbus_message_iter_close_container(&sub2, &sub3) ||
701 !dbus_message_iter_close_container(&sub, &sub2) ||
702 !dbus_message_iter_close_container(&iter, &sub))
705 reply = dbus_connection_send_with_reply_and_block(manager->bus, m, -1, error);
713 if (!dbus_message_get_args(reply, error, DBUS_TYPE_OBJECT_PATH, &j, DBUS_TYPE_INVALID))
726 int manager_stop_unit(Manager *manager, const char *unit, DBusError *error, char **job) {
727 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
728 const char *fail = "fail";
734 r = bus_method_call_with_reply(
736 "org.freedesktop.systemd1",
737 "/org/freedesktop/systemd1",
738 "org.freedesktop.systemd1.Manager",
742 DBUS_TYPE_STRING, &unit,
743 DBUS_TYPE_STRING, &fail,
746 if (dbus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
747 dbus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
752 dbus_error_free(error);
756 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
764 if (!dbus_message_get_args(reply, error,
765 DBUS_TYPE_OBJECT_PATH, &j,
766 DBUS_TYPE_INVALID)) {
767 log_error("Failed to parse reply.");
781 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, DBusError *error) {
782 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
789 w = who == KILL_LEADER ? "process" : "cgroup";
790 assert_cc(sizeof(signo) == sizeof(int32_t));
792 r = bus_method_call_with_reply(
794 "org.freedesktop.systemd1",
795 "/org/freedesktop/systemd1",
796 "org.freedesktop.systemd1.Manager",
800 DBUS_TYPE_STRING, &unit,
801 DBUS_TYPE_STRING, &w,
802 DBUS_TYPE_INT32, &signo,
805 log_error("Failed to stop unit %s: %s", unit, bus_error(error, r));
812 int manager_unit_is_active(Manager *manager, const char *unit) {
814 const char *interface = "org.freedesktop.systemd1.Unit";
815 const char *property = "ActiveState";
816 _cleanup_dbus_message_unref_ DBusMessage *reply = NULL;
817 _cleanup_free_ char *path = NULL;
818 DBusMessageIter iter, sub;
826 dbus_error_init(&error);
828 path = unit_dbus_path_from_name(unit);
832 r = bus_method_call_with_reply(
834 "org.freedesktop.systemd1",
836 "org.freedesktop.DBus.Properties",
840 DBUS_TYPE_STRING, &interface,
841 DBUS_TYPE_STRING, &property,
844 if (dbus_error_has_name(&error, DBUS_ERROR_NO_REPLY) ||
845 dbus_error_has_name(&error, DBUS_ERROR_DISCONNECTED)) {
846 dbus_error_free(&error);
850 if (dbus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
851 dbus_error_has_name(&error, BUS_ERROR_LOAD_FAILED)) {
852 dbus_error_free(&error);
856 log_error("Failed to query ActiveState: %s", bus_error(&error, r));
857 dbus_error_free(&error);
861 if (!dbus_message_iter_init(reply, &iter) ||
862 dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) {
863 log_error("Failed to parse reply.");
867 dbus_message_iter_recurse(&iter, &sub);
868 if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
869 log_error("Failed to parse reply.");
873 dbus_message_iter_get_basic(&sub, &state);
875 return !streq(state, "inactive") && !streq(state, "failed");