execute.c revision 6ac8fdc9554a40024827ad9f64d02b4d8d2ab8ba
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering This file is part of systemd.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Copyright 2010 Lennart Poettering
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is free software; you can redistribute it and/or modify it
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering under the terms of the GNU Lesser General Public License as published by
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering (at your option) any later version.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering systemd is distributed in the hope that it will be useful, but
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering Lesser General Public License for more details.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering You should have received a copy of the GNU Lesser General Public License
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering/* This assumes there is a 'tty' group */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int shift_fds(int fds[], unsigned n_fds) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Modifies the fds array! (sorts it) */
ccf23ad5faf228d450d263d7291156a948b61af2Christian Seiler /* Already at right index? */
ccf23ad5faf228d450d263d7291156a948b61af2Christian Seiler if ((nfd = fcntl(fds[i], F_DUPFD, i+3)) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Hmm, the fd we wanted isn't free? Then
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * let's remember that and try again from here*/
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering for (i = 0; i < n_fds; i++) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((r = fd_nonblock(fds[i], nonblock)) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* We unconditionally drop FD_CLOEXEC from the fds,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * since after all we want to pass these fds to our
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic const char *tty_path(const ExecContext *context) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringvoid exec_context_tty_reset(const ExecContext *context) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (context->tty_vt_disallocate && context->tty_path)
5ffa8c818120e35c89becd938d160235c069dd12Zbigniew Jędrzejewski-Szmekstatic bool is_terminal_output(ExecOutput o) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int open_null_as(int flags, int nfd) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering strncpy(sa.un.sun_path, "/run/systemd/journal/stdout", sizeof(sa.un.sun_path));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering context->syslog_identifier ? context->syslog_identifier : ident,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poetteringstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic bool is_terminal_input(ExecInput i) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (is_terminal_input(std_input) && !apply_tty_stdin)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int fixup_output(ExecOutput std_output, int socket_fd) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poetteringstatic int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering return open_null_as(O_RDONLY, STDIN_FILENO);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering assert_not_reached("Unknown input type");
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poetteringstatic int setup_output(const ExecContext *context, int fileno, int socket_fd, const char *ident, const char *unit_id, bool apply_tty_stdin) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering o = fixup_output(context->std_output, socket_fd);
5809560d858f45351856d6fe786a8117306dd0f2Lennart Poettering e = fixup_output(context->std_error, socket_fd);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* This expects the input and output are already set up */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* Don't change the stderr file descriptor if we inherit all
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering * the way and are not on a tty */
40b71e89bae4e51768db4dc50ec64c1e9c96eec4Sebastian Thorarensen !is_terminal_input(context->std_input) &&
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering /* Duplicate from stdout if possible */
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering if (e == o || e == EXEC_OUTPUT_INHERIT)
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
8457f8d6ac7adc6c6ef31378e6e7761cce522141Lennart Poettering } else if (o == EXEC_OUTPUT_INHERIT) {
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* If input got downgraded, inherit the original value */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering return open_terminal_as(tty_path(context), O_WRONLY, fileno);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* If the input is connected to anything that's not a /dev/null, inherit that... */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
63c372cb9df3bee01e3bf8cd7f96f336bddda846Lennart Poettering /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* We need to open /dev/null here anew, to get the right access mode. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering /* We don't reset the terminal if this is just about output */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering return open_terminal_as(tty_path(context), O_WRONLY, fileno);
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt r = connect_logger_as(context, o, ident, unit_id, fileno);
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering "MESSAGE=Failed to connect std%s of %s to the journal socket: %s",
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt "ERRNO=%d", -r,
4a61c3e51e96a747c30598d78ee3a24e7c569e9fZbigniew Jędrzejewski-Szmek r = open_null_as(O_WRONLY, fileno);
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
6baa7db00812437bbc87e73faa1a11b6cf631958Lennart Poettering assert_not_reached("Unknown error type");
d682b3a7e7c7c2941a4d3e193f1e330dbc9fae89Lennart Poetteringstatic int chown_terminal(int fd, uid_t uid) {
4a62c710b62a5a3c7a8a278b810b9d5b5a0c8f4fMichal Schmidt /* This might fail. What matters are the results. */
35e2e347d38cc2f8bd7c38a0d8a5129f5fbb0ab9Lennart Poettering if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
178cc7700c23ac088cd7190d7854282075028d91Lennart Poetteringstatic int setup_confirm_stdio(int *_saved_stdin,
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering int fd = -1, saved_stdin, saved_stdout = -1, r;
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
178cc7700c23ac088cd7190d7854282075028d91Lennart Poettering saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
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;
cap_t d;
if (uid != 0) {
return -errno;
return -errno;
r = -errno;
cap_free(d);
if (cap_set_proc(d) < 0) {
r = -errno;
cap_free(d);
cap_free(d);
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;
goto fail;
if (tty)
goto fail;
goto fail;
goto fail;
close_session = true;
goto fail;
goto fail;
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;
char **argv,
char **environment,
bool apply_permissions,
bool apply_chroot,
bool apply_tty_stdin,
bool confirm_spawn,
const char *cgroup_suffix,
const char *unit_id,
char *line;
int socket_fd;
return -EINVAL;
n_fds = 0;
NULL);
if (!argv)
if (!line)
return log_oom();
NULL);
if (pid < 0)
return -errno;
if (pid == 0) {
int i, err;
unsigned n_env = 0;
bool set_access = false;
r = EXIT_SIGNAL_MASK;
goto fail_child;
if (idle_pipe) {
if (idle_pipe[0] >= 0) {
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, path_get_file_name(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);
if (err < 0) {
r = EXIT_STDERR;
goto fail_child;
if (cgroup_bondings) {
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 = 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;
if (err >= 0)
err = cgroup_bonding_set_task_access_list(cgroup_bondings, 0644, uid, gid, context->control_group_persistent);
if (err < 0) {
r = EXIT_CGROUP;
goto fail_child;
set_access = true;
err = cgroup_bonding_set_task_access_list(cgroup_bondings, (mode_t) -1, (uid_t) -1, (uid_t) -1, context->control_group_persistent);
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;
r = EXIT_NETWORK;
goto fail_child;
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;
r = EXIT_MEMORY;
goto fail_child;
if (n_fds > 0)
r = EXIT_MEMORY;
goto fail_child;
if (home)
r = EXIT_MEMORY;
goto fail_child;
if (username)
r = EXIT_MEMORY;
goto fail_child;
r = EXIT_MEMORY;
goto fail_child;
NULL))) {
r = EXIT_MEMORY;
goto fail_child;
r = EXIT_MEMORY;
goto fail_child;
r = EXIT_EXEC;
log_open();
NULL);
log_close();
_exit(r);
NULL);
if (cgroup_bondings)
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);
if (!prefix)
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,
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) {
fprintf(f,
if (c->capabilities) {
prefix, t);
cap_free(t);
if (c->secure_bits)
if (c->capability_bounding_set_drop) {
for (l = 0; l <= cap_last_cap(); l++)
if ((t = cap_to_name(l))) {
cap_free(t);
if (c->user)
if (c->group)
if (c->pam_name)
if (c->utmp_id)
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;