-/*-*- Mode: C; c-basic-offset: 8 -*-*/
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
#include <sys/mount.h>
#include <linux/fs.h>
+#ifdef HAVE_PAM
+#include <security/pam_appl.h>
+#endif
+
#include "execute.h"
#include "strv.h"
#include "macro.h"
#include "securebits.h"
#include "cgroup.h"
#include "namespace.h"
+#include "tcpwrap.h"
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
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, sizeof(sa_family_t) + 1 + sizeof(LOGGER_SOCKET) - 1) < 0) {
close_nointr_nofail(fd);
return -errno;
}
"%i\n"
"%s\n"
"%i\n",
- output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
+ 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;
i == EXEC_INPUT_TTY_FAIL;
}
-static int fixup_input(const ExecContext *context, int socket_fd) {
- assert(context);
+static int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
- if (socket_fd < 0 && context->std_input == EXEC_INPUT_SOCKET)
+ if (is_terminal_input(std_input) && !apply_tty_stdin)
return EXEC_INPUT_NULL;
- return context->std_input;
-}
-
-static int fixup_output(const ExecContext *context, int socket_fd) {
- assert(context);
-
- if (socket_fd < 0 && context->std_output == EXEC_OUTPUT_SOCKET)
- return EXEC_OUTPUT_INHERIT;
+ if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
+ return EXEC_INPUT_NULL;
- return context->std_output;
+ return std_input;
}
-static int fixup_error(const ExecContext *context, int socket_fd) {
- assert(context);
+static int fixup_output(ExecOutput std_output, int socket_fd) {
- if (socket_fd < 0 && context->std_error == EXEC_OUTPUT_SOCKET)
+ if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
return EXEC_OUTPUT_INHERIT;
- return context->std_error;
+ 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, socket_fd);
+ i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
switch (i) {
}
}
-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, socket_fd);
- o = fixup_output(context, 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 */
case EXEC_OUTPUT_INHERIT:
- /* If the input is connected to a terminal, inherit that... */
- if (is_terminal_input(i) || i == EXEC_INPUT_SOCKET)
+ /* 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;
- return STDIN_FILENO;
+ /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
+ if (getppid() != 1)
+ 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);
return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_KERNEL:
+ case EXEC_OUTPUT_KMSG:
return connect_logger_as(context, o, ident, STDOUT_FILENO);
case EXEC_OUTPUT_SOCKET:
}
}
-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, socket_fd);
- o = fixup_output(context, socket_fd);
- e = fixup_error(context, 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);
/* This expects the input and output are already set up */
* the way and are not on a tty */
if (e == EXEC_OUTPUT_INHERIT &&
o == EXEC_OUTPUT_INHERIT &&
- !is_terminal_input(i))
+ 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;
return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
case EXEC_OUTPUT_SYSLOG:
- case EXEC_OUTPUT_KERNEL:
+ case EXEC_OUTPUT_KMSG:
return connect_logger_as(context, e, ident, STDERR_FILENO);
case EXEC_OUTPUT_SOCKET:
return 0;
}
+#ifdef HAVE_PAM
+
+static int null_conv(
+ int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr) {
+
+ /* We don't support conversations */
+
+ return PAM_CONV_ERR;
+}
+
+static int setup_pam(
+ const char *name,
+ const char *user,
+ const char *tty,
+ char ***pam_env,
+ int fds[], unsigned n_fds) {
+
+ static const struct pam_conv conv = {
+ .conv = null_conv,
+ .appdata_ptr = NULL
+ };
+
+ pam_handle_t *handle = NULL;
+ sigset_t ss, old_ss;
+ int pam_code = PAM_SUCCESS;
+ char **e = NULL;
+ bool close_session = false;
+ pid_t pam_pid = 0, parent_pid;
+
+ assert(name);
+ assert(user);
+ 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
+ * 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
+ * of the daemon is the one we initially fork()ed. */
+
+ if ((pam_code = pam_start(name, user, &conv, &handle)) != PAM_SUCCESS) {
+ handle = NULL;
+ goto fail;
+ }
+
+ if (tty)
+ if ((pam_code = pam_set_item(handle, PAM_TTY, tty)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_acct_mgmt(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((pam_code = pam_open_session(handle, PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ close_session = true;
+
+ if ((pam_code = pam_setcred(handle, PAM_ESTABLISH_CRED | PAM_SILENT)) != PAM_SUCCESS)
+ goto fail;
+
+ if ((!(e = pam_getenvlist(handle)))) {
+ pam_code = PAM_BUF_ERR;
+ goto fail;
+ }
+
+ /* Block SIGTERM, so that we know that it won't get lost in
+ * the child */
+ if (sigemptyset(&ss) < 0 ||
+ sigaddset(&ss, SIGTERM) < 0 ||
+ sigprocmask(SIG_BLOCK, &ss, &old_ss) < 0)
+ goto fail;
+
+ parent_pid = getpid();
+
+ if ((pam_pid = fork()) < 0)
+ goto fail;
+
+ if (pam_pid == 0) {
+ int sig;
+ int r = EXIT_PAM;
+
+ /* The child's job is to reset the PAM session on
+ * termination */
+
+ /* This string must fit in 10 chars (i.e. the length
+ * of "/sbin/init") */
+ rename_process("sd:pam");
+
+ /* Make sure we don't keep open the passed fds in this
+ child. We assume that otherwise only those fds are
+ open here that have been opened by PAM. */
+ close_many(fds, n_fds);
+
+ /* Wait until our parent died. This will most likely
+ * not work since the kernel does not allow
+ * unpriviliged paretns kill their priviliged children
+ * this way. We rely on the control groups kill logic
+ * to do the rest for us. */
+ if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
+ goto child_finish;
+
+ /* Check if our parent process might already have
+ * died? */
+ if (getppid() == parent_pid) {
+ if (sigwait(&ss, &sig) < 0)
+ goto child_finish;
+
+ assert(sig == SIGTERM);
+ }
+
+ /* Only if our parent died we'll end the session */
+ if (getppid() != parent_pid)
+ if ((pam_code = pam_close_session(handle, PAM_DATA_SILENT)) != PAM_SUCCESS)
+ goto child_finish;
+
+ r = 0;
+
+ child_finish:
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ _exit(r);
+ }
+
+ /* If the child was forked off successfully it will do all the
+ * cleanups, so forget about the handle here. */
+ handle = NULL;
+
+ /* Unblock SIGSUR1 again in the parent */
+ if (sigprocmask(SIG_SETMASK, &old_ss, NULL) < 0)
+ goto fail;
+
+ /* We close the log explicitly here, since the PAM modules
+ * might have opened it, but we don't want this fd around. */
+ closelog();
+
+ return 0;
+
+fail:
+ if (handle) {
+ if (close_session)
+ pam_code = pam_close_session(handle, PAM_DATA_SILENT);
+
+ pam_end(handle, pam_code | PAM_DATA_SILENT);
+ }
+
+ strv_free(e);
+
+ closelog();
+
+ if (pam_pid > 1)
+ kill(pam_pid, SIGTERM);
+
+ return EXIT_PAM;
+}
+#endif
+
int exec_spawn(ExecCommand *command,
char **argv,
const ExecContext *context,
char **environment,
bool apply_permissions,
bool apply_chroot,
+ bool apply_tty_stdin,
bool confirm_spawn,
CGroupBonding *cgroup_bondings,
pid_t *ret) {
const char *username = NULL, *home = NULL;
uid_t uid = (uid_t) -1;
gid_t gid = (gid_t) -1;
- char **our_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;
/* child */
- reset_all_signal_handlers();
+ /* This string must fit in 10 chars (i.e. the length
+ * of "/sbin/init") */
+ rename_process("sd:exec");
+
+ /* We reset exactly these signals, since they are the
+ * only ones we set to SIG_IGN in the main daemon. All
+ * others we leave untouched because we set them to
+ * SIG_DFL or a valid handler initially, both of which
+ * will be demoted to SIG_DFL. */
+ default_signals(SIGNALS_CRASH_HANDLER,
+ SIGNALS_IGNORE, -1);
if (sigemptyset(&ss) < 0 ||
sigprocmask(SIG_SETMASK, &ss, NULL) < 0) {
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;
}
- if (confirm_spawn) {
+ if (context->tcpwrap_name) {
+ if (socket_fd >= 0)
+ if (!socket_tcpwrap(socket_fd, context->tcpwrap_name)) {
+ r = EXIT_TCPWRAP;
+ goto fail;
+ }
+
+ for (i = 0; i < (int) n_fds; i++) {
+ if (!socket_tcpwrap(fds[i], context->tcpwrap_name)) {
+ r = EXIT_TCPWRAP;
+ goto fail;
+ }
+ }
+ }
+
+ /* 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 */
}
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->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;
}
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 (strv_length(context->read_write_dirs) > 0 ||
- strv_length(context->read_only_dirs) > 0 ||
- strv_length(context->inaccessible_dirs) > 0 ||
- context->mount_flags != MS_SHARED ||
- context->private_tmp)
- if ((r = setup_namespace(
- context->read_write_dirs,
- context->read_only_dirs,
- context->inaccessible_dirs,
- context->private_tmp,
- context->mount_flags)) < 0)
- goto fail;
-
if (context->user) {
username = context->user;
if (get_user_creds(&username, &uid, &gid, &home) < 0) {
}
}
+#ifdef HAVE_PAM
+ if (context->pam_name && username) {
+ if (setup_pam(context->pam_name, username, context->tty_path, &pam_env, fds, n_fds) < 0) {
+ r = EXIT_PAM;
+ goto fail;
+ }
+ }
+#endif
+
if (apply_permissions)
if (enforce_groups(context, username, uid) < 0) {
r = EXIT_GROUP;
umask(context->umask);
+ if (strv_length(context->read_write_dirs) > 0 ||
+ strv_length(context->read_only_dirs) > 0 ||
+ strv_length(context->inaccessible_dirs) > 0 ||
+ context->mount_flags != MS_SHARED ||
+ context->private_tmp)
+ if ((r = setup_namespace(
+ context->read_write_dirs,
+ context->read_only_dirs,
+ context->inaccessible_dirs,
+ context->private_tmp,
+ context->mount_flags)) < 0)
+ goto fail;
+
if (apply_chroot) {
if (context->root_directory)
if (chroot(context->root_directory) < 0) {
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) {
}
if (n_fds > 0)
- if (asprintf(our_env + n_env++, "LISTEN_PID=%llu", (unsigned long long) getpid()) < 0 ||
+ if (asprintf(our_env + n_env++, "LISTEN_PID=%lu", (unsigned long) getpid()) < 0 ||
asprintf(our_env + n_env++, "LISTEN_FDS=%u", n_fds) < 0) {
r = EXIT_MEMORY;
goto fail;
assert(n_env <= 6);
- if (!(final_env = strv_env_merge(environment, our_env, context->environment, NULL))) {
+ if (!(final_env = strv_env_merge(
+ 4,
+ environment,
+ our_env,
+ context->environment,
+ pam_env,
+ NULL))) {
r = EXIT_MEMORY;
goto fail;
}
- execve(command->path, argv, final_env);
+ if (!(final_argv = replace_env_argv(argv, final_env))) {
+ r = EXIT_MEMORY;
+ goto fail;
+ }
+
+ 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);
if (cgroup_bondings)
cgroup_bonding_install_list(cgroup_bondings, pid);
- log_debug("Forked %s as %llu", command->path, (unsigned long long) pid);
+ log_debug("Forked %s as %lu", command->path, (unsigned long) pid);
- command->exec_status.pid = pid;
- command->exec_status.start_timestamp = now(CLOCK_REALTIME);
+ exec_status_start(&command->exec_status, pid);
*ret = pid;
return 0;
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;
}
void exec_context_done(ExecContext *c) {
free(c->tty_path);
c->tty_path = NULL;
+ free(c->tcpwrap_name);
+ c->tcpwrap_name = NULL;
+
free(c->syslog_identifier);
c->syslog_identifier = NULL;
strv_free(c->supplementary_groups);
c->supplementary_groups = NULL;
+ free(c->pam_name);
+ c->pam_name = NULL;
+
if (c->capabilities) {
cap_free(c->capabilities);
c->capabilities = NULL;
strv_free(c->inaccessible_dirs);
c->inaccessible_dirs = NULL;
+
+ if (c->cpuset)
+ CPU_FREE(c->cpuset);
}
void exec_command_done(ExecCommand *c) {
for (e = c->environment; *e; e++)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
+ if (c->tcpwrap_name)
+ fprintf(f,
+ "%sTCPWrapName: %s\n",
+ prefix, c->tcpwrap_name);
+
if (c->nice_set)
fprintf(f,
"%sNice: %i\n",
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"
"%sTTYPath: %s\n",
prefix, c->tty_path);
- if (c->std_output == EXEC_OUTPUT_SYSLOG || c->std_output == EXEC_OUTPUT_KERNEL ||
- c->std_error == EXEC_OUTPUT_SYSLOG || c->std_error == EXEC_OUTPUT_KERNEL)
+ 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)
fprintf(f,
"%sSyslogFacility: %s\n"
"%sSyslogLevel: %s\n",
}
if (c->user)
- fprintf(f, "%sUser: %s", prefix, c->user);
+ fprintf(f, "%sUser: %s\n", prefix, c->user);
if (c->group)
- fprintf(f, "%sGroup: %s", prefix, c->group);
+ fprintf(f, "%sGroup: %s\n", prefix, c->group);
if (strv_length(c->supplementary_groups) > 0) {
fprintf(f, "%sSupplementaryGroups:", prefix);
fputs("\n", f);
}
+ if (c->pam_name)
+ fprintf(f, "%sPAMName: %s\n", prefix, c->pam_name);
+
if (strv_length(c->read_write_dirs) > 0) {
fprintf(f, "%sReadWriteDirs:", prefix);
strv_fprintf(f, c->read_write_dirs);
strv_fprintf(f, c->inaccessible_dirs);
fputs("\n", f);
}
+
+ fprintf(f,
+ "%sKillMode: %s\n"
+ "%sKillSignal: SIG%s\n",
+ prefix, kill_mode_to_string(c->kill_mode),
+ prefix, signal_to_string(c->kill_signal));
}
-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;
- s->exit_timestamp = now(CLOCK_REALTIME);
+ dual_timestamp_get(&s->start_timestamp);
+}
+
+void exec_status_exit(ExecStatus *s, pid_t pid, int code, int status) {
+ 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;
return;
fprintf(f,
- "%sPID: %llu\n",
- prefix, (unsigned long long) s->pid);
+ "%sPID: %lu\n",
+ prefix, (unsigned long) s->pid);
- if (s->start_timestamp > 0)
+ if (s->start_timestamp.realtime > 0)
fprintf(f,
"%sStart Timestamp: %s\n",
- prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp));
+ prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
- if (s->exit_timestamp > 0)
+ if (s->exit_timestamp.realtime > 0)
fprintf(f,
"%sExit Timestamp: %s\n"
"%sExit Code: %s\n"
"%sExit Status: %i\n",
- prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp),
+ prefix, format_timestamp(buf, sizeof(buf), s->exit_timestamp.realtime),
prefix, sigchld_code_to_string(s->code),
prefix, s->status);
}
case EXIT_STDERR:
return "STDERR";
+ case EXIT_TCPWRAP:
+ return "TCPWRAP";
+
+ case EXIT_PAM:
+ return "PAM";
+
default:
return NULL;
}
[EXEC_OUTPUT_NULL] = "null",
[EXEC_OUTPUT_TTY] = "tty",
[EXEC_OUTPUT_SYSLOG] = "syslog",
- [EXEC_OUTPUT_KERNEL] = "kernel",
+ [EXEC_OUTPUT_KMSG] = "kmsg",
[EXEC_OUTPUT_SOCKET] = "socket"
};