]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/machine/machine.c
build(deps): bump redhat-plumbers-in-action/differential-shellcheck
[thirdparty/systemd.git] / src / machine / machine.c
index ca43c977b00889e5632619a40e652a23a32b619a..0eb9a55e5f74dde6cd34dbecec1fa99f09e46808 100644 (file)
@@ -9,6 +9,7 @@
 #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"
@@ -17,6 +18,7 @@
 #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 "path-util.h"
 #include "process-util.h"
 #include "serialize.h"
+#include "socket-util.h"
 #include "special.h"
 #include "stdio-util.h"
 #include "string-table.h"
+#include "string-util.h"
 #include "terminal-util.h"
 #include "tmpfile-util.h"
+#include "uid-range.h"
 #include "unit-name.h"
 #include "user-util.h"
 
-DEFINE_TRIVIAL_CLEANUP_FUNC(Machine*, machine_free);
-
-Machine* machine_new(Manager *manager, MachineClass class, const char *name) {
+int machine_new(MachineClass class, const char *name, Machine **ret) {
         _cleanup_(machine_freep) Machine *m = NULL;
 
-        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->name = strdup(name);
-        if (!m->name)
-                return NULL;
+        *m = (Machine) {
+                .leader = PIDREF_NULL,
+                .vsock_cid = VMADDR_CID_ANY,
+        };
 
-        if (class != MACHINE_HOST) {
-                m->state_file = path_join("/run/systemd/machines", m->name);
-                if (!m->state_file)
-                        return NULL;
+        if (name) {
+                m->name = strdup(name);
+                if (!m->name)
+                        return -ENOMEM;
         }
 
         m->class = class;
 
-        if (hashmap_put(manager->machines, m->name, m) < 0)
-                return NULL;
+        *ret = TAKE_PTR(m);
+        return 0;
+}
+
+int machine_link(Manager *manager, Machine *machine) {
+        int r;
+
+        assert(manager);
+        assert(machine);
+
+        if (machine->manager)
+                return -EEXIST;
+        if (!machine->name)
+                return -EINVAL;
+
+        if (machine->class != MACHINE_HOST) {
+                char *temp = path_join("/run/systemd/machines", machine->name);
+                if (!temp)
+                        return -ENOMEM;
 
-        m->manager = manager;
+                free_and_replace(machine->state_file, temp);
+        }
+
+        r = hashmap_put(manager->machines, machine->name, machine);
+        if (r < 0)
+                return r;
 
-        return TAKE_PTR(m);
+        machine->manager = manager;
+
+        return 0;
 }
 
 Machine* machine_free(Machine *m) {
@@ -77,33 +104,41 @@ Machine* machine_free(Machine *m) {
         while (m->operations)
                 operation_free(m->operations);
 
-        if (m->in_gc_queue)
+        if (m->in_gc_queue) {
+                assert(m->manager);
                 LIST_REMOVE(gc_queue, m->manager->machine_gc_queue, m);
+        }
 
-        machine_release_unit(m);
-
-        free(m->scope_job);
+        if (m->manager) {
+                machine_release_unit(m);
 
-        (void) hashmap_remove(m->manager->machines, m->name);
+                (void) hashmap_remove(m->manager->machines, m->name);
 
-        if (m->manager->host_machine == m)
-                m->manager->host_machine = NULL;
+                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)) {
+                if (m->manager)
+                        (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);
 
         free(m->name);
+        free(m->scope_job);
         free(m->state_file);
         free(m->service);
         free(m->root_directory);
         free(m->netif);
+        free(m->ssh_address);
+        free(m->ssh_private_key_path);
         return mfree(m);
 }
 
 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;
 
@@ -170,8 +205,8 @@ int machine_save(Machine *m) {
         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));
@@ -207,6 +242,8 @@ int machine_save(Machine *m) {
                 goto fail;
         }
 
+        temp_path = mfree(temp_path);
+
         if (m->unit) {
                 char *sl;
 
@@ -222,9 +259,6 @@ int machine_save(Machine *m) {
 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);
 }
 
@@ -268,10 +302,14 @@ int machine_load(Machine *m) {
                 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;
@@ -324,16 +362,18 @@ int machine_load(Machine *m) {
 
 static int machine_start_scope(
                 Machine *machine,
+                bool allow_pidfd,
                 sd_bus_message *more_properties,
                 sd_bus_error *error) {
 
         _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL, *reply = NULL;
+        _cleanup_(sd_bus_error_free) sd_bus_error e = SD_BUS_ERROR_NULL;
         _cleanup_free_ char *escaped = NULL, *unit = NULL;
         const char *description;
         int r;
 
         assert(machine);
-        assert(machine->leader > 0);
+        assert(pidref_is_set(&machine->leader));
         assert(!machine->unit);
 
         escaped = unit_name_escape(machine->name);
@@ -369,8 +409,11 @@ static int machine_start_scope(
         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);
+        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,
@@ -392,9 +435,16 @@ static int machine_start_scope(
         if (r < 0)
                 return r;
 
-        r = sd_bus_call(NULL, m, 0, error, &reply);
-        if (r < 0)
-                return r;
+        r = sd_bus_call(NULL, m, 0, &e, &reply);
+        if (r < 0) {
+                /* If this failed with a property we couldn't write, this is quite likely because the server
+                 * doesn't support PIDFDs yet, let's try without. */
+                if (allow_pidfd &&
+                    sd_bus_error_has_names(&e, SD_BUS_ERROR_UNKNOWN_PROPERTY, SD_BUS_ERROR_PROPERTY_READ_ONLY))
+                        return machine_start_scope(machine, /* allow_pidfd = */ false, more_properties, error);
+
+                return sd_bus_error_move(error, &e);
+        }
 
         machine->unit = TAKE_PTR(unit);
         machine->referenced = true;
@@ -414,7 +464,7 @@ static int machine_ensure_scope(Machine *m, sd_bus_message *properties, sd_bus_e
         assert(m->class != MACHINE_HOST);
 
         if (!m->unit) {
-                r = machine_start_scope(m, properties, error);
+                r = machine_start_scope(m, /* allow_pidfd = */ true, properties, error);
                 if (r < 0)
                         return log_error_errno(r, "Failed to start machine scope: %s", bus_error_message(error, r));
         }
@@ -436,7 +486,7 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
         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;
 
@@ -448,11 +498,11 @@ int machine_start(Machine *m, sd_bus_message *properties, sd_bus_error *error) {
         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;
 
@@ -499,7 +549,7 @@ int machine_finalize(Machine *m) {
                 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. */
@@ -569,7 +619,7 @@ int machine_kill(Machine *m, KillWho who, int signo) {
                 return -ESRCH;
 
         if (who == KILL_LEADER) /* If we shall simply kill the leader, do so directly */
-                return RET_NERRNO(kill(m->leader, signo));
+                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);
@@ -581,14 +631,13 @@ int machine_openpt(Machine *m, int flags, char **ret_slave) {
         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;
@@ -604,10 +653,10 @@ int machine_open_terminal(Machine *m, const char *path, int mode) {
                 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;
@@ -626,8 +675,9 @@ void machine_release_unit(Machine *m) {
 
                 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));
+                        log_full_errno(ERRNO_IS_DISCONNECT(r) ? LOG_DEBUG : LOG_WARNING, r,
+                                       "Failed to drop reference to machine scope, ignoring: %s",
+                                       bus_error_message(&error, r));
 
                 m->referenced = false;
         }
@@ -641,7 +691,7 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         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);
@@ -660,7 +710,7 @@ int machine_get_uid_shift(Machine *m, uid_t *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) {
@@ -673,14 +723,9 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
         }
 
         /* 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)
@@ -698,20 +743,19 @@ int machine_get_uid_shift(Machine *m, uid_t *ret) {
 
         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);
@@ -740,6 +784,7 @@ static int machine_owns_uid_internal(
 
         _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));
@@ -752,7 +797,7 @@ static int machine_owns_uid_internal(
         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);
@@ -761,18 +806,12 @@ static int machine_owns_uid_internal(
 
         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)
@@ -814,6 +853,7 @@ static int machine_translate_uid_internal(
 
         _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));
@@ -826,25 +866,19 @@ static int machine_translate_uid_internal(
 
         /* 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;
@@ -855,6 +889,7 @@ static int machine_translate_uid_internal(
 
                 if (ret_host_uid)
                         *ret_host_uid = converted;
+
                 return 0;
         }