chiark / gitweb /
Spelling Corrections
[elogind.git] / src / execute.c
index d7239d544000f40f8073d3c9c25e3263d26c0a8e..d6f09e26fea39c9519fe5cd13a485553f44e516c 100644 (file)
@@ -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 <pwd.h>
 #include <sys/mount.h>
 #include <linux/fs.h>
+#include <linux/oom.h>
 
 #ifdef HAVE_PAM
 #include <security/pam_appl.h>
@@ -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;
         }
@@ -194,7 +198,10 @@ static int connect_logger_as(const ExecContext *context, ExecOutput output, cons
                 "%i\n"
                 "%s\n"
                 "%i\n",
-                output == EXEC_OUTPUT_KMSG ? "kmsg" : "syslog",
+                output == EXEC_OUTPUT_KMSG ?             "kmsg" :
+                output == EXEC_OUTPUT_KMSG_AND_CONSOLE ? "kmsg+console" :
+                output == EXEC_OUTPUT_SYSLOG ?           "syslog" :
+                                                         "syslog+console",
                 context->syslog_priority,
                 context->syslog_identifier ? context->syslog_identifier : ident,
                 context->syslog_level_prefix);
@@ -232,7 +239,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 +258,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 +299,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,6 +315,10 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i
 
         case EXEC_OUTPUT_INHERIT:
 
+                /* 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;
@@ -327,7 +341,9 @@ static int setup_output(const ExecContext *context, int socket_fd, const char *i
                 return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
 
         case EXEC_OUTPUT_SYSLOG:
+        case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
         case EXEC_OUTPUT_KMSG:
+        case EXEC_OUTPUT_KMSG_AND_CONSOLE:
                 return connect_logger_as(context, o, ident, STDOUT_FILENO);
 
         case EXEC_OUTPUT_SOCKET:
@@ -339,14 +355,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);
 
@@ -357,10 +373,11 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id
         if (e == EXEC_OUTPUT_INHERIT &&
             o == EXEC_OUTPUT_INHERIT &&
             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;
 
@@ -377,7 +394,9 @@ static int setup_error(const ExecContext *context, int socket_fd, const char *id
                 return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
 
         case EXEC_OUTPUT_SYSLOG:
+        case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
         case EXEC_OUTPUT_KMSG:
+        case EXEC_OUTPUT_KMSG_AND_CONSOLE:
                 return connect_logger_as(context, e, ident, STDERR_FILENO);
 
         case EXEC_OUTPUT_SOCKET:
@@ -573,7 +592,7 @@ static int get_user_creds(const char **username, uid_t *uid, gid_t *gid, const c
 
                 /* If there are multiple users with the same id, make
                  * sure to leave $USER to the configured value instead
-                 * of the first occurence in the database. However if
+                 * of the first occurrence in the database. However if
                  * the uid was configured by a numeric uid, then let's
                  * pick the real username from /etc/passwd. */
                 if (*username && p)
@@ -598,7 +617,7 @@ static int enforce_groups(const ExecContext *context, const char *username, gid_
 
         assert(context);
 
-        /* Lookup and ser GID and supplementary group list. Here too
+        /* Lookup and set GID and supplementary group list. Here too
          * we avoid NSS lookups for gid=0. */
 
         if (context->group || username) {
@@ -681,7 +700,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
 
                 /* First step: If we need to keep capabilities but
                  * drop privileges we need to make sure we keep our
-                 * caps, whiel we drop priviliges. */
+                 * caps, whiel we drop privileges. */
                 if (uid != 0) {
                         int sb = context->secure_bits|SECURE_KEEP_CAPS;
 
@@ -690,7 +709,7 @@ static int enforce_user(const ExecContext *context, uid_t uid) {
                                         return -errno;
                 }
 
-                /* Second step: set the capabilites. This will reduce
+                /* Second step: set the capabilities. This will reduce
                  * the capabilities to the minimum we need. */
 
                 if (!(d = cap_dup(context->capabilities)))
@@ -761,7 +780,7 @@ static int setup_pam(
         assert(pam_env);
 
         /* We set up PAM in the parent process, then fork. The child
-         * will then stay around untill killed via PR_GET_PDEATHSIG or
+         * will then stay around until killed via PR_GET_PDEATHSIG or
          * systemd via the cgroup logic. It will then remove the PAM
          * session again. The parent process will exec() the actual
          * daemon. We do things this way to ensure that the main PID
@@ -822,7 +841,7 @@ static int setup_pam(
 
                 /* Wait until our parent died. This will most likely
                  * not work since the kernel does not allow
-                 * unpriviliged paretns kill their priviliged children
+                 * unprivileged parents kill their privileged children
                  * this way. We rely on the control groups kill logic
                  * to do the rest for us. */
                 if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
@@ -889,6 +908,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 +959,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,6 +984,15 @@ int exec_spawn(ExecCommand *command,
                         goto fail;
                 }
 
+                /* 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;
@@ -985,7 +1014,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 +1048,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;
+                                }
                         }
                 }
 
@@ -1089,6 +1137,9 @@ int exec_spawn(ExecCommand *command,
                                 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 +1156,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 +1215,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) {
@@ -1199,7 +1242,7 @@ int exec_spawn(ExecCommand *command,
                                         goto fail;
                                 }
 
-                        /* PR_GET_SECUREBITS is not priviliged, while
+                        /* PR_GET_SECUREBITS is not privileged, while
                          * PR_SET_SECUREBITS is. So to suppress
                          * potential EPERMs we'll try not to call
                          * PR_SET_SECUREBITS unless necessary. */
@@ -1216,7 +1259,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 +1284,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 +1305,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);
@@ -1296,6 +1355,8 @@ void exec_context_init(ExecContext *c) {
         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) {
@@ -1353,6 +1414,9 @@ void exec_context_done(ExecContext *c) {
 
         if (c->cpuset)
                 CPU_FREE(c->cpuset);
+
+        free(c->utmp_id);
+        c->utmp_id = NULL;
 }
 
 void exec_command_done(ExecCommand *c) {
@@ -1436,10 +1500,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])
@@ -1486,7 +1550,9 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
                         prefix, c->tty_path);
 
         if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KMSG ||
-            c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG)
+            c->std_output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_output == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
+            c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KMSG ||
+            c->std_error == EXEC_OUTPUT_SYSLOG_AND_CONSOLE || c->std_error == EXEC_OUTPUT_KMSG_AND_CONSOLE)
                 fprintf(f,
                         "%sSyslogFacility: %s\n"
                         "%sSyslogLevel: %s\n",
@@ -1559,6 +1625,19 @@ 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_start(ExecStatus *s, pid_t pid) {
@@ -1569,7 +1648,7 @@ void exec_status_start(ExecStatus *s, pid_t pid) {
         dual_timestamp_get(&s->start_timestamp);
 }
 
-void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status) {
+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) ||
@@ -1581,6 +1660,9 @@ void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status) {
 
         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) {
@@ -1697,7 +1779,7 @@ void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
         assert(e);
 
         if (*l) {
-                /* It's kinda important that we keep the order here */
+                /* 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);
         } else
@@ -1732,117 +1814,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",
@@ -1851,15 +1822,34 @@ 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",
         [EXEC_OUTPUT_TTY] = "tty",
         [EXEC_OUTPUT_SYSLOG] = "syslog",
+        [EXEC_OUTPUT_SYSLOG_AND_CONSOLE] = "syslog+console",
         [EXEC_OUTPUT_KMSG] = "kmsg",
+        [EXEC_OUTPUT_KMSG_AND_CONSOLE] = "kmsg+console",
         [EXEC_OUTPUT_SOCKET] = "socket"
 };
 
 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);