+ if ((r = fd_nonblock(fds[i], nonblock)) < 0)
+ return r;
+
+ /* We unconditionally drop FD_CLOEXEC from the fds,
+ * since after all we want to pass these fds to our
+ * children */
+
+ if ((r = fd_cloexec(fds[i], false)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
+static const char *tty_path(const ExecContext *context) {
+ assert(context);
+
+ if (context->tty_path)
+ return context->tty_path;
+
+ return "/dev/console";
+}
+
+static int open_null_as(int flags, int nfd) {
+ int fd, r;
+
+ assert(nfd >= 0);
+
+ if ((fd = open("/dev/null", flags|O_NOCTTY)) < 0)
+ return -errno;
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+
+static int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, int nfd) {
+ int fd, r;
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa;
+
+ assert(context);
+ assert(output < _EXEC_OUTPUT_MAX);
+ assert(ident);
+ assert(nfd >= 0);
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ return -errno;
+
+ zero(sa);
+ 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) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ if (shutdown(fd, SHUT_RD) < 0) {
+ close_nointr_nofail(fd);
+ return -errno;
+ }
+
+ /* We speak a very simple protocol between log server
+ * and client: one line for the log destination (kmsg
+ * or syslog), followed by the priority field,
+ * followed by the process name. Since we replaced
+ * stdin/stderr we simple use stdio to write to
+ * it. Note that we use stderr, to minimize buffer
+ * flushing issues. */
+
+ dprintf(fd,
+ "%s\n"
+ "%i\n"
+ "%s\n",
+ output == EXEC_OUTPUT_KERNEL ? "kmsg" : "syslog",
+ context->syslog_priority,
+ context->syslog_identifier ? context->syslog_identifier : ident);
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+static int open_terminal_as(const char *path, mode_t mode, int nfd) {
+ int fd, r;
+
+ assert(path);
+ assert(nfd >= 0);
+
+ if ((fd = open_terminal(path, mode | O_NOCTTY)) < 0)
+ return fd;
+
+ if (fd != nfd) {
+ r = dup2(fd, nfd) < 0 ? -errno : nfd;
+ close_nointr_nofail(fd);
+ } else
+ r = nfd;
+
+ return r;
+}
+
+static bool is_terminal_input(ExecInput i) {
+ return
+ i == EXEC_INPUT_TTY ||
+ i == EXEC_INPUT_TTY_FORCE ||
+ i == EXEC_INPUT_TTY_FAIL;
+}
+
+static int fixup_input(const ExecContext *context, int socket_fd) {
+ assert(context);
+
+ if (socket_fd < 0 && context->std_input == EXEC_INPUT_SOCKET)
+ 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;
+
+ return context->std_output;
+}
+
+static int fixup_error(const ExecContext *context, int socket_fd) {
+ assert(context);
+
+ if (socket_fd < 0 && context->std_error == EXEC_OUTPUT_SOCKET)
+ return EXEC_OUTPUT_INHERIT;
+
+ return context->std_error;
+}
+
+static int setup_input(const ExecContext *context, int socket_fd) {
+ ExecInput i;
+
+ assert(context);
+
+ i = fixup_input(context, socket_fd);
+
+ switch (i) {
+
+ case EXEC_INPUT_NULL:
+ return open_null_as(O_RDONLY, STDIN_FILENO);
+
+ case EXEC_INPUT_TTY:
+ case EXEC_INPUT_TTY_FORCE:
+ case EXEC_INPUT_TTY_FAIL: {
+ int fd, r;
+
+ if ((fd = acquire_terminal(
+ tty_path(context),
+ i == EXEC_INPUT_TTY_FAIL,
+ i == EXEC_INPUT_TTY_FORCE)) < 0)
+ return fd;
+
+ if (fd != STDIN_FILENO) {
+ r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+ close_nointr_nofail(fd);
+ } else
+ r = STDIN_FILENO;
+
+ return r;
+ }
+
+ case EXEC_INPUT_SOCKET:
+ return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
+
+ default:
+ assert_not_reached("Unknown input type");
+ }
+}
+
+static int setup_output(const ExecContext *context, int socket_fd, const char *ident) {
+ ExecOutput o;
+ ExecInput i;
+
+ assert(context);
+ assert(ident);
+
+ i = fixup_input(context, socket_fd);
+ o = fixup_output(context, socket_fd);
+
+ /* This expects the input is already set up */
+
+ switch (o) {
+
+ case EXEC_OUTPUT_INHERIT:
+
+ /* If the input is connected to a terminal, inherit that... */
+ if (is_terminal_input(i) || i == EXEC_INPUT_SOCKET)
+ return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ return STDIN_FILENO;
+
+ case EXEC_OUTPUT_NULL:
+ return open_null_as(O_WRONLY, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_TTY:
+ if (is_terminal_input(i))
+ return dup2(STDIN_FILENO, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ /* We don't reset the terminal if this is just about output */
+ return open_terminal_as(tty_path(context), O_WRONLY, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_SYSLOG:
+ case EXEC_OUTPUT_KERNEL:
+ return connect_logger_as(context, o, ident, STDOUT_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDOUT_FILENO) < 0 ? -errno : STDOUT_FILENO;
+
+ default:
+ assert_not_reached("Unknown output type");
+ }
+}
+
+static int setup_error(const ExecContext *context, int socket_fd, const char *ident) {
+ 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);
+
+ /* This expects the input and output are already set up */
+
+ /* Don't change the stderr file descriptor if we inherit all
+ * the way and are not on a tty */
+ if (e == EXEC_OUTPUT_INHERIT &&
+ o == EXEC_OUTPUT_INHERIT &&
+ !is_terminal_input(i))
+ return STDERR_FILENO;
+
+ /* Duplicate form stdout if possible */
+ if (e == o || e == EXEC_OUTPUT_INHERIT)
+ return dup2(STDOUT_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ switch (e) {
+
+ case EXEC_OUTPUT_NULL:
+ return open_null_as(O_WRONLY, STDERR_FILENO);
+
+ case EXEC_OUTPUT_TTY:
+ if (is_terminal_input(i))
+ return dup2(STDIN_FILENO, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ /* We don't reset the terminal if this is just about output */
+ return open_terminal_as(tty_path(context), O_WRONLY, STDERR_FILENO);
+
+ case EXEC_OUTPUT_SYSLOG:
+ case EXEC_OUTPUT_KERNEL:
+ return connect_logger_as(context, e, ident, STDERR_FILENO);
+
+ case EXEC_OUTPUT_SOCKET:
+ assert(socket_fd >= 0);
+ return dup2(socket_fd, STDERR_FILENO) < 0 ? -errno : STDERR_FILENO;
+
+ default:
+ assert_not_reached("Unknown error type");
+ }
+}
+
+static int chown_terminal(int fd, uid_t uid) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ /* This might fail. What matters are the results. */
+ fchown(fd, uid, -1);
+ fchmod(fd, TTY_MODE);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
+ return -EPERM;
+
+ return 0;
+}
+
+static int setup_confirm_stdio(const ExecContext *context,
+ int *_saved_stdin,
+ int *_saved_stdout) {
+ int fd = -1, saved_stdin, saved_stdout = -1, r;
+
+ assert(context);
+ assert(_saved_stdin);
+ assert(_saved_stdout);
+
+ /* This returns positive EXIT_xxx return values instead of
+ * negative errno style values! */
+
+ if ((saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3)) < 0)
+ return EXIT_STDIN;
+
+ if ((saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3)) < 0) {
+ r = EXIT_STDOUT;
+ goto fail;
+ }
+
+ if ((fd = acquire_terminal(
+ tty_path(context),
+ context->std_input == EXEC_INPUT_TTY_FAIL,
+ context->std_input == EXEC_INPUT_TTY_FORCE)) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (chown_terminal(fd, getuid()) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (dup2(fd, STDIN_FILENO) < 0) {
+ r = EXIT_STDIN;
+ goto fail;
+ }
+
+ if (dup2(fd, STDOUT_FILENO) < 0) {
+ r = EXIT_STDOUT;
+ goto fail;
+ }
+
+ if (fd >= 2)
+ close_nointr_nofail(fd);
+
+ *_saved_stdin = saved_stdin;
+ *_saved_stdout = saved_stdout;
+
+ return 0;
+
+fail:
+ if (saved_stdout >= 0)
+ close_nointr_nofail(saved_stdout);
+
+ if (saved_stdin >= 0)
+ close_nointr_nofail(saved_stdin);
+
+ if (fd >= 0)
+ close_nointr_nofail(fd);
+
+ return r;
+}
+
+static int restore_conform_stdio(const ExecContext *context,
+ int *saved_stdin,
+ int *saved_stdout,
+ bool *keep_stdin,
+ bool *keep_stdout) {
+
+ assert(context);
+ assert(saved_stdin);
+ assert(*saved_stdin >= 0);
+ assert(saved_stdout);
+ assert(*saved_stdout >= 0);
+
+ /* This returns positive EXIT_xxx return values instead of
+ * negative errno style values! */
+
+ if (is_terminal_input(context->std_input)) {
+
+ /* The service wants terminal input. */
+
+ *keep_stdin = true;
+ *keep_stdout =
+ context->std_output == EXEC_OUTPUT_INHERIT ||
+ context->std_output == EXEC_OUTPUT_TTY;
+
+ } else {
+ /* If the service doesn't want a controlling terminal,
+ * then we need to get rid entirely of what we have
+ * already. */