#include "alloc-util.h"
#include "bus-error.h"
+#include "bus-locator.h"
+#include "bus-unit-util.h"
#include "bus-util.h"
#include "env-file.h"
#include "errno-util.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
+#include "fs-util.h"
#include "hashmap.h"
#include "machine-dbus.h"
#include "machine.h"
-#include "mkdir.h"
+#include "mkdir-label.h"
#include "parse-util.h"
#include "path-util.h"
#include "process-util.h"
#include "string-table.h"
#include "terminal-util.h"
#include "tmpfile-util.h"
+#include "uid-range.h"
#include "unit-name.h"
#include "user-util.h"
-#include "util.h"
-Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
- Machine *m;
+DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
+
+int machine_new(Manager *manager, MachineClass class, const char *name, Machine **ret) {
+ _cleanup_(machine_freep) Machine *m = NULL;
+ int r;
assert(manager);
assert(class < _MACHINE_CLASS_MAX);
assert(name);
+ assert(ret);
/* 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. */
- m = new0(Machine, 1);
+ m = new(Machine, 1);
if (!m)
- return NULL;
+ return -ENOMEM;
+
+ *m = (Machine) {
+ .leader = PIDREF_NULL,
+ };
m->name = strdup(name);
if (!m->name)
- goto fail;
+ return -ENOMEM;
if (class != MACHINE_HOST) {
m->state_file = path_join("/run/systemd/machines", m->name);
if (!m->state_file)
- goto fail;
+ return -ENOMEM;
}
m->class = class;
- if (hashmap_put(manager->machines, m->name, m) < 0)
- goto fail;
+ r = hashmap_put(manager->machines, m->name, m);
+ if (r < 0)
+ return r;
m->manager = manager;
- return m;
-
-fail:
- free(m->state_file);
- free(m->name);
- return mfree(m);
+ *ret = TAKE_PTR(m);
+ return 0;
}
Machine* machine_free(Machine *m) {
if (m->manager->host_machine == m)
m->manager->host_machine = NULL;
- if (m->leader > 0)
- (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
+ if (pidref_is_set(&m->leader)) {
+ (void) hashmap_remove_value(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
+ pidref_done(&m->leader);
+ }
sd_bus_message_unref(m->create_message);
}
int machine_save(Machine *m) {
- _cleanup_free_ char *temp_path = NULL;
+ _cleanup_(unlink_and_freep) char *temp_path = NULL;
_cleanup_fclose_ FILE *f = NULL;
int r;
if (!sd_id128_is_null(m->id))
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 (pidref_is_set(&m->leader))
+ fprintf(f, "LEADER="PID_FMT"\n", m->leader.pid);
if (m->class != _MACHINE_CLASS_INVALID)
fprintf(f, "CLASS=%s\n", machine_class_to_string(m->class));
goto fail;
}
+ temp_path = mfree(temp_path);
+
if (m->unit) {
char *sl;
fail:
(void) unlink(m->state_file);
- if (temp_path)
- (void) unlink(temp_path);
-
return log_error_errno(r, "Failed to save machine data %s: %m", m->state_file);
}
"REALTIME", &realtime,
"MONOTONIC", &monotonic,
"NETIF", &netif);
- if (r < 0) {
- if (r == -ENOENT)
- return 0;
-
+ if (r == -ENOENT)
+ return 0;
+ if (r < 0)
return log_error_errno(r, "Failed to read %s: %m", m->state_file);
- }
if (id)
- sd_id128_from_string(id, &m->id);
+ (void) sd_id128_from_string(id, &m->id);
- if (leader)
- parse_pid(leader, &m->leader);
+ if (leader) {
+ pidref_done(&m->leader);
+ r = pidref_set_pidstr(&m->leader, leader);
+ if (r < 0)
+ log_debug_errno(r, "Failed to set leader PID to '%s', ignoring: %m", leader);
+ }
if (class) {
MachineClass c;
(void) deserialize_usec(monotonic, &m->timestamp.monotonic);
if (netif) {
- size_t allocated = 0, nr = 0;
- const char *p;
_cleanup_free_ int *ni = NULL;
+ size_t nr = 0;
+ const char *p;
p = netif;
for (;;) {
if (r < 0)
continue;
- if (!GREEDY_REALLOC(ni, allocated, nr + 1))
+ if (!GREEDY_REALLOC(ni, nr + 1))
return log_oom();
ni[nr++] = r;
}
- free(m->netif);
- m->netif = TAKE_PTR(ni);
+ free_and_replace(m->netif, ni);
m->n_netif = nr;
}
int r;
assert(machine);
- assert(machine->leader > 0);
+ assert(pidref_is_set(&machine->leader));
assert(!machine->unit);
escaped = unit_name_escape(machine->name);
if (!unit)
return log_oom();
- r = sd_bus_message_new_method_call(
+ r = bus_message_new_method_call(
machine->manager->bus,
&m,
- "org.freedesktop.systemd1",
- "/org/freedesktop/systemd1",
- "org.freedesktop.systemd1.Manager",
+ bus_systemd_mgr,
"StartTransientUnit");
if (r < 0)
return r;
if (r < 0)
return r;
- r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)(sv)",
- "PIDs", "au", 1, machine->leader,
+ r = bus_append_scope_pidref(m, &machine->leader, /* allow_pidfd = */ true);
+ if (r < 0)
+ return r;
+
+ r = sd_bus_message_append(m, "(sv)(sv)(sv)(sv)",
"Delegate", "b", 1,
"CollectMode", "s", "inactive-or-failed",
"AddRef", "b", 1,
if (m->started)
return 0;
- r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader), m);
+ r = hashmap_put(m->manager->machine_leaders, PID_TO_PTR(m->leader.pid), m);
if (r < 0)
return r;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_MACHINE_START_STR,
"NAME=%s", m->name,
- "LEADER="PID_FMT, m->leader,
+ "LEADER="PID_FMT, m->leader.pid,
LOG_MESSAGE("New machine %s.", m->name));
if (!dual_timestamp_is_set(&m->timestamp))
- dual_timestamp_get(&m->timestamp);
+ dual_timestamp_now(&m->timestamp);
m->started = true;
log_struct(LOG_INFO,
"MESSAGE_ID=" SD_MESSAGE_MACHINE_STOP_STR,
"NAME=%s", m->name,
- "LEADER="PID_FMT, m->leader,
+ "LEADER="PID_FMT, m->leader.pid,
LOG_MESSAGE("Machine %s terminated.", m->name));
m->stopping = true; /* The machine is supposed to be going away. Don't try to kill it. */
if (!m->unit)
return -ESRCH;
- if (who == KILL_LEADER) {
- /* If we shall simply kill the leader, do so directly */
-
- if (kill(m->leader, signo) < 0)
- return -errno;
-
- return 0;
- }
+ if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
+ return pidref_kill(&m->leader, signo);
/* Otherwise, make PID 1 do it for us, for the entire cgroup */
return manager_kill_unit(m->manager, m->unit, signo, NULL);
switch (m->class) {
case MACHINE_HOST:
-
return openpt_allocate(flags, ret_slave);
case MACHINE_CONTAINER:
- if (m->leader <= 0)
+ if (!pidref_is_set(&m->leader))
return -EINVAL;
- return openpt_allocate_in_namespace(m->leader, flags, ret_slave);
+ return openpt_allocate_in_namespace(m->leader.pid, flags, ret_slave);
default:
return -EOPNOTSUPP;
return open_terminal(path, mode);
case MACHINE_CONTAINER:
- if (m->leader <= 0)
+ if (!pidref_is_set(&m->leader))
return -EINVAL;
- return open_terminal_in_namespace(m->leader, path, mode);
+ return open_terminal_in_namespace(m->leader.pid, path, mode);
default:
return -EOPNOTSUPP;
uid_t uid_base, uid_shift, uid_range;
gid_t gid_base, gid_shift, gid_range;
_cleanup_fclose_ FILE *f = NULL;
- int k, r;
+ int r;
assert(m);
assert(ret);
if (m->class != MACHINE_CONTAINER)
return -EOPNOTSUPP;
- xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader);
+ xsprintf(p, "/proc/" PID_FMT "/uid_map", m->leader.pid);
f = fopen(p, "re");
if (!f) {
if (errno == ENOENT) {
}
/* 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))
- return errno_or_else(EIO);
-
- return -EBADMSG;
- }
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r < 0)
+ return r;
/* Not a mapping starting at 0? Then it's a complex mapping we can't expose here. */
if (uid_base != 0)
fclose(f);
- xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader);
+ xsprintf(p, "/proc/" PID_FMT "/gid_map", m->leader.pid);
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))
- return errno_or_else(EIO);
-
+ r = fscanf(f, GID_FMT " " GID_FMT " " GID_FMT "\n", &gid_base, &gid_shift, &gid_range);
+ if (r == EOF)
+ return errno_or_else(ENOMSG);
+ assert(r >= 0);
+ if (r != 3)
return -EBADMSG;
- }
/* If there's more than one line, then we don't support this file. */
r = safe_fgetc(f, NULL);
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* 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));
if (machine->class != MACHINE_CONTAINER)
goto negative;
- p = procfs_file_alloca(machine->leader, map_file);
+ p = procfs_file_alloca(machine->leader.pid, map_file);
f = fopen(p, "re");
if (!f) {
log_debug_errno(errno, "Failed to open %s, ignoring.", p);
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))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
/* The private user namespace is disabled, ignoring. */
if (uid_shift == 0)
_cleanup_fclose_ FILE *f = NULL;
const char *p;
+ int r;
/* 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));
/* Translates a machine UID into a host UID */
- p = procfs_file_alloca(machine->leader, map_file);
+ p = procfs_file_alloca(machine->leader.pid, 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))
+ r = uid_map_read_one(f, &uid_base, &uid_shift, &uid_range);
+ if (r == -ENOMSG)
break;
- if (k != 3) {
- if (ferror(f))
- return errno_or_else(EIO);
-
- return -EIO;
- }
+ if (r < 0)
+ return r;
if (uid < uid_base || uid >= uid_base + uid_range)
continue;
if (ret_host_uid)
*ret_host_uid = converted;
+
return 0;
}