X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fexecute.c;h=10ce951c599fb02ecede8ca9718b9a14b8286956;hp=9ded1c77882cd3e5750f3f8cb348eedeb6ffbc3b;hb=ba035df230e41bf9d70ebb47915c9472b7884412;hpb=c5da34ef1ba450351638be0d71bddb54677a4d6e diff --git a/src/execute.c b/src/execute.c index 9ded1c778..10ce951c5 100644 --- a/src/execute.c +++ b/src/execute.c @@ -1,4 +1,4 @@ -/*-*- Mode: C; c-basic-offset: 8 -*-*/ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ /*** This file is part of systemd. @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef HAVE_PAM #include @@ -51,6 +52,9 @@ #include "cgroup.h" #include "namespace.h" #include "tcpwrap.h" +#include "exit-status.h" +#include "missing.h" +#include "utmp-wtmp.h" /* This assumes there is a 'tty' group */ #define TTY_MODE 0620 @@ -171,7 +175,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons sa.sa.sa_family = AF_UNIX; strncpy(sa.un.sun_path+1, LOGGER_SOCKET, sizeof(sa.un.sun_path)-1); - if (connect(fd, &sa.sa, sizeof(sa)) < 0) { + if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + 1 + sizeof(LOGGER_SOCKET) - 1) < 0) { close_nointr_nofail(fd); return -errno; } @@ -197,7 +201,7 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons output == EXEC_OUTPUT_KMSG ? "kmsg" : "syslog", context->syslog_priority, context->syslog_identifier ? context->syslog_identifier : ident, - !context->syslog_no_prefix); + context->syslog_level_prefix); if (fd != nfd) { r = dup2(fd, nfd) < 0 ? -errno : nfd; @@ -232,7 +236,10 @@ static bool is_terminal_input(ExecInput i) { i == EXEC_INPUT_TTY_FAIL; } -static int fixup_input(ExecInput std_input, int socket_fd) { +static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) { + + if (is_terminal_input(std_input) && !apply_tty_stdin) + return EXEC_INPUT_NULL; if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0) return EXEC_INPUT_NULL; @@ -248,12 +255,12 @@ static int fixup_output(ExecOutput std_output, int socket_fd) { return std_output; } -static int setup_input(const ExecContext *context, int socket_fd) { +static int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) { ExecInput i; assert(context); - i = fixup_input(context->std_input, socket_fd); + i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); switch (i) { @@ -289,14 +296,14 @@ static int setup_input(const ExecContext *context, int socket_fd) { } } -static int setup_output(const ExecContext *context, int socket_fd, const char *ident) { +static int setup_output(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) { ExecOutput o; ExecInput i; assert(context); assert(ident); - i = fixup_input(context->std_input, socket_fd); + i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); o = fixup_output(context->std_output, socket_fd); /* This expects the input is already set up */ @@ -305,16 +312,20 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i case EXEC_OUTPUT_INHERIT: - /* If the input is connected to a terminal, inherit that... */ + /* If input got downgraded, inherit the original value */ + if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input)) + return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); + + /* If the input is connected to anything that's not a /dev/null, inherit that... */ if (i != EXEC_INPUT_NULL) return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO; - /* For PID 1 stdout is always connected to /dev/null, - * hence reopen the console if out parent is PID1. */ - if (getppid() == 1) - return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO); + /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */ + if (getppid() != 1) + return STDOUT_FILENO; - return STDOUT_FILENO; + /* We need to open /dev/null here anew, to get the + * right access mode. So we fall through */ case EXEC_OUTPUT_NULL: return open_null_as(O_WRONLY, STDOUT_FILENO); @@ -339,14 +350,14 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i } } -static int setup_error(const ExecContext *context, int socket_fd, const char *ident) { +static int setup_error(const ExecContext *context, int socket_fd, const char *ident, bool apply_tty_stdin) { ExecOutput o, e; ExecInput i; assert(context); assert(ident); - i = fixup_input(context->std_input, socket_fd); + i = fixup_input(context->std_input, socket_fd, apply_tty_stdin); o = fixup_output(context->std_output, socket_fd); e = fixup_output(context->std_error, socket_fd); @@ -356,11 +367,12 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id * the way and are not on a tty */ if (e == EXEC_OUTPUT_INHERIT && o == EXEC_OUTPUT_INHERIT && - i != EXEC_INPUT_NULL && + i == EXEC_INPUT_NULL && + !is_terminal_input(context->std_input) && getppid () != 1) return STDERR_FILENO; - /* Duplicate form stdout if possible */ + /* Duplicate from stdout if possible */ if (e == o || e == EXEC_OUTPUT_INHERIT) return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO; @@ -889,6 +901,7 @@ int exec_spawn(ExecCommand *command, char **environment, bool apply_permissions, bool apply_chroot, + bool apply_tty_stdin, bool confirm_spawn, CGroupBonding *cgroup_bondings, pid_t *ret) { @@ -939,7 +952,7 @@ int exec_spawn(ExecCommand *command, const char *username = NULL, *home = NULL; uid_t uid = (uid_t) -1; gid_t gid = (gid_t) -1; - char **our_env = NULL, **pam_env = NULL, **final_env = NULL; + char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL; unsigned n_env = 0; int saved_stdout = -1, saved_stdin = -1; bool keep_stdout = false, keep_stdin = false; @@ -964,7 +977,16 @@ int exec_spawn(ExecCommand *command, goto fail; } - if (!context->no_setsid) + /* Close sockets very early to make sure we don't + * block init reexecution because it cannot bind its + * sockets */ + if (close_all_fds(socket_fd >= 0 ? &socket_fd : fds, + socket_fd >= 0 ? 1 : n_fds) < 0) { + r = EXIT_FDS; + goto fail; + } + + if (!context->same_pgrp) if (setsid() < 0) { r = EXIT_SETSID; goto fail; @@ -985,7 +1007,9 @@ int exec_spawn(ExecCommand *command, } } - if (confirm_spawn) { + /* We skip the confirmation step if we shall not apply the TTY */ + if (confirm_spawn && + (!is_terminal_input(context->std_input) || apply_tty_stdin)) { char response; /* Set up terminal for the question */ @@ -1017,38 +1041,55 @@ int exec_spawn(ExecCommand *command, goto fail; } + /* If a socket is connected to STDIN/STDOUT/STDERR, we + * must sure to drop O_NONBLOCK */ + if (socket_fd >= 0) + fd_nonblock(socket_fd, false); + if (!keep_stdin) - if (setup_input(context, socket_fd) < 0) { + if (setup_input(context, socket_fd, apply_tty_stdin) < 0) { r = EXIT_STDIN; goto fail; } if (!keep_stdout) - if (setup_output(context, socket_fd, file_name_from_path(command->path)) < 0) { + if (setup_output(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { r = EXIT_STDOUT; goto fail; } - if (setup_error(context, socket_fd, file_name_from_path(command->path)) < 0) { + if (setup_error(context, socket_fd, file_name_from_path(command->path), apply_tty_stdin) < 0) { r = EXIT_STDERR; goto fail; } if (cgroup_bondings) - if ((r = cgroup_bonding_install_list(cgroup_bondings, 0)) < 0) { + if (cgroup_bonding_install_list(cgroup_bondings, 0) < 0) { r = EXIT_CGROUP; goto fail; } - if (context->oom_adjust_set) { + if (context->oom_score_adjust_set) { char t[16]; - snprintf(t, sizeof(t), "%i", context->oom_adjust); + snprintf(t, sizeof(t), "%i", context->oom_score_adjust); char_array_0(t); - if (write_one_line_file("/proc/self/oom_adj", t) < 0) { - r = EXIT_OOM_ADJUST; - goto fail; + if (write_one_line_file("/proc/self/oom_score_adj", t) < 0) { + /* Compatibility with Linux <= 2.6.35 */ + + int adj; + + adj = (context->oom_score_adjust * -OOM_DISABLE) / OOM_SCORE_ADJ_MAX; + adj = CLAMP(adj, OOM_DISABLE, OOM_ADJUST_MAX); + + snprintf(t, sizeof(t), "%i", adj); + char_array_0(t); + + if (write_one_line_file("/proc/self/oom_adj", t) < 0) { + r = EXIT_OOM_ADJUST; + goto fail; + } } } @@ -1071,8 +1112,8 @@ int exec_spawn(ExecCommand *command, } } - if (context->cpu_affinity_set) - if (sched_setaffinity(0, sizeof(context->cpu_affinity), &context->cpu_affinity) < 0) { + if (context->cpuset) + if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) { r = EXIT_CPUAFFINITY; goto fail; } @@ -1083,12 +1124,15 @@ int exec_spawn(ExecCommand *command, goto fail; } - if (context->timer_slack_ns_set) - if (prctl(PR_SET_TIMERSLACK, context->timer_slack_ns_set) < 0) { + if (context->timer_slack_nsec_set) + if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) { r = EXIT_TIMERSLACK; goto fail; } + if (context->utmp_id) + utmp_put_init_process(0, context->utmp_id, getpid(), getsid(0), context->tty_path); + if (context->user) { username = context->user; if (get_user_creds(&username, &uid, &gid, &home) < 0) { @@ -1105,16 +1149,6 @@ int exec_spawn(ExecCommand *command, #ifdef HAVE_PAM if (context->pam_name && username) { - /* Make sure no fds leak into the PAM - * supervisor process. We will call this later - * on again to make sure that any fds leaked - * by the PAM modules get closed before our - * exec(). */ - if (close_all_fds(fds, n_fds) < 0) { - r = EXIT_FDS; - goto fail; - } - if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) < 0) { r = EXIT_PAM; goto fail; @@ -1174,6 +1208,8 @@ int exec_spawn(ExecCommand *command, free(d); } + /* We repeat the fd closing here, to make sure that + * nothing is leaked from the PAM modules */ if (close_all_fds(fds, n_fds) < 0 || shift_fds(fds, n_fds) < 0 || flags_fds(fds, n_fds, context->non_blocking) < 0) { @@ -1216,7 +1252,7 @@ int exec_spawn(ExecCommand *command, } } - if (!(our_env = new0(char*, 6))) { + if (!(our_env = new0(char*, 7))) { r = EXIT_MEMORY; goto fail; } @@ -1241,7 +1277,15 @@ int exec_spawn(ExecCommand *command, goto fail; } - assert(n_env <= 6); + 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))))) { + r = EXIT_MEMORY; + goto fail; + } + + assert(n_env <= 7); if (!(final_env = strv_env_merge( 4, @@ -1254,13 +1298,21 @@ int exec_spawn(ExecCommand *command, goto fail; } - execve(command->path, argv, final_env); + if (!(final_argv = replace_env_argv(argv, final_env))) { + r = EXIT_MEMORY; + goto fail; + } + + final_env = strv_env_clean(final_env); + + execve(command->path, final_argv, final_env); r = EXIT_EXEC; fail: strv_free(our_env); strv_free(final_env); strv_free(pam_env); + strv_free(final_argv); if (saved_stdin >= 0) close_nointr_nofail(saved_stdin); @@ -1281,8 +1333,7 @@ int exec_spawn(ExecCommand *command, log_debug("Forked %s as %lu", command->path, (unsigned long) pid); - command->exec_status.pid = pid; - dual_timestamp_get(&command->exec_status.start_timestamp); + exec_status_start(&command->exec_status, pid); *ret = pid; return 0; @@ -1295,7 +1346,10 @@ void exec_context_init(ExecContext *c) { c->ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 0); c->cpu_sched_policy = SCHED_OTHER; c->syslog_priority = LOG_DAEMON|LOG_INFO; + c->syslog_level_prefix = true; c->mount_flags = MS_SHARED; + c->kill_signal = SIGTERM; + c->send_sigkill = true; } void exec_context_done(ExecContext *c) { @@ -1350,6 +1404,12 @@ void exec_context_done(ExecContext *c) { strv_free(c->inaccessible_dirs); c->inaccessible_dirs = NULL; + + if (c->cpuset) + CPU_FREE(c->cpuset); + + free(c->utmp_id); + c->utmp_id = NULL; } void exec_command_done(ExecCommand *c) { @@ -1433,10 +1493,10 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { "%sNice: %i\n", prefix, c->nice); - if (c->oom_adjust_set) + if (c->oom_score_adjust_set) fprintf(f, - "%sOOMAdjust: %i\n", - prefix, c->oom_adjust); + "%sOOMScoreAdjust: %i\n", + prefix, c->oom_score_adjust); for (i = 0; i < RLIM_NLIMITS; i++) if (c->rlimit[i]) @@ -1458,16 +1518,16 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { prefix, c->cpu_sched_priority, prefix, yes_no(c->cpu_sched_reset_on_fork)); - if (c->cpu_affinity_set) { + if (c->cpuset) { fprintf(f, "%sCPUAffinity:", prefix); - for (i = 0; i < CPU_SETSIZE; i++) - if (CPU_ISSET(i, &c->cpu_affinity)) + 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); fputs("\n", f); } - if (c->timer_slack_ns_set) - fprintf(f, "%sTimerSlackNS: %lu\n", prefix, c->timer_slack_ns); + if (c->timer_slack_nsec_set) + fprintf(f, "%sTimerSlackNSec: %lu\n", prefix, c->timer_slack_nsec); fprintf(f, "%sStandardInput: %s\n" @@ -1556,16 +1616,44 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { strv_fprintf(f, c->inaccessible_dirs); fputs("\n", f); } + + fprintf(f, + "%sKillMode: %s\n" + "%sKillSignal: SIG%s\n" + "%sSendSIGKILL: %s\n", + prefix, kill_mode_to_string(c->kill_mode), + prefix, signal_to_string(c->kill_signal), + prefix, yes_no(c->send_sigkill)); + + if (c->utmp_id) + fprintf(f, + "%sUtmpIdentifier: %s\n", + prefix, c->utmp_id); } -void exec_status_fill(ExecStatus *s, pid_t pid, int code, int status) { +void exec_status_start(ExecStatus *s, pid_t pid) { assert(s); + zero(*s); + s->pid = pid; + dual_timestamp_get(&s->start_timestamp); +} + +void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status, const char *utmp_id) { + assert(s); + + if ((s->pid && s->pid != pid) || + !s->start_timestamp.realtime <= 0) + zero(*s); + s->pid = pid; dual_timestamp_get(&s->exit_timestamp); s->code = code; s->status = status; + + if (utmp_id) + utmp_put_dead_process(utmp_id, pid, code, status); } void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) { @@ -1717,117 +1805,6 @@ int exec_command_set(ExecCommand *c, const char *path, ...) { return 0; } -const char* exit_status_to_string(ExitStatus status) { - - /* We cast to int here, so that -Wenum doesn't complain that - * EXIT_SUCCESS/EXIT_FAILURE aren't in the enum */ - - switch ((int) status) { - - case EXIT_SUCCESS: - return "SUCCESS"; - - case EXIT_FAILURE: - return "FAILURE"; - - case EXIT_INVALIDARGUMENT: - return "INVALIDARGUMENT"; - - case EXIT_NOTIMPLEMENTED: - return "NOTIMPLEMENTED"; - - case EXIT_NOPERMISSION: - return "NOPERMISSION"; - - case EXIT_NOTINSTALLED: - return "NOTINSSTALLED"; - - case EXIT_NOTCONFIGURED: - return "NOTCONFIGURED"; - - case EXIT_NOTRUNNING: - return "NOTRUNNING"; - - case EXIT_CHDIR: - return "CHDIR"; - - case EXIT_NICE: - return "NICE"; - - case EXIT_FDS: - return "FDS"; - - case EXIT_EXEC: - return "EXEC"; - - case EXIT_MEMORY: - return "MEMORY"; - - case EXIT_LIMITS: - return "LIMITS"; - - case EXIT_OOM_ADJUST: - return "OOM_ADJUST"; - - case EXIT_SIGNAL_MASK: - return "SIGNAL_MASK"; - - case EXIT_STDIN: - return "STDIN"; - - case EXIT_STDOUT: - return "STDOUT"; - - case EXIT_CHROOT: - return "CHROOT"; - - case EXIT_IOPRIO: - return "IOPRIO"; - - case EXIT_TIMERSLACK: - return "TIMERSLACK"; - - case EXIT_SECUREBITS: - return "SECUREBITS"; - - case EXIT_SETSCHEDULER: - return "SETSCHEDULER"; - - case EXIT_CPUAFFINITY: - return "CPUAFFINITY"; - - case EXIT_GROUP: - return "GROUP"; - - case EXIT_USER: - return "USER"; - - case EXIT_CAPABILITIES: - return "CAPABILITIES"; - - case EXIT_CGROUP: - return "CGROUP"; - - case EXIT_SETSID: - return "SETSID"; - - case EXIT_CONFIRM: - return "CONFIRM"; - - case EXIT_STDERR: - return "STDERR"; - - case EXIT_TCPWRAP: - return "TCPWRAP"; - - case EXIT_PAM: - return "PAM"; - - default: - return NULL; - } -} - static const char* const exec_input_table[_EXEC_INPUT_MAX] = { [EXEC_INPUT_NULL] = "null", [EXEC_INPUT_TTY] = "tty", @@ -1836,6 +1813,8 @@ static const char* const exec_input_table[_EXEC_INPUT_MAX] = { [EXEC_INPUT_SOCKET] = "socket" }; +DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); + static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { [EXEC_OUTPUT_INHERIT] = "inherit", [EXEC_OUTPUT_NULL] = "null", @@ -1847,4 +1826,19 @@ static const char* const exec_output_table[_EXEC_OUTPUT_MAX] = { DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput); -DEFINE_STRING_TABLE_LOOKUP(exec_input, ExecInput); +static const char* const kill_mode_table[_KILL_MODE_MAX] = { + [KILL_CONTROL_GROUP] = "control-group", + [KILL_PROCESS_GROUP] = "process-group", + [KILL_PROCESS] = "process", + [KILL_NONE] = "none" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_mode, KillMode); + +static const char* const kill_who_table[_KILL_WHO_MAX] = { + [KILL_MAIN] = "main", + [KILL_CONTROL] = "control", + [KILL_ALL] = "all" +}; + +DEFINE_STRING_TABLE_LOOKUP(kill_who, KillWho);