execute.c revision 5482192e5774f52f2af0665a3b58539295e9c0a4
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek This file is part of systemd.
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek Copyright 2010 Lennart Poettering
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek systemd is free software; you can redistribute it and/or modify it
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek under the terms of the GNU Lesser General Public License as published by
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek the Free Software Foundation; either version 2.1 of the License, or
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek (at your option) any later version.
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek systemd is distributed in the hope that it will be useful, but
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek WITHOUT ANY WARRANTY; without even the implied warranty of
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek Lesser General Public License for more details.
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek You should have received a copy of the GNU Lesser General Public License
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek along with systemd; If not, see <http://www.gnu.org/licenses/>.
cab6235f748e365198a7939f23c87ab3b8f59b2eLennart Poettering#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek/* This assumes there is a 'tty' group */
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek#define SNDBUF_SIZE (8*1024*1024)
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmekstatic int shift_fds(int fds[], unsigned n_fds) {
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek /* Modifies the fds array! (sorts it) */
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek /* Already at right index? */
6aaa8c2f783cd1b3ac27c5ce40625d032e7e3d71Zbigniew Jędrzejewski-Szmek if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek /* Hmm, the fd we wanted isn't free? Then
19f6d710772305610b928bc2678b9d77fe11e770Lennart Poettering * let's remember that and try again from here*/
7584d236eac91f9b7128b1eb08bddf18be2bce9fZbigniew Jędrzejewski-Szmek if (nfd != i+3 && restart_from < 0)
restart_from = i;
if (restart_from < 0)
if (n_fds <= 0)
for (i = 0; i < n_fds; i++) {
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 (fd < 0)
return fd;
int *saved_stdout) {
if (*saved_stdin >= 0)
r = -errno;
if (*saved_stdout >= 0)
r = -errno;
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);
#ifdef HAVE_SECCOMP
Iterator i;
void *id;
assert(c);
if (!seccomp)
return -ENOMEM;
if (c->syscall_archs) {
if (r == -EEXIST)
goto finish;
goto finish;
goto finish;
goto finish;
Iterator i;
assert(c);
if (!seccomp)
return -ENOMEM;
goto finish;
if (c->address_families_whitelist) {
void *afp;
if (first == 0) {
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
r = seccomp_rule_add(
goto finish;
void *af;
r = seccomp_rule_add(
goto finish;
goto finish;
if (idle_pipe[0] >= 0) {
static int build_environment(
const 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,
int socket_fd,
char **files_env,
int *error) {
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
unsigned n_dont_close = 0;
int i, err;
if (err < 0) {
return err;
if (socket_fd >= 0)
if (n_fds > 0) {
if (runtime) {
if (err < 0) {
return err;
if (setsid() < 0) {
return -errno;
char response;
else if (err < 0)
write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-err));
return -ECANCELED;
*error = 0;
if (socket_fd >= 0)
if (err < 0) {
return err;
err = setup_output(context, STDOUT_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
if (err < 0) {
return err;
err = setup_output(context, STDERR_FILENO, socket_fd, basename(command->path), params->unit_id, params->apply_tty_stdin);
if (err < 0) {
return err;
if (err < 0) {
return err;
char_array_0(t);
return -errno;
return -errno;
SCHED_RESET_ON_FORK : 0),
¶m);
if (err < 0) {
return -errno;
return -errno;
return -errno;
return -errno;
return -errno;
if (err < 0) {
return err;
if (err < 0) {
return err;
#ifdef ENABLE_KDBUS
if (err < 0) {
return err;
#ifdef HAVE_PAM
if (err < 0) {
return err;
if (err < 0) {
return err;
char **rt;
_cleanup_free_ char *p;
return -ENOMEM;
if (err < 0) {
return err;
if (err < 0) {
return err;
#ifdef HAVE_PAM
if (err < 0) {
return err;
if (err < 0) {
return err;
tmp,
var,
if (err < 0) {
return err;
return -errno;
return -errno;
return -ENOMEM;
if (chdir(d) < 0) {
return -errno;
if (err >= 0)
if (err >= 0)
if (err < 0) {
return err;
for (i = 0; i < _RLIMIT_MAX; i++) {
return -errno;
if (err < 0) {
return err;
if (err < 0) {
return err;
return -errno;
return -errno;
return -errno;
#ifdef HAVE_SECCOMP
if (err < 0) {
return err;
if (err < 0) {
return err;
#ifdef HAVE_SELINUX
if (use_selinux()) {
return err;
if (err < 0) {
return err;
if (err < 0) {
return err;
#ifdef HAVE_APPARMOR
return -errno;
if (err < 0) {
return err;
NULL);
if (!final_env) {
return -ENOMEM;
if (!final_argv) {
return -ENOMEM;
if (line) {
log_open();
NULL);
log_close();
return -errno;
int socket_fd;
int err;
return -EINVAL;
if (err < 0) {
NULL);
return err;
if (!line)
return log_oom();
NULL);
if (pid < 0)
return -errno;
if (pid == 0) {
argv,
log_open();
NULL);
log_close();
_exit(r);
NULL);
assert(c);
c->syslog_level_prefix = true;
c->ignore_sigpipe = true;
assert(c);
if (c->capabilities) {
if (c->cpuset)
assert(c);
if (!runtime_prefix)
_cleanup_free_ char *p;
return -ENOMEM;
rm_rf_dangerous(p, false, true, false);
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;
char *console;
if (!console)
assert(f);
STRV_FOREACH(g, l)
assert(c);
assert(f);
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])
if (c->ioprio_set) {
fprintf(f,
if (c->cpu_sched_set) {
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,
fprintf(f,
if (c->syscall_filter) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
bool first = true;
fprintf(f,
prefix);
if (!c->syscall_whitelist)
#ifdef HAVE_SECCOMP
if (first)
first = false;
if (c->syscall_archs) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
fprintf(f,
prefix);
#ifdef HAVE_SECCOMP
if (c->syscall_errno != 0)
fprintf(f,
if (c->apparmor_profile)
fprintf(f,
assert(s);
zero(*s);
assert(s);
zero(*s);
if (context) {
assert(s);
assert(f);
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);
const char *prefix2;
assert(c);
assert(f);
fprintf(f,
assert(f);
assert(l);
assert(e);
assert(c);
return -ENOMEM;
strv_free(l);
return -ENOMEM;
c->path = p;
c->argv = l;
assert(c);
return -ENOMEM;
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)