activate.c revision 8dd4c05b5495c7ffe0f12ace87e71abe17bd0a0e
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering/***
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering This file is part of systemd.
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering Copyright 2013 Zbigniew Jędrzejewski-Szmek
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering systemd is free software; you can redistribute it and/or modify it
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering under the terms of the GNU Lesser General Public License as published by
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering the Free Software Foundation; either version 2.1 of the License, or
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering (at your option) any later version.
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering systemd is distributed in the hope that it will be useful, but
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering Lesser General Public License for more details.
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering You should have received a copy of the GNU Lesser General Public License
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering***/
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include <getopt.h>
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include <sys/epoll.h>
6bedfcbb2970e06a4d3280c8fb62083d252ede73Lennart Poettering#include <sys/prctl.h>
8b43440b7ef4b81c69c31de7ff820dc07a780254Lennart Poettering#include <sys/socket.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <sys/wait.h>
07630cea1f3a845c09309f197ac7c4f11edd3b62Lennart Poettering#include <unistd.h>
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "sd-daemon.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "log.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "macro.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "signal-util.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "socket-util.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering#include "strv.h"
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic char** arg_listen = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic bool arg_accept = false;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic char** arg_args = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic char** arg_setenv = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic const char *arg_fdname = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic int add_epoll(int epoll_fd, int fd) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering struct epoll_event ev = {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering .events = EPOLLIN
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering };
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering int r;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering assert(epoll_fd >= 0);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering assert(fd >= 0);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering ev.data.fd = fd;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (r < 0)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return 0;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering}
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic int open_sockets(int *epoll_fd, bool accept) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering char **address;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering int n, fd, r;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering int count = 0;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering n = sd_listen_fds(true);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (n < 0)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (n > 0) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering log_info("Received %i descriptors via the environment.", n);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering r = fd_cloexec(fd, arg_accept);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (r < 0)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return r;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering count ++;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering /* Close logging and all other descriptors */
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (arg_listen) {
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering int except[3 + n];
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++)
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering except[fd] = fd;
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering log_close();
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering close_all_fds(except, 3 + n);
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering /** Note: we leak some fd's on error here. I doesn't matter
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering * much, since the program will exit immediately anyway, but
765afd5c4dbc71940d6dd6007ecc3eaa5a0b2aa1Lennart Poettering * would be a pain to fix.
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering */
85e070c2b3f66d838c11f8f008106b9e42c9afafMichal Sekletar
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering STRV_FOREACH(address, arg_listen) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC));
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (fd < 0) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering log_open();
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_error_errno(fd, "Failed to open '%s': %m", *address);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering assert(fd == SD_LISTEN_FDS_START + count);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering count ++;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (arg_listen)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering log_open();
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (*epoll_fd < 0)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_error_errno(errno, "Failed to create epoll object: %m");
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering _cleanup_free_ char *name = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering getsockname_pretty(fd, &name);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering log_info("Listening on %s as %i.", strna(name), fd);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering r = add_epoll(*epoll_fd, fd);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (r < 0)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return r;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering }
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return count;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering}
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poetteringstatic int launch(char* name, char **argv, char **env, int fds) {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="};
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering _cleanup_strv_free_ char **envp = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering _cleanup_free_ char *tmp = NULL;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering unsigned n_env = 0, length;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering char **s;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering unsigned i;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering length = strv_length(arg_setenv);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering envp = new0(char *, length + 8);
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (!envp)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_oom();
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering STRV_FOREACH(s, arg_setenv) {
12ca818ffddb77eb6a0fabe369a5bcbf6994ff8bLennart Poettering if (strchr(*s, '='))
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering envp[n_env++] = *s;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering else {
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering _cleanup_free_ char *p;
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering p = strappend(*s, "=");
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering if (!p)
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering return log_oom();
fc2f9534d07db2b185c02b1961428b53daf1986eLennart Poettering envp[n_env] = strv_find_prefix(env, p);
if (envp[n_env])
n_env ++;
}
}
for (i = 0; i < ELEMENTSOF(tocopy); i++) {
envp[n_env] = strv_find_prefix(env, tocopy[i]);
if (envp[n_env])
n_env ++;
}
if ((asprintf((char**)(envp + n_env++), "LISTEN_FDS=%d", fds) < 0) ||
(asprintf((char**)(envp + n_env++), "LISTEN_PID=%d", getpid()) < 0))
return log_oom();
if (arg_fdname) {
char *e;
e = strappend("LISTEN_FDNAMES=", arg_fdname);
if (!e)
return log_oom();
for (i = 1; i < (unsigned) fds; i++) {
char *c;
c = strjoin(e, ":", arg_fdname, NULL);
if (!c) {
free(e);
return log_oom();
}
free(e);
e = c;
}
envp[n_env++] = e;
}
tmp = strv_join(argv, " ");
if (!tmp)
return log_oom();
log_info("Execing %s (%s)", name, tmp);
execvpe(name, argv, envp);
return log_error_errno(errno, "Failed to execp %s (%s): %m", name, tmp);
}
static int launch1(const char* child, char** argv, char **env, int fd) {
_cleanup_free_ char *tmp = NULL;
pid_t parent_pid, child_pid;
int r;
tmp = strv_join(argv, " ");
if (!tmp)
return log_oom();
parent_pid = getpid();
child_pid = fork();
if (child_pid < 0)
return log_error_errno(errno, "Failed to fork: %m");
/* In the child */
if (child_pid == 0) {
(void) reset_all_signal_handlers();
(void) reset_signal_mask();
r = dup2(fd, STDIN_FILENO);
if (r < 0) {
log_error_errno(errno, "Failed to dup connection to stdin: %m");
_exit(EXIT_FAILURE);
}
r = dup2(fd, STDOUT_FILENO);
if (r < 0) {
log_error_errno(errno, "Failed to dup connection to stdout: %m");
_exit(EXIT_FAILURE);
}
r = close(fd);
if (r < 0) {
log_error_errno(errno, "Failed to close dupped connection: %m");
_exit(EXIT_FAILURE);
}
/* Make sure the child goes away when the parent dies */
if (prctl(PR_SET_PDEATHSIG, SIGTERM) < 0)
_exit(EXIT_FAILURE);
/* Check whether our parent died before we were able
* to set the death signal */
if (getppid() != parent_pid)
_exit(EXIT_SUCCESS);
execvp(child, argv);
log_error_errno(errno, "Failed to exec child %s: %m", child);
_exit(EXIT_FAILURE);
}
log_info("Spawned %s (%s) as PID %d", child, tmp, child_pid);
return 0;
}
static int do_accept(const char* name, char **argv, char **envp, int fd) {
_cleanup_free_ char *local = NULL, *peer = NULL;
_cleanup_close_ int fd2 = -1;
fd2 = accept(fd, NULL, NULL);
if (fd2 < 0) {
log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
return fd2;
}
getsockname_pretty(fd2, &local);
getpeername_pretty(fd2, &peer);
log_info("Connection from %s to %s", strna(peer), strna(local));
return launch1(name, argv, envp, fd2);
}
/* SIGCHLD handler. */
static void sigchld_hdl(int sig, siginfo_t *t, void *data) {
PROTECT_ERRNO;
log_info("Child %d died with code %d", t->si_pid, t->si_status);
/* Wait for a dead child. */
waitpid(t->si_pid, NULL, 0);
}
static int install_chld_handler(void) {
int r;
struct sigaction act = {
.sa_flags = SA_SIGINFO,
.sa_sigaction = sigchld_hdl,
};
r = sigaction(SIGCHLD, &act, 0);
if (r < 0)
log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
return r;
}
static void help(void) {
printf("%s [OPTIONS...]\n\n"
"Listen on sockets and launch child on connection.\n\n"
"Options:\n"
" -l --listen=ADDR Listen for raw connections at ADDR\n"
" -a --accept Spawn separate child for each connection\n"
" -h --help Show this help and exit\n"
" -E --setenv=NAME[=VALUE] Pass an environment variable to children\n"
" --version Print version string and exit\n"
"\n"
"Note: file descriptors from sd_listen_fds() will be passed through.\n"
, program_invocation_short_name);
}
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_VERSION = 0x100,
ARG_FDNAME,
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, ARG_VERSION },
{ "listen", required_argument, NULL, 'l' },
{ "accept", no_argument, NULL, 'a' },
{ "setenv", required_argument, NULL, 'E' },
{ "environment", required_argument, NULL, 'E' }, /* legacy alias */
{ "fdname", required_argument, NULL, ARG_FDNAME },
{}
};
int c, r;
assert(argc >= 0);
assert(argv);
while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0)
switch(c) {
case 'h':
help();
return 0;
case ARG_VERSION:
return version();
case 'l':
r = strv_extend(&arg_listen, optarg);
if (r < 0)
return log_oom();
break;
case 'a':
arg_accept = true;
break;
case 'E':
r = strv_extend(&arg_setenv, optarg);
if (r < 0)
return log_oom();
break;
case ARG_FDNAME:
arg_fdname = optarg;
break;
case '?':
return -EINVAL;
default:
assert_not_reached("Unhandled option");
}
if (optind == argc) {
log_error("%s: command to execute is missing.",
program_invocation_short_name);
return -EINVAL;
}
arg_args = argv + optind;
return 1 /* work to do */;
}
int main(int argc, char **argv, char **envp) {
int r, n;
int epoll_fd = -1;
log_parse_environment();
log_open();
r = parse_argv(argc, argv);
if (r <= 0)
return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
r = install_chld_handler();
if (r < 0)
return EXIT_FAILURE;
n = open_sockets(&epoll_fd, arg_accept);
if (n < 0)
return EXIT_FAILURE;
if (n == 0) {
log_error("No sockets to listen on specified or passed in.");
return EXIT_FAILURE;
}
for (;;) {
struct epoll_event event;
r = epoll_wait(epoll_fd, &event, 1, -1);
if (r < 0) {
if (errno == EINTR)
continue;
log_error_errno(errno, "epoll_wait() failed: %m");
return EXIT_FAILURE;
}
log_info("Communication attempt on fd %i.", event.data.fd);
if (arg_accept) {
r = do_accept(argv[optind], argv + optind, envp,
event.data.fd);
if (r < 0)
return EXIT_FAILURE;
} else
break;
}
launch(argv[optind], argv + optind, envp, n);
return EXIT_SUCCESS;
}