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);
181 if (ferror(f) || rename(temp_path, m->state_file) < 0) {
183 unlink(m->state_file);
190 /* Create a symlink from the unit name to the machine
191 * name, so that we can quickly find the machine for
193 sl = strappenda("/run/systemd/machines/unit:", m->unit);
194 symlink(m->name, sl);
199 log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
204 static void machine_unlink(Machine *m) {
211 sl = strappenda("/run/systemd/machines/unit:", m->unit);
216 unlink(m->state_file);
219 int machine_load(Machine *m) {
220 _cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
225 r = parse_env_file(m->state_file, NEWLINE,
227 "SCOPE_JOB", &m->scope_job,
228 "SERVICE", &m->service,
229 "ROOT", &m->root_directory,
233 "REALTIME", &realtime,
234 "MONOTONIC", &monotonic,
240 log_error("Failed to read %s: %s", m->state_file, strerror(-r));
245 sd_id128_from_string(id, &m->id);
248 parse_pid(leader, &m->leader);
253 c = machine_class_from_string(class);
259 unsigned long long l;
260 if (sscanf(realtime, "%llu", &l) > 0)
261 m->timestamp.realtime = l;
265 unsigned long long l;
266 if (sscanf(monotonic, "%llu", &l) > 0)
267 m->timestamp.monotonic = l;
273 static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
279 _cleanup_free_ char *escaped = NULL;
280 char *scope, *description, *job = NULL;
282 escaped = unit_name_escape(m->name);
286 scope = strjoin("machine-", escaped, ".scope", NULL);
290 description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
292 r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
294 log_error("Failed to start machine scope: %s", bus_error_message(error, r));
306 hashmap_put(m->manager->machine_units, m->unit, m);
311 int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
319 r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
324 r = machine_start_scope(m, properties, error);
329 MESSAGE_ID(SD_MESSAGE_MACHINE_START),
331 "LEADER="PID_FMT, m->leader,
332 "MESSAGE=New machine %s.", m->name,
335 if (!dual_timestamp_is_set(&m->timestamp))
336 dual_timestamp_get(&m->timestamp);
340 /* Save new machine data */
343 machine_send_signal(m, true);
348 static int machine_stop_scope(Machine *m) {
349 _cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
358 r = manager_stop_unit(m->manager, m->unit, &error, &job);
360 log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
370 int machine_stop(Machine *m) {
376 MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
378 "LEADER="PID_FMT, m->leader,
379 "MESSAGE=Machine %s terminated.", m->name,
383 k = machine_stop_scope(m);
388 machine_add_to_gc_queue(m);
391 machine_send_signal(m, false);
398 bool machine_check_gc(Machine *m, bool drop_not_started) {
401 if (drop_not_started && !m->started)
404 if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
407 if (m->unit && manager_unit_is_active(m->manager, m->unit))
413 void machine_add_to_gc_queue(Machine *m) {
419 LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
420 m->in_gc_queue = true;
423 MachineState machine_get_state(Machine *s) {
427 return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
429 return MACHINE_RUNNING;
432 int machine_kill(Machine *m, KillWho who, int signo) {
438 if (who == KILL_LEADER) {
439 /* If we shall simply kill the leader, do so directly */
441 if (kill(m->leader, signo) < 0)
445 /* Otherwise make PID 1 do it for us, for the entire cgroup */
446 return manager_kill_unit(m->manager, m->unit, signo, NULL);
449 static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
450 [MACHINE_CONTAINER] = "container",
454 DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
456 static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
457 [MACHINE_OPENING] = "opening",
458 [MACHINE_RUNNING] = "running",
459 [MACHINE_CLOSING] = "closing"
462 DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
464 static const char* const kill_who_table[_KILL_WHO_MAX] = {
465 [KILL_LEADER] = "leader",
469 DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);