machine.c revision 90b2de37b80603168f4e9c9c81cff7eea4efa21a
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering/***
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering This file is part of systemd.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering Copyright 2011 Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering systemd is free software; you can redistribute it and/or modify it
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering under the terms of the GNU Lesser General Public License as published by
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering (at your option) any later version.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering systemd is distributed in the hope that it will be useful, but
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering Lesser General Public License for more details.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering You should have received a copy of the GNU Lesser General Public License
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering***/
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include <string.h>
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include <unistd.h>
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include <errno.h>
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek#include "sd-messages.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "util.h"
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek#include "mkdir.h"
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek#include "hashmap.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "strv.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "fileio.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "special.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "unit-name.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "machine.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "bus-util.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering#include "bus-error.h"
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart PoetteringMachine* machine_new(Manager *manager, const char *name) {
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering Machine *m;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering assert(manager);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering assert(name);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
ef5bfcf668e6029faa78534dfeb2591df854cdefLennart Poettering m = new0(Machine, 1);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (!m)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering return NULL;
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering m->name = strdup(name);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (!m->name)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering goto fail;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek m->state_file = strappend("/run/systemd/machines/", m->name);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek if (!m->state_file)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering goto fail;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
875c6e1b48f37a07dfbb80d6653c73f205e94260Lennart Poettering if (hashmap_put(manager->machines, m->name, m) < 0)
875c6e1b48f37a07dfbb80d6653c73f205e94260Lennart Poettering goto fail;
1a0464230c08506c3fd715ff7cc56660df3a85caBastien Nocera
1a0464230c08506c3fd715ff7cc56660df3a85caBastien Nocera m->class = _MACHINE_CLASS_INVALID;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering m->manager = manager;
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
1a0464230c08506c3fd715ff7cc56660df3a85caBastien Nocera return m;
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
875c6e1b48f37a07dfbb80d6653c73f205e94260Lennart Poetteringfail:
875c6e1b48f37a07dfbb80d6653c73f205e94260Lennart Poettering free(m->state_file);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek free(m->name);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
1a0464230c08506c3fd715ff7cc56660df3a85caBastien Nocera return NULL;
1a0464230c08506c3fd715ff7cc56660df3a85caBastien Nocera}
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmekvoid machine_free(Machine *m) {
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering assert(m);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
ef5bfcf668e6029faa78534dfeb2591df854cdefLennart Poettering if (m->in_gc_queue)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek if (m->scope) {
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering hashmap_remove(m->manager->machine_units, m->scope);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->scope);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering }
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->scope_job);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering hashmap_remove(m->manager->machines, m->name);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek if (m->leader > 0)
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek hashmap_remove_value(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering sd_bus_message_unref(m->create_message);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->name);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->state_file);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->service);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m->root_directory);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering free(m);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek}
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poetteringint machine_save(Machine *m) {
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering _cleanup_free_ char *temp_path = NULL;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering _cleanup_fclose_ FILE *f = NULL;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering int r;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering assert(m);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering assert(m->state_file);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (!m->started)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering return 0;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (r < 0)
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek goto finish;
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering r = fopen_temporary(m->state_file, &f, &temp_path);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering if (r < 0)
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering goto finish;
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering fchmod(fileno(f), 0644);
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek fprintf(f,
3731acf1acfb4a6eb68374a5b137f3b368f63381Lennart Poettering "# This is private data. Do not parse.\n"
"NAME=%s\n",
m->name);
if (m->scope)
fprintf(f, "SCOPE=%s\n", m->scope);
if (m->scope_job)
fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
if (m->service)
fprintf(f, "SERVICE=%s\n", m->service);
if (m->root_directory)
fprintf(f, "ROOT=%s\n", m->root_directory);
if (!sd_id128_equal(m->id, SD_ID128_NULL))
fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
if (m->leader != 0)
fprintf(f, "LEADER="PID_FMT"\n", m->leader);
if (m->class != _MACHINE_CLASS_INVALID)
fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
if (dual_timestamp_is_set(&m->timestamp))
fprintf(f,
"REALTIME="USEC_FMT"\n"
"MONOTONIC="USEC_FMT"\n",
m->timestamp.realtime,
m->timestamp.monotonic);
fflush(f);
if (ferror(f) || rename(temp_path, m->state_file) < 0) {
r = -errno;
unlink(m->state_file);
unlink(temp_path);
}
finish:
if (r < 0)
log_error("Failed to save machine data %s: %s", m->state_file, strerror(-r));
return r;
}
int machine_load(Machine *m) {
_cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL;
int r;
assert(m);
r = parse_env_file(m->state_file, NEWLINE,
"SCOPE", &m->scope,
"SCOPE_JOB", &m->scope_job,
"SERVICE", &m->service,
"ROOT", &m->root_directory,
"ID", &id,
"LEADER", &leader,
"CLASS", &class,
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
NULL);
if (r < 0) {
if (r == -ENOENT)
return 0;
log_error("Failed to read %s: %s", m->state_file, strerror(-r));
return r;
}
if (id)
sd_id128_from_string(id, &m->id);
if (leader)
parse_pid(leader, &m->leader);
if (class) {
MachineClass c;
c = machine_class_from_string(class);
if (c >= 0)
m->class = c;
}
if (realtime) {
unsigned long long l;
if (sscanf(realtime, "%llu", &l) > 0)
m->timestamp.realtime = l;
}
if (monotonic) {
unsigned long long l;
if (sscanf(monotonic, "%llu", &l) > 0)
m->timestamp.monotonic = l;
}
return r;
}
static int machine_start_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
int r = 0;
assert(m);
if (!m->scope) {
_cleanup_free_ char *escaped = NULL;
char *scope, *description, *job;
escaped = unit_name_escape(m->name);
if (!escaped)
return log_oom();
scope = strjoin("machine-", escaped, ".scope", NULL);
if (!scope)
return log_oom();
description = strappenda(m->class == MACHINE_VM ? "Virtual Machine " : "Container ", m->name);
r = manager_start_scope(m->manager, scope, m->leader, SPECIAL_MACHINE_SLICE, description, properties, error, &job);
if (r < 0) {
log_error("Failed to start machine scope: %s", bus_error_message(error, r));
free(scope);
return r;
} else {
m->scope = scope;
free(m->scope_job);
m->scope_job = job;
}
}
if (m->scope)
hashmap_put(m->manager->machine_units, m->scope, m);
return r;
}
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
int r;
assert(m);
if (m->started)
return 0;
r = hashmap_put(m->manager->machine_leaders, UINT_TO_PTR(m->leader), m);
if (r < 0)
return r;
/* Create cgroup */
r = machine_start_scope(m, properties, error);
if (r < 0)
return r;
log_struct(LOG_INFO,
MESSAGE_ID(SD_MESSAGE_MACHINE_START),
"NAME=%s", m->name,
"LEADER=%lu", (unsigned long) m->leader,
"MESSAGE=New machine %s.", m->name,
NULL);
if (!dual_timestamp_is_set(&m->timestamp))
dual_timestamp_get(&m->timestamp);
m->started = true;
/* Save new machine data */
machine_save(m);
machine_send_signal(m, true);
return 0;
}
static int machine_stop_scope(Machine *m) {
_cleanup_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
char *job;
int r;
assert(m);
if (!m->scope)
return 0;
r = manager_stop_unit(m->manager, m->scope, &error, &job);
if (r < 0) {
log_error("Failed to stop machine scope: %s", bus_error_message(&error, r));
return r;
}
free(m->scope_job);
m->scope_job = job;
return r;
}
int machine_stop(Machine *m) {
int r = 0, k;
assert(m);
if (m->started)
log_struct(LOG_INFO,
MESSAGE_ID(SD_MESSAGE_MACHINE_STOP),
"NAME=%s", m->name,
"LEADER=%lu", (unsigned long) m->leader,
"MESSAGE=Machine %s terminated.", m->name,
NULL);
/* Kill cgroup */
k = machine_stop_scope(m);
if (k < 0)
r = k;
unlink(m->state_file);
machine_add_to_gc_queue(m);
if (m->started)
machine_send_signal(m, false);
m->started = false;
return r;
}
bool machine_check_gc(Machine *m, bool drop_not_started) {
assert(m);
if (drop_not_started && !m->started)
return false;
if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
return true;
if (m->scope && manager_unit_is_active(m->manager, m->scope))
return true;
return false;
}
void machine_add_to_gc_queue(Machine *m) {
assert(m);
if (m->in_gc_queue)
return;
LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
m->in_gc_queue = true;
}
MachineState machine_get_state(Machine *s) {
assert(s);
if (s->scope_job)
return s->started ? MACHINE_OPENING : MACHINE_CLOSING;
return MACHINE_RUNNING;
}
int machine_kill(Machine *m, KillWho who, int signo) {
assert(m);
if (!m->scope)
return -ESRCH;
return manager_kill_unit(m->manager, m->scope, who, signo, NULL);
}
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
[MACHINE_CONTAINER] = "container",
[MACHINE_VM] = "vm"
};
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
static const char* const machine_state_table[_MACHINE_STATE_MAX] = {
[MACHINE_OPENING] = "opening",
[MACHINE_RUNNING] = "running",
[MACHINE_CLOSING] = "closing"
};
DEFINE_STRING_TABLE_LOOKUP(machine_state, MachineState);
static const char* const kill_who_table[_KILL_WHO_MAX] = {
[KILL_LEADER] = "leader",
[KILL_ALL] = "all"
};
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);