execute.c revision 0d3f7bb3a5bc6d5c0712f88a080fed388981bca3
3802a3d3d7af51ddff31943d5514382f01265770Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek This file is part of systemd.
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek Copyright 2010 Lennart Poettering
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek (at your option) any later version.
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek/* This assumes there is a 'tty' group */
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmekstatic int shift_fds(int fds[], unsigned n_fds) {
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek /* Modifies the fds array! (sorts it) */
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek for (i = start; i < (int) n_fds; i++) {
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek /* Already at right index? */
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
b8bde11658366290521e3d03316378b482600323Jan Engelhardt /* Hmm, the fd we wanted isn't free? Then
b8bde11658366290521e3d03316378b482600323Jan Engelhardt * let's remember that and try again from here*/
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmekstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
5aded369782f28255bc6b494ca905d7acaea7a56Zbigniew Jędrzejewski-Szmek /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek for (i = 0; i < n_fds; i++) {
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek if ((r = fd_nonblock(fds[i], nonblock)) < 0)
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek /* We unconditionally drop FD_CLOEXEC from the fds,
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek * since after all we want to pass these fds to our
effbc8e4f70fc70e4a4a8a1dc77228dd187f9c22Zbigniew Jędrzejewski-Szmek if ((r = fd_cloexec(fds[i], false)) < 0)
o == EXEC_OUTPUT_TTY ||
o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
int fd, r;
if (fd < 0)
return -errno;
r = nfd;
static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
int fd, r;
assert(ident);
if (fd < 0)
return -errno;
return -errno;
return -errno;
r = nfd;
int fd, r;
return fd;
r = nfd;
i == EXEC_INPUT_TTY ||
i == EXEC_INPUT_TTY_FORCE ||
i == EXEC_INPUT_TTY_FAIL;
return EXEC_INPUT_NULL;
return EXEC_INPUT_NULL;
return std_input;
return EXEC_OUTPUT_INHERIT;
return std_output;
ExecInput i;
case EXEC_INPUT_NULL:
case EXEC_INPUT_TTY:
case EXEC_INPUT_TTY_FORCE:
case EXEC_INPUT_TTY_FAIL: {
int fd, r;
i == EXEC_INPUT_TTY_FAIL,
i == EXEC_INPUT_TTY_FORCE,
if (fd < 0)
return fd;
r = STDIN_FILENO;
case EXEC_INPUT_SOCKET:
static int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
ExecOutput o;
ExecInput i;
assert(ident);
ExecOutput e;
if (e == EXEC_OUTPUT_INHERIT &&
o == EXEC_OUTPUT_INHERIT &&
i == EXEC_INPUT_NULL &&
return fileno;
if (e == o || e == EXEC_OUTPUT_INHERIT)
} else if (o == EXEC_OUTPUT_INHERIT) {
if (i != EXEC_INPUT_NULL)
return fileno;
case EXEC_OUTPUT_NULL:
case EXEC_OUTPUT_TTY:
if (is_terminal_input(i))
case EXEC_OUTPUT_SYSLOG:
case EXEC_OUTPUT_KMSG:
case EXEC_OUTPUT_JOURNAL:
NULL);
case EXEC_OUTPUT_SOCKET:
return -errno;
return -EPERM;
int *_saved_stdout) {
if (saved_stdin < 0)
return -errno;
if (saved_stdout < 0) {
r = errno;
goto fail;
if (fd < 0) {
r = fd;
goto fail;
goto fail;
r = -errno;
goto fail;
r = -errno;
goto fail;
fail:
if (saved_stdout >= 0)
if (saved_stdin >= 0)
if (fd >= 0)
int fd;
if (fd < 0)
return fd;
int *saved_stdout) {
if (*saved_stdin >= 0)
r = -errno;
if (*saved_stdout >= 0)
r = -errno;
if (*saved_stdin >= 0)
if (*saved_stdout >= 0)
char *line;
if (!line)
return -ENOMEM;
bool keep_groups = false;
return -errno;
keep_groups = true;
return -errno;
int ngroups_max, k;
return -ENOMEM;
if (keep_groups) {
return -errno;
if (k >= ngroups_max) {
return -E2BIG;
return -errno;
if (uid != 0) {
return -errno;
return -errno;
return -errno;
if (cap_set_proc(d) < 0)
return -errno;
return -errno;
#ifdef HAVE_PAM
static int null_conv(
int num_msg,
void *appdata_ptr) {
return PAM_CONV_ERR;
static int setup_pam(
const char *name,
const char *user,
const char *tty,
char ***pam_env,
int err;
char **e = NULL;
bool close_session = false;
int flags = 0;
goto fail;
if (tty) {
goto fail;
goto fail;
goto fail;
close_session = true;
goto fail;
goto fail;
if (pam_pid < 0)
goto fail;
if (pam_pid == 0) {
int sig;
int r = EXIT_PAM;
goto child_finish;
goto child_finish;
goto child_finish;
_exit(r);
goto fail;
closelog();
*pam_env = e;
e = NULL;
fail:
if (handle) {
if (close_session)
strv_free(e);
closelog();
return err;
size_t l;
if (isempty(p)) {
l = strlen(p);
struct sock_filter *f;
for (i = 0, n = 0; i < syscall_max(); i++)
for (i = 0, n = 0; i < syscall_max(); i++)
return -errno;
if (idle_pipe[0] >= 0) {
static int build_environment(
ExecContext *c,
unsigned n_fds,
const char *home,
const char *username,
const char *shell,
char ***ret) {
unsigned n_env = 0;
assert(c);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
return -ENOMEM;
return -ENOMEM;
if (watchdog_usec > 0) {
return -ENOMEM;
return -ENOMEM;
if (home) {
return -ENOMEM;
if (username) {
return -ENOMEM;
return -ENOMEM;
if (shell) {
return -ENOMEM;
c->tty_path) {
return -ENOMEM;
char **argv,
char **environment,
bool apply_permissions,
bool apply_chroot,
bool apply_tty_stdin,
bool confirm_spawn,
const char *cgroup_path,
const char *unit_id,
int socket_fd;
char *line;
return -EINVAL;
n_fds = 0;
NULL);
if (!argv)
if (!line)
return log_oom();
NULL);
if (pid < 0)
return -errno;
if (pid == 0) {
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
unsigned n_dont_close = 0;
int i, err;
r = EXIT_SIGNAL_MASK;
goto fail_child;
if (idle_pipe)
if (socket_fd >= 0)
if (n_fds > 0) {
if (runtime) {
if (err < 0) {
r = EXIT_FDS;
goto fail_child;
if (setsid() < 0) {
r = EXIT_SETSID;
goto fail_child;
if (socket_fd >= 0)
r = EXIT_TCPWRAP;
goto fail_child;
for (i = 0; i < (int) n_fds; i++) {
r = EXIT_TCPWRAP;
goto fail_child;
if (confirm_spawn) {
char response;
else if (err < 0)
write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-err));
r = EXIT_CONFIRM;
goto fail_child;
err = r = 0;
goto fail_child;
if (socket_fd >= 0)
if (err < 0) {
r = EXIT_STDIN;
goto fail_child;
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, basename(command->path), unit_id, apply_tty_stdin);
if (err < 0) {
r = EXIT_STDERR;
goto fail_child;
if (cgroup_path) {
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
char_array_0(t);
r = EXIT_OOM_ADJUST;
goto fail_child;
r = EXIT_NICE;
goto fail_child;
r = sched_setscheduler(0,
SCHED_RESET_ON_FORK : 0),
¶m);
r = EXIT_SETSCHEDULER;
goto fail_child;
r = EXIT_CPUAFFINITY;
goto fail_child;
r = EXIT_IOPRIO;
goto fail_child;
r = EXIT_TIMERSLACK;
goto fail_child;
if (err < 0) {
r = EXIT_USER;
goto fail_child;
if (err < 0) {
r = EXIT_STDIN;
goto fail_child;
#ifdef HAVE_PAM
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
if (apply_permissions) {
if (err < 0) {
r = EXIT_GROUP;
goto fail_child;
#ifdef HAVE_PAM
if (err < 0) {
r = EXIT_PAM;
goto fail_child;
if (err < 0) {
r = EXIT_NETWORK;
goto fail_child;
tmp,
var,
if (err < 0) {
r = EXIT_NAMESPACE;
goto fail_child;
if (apply_chroot) {
r = EXIT_CHROOT;
goto fail_child;
r = EXIT_CHDIR;
goto fail_child;
r = EXIT_MEMORY;
goto fail_child;
if (chdir(d) < 0) {
r = EXIT_CHDIR;
goto fail_child;
if (err >= 0)
if (err >= 0)
if (err < 0) {
r = EXIT_FDS;
goto fail_child;
if (apply_permissions) {
for (i = 0; i < RLIMIT_NLIMITS; i++) {
r = EXIT_LIMITS;
goto fail_child;
if (err < 0) {
r = EXIT_CAPABILITIES;
goto fail_child;
if (err < 0) {
r = EXIT_USER;
goto fail_child;
r = EXIT_SECUREBITS;
goto fail_child;
r = EXIT_CAPABILITIES;
goto fail_child;
goto fail_child;
if (err < 0) {
r = EXIT_SECCOMP;
goto fail_child;
#ifdef HAVE_SELINUX
bool ignore;
ignore = true;
ignore = false;
r = EXIT_SELINUX_CONTEXT;
goto fail_child;
r = EXIT_MEMORY;
goto fail_child;
NULL);
if (!final_env) {
r = EXIT_MEMORY;
goto fail_child;
if (!final_argv) {
r = EXIT_MEMORY;
goto fail_child;
if (line) {
log_open();
NULL);
log_close();
r = EXIT_EXEC;
log_open();
NULL);
log_close();
_exit(r);
NULL);
if (cgroup_path)
assert(c);
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
assert(c);
if (c->capabilities) {
if (c->cpuset)
assert(c);
exec_command_done(c+i);
ExecCommand *i;
free(i);
exec_command_free_list(c[i]);
c[i] = NULL;
char **i, **r = NULL;
assert(c);
assert(l);
char *fn;
bool ignore = false;
int count, n;
fn = *i;
ignore = true;
fn ++;
if (ignore)
strv_free(r);
return -EINVAL;
errno = 0;
if (ignore)
strv_free(r);
if (count == 0) {
if (ignore)
strv_free(r);
return -EINVAL;
for (n = 0; n < count; n++) {
if (ignore)
strv_free(r);
if (r == NULL)
strv_free(r);
strv_free(p);
return -ENOMEM;
if (!console)
assert(f);
STRV_FOREACH(g, l)
assert(c);
assert(f);
fprintf(f,
if (c->tcpwrap_name)
fprintf(f,
if (c->nice_set)
fprintf(f,
if (c->oom_score_adjust_set)
fprintf(f,
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i])
fprintf(f, "%s%s: %llu\n", prefix, rlimit_to_string(i), (unsigned long long) c->rlimit[i]->rlim_max);
if (c->ioprio_set) {
char *class_str;
fprintf(f,
if (c->cpu_sched_set) {
char *policy_str;
fprintf(f,
if (c->cpuset) {
for (i = 0; i < c->cpuset_ncpus; i++)
fprintf(f,
if (c->tty_path)
fprintf(f,
fprintf(f,
if (c->capabilities) {
_cleanup_cap_free_charp_ char *t;
if (c->secure_bits)
if (c->capability_bounding_set_drop) {
for (l = 0; l <= cap_last_cap(); l++)
_cleanup_cap_free_charp_ char *t;
t = cap_to_name(l);
if (c->user)
if (c->group)
if (c->pam_name)
if (c->utmp_id)
fprintf(f,
if (c->selinux_context)
fprintf(f,
assert(s);
zero(*s);
assert(s);
zero(*s);
if (context) {
assert(s);
assert(f);
if (!prefix)
if (s->pid <= 0)
fprintf(f,
fprintf(f,
fprintf(f,
size_t k;
bool first = true;
if (!(n = new(char, k)))
return NULL;
if (!first)
first = false;
p = stpcpy(p, *a);
p = stpcpy(p, *a);
char *p2;
const char *prefix2;
char *cmd;
assert(c);
assert(f);
if (!prefix)
fprintf(f,
assert(f);
if (!prefix)
assert(l);
assert(e);
assert(c);
return -ENOMEM;
strv_free(l);
return -ENOMEM;
c->path = p;
c->argv = l;
if (*rt)
if (!*rt)
return -ENOMEM;
assert(c);
if (*rt)
return -errno;
assert(r);
r->n_ref++;
return NULL;
r->n_ref--;
if (r->n_ref <= 0) {
free(r);
return NULL;
assert(u);
assert(f);
if (!rt)
int copy;
if (copy < 0)
return copy;
int copy;
if (copy < 0)
return copy;
int exec_runtime_deserialize_item(ExecRuntime **rt, Unit *u, const char *key, const char *value, FDSet *fds) {
char *copy;
if (!copy)
return log_oom();
char *copy;
if (!copy)
return log_oom();
int fd;
int fd;
static void *remove_tmpdir_thread(void *p) {
return NULL;
if (!rt)