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 "sd-messages.h"
34 #include "unit-name.h"
37 #include "bus-error.h"
39 Machine* machine_new(Manager *manager, const char *name) {
49 m->name = strdup(name);
53 m->state_file = strappend("/run/systemd/machines/", m->name);
57 if (hashmap_put(manager->machines, m->name, m) < 0)
60 m->class = _MACHINE_CLASS_INVALID;
73 void machine_free(Machine *m) {
77 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
80 hashmap_remove(m->manager->machine_units, m->unit);
86 hashmap_remove(m->manager->machines, m->name);
89 hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
91 sd_bus_message_unref(m->create_message);
96 free(m->root_directory);
100 int machine_save(Machine *m) {
101 _cleanup_free_ char *temp_path = NULL;
102 _cleanup_fclose_ FILE *f = NULL;
106 assert(m->state_file);
111 r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
115 r = fopen_temporary(m->state_file, &f, &temp_path);
119 fchmod(fileno(f), 0644);
122 "# This is private data. Do not parse.\n"
127 _cleanup_free_ char *escaped;
129 escaped = cescape(m->unit);
135 fprintf(f, "SCOPE=%s\n", escaped); /* We continue to call this "SCOPE=" because it is internal only, and we want to stay compatible with old files */
139 fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
142 _cleanup_free_ char *escaped;
144 escaped = cescape(m->service);
149 fprintf(f, "SERVICE=%s\n", escaped);
152 if (m->root_directory) {
153 _cleanup_free_ char *escaped;
155 escaped = cescape(m->root_directory);
160 fprintf(f, "ROOT=%s\n", escaped);
163 if (!sd_id128_equal(m->id, SD_ID128_NULL))
164 fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
167 fprintf(f, "LEADER="PID_FMT"\n", m->leader);
169 if (m->class != _MACHINE_CLASS_INVALID)
170 fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
172 if (dual_timestamp_is_set(&m->timestamp))
174 "REALTIME="USEC_FMT"\n"
175 "MONOTONIC="USEC_FMT"\n",
176 m->timestamp.realtime,
177 m->timestamp.monotonic);
179 r = fflush_and_check(f);
183 if (rename(temp_path, m->state_file) < 0) {
191 /* Create a symlink from the unit name to the machine
192 * name, so that we can quickly find the machine for
194 sl = strappenda("/run/systemd/machines/unit:", m->unit);
195 symlink(m->name, sl);
203 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
209 static void machine_unlink(Machine *m) {
216 sl = strappenda("/run/systemd/machines/unit:", m->unit);
221 unlink(m->state_file);
224 int machine_load(Machine *m) {
225 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
230 r = parse_env_file(m->state_file, NEWLINE,
232 "SCOPE_JOB", &m->scope_job,
233 "SERVICE", &m->service,
234 "ROOT", &m->root_directory,
238 "REALTIME", &realtime,
239 "MONOTONIC", &monotonic,
245 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
250 sd_id128_from_string(id, &m->id);
253 parse_pid(leader, &m->leader);
258 c = machine_class_from_string(class);
264 unsigned long long l;
265 if (sscanf(realtime, "%llu", &l) > 0)
266 m->timestamp.realtime = l;
270 unsigned long long l;
271 if (sscanf(monotonic, "%llu", &l) > 0)
272 m->timestamp.monotonic = l;
278 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
284 _cleanup_free_ char *escaped = NULL;
285 char *scope, *description, *job = NULL;
287 escaped = unit_name_escape(m->name);
291 scope = strjoin("machine-", escaped, ".scope", NULL);
295 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
297 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
299 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
311 hashmap_put(m->manager->machine_units, m->unit, m);
316 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
324 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
329 r = machine_start_scope(m, properties, error);
334 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
336 "LEADER="PID_FMT, m->leader,
337 "MESSAGE=New machine %s.", m->name,
340 if (!dual_timestamp_is_set(&m->timestamp))
341 dual_timestamp_get(&m->timestamp);
345 /* Save new machine data */
348 machine_send_signal(m, true);
353 static int machine_stop_scope(Machine *m) {
354 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
363 if (!m->registered) {
364 r = manager_stop_unit(m->manager, m->unit, &error, &job);
366 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
377 int machine_stop(Machine *m) {
383 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
385 "LEADER="PID_FMT, m->leader,
386 "MESSAGE=Machine %s terminated.", m->name,
390 k = machine_stop_scope(m);
395 machine_add_to_gc_queue(m);
398 machine_send_signal(m, false);
405 bool machine_check_gc(Machine *m, bool drop_not_started) {
408 if (drop_not_started && !m->started)
411 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
414 if (m->unit && manager_unit_is_active(m->manager, m->unit))
420 void machine_add_to_gc_queue(Machine *m) {
426 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
427 m->in_gc_queue = true;
430 MachineState machine_get_state(Machine *s) {
434 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
436 return MACHINE_RUNNING;
439 int machine_kill(Machine *m, KillWho who, int signo) {
445 if (who == KILL_LEADER) {
446 /* If we shall simply kill the leader, do so directly */
448 if (kill(m->leader, signo) < 0)
452 /* Otherwise make PID 1 do it for us, for the entire cgroup */
453 return manager_kill_unit(m->manager, m->unit, signo, NULL);
456 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
457 [MACHINE_CONTAINER] = "container",
461 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
463 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
464 [MACHINE_OPENING] = "opening",
465 [MACHINE_RUNNING] = "running",
466 [MACHINE_CLOSING] = "closing"
469 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
471 static const char* const kill_who_table[_KILL_WHO_MAX] = {
472 [KILL_LEADER] = "leader",
476 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);