2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-10-07 22:45:48 +02:00
|
|
|
#include <errno.h>
|
2013-06-20 03:45:08 +02:00
|
|
|
#include <unistd.h>
|
2019-03-27 11:32:41 +01:00
|
|
|
#include <sys/stat.h>
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2013-10-30 02:06:55 +01:00
|
|
|
#include "sd-messages.h"
|
2013-07-02 01:46:30 +02:00
|
|
|
|
2015-10-27 03:01:06 +01:00
|
|
|
#include "alloc-util.h"
|
2015-10-07 22:45:48 +02:00
|
|
|
#include "bus-error.h"
|
|
|
|
#include "bus-util.h"
|
2018-11-30 22:08:41 +01:00
|
|
|
#include "env-file.h"
|
2019-07-11 15:42:14 +02:00
|
|
|
#include "errno-util.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
#include "escape.h"
|
2015-11-16 22:09:36 +01:00
|
|
|
#include "extract-word.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "fd-util.h"
|
2013-06-20 03:45:08 +02:00
|
|
|
#include "fileio.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2015-10-07 22:45:48 +02:00
|
|
|
#include "hashmap.h"
|
2015-10-23 18:52:53 +02:00
|
|
|
#include "machine-dbus.h"
|
2015-10-25 13:14:12 +01:00
|
|
|
#include "machine.h"
|
2015-10-07 22:45:48 +02:00
|
|
|
#include "mkdir.h"
|
2015-10-26 16:18:16 +01:00
|
|
|
#include "parse-util.h"
|
2019-07-11 19:14:16 +02:00
|
|
|
#include "path-util.h"
|
2015-11-17 00:00:32 +01:00
|
|
|
#include "process-util.h"
|
2018-10-17 20:40:09 +02:00
|
|
|
#include "serialize.h"
|
2013-06-20 03:45:08 +02:00
|
|
|
#include "special.h"
|
2017-02-10 19:44:09 +01:00
|
|
|
#include "stdio-util.h"
|
2015-10-26 22:31:05 +01:00
|
|
|
#include "string-table.h"
|
2015-10-07 22:45:48 +02:00
|
|
|
#include "terminal-util.h"
|
2018-11-30 21:05:27 +01:00
|
|
|
#include "tmpfile-util.h"
|
2013-07-02 01:46:30 +02:00
|
|
|
#include "unit-name.h"
|
2017-12-04 17:06:56 +01:00
|
|
|
#include "user-util.h"
|
2015-10-07 22:45:48 +02:00
|
|
|
#include "util.h"
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
|
2013-06-20 03:45:08 +02:00
|
|
|
Machine *m;
|
|
|
|
|
|
|
|
assert(manager);
|
2015-08-24 21:05:09 +02:00
|
|
|
assert(class < _MACHINE_CLASS_MAX);
|
2013-06-20 03:45:08 +02:00
|
|
|
assert(name);
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
/* Passing class == _MACHINE_CLASS_INVALID here is fine. It
|
|
|
|
* means as much as "we don't know yet", and that we'll figure
|
|
|
|
* it out later when loading the state file. */
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
m = new0(Machine, 1);
|
|
|
|
if (!m)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
m->name = strdup(name);
|
|
|
|
if (!m->name)
|
|
|
|
goto fail;
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (class != MACHINE_HOST) {
|
2019-07-11 19:14:16 +02:00
|
|
|
m->state_file = path_join("/run/systemd/machines", m->name);
|
2015-08-24 21:05:09 +02:00
|
|
|
if (!m->state_file)
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
m->class = class;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
if (hashmap_put(manager->machines, m->name, m) < 0)
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
m->manager = manager;
|
|
|
|
|
|
|
|
return m;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
free(m->state_file);
|
|
|
|
free(m->name);
|
2016-10-17 00:28:30 +02:00
|
|
|
return mfree(m);
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2018-11-28 14:55:13 +01:00
|
|
|
Machine* machine_free(Machine *m) {
|
|
|
|
if (!m)
|
|
|
|
return NULL;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-02-17 19:22:03 +01:00
|
|
|
while (m->operations)
|
2016-04-29 20:32:56 +02:00
|
|
|
operation_free(m->operations);
|
2015-02-17 19:22:03 +01:00
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
if (m->in_gc_queue)
|
2013-10-14 06:10:14 +02:00
|
|
|
LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-04-28 21:17:35 +02:00
|
|
|
machine_release_unit(m);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
free(m->scope_job);
|
|
|
|
|
2015-04-28 21:17:35 +02:00
|
|
|
(void) hashmap_remove(m->manager->machines, m->name);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (m->manager->host_machine == m)
|
|
|
|
m->manager->host_machine = NULL;
|
|
|
|
|
2013-11-06 02:03:04 +01:00
|
|
|
if (m->leader > 0)
|
2015-11-17 00:00:32 +01:00
|
|
|
(void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
|
2013-11-06 02:03:04 +01:00
|
|
|
|
2013-10-30 02:06:55 +01:00
|
|
|
sd_bus_message_unref(m->create_message);
|
2013-07-02 01:46:30 +02:00
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
free(m->name);
|
|
|
|
free(m->state_file);
|
|
|
|
free(m->service);
|
|
|
|
free(m->root_directory);
|
2014-07-10 22:47:55 +02:00
|
|
|
free(m->netif);
|
2018-11-28 14:55:13 +01:00
|
|
|
return mfree(m);
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int machine_save(Machine *m) {
|
|
|
|
_cleanup_free_ char *temp_path = NULL;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
2015-08-24 21:05:09 +02:00
|
|
|
|
|
|
|
if (!m->state_file)
|
|
|
|
return 0;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
if (!m->started)
|
|
|
|
return 0;
|
|
|
|
|
tree-wide: warn when a directory path already exists but has bad mode/owner/type
When we are attempting to create directory somewhere in the bowels of /var/lib
and get an error that it already exists, it can be quite hard to diagnose what
is wrong (especially for a user who is not aware that the directory must have
the specified owner, and permissions not looser than what was requested). Let's
print a warning in most cases. A warning is appropriate, because such state is
usually a sign of borked installation and needs to be resolved by the adminstrator.
$ build/test-fs-util
Path "/tmp/test-readlink_and_make_absolute" already exists and is not a directory, refusing.
(or)
Directory "/tmp/test-readlink_and_make_absolute" already exists, but has mode 0775 that is too permissive (0755 was requested), refusing.
(or)
Directory "/tmp/test-readlink_and_make_absolute" already exists, but is owned by 1001:1000 (1000:1000 was requested), refusing.
Assertion 'mkdir_safe(tempdir, 0755, getuid(), getgid(), MKDIR_WARN_MODE) >= 0' failed at ../src/test/test-fs-util.c:320, function test_readlink_and_make_absolute(). Aborting.
No functional change except for the new log lines.
2018-03-22 13:03:41 +01:00
|
|
|
r = mkdir_safe_label("/run/systemd/machines", 0755, 0, 0, MKDIR_WARN_MODE);
|
2013-06-20 03:45:08 +02:00
|
|
|
if (r < 0)
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
r = fopen_temporary(m->state_file, &f, &temp_path);
|
|
|
|
if (r < 0)
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-07-29 20:31:07 +02:00
|
|
|
(void) fchmod(fileno(f), 0644);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
fprintf(f,
|
|
|
|
"# This is private data. Do not parse.\n"
|
|
|
|
"NAME=%s\n",
|
|
|
|
m->name);
|
|
|
|
|
2014-05-22 03:26:23 +02:00
|
|
|
if (m->unit) {
|
|
|
|
_cleanup_free_ char *escaped;
|
|
|
|
|
|
|
|
escaped = cescape(m->unit);
|
|
|
|
if (!escaped) {
|
|
|
|
r = -ENOMEM;
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2014-05-22 03:26:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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 */
|
|
|
|
}
|
2013-07-02 01:46:30 +02:00
|
|
|
|
|
|
|
if (m->scope_job)
|
|
|
|
fprintf(f, "SCOPE_JOB=%s\n", m->scope_job);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-05-22 03:26:23 +02:00
|
|
|
if (m->service) {
|
|
|
|
_cleanup_free_ char *escaped;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-05-22 03:26:23 +02:00
|
|
|
escaped = cescape(m->service);
|
|
|
|
if (!escaped) {
|
|
|
|
r = -ENOMEM;
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2014-05-22 03:26:23 +02:00
|
|
|
}
|
|
|
|
fprintf(f, "SERVICE=%s\n", escaped);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->root_directory) {
|
|
|
|
_cleanup_free_ char *escaped;
|
|
|
|
|
|
|
|
escaped = cescape(m->root_directory);
|
|
|
|
if (!escaped) {
|
|
|
|
r = -ENOMEM;
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2014-05-22 03:26:23 +02:00
|
|
|
}
|
|
|
|
fprintf(f, "ROOT=%s\n", escaped);
|
|
|
|
}
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2016-07-21 16:06:31 +02:00
|
|
|
if (!sd_id128_is_null(m->id))
|
2013-06-20 03:45:08 +02:00
|
|
|
fprintf(f, "ID=" SD_ID128_FORMAT_STR "\n", SD_ID128_FORMAT_VAL(m->id));
|
|
|
|
|
|
|
|
if (m->leader != 0)
|
2014-02-04 01:31:53 +01:00
|
|
|
fprintf(f, "LEADER="PID_FMT"\n", m->leader);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
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,
|
2014-02-04 01:31:53 +01:00
|
|
|
"REALTIME="USEC_FMT"\n"
|
|
|
|
"MONOTONIC="USEC_FMT"\n",
|
|
|
|
m->timestamp.realtime,
|
|
|
|
m->timestamp.monotonic);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-07-10 22:47:55 +02:00
|
|
|
if (m->n_netif > 0) {
|
2018-10-09 09:45:55 +02:00
|
|
|
size_t i;
|
2014-07-10 22:47:55 +02:00
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputs("NETIF=", f);
|
2014-07-10 22:47:55 +02:00
|
|
|
|
|
|
|
for (i = 0; i < m->n_netif; i++) {
|
|
|
|
if (i != 0)
|
2017-12-11 19:50:30 +01:00
|
|
|
fputc(' ', f);
|
2014-07-10 22:47:55 +02:00
|
|
|
|
|
|
|
fprintf(f, "%i", m->netif[i]);
|
|
|
|
}
|
|
|
|
|
2017-12-11 19:50:30 +01:00
|
|
|
fputc('\n', f);
|
2014-07-10 22:47:55 +02:00
|
|
|
}
|
|
|
|
|
2014-06-12 23:06:56 +02:00
|
|
|
r = fflush_and_check(f);
|
|
|
|
if (r < 0)
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-06-12 23:06:56 +02:00
|
|
|
if (rename(temp_path, m->state_file) < 0) {
|
2013-06-20 03:45:08 +02:00
|
|
|
r = -errno;
|
2015-07-29 20:31:07 +02:00
|
|
|
goto fail;
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
if (m->unit) {
|
|
|
|
char *sl;
|
|
|
|
|
|
|
|
/* Create a symlink from the unit name to the machine
|
|
|
|
* name, so that we can quickly find the machine for
|
2015-03-15 22:17:24 +01:00
|
|
|
* each given unit. Ignore error. */
|
2015-02-03 02:05:59 +01:00
|
|
|
sl = strjoina("/run/systemd/machines/unit:", m->unit);
|
2015-03-15 22:17:24 +01:00
|
|
|
(void) symlink(m->name, sl);
|
2014-02-11 17:15:38 +01:00
|
|
|
}
|
|
|
|
|
2015-07-29 20:31:07 +02:00
|
|
|
return 0;
|
2014-06-12 23:06:56 +02:00
|
|
|
|
2015-07-29 20:31:07 +02:00
|
|
|
fail:
|
|
|
|
(void) unlink(m->state_file);
|
|
|
|
|
|
|
|
if (temp_path)
|
|
|
|
(void) unlink(temp_path);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-07-29 20:31:07 +02:00
|
|
|
return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
static void machine_unlink(Machine *m) {
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (m->unit) {
|
|
|
|
char *sl;
|
|
|
|
|
2015-02-03 02:05:59 +01:00
|
|
|
sl = strjoina("/run/systemd/machines/unit:", m->unit);
|
2015-08-06 15:48:17 +02:00
|
|
|
(void) unlink(sl);
|
2014-02-11 17:15:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (m->state_file)
|
2015-08-06 15:48:17 +02:00
|
|
|
(void) unlink(m->state_file);
|
2014-02-11 17:15:38 +01:00
|
|
|
}
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
int machine_load(Machine *m) {
|
2014-07-10 22:47:55 +02:00
|
|
|
_cleanup_free_ char *realtime = NULL, *monotonic = NULL, *id = NULL, *leader = NULL, *class = NULL, *netif = NULL;
|
2013-06-20 03:45:08 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (!m->state_file)
|
|
|
|
return 0;
|
|
|
|
|
2018-11-12 14:04:47 +01:00
|
|
|
r = parse_env_file(NULL, m->state_file,
|
2014-02-11 17:15:38 +01:00
|
|
|
"SCOPE", &m->unit,
|
2013-07-02 01:46:30 +02:00
|
|
|
"SCOPE_JOB", &m->scope_job,
|
2013-06-20 03:45:08 +02:00
|
|
|
"SERVICE", &m->service,
|
|
|
|
"ROOT", &m->root_directory,
|
|
|
|
"ID", &id,
|
|
|
|
"LEADER", &leader,
|
|
|
|
"CLASS", &class,
|
|
|
|
"REALTIME", &realtime,
|
|
|
|
"MONOTONIC", &monotonic,
|
2018-11-12 14:18:03 +01:00
|
|
|
"NETIF", &netif);
|
2013-06-20 03:45:08 +02:00
|
|
|
if (r < 0) {
|
|
|
|
if (r == -ENOENT)
|
|
|
|
return 0;
|
|
|
|
|
2014-11-28 19:13:53 +01:00
|
|
|
return log_error_errno(r, "Failed to read %s: %m", m->state_file);
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-15 23:26:34 +01:00
|
|
|
if (realtime)
|
2018-10-17 20:40:09 +02:00
|
|
|
(void) deserialize_usec(realtime, &m->timestamp.realtime);
|
2016-02-15 23:26:34 +01:00
|
|
|
if (monotonic)
|
2018-10-17 20:40:09 +02:00
|
|
|
(void) deserialize_usec(monotonic, &m->timestamp.monotonic);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-07-10 22:47:55 +02:00
|
|
|
if (netif) {
|
2015-10-31 23:06:07 +01:00
|
|
|
size_t allocated = 0, nr = 0;
|
|
|
|
const char *p;
|
2019-12-18 13:54:13 +01:00
|
|
|
_cleanup_free_ int *ni = NULL;
|
2014-07-10 22:47:55 +02:00
|
|
|
|
2015-10-31 23:06:07 +01:00
|
|
|
p = netif;
|
2016-02-23 18:52:52 +01:00
|
|
|
for (;;) {
|
2015-10-31 23:06:07 +01:00
|
|
|
_cleanup_free_ char *word = NULL;
|
2014-07-10 22:47:55 +02:00
|
|
|
|
2015-10-31 23:06:07 +01:00
|
|
|
r = extract_first_word(&p, &word, NULL, 0);
|
|
|
|
if (r == 0)
|
|
|
|
break;
|
2015-11-02 23:47:38 +01:00
|
|
|
if (r == -ENOMEM)
|
2015-11-02 12:25:59 +01:00
|
|
|
return log_oom();
|
2015-11-02 23:47:38 +01:00
|
|
|
if (r < 0) {
|
2015-11-02 12:25:59 +01:00
|
|
|
log_warning_errno(r, "Failed to parse NETIF: %s", netif);
|
2015-11-02 23:47:38 +01:00
|
|
|
break;
|
2015-11-02 12:25:59 +01:00
|
|
|
}
|
2015-10-31 23:06:07 +01:00
|
|
|
|
2019-12-18 13:54:13 +01:00
|
|
|
r = parse_ifindex(word);
|
|
|
|
if (r < 0)
|
2014-07-10 22:47:55 +02:00
|
|
|
continue;
|
|
|
|
|
2019-12-18 13:54:13 +01:00
|
|
|
if (!GREEDY_REALLOC(ni, allocated, nr + 1))
|
2014-07-10 22:47:55 +02:00
|
|
|
return log_oom();
|
|
|
|
|
2019-12-18 13:54:13 +01:00
|
|
|
ni[nr++] = r;
|
2014-07-10 22:47:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
free(m->netif);
|
2019-12-18 13:54:13 +01:00
|
|
|
m->netif = TAKE_PTR(ni);
|
2014-07-10 22:47:55 +02:00
|
|
|
m->n_netif = nr;
|
|
|
|
}
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2019-11-21 13:44:33 +01:00
|
|
|
static int machine_start_scope(
|
2019-11-21 14:32:51 +01:00
|
|
|
Machine *machine,
|
2019-11-21 13:44:33 +01:00
|
|
|
sd_bus_message *more_properties,
|
2019-11-21 14:32:51 +01:00
|
|
|
sd_bus_error *error) {
|
2019-11-21 13:44:33 +01:00
|
|
|
|
|
|
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
|
2019-11-21 14:32:51 +01:00
|
|
|
_cleanup_free_ char *escaped = NULL, *unit = NULL;
|
|
|
|
const char *description;
|
2019-11-21 13:44:33 +01:00
|
|
|
int r;
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
assert(machine);
|
|
|
|
assert(machine->leader > 0);
|
|
|
|
assert(!machine->unit);
|
|
|
|
|
|
|
|
escaped = unit_name_escape(machine->name);
|
|
|
|
if (!escaped)
|
|
|
|
return log_oom();
|
|
|
|
|
|
|
|
unit = strjoin("machine-", escaped, ".scope");
|
|
|
|
if (!unit)
|
|
|
|
return log_oom();
|
2019-11-21 13:44:33 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_new_method_call(
|
2019-11-21 14:32:51 +01:00
|
|
|
machine->manager->bus,
|
2019-11-21 13:44:33 +01:00
|
|
|
&m,
|
|
|
|
"org.freedesktop.systemd1",
|
|
|
|
"/org/freedesktop/systemd1",
|
|
|
|
"org.freedesktop.systemd1.Manager",
|
|
|
|
"StartTransientUnit");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
r = sd_bus_message_append(m, "ss", unit, "fail");
|
2019-11-21 13:44:33 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_open_container(m, 'a', "(sv)");
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
r = sd_bus_message_append(m, "(sv)", "Slice", "s", SPECIAL_MACHINE_SLICE);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2019-11-21 13:44:33 +01:00
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
description = strjoina(machine->class == MACHINE_VM ? "Virtual Machine " : "Container ", machine->name);
|
|
|
|
r = sd_bus_message_append(m, "(sv)", "Description", "s", description);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2019-11-21 13:44:33 +01:00
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
|
2019-11-21 14:32:51 +01:00
|
|
|
"PIDs", "au", 1, machine->leader,
|
2019-11-21 13:44:33 +01:00
|
|
|
"Delegate", "b", 1,
|
|
|
|
"CollectMode", "s", "inactive-or-failed",
|
|
|
|
"AddRef", "b", 1,
|
|
|
|
"TasksMax", "t", UINT64_C(16384));
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
if (more_properties) {
|
|
|
|
r = sd_bus_message_copy(m, more_properties, true);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = sd_bus_message_close_container(m);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_bus_message_append(m, "a(sa(sv))", 0);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
r = sd_bus_call(NULL, m, 0, error, &reply);
|
2019-11-21 13:44:33 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
machine->unit = TAKE_PTR(unit);
|
|
|
|
machine->referenced = true;
|
2019-11-21 13:44:33 +01:00
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
const char *job;
|
|
|
|
r = sd_bus_message_read(reply, "o", &job);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2019-11-21 13:44:33 +01:00
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
return free_and_strdup(&machine->scope_job, job);
|
2019-11-21 13:44:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
|
2019-11-21 14:32:51 +01:00
|
|
|
int r;
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
assert(m);
|
2015-08-24 21:05:09 +02:00
|
|
|
assert(m->class != MACHINE_HOST);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
if (!m->unit) {
|
2019-11-21 14:32:51 +01:00
|
|
|
r = machine_start_scope(m, properties, error);
|
2018-08-07 03:11:56 +02:00
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2019-11-21 14:32:51 +01:00
|
|
|
assert(m->unit);
|
|
|
|
hashmap_put(m->manager->machine_units, m->unit, m);
|
2013-07-02 17:17:35 +02:00
|
|
|
|
2018-08-07 03:11:56 +02:00
|
|
|
return 0;
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2013-10-30 02:06:55 +01:00
|
|
|
int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
|
2013-06-20 03:45:08 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(m);
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
if (m->started)
|
|
|
|
return 0;
|
|
|
|
|
2015-11-17 00:00:32 +01:00
|
|
|
r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
|
2013-11-06 02:03:04 +01:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
/* Create cgroup */
|
2019-11-21 13:44:33 +01:00
|
|
|
r = machine_ensure_scope(m, properties, error);
|
2013-07-02 01:46:30 +02:00
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
log_struct(LOG_INFO,
|
tree-wide: add SD_ID128_MAKE_STR, remove LOG_MESSAGE_ID
Embedding sd_id128_t's in constant strings was rather cumbersome. We had
SD_ID128_CONST_STR which returned a const char[], but it had two problems:
- it wasn't possible to statically concatanate this array with a normal string
- gcc wasn't really able to optimize this, and generated code to perform the
"conversion" at runtime.
Because of this, even our own code in coredumpctl wasn't using
SD_ID128_CONST_STR.
Add a new macro to generate a constant string: SD_ID128_MAKE_STR.
It is not as elegant as SD_ID128_CONST_STR, because it requires a repetition
of the numbers, but in practice it is more convenient to use, and allows gcc
to generate smarter code:
$ size .libs/systemd{,-logind,-journald}{.old,}
text data bss dec hex filename
1265204 149564 4808 1419576 15a938 .libs/systemd.old
1260268 149564 4808 1414640 1595f0 .libs/systemd
246805 13852 209 260866 3fb02 .libs/systemd-logind.old
240973 13852 209 255034 3e43a .libs/systemd-logind
146839 4984 34 151857 25131 .libs/systemd-journald.old
146391 4984 34 151409 24f71 .libs/systemd-journald
It is also much easier to check if a certain binary uses a certain MESSAGE_ID:
$ strings .libs/systemd.old|grep MESSAGE_ID
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
$ strings .libs/systemd|grep MESSAGE_ID
MESSAGE_ID=c7a787079b354eaaa9e77b371893cd27
MESSAGE_ID=b07a249cd024414a82dd00cd181378ff
MESSAGE_ID=641257651c1b4ec9a8624d7a40a9e1e7
MESSAGE_ID=de5b426a63be47a7b6ac3eaac82e2f6f
MESSAGE_ID=d34d037fff1847e6ae669a370e694725
MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5
MESSAGE_ID=1dee0369c7fc4736b7099b38ecb46ee7
MESSAGE_ID=39f53479d3a045ac8e11786248231fbf
MESSAGE_ID=be02cf6855d2428ba40df7e9d022f03d
MESSAGE_ID=7b05ebc668384222baa8881179cfda54
MESSAGE_ID=9d1aaa27d60140bd96365438aad20286
2016-11-06 18:48:23 +01:00
|
|
|
"MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
|
2013-06-20 03:45:08 +02:00
|
|
|
"NAME=%s", m->name,
|
2014-04-25 13:45:15 +02:00
|
|
|
"LEADER="PID_FMT, m->leader,
|
2018-06-04 12:59:22 +02:00
|
|
|
LOG_MESSAGE("New machine %s.", m->name));
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
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);
|
2018-12-07 16:49:52 +01:00
|
|
|
(void) manager_enqueue_nscd_cache_flush(m->manager);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int machine_stop(Machine *m) {
|
2015-08-06 15:50:54 +02:00
|
|
|
int r;
|
2019-11-21 14:54:11 +01:00
|
|
|
|
2015-08-06 15:50:54 +02:00
|
|
|
assert(m);
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (!IN_SET(m->class, MACHINE_CONTAINER, MACHINE_VM))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2019-11-21 14:54:11 +01:00
|
|
|
if (m->unit) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
char *job = NULL;
|
|
|
|
|
|
|
|
r = manager_stop_unit(m->manager, m->unit, &error, &job);
|
|
|
|
if (r < 0)
|
|
|
|
return log_error_errno(r, "Failed to stop machine scope: %s", bus_error_message(&error, r));
|
|
|
|
|
|
|
|
free_and_replace(m->scope_job, job);
|
|
|
|
}
|
2015-08-06 15:50:54 +02:00
|
|
|
|
|
|
|
m->stopping = true;
|
|
|
|
|
|
|
|
machine_save(m);
|
2018-12-07 16:49:52 +01:00
|
|
|
(void) manager_enqueue_nscd_cache_flush(m->manager);
|
2015-08-06 15:50:54 +02:00
|
|
|
|
2019-11-21 14:54:11 +01:00
|
|
|
return 0;
|
2015-08-06 15:50:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int machine_finalize(Machine *m) {
|
2013-06-20 03:45:08 +02:00
|
|
|
assert(m);
|
|
|
|
|
2019-10-29 09:43:07 +01:00
|
|
|
if (m->started) {
|
2013-06-20 03:45:08 +02:00
|
|
|
log_struct(LOG_INFO,
|
tree-wide: add SD_ID128_MAKE_STR, remove LOG_MESSAGE_ID
Embedding sd_id128_t's in constant strings was rather cumbersome. We had
SD_ID128_CONST_STR which returned a const char[], but it had two problems:
- it wasn't possible to statically concatanate this array with a normal string
- gcc wasn't really able to optimize this, and generated code to perform the
"conversion" at runtime.
Because of this, even our own code in coredumpctl wasn't using
SD_ID128_CONST_STR.
Add a new macro to generate a constant string: SD_ID128_MAKE_STR.
It is not as elegant as SD_ID128_CONST_STR, because it requires a repetition
of the numbers, but in practice it is more convenient to use, and allows gcc
to generate smarter code:
$ size .libs/systemd{,-logind,-journald}{.old,}
text data bss dec hex filename
1265204 149564 4808 1419576 15a938 .libs/systemd.old
1260268 149564 4808 1414640 1595f0 .libs/systemd
246805 13852 209 260866 3fb02 .libs/systemd-logind.old
240973 13852 209 255034 3e43a .libs/systemd-logind
146839 4984 34 151857 25131 .libs/systemd-journald.old
146391 4984 34 151409 24f71 .libs/systemd-journald
It is also much easier to check if a certain binary uses a certain MESSAGE_ID:
$ strings .libs/systemd.old|grep MESSAGE_ID
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
MESSAGE_ID=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x
$ strings .libs/systemd|grep MESSAGE_ID
MESSAGE_ID=c7a787079b354eaaa9e77b371893cd27
MESSAGE_ID=b07a249cd024414a82dd00cd181378ff
MESSAGE_ID=641257651c1b4ec9a8624d7a40a9e1e7
MESSAGE_ID=de5b426a63be47a7b6ac3eaac82e2f6f
MESSAGE_ID=d34d037fff1847e6ae669a370e694725
MESSAGE_ID=7d4958e842da4a758f6c1cdc7b36dcc5
MESSAGE_ID=1dee0369c7fc4736b7099b38ecb46ee7
MESSAGE_ID=39f53479d3a045ac8e11786248231fbf
MESSAGE_ID=be02cf6855d2428ba40df7e9d022f03d
MESSAGE_ID=7b05ebc668384222baa8881179cfda54
MESSAGE_ID=9d1aaa27d60140bd96365438aad20286
2016-11-06 18:48:23 +01:00
|
|
|
"MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
|
2013-06-20 03:45:08 +02:00
|
|
|
"NAME=%s", m->name,
|
2014-04-25 13:45:15 +02:00
|
|
|
"LEADER="PID_FMT, m->leader,
|
2018-06-04 12:59:22 +02:00
|
|
|
LOG_MESSAGE("Machine %s terminated.", m->name));
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2019-10-29 09:43:07 +01:00
|
|
|
m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
|
|
|
|
}
|
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
machine_unlink(m);
|
2013-06-20 03:45:08 +02:00
|
|
|
machine_add_to_gc_queue(m);
|
|
|
|
|
2015-08-06 15:50:54 +02:00
|
|
|
if (m->started) {
|
2013-06-20 03:45:08 +02:00
|
|
|
machine_send_signal(m, false);
|
2015-08-06 15:50:54 +02:00
|
|
|
m->started = false;
|
|
|
|
}
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-08-06 15:50:54 +02:00
|
|
|
return 0;
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2018-02-15 13:15:45 +01:00
|
|
|
bool machine_may_gc(Machine *m, bool drop_not_started) {
|
2013-06-20 03:45:08 +02:00
|
|
|
assert(m);
|
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (m->class == MACHINE_HOST)
|
2018-02-15 13:15:45 +01:00
|
|
|
return false;
|
2015-08-24 21:05:09 +02:00
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
if (drop_not_started && !m->started)
|
2018-02-15 13:15:45 +01:00
|
|
|
return true;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2013-10-30 02:06:55 +01:00
|
|
|
if (m->scope_job && manager_job_is_active(m->manager, m->scope_job))
|
2018-02-15 13:15:45 +01:00
|
|
|
return false;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
if (m->unit && manager_unit_is_active(m->manager, m->unit))
|
2018-02-15 13:15:45 +01:00
|
|
|
return false;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2018-02-15 13:15:45 +01:00
|
|
|
return true;
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void machine_add_to_gc_queue(Machine *m) {
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (m->in_gc_queue)
|
|
|
|
return;
|
|
|
|
|
2013-10-14 06:10:14 +02:00
|
|
|
LIST_PREPEND(gc_queue, m->manager->machine_gc_queue, m);
|
2013-06-20 03:45:08 +02:00
|
|
|
m->in_gc_queue = true;
|
|
|
|
}
|
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
MachineState machine_get_state(Machine *s) {
|
|
|
|
assert(s);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (s->class == MACHINE_HOST)
|
|
|
|
return MACHINE_RUNNING;
|
|
|
|
|
2015-08-06 15:50:54 +02:00
|
|
|
if (s->stopping)
|
|
|
|
return MACHINE_CLOSING;
|
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
if (s->scope_job)
|
2015-08-06 15:50:54 +02:00
|
|
|
return MACHINE_OPENING;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
return MACHINE_RUNNING;
|
|
|
|
}
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2013-07-02 01:46:30 +02:00
|
|
|
int machine_kill(Machine *m, KillWho who, int signo) {
|
|
|
|
assert(m);
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2015-08-24 21:05:09 +02:00
|
|
|
if (!IN_SET(m->class, MACHINE_VM, MACHINE_CONTAINER))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2014-02-11 17:15:38 +01:00
|
|
|
if (!m->unit)
|
2013-07-02 01:46:30 +02:00
|
|
|
return -ESRCH;
|
2013-06-20 03:45:08 +02:00
|
|
|
|
2014-03-18 04:43:08 +01:00
|
|
|
if (who == KILL_LEADER) {
|
|
|
|
/* If we shall simply kill the leader, do so directly */
|
|
|
|
|
|
|
|
if (kill(m->leader, signo) < 0)
|
|
|
|
return -errno;
|
2014-08-14 19:59:16 +02:00
|
|
|
|
|
|
|
return 0;
|
2014-03-18 04:43:08 +01:00
|
|
|
}
|
|
|
|
|
2014-08-03 07:11:12 +02:00
|
|
|
/* Otherwise, make PID 1 do it for us, for the entire cgroup */
|
2014-03-18 04:43:08 +01:00
|
|
|
return manager_kill_unit(m->manager, m->unit, signo, NULL);
|
2013-06-20 03:45:08 +02:00
|
|
|
}
|
|
|
|
|
2019-06-07 10:17:11 +02:00
|
|
|
int machine_openpt(Machine *m, int flags, char **ret_slave) {
|
2015-08-24 21:05:09 +02:00
|
|
|
assert(m);
|
|
|
|
|
|
|
|
switch (m->class) {
|
|
|
|
|
2019-06-07 10:17:11 +02:00
|
|
|
case MACHINE_HOST:
|
2015-09-05 20:24:52 +02:00
|
|
|
|
2019-06-07 10:17:11 +02:00
|
|
|
return openpt_allocate(flags, ret_slave);
|
2015-08-24 21:05:09 +02:00
|
|
|
|
|
|
|
case MACHINE_CONTAINER:
|
|
|
|
if (m->leader <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2019-06-07 10:17:11 +02:00
|
|
|
return openpt_allocate_in_namespace(m->leader, flags, ret_slave);
|
2015-08-24 21:05:09 +02:00
|
|
|
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-07 23:38:20 +02:00
|
|
|
int machine_open_terminal(Machine *m, const char *path, int mode) {
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
switch (m->class) {
|
|
|
|
|
|
|
|
case MACHINE_HOST:
|
|
|
|
return open_terminal(path, mode);
|
|
|
|
|
|
|
|
case MACHINE_CONTAINER:
|
|
|
|
if (m->leader <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
return open_terminal_in_namespace(m->leader, path, mode);
|
|
|
|
|
|
|
|
default:
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-28 21:17:35 +02:00
|
|
|
void machine_release_unit(Machine *m) {
|
|
|
|
assert(m);
|
|
|
|
|
|
|
|
if (!m->unit)
|
|
|
|
return;
|
|
|
|
|
machined: simplify reference handling for units
Before, we'd unref from machine_stop_unit, still keeping the unit name around,
and only forget the name later, when garbage collecting. If we didn't call
manager_stop_unit(), then we wouldn't do the unref. Let's unref at the same
point where we do garbage collection, so that it is always true that
iff we have the name generated with AddRef=1, then have a reference to the unit,
and as soon as we forget the name, we drop the reference.
This should fix the issue when repeated systemd-nspawn --register=yes fails
with "scope already exists" error.
Incidentally, this fixes an error in the code path where r was used instead of q.
2019-11-21 14:41:32 +01:00
|
|
|
if (m->referenced) {
|
|
|
|
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
r = manager_unref_unit(m->manager, m->unit, &error);
|
|
|
|
if (r < 0)
|
|
|
|
log_warning_errno(r, "Failed to drop reference to machine scope, ignoring: %s",
|
|
|
|
bus_error_message(&error, r));
|
|
|
|
|
|
|
|
m->referenced = false;
|
|
|
|
}
|
|
|
|
|
2015-04-28 21:17:35 +02:00
|
|
|
(void) hashmap_remove(m->manager->machine_units, m->unit);
|
2015-09-08 18:43:11 +02:00
|
|
|
m->unit = mfree(m->unit);
|
2015-04-28 21:17:35 +02:00
|
|
|
}
|
|
|
|
|
2017-02-10 19:44:09 +01:00
|
|
|
int machine_get_uid_shift(Machine *m, uid_t *ret) {
|
2017-12-14 19:02:29 +01:00
|
|
|
char p[STRLEN("/proc//uid_map") + DECIMAL_STR_MAX(pid_t) + 1];
|
2017-02-10 19:44:09 +01:00
|
|
|
uid_t uid_base, uid_shift, uid_range;
|
|
|
|
gid_t gid_base, gid_shift, gid_range;
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
2018-12-17 11:22:38 +01:00
|
|
|
int k, r;
|
2017-02-10 19:44:09 +01:00
|
|
|
|
|
|
|
assert(m);
|
|
|
|
assert(ret);
|
|
|
|
|
|
|
|
/* Return the base UID/GID of the specified machine. Note that this only works for containers with simple
|
|
|
|
* mappings. In most cases setups should be simple like this, and administrators should only care about the
|
|
|
|
* basic offset a container has relative to the host. This is what this function exposes.
|
|
|
|
*
|
|
|
|
* If we encounter any more complex mappings we politely refuse this with ENXIO. */
|
|
|
|
|
|
|
|
if (m->class == MACHINE_HOST) {
|
|
|
|
*ret = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m->class != MACHINE_CONTAINER)
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
|
|
|
xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
|
|
|
|
f = fopen(p, "re");
|
|
|
|
if (!f) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
/* If the file doesn't exist, user namespacing is off in the kernel, return a zero mapping hence. */
|
|
|
|
*ret = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read the first line. There's at least one. */
|
|
|
|
errno = 0;
|
|
|
|
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT "\n", &uid_base, &uid_shift, &uid_range);
|
|
|
|
if (k != 3) {
|
|
|
|
if (ferror(f))
|
2019-07-11 15:42:14 +02:00
|
|
|
return errno_or_else(EIO);
|
2017-02-10 19:44:09 +01:00
|
|
|
|
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
|
|
|
|
if (uid_base != 0)
|
|
|
|
return -ENXIO;
|
|
|
|
/* Insist that at least the nobody user is mapped, everything else is weird, and hence complex, and we don't support it */
|
2017-12-04 17:06:56 +01:00
|
|
|
if (uid_range < UID_NOBODY)
|
2017-02-10 19:44:09 +01:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
/* If there's more than one line, then we don't support this mapping. */
|
2018-12-17 11:22:38 +01:00
|
|
|
r = safe_fgetc(f, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r != 0) /* Insist on EOF */
|
2017-02-10 19:44:09 +01:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
|
|
|
|
f = fopen(p, "re");
|
|
|
|
if (!f)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
/* Read the first line. There's at least one. */
|
|
|
|
errno = 0;
|
|
|
|
k = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
|
|
|
|
if (k != 3) {
|
|
|
|
if (ferror(f))
|
2019-07-11 15:42:14 +02:00
|
|
|
return errno_or_else(EIO);
|
2017-02-10 19:44:09 +01:00
|
|
|
|
|
|
|
return -EBADMSG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there's more than one line, then we don't support this file. */
|
2018-12-17 11:22:38 +01:00
|
|
|
r = safe_fgetc(f, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
if (r != 0) /* Insist on EOF */
|
2017-02-10 19:44:09 +01:00
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
/* If the UID and GID mapping doesn't match, we don't support this mapping. */
|
|
|
|
if (uid_base != (uid_t) gid_base)
|
|
|
|
return -ENXIO;
|
|
|
|
if (uid_shift != (uid_t) gid_shift)
|
|
|
|
return -ENXIO;
|
|
|
|
if (uid_range != (uid_t) gid_range)
|
|
|
|
return -ENXIO;
|
|
|
|
|
|
|
|
*ret = uid_shift;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-07-07 11:58:06 +02:00
|
|
|
static int machine_owns_uid_internal(
|
|
|
|
Machine *machine,
|
|
|
|
const char *map_file, /* "uid_map" or "gid_map" */
|
|
|
|
uid_t uid,
|
|
|
|
uid_t *ret_internal_uid) {
|
|
|
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
|
|
|
|
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
|
|
|
|
|
|
|
assert(machine);
|
|
|
|
|
|
|
|
/* Checks if the specified host UID is owned by the machine, and returns the UID it maps to
|
|
|
|
* internally in the machine */
|
|
|
|
|
|
|
|
if (machine->class != MACHINE_CONTAINER)
|
|
|
|
goto negative;
|
|
|
|
|
|
|
|
p = procfs_file_alloca(machine->leader, map_file);
|
|
|
|
f = fopen(p, "re");
|
|
|
|
if (!f) {
|
|
|
|
log_debug_errno(errno, "Failed to open %s, ignoring.", p);
|
|
|
|
goto negative;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
uid_t uid_base, uid_shift, uid_range, converted;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
|
|
|
|
if (k < 0 && feof(f))
|
|
|
|
break;
|
|
|
|
if (k != 3) {
|
|
|
|
if (ferror(f))
|
|
|
|
return errno_or_else(EIO);
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The private user namespace is disabled, ignoring. */
|
|
|
|
if (uid_shift == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (uid < uid_shift || uid >= uid_shift + uid_range)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
converted = (uid - uid_shift + uid_base);
|
|
|
|
if (!uid_is_valid(converted))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (ret_internal_uid)
|
|
|
|
*ret_internal_uid = converted;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
negative:
|
|
|
|
if (ret_internal_uid)
|
|
|
|
*ret_internal_uid = UID_INVALID;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int machine_owns_uid(Machine *machine, uid_t uid, uid_t *ret_internal_uid) {
|
|
|
|
return machine_owns_uid_internal(machine, "uid_map", uid, ret_internal_uid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int machine_owns_gid(Machine *machine, gid_t gid, gid_t *ret_internal_gid) {
|
|
|
|
return machine_owns_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_internal_gid);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int machine_translate_uid_internal(
|
|
|
|
Machine *machine,
|
|
|
|
const char *map_file, /* "uid_map" or "gid_map" */
|
|
|
|
uid_t uid,
|
|
|
|
uid_t *ret_host_uid) {
|
|
|
|
|
|
|
|
_cleanup_fclose_ FILE *f = NULL;
|
|
|
|
const char *p;
|
|
|
|
|
|
|
|
/* This is a generic implementation for both uids and gids, under the assumptions they have the same types and semantics. */
|
|
|
|
assert_cc(sizeof(uid_t) == sizeof(gid_t));
|
|
|
|
|
|
|
|
assert(machine);
|
|
|
|
assert(uid_is_valid(uid));
|
|
|
|
|
|
|
|
if (machine->class != MACHINE_CONTAINER)
|
|
|
|
return -ESRCH;
|
|
|
|
|
|
|
|
/* Translates a machine UID into a host UID */
|
|
|
|
|
|
|
|
p = procfs_file_alloca(machine->leader, map_file);
|
|
|
|
f = fopen(p, "re");
|
|
|
|
if (!f)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
uid_t uid_base, uid_shift, uid_range, converted;
|
|
|
|
int k;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
k = fscanf(f, UID_FMT " " UID_FMT " " UID_FMT, &uid_base, &uid_shift, &uid_range);
|
|
|
|
if (k < 0 && feof(f))
|
|
|
|
break;
|
|
|
|
if (k != 3) {
|
|
|
|
if (ferror(f))
|
|
|
|
return errno_or_else(EIO);
|
|
|
|
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (uid < uid_base || uid >= uid_base + uid_range)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
converted = uid - uid_base + uid_shift;
|
|
|
|
if (!uid_is_valid(converted))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (ret_host_uid)
|
|
|
|
*ret_host_uid = converted;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ESRCH;
|
|
|
|
}
|
|
|
|
|
|
|
|
int machine_translate_uid(Machine *machine, gid_t uid, gid_t *ret_host_uid) {
|
|
|
|
return machine_translate_uid_internal(machine, "uid_map", uid, ret_host_uid);
|
|
|
|
}
|
|
|
|
|
|
|
|
int machine_translate_gid(Machine *machine, gid_t gid, gid_t *ret_host_gid) {
|
|
|
|
return machine_translate_uid_internal(machine, "gid_map", (uid_t) gid, (uid_t*) ret_host_gid);
|
|
|
|
}
|
|
|
|
|
2013-06-20 03:45:08 +02:00
|
|
|
static const char* const machine_class_table[_MACHINE_CLASS_MAX] = {
|
|
|
|
[MACHINE_CONTAINER] = "container",
|
2015-08-24 21:05:09 +02:00
|
|
|
[MACHINE_VM] = "vm",
|
|
|
|
[MACHINE_HOST] = "host",
|
2013-06-20 03:45:08 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(machine_class, MachineClass);
|
2013-07-02 01:46:30 +02:00
|
|
|
|
|
|
|
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);
|
2013-07-02 03:47:23 +02:00
|
|
|
|
|
|
|
static const char* const kill_who_table[_KILL_WHO_MAX] = {
|
|
|
|
[KILL_LEADER] = "leader",
|
|
|
|
[KILL_ALL] = "all"
|
|
|
|
};
|
|
|
|
|
|
|
|
DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);
|