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/>.
28 #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) {
62 _cleanup_free_ char *p = NULL;
63 Manager *m = userdata;
72 r = sd_bus_message_read(message, "s", &name);
74 return sd_bus_reply_method_errno(message, r, NULL);
76 machine = hashmap_get(m->machines, name);
78 return sd_bus_reply_method_errorf(message, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
80 p = machine_bus_path(machine);
82 return sd_bus_reply_method_errno(message, -ENOMEM, NULL);
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) {
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);
102 return sd_bus_reply_method_errno(message, r, NULL);
105 r = sd_bus_get_owner_pid(bus, sd_bus_message_get_sender(message), &pid);
107 return sd_bus_reply_method_errno(message, r, NULL);
110 r = manager_get_machine_by_pid(m, pid, &machine);
112 return sd_bus_reply_method_errno(message, r, NULL);
114 return sd_bus_reply_method_errorf(message, BUS_ERROR_NO_MACHINE_FOR_PID, "PID %lu does not belong to any known machine", (unsigned long) pid);
116 p = machine_bus_path(machine);
118 return sd_bus_reply_method_errno(message, -ENOMEM, NULL);
120 return sd_bus_reply_method_return(message, "o", p);
123 static int method_list_machines(sd_bus *bus, sd_bus_message *message, void *userdata) {
124 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
125 Manager *m = userdata;
134 r = sd_bus_message_new_method_return(message, &reply);
136 return sd_bus_reply_method_errno(message, r, NULL);
138 r = sd_bus_message_open_container(reply, 'a', "(ssso)");
140 return sd_bus_reply_method_errno(message, r, NULL);
142 HASHMAP_FOREACH(machine, m->machines, i) {
143 _cleanup_free_ char *p = NULL;
145 p = machine_bus_path(machine);
147 return sd_bus_reply_method_errno(message, -ENOMEM, NULL);
149 r = sd_bus_message_append(reply, "(ssso)",
151 strempty(machine_class_to_string(machine->class)),
155 return sd_bus_reply_method_errno(message, r, NULL);
158 r = sd_bus_message_close_container(reply);
160 return sd_bus_reply_method_errno(message, r, NULL);
162 return sd_bus_send(bus, reply, NULL);
165 static int method_create_machine(sd_bus *bus, sd_bus_message *message, void *userdata) {
166 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
167 const char *name, *service, *class, *root_directory;
168 Manager *manager = userdata;
181 r = sd_bus_message_read(message, "s", &name);
183 return sd_bus_reply_method_errno(message, r, NULL);
184 if (!valid_machine_name(name))
185 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine name");
187 r = sd_bus_message_read_array(message, 'y', &v, &n);
189 return sd_bus_reply_method_errno(message, r, NULL);
195 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine ID parameter");
197 r = sd_bus_message_read(message, "ssus", &service, &class, &leader, &root_directory);
199 return sd_bus_reply_method_errno(message, r, NULL);
202 c = _MACHINE_CLASS_INVALID;
204 c = machine_class_from_string(class);
206 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid machine class parameter");
210 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid leader PID");
212 if (!isempty(root_directory) && !path_is_absolute(root_directory))
213 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Root directory must be empty or an absolute path");
215 r = sd_bus_message_enter_container(message, 'a', "(sv)");
217 return sd_bus_reply_method_errno(message, r, NULL);
220 assert_cc(sizeof(uint32_t) == sizeof(pid_t));
222 r = sd_bus_get_owner_pid(bus, sd_bus_message_get_sender(message), (pid_t*) &leader);
224 return sd_bus_reply_method_errno(message, r, NULL);
227 if (hashmap_get(manager->machines, name))
228 return sd_bus_reply_method_errorf(message, BUS_ERROR_MACHINE_EXISTS, "Machine '%s' already exists", name);
230 r = manager_add_machine(manager, name, &m);
232 return sd_bus_reply_method_errno(message, r, NULL);
238 if (!isempty(service)) {
239 m->service = strdup(service);
241 r = sd_bus_reply_method_errno(message, -ENOMEM, NULL);
246 if (!isempty(root_directory)) {
247 m->root_directory = strdup(root_directory);
248 if (!m->root_directory) {
249 r = sd_bus_reply_method_errno(message, -ENOMEM, NULL);
254 r = machine_start(m, message, &error);
256 r = sd_bus_reply_method_errno(message, r, &error);
260 m->create_message = sd_bus_message_ref(message);
265 machine_add_to_gc_queue(m);
270 static int method_terminate_machine(sd_bus *bus, sd_bus_message *message, void *userdata) {
271 Manager *m = userdata;
280 r = sd_bus_message_read(message, "s", &name);
282 return sd_bus_reply_method_errno(message, r, NULL);
284 machine = hashmap_get(m->machines, name);
286 return sd_bus_reply_method_errorf(message, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
288 r = machine_stop(machine);
290 return sd_bus_reply_method_errno(message, r, NULL);
292 return sd_bus_reply_method_return(message, NULL);
295 static int method_kill_machine(sd_bus *bus, sd_bus_message *message, void *userdata) {
296 Manager *m = userdata;
308 r = sd_bus_message_read(message, "ssi", &name, &swho, &signo);
310 return sd_bus_reply_method_errno(message, r, NULL);
315 who = kill_who_from_string(swho);
317 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid kill parameter '%s'", swho);
320 if (signo <= 0 || signo >= _NSIG)
321 return sd_bus_reply_method_errorf(message, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal %i", signo);
323 machine = hashmap_get(m->machines, name);
325 return sd_bus_reply_method_errorf(message, BUS_ERROR_NO_SUCH_MACHINE, "No machine '%s' known", name);
327 r = machine_kill(machine, who, signo);
329 return sd_bus_reply_method_errno(message, r, NULL);
331 return sd_bus_reply_method_return(message, NULL);
334 const sd_bus_vtable manager_vtable[] = {
335 SD_BUS_VTABLE_START(0),
336 SD_BUS_METHOD("GetMachine", "s", "o", method_get_machine, 0),
337 SD_BUS_METHOD("GetMachineByPID", "u", "o", method_get_machine_by_pid, 0),
338 SD_BUS_METHOD("ListMachines", NULL, "a(ssso)", method_list_machines, 0),
339 SD_BUS_METHOD("CreateMachine", "sayssusa(sv)", "o", method_create_machine, 0),
340 SD_BUS_METHOD("KillMachine", "ssi", NULL, method_kill_machine, 0),
341 SD_BUS_METHOD("TerminateMachine", "s", NULL, method_terminate_machine, 0),
342 SD_BUS_SIGNAL("MachineNew", "so", 0),
343 SD_BUS_SIGNAL("MachineRemoved", "so", 0),
347 int match_job_removed(sd_bus *bus, sd_bus_message *message, void *userdata) {
348 const char *path, *result, *unit;
349 Manager *m = userdata;
358 r = sd_bus_message_read(message, "uoss", &id, &path, &unit, &result);
360 log_error("Failed to parse JobRemoved message: %s", strerror(-r));
364 machine = hashmap_get(m->machine_units, unit);
368 if (streq_ptr(path, machine->scope_job)) {
369 free(machine->scope_job);
370 machine->scope_job = NULL;
372 if (machine->started) {
373 if (streq(result, "done"))
374 machine_send_create_reply(machine, NULL);
376 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
378 sd_bus_error_setf(&error, BUS_ERROR_JOB_FAILED, "Start job for unit %s failed with '%s'", unit, result);
380 machine_send_create_reply(machine, &error);
383 machine_save(machine);
386 machine_add_to_gc_queue(machine);
390 int match_properties_changed(sd_bus *bus, sd_bus_message *message, void *userdata) {
391 _cleanup_free_ char *unit = NULL;
392 Manager *m = userdata;
400 path = sd_bus_message_get_path(message);
404 unit_name_from_dbus_path(path, &unit);
408 machine = hashmap_get(m->machine_units, unit);
410 machine_add_to_gc_queue(machine);
415 int match_unit_removed(sd_bus *bus, sd_bus_message *message, void *userdata) {
416 const char *path, *unit;
417 Manager *m = userdata;
425 r = sd_bus_message_read(message, "so", &unit, &path);
427 log_error("Failed to parse UnitRemoved message: %s", strerror(-r));
431 machine = hashmap_get(m->machine_units, unit);
433 machine_add_to_gc_queue(machine);
438 int match_reloading(sd_bus *bus, sd_bus_message *message, void *userdata) {
439 Manager *m = userdata;
446 r = sd_bus_message_read(message, "b", &b);
448 log_error("Failed to parse Reloading message: %s", strerror(-r));
455 /* systemd finished reloading, let's recheck all our machines */
456 log_debug("System manager has been reloaded, rechecking machines...");
458 HASHMAP_FOREACH(machine, m->machines, i)
459 machine_add_to_gc_queue(machine);
464 int manager_start_scope(
469 const char *description,
470 sd_bus_message *more_properties,
474 _cleanup_bus_message_unref_ sd_bus_message *m = NULL, *reply = NULL;
481 r = sd_bus_message_new_method_call(
483 "org.freedesktop.systemd1",
484 "/org/freedesktop/systemd1",
485 "org.freedesktop.systemd1.Manager",
486 "StartTransientUnit",
491 r = sd_bus_message_append(m, "ss", strempty(scope), "fail");
495 r = sd_bus_message_open_container(m, 'a', "(sv)");
499 if (!isempty(slice)) {
500 r = sd_bus_message_append(m, "(sv)", "Slice", "s", slice);
505 if (!isempty(description)) {
506 r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
511 /* cgroup empty notification is not available in containers
512 * currently. To make this less problematic, let's shorten the
513 * stop timeout for machines, so that we don't wait
515 r = sd_bus_message_append(m, "(sv)", "TimeoutStopUSec", "t", 500 * USEC_PER_MSEC);
519 r = sd_bus_message_append(m, "(sv)", "PIDs", "au", 1, pid);
523 if (more_properties) {
524 r = sd_bus_message_copy(m, more_properties, true);
529 r = sd_bus_message_close_container(m);
533 r = sd_bus_call(manager->bus, m, 0, error, &reply);
541 r = sd_bus_message_read(reply, "o", &j);
555 int manager_stop_unit(Manager *manager, const char *unit, sd_bus_error *error, char **job) {
556 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
562 r = sd_bus_call_method(
564 "org.freedesktop.systemd1",
565 "/org/freedesktop/systemd1",
566 "org.freedesktop.systemd1.Manager",
572 if (sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) ||
573 sd_bus_error_has_name(error, BUS_ERROR_LOAD_FAILED)) {
578 sd_bus_error_free(error);
589 r = sd_bus_message_read(reply, "o", &j);
603 int manager_kill_unit(Manager *manager, const char *unit, KillWho who, int signo, sd_bus_error *error) {
607 return sd_bus_call_method(
609 "org.freedesktop.systemd1",
610 "/org/freedesktop/systemd1",
611 "org.freedesktop.systemd1.Manager",
615 "ssi", unit, who == KILL_LEADER ? "main" : "all", signo);
618 int manager_unit_is_active(Manager *manager, const char *unit) {
619 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
620 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
621 _cleanup_free_ char *path = NULL;
628 path = unit_dbus_path_from_name(unit);
632 r = sd_bus_get_property(
634 "org.freedesktop.systemd1",
636 "org.freedesktop.systemd1.Unit",
642 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
643 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
646 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_UNIT) ||
647 sd_bus_error_has_name(&error, BUS_ERROR_LOAD_FAILED))
653 r = sd_bus_message_read(reply, "s", &state);
657 return !streq(state, "inactive") && !streq(state, "failed");
660 int manager_job_is_active(Manager *manager, const char *path) {
661 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
662 _cleanup_bus_message_unref_ sd_bus_message *reply = NULL;
668 r = sd_bus_get_property(
670 "org.freedesktop.systemd1",
672 "org.freedesktop.systemd1.Job",
678 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) ||
679 sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED))
682 if (sd_bus_error_has_name(&error, SD_BUS_ERROR_UNKNOWN_OBJECT))
688 /* We don't actually care about the state really. The fact
689 * that we could read the job state is enough for us */
694 int manager_get_machine_by_pid(Manager *m, pid_t pid, Machine **machine) {
695 _cleanup_free_ char *unit = NULL;
703 r = cg_pid_get_unit(pid, &unit);
705 mm = hashmap_get(m->machine_leaders, UINT_TO_PTR(pid));
707 mm = hashmap_get(m->machine_units, unit);
716 int manager_add_machine(Manager *m, const char *name, Machine **_machine) {
722 machine = hashmap_get(m->machines, name);
724 machine = machine_new(m, name);