]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/core/execute.c
Merge pull request #27033 from dtardon/array-cleanup
[thirdparty/systemd.git] / src / core / execute.c
index c41f459f3dfa1df38a8d75cc99c976a6d32aa5fa..93024b1ce4ca00896e28afe8322e48faa11fc0d4 100644 (file)
@@ -2210,7 +2210,7 @@ bool exec_needs_mount_namespace(
         if (!IN_SET(context->mount_propagation_flag, 0, MS_SHARED))
                 return true;
 
-        if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
+        if (context->private_tmp && runtime && runtime->shared && (runtime->shared->tmp_dir || runtime->shared->var_tmp_dir))
                 return true;
 
         if (context->private_devices ||
@@ -2412,6 +2412,8 @@ static int setup_private_users(uid_t ouid, gid_t ogid, uid_t uid, gid_t gid) {
 }
 
 static bool exec_directory_is_private(const ExecContext *context, ExecDirectoryType type) {
+        assert(context);
+
         if (!context->dynamic_user)
                 return false;
 
@@ -3728,16 +3730,16 @@ static int apply_mount_namespace(
                  * that is sticky, and that's the one we want to use here.
                  * This does not apply when we are using /run/systemd/empty as fallback. */
 
-                if (context->private_tmp && runtime) {
-                        if (streq_ptr(runtime->tmp_dir, RUN_SYSTEMD_EMPTY))
-                                tmp_dir = runtime->tmp_dir;
-                        else if (runtime->tmp_dir)
-                                tmp_dir = strjoina(runtime->tmp_dir, "/tmp");
+                if (context->private_tmp && runtime && runtime->shared) {
+                        if (streq_ptr(runtime->shared->tmp_dir, RUN_SYSTEMD_EMPTY))
+                                tmp_dir = runtime->shared->tmp_dir;
+                        else if (runtime->shared->tmp_dir)
+                                tmp_dir = strjoina(runtime->shared->tmp_dir, "/tmp");
 
-                        if (streq_ptr(runtime->var_tmp_dir, RUN_SYSTEMD_EMPTY))
-                                var_tmp_dir = runtime->var_tmp_dir;
-                        else if (runtime->var_tmp_dir)
-                                var_tmp_dir = strjoina(runtime->var_tmp_dir, "/tmp");
+                        if (streq_ptr(runtime->shared->var_tmp_dir, RUN_SYSTEMD_EMPTY))
+                                var_tmp_dir = runtime->shared->var_tmp_dir;
+                        else if (runtime->shared->var_tmp_dir)
+                                var_tmp_dir = strjoina(runtime->shared->var_tmp_dir, "/tmp");
                 }
 
                 ns_info = (NamespaceInfo) {
@@ -4035,7 +4037,6 @@ static void append_socket_pair(int *array, size_t *n, const int pair[static 2])
 static int close_remaining_fds(
                 const ExecParameters *params,
                 const ExecRuntime *runtime,
-                const DynamicCreds *dcreds,
                 int user_lookup_fd,
                 int socket_fd,
                 const int *fds, size_t n_fds) {
@@ -4059,16 +4060,16 @@ static int close_remaining_fds(
                 n_dont_close += n_fds;
         }
 
-        if (runtime) {
-                append_socket_pair(dont_close, &n_dont_close, runtime->netns_storage_socket);
-                append_socket_pair(dont_close, &n_dont_close, runtime->ipcns_storage_socket);
+        if (runtime && runtime->shared) {
+                append_socket_pair(dont_close, &n_dont_close, runtime->shared->netns_storage_socket);
+                append_socket_pair(dont_close, &n_dont_close, runtime->shared->ipcns_storage_socket);
         }
 
-        if (dcreds) {
-                if (dcreds->user)
-                        append_socket_pair(dont_close, &n_dont_close, dcreds->user->storage_socket);
-                if (dcreds->group)
-                        append_socket_pair(dont_close, &n_dont_close, dcreds->group->storage_socket);
+        if (runtime && runtime->dynamic_creds) {
+                if (runtime->dynamic_creds->user)
+                        append_socket_pair(dont_close, &n_dont_close, runtime->dynamic_creds->user->storage_socket);
+                if (runtime->dynamic_creds->group)
+                        append_socket_pair(dont_close, &n_dont_close, runtime->dynamic_creds->group->storage_socket);
         }
 
         if (user_lookup_fd >= 0)
@@ -4376,13 +4377,28 @@ static int collect_open_file_fds(
         return 0;
 }
 
+static void log_command_line(Unit *unit, const char *msg, const char *executable, char **argv) {
+        assert(unit);
+        assert(msg);
+        assert(executable);
+
+        if (!DEBUG_LOGGING)
+                return;
+
+        _cleanup_free_ char *cmdline = quote_command_line(argv, SHELL_ESCAPE_EMPTY);
+
+        log_unit_struct(unit, LOG_DEBUG,
+                        "EXECUTABLE=%s", executable,
+                        LOG_UNIT_MESSAGE(unit, "%s: %s", msg, strnull(cmdline)),
+                        LOG_UNIT_INVOCATION_ID(unit));
+}
+
 static int exec_child(
                 Unit *unit,
                 const ExecCommand *command,
                 const ExecContext *context,
                 const ExecParameters *params,
                 ExecRuntime *runtime,
-                DynamicCreds *dcreds,
                 const CGroupContext *cgroup_context,
                 int socket_fd,
                 const int named_iofds[static 3],
@@ -4515,7 +4531,7 @@ static int exec_child(
         }
 #endif
 
-        r = close_remaining_fds(params, runtime, dcreds, user_lookup_fd, socket_fd, keep_fds, n_keep_fds);
+        r = close_remaining_fds(params, runtime, user_lookup_fd, socket_fd, keep_fds, n_keep_fds);
         if (r < 0) {
                 *exit_status = EXIT_FDS;
                 return log_unit_error_errno(unit, r, "Failed to close unwanted file descriptors: %m");
@@ -4561,7 +4577,7 @@ static int exec_child(
                 return log_unit_error_errno(unit, errno, "Failed to update environment: %m");
         }
 
-        if (context->dynamic_user && dcreds) {
+        if (context->dynamic_user && runtime && runtime->dynamic_creds) {
                 _cleanup_strv_free_ char **suggested_paths = NULL;
 
                 /* On top of that, make sure we bypass our own NSS module nss-systemd comprehensively for any NSS
@@ -4577,7 +4593,7 @@ static int exec_child(
                         return log_oom();
                 }
 
-                r = dynamic_creds_realize(dcreds, suggested_paths, &uid, &gid);
+                r = dynamic_creds_realize(runtime->dynamic_creds, suggested_paths, &uid, &gid);
                 if (r < 0) {
                         *exit_status = EXIT_USER;
                         if (r == -EILSEQ)
@@ -4596,8 +4612,8 @@ static int exec_child(
                         return log_unit_error_errno(unit, SYNTHETIC_ERRNO(ESRCH), "GID validation failed for \""GID_FMT"\"", gid);
                 }
 
-                if (dcreds->user)
-                        username = dcreds->user->name;
+                if (runtime->dynamic_creds->user)
+                        username = runtime->dynamic_creds->user->name;
 
         } else {
                 r = get_fixed_user(context, &username, &uid, &gid, &home, &shell);
@@ -4635,8 +4651,7 @@ static int exec_child(
                 return log_unit_error_errno(unit, r, "Failed to determine $HOME for user: %m");
         }
 
-        /* If a socket is connected to STDIN/STDOUT/STDERR, we
-         * must sure to drop O_NONBLOCK */
+        /* If a socket is connected to STDIN/STDOUT/STDERR, we must drop O_NONBLOCK */
         if (socket_fd >= 0)
                 (void) fd_nonblock(socket_fd, false);
 
@@ -4664,16 +4679,16 @@ static int exec_child(
                 }
         }
 
-        if (context->network_namespace_path && runtime && runtime->netns_storage_socket[0] >= 0) {
-                r = open_shareable_ns_path(runtime->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
+        if (context->network_namespace_path && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
+                r = open_shareable_ns_path(runtime->shared->netns_storage_socket, context->network_namespace_path, CLONE_NEWNET);
                 if (r < 0) {
                         *exit_status = EXIT_NETWORK;
                         return log_unit_error_errno(unit, r, "Failed to open network namespace path %s: %m", context->network_namespace_path);
                 }
         }
 
-        if (context->ipc_namespace_path && runtime && runtime->ipcns_storage_socket[0] >= 0) {
-                r = open_shareable_ns_path(runtime->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC);
+        if (context->ipc_namespace_path && runtime && runtime->shared && runtime->shared->ipcns_storage_socket[0] >= 0) {
+                r = open_shareable_ns_path(runtime->shared->ipcns_storage_socket, context->ipc_namespace_path, CLONE_NEWIPC);
                 if (r < 0) {
                         *exit_status = EXIT_NAMESPACE;
                         return log_unit_error_errno(unit, r, "Failed to open IPC namespace path %s: %m", context->ipc_namespace_path);
@@ -5023,10 +5038,10 @@ static int exec_child(
                 }
         }
 
-        if (exec_needs_network_namespace(context) && runtime && runtime->netns_storage_socket[0] >= 0) {
+        if (exec_needs_network_namespace(context) && runtime && runtime->shared && runtime->shared->netns_storage_socket[0] >= 0) {
 
                 if (ns_type_supported(NAMESPACE_NET)) {
-                        r = setup_shareable_ns(runtime->netns_storage_socket, CLONE_NEWNET);
+                        r = setup_shareable_ns(runtime->shared->netns_storage_socket, CLONE_NEWNET);
                         if (r < 0) {
                                 if (ERRNO_IS_PRIVILEGE(r))
                                         log_unit_warning_errno(unit, r,
@@ -5044,10 +5059,10 @@ static int exec_child(
                         log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring.");
         }
 
-        if (exec_needs_ipc_namespace(context) && runtime && runtime->ipcns_storage_socket[0] >= 0) {
+        if (exec_needs_ipc_namespace(context) && runtime && runtime->shared && runtime->shared->ipcns_storage_socket[0] >= 0) {
 
                 if (ns_type_supported(NAMESPACE_IPC)) {
-                        r = setup_shareable_ns(runtime->ipcns_storage_socket, CLONE_NEWIPC);
+                        r = setup_shareable_ns(runtime->shared->ipcns_storage_socket, CLONE_NEWIPC);
                         if (r == -EPERM)
                                 log_unit_warning_errno(unit, r,
                                                        "PrivateIPC=yes is configured, but IPC namespace setup failed, ignoring: %m");
@@ -5177,9 +5192,10 @@ static int exec_child(
         }
 #endif
 
-        /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that we are
-         * more aggressive this time since socket_fd and the netns and ipcns fds we don't need anymore. We do keep the exec_fd
-         * however if we have it as we want to keep it open until the final execve(). */
+        /* We repeat the fd closing here, to make sure that nothing is leaked from the PAM modules. Note that
+         * we are more aggressive this time, since we don't need socket_fd and the netns and ipcns fds any
+         * more. We do keep exec_fd however, if we have it, since we need to keep it open until the final
+         * execve(). */
 
         r = close_all_fds(keep_fds, n_keep_fds);
         if (r >= 0)
@@ -5201,9 +5217,9 @@ static int exec_child(
         if (needs_sandboxing) {
                 uint64_t bset;
 
-                /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly
-                 * requested. (Note this is placed after the general resource limit initialization, see
-                 * above, in order to take precedence.) */
+                /* Set the RTPRIO resource limit to 0, but only if nothing else was explicitly requested.
+                 * (Note this is placed after the general resource limit initialization, see above, in order
+                 * to take precedence.) */
                 if (context->restrict_realtime && !context->rlimit[RLIMIT_RTPRIO]) {
                         if (setrlimit(RLIMIT_RTPRIO, &RLIMIT_MAKE_CONST(0)) < 0) {
                                 *exit_status = EXIT_LIMITS;
@@ -5476,19 +5492,7 @@ static int exec_child(
         } else
                 final_argv = command->argv;
 
-        if (DEBUG_LOGGING) {
-                _cleanup_free_ char *line = NULL;
-
-                line = quote_command_line(final_argv, SHELL_ESCAPE_EMPTY);
-                if (!line) {
-                        *exit_status = EXIT_MEMORY;
-                        return log_oom();
-                }
-
-                log_unit_struct(unit, LOG_DEBUG,
-                                "EXECUTABLE=%s", executable,
-                                LOG_UNIT_MESSAGE(unit, "Executing: %s", line));
-        }
+        log_command_line(unit, "Executing", executable, final_argv);
 
         if (exec_fd >= 0) {
                 uint8_t hot = 1;
@@ -5528,7 +5532,6 @@ int exec_spawn(Unit *unit,
                const ExecContext *context,
                const ExecParameters *params,
                ExecRuntime *runtime,
-               DynamicCreds *dcreds,
                const CGroupContext *cgroup_context,
                pid_t *ret) {
 
@@ -5536,7 +5539,6 @@ int exec_spawn(Unit *unit,
         _cleanup_free_ char *subcgroup_path = NULL;
         _cleanup_strv_free_ char **files_env = NULL;
         size_t n_storage_fds = 0, n_socket_fds = 0;
-        _cleanup_free_ char *line = NULL;
         pid_t pid;
 
         assert(unit);
@@ -5574,21 +5576,13 @@ int exec_spawn(Unit *unit,
         if (r < 0)
                 return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
 
-        line = quote_command_line(command->argv, SHELL_ESCAPE_EMPTY);
-        if (!line)
-                return log_oom();
-
         /* Fork with up-to-date SELinux label database, so the child inherits the up-to-date db
            and, until the next SELinux policy changes, we save further reloads in future children. */
         mac_selinux_maybe_reload();
 
-        log_unit_struct(unit, LOG_DEBUG,
-                        LOG_UNIT_MESSAGE(unit, "About to execute %s", line),
-                        "EXECUTABLE=%s", command->path, /* We won't know the real executable path until we create
-                                                           the mount namespace in the child, but we want to log
-                                                           from the parent, so we need to use the (possibly
-                                                           inaccurate) path here. */
-                        LOG_UNIT_INVOCATION_ID(unit));
+        /* We won't know the real executable path until we create the mount namespace in the child, but we
+           want to log from the parent, so we use the possibly inaccurate path here. */
+        log_command_line(unit, "About to execute", command->path, command->argv);
 
         if (params->cgroup_path) {
                 r = exec_parameters_get_cgroup_path(params, &subcgroup_path);
@@ -5618,7 +5612,6 @@ int exec_spawn(Unit *unit,
                                context,
                                params,
                                runtime,
-                               dcreds,
                                cgroup_context,
                                socket_fd,
                                named_iofds,
@@ -6877,7 +6870,7 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
                 end = LIST_FIND_TAIL(command, *l);
                 LIST_INSERT_AFTER(command, *l, end, e);
         } else
-              *l = e;
+                *l = e;
 }
 
 int exec_command_set(ExecCommand *c, const char *path, ...) {
@@ -6934,18 +6927,37 @@ static void *remove_tmpdir_thread(void *p) {
         return NULL;
 }
 
-static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
+static ExecSharedRuntime* exec_shared_runtime_free(ExecSharedRuntime *rt) {
+        if (!rt)
+                return NULL;
+
+        if (rt->manager)
+                (void) hashmap_remove(rt->manager->exec_shared_runtime_by_id, rt->id);
+
+        rt->id = mfree(rt->id);
+        rt->tmp_dir = mfree(rt->tmp_dir);
+        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
+        safe_close_pair(rt->netns_storage_socket);
+        safe_close_pair(rt->ipcns_storage_socket);
+        return mfree(rt);
+}
+
+DEFINE_TRIVIAL_UNREF_FUNC(ExecSharedRuntime, exec_shared_runtime, exec_shared_runtime_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(ExecSharedRuntime*, exec_shared_runtime_free);
+
+ExecSharedRuntime* exec_shared_runtime_destroy(ExecSharedRuntime *rt) {
         int r;
 
         if (!rt)
                 return NULL;
 
-        if (rt->manager)
-                (void) hashmap_remove(rt->manager->exec_runtime_by_id, rt->id);
+        assert(rt->n_ref > 0);
+        rt->n_ref--;
 
-        /* When destroy is true, then rm_rf tmp_dir and var_tmp_dir. */
+        if (rt->n_ref > 0)
+                return NULL;
 
-        if (destroy && rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) {
+        if (rt->tmp_dir && !streq(rt->tmp_dir, RUN_SYSTEMD_EMPTY)) {
                 log_debug("Spawning thread to nuke %s", rt->tmp_dir);
 
                 r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
@@ -6955,7 +6967,7 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
                         rt->tmp_dir = NULL;
         }
 
-        if (destroy && rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) {
+        if (rt->var_tmp_dir && !streq(rt->var_tmp_dir, RUN_SYSTEMD_EMPTY)) {
                 log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
 
                 r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
@@ -6965,21 +6977,12 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) {
                         rt->var_tmp_dir = NULL;
         }
 
-        rt->id = mfree(rt->id);
-        rt->tmp_dir = mfree(rt->tmp_dir);
-        rt->var_tmp_dir = mfree(rt->var_tmp_dir);
-        safe_close_pair(rt->netns_storage_socket);
-        safe_close_pair(rt->ipcns_storage_socket);
-        return mfree(rt);
-}
-
-static void exec_runtime_freep(ExecRuntime **rt) {
-        (void) exec_runtime_free(*rt, false);
+        return exec_shared_runtime_free(rt);
 }
 
-static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
+static int exec_shared_runtime_allocate(ExecSharedRuntime **ret, const char *id) {
         _cleanup_free_ char *id_copy = NULL;
-        ExecRuntime *n;
+        ExecSharedRuntime *n;
 
         assert(ret);
 
@@ -6987,11 +6990,11 @@ static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
         if (!id_copy)
                 return -ENOMEM;
 
-        n = new(ExecRuntime, 1);
+        n = new(ExecSharedRuntime, 1);
         if (!n)
                 return -ENOMEM;
 
-        *n = (ExecRuntime) {
+        *n = (ExecSharedRuntime) {
                 .id = TAKE_PTR(id_copy),
                 .netns_storage_socket = PIPE_EBADF,
                 .ipcns_storage_socket = PIPE_EBADF,
@@ -7001,16 +7004,16 @@ static int exec_runtime_allocate(ExecRuntime **ret, const char *id) {
         return 0;
 }
 
-static int exec_runtime_add(
+static int exec_shared_runtime_add(
                 Manager *m,
                 const char *id,
                 char **tmp_dir,
                 char **var_tmp_dir,
                 int netns_storage_socket[2],
                 int ipcns_storage_socket[2],
-                ExecRuntime **ret) {
+                ExecSharedRuntime **ret) {
 
-        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
+        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt = NULL;
         int r;
 
         assert(m);
@@ -7018,11 +7021,11 @@ static int exec_runtime_add(
 
         /* tmp_dir, var_tmp_dir, {net,ipc}ns_storage_socket fds are donated on success */
 
-        r = exec_runtime_allocate(&rt, id);
+        r = exec_shared_runtime_allocate(&rt, id);
         if (r < 0)
                 return r;
 
-        r = hashmap_ensure_put(&m->exec_runtime_by_id, &string_hash_ops, rt->id, rt);
+        r = hashmap_ensure_put(&m->exec_shared_runtime_by_id, &string_hash_ops, rt->id, rt);
         if (r < 0)
                 return r;
 
@@ -7044,16 +7047,16 @@ static int exec_runtime_add(
 
         if (ret)
                 *ret = rt;
-        /* do not remove created ExecRuntime object when the operation succeeds. */
+        /* do not remove created ExecSharedRuntime object when the operation succeeds. */
         TAKE_PTR(rt);
         return 0;
 }
 
-static int exec_runtime_make(
+static int exec_shared_runtime_make(
                 Manager *m,
                 const ExecContext *c,
                 const char *id,
-                ExecRuntime **ret) {
+                ExecSharedRuntime **ret) {
 
         _cleanup_(namespace_cleanup_tmpdirp) char *tmp_dir = NULL, *var_tmp_dir = NULL;
         _cleanup_close_pair_ int netns_storage_socket[2] = PIPE_EBADF, ipcns_storage_socket[2] = PIPE_EBADF;
@@ -7063,7 +7066,7 @@ static int exec_runtime_make(
         assert(c);
         assert(id);
 
-        /* It is not necessary to create ExecRuntime object. */
+        /* It is not necessary to create ExecSharedRuntime object. */
         if (!exec_needs_network_namespace(c) && !exec_needs_ipc_namespace(c) && !c->private_tmp) {
                 *ret = NULL;
                 return 0;
@@ -7088,24 +7091,24 @@ static int exec_runtime_make(
                         return -errno;
         }
 
-        r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
+        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_storage_socket, ipcns_storage_socket, ret);
         if (r < 0)
                 return r;
 
         return 1;
 }
 
-int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecRuntime **ret) {
-        ExecRuntime *rt;
+int exec_shared_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool create, ExecSharedRuntime **ret) {
+        ExecSharedRuntime *rt;
         int r;
 
         assert(m);
         assert(id);
         assert(ret);
 
-        rt = hashmap_get(m->exec_runtime_by_id, id);
+        rt = hashmap_get(m->exec_shared_runtime_by_id, id);
         if (rt)
-                /* We already have an ExecRuntime object, let's increase the ref count and reuse it */
+                /* We already have an ExecSharedRuntime object, let's increase the ref count and reuse it */
                 goto ref;
 
         if (!create) {
@@ -7114,11 +7117,11 @@ int exec_runtime_acquire(Manager *m, const ExecContext *c, const char *id, bool
         }
 
         /* If not found, then create a new object. */
-        r = exec_runtime_make(m, c, id, &rt);
+        r = exec_shared_runtime_make(m, c, id, &rt);
         if (r < 0)
                 return r;
         if (r == 0) {
-                /* When r == 0, it is not necessary to create ExecRuntime object. */
+                /* When r == 0, it is not necessary to create ExecSharedRuntime object. */
                 *ret = NULL;
                 return 0;
         }
@@ -7130,27 +7133,14 @@ ref:
         return 1;
 }
 
-ExecRuntime *exec_runtime_unref(ExecRuntime *rt, bool destroy) {
-        if (!rt)
-                return NULL;
-
-        assert(rt->n_ref > 0);
-
-        rt->n_ref--;
-        if (rt->n_ref > 0)
-                return NULL;
-
-        return exec_runtime_free(rt, destroy);
-}
-
-int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
-        ExecRuntime *rt;
+int exec_shared_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
+        ExecSharedRuntime *rt;
 
         assert(m);
         assert(f);
         assert(fds);
 
-        HASHMAP_FOREACH(rt, m->exec_runtime_by_id) {
+        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
                 fprintf(f, "exec-runtime=%s", rt->id);
 
                 if (rt->tmp_dir)
@@ -7205,33 +7195,33 @@ int exec_runtime_serialize(const Manager *m, FILE *f, FDSet *fds) {
         return 0;
 }
 
-int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
-        _cleanup_(exec_runtime_freep) ExecRuntime *rt_create = NULL;
-        ExecRuntime *rt;
+int exec_shared_runtime_deserialize_compat(Unit *u, const char *key, const char *value, FDSet *fds) {
+        _cleanup_(exec_shared_runtime_freep) ExecSharedRuntime *rt_create = NULL;
+        ExecSharedRuntime *rt;
         int r;
 
         /* This is for the migration from old (v237 or earlier) deserialization text.
          * Due to the bug #7790, this may not work with the units that use JoinsNamespaceOf=.
-         * Even if the ExecRuntime object originally created by the other unit, we cannot judge
+         * Even if the ExecSharedRuntime object originally created by the other unit, we cannot judge
          * so or not from the serialized text, then we always creates a new object owned by this. */
 
         assert(u);
         assert(key);
         assert(value);
 
-        /* Manager manages ExecRuntime objects by the unit id.
+        /* Manager manages ExecSharedRuntime objects by the unit id.
          * So, we omit the serialized text when the unit does not have id (yet?)... */
         if (isempty(u->id)) {
                 log_unit_debug(u, "Invocation ID not found. Dropping runtime parameter.");
                 return 0;
         }
 
-        if (hashmap_ensure_allocated(&u->manager->exec_runtime_by_id, &string_hash_ops) < 0)
+        if (hashmap_ensure_allocated(&u->manager->exec_shared_runtime_by_id, &string_hash_ops) < 0)
                 return log_oom();
 
-        rt = hashmap_get(u->manager->exec_runtime_by_id, u->id);
+        rt = hashmap_get(u->manager->exec_shared_runtime_by_id, u->id);
         if (!rt) {
-                if (exec_runtime_allocate(&rt_create, u->id) < 0)
+                if (exec_shared_runtime_allocate(&rt_create, u->id) < 0)
                         return log_oom();
 
                 rt = rt_create;
@@ -7270,9 +7260,9 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
         } else
                 return 0;
 
-        /* If the object is newly created, then put it to the hashmap which manages ExecRuntime objects. */
+        /* If the object is newly created, then put it to the hashmap which manages ExecSharedRuntime objects. */
         if (rt_create) {
-                r = hashmap_put(u->manager->exec_runtime_by_id, rt_create->id, rt_create);
+                r = hashmap_put(u->manager->exec_shared_runtime_by_id, rt_create->id, rt_create);
                 if (r < 0) {
                         log_unit_debug_errno(u, r, "Failed to put runtime parameter to manager's storage: %m");
                         return 0;
@@ -7287,7 +7277,7 @@ int exec_runtime_deserialize_compat(Unit *u, const char *key, const char *value,
         return 1;
 }
 
-int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
+int exec_shared_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
         _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL;
         char *id = NULL;
         int r, netns_fdpair[] = {-1, -1}, ipcns_fdpair[] = {-1, -1};
@@ -7399,25 +7389,66 @@ int exec_runtime_deserialize_one(Manager *m, const char *value, FDSet *fds) {
         }
 
 finalize:
-        r = exec_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
+        r = exec_shared_runtime_add(m, id, &tmp_dir, &var_tmp_dir, netns_fdpair, ipcns_fdpair, NULL);
         if (r < 0)
                 return log_debug_errno(r, "Failed to add exec-runtime: %m");
         return 0;
 }
 
-void exec_runtime_vacuum(Manager *m) {
-        ExecRuntime *rt;
+void exec_shared_runtime_vacuum(Manager *m) {
+        ExecSharedRuntime *rt;
 
         assert(m);
 
-        /* Free unreferenced ExecRuntime objects. This is used after manager deserialization process. */
+        /* Free unreferenced ExecSharedRuntime objects. This is used after manager deserialization process. */
 
-        HASHMAP_FOREACH(rt, m->exec_runtime_by_id) {
+        HASHMAP_FOREACH(rt, m->exec_shared_runtime_by_id) {
                 if (rt->n_ref > 0)
                         continue;
 
-                (void) exec_runtime_free(rt, false);
+                (void) exec_shared_runtime_free(rt);
+        }
+}
+
+int exec_runtime_make(ExecSharedRuntime *shared, DynamicCreds *creds, ExecRuntime **ret) {
+        _cleanup_(exec_runtime_freep) ExecRuntime *rt = NULL;
+
+        assert(ret);
+
+        if (!shared && !creds) {
+                *ret = NULL;
+                return 0;
         }
+
+        rt = new(ExecRuntime, 1);
+        if (!rt)
+                return -ENOMEM;
+
+        *rt = (ExecRuntime) {
+                .shared = shared,
+                .dynamic_creds = creds,
+        };
+
+        *ret = TAKE_PTR(rt);
+        return 1;
+}
+
+ExecRuntime* exec_runtime_free(ExecRuntime *rt) {
+        if (!rt)
+                return NULL;
+
+        exec_shared_runtime_unref(rt->shared);
+        dynamic_creds_unref(rt->dynamic_creds);
+        return mfree(rt);
+}
+
+ExecRuntime* exec_runtime_destroy(ExecRuntime *rt) {
+        if (!rt)
+                return NULL;
+
+        rt->shared = exec_shared_runtime_destroy(rt->shared);
+        rt->dynamic_creds = dynamic_creds_destroy(rt->dynamic_creds);
+        return exec_runtime_free(rt);
 }
 
 void exec_params_clear(ExecParameters *p) {