#include <linux/seccomp-bpf.h>
#include <glob.h>
#include <libgen.h>
+#undef basename
#ifdef HAVE_PAM
#include <security/pam_appl.h>
#endif
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
#include "execute.h"
#include "strv.h"
#include "macro.h"
#include "missing.h"
#include "utmp-wtmp.h"
#include "def.h"
-#include "loopback-setup.h"
#include "path-util.h"
#include "syscall-list.h"
#include "env-util.h"
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
+#define SNDBUF_SIZE (8*1024*1024)
+
static int shift_fds(int fds[], unsigned n_fds) {
int start, restart_from;
return "/dev/console";
}
-void exec_context_tty_reset(const ExecContext *context) {
+static void exec_context_tty_reset(const ExecContext *context) {
assert(context);
if (context->tty_vhangup)
o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
}
-void exec_context_serialize(const ExecContext *context, Unit *u, FILE *f) {
- assert(context);
- assert(u);
- assert(f);
-
- if (context->tmp_dir)
- unit_serialize_item(u, f, "tmp-dir", context->tmp_dir);
-
- if (context->var_tmp_dir)
- unit_serialize_item(u, f, "var-tmp-dir", context->var_tmp_dir);
-}
-
static int open_null_as(int flags, int nfd) {
int fd, r;
assert(nfd >= 0);
- if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
+ fd = open("/dev/null", flags|O_NOCTTY);
+ if (fd < 0)
return -errno;
if (fd != nfd) {
return -errno;
}
+ fd_inc_sndbuf(fd, SNDBUF_SIZE);
+
dprintf(fd,
"%s\n"
"%s\n"
case EXEC_INPUT_TTY_FAIL: {
int fd, r;
- if ((fd = acquire_terminal(
- tty_path(context),
- i == EXEC_INPUT_TTY_FAIL,
- i == EXEC_INPUT_TTY_FORCE,
- false,
- (usec_t) -1)) < 0)
+ fd = acquire_terminal(tty_path(context),
+ i == EXEC_INPUT_TTY_FAIL,
+ i == EXEC_INPUT_TTY_FORCE,
+ false,
+ (usec_t) -1);
+ if (fd < 0)
return fd;
if (fd != STDIN_FILENO) {
return r;
}
-_printf_attr_(1, 2) static int write_confirm_message(const char *format, ...) {
+_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
int fd;
va_list ap;
}
static int enforce_user(const ExecContext *context, uid_t uid) {
- int r;
assert(context);
/* Sets (but doesn't lookup) the uid and make sure we keep the
* capabilities while doing so. */
if (context->capabilities) {
- cap_t d;
+ _cleanup_cap_free_ cap_t d = NULL;
static const cap_value_t bits[] = {
CAP_SETUID, /* Necessary so that we can run setresuid() below */
CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
/* Second step: set the capabilities. This will reduce
* the capabilities to the minimum we need. */
- if (!(d = cap_dup(context->capabilities)))
+ d = cap_dup(context->capabilities);
+ if (!d)
return -errno;
if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
- cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0) {
- r = -errno;
- cap_free(d);
- return r;
- }
-
- if (cap_set_proc(d) < 0) {
- r = -errno;
- cap_free(d);
- return r;
- }
+ cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
+ return -errno;
- cap_free(d);
+ if (cap_set_proc(d) < 0)
+ return -errno;
}
/* Third step: actually set the uids */
char **e = NULL;
bool close_session = false;
pid_t pam_pid = 0, parent_pid;
+ int flags = 0;
assert(name);
assert(user);
* daemon. We do things this way to ensure that the main PID
* of the daemon is the one we initially fork()ed. */
+ if (log_get_max_level() < LOG_PRI(LOG_DEBUG))
+ flags |= PAM_SILENT;
+
pam_code = pam_start(name, user, &conv, &handle);
if (pam_code != PAM_SUCCESS) {
handle = NULL;
goto fail;
}
- pam_code = pam_acct_mgmt(handle, PAM_SILENT);
+ pam_code = pam_acct_mgmt(handle, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
- pam_code = pam_open_session(handle, PAM_SILENT);
+ pam_code = pam_open_session(handle, flags);
if (pam_code != PAM_SUCCESS)
goto fail;
/* If our parent died we'll end the session */
if (getppid() != parent_pid) {
- pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+ pam_code = pam_close_session(handle, flags);
if (pam_code != PAM_SUCCESS)
goto child_finish;
}
r = 0;
child_finish:
- pam_end(handle, pam_code | PAM_DATA_SILENT);
+ pam_end(handle, pam_code | flags);
_exit(r);
}
return 0;
fail:
- if (pam_code != PAM_SUCCESS)
+ if (pam_code != PAM_SUCCESS) {
+ log_error("PAM failed: %s", pam_strerror(handle, pam_code));
err = -EPERM; /* PAM errors do not map to errno */
- else
+ } else {
+ log_error("PAM failed: %m");
err = -errno;
+ }
if (handle) {
if (close_session)
- pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+ pam_code = pam_close_session(handle, flags);
- pam_end(handle, pam_code | PAM_DATA_SILENT);
+ pam_end(handle, pam_code | flags);
}
strv_free(e);
/* This resulting string must fit in 10 chars (i.e. the length
* of "/sbin/init") to look pretty in /bin/ps */
- p = path_get_file_name(path);
+ p = basename(path);
if (isempty(p)) {
rename_process("(...)");
return;
close_nointr_nofail(idle_pipe[3]);
}
+static int build_environment(
+ ExecContext *c,
+ unsigned n_fds,
+ usec_t watchdog_usec,
+ const char *home,
+ const char *username,
+ const char *shell,
+ char ***ret) {
+
+ _cleanup_strv_free_ char **our_env = NULL;
+ unsigned n_env = 0;
+ char *x;
+
+ assert(c);
+ assert(ret);
+
+ our_env = new0(char*, 10);
+ if (!our_env)
+ return -ENOMEM;
+
+ if (n_fds > 0) {
+ if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+
+ if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ if (watchdog_usec > 0) {
+ if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+
+ if (asprintf(&x, "WATCHDOG_USEC=%llu", (unsigned long long) watchdog_usec) < 0)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ if (home) {
+ x = strappend("HOME=", home);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ if (username) {
+ x = strappend("LOGNAME=", username);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+
+ x = strappend("USER=", username);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ if (shell) {
+ x = strappend("SHELL=", shell);
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ if (is_terminal_input(c->std_input) ||
+ c->std_output == EXEC_OUTPUT_TTY ||
+ c->std_error == EXEC_OUTPUT_TTY ||
+ c->tty_path) {
+
+ x = strdup(default_term_for_tty(tty_path(c)));
+ if (!x)
+ return -ENOMEM;
+ our_env[n_env++] = x;
+ }
+
+ our_env[n_env++] = NULL;
+ assert(n_env <= 10);
+
+ *ret = our_env;
+ our_env = NULL;
+
+ return 0;
+}
+
int exec_spawn(ExecCommand *command,
char **argv,
ExecContext *context,
CGroupControllerMask cgroup_supported,
const char *cgroup_path,
const char *unit_id,
+ usec_t watchdog_usec,
int idle_pipe[4],
+ ExecRuntime *runtime,
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
NULL);
free(line);
- if (context->private_tmp && !context->tmp_dir && !context->var_tmp_dir) {
- r = setup_tmpdirs(&context->tmp_dir, &context->var_tmp_dir);
- if (r < 0)
- return r;
- }
-
pid = fork();
if (pid < 0)
return -errno;
if (pid == 0) {
- int i, err;
- sigset_t ss;
- const char *username = NULL, *home = NULL;
+ _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
+ const char *username = NULL, *home = NULL, *shell = NULL;
+ unsigned n_dont_close = 0;
+ int dont_close[n_fds + 3];
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- _cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL,
- **final_env = NULL, **final_argv = NULL;
- unsigned n_env = 0;
+ sigset_t ss;
+ int i, err;
/* child */
* block init reexecution because it cannot bind its
* sockets */
log_forget_fds();
- err = close_all_fds(socket_fd >= 0 ? &socket_fd : fds,
- socket_fd >= 0 ? 1 : n_fds);
+
+ if (socket_fd >= 0)
+ dont_close[n_dont_close++] = socket_fd;
+ if (n_fds > 0) {
+ memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
+ n_dont_close += n_fds;
+ }
+ if (runtime) {
+ if (runtime->netns_storage_socket[0] >= 0)
+ dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
+ if (runtime->netns_storage_socket[1] >= 0)
+ dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
+ }
+
+ err = close_all_fds(dont_close, n_dont_close);
if (err < 0) {
r = EXIT_FDS;
goto fail_child;
goto fail_child;
}
- err = setup_output(context, STDOUT_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDOUT;
goto fail_child;
}
- err = setup_output(context, STDERR_FILENO, socket_fd, path_get_file_name(command->path), unit_id, apply_tty_stdin);
+ err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDERR;
goto fail_child;
if (context->user) {
username = context->user;
- err = get_user_creds(&username, &uid, &gid, &home, NULL);
+ err = get_user_creds(&username, &uid, &gid, &home, &shell);
if (err < 0) {
r = EXIT_USER;
goto fail_child;
}
}
#endif
- if (context->private_network) {
- if (unshare(CLONE_NEWNET) < 0) {
- err = -errno;
+ if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) {
+ err = setup_netns(runtime->netns_storage_socket);
+ if (err < 0) {
r = EXIT_NETWORK;
goto fail_child;
}
-
- loopback_setup();
}
- if (strv_length(context->read_write_dirs) > 0 ||
- strv_length(context->read_only_dirs) > 0 ||
- strv_length(context->inaccessible_dirs) > 0 ||
+ if (!strv_isempty(context->read_write_dirs) ||
+ !strv_isempty(context->read_only_dirs) ||
+ !strv_isempty(context->inaccessible_dirs) ||
context->mount_flags != 0 ||
- context->private_tmp) {
- err = setup_namespace(context->read_write_dirs,
- context->read_only_dirs,
- context->inaccessible_dirs,
- context->tmp_dir,
- context->var_tmp_dir,
- context->private_tmp,
- context->mount_flags);
+ (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) ||
+ context->private_devices) {
+
+ char *tmp = NULL, *var = NULL;
+
+ /* The runtime struct only contains the parent
+ * of the private /tmp, which is
+ * non-accessible to world users. Inside of it
+ * there's a /tmp that is sticky, and that's
+ * the one we want to use here. */
+
+ if (context->private_tmp && runtime) {
+ if (runtime->tmp_dir)
+ tmp = strappenda(runtime->tmp_dir, "/tmp");
+ if (runtime->var_tmp_dir)
+ var = strappenda(runtime->var_tmp_dir, "/tmp");
+ }
+
+ err = setup_namespace(
+ context->read_write_dirs,
+ context->read_only_dirs,
+ context->inaccessible_dirs,
+ tmp,
+ var,
+ context->private_devices,
+ context->mount_flags);
+
if (err < 0) {
r = EXIT_NAMESPACE;
goto fail_child;
goto fail_child;
}
}
+#ifdef HAVE_SELINUX
+ if (context->selinux_context && use_selinux()) {
+ err = security_check_context(context->selinux_context);
+ if (err < 0) {
+ r = EXIT_SELINUX_CONTEXT;
+ goto fail_child;
+ }
+ err = setexeccon(context->selinux_context);
+ if (err < 0) {
+ r = EXIT_SELINUX_CONTEXT;
+ goto fail_child;
+ }
+ }
+#endif
}
- our_env = new0(char*, 7);
- if (!our_env) {
- err = -ENOMEM;
+ err = build_environment(context, n_fds, watchdog_usec, home, username, shell, &our_env);
+ if (r < 0) {
r = EXIT_MEMORY;
goto fail_child;
}
- if (n_fds > 0)
- if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 ||
- asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (home)
- if (asprintf(our_env + n_env++, "HOME=%s", home) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (username)
- if (asprintf(our_env + n_env++, "LOGNAME=%s", username) < 0 ||
- asprintf(our_env + n_env++, "USER=%s", username) < 0) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- if (is_terminal_input(context->std_input) ||
- context->std_output == EXEC_OUTPUT_TTY ||
- context->std_error == EXEC_OUTPUT_TTY)
- if (!(our_env[n_env++] = strdup(default_term_for_tty(tty_path(context))))) {
- err = -ENOMEM;
- r = EXIT_MEMORY;
- goto fail_child;
- }
-
- assert(n_env <= 7);
-
final_env = strv_env_merge(5,
environment,
our_env,
log_struct_unit(LOG_DEBUG,
unit_id,
- "MESSAGE=Forked %s as %lu",
- command->path, (unsigned long) pid,
+ "MESSAGE=Forked %s as "PID_FMT,
+ command->path, pid,
NULL);
/* We add the new process to the cgroup both in the child (so
c->timer_slack_nsec = (nsec_t) -1;
}
-static void *remove_tmpdir_thread(void *p) {
- int r;
- _cleanup_free_ char *dirp = p;
- char *dir;
-
- assert(dirp);
-
- r = rm_rf_dangerous(dirp, false, true, false);
- dir = dirname(dirp);
- if (r < 0)
- log_warning("Failed to remove content of temporary directory %s: %s",
- dir, strerror(-r));
- else {
- r = rmdir(dir);
- if (r < 0)
- log_warning("Failed to remove temporary directory %s: %s",
- dir, strerror(-r));
- }
-
- return NULL;
-}
-
-void exec_context_tmp_dirs_done(ExecContext *c) {
- char* dirs[] = {c->tmp_dir ? c->tmp_dir : c->var_tmp_dir,
- c->tmp_dir ? c->var_tmp_dir : NULL,
- NULL};
- char **dirp;
-
- for(dirp = dirs; *dirp; dirp++) {
- log_debug("Spawning thread to nuke %s", *dirp);
- asynchronous_job(remove_tmpdir_thread, *dirp);
- }
-
- c->tmp_dir = c->var_tmp_dir = NULL;
-}
-
-void exec_context_done(ExecContext *c, bool reloading_or_reexecuting) {
+void exec_context_done(ExecContext *c) {
unsigned l;
assert(c);
free(c->utmp_id);
c->utmp_id = NULL;
+ free(c->selinux_context);
+ c->selinux_context = NULL;
+
free(c->syscall_filter);
c->syscall_filter = NULL;
-
- if (!reloading_or_reexecuting)
- exec_context_tmp_dirs_done(c);
}
void exec_command_done(ExecCommand *c) {
ExecCommand *i;
while ((i = c)) {
- LIST_REMOVE(ExecCommand, command, c, i);
+ LIST_REMOVE(command, c, i);
exec_command_done(i);
free(i);
}
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
"%sPrivateNetwork: %s\n"
+ "%sPrivateDevices: %s\n"
"%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->private_network),
+ prefix, yes_no(c->private_devices),
prefix, yes_no(c->ignore_sigpipe));
STRV_FOREACH(e, c->environment)
fprintf(f, "%sCPUAffinity:", prefix);
for (i = 0; i < c->cpuset_ncpus; i++)
if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
- fprintf(f, " %i", i);
+ fprintf(f, " %u", i);
fputs("\n", f);
}
if (c->timer_slack_nsec != (nsec_t) -1)
- fprintf(f, "%sTimerSlackNSec: %lu\n", prefix, (unsigned long)c->timer_slack_nsec);
+ fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
fprintf(f,
"%sStandardInput: %s\n"
prefix, yes_no(c->tty_vhangup),
prefix, yes_no(c->tty_vt_disallocate));
- if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG || c->std_output == EXEC_OUTPUT_JOURNAL ||
- c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
- c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG || c->std_error == EXEC_OUTPUT_JOURNAL ||
- c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) {
- char *fac_str, *lvl_str;
- int r;
+ if (c->std_output == EXEC_OUTPUT_SYSLOG ||
+ c->std_output == EXEC_OUTPUT_KMSG ||
+ c->std_output == EXEC_OUTPUT_JOURNAL ||
+ c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+ c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+ c->std_output == EXEC_OUTPUT_JOURNAL_AND_CONSOLE ||
+ c->std_error == EXEC_OUTPUT_SYSLOG ||
+ c->std_error == EXEC_OUTPUT_KMSG ||
+ c->std_error == EXEC_OUTPUT_JOURNAL ||
+ c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
+ c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+ c->std_error == EXEC_OUTPUT_JOURNAL_AND_CONSOLE) {
- r = log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
- if (r < 0)
- fac_str = NULL;
+ _cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
- r = log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
- if (r < 0)
- lvl_str = NULL;
+ log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
+ log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
fprintf(f,
"%sSyslogFacility: %s\n"
"%sSyslogLevel: %s\n",
prefix, strna(fac_str),
prefix, strna(lvl_str));
- free(lvl_str);
- free(fac_str);
}
if (c->capabilities) {
- char *t;
- if ((t = cap_to_text(c->capabilities, NULL))) {
- fprintf(f, "%sCapabilities: %s\n",
- prefix, t);
- cap_free(t);
- }
+ _cleanup_cap_free_charp_ char *t;
+
+ t = cap_to_text(c->capabilities, NULL);
+ if (t)
+ fprintf(f, "%sCapabilities: %s\n", prefix, t);
}
if (c->secure_bits)
for (l = 0; l <= cap_last_cap(); l++)
if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) {
- char *t;
+ _cleanup_cap_free_charp_ char *t;
- if ((t = cap_to_name(l))) {
+ t = cap_to_name(l);
+ if (t)
fprintf(f, " %s", t);
- cap_free(t);
- }
}
fputs("\n", f);
fprintf(f,
"%sUtmpIdentifier: %s\n",
prefix, c->utmp_id);
+
+ if (c->selinux_context)
+ fprintf(f,
+ "%sSELinuxContext: %s\n",
+ prefix, c->selinux_context);
+
}
void exec_status_start(ExecStatus *s, pid_t pid) {
return;
fprintf(f,
- "%sPID: %lu\n",
- prefix, (unsigned long) s->pid);
+ "%sPID: "PID_FMT"\n",
+ prefix, s->pid);
if (s->start_timestamp.realtime > 0)
fprintf(f,
if (*l) {
/* It's kind of important, that we keep the order here */
- LIST_FIND_TAIL(ExecCommand, command, *l, end);
- LIST_INSERT_AFTER(ExecCommand, command, *l, end, e);
+ LIST_FIND_TAIL(command, *l, end);
+ LIST_INSERT_AFTER(command, *l, end, e);
} else
*l = e;
}
if (!l)
return -ENOMEM;
- if (!(p = strdup(path))) {
+ p = strdup(path);
+ if (!p) {
strv_free(l);
return -ENOMEM;
}
return 0;
}
+static int exec_runtime_allocate(ExecRuntime **rt) {
+
+ if (*rt)
+ return 0;
+
+ *rt = new0(ExecRuntime, 1);
+ if (!*rt)
+ return -ENOMEM;
+
+ (*rt)->n_ref = 1;
+ (*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1;
+
+ return 0;
+}
+
+int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
+ int r;
+
+ assert(rt);
+ assert(c);
+ assert(id);
+
+ if (*rt)
+ return 1;
+
+ if (!c->private_network && !c->private_tmp)
+ return 0;
+
+ r = exec_runtime_allocate(rt);
+ if (r < 0)
+ return r;
+
+ if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (*rt)->netns_storage_socket) < 0)
+ return -errno;
+ }
+
+ if (c->private_tmp && !(*rt)->tmp_dir) {
+ r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir);
+ if (r < 0)
+ return r;
+ }
+
+ return 1;
+}
+
+ExecRuntime *exec_runtime_ref(ExecRuntime *r) {
+ assert(r);
+ assert(r->n_ref > 0);
+
+ r->n_ref++;
+ return r;
+}
+
+ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
+
+ if (!r)
+ return NULL;
+
+ assert(r->n_ref > 0);
+
+ r->n_ref--;
+ if (r->n_ref <= 0) {
+ free(r->tmp_dir);
+ free(r->var_tmp_dir);
+ close_pipe(r->netns_storage_socket);
+ free(r);
+ }
+
+ return NULL;
+}
+
+int exec_runtime_serialize(ExecRuntime *rt, Unit *u, FILE *f, FDSet *fds) {
+ assert(u);
+ assert(f);
+ assert(fds);
+
+ if (!rt)
+ return 0;
+
+ if (rt->tmp_dir)
+ unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir);
+
+ if (rt->var_tmp_dir)
+ unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir);
+
+ if (rt->netns_storage_socket[0] >= 0) {
+ int copy;
+
+ copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
+ if (copy < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy);
+ }
+
+ if (rt->netns_storage_socket[1] >= 0) {
+ int copy;
+
+ copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
+ if (copy < 0)
+ return copy;
+
+ unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy);
+ }
+
+ return 0;
+}
+
+int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds) {
+ int r;
+
+ assert(rt);
+ assert(key);
+ assert(value);
+
+ if (streq(key, "tmp-dir")) {
+ char *copy;
+
+ r = exec_runtime_allocate(rt);
+ if (r < 0)
+ return r;
+
+ copy = strdup(value);
+ if (!copy)
+ return log_oom();
+
+ free((*rt)->tmp_dir);
+ (*rt)->tmp_dir = copy;
+
+ } else if (streq(key, "var-tmp-dir")) {
+ char *copy;
+
+ r = exec_runtime_allocate(rt);
+ if (r < 0)
+ return r;
+
+ copy = strdup(value);
+ if (!copy)
+ return log_oom();
+
+ free((*rt)->var_tmp_dir);
+ (*rt)->var_tmp_dir = copy;
+
+ } else if (streq(key, "netns-socket-0")) {
+ int fd;
+
+ r = exec_runtime_allocate(rt);
+ if (r < 0)
+ return r;
+
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
+ log_debug_unit(u->id, "Failed to parse netns socket value %s", value);
+ else {
+ if ((*rt)->netns_storage_socket[0] >= 0)
+ close_nointr_nofail((*rt)->netns_storage_socket[0]);
+
+ (*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
+ }
+ } else if (streq(key, "netns-socket-1")) {
+ int fd;
+
+ r = exec_runtime_allocate(rt);
+ if (r < 0)
+ return r;
+
+ if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
+ log_debug_unit(u->id, "Failed to parse netns socket value %s", value);
+ else {
+ if ((*rt)->netns_storage_socket[1] >= 0)
+ close_nointr_nofail((*rt)->netns_storage_socket[1]);
+
+ (*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
+ }
+ } else
+ return 0;
+
+ return 1;
+}
+
+static void *remove_tmpdir_thread(void *p) {
+ _cleanup_free_ char *path = p;
+
+ rm_rf_dangerous(path, false, true, false);
+ return NULL;
+}
+
+void exec_runtime_destroy(ExecRuntime *rt) {
+ if (!rt)
+ return;
+
+ /* If there are multiple users of this, let's leave the stuff around */
+ if (rt->n_ref > 1)
+ return;
+
+ if (rt->tmp_dir) {
+ log_debug("Spawning thread to nuke %s", rt->tmp_dir);
+ asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
+ rt->tmp_dir = NULL;
+ }
+
+ if (rt->var_tmp_dir) {
+ log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
+ asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
+ rt->var_tmp_dir = NULL;
+ }
+
+ close_pipe(rt->netns_storage_socket);
+}
+
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",