execute.c revision b213e1c11d5a383faf5c456a31389d5c0c0f039b
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt/***
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt This file is part of systemd.
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt Copyright 2010 Lennart Poettering
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt systemd is free software; you can redistribute it and/or modify it
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt under the terms of the GNU Lesser General Public License as published by
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt the Free Software Foundation; either version 2.1 of the License, or
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt (at your option) any later version.
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt systemd is distributed in the hope that it will be useful, but
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt WITHOUT ANY WARRANTY; without even the implied warranty of
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt Lesser General Public License for more details.
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt You should have received a copy of the GNU Lesser General Public License
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt along with systemd; If not, see <http://www.gnu.org/licenses/>.
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt***/
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <errno.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <fcntl.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <unistd.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <string.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <signal.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/socket.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/un.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/prctl.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/stat.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <grp.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <poll.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <glob.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <utmpx.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/personality.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_PAM
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <security/pam_appl.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_SELINUX
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <selinux/selinux.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_SECCOMP
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <seccomp.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_APPARMOR
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include <sys/apparmor.h>
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "sd-messages.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "rm-rf.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "strv.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "macro.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "capability.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "log.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "ioprio.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "securebits.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "namespace.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "exit-status.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "missing.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "utmp-wtmp.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "def.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "path-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "env-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "fileio.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "unit.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "async.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "selinux-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "errno-list.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "af-list.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "mkdir.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "smack-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "bus-endpoint.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "cap-list.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "formats-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "process-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "terminal-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "signal-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_APPARMOR
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "apparmor-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#ifdef HAVE_SECCOMP
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "seccomp-util.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#endif
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#include "execute.h"
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt/* This assumes there is a 'tty' group */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#define TTY_MODE 0620
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt#define SNDBUF_SIZE (8*1024*1024)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int shift_fds(int fds[], unsigned n_fds) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int start, restart_from;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (n_fds <= 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Modifies the fds array! (sorts it) */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(fds);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt start = 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt for (;;) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int i;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt restart_from = -1;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt for (i = start; i < (int) n_fds; i++) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int nfd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Already at right index? */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fds[i] == i+3)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt continue;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt nfd = fcntl(fds[i], F_DUPFD, i + 3);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (nfd < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(fds[i]);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt fds[i] = nfd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Hmm, the fd we wanted isn't free? Then
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * let's remember that and try again from here */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (nfd != i+3 && restart_from < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt restart_from = i;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (restart_from < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt break;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt start = restart_from;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int flags_fds(const int fds[], unsigned n_fds, bool nonblock) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt unsigned i;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (n_fds <= 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(fds);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Drops/Sets O_NONBLOCK and FD_CLOEXEC from the file flags */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt for (i = 0; i < n_fds; i++) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = fd_nonblock(fds[i], nonblock);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (r < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* We unconditionally drop FD_CLOEXEC from the fds,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * since after all we want to pass these fds to our
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * children */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = fd_cloexec(fds[i], false);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (r < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt_pure_ static const char *tty_path(const ExecContext *context) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->tty_path)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return context->tty_path;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return "/dev/console";
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic void exec_context_tty_reset(const ExecContext *context) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->tty_vhangup)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt terminal_vhangup(tty_path(context));
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->tty_reset)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt reset_terminal(tty_path(context));
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->tty_vt_disallocate && context->tty_path)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt vt_disallocate(context->tty_path);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
e80afdb3e4a1239ce0bffa4215d6a227caf3d833Michal Schmidt
e80afdb3e4a1239ce0bffa4215d6a227caf3d833Michal Schmidtstatic bool is_terminal_output(ExecOutput o) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt o == EXEC_OUTPUT_TTY ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt o == EXEC_OUTPUT_SYSLOG_AND_CONSOLE ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt o == EXEC_OUTPUT_KMSG_AND_CONSOLE ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt o == EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int open_null_as(int flags, int nfd) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int fd, r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(nfd >= 0);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt fd = open("/dev/null", flags|O_NOCTTY);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fd < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fd != nfd) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = dup2(fd, nfd) < 0 ? -errno : nfd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(fd);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt } else
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = nfd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int connect_journal_socket(int fd, uid_t uid, gid_t gid) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt union sockaddr_union sa = {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt .un.sun_family = AF_UNIX,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt .un.sun_path = "/run/systemd/journal/stdout",
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt };
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt uid_t olduid = UID_INVALID;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt gid_t oldgid = GID_INVALID;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (gid != GID_INVALID) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt oldgid = getgid();
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = setegid(gid);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (r < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (uid != UID_INVALID) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt olduid = getuid();
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = seteuid(uid);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (r < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt goto restore_gid;
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt }
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
61c81750217af7492be86adde81b28e310cd5e82Tom Gundersen r = connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path));
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (r < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* If we fail to restore the uid or gid, things will likely
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt fail later on. This should only happen if an LSM interferes. */
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt if (uid != UID_INVALID)
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt (void) seteuid(olduid);
435fc3176520a58f1c20ccb983c9fb40b30a1471Martin Pitt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt restore_gid:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (gid != GID_INVALID)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt (void) setegid(oldgid);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int connect_logger_as(const ExecContext *context, ExecOutput output, const char *ident, const char *unit_id, int nfd, uid_t uid, gid_t gid) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int fd, r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(output < _EXEC_OUTPUT_MAX);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(ident);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(nfd >= 0);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt fd = socket(AF_UNIX, SOCK_STREAM, 0);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = connect_journal_socket(fd, uid, gid);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (r < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (shutdown(fd, SHUT_RD) < 0) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt safe_close(fd);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt fd_inc_sndbuf(fd, SNDBUF_SIZE);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt dprintf(fd,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%s\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%s\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%i\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%i\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%i\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%i\n"
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt "%i\n",
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt context->syslog_identifier ? context->syslog_identifier : ident,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt unit_id,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt context->syslog_priority,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt !!context->syslog_level_prefix,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt output == EXEC_OUTPUT_SYSLOG || output == EXEC_OUTPUT_SYSLOG_AND_CONSOLE,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt output == EXEC_OUTPUT_KMSG || output == EXEC_OUTPUT_KMSG_AND_CONSOLE,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt is_terminal_output(output));
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd != nfd) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = dup2(fd, nfd) < 0 ? -errno : nfd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt safe_close(fd);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt } else
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = nfd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int open_terminal_as(const char *path, mode_t mode, int nfd) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int fd, r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(path);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(nfd >= 0);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt fd = open_terminal(path, mode | O_NOCTTY);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return fd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd != nfd) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = dup2(fd, nfd) < 0 ? -errno : nfd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt safe_close(fd);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt } else
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = nfd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic bool is_terminal_input(ExecInput i) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i == EXEC_INPUT_TTY ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i == EXEC_INPUT_TTY_FORCE ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i == EXEC_INPUT_TTY_FAIL;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int fixup_input(ExecInput std_input, int socket_fd, bool apply_tty_stdin) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (is_terminal_input(std_input) && !apply_tty_stdin)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return EXEC_INPUT_NULL;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (std_input == EXEC_INPUT_SOCKET && socket_fd < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return EXEC_INPUT_NULL;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return std_input;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int fixup_output(ExecOutput std_output, int socket_fd) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (std_output == EXEC_OUTPUT_SOCKET && socket_fd < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return EXEC_OUTPUT_INHERIT;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return std_output;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int setup_input(const ExecContext *context, int socket_fd, bool apply_tty_stdin) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt ExecInput i;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt switch (i) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_INPUT_NULL:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return open_null_as(O_RDONLY, STDIN_FILENO);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_INPUT_TTY:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_INPUT_TTY_FORCE:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_INPUT_TTY_FAIL: {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int fd, r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt fd = acquire_terminal(tty_path(context),
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i == EXEC_INPUT_TTY_FAIL,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i == EXEC_INPUT_TTY_FORCE,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt false,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt USEC_INFINITY);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return fd;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fd != STDIN_FILENO) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = dup2(fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt safe_close(fd);
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt } else
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = STDIN_FILENO;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt case EXEC_INPUT_SOCKET:
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return dup2(socket_fd, STDIN_FILENO) < 0 ? -errno : STDIN_FILENO;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt default:
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert_not_reached("Unknown input type");
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int setup_output(Unit *unit, const ExecContext *context, int fileno, int socket_fd, const char *ident, bool apply_tty_stdin, uid_t uid, gid_t gid) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt ExecOutput o;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt ExecInput i;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(unit);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(ident);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt i = fixup_input(context->std_input, socket_fd, apply_tty_stdin);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt o = fixup_output(context->std_output, socket_fd);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (fileno == STDERR_FILENO) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt ExecOutput e;
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt e = fixup_output(context->std_error, socket_fd);
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt /* This expects the input and output are already set up */
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt /* Don't change the stderr file descriptor if we inherit all
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt * the way and are not on a tty */
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt if (e == EXEC_OUTPUT_INHERIT &&
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt o == EXEC_OUTPUT_INHERIT &&
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt i == EXEC_INPUT_NULL &&
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt !is_terminal_input(context->std_input) &&
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt getppid () != 1)
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt return fileno;
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt /* Duplicate from stdout if possible */
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt if (e == o || e == EXEC_OUTPUT_INHERIT)
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt return dup2(STDOUT_FILENO, fileno) < 0 ? -errno : fileno;
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt o = e;
e1323fbfbe8a574f28b704f2df8ce7f99e3a28f5Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt } else if (o == EXEC_OUTPUT_INHERIT) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* If input got downgraded, inherit the original value */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (i == EXEC_INPUT_NULL && is_terminal_input(context->std_input))
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return open_terminal_as(tty_path(context), O_WRONLY, fileno);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* If the input is connected to anything that's not a /dev/null, inherit that... */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (i != EXEC_INPUT_NULL)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* If we are not started from PID 1 we just inherit STDOUT from our parent process. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (getppid() != 1)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return fileno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* We need to open /dev/null here anew, to get the right access mode. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return open_null_as(O_WRONLY, fileno);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt switch (o) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_NULL:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return open_null_as(O_WRONLY, fileno);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_TTY:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (is_terminal_input(i))
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return dup2(STDIN_FILENO, fileno) < 0 ? -errno : fileno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* We don't reset the terminal if this is just about output */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return open_terminal_as(tty_path(context), O_WRONLY, fileno);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_SYSLOG:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_SYSLOG_AND_CONSOLE:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_KMSG:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_KMSG_AND_CONSOLE:
8927b1dad2d4a7330174cb924090b4635a2547fbDavid Herrmann case EXEC_OUTPUT_JOURNAL:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_JOURNAL_AND_CONSOLE:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = connect_logger_as(context, o, ident, unit->id, fileno, uid, gid);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (r < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt log_unit_error_errno(unit, r, "Failed to connect %s to the journal socket, ignoring: %m", fileno == STDOUT_FILENO ? "stdout" : "stderr");
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = open_null_as(O_WRONLY, fileno);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt case EXEC_OUTPUT_SOCKET:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(socket_fd >= 0);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return dup2(socket_fd, fileno) < 0 ? -errno : fileno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt default:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert_not_reached("Unknown error type");
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int chown_terminal(int fd, uid_t uid) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt struct stat st;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(fd >= 0);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* This might fail. What matters are the results. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt (void) fchown(fd, uid, -1);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt (void) fchmod(fd, TTY_MODE);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fstat(fd, &st) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (st.st_uid != uid || (st.st_mode & 0777) != TTY_MODE)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -EPERM;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int setup_confirm_stdio(int *_saved_stdin,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int *_saved_stdout) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int fd = -1, saved_stdin, saved_stdout = -1, r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(_saved_stdin);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(_saved_stdout);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt saved_stdin = fcntl(STDIN_FILENO, F_DUPFD, 3);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (saved_stdin < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt saved_stdout = fcntl(STDOUT_FILENO, F_DUPFD, 3);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (saved_stdout < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt fd = acquire_terminal(
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt "/dev/console",
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt false,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt false,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt false,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt DEFAULT_CONFIRM_USEC);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fd < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = fd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = chown_terminal(fd, getuid());
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (r < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (dup2(fd, STDIN_FILENO) < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (dup2(fd, STDOUT_FILENO) < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fd >= 2)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(fd);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt *_saved_stdin = saved_stdin;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt *_saved_stdout = saved_stdout;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtfail:
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(saved_stdout);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(saved_stdin);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(fd);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt_printf_(1, 2) static int write_confirm_message(const char *format, ...) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt _cleanup_close_ int fd = -1;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt va_list ap;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(format);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt fd = open_terminal("/dev/console", O_WRONLY|O_NOCTTY|O_CLOEXEC);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (fd < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return fd;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt va_start(ap, format);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt vdprintf(fd, format, ap);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt va_end(ap);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int restore_confirm_stdio(int *saved_stdin,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int *saved_stdout) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int r = 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(saved_stdin);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(saved_stdout);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt release_terminal();
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (*saved_stdin >= 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (dup2(*saved_stdin, STDIN_FILENO) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (*saved_stdout >= 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (dup2(*saved_stdout, STDOUT_FILENO) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(*saved_stdin);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt safe_close(*saved_stdout);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int ask_for_confirmation(char *response, char **argv) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int saved_stdout = -1, saved_stdin = -1, r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt _cleanup_free_ char *line = NULL;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = setup_confirm_stdio(&saved_stdin, &saved_stdout);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (r < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt line = exec_command_line(argv);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (!line)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -ENOMEM;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt r = ask_char(response, "yns", "Execute %s? [Yes, No, Skip] ", line);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt restore_confirm_stdio(&saved_stdin, &saved_stdout);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int enforce_groups(const ExecContext *context, const char *username, gid_t gid) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt bool keep_groups = false;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int r;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(context);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Lookup and set GID and supplementary group list. Here too
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * we avoid NSS lookups for gid=0. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (context->group || username) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* First step, initialize groups from /etc/groups */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (username && gid != 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (initgroups(username, gid) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt keep_groups = true;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Second step, set our gids */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (setresgid(gid, gid, gid) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->supplementary_groups) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int ngroups_max, k;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt gid_t *gids;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt char **i;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Final step, initialize any manually set supplementary groups */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert_se((ngroups_max = (int) sysconf(_SC_NGROUPS_MAX)) > 0);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (!(gids = new(gid_t, ngroups_max)))
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -ENOMEM;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (keep_groups) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt k = getgroups(ngroups_max, gids);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (k < 0) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt free(gids);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt } else
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt k = 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt STRV_FOREACH(i, context->supplementary_groups) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt const char *g;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (k >= ngroups_max) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt free(gids);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -E2BIG;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt g = *i;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt r = get_group_creds(&g, gids+k);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (r < 0) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt free(gids);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return r;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt k++;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (setgroups(k, gids) < 0) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt free(gids);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
b669934fae49c9158c35e612e54e1765edca8584Thomas Hindoe Paaboel Andersen }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt free(gids);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int enforce_user(const ExecContext *context, uid_t uid) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt assert(context);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* Sets (but doesn't lookup) the uid and make sure we keep the
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * capabilities while doing so. */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (context->capabilities) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt _cleanup_cap_free_ cap_t d = NULL;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt static const cap_value_t bits[] = {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt CAP_SETUID, /* Necessary so that we can run setresuid() below */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt CAP_SETPCAP /* Necessary so that we can set PR_SET_SECUREBITS later on */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt };
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* First step: If we need to keep capabilities but
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * drop privileges we need to make sure we keep our
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * caps, while we drop privileges. */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (uid != 0) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int sb = context->secure_bits | 1<<SECURE_KEEP_CAPS;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (prctl(PR_GET_SECUREBITS) != sb)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (prctl(PR_SET_SECUREBITS, sb) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* Second step: set the capabilities. This will reduce
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * the capabilities to the minimum we need. */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt d = cap_dup(context->capabilities);
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (!d)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (cap_set_flag(d, CAP_EFFECTIVE, ELEMENTSOF(bits), bits, CAP_SET) < 0 ||
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt cap_set_flag(d, CAP_PERMITTED, ELEMENTSOF(bits), bits, CAP_SET) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt return -errno;
ce79279bff6e7a1a17070509a039ab635796f129Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (cap_set_proc(d) < 0)
ce79279bff6e7a1a17070509a039ab635796f129Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* Third step: actually set the uids */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (setresuid(uid, uid, uid) < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return -errno;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* At this point we should have all necessary capabilities but
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt are otherwise a normal user. However, the caps might got
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt corrupted due to the setresuid() so we need clean them up
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt later. This is done outside of this call. */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return 0;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt#ifdef HAVE_PAM
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidtstatic int null_conv(
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt int num_msg,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt const struct pam_message **msg,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt struct pam_response **resp,
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt void *appdata_ptr) {
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* We don't support conversations */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt return PAM_CONV_ERR;
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt}
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidtstatic int setup_pam(
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt const char *name,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt const char *user,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt uid_t uid,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt const char *tty,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt char ***pam_env,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int fds[], unsigned n_fds) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt static const struct pam_conv conv = {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt .conv = null_conv,
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt .appdata_ptr = NULL
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt };
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_handle_t *handle = NULL;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt sigset_t old_ss;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int pam_code = PAM_SUCCESS;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int err;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt char **e = NULL;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt bool close_session = false;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pid_t pam_pid = 0, parent_pid;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt int flags = 0;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(name);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(user);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt assert(pam_env);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* We set up PAM in the parent process, then fork. The child
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * will then stay around until killed via PR_GET_PDEATHSIG or
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * systemd via the cgroup logic. It will then remove the PAM
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * session again. The parent process will exec() the actual
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * daemon. We do things this way to ensure that the main PID
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * of the daemon is the one we initially fork()ed. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (log_get_max_level() < LOG_DEBUG)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt flags |= PAM_SILENT;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_code = pam_start(name, user, &conv, &handle);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (pam_code != PAM_SUCCESS) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt handle = NULL;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (tty) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_code = pam_set_item(handle, PAM_TTY, tty);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (pam_code != PAM_SUCCESS)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_code = pam_acct_mgmt(handle, flags);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (pam_code != PAM_SUCCESS)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_code = pam_open_session(handle, flags);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (pam_code != PAM_SUCCESS)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt close_session = true;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt e = pam_getenvlist(handle);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (!e) {
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt pam_code = PAM_BUF_ERR;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt goto fail;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt }
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Block SIGTERM, so that we know that it won't get lost in
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * the child */
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt assert_se(sigprocmask_many(SIG_BLOCK, &old_ss, SIGTERM, -1) >= 0);
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt parent_pid = getpid();
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt pam_pid = fork();
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt if (pam_pid < 0)
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt goto fail;
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt if (pam_pid == 0) {
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt int sig;
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt int r = EXIT_PAM;
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt /* The child's job is to reset the PAM session on
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt * termination */
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt /* This string must fit in 10 chars (i.e. the length
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * of "/sbin/init"), to look pretty in /bin/ps */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt rename_process("(sd-pam)");
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Make sure we don't keep open the passed fds in this
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt child. We assume that otherwise only those fds are
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt open here that have been opened by PAM. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt close_many(fds, n_fds);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt /* Drop privileges - we don't need any to pam_close_session
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * and this will make PR_SET_PDEATHSIG work in most cases.
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * If this fails, ignore the error - but expect sd-pam threads
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * to fail to exit normally */
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt if (setresuid(uid, uid, uid) < 0)
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt log_error_errno(r, "Error: Failed to setresuid() in sd-pam: %m");
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt (void) ignore_signals(SIGPIPE, -1);
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Wait until our parent died. This will only work if
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * the above setresuid() succeeds, otherwise the kernel
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * will not allow unprivileged parents kill their privileged
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt * children this way. We rely on the control groups kill logic
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * to do the rest for us. */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
9ba81d5a61b7c992a1d2e5e02f334b8e2a0b0c22Michal Schmidt goto child_finish;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt /* Check if our parent process might already have
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt * died? */
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt if (getppid() == parent_pid) {
8f88aed740ded77af443bb1b7c79bb229b50f8f8Michal Schmidt sigset_t ss;
32a4456cc252689f51f383d150d34ed636bfec4dMichal Schmidt
assert_se(sigemptyset(&ss) >= 0);
assert_se(sigaddset(&ss, SIGTERM) >= 0);
for (;;) {
if (sigwait(&ss, &sig) < 0) {
if (errno == EINTR)
continue;
goto child_finish;
}
assert(sig == SIGTERM);
break;
}
}
/* If our parent died we'll end the session */
if (getppid() != parent_pid) {
pam_code = pam_close_session(handle, flags);
if (pam_code != PAM_SUCCESS)
goto child_finish;
}
r = 0;
child_finish:
pam_end(handle, pam_code | flags);
_exit(r);
}
/* If the child was forked off successfully it will do all the
* cleanups, so forget about the handle here. */
handle = NULL;
/* Unblock SIGTERM again in the parent */
assert_se(sigprocmask(SIG_SETMASK, &old_ss, NULL) >= 0);
/* We close the log explicitly here, since the PAM modules
* might have opened it, but we don't want this fd around. */
closelog();
*pam_env = e;
e = NULL;
return 0;
fail:
if (pam_code != PAM_SUCCESS) {
log_error("PAM failed: %s", pam_strerror(handle, pam_code));
err = -EPERM; /* PAM errors do not map to errno */
} else {
err = log_error_errno(errno, "PAM failed: %m");
}
if (handle) {
if (close_session)
pam_code = pam_close_session(handle, flags);
pam_end(handle, pam_code | flags);
}
strv_free(e);
closelog();
if (pam_pid > 1) {
kill(pam_pid, SIGTERM);
kill(pam_pid, SIGCONT);
}
return err;
}
#endif
static void rename_process_from_path(const char *path) {
char process_name[11];
const char *p;
size_t l;
/* This resulting string must fit in 10 chars (i.e. the length
* of "/sbin/init") to look pretty in /bin/ps */
p = basename(path);
if (isempty(p)) {
rename_process("(...)");
return;
}
l = strlen(p);
if (l > 8) {
/* The end of the process name is usually more
* interesting, since the first bit might just be
* "systemd-" */
p = p + l - 8;
l = 8;
}
process_name[0] = '(';
memcpy(process_name+1, p, l);
process_name[1+l] = ')';
process_name[1+l+1] = 0;
rename_process(process_name);
}
#ifdef HAVE_SECCOMP
static int apply_seccomp(const ExecContext *c) {
uint32_t negative_action, action;
scmp_filter_ctx *seccomp;
Iterator i;
void *id;
int r;
assert(c);
negative_action = c->syscall_errno == 0 ? SCMP_ACT_KILL : SCMP_ACT_ERRNO(c->syscall_errno);
seccomp = seccomp_init(c->syscall_whitelist ? negative_action : SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
if (c->syscall_archs) {
SET_FOREACH(id, c->syscall_archs, i) {
r = seccomp_arch_add(seccomp, PTR_TO_UINT32(id) - 1);
if (r == -EEXIST)
continue;
if (r < 0)
goto finish;
}
} else {
r = seccomp_add_secondary_archs(seccomp);
if (r < 0)
goto finish;
}
action = c->syscall_whitelist ? SCMP_ACT_ALLOW : negative_action;
SET_FOREACH(id, c->syscall_filter, i) {
r = seccomp_rule_add(seccomp, action, PTR_TO_INT(id) - 1, 0);
if (r < 0)
goto finish;
}
r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
if (r < 0)
goto finish;
r = seccomp_load(seccomp);
finish:
seccomp_release(seccomp);
return r;
}
static int apply_address_families(const ExecContext *c) {
scmp_filter_ctx *seccomp;
Iterator i;
int r;
assert(c);
seccomp = seccomp_init(SCMP_ACT_ALLOW);
if (!seccomp)
return -ENOMEM;
r = seccomp_add_secondary_archs(seccomp);
if (r < 0)
goto finish;
if (c->address_families_whitelist) {
int af, first = 0, last = 0;
void *afp;
/* If this is a whitelist, we first block the address
* families that are out of range and then everything
* that is not in the set. First, we find the lowest
* and highest address family in the set. */
SET_FOREACH(afp, c->address_families, i) {
af = PTR_TO_INT(afp);
if (af <= 0 || af >= af_max())
continue;
if (first == 0 || af < first)
first = af;
if (last == 0 || af > last)
last = af;
}
assert((first == 0) == (last == 0));
if (first == 0) {
/* No entries in the valid range, block everything */
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
0);
if (r < 0)
goto finish;
} else {
/* Block everything below the first entry */
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
1,
SCMP_A0(SCMP_CMP_LT, first));
if (r < 0)
goto finish;
/* Block everything above the last entry */
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
1,
SCMP_A0(SCMP_CMP_GT, last));
if (r < 0)
goto finish;
/* Block everything between the first and last
* entry */
for (af = 1; af < af_max(); af++) {
if (set_contains(c->address_families, INT_TO_PTR(af)))
continue;
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
1,
SCMP_A0(SCMP_CMP_EQ, af));
if (r < 0)
goto finish;
}
}
} else {
void *af;
/* If this is a blacklist, then generate one rule for
* each address family that are then combined in OR
* checks. */
SET_FOREACH(af, c->address_families, i) {
r = seccomp_rule_add(
seccomp,
SCMP_ACT_ERRNO(EPROTONOSUPPORT),
SCMP_SYS(socket),
1,
SCMP_A0(SCMP_CMP_EQ, PTR_TO_INT(af)));
if (r < 0)
goto finish;
}
}
r = seccomp_attr_set(seccomp, SCMP_FLTATR_CTL_NNP, 0);
if (r < 0)
goto finish;
r = seccomp_load(seccomp);
finish:
seccomp_release(seccomp);
return r;
}
#endif
static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
idle_pipe[1] = safe_close(idle_pipe[1]);
idle_pipe[2] = safe_close(idle_pipe[2]);
if (idle_pipe[0] >= 0) {
int r;
r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
ssize_t n;
/* Signal systemd that we are bored and want to continue. */
n = write(idle_pipe[3], "x", 1);
if (n > 0)
/* Wait for systemd to react to the signal above. */
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
idle_pipe[0] = safe_close(idle_pipe[0]);
}
idle_pipe[3] = safe_close(idle_pipe[3]);
}
static int build_environment(
const ExecContext *c,
unsigned n_fds,
usec_t watchdog_usec,
const char *home,
const char *username,
const char *shell,
char ***ret) {
_cleanup_strv_free_ char **our_env = NULL;
unsigned n_env = 0;
char *x;
assert(c);
assert(ret);
our_env = new0(char*, 10);
if (!our_env)
return -ENOMEM;
if (n_fds > 0) {
if (asprintf(&x, "LISTEN_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
if (asprintf(&x, "LISTEN_FDS=%u", n_fds) < 0)
return -ENOMEM;
our_env[n_env++] = x;
}
if (watchdog_usec > 0) {
if (asprintf(&x, "WATCHDOG_PID="PID_FMT, getpid()) < 0)
return -ENOMEM;
our_env[n_env++] = x;
if (asprintf(&x, "WATCHDOG_USEC="USEC_FMT, watchdog_usec) < 0)
return -ENOMEM;
our_env[n_env++] = x;
}
if (home) {
x = strappend("HOME=", home);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (username) {
x = strappend("LOGNAME=", username);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
x = strappend("USER=", username);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (shell) {
x = strappend("SHELL=", shell);
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
if (is_terminal_input(c->std_input) ||
c->std_output == EXEC_OUTPUT_TTY ||
c->std_error == EXEC_OUTPUT_TTY ||
c->tty_path) {
x = strdup(default_term_for_tty(tty_path(c)));
if (!x)
return -ENOMEM;
our_env[n_env++] = x;
}
our_env[n_env++] = NULL;
assert(n_env <= 10);
*ret = our_env;
our_env = NULL;
return 0;
}
static bool exec_needs_mount_namespace(
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime) {
assert(context);
assert(params);
if (!strv_isempty(context->read_write_dirs) ||
!strv_isempty(context->read_only_dirs) ||
!strv_isempty(context->inaccessible_dirs))
return true;
if (context->mount_flags != 0)
return true;
if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir))
return true;
if (params->bus_endpoint_path)
return true;
if (context->private_devices ||
context->protect_system != PROTECT_SYSTEM_NO ||
context->protect_home != PROTECT_HOME_NO)
return true;
return false;
}
static int exec_child(
Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
char **argv,
int socket_fd,
int *fds, unsigned n_fds,
char **files_env,
int *exit_status) {
_cleanup_strv_free_ char **our_env = NULL, **pam_env = NULL, **final_env = NULL, **final_argv = NULL;
_cleanup_free_ char *mac_selinux_context_net = NULL;
const char *username = NULL, *home = NULL, *shell = NULL;
unsigned n_dont_close = 0;
int dont_close[n_fds + 4];
uid_t uid = UID_INVALID;
gid_t gid = GID_INVALID;
int i, r;
bool needs_mount_namespace;
assert(unit);
assert(command);
assert(context);
assert(params);
assert(exit_status);
rename_process_from_path(command->path);
/* 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. */
(void) default_signals(SIGNALS_CRASH_HANDLER,
SIGNALS_IGNORE, -1);
if (context->ignore_sigpipe)
(void) ignore_signals(SIGPIPE, -1);
r = reset_signal_mask();
if (r < 0) {
*exit_status = EXIT_SIGNAL_MASK;
return r;
}
if (params->idle_pipe)
do_idle_pipe_dance(params->idle_pipe);
/* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its
* sockets */
log_forget_fds();
if (socket_fd >= 0)
dont_close[n_dont_close++] = socket_fd;
if (n_fds > 0) {
memcpy(dont_close + n_dont_close, fds, sizeof(int) * n_fds);
n_dont_close += n_fds;
}
if (params->bus_endpoint_fd >= 0)
dont_close[n_dont_close++] = params->bus_endpoint_fd;
if (runtime) {
if (runtime->netns_storage_socket[0] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[0];
if (runtime->netns_storage_socket[1] >= 0)
dont_close[n_dont_close++] = runtime->netns_storage_socket[1];
}
r = close_all_fds(dont_close, n_dont_close);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
}
if (!context->same_pgrp)
if (setsid() < 0) {
*exit_status = EXIT_SETSID;
return -errno;
}
exec_context_tty_reset(context);
if (params->confirm_spawn) {
char response;
r = ask_for_confirmation(&response, argv);
if (r == -ETIMEDOUT)
write_confirm_message("Confirmation question timed out, assuming positive response.\n");
else if (r < 0)
write_confirm_message("Couldn't ask confirmation question, assuming positive response: %s\n", strerror(-r));
else if (response == 's') {
write_confirm_message("Skipping execution.\n");
*exit_status = EXIT_CONFIRM;
return -ECANCELED;
} else if (response == 'n') {
write_confirm_message("Failing execution.\n");
*exit_status = 0;
return 0;
}
}
if (context->user) {
username = context->user;
r = get_user_creds(&username, &uid, &gid, &home, &shell);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
}
if (context->group) {
const char *g = context->group;
r = get_group_creds(&g, &gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
return r;
}
}
/* 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);
r = setup_input(context, socket_fd, params->apply_tty_stdin);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
r = setup_output(unit, context, STDOUT_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
if (r < 0) {
*exit_status = EXIT_STDOUT;
return r;
}
r = setup_output(unit, context, STDERR_FILENO, socket_fd, basename(command->path), params->apply_tty_stdin, uid, gid);
if (r < 0) {
*exit_status = EXIT_STDERR;
return r;
}
if (params->cgroup_path) {
r = cg_attach_everywhere(params->cgroup_supported, params->cgroup_path, 0, NULL, NULL);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
}
if (context->oom_score_adjust_set) {
char t[DECIMAL_STR_MAX(context->oom_score_adjust)];
/* When we can't make this change due to EPERM, then
* let's silently skip over it. User namespaces
* prohibit write access to this file, and we
* shouldn't trip up over that. */
sprintf(t, "%i", context->oom_score_adjust);
r = write_string_file("/proc/self/oom_score_adj", t, 0);
if (r == -EPERM || r == -EACCES) {
log_open();
log_unit_debug_errno(unit, r, "Failed to adjust OOM setting, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_OOM_ADJUST;
return -errno;
}
}
if (context->nice_set)
if (setpriority(PRIO_PROCESS, 0, context->nice) < 0) {
*exit_status = EXIT_NICE;
return -errno;
}
if (context->cpu_sched_set) {
struct sched_param param = {
.sched_priority = context->cpu_sched_priority,
};
r = sched_setscheduler(0,
context->cpu_sched_policy |
(context->cpu_sched_reset_on_fork ?
SCHED_RESET_ON_FORK : 0),
&param);
if (r < 0) {
*exit_status = EXIT_SETSCHEDULER;
return -errno;
}
}
if (context->cpuset)
if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) {
*exit_status = EXIT_CPUAFFINITY;
return -errno;
}
if (context->ioprio_set)
if (ioprio_set(IOPRIO_WHO_PROCESS, 0, context->ioprio) < 0) {
*exit_status = EXIT_IOPRIO;
return -errno;
}
if (context->timer_slack_nsec != NSEC_INFINITY)
if (prctl(PR_SET_TIMERSLACK, context->timer_slack_nsec) < 0) {
*exit_status = EXIT_TIMERSLACK;
return -errno;
}
if (context->personality != PERSONALITY_INVALID)
if (personality(context->personality) < 0) {
*exit_status = EXIT_PERSONALITY;
return -errno;
}
if (context->utmp_id)
utmp_put_init_process(context->utmp_id, getpid(), getsid(0), context->tty_path,
context->utmp_mode == EXEC_UTMP_INIT ? INIT_PROCESS :
context->utmp_mode == EXEC_UTMP_LOGIN ? LOGIN_PROCESS :
USER_PROCESS,
username ? "root" : context->user);
if (context->user && is_terminal_input(context->std_input)) {
r = chown_terminal(STDIN_FILENO, uid);
if (r < 0) {
*exit_status = EXIT_STDIN;
return r;
}
}
if (params->bus_endpoint_fd >= 0 && context->bus_endpoint) {
uid_t ep_uid = (uid == UID_INVALID) ? 0 : uid;
r = bus_kernel_set_endpoint_policy(params->bus_endpoint_fd, ep_uid, context->bus_endpoint);
if (r < 0) {
*exit_status = EXIT_BUS_ENDPOINT;
return r;
}
}
/* If delegation is enabled we'll pass ownership of the cgroup
* (but only in systemd's own controller hierarchy!) to the
* user of the new process. */
if (params->cgroup_path && context->user && params->cgroup_delegate) {
r = cg_set_task_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0644, uid, gid);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
r = cg_set_group_access(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, 0755, uid, gid);
if (r < 0) {
*exit_status = EXIT_CGROUP;
return r;
}
}
if (!strv_isempty(context->runtime_directory) && params->runtime_prefix) {
char **rt;
STRV_FOREACH(rt, context->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(params->runtime_prefix, "/", *rt, NULL);
if (!p) {
*exit_status = EXIT_RUNTIME_DIRECTORY;
return -ENOMEM;
}
r = mkdir_p_label(p, context->runtime_directory_mode);
if (r < 0) {
*exit_status = EXIT_RUNTIME_DIRECTORY;
return r;
}
r = chmod_and_chown(p, context->runtime_directory_mode, uid, gid);
if (r < 0) {
*exit_status = EXIT_RUNTIME_DIRECTORY;
return r;
}
}
}
umask(context->umask);
if (params->apply_permissions) {
r = enforce_groups(context, username, gid);
if (r < 0) {
*exit_status = EXIT_GROUP;
return r;
}
#ifdef HAVE_SMACK
if (context->smack_process_label) {
r = mac_smack_apply_pid(0, context->smack_process_label);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
}
}
#ifdef SMACK_DEFAULT_PROCESS_LABEL
else {
_cleanup_free_ char *exec_label = NULL;
r = mac_smack_read(command->path, SMACK_ATTR_EXEC, &exec_label);
if (r < 0 && r != -ENODATA && r != -EOPNOTSUPP) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
}
r = mac_smack_apply_pid(0, exec_label ? : SMACK_DEFAULT_PROCESS_LABEL);
if (r < 0) {
*exit_status = EXIT_SMACK_PROCESS_LABEL;
return r;
}
}
#endif
#endif
#ifdef HAVE_PAM
if (context->pam_name && username) {
r = setup_pam(context->pam_name, username, uid, context->tty_path, &pam_env, fds, n_fds);
if (r < 0) {
*exit_status = EXIT_PAM;
return r;
}
}
#endif
}
if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) {
r = setup_netns(runtime->netns_storage_socket);
if (r < 0) {
*exit_status = EXIT_NETWORK;
return r;
}
}
needs_mount_namespace = exec_needs_mount_namespace(context, params, runtime);
if (needs_mount_namespace) {
char *tmp = NULL, *var = NULL;
/* The runtime struct only contains the parent
* of the private /tmp, which is
* non-accessible to world users. Inside of it
* there's a /tmp that is sticky, and that's
* the one we want to use here. */
if (context->private_tmp && runtime) {
if (runtime->tmp_dir)
tmp = strjoina(runtime->tmp_dir, "/tmp");
if (runtime->var_tmp_dir)
var = strjoina(runtime->var_tmp_dir, "/tmp");
}
r = setup_namespace(
params->apply_chroot ? context->root_directory : NULL,
context->read_write_dirs,
context->read_only_dirs,
context->inaccessible_dirs,
tmp,
var,
params->bus_endpoint_path,
context->private_devices,
context->protect_home,
context->protect_system,
context->mount_flags);
/* If we couldn't set up the namespace this is
* probably due to a missing capability. In this case,
* silently proceeed. */
if (r == -EPERM || r == -EACCES) {
log_open();
log_unit_debug_errno(unit, r, "Failed to set up namespace, assuming containerized execution, ignoring: %m");
log_close();
} else if (r < 0) {
*exit_status = EXIT_NAMESPACE;
return r;
}
}
if (params->apply_chroot) {
if (!needs_mount_namespace && context->root_directory)
if (chroot(context->root_directory) < 0) {
*exit_status = EXIT_CHROOT;
return -errno;
}
if (chdir(context->working_directory ?: "/") < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
} else {
_cleanup_free_ char *d = NULL;
if (asprintf(&d, "%s/%s",
context->root_directory ?: "",
context->working_directory ?: "") < 0) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
if (chdir(d) < 0 &&
!context->working_directory_missing_ok) {
*exit_status = EXIT_CHDIR;
return -errno;
}
}
#ifdef HAVE_SELINUX
if (params->apply_permissions && mac_selinux_use() && params->selinux_context_net && socket_fd >= 0) {
r = mac_selinux_get_child_mls_label(socket_fd, command->path, context->selinux_context, &mac_selinux_context_net);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return r;
}
}
#endif
/* We repeat the fd closing here, to make sure that
* nothing is leaked from the PAM modules. Note that
* we are more aggressive this time since socket_fd
* and the netns fds we don't need anymore. The custom
* endpoint fd was needed to upload the policy and can
* now be closed as well. */
r = close_all_fds(fds, n_fds);
if (r >= 0)
r = shift_fds(fds, n_fds);
if (r >= 0)
r = flags_fds(fds, n_fds, context->non_blocking);
if (r < 0) {
*exit_status = EXIT_FDS;
return r;
}
if (params->apply_permissions) {
for (i = 0; i < _RLIMIT_MAX; i++) {
if (!context->rlimit[i])
continue;
if (setrlimit_closest(i, context->rlimit[i]) < 0) {
*exit_status = EXIT_LIMITS;
return -errno;
}
}
if (context->capability_bounding_set_drop) {
r = capability_bounding_set_drop(context->capability_bounding_set_drop, false);
if (r < 0) {
*exit_status = EXIT_CAPABILITIES;
return r;
}
}
if (context->user) {
r = enforce_user(context, uid);
if (r < 0) {
*exit_status = EXIT_USER;
return r;
}
}
/* 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. */
if (prctl(PR_GET_SECUREBITS) != context->secure_bits)
if (prctl(PR_SET_SECUREBITS, context->secure_bits) < 0) {
*exit_status = EXIT_SECUREBITS;
return -errno;
}
if (context->capabilities)
if (cap_set_proc(context->capabilities) < 0) {
*exit_status = EXIT_CAPABILITIES;
return -errno;
}
if (context->no_new_privileges)
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
*exit_status = EXIT_NO_NEW_PRIVILEGES;
return -errno;
}
#ifdef HAVE_SECCOMP
if (context->address_families_whitelist ||
!set_isempty(context->address_families)) {
r = apply_address_families(context);
if (r < 0) {
*exit_status = EXIT_ADDRESS_FAMILIES;
return r;
}
}
if (context->syscall_whitelist ||
!set_isempty(context->syscall_filter) ||
!set_isempty(context->syscall_archs)) {
r = apply_seccomp(context);
if (r < 0) {
*exit_status = EXIT_SECCOMP;
return r;
}
}
#endif
#ifdef HAVE_SELINUX
if (mac_selinux_use()) {
char *exec_context = mac_selinux_context_net ?: context->selinux_context;
if (exec_context) {
r = setexeccon(exec_context);
if (r < 0) {
*exit_status = EXIT_SELINUX_CONTEXT;
return r;
}
}
}
#endif
#ifdef HAVE_APPARMOR
if (context->apparmor_profile && mac_apparmor_use()) {
r = aa_change_onexec(context->apparmor_profile);
if (r < 0 && !context->apparmor_profile_ignore) {
*exit_status = EXIT_APPARMOR_PROFILE;
return -errno;
}
}
#endif
}
r = build_environment(context, n_fds, params->watchdog_usec, home, username, shell, &our_env);
if (r < 0) {
*exit_status = EXIT_MEMORY;
return r;
}
final_env = strv_env_merge(5,
params->environment,
our_env,
context->environment,
files_env,
pam_env,
NULL);
if (!final_env) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
final_argv = replace_env_argv(argv, final_env);
if (!final_argv) {
*exit_status = EXIT_MEMORY;
return -ENOMEM;
}
final_env = strv_env_clean(final_env);
if (_unlikely_(log_get_max_level() >= LOG_DEBUG)) {
_cleanup_free_ char *line;
line = exec_command_line(final_argv);
if (line) {
log_open();
log_struct(LOG_DEBUG,
LOG_UNIT_ID(unit),
"EXECUTABLE=%s", command->path,
LOG_UNIT_MESSAGE(unit, "Executing: %s", line),
NULL);
log_close();
}
}
execve(command->path, final_argv, final_env);
*exit_status = EXIT_EXEC;
return -errno;
}
int exec_spawn(Unit *unit,
ExecCommand *command,
const ExecContext *context,
const ExecParameters *params,
ExecRuntime *runtime,
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
int *fds = NULL; unsigned n_fds = 0;
_cleanup_free_ char *line = NULL;
int socket_fd, r;
char **argv;
pid_t pid;
assert(unit);
assert(command);
assert(context);
assert(ret);
assert(params);
assert(params->fds || params->n_fds <= 0);
if (context->std_input == EXEC_INPUT_SOCKET ||
context->std_output == EXEC_OUTPUT_SOCKET ||
context->std_error == EXEC_OUTPUT_SOCKET) {
if (params->n_fds != 1) {
log_unit_error(unit, "Got more than one socket.");
return -EINVAL;
}
socket_fd = params->fds[0];
} else {
socket_fd = -1;
fds = params->fds;
n_fds = params->n_fds;
}
r = exec_context_load_environment(unit, context, &files_env);
if (r < 0)
return log_unit_error_errno(unit, r, "Failed to load environment files: %m");
argv = params->argv ?: command->argv;
line = exec_command_line(argv);
if (!line)
return log_oom();
log_struct(LOG_DEBUG,
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "About to execute: %s", line),
"EXECUTABLE=%s", command->path,
NULL);
pid = fork();
if (pid < 0)
return log_unit_error_errno(unit, r, "Failed to fork: %m");
if (pid == 0) {
int exit_status;
r = exec_child(unit,
command,
context,
params,
runtime,
argv,
socket_fd,
fds, n_fds,
files_env,
&exit_status);
if (r < 0) {
log_open();
log_struct_errno(LOG_ERR, r,
LOG_MESSAGE_ID(SD_MESSAGE_SPAWN_FAILED),
LOG_UNIT_ID(unit),
LOG_UNIT_MESSAGE(unit, "Failed at step %s spawning %s: %m",
exit_status_to_string(exit_status, EXIT_STATUS_SYSTEMD),
command->path),
"EXECUTABLE=%s", command->path,
NULL);
}
_exit(exit_status);
}
log_unit_debug(unit, "Forked %s as "PID_FMT, command->path, pid);
/* We add the new process to the cgroup both in the child (so
* that we can be sure that no user code is ever executed
* outside of the cgroup) and in the parent (so that we can be
* sure that when we kill the cgroup the process will be
* killed too). */
if (params->cgroup_path)
(void) cg_attach(SYSTEMD_CGROUP_CONTROLLER, params->cgroup_path, pid);
exec_status_start(&command->exec_status, pid);
*ret = pid;
return 0;
}
void exec_context_init(ExecContext *c) {
assert(c);
c->umask = 0022;
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->ignore_sigpipe = true;
c->timer_slack_nsec = NSEC_INFINITY;
c->personality = PERSONALITY_INVALID;
c->runtime_directory_mode = 0755;
}
void exec_context_done(ExecContext *c) {
unsigned l;
assert(c);
c->environment = strv_free(c->environment);
c->environment_files = strv_free(c->environment_files);
for (l = 0; l < ELEMENTSOF(c->rlimit); l++)
c->rlimit[l] = mfree(c->rlimit[l]);
c->working_directory = mfree(c->working_directory);
c->root_directory = mfree(c->root_directory);
c->tty_path = mfree(c->tty_path);
c->syslog_identifier = mfree(c->syslog_identifier);
c->user = mfree(c->user);
c->group = mfree(c->group);
c->supplementary_groups = strv_free(c->supplementary_groups);
c->pam_name = mfree(c->pam_name);
if (c->capabilities) {
cap_free(c->capabilities);
c->capabilities = NULL;
}
c->read_only_dirs = strv_free(c->read_only_dirs);
c->read_write_dirs = strv_free(c->read_write_dirs);
c->inaccessible_dirs = strv_free(c->inaccessible_dirs);
if (c->cpuset)
CPU_FREE(c->cpuset);
c->utmp_id = mfree(c->utmp_id);
c->selinux_context = mfree(c->selinux_context);
c->apparmor_profile = mfree(c->apparmor_profile);
c->syscall_filter = set_free(c->syscall_filter);
c->syscall_archs = set_free(c->syscall_archs);
c->address_families = set_free(c->address_families);
c->runtime_directory = strv_free(c->runtime_directory);
bus_endpoint_free(c->bus_endpoint);
c->bus_endpoint = NULL;
}
int exec_context_destroy_runtime_directory(ExecContext *c, const char *runtime_prefix) {
char **i;
assert(c);
if (!runtime_prefix)
return 0;
STRV_FOREACH(i, c->runtime_directory) {
_cleanup_free_ char *p;
p = strjoin(runtime_prefix, "/", *i, NULL);
if (!p)
return -ENOMEM;
/* We execute this synchronously, since we need to be
* sure this is gone when we start the service
* next. */
(void) rm_rf(p, REMOVE_ROOT);
}
return 0;
}
void exec_command_done(ExecCommand *c) {
assert(c);
c->path = mfree(c->path);
c->argv = strv_free(c->argv);
}
void exec_command_done_array(ExecCommand *c, unsigned n) {
unsigned i;
for (i = 0; i < n; i++)
exec_command_done(c+i);
}
ExecCommand* exec_command_free_list(ExecCommand *c) {
ExecCommand *i;
while ((i = c)) {
LIST_REMOVE(command, c, i);
exec_command_done(i);
free(i);
}
return NULL;
}
void exec_command_free_array(ExecCommand **c, unsigned n) {
unsigned i;
for (i = 0; i < n; i++)
c[i] = exec_command_free_list(c[i]);
}
typedef struct InvalidEnvInfo {
Unit *unit;
const char *path;
} InvalidEnvInfo;
static void invalid_env(const char *p, void *userdata) {
InvalidEnvInfo *info = userdata;
log_unit_error(info->unit, "Ignoring invalid environment assignment '%s': %s", p, info->path);
}
int exec_context_load_environment(Unit *unit, const ExecContext *c, char ***l) {
char **i, **r = NULL;
assert(c);
assert(l);
STRV_FOREACH(i, c->environment_files) {
char *fn;
int k;
bool ignore = false;
char **p;
_cleanup_globfree_ glob_t pglob = {};
int count, n;
fn = *i;
if (fn[0] == '-') {
ignore = true;
fn ++;
}
if (!path_is_absolute(fn)) {
if (ignore)
continue;
strv_free(r);
return -EINVAL;
}
/* Filename supports globbing, take all matching files */
errno = 0;
if (glob(fn, 0, NULL, &pglob) != 0) {
if (ignore)
continue;
strv_free(r);
return errno ? -errno : -EINVAL;
}
count = pglob.gl_pathc;
if (count == 0) {
if (ignore)
continue;
strv_free(r);
return -EINVAL;
}
for (n = 0; n < count; n++) {
k = load_env_file(NULL, pglob.gl_pathv[n], NULL, &p);
if (k < 0) {
if (ignore)
continue;
strv_free(r);
return k;
}
/* Log invalid environment variables with filename */
if (p) {
InvalidEnvInfo info = {
.unit = unit,
.path = pglob.gl_pathv[n]
};
p = strv_env_clean_with_callback(p, invalid_env, &info);
}
if (r == NULL)
r = p;
else {
char **m;
m = strv_env_merge(2, r, p);
strv_free(r);
strv_free(p);
if (!m)
return -ENOMEM;
r = m;
}
}
}
*l = r;
return 0;
}
static bool tty_may_match_dev_console(const char *tty) {
_cleanup_free_ char *active = NULL;
char *console;
if (startswith(tty, "/dev/"))
tty += 5;
/* trivial identity? */
if (streq(tty, "console"))
return true;
console = resolve_dev_console(&active);
/* if we could not resolve, assume it may */
if (!console)
return true;
/* "tty0" means the active VC, so it may be the same sometimes */
return streq(console, tty) || (streq(console, "tty0") && tty_is_vc(tty));
}
bool exec_context_may_touch_console(ExecContext *ec) {
return (ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate ||
is_terminal_input(ec->std_input) ||
is_terminal_output(ec->std_output) ||
is_terminal_output(ec->std_error)) &&
tty_may_match_dev_console(tty_path(ec));
}
static void strv_fprintf(FILE *f, char **l) {
char **g;
assert(f);
STRV_FOREACH(g, l)
fprintf(f, " %s", *g);
}
void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
char **e;
unsigned i;
assert(c);
assert(f);
prefix = strempty(prefix);
fprintf(f,
"%sUMask: %04o\n"
"%sWorkingDirectory: %s\n"
"%sRootDirectory: %s\n"
"%sNonBlocking: %s\n"
"%sPrivateTmp: %s\n"
"%sPrivateNetwork: %s\n"
"%sPrivateDevices: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
"%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
prefix, c->root_directory ? c->root_directory : "/",
prefix, yes_no(c->non_blocking),
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->private_network),
prefix, yes_no(c->private_devices),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
prefix, yes_no(c->ignore_sigpipe));
STRV_FOREACH(e, c->environment)
fprintf(f, "%sEnvironment: %s\n", prefix, *e);
STRV_FOREACH(e, c->environment_files)
fprintf(f, "%sEnvironmentFile: %s\n", prefix, *e);
if (c->nice_set)
fprintf(f,
"%sNice: %i\n",
prefix, c->nice);
if (c->oom_score_adjust_set)
fprintf(f,
"%sOOMScoreAdjust: %i\n",
prefix, c->oom_score_adjust);
for (i = 0; i < RLIM_NLIMITS; i++)
if (c->rlimit[i])
fprintf(f, "%s%s: "RLIM_FMT"\n",
prefix, rlimit_to_string(i), c->rlimit[i]->rlim_max);
if (c->ioprio_set) {
_cleanup_free_ char *class_str = NULL;
ioprio_class_to_string_alloc(IOPRIO_PRIO_CLASS(c->ioprio), &class_str);
fprintf(f,
"%sIOSchedulingClass: %s\n"
"%sIOPriority: %i\n",
prefix, strna(class_str),
prefix, (int) IOPRIO_PRIO_DATA(c->ioprio));
}
if (c->cpu_sched_set) {
_cleanup_free_ char *policy_str = NULL;
sched_policy_to_string_alloc(c->cpu_sched_policy, &policy_str);
fprintf(f,
"%sCPUSchedulingPolicy: %s\n"
"%sCPUSchedulingPriority: %i\n"
"%sCPUSchedulingResetOnFork: %s\n",
prefix, strna(policy_str),
prefix, c->cpu_sched_priority,
prefix, yes_no(c->cpu_sched_reset_on_fork));
}
if (c->cpuset) {
fprintf(f, "%sCPUAffinity:", prefix);
for (i = 0; i < c->cpuset_ncpus; i++)
if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
fprintf(f, " %u", i);
fputs("\n", f);
}
if (c->timer_slack_nsec != NSEC_INFINITY)
fprintf(f, "%sTimerSlackNSec: "NSEC_FMT "\n", prefix, c->timer_slack_nsec);
fprintf(f,
"%sStandardInput: %s\n"
"%sStandardOutput: %s\n"
"%sStandardError: %s\n",
prefix, exec_input_to_string(c->std_input),
prefix, exec_output_to_string(c->std_output),
prefix, exec_output_to_string(c->std_error));
if (c->tty_path)
fprintf(f,
"%sTTYPath: %s\n"
"%sTTYReset: %s\n"
"%sTTYVHangup: %s\n"
"%sTTYVTDisallocate: %s\n",
prefix, c->tty_path,
prefix, yes_no(c->tty_reset),
prefix, yes_no(c->tty_vhangup),
prefix, yes_no(c->tty_vt_disallocate));
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) {
_cleanup_free_ char *fac_str = NULL, *lvl_str = NULL;
log_facility_unshifted_to_string_alloc(c->syslog_priority >> 3, &fac_str);
log_level_to_string_alloc(LOG_PRI(c->syslog_priority), &lvl_str);
fprintf(f,
"%sSyslogFacility: %s\n"
"%sSyslogLevel: %s\n",
prefix, strna(fac_str),
prefix, strna(lvl_str));
}
if (c->capabilities) {
_cleanup_cap_free_charp_ char *t;
t = cap_to_text(c->capabilities, NULL);
if (t)
fprintf(f, "%sCapabilities: %s\n", prefix, t);
}
if (c->secure_bits)
fprintf(f, "%sSecure Bits:%s%s%s%s%s%s\n",
prefix,
(c->secure_bits & 1<<SECURE_KEEP_CAPS) ? " keep-caps" : "",
(c->secure_bits & 1<<SECURE_KEEP_CAPS_LOCKED) ? " keep-caps-locked" : "",
(c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP) ? " no-setuid-fixup" : "",
(c->secure_bits & 1<<SECURE_NO_SETUID_FIXUP_LOCKED) ? " no-setuid-fixup-locked" : "",
(c->secure_bits & 1<<SECURE_NOROOT) ? " noroot" : "",
(c->secure_bits & 1<<SECURE_NOROOT_LOCKED) ? "noroot-locked" : "");
if (c->capability_bounding_set_drop) {
unsigned long l;
fprintf(f, "%sCapabilityBoundingSet:", prefix);
for (l = 0; l <= cap_last_cap(); l++)
if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l)))
fprintf(f, " %s", strna(capability_to_name(l)));
fputs("\n", f);
}
if (c->user)
fprintf(f, "%sUser: %s\n", prefix, c->user);
if (c->group)
fprintf(f, "%sGroup: %s\n", prefix, c->group);
if (strv_length(c->supplementary_groups) > 0) {
fprintf(f, "%sSupplementaryGroups:", prefix);
strv_fprintf(f, c->supplementary_groups);
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);
fputs("\n", f);
}
if (strv_length(c->read_only_dirs) > 0) {
fprintf(f, "%sReadOnlyDirs:", prefix);
strv_fprintf(f, c->read_only_dirs);
fputs("\n", f);
}
if (strv_length(c->inaccessible_dirs) > 0) {
fprintf(f, "%sInaccessibleDirs:", prefix);
strv_fprintf(f, c->inaccessible_dirs);
fputs("\n", f);
}
if (c->utmp_id)
fprintf(f,
"%sUtmpIdentifier: %s\n",
prefix, c->utmp_id);
if (c->selinux_context)
fprintf(f,
"%sSELinuxContext: %s%s\n",
prefix, c->selinux_context_ignore ? "-" : "", c->selinux_context);
if (c->personality != PERSONALITY_INVALID)
fprintf(f,
"%sPersonality: %s\n",
prefix, strna(personality_to_string(c->personality)));
if (c->syscall_filter) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
bool first = true;
#endif
fprintf(f,
"%sSystemCallFilter: ",
prefix);
if (!c->syscall_whitelist)
fputc('~', f);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_filter, j) {
_cleanup_free_ char *name = NULL;
if (first)
first = false;
else
fputc(' ', f);
name = seccomp_syscall_resolve_num_arch(SCMP_ARCH_NATIVE, PTR_TO_INT(id) - 1);
fputs(strna(name), f);
}
#endif
fputc('\n', f);
}
if (c->syscall_archs) {
#ifdef HAVE_SECCOMP
Iterator j;
void *id;
#endif
fprintf(f,
"%sSystemCallArchitectures:",
prefix);
#ifdef HAVE_SECCOMP
SET_FOREACH(id, c->syscall_archs, j)
fprintf(f, " %s", strna(seccomp_arch_to_string(PTR_TO_UINT32(id) - 1)));
#endif
fputc('\n', f);
}
if (c->syscall_errno != 0)
fprintf(f,
"%sSystemCallErrorNumber: %s\n",
prefix, strna(errno_to_name(c->syscall_errno)));
if (c->apparmor_profile)
fprintf(f,
"%sAppArmorProfile: %s%s\n",
prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile);
}
bool exec_context_maintains_privileges(ExecContext *c) {
assert(c);
/* Returns true if the process forked off would run run under
* an unchanged UID or as root. */
if (!c->user)
return true;
if (streq(c->user, "root") || streq(c->user, "0"))
return true;
return false;
}
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, ExecContext *context, pid_t pid, int code, int status) {
assert(s);
if (s->pid && s->pid != pid)
zero(*s);
s->pid = pid;
dual_timestamp_get(&s->exit_timestamp);
s->code = code;
s->status = status;
if (context) {
if (context->utmp_id)
utmp_put_dead_process(context->utmp_id, pid, code, status);
exec_context_tty_reset(context);
}
}
void exec_status_dump(ExecStatus *s, FILE *f, const char *prefix) {
char buf[FORMAT_TIMESTAMP_MAX];
assert(s);
assert(f);
if (s->pid <= 0)
return;
prefix = strempty(prefix);
fprintf(f,
"%sPID: "PID_FMT"\n",
prefix, s->pid);
if (s->start_timestamp.realtime > 0)
fprintf(f,
"%sStart Timestamp: %s\n",
prefix, format_timestamp(buf, sizeof(buf), s->start_timestamp.realtime));
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.realtime),
prefix, sigchld_code_to_string(s->code),
prefix, s->status);
}
char *exec_command_line(char **argv) {
size_t k;
char *n, *p, **a;
bool first = true;
assert(argv);
k = 1;
STRV_FOREACH(a, argv)
k += strlen(*a)+3;
if (!(n = new(char, k)))
return NULL;
p = n;
STRV_FOREACH(a, argv) {
if (!first)
*(p++) = ' ';
else
first = false;
if (strpbrk(*a, WHITESPACE)) {
*(p++) = '\'';
p = stpcpy(p, *a);
*(p++) = '\'';
} else
p = stpcpy(p, *a);
}
*p = 0;
/* FIXME: this doesn't really handle arguments that have
* spaces and ticks in them */
return n;
}
void exec_command_dump(ExecCommand *c, FILE *f, const char *prefix) {
_cleanup_free_ char *cmd = NULL;
const char *prefix2;
assert(c);
assert(f);
prefix = strempty(prefix);
prefix2 = strjoina(prefix, "\t");
cmd = exec_command_line(c->argv);
fprintf(f,
"%sCommand Line: %s\n",
prefix, cmd ? cmd : strerror(ENOMEM));
exec_status_dump(&c->exec_status, f, prefix2);
}
void exec_command_dump_list(ExecCommand *c, FILE *f, const char *prefix) {
assert(f);
prefix = strempty(prefix);
LIST_FOREACH(command, c, c)
exec_command_dump(c, f, prefix);
}
void exec_command_append_list(ExecCommand **l, ExecCommand *e) {
ExecCommand *end;
assert(l);
assert(e);
if (*l) {
/* It's kind of important, that we keep the order here */
LIST_FIND_TAIL(command, *l, end);
LIST_INSERT_AFTER(command, *l, end, e);
} else
*l = e;
}
int exec_command_set(ExecCommand *c, const char *path, ...) {
va_list ap;
char **l, *p;
assert(c);
assert(path);
va_start(ap, path);
l = strv_new_ap(path, ap);
va_end(ap);
if (!l)
return -ENOMEM;
p = strdup(path);
if (!p) {
strv_free(l);
return -ENOMEM;
}
free(c->path);
c->path = p;
strv_free(c->argv);
c->argv = l;
return 0;
}
int exec_command_append(ExecCommand *c, const char *path, ...) {
_cleanup_strv_free_ char **l = NULL;
va_list ap;
int r;
assert(c);
assert(path);
va_start(ap, path);
l = strv_new_ap(path, ap);
va_end(ap);
if (!l)
return -ENOMEM;
r = strv_extend_strv(&c->argv, l);
if (r < 0)
return r;
return 0;
}
static int exec_runtime_allocate(ExecRuntime **rt) {
if (*rt)
return 0;
*rt = new0(ExecRuntime, 1);
if (!*rt)
return -ENOMEM;
(*rt)->n_ref = 1;
(*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1;
return 0;
}
int exec_runtime_make(ExecRuntime **rt, ExecContext *c, const char *id) {
int r;
assert(rt);
assert(c);
assert(id);
if (*rt)
return 1;
if (!c->private_network && !c->private_tmp)
return 0;
r = exec_runtime_allocate(rt);
if (r < 0)
return r;
if (c->private_network && (*rt)->netns_storage_socket[0] < 0) {
if (socketpair(AF_UNIX, SOCK_DGRAM, 0, (*rt)->netns_storage_socket) < 0)
return -errno;
}
if (c->private_tmp && !(*rt)->tmp_dir) {
r = setup_tmp_dirs(id, &(*rt)->tmp_dir, &(*rt)->var_tmp_dir);
if (r < 0)
return r;
}
return 1;
}
ExecRuntime *exec_runtime_ref(ExecRuntime *r) {
assert(r);
assert(r->n_ref > 0);
r->n_ref++;
return r;
}
ExecRuntime *exec_runtime_unref(ExecRuntime *r) {
if (!r)
return NULL;
assert(r->n_ref > 0);
r->n_ref--;
if (r->n_ref > 0)
return NULL;
free(r->tmp_dir);
free(r->var_tmp_dir);
safe_close_pair(r->netns_storage_socket);
free(r);
return NULL;
}
int exec_runtime_serialize(Unit *u, ExecRuntime *rt, FILE *f, FDSet *fds) {
assert(u);
assert(f);
assert(fds);
if (!rt)
return 0;
if (rt->tmp_dir)
unit_serialize_item(u, f, "tmp-dir", rt->tmp_dir);
if (rt->var_tmp_dir)
unit_serialize_item(u, f, "var-tmp-dir", rt->var_tmp_dir);
if (rt->netns_storage_socket[0] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[0]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-0", "%i", copy);
}
if (rt->netns_storage_socket[1] >= 0) {
int copy;
copy = fdset_put_dup(fds, rt->netns_storage_socket[1]);
if (copy < 0)
return copy;
unit_serialize_item_format(u, f, "netns-socket-1", "%i", copy);
}
return 0;
}
int exec_runtime_deserialize_item(Unit *u, ExecRuntime **rt, const char *key, const char *value, FDSet *fds) {
int r;
assert(rt);
assert(key);
assert(value);
if (streq(key, "tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->tmp_dir);
(*rt)->tmp_dir = copy;
} else if (streq(key, "var-tmp-dir")) {
char *copy;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
copy = strdup(value);
if (!copy)
return log_oom();
free((*rt)->var_tmp_dir);
(*rt)->var_tmp_dir = copy;
} else if (streq(key, "netns-socket-0")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[0]);
(*rt)->netns_storage_socket[0] = fdset_remove(fds, fd);
}
} else if (streq(key, "netns-socket-1")) {
int fd;
r = exec_runtime_allocate(rt);
if (r < 0)
return log_oom();
if (safe_atoi(value, &fd) < 0 || !fdset_contains(fds, fd))
log_unit_debug(u, "Failed to parse netns socket value: %s", value);
else {
safe_close((*rt)->netns_storage_socket[1]);
(*rt)->netns_storage_socket[1] = fdset_remove(fds, fd);
}
} else
return 0;
return 1;
}
static void *remove_tmpdir_thread(void *p) {
_cleanup_free_ char *path = p;
(void) rm_rf(path, REMOVE_ROOT|REMOVE_PHYSICAL);
return NULL;
}
void exec_runtime_destroy(ExecRuntime *rt) {
int r;
if (!rt)
return;
/* If there are multiple users of this, let's leave the stuff around */
if (rt->n_ref > 1)
return;
if (rt->tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, rt->tmp_dir);
if (r < 0) {
log_warning_errno(r, "Failed to nuke %s: %m", rt->tmp_dir);
free(rt->tmp_dir);
}
rt->tmp_dir = NULL;
}
if (rt->var_tmp_dir) {
log_debug("Spawning thread to nuke %s", rt->var_tmp_dir);
r = asynchronous_job(remove_tmpdir_thread, rt->var_tmp_dir);
if (r < 0) {
log_warning_errno(r, "Failed to nuke %s: %m", rt->var_tmp_dir);
free(rt->var_tmp_dir);
}
rt->var_tmp_dir = NULL;
}
safe_close_pair(rt->netns_storage_socket);
}
static const char* const exec_input_table[_EXEC_INPUT_MAX] = {
[EXEC_INPUT_NULL] = "null",
[EXEC_INPUT_TTY] = "tty",
[EXEC_INPUT_TTY_FORCE] = "tty-force",
[EXEC_INPUT_TTY_FAIL] = "tty-fail",
[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_JOURNAL] = "journal",
[EXEC_OUTPUT_JOURNAL_AND_CONSOLE] = "journal+console",
[EXEC_OUTPUT_SOCKET] = "socket"
};
DEFINE_STRING_TABLE_LOOKUP(exec_output, ExecOutput);
static const char* const exec_utmp_mode_table[_EXEC_UTMP_MODE_MAX] = {
[EXEC_UTMP_INIT] = "init",
[EXEC_UTMP_LOGIN] = "login",
[EXEC_UTMP_USER] = "user",
};
DEFINE_STRING_TABLE_LOOKUP(exec_utmp_mode, ExecUtmpMode);