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 r = manager_stop_unit(m->manager, m->unit, &error, &job);
365 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
375 int machine_stop(Machine *m) {
381 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
383 "LEADER="PID_FMT, m->leader,
384 "MESSAGE=Machine %s terminated.", m->name,
388 k = machine_stop_scope(m);
393 machine_add_to_gc_queue(m);
396 machine_send_signal(m, false);
403 bool machine_check_gc(Machine *m, bool drop_not_started) {
406 if (drop_not_started && !m->started)
409 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
412 if (m->unit && manager_unit_is_active(m->manager, m->unit))
418 void machine_add_to_gc_queue(Machine *m) {
424 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
425 m->in_gc_queue = true;
428 MachineState machine_get_state(Machine *s) {
432 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
434 return MACHINE_RUNNING;
437 int machine_kill(Machine *m, KillWho who, int signo) {
443 if (who == KILL_LEADER) {
444 /* If we shall simply kill the leader, do so directly */
446 if (kill(m->leader, signo) < 0)
450 /* Otherwise make PID 1 do it for us, for the entire cgroup */
451 return manager_kill_unit(m->manager, m->unit, signo, NULL);
454 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
455 [MACHINE_CONTAINER] = "container",
459 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
461 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
462 [MACHINE_OPENING] = "opening",
463 [MACHINE_RUNNING] = "running",
464 [MACHINE_CLOSING] = "closing"
467 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
469 static const char* const kill_who_table[_KILL_WHO_MAX] = {
470 [KILL_LEADER] = "leader",
474 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);