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/>.
26 #include <sys/capability.h>
29 #include "sd-messages.h"
32 #include "path-util.h"
34 #include "fileio-label.h"
37 #include "unit-name.h"
39 #include "bus-errors.h"
40 #include "time-util.h"
41 #include "cgroup-util.h"
44 static bool valid_machine_name(const char *p) {
47 if (!filename_is_safe(p))
50 if (!ascii_is_valid(p))
61 static int method_get_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
62 _cleanup_free_ char *p = NULL;
63 Manager *m = userdata;
72 r = sd_bus_message_read(message, "s", &name);
76 machine = hashmap_get(m->machines, name);
78 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
80 p = machine_bus_path(machine);
84 return sd_bus_reply_method_return(message, "o", p);
87 static int method_get_machine_by_pid(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
88 _cleanup_free_ char *p = NULL;
89 Manager *m = userdata;
90 Machine *machine = NULL;
98 assert_cc(sizeof(pid_t) == sizeof(uint32_t));
100 r = sd_bus_message_read(message, "u", &pid);
105 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
107 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
111 r = sd_bus_creds_get_pid(creds, &pid);
116 r = manager_get_machine_by_pid(m, pid, &machine);
120 return sd_bus_error_setf(error, BUS_ERROR_NO_MACHINE_FOR_PID, "PID "PID_FMT" does not belong to any known machine", pid);
122 p = machine_bus_path(machine);
126 return sd_bus_reply_method_return(message, "o", p);
129 static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
130 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
131 Manager *m = userdata;
140 r = sd_bus_message_new_method_return(message, &reply);
142 return sd_bus_error_set_errno(error, r);
144 r = sd_bus_message_open_container(reply, 'a', "(ssso)");
146 return sd_bus_error_set_errno(error, r);
148 HASHMAP_FOREACH(machine, m->machines, i) {
149 _cleanup_free_ char *p = NULL;
151 p = machine_bus_path(machine);
155 r = sd_bus_message_append(reply, "(ssso)",
157 strempty(machine_class_to_string(machine->class)),
161 return sd_bus_error_set_errno(error, r);
164 r = sd_bus_message_close_container(reply);
166 return sd_bus_error_set_errno(error, r);
168 return sd_bus_send(bus, reply, NULL);
171 static int method_create_or_register_machine(Manager *manager, sd_bus_message *message, Machine **_m, sd_bus_error *error) {
172 const char *name, *service, *class, *root_directory;
185 r = sd_bus_message_read(message, "s", &name);
188 if (!valid_machine_name(name))
189 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
191 r = sd_bus_message_read_array(message, 'y', &v, &n);
199 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
201 r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
206 c = _MACHINE_CLASS_INVALID;
208 c = machine_class_from_string(class);
210 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
214 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
216 if (!isempty(root_directory) && !path_is_absolute(root_directory))
217 return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
220 _cleanup_bus_creds_unref_ sd_bus_creds *creds = NULL;
222 r = sd_bus_query_sender_creds(message, SD_BUS_CREDS_PID, &creds);
226 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
228 r = sd_bus_creds_get_pid(creds, (pid_t*) &leader);
233 if (hashmap_get(manager->machines, name))
234 return sd_bus_error_setf(error, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
236 r = manager_add_machine(manager, name, &m);
244 if (!isempty(service)) {
245 m->service = strdup(service);
252 if (!isempty(root_directory)) {
253 m->root_directory = strdup(root_directory);
254 if (!m->root_directory) {
265 machine_add_to_gc_queue(m);
269 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
270 Manager *manager = userdata;
274 r = method_create_or_register_machine(manager, message, &m, error);
278 r = sd_bus_message_enter_container(message, 'a', "(sv)");
282 r = machine_start(m, message, error);
286 m->create_message = sd_bus_message_ref(message);
290 machine_add_to_gc_queue(m);
294 static int method_register_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
295 Manager *manager = userdata;
296 _cleanup_free_ char *p = NULL;
300 r = method_create_or_register_machine(manager, message, &m, error);
304 r = cg_pid_get_unit(m->leader, &m->unit);
306 r = sd_bus_error_set_errnof(error, r, "Failed to determine unit of process "PID_FMT" : %s", m->leader, strerror(-r));
310 r = machine_start(m, NULL, error);
314 p = machine_bus_path(m);
320 return sd_bus_reply_method_return(message, "o", p);
323 machine_add_to_gc_queue(m);
327 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
328 Manager *m = userdata;
337 r = sd_bus_message_read(message, "s", &name);
339 return sd_bus_error_set_errno(error, r);
341 machine = hashmap_get(m->machines, name);
343 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
345 return bus_machine_method_terminate(bus, message, machine, error);
348 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
349 Manager *m = userdata;
358 r = sd_bus_message_read(message, "s", &name);
360 return sd_bus_error_set_errno(error, r);
362 machine = hashmap_get(m->machines, name);
364 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
366 return bus_machine_method_kill(bus, message, machine, error);
369 static int method_get_machine_addresses(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
370 Manager *m = userdata;
379 r = sd_bus_message_read(message, "s", &name);
381 return sd_bus_error_set_errno(error, r);
383 machine = hashmap_get(m->machines, name);
385 return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
387 return bus_machine_method_get_addresses(bus, message, machine, error);
390 const sd_bus_vtable manager_vtable[] = {
391 SD_BUS_VTABLE_START(0),
392 SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, SD_BUS_VTABLE_UNPRIVILEGED),
393 SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, SD_BUS_VTABLE_UNPRIVILEGED),
394 SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, SD_BUS_VTABLE_UNPRIVILEGED),
395 SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
396 SD_BUS_METHOD("RegisterMachine", "sayssus", "o", method_register_machine, 0),
397 SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
398 SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, SD_BUS_VTABLE_CAPABILITY(CAP_KILL)),
399 SD_BUS_METHOD("GetMachineAddresses", "s", "a(yay)", method_get_machine_addresses, SD_BUS_VTABLE_UNPRIVILEGED),
400 SD_BUS_SIGNAL("MachineNew", "so", 0),
401 SD_BUS_SIGNAL("MachineRemoved", "so", 0),
405 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
406 const char *path, *result, *unit;
407 Manager *m = userdata;
416 r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
418 bus_log_parse_error(r);
422 machine = hashmap_get(m->machine_units, unit);
426 if (streq_ptr(path, machine->scope_job)) {
427 free(machine->scope_job);
428 machine->scope_job = NULL;
430 if (machine->started) {
431 if (streq(result, "done"))
432 machine_send_create_reply(machine, NULL);
434 _cleanup_bus_error_free_ sd_bus_error e = SD_BUS_ERROR_NULL;
436 sd_bus_error_setf(&e, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
438 machine_send_create_reply(machine, &e);
441 machine_save(machine);
444 machine_add_to_gc_queue(machine);
448 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
449 _cleanup_free_ char *unit = NULL;
450 Manager *m = userdata;
459 path = sd_bus_message_get_path(message);
463 r = unit_name_from_dbus_path(path, &unit);
467 machine = hashmap_get(m->machine_units, unit);
469 machine_add_to_gc_queue(machine);
474 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
475 const char *path, *unit;
476 Manager *m = userdata;
484 r = sd_bus_message_read(message, "so", &unit, &path);
486 bus_log_parse_error(r);
490 machine = hashmap_get(m->machine_units, unit);
492 machine_add_to_gc_queue(machine);
497 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata, sd_bus_error *error) {
498 Manager *m = userdata;
505 r = sd_bus_message_read(message, "b", &b);
507 bus_log_parse_error(r);
513 /* systemd finished reloading, let's recheck all our machines */
514 log_debug("System manager has been reloaded, rechecking machines...");
516 HASHMAP_FOREACH(machine, m->machines, i)
517 machine_add_to_gc_queue(machine);
522 int manager_start_scope(
527 const char *description,
528 sd_bus_message *more_properties,
532 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
539 r = sd_bus_message_new_method_call(
542 "org.freedesktop.systemd1",
543 "/org/freedesktop/systemd1",
544 "org.freedesktop.systemd1.Manager",
545 "StartTransientUnit");
549 r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
553 r = sd_bus_message_open_container(m, 'a', "(sv)");
557 if (!isempty(slice)) {
558 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
563 if (!isempty(description)) {
564 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
569 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
573 if (more_properties) {
574 r = sd_bus_message_copy(m, more_properties, true);
579 r = sd_bus_message_close_container(m);
583 r = sd_bus_message_append(m, "a(sa(sv))", 0);
587 r = sd_bus_call(manager->bus, m, 0, error, &reply);
595 r = sd_bus_message_read(reply, "o", &j);
609 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
610 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
616 r = sd_bus_call_method(
618 "org.freedesktop.systemd1",
619 "/org/freedesktop/systemd1",
620 "org.freedesktop.systemd1.Manager",
626 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
627 sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
632 sd_bus_error_free(error);
643 r = sd_bus_message_read(reply, "o", &j);
657 int manager_kill_unit(Manager *manager, const char *unit, int signo, sd_bus_error *error) {
661 return sd_bus_call_method(
663 "org.freedesktop.systemd1",
664 "/org/freedesktop/systemd1",
665 "org.freedesktop.systemd1.Manager",
669 "ssi", unit, "all", signo);
672 int manager_unit_is_active(Manager *manager, const char *unit) {
673 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
674 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
675 _cleanup_free_ char *path = NULL;
682 path = unit_dbus_path_from_name(unit);
686 r = sd_bus_get_property(
688 "org.freedesktop.systemd1",
690 "org.freedesktop.systemd1.Unit",
696 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
697 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
700 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
701 sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
707 r = sd_bus_message_read(reply, "s", &state);
711 return !streq(state, "inactive") && !streq(state, "failed");
714 int manager_job_is_active(Manager *manager, const char *path) {
715 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
716 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
722 r = sd_bus_get_property(
724 "org.freedesktop.systemd1",
726 "org.freedesktop.systemd1.Job",
732 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
733 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
736 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
742 /* We don't actually care about the state really. The fact
743 * that we could read the job state is enough for us */
748 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
749 _cleanup_free_ char *unit = NULL;
757 r = cg_pid_get_unit(pid, &unit);
759 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
761 mm = hashmap_get(m->machine_units, unit);
770 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
776 machine = hashmap_get(m->machines, name);
778 machine = machine_new(m, name);