X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Factivate%2Factivate.c;h=89427738663de4ed8a8e438647b8bf8f810ed3eb;hp=738cd83c528da68808fab432b911de475edb3361;hb=e70bc43cdf75b36e7ad3d29e9a6f8ee1461e7d5e;hpb=eceb8483e5a02e8e337486b89719a3b99cfcb7ce diff --git a/src/activate/activate.c b/src/activate/activate.c index 738cd83c5..894277386 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -27,7 +27,7 @@ #include #include -#include +#include "systemd/sd-daemon.h" #include "socket-util.h" #include "build.h" @@ -38,63 +38,31 @@ static char** arg_listen = NULL; static bool arg_accept = false; static char** arg_args = NULL; -static char** arg_environ = NULL; +static char** arg_setenv = NULL; static int add_epoll(int epoll_fd, int fd) { + struct epoll_event ev = { + .events = EPOLLIN + }; int r; - struct epoll_event ev = {EPOLLIN}; - ev.data.fd = fd; assert(epoll_fd >= 0); assert(fd >= 0); + ev.data.fd = fd; r = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev); - if (r < 0) - log_error("Failed to add event on epoll fd:%d for fd:%d: %s", - epoll_fd, fd, strerror(-r)); - return r; -} - -static int print_socket(const char* desc, int fd) { - int r; - SocketAddress addr = { - .size = sizeof(union sockaddr_union), - .type = SOCK_STREAM, - }; - int family; - - r = getsockname(fd, &addr.sockaddr.sa, &addr.size); if (r < 0) { - log_warning("Failed to query socket on fd:%d: %m", fd); - return 0; - } - - family = socket_address_family(&addr); - switch(family) { - case AF_INET: - case AF_INET6: { - char* _cleanup_free_ a = NULL; - r = socket_address_print(&addr, &a); - if (r < 0) - log_warning("socket_address_print(): %s", strerror(-r)); - else - log_info("%s %s address %s", - desc, - family == AF_INET ? "IP" : "IPv6", - a); - break; - } - default: - log_warning("Connection with unknown family %d", family); + log_error("Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd); + return -errno; } return 0; } static int open_sockets(int *epoll_fd, bool accept) { + char **address; int n, fd, r; int count = 0; - char **address; n = sd_listen_fds(true); if (n < 0) { @@ -102,17 +70,27 @@ static int open_sockets(int *epoll_fd, bool accept) { strerror(-n)); return n; } - log_info("Received %d descriptors", n); + if (n > 0) { + log_info("Received %i descriptors via the environment.", n); + + for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { + r = fd_cloexec(fd, arg_accept); + if (r < 0) + return r; - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) { - log_debug("Received descriptor fd:%d", fd); - print_socket("Listening on", fd); + count ++; + } + } - r = fd_cloexec(fd, arg_accept); - if (r < 0) - return r; + /* Close logging and all other descriptors */ + if (arg_listen) { + int except[3 + n]; - count ++; + for (fd = 0; fd < SD_LISTEN_FDS_START + n; fd++) + except[fd] = fd; + + log_close(); + close_all_fds(except, 3 + n); } /** Note: we leak some fd's on error here. I doesn't matter @@ -121,25 +99,33 @@ static int open_sockets(int *epoll_fd, bool accept) { */ STRV_FOREACH(address, arg_listen) { - log_info("Opening address %s", *address); - fd = make_socket_fd(*address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC)); + fd = make_socket_fd(LOG_DEBUG, *address, SOCK_STREAM | (arg_accept*SOCK_CLOEXEC)); if (fd < 0) { + log_open(); log_error("Failed to open '%s': %s", *address, strerror(-fd)); return fd; } + assert(fd == SD_LISTEN_FDS_START + count); count ++; } + if (arg_listen) + log_open(); + *epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (*epoll_fd < 0) { log_error("Failed to create epoll object: %m"); return -errno; } - for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) { + _cleanup_free_ char *name = NULL; + + getsockname_pretty(fd, &name); + log_info("Listening on %s as %i.", strna(name), fd); + r = add_epoll(*epoll_fd, fd); if (r < 0) return r; @@ -149,18 +135,22 @@ static int open_sockets(int *epoll_fd, bool accept) { } static int launch(char* name, char **argv, char **env, int fds) { - unsigned n_env = 0, length; - _cleanup_strv_free_ char **envp = NULL; - char **s; + static const char* tocopy[] = {"TERM=", "PATH=", "USER=", "HOME="}; + _cleanup_strv_free_ char **envp = NULL; _cleanup_free_ char *tmp = NULL; + unsigned n_env = 0, length; + char **s; unsigned i; - length = strv_length(arg_environ); + length = strv_length(arg_setenv); + /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, NULL */ - envp = new(char *, length + 7); + envp = new0(char *, length + 7); + if (!envp) + return log_oom(); - STRV_FOREACH(s, arg_environ) { + STRV_FOREACH(s, arg_setenv) { if (strchr(*s, '=')) envp[n_env++] = *s; else { @@ -190,14 +180,15 @@ static int launch(char* name, char **argv, char **env, int fds) { log_info("Execing %s (%s)", name, tmp); execvpe(name, argv, envp); log_error("Failed to execp %s (%s): %m", name, tmp); + return -errno; } 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; - _cleanup_free_ char *tmp = NULL; tmp = strv_join(argv, " "); if (!tmp) return log_oom(); @@ -250,26 +241,26 @@ static int launch1(const char* child, char** argv, char **env, int fd) { } static int do_accept(const char* name, char **argv, char **envp, int fd) { - SocketAddress addr = { - .size = sizeof(union sockaddr_union), - .type = SOCK_STREAM, - }; - int fd2, r; + _cleanup_free_ char *local = NULL, *peer = NULL; + int fd2; - fd2 = accept(fd, &addr.sockaddr.sa, &addr.size); + fd2 = accept(fd, NULL, NULL); if (fd2 < 0) { log_error("Failed to accept connection on fd:%d: %m", fd); return fd2; } - print_socket("Connection from", fd2); + getsockname_pretty(fd2, &local); + getpeername_pretty(fd2, &peer); + log_info("Connection from %s to %s", strna(peer), strna(local)); - r = launch1(name, argv, envp, fd2); - return r; + 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); @@ -277,10 +268,10 @@ static void sigchld_hdl(int sig, siginfo_t *t, void *data) { static int install_chld_handler(void) { int r; - struct sigaction act; - zero(act); - act.sa_flags = SA_SIGINFO; - act.sa_sigaction = sigchld_hdl; + struct sigaction act = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = sigchld_hdl, + }; r = sigaction(SIGCHLD, &act, 0); if (r < 0) @@ -288,20 +279,18 @@ static int install_chld_handler(void) { return r; } -static int help(void) { +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" - " --version Print version string and exit\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 - ); - - return 0; + , program_invocation_short_name); } static int parse_argv(int argc, char *argv[]) { @@ -310,11 +299,12 @@ static int parse_argv(int argc, char *argv[]) { }; 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' }, - { "environment", required_argument, NULL, 'E' }, + { "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' }, /* alias */ {} }; @@ -323,10 +313,11 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hl:saE:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hl:aE:", options, NULL)) >= 0) switch(c) { case 'h': - return help(); + help(); + return 0; case ARG_VERSION: puts(PACKAGE_STRING); @@ -346,7 +337,7 @@ static int parse_argv(int argc, char *argv[]) { break; case 'E': { - int r = strv_extend(&arg_environ, optarg); + int r = strv_extend(&arg_setenv, optarg); if (r < 0) return r; @@ -361,7 +352,7 @@ static int parse_argv(int argc, char *argv[]) { } if (optind == argc) { - log_error("Usage: %s [OPTION...] PROGRAM [OPTION...]", + log_error("%s: command to execute is missing.", program_invocation_short_name); return -EINVAL; } @@ -389,6 +380,10 @@ int main(int argc, char **argv, char **envp) { 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; @@ -402,7 +397,7 @@ int main(int argc, char **argv, char **envp) { return EXIT_FAILURE; } - log_info("Communication attempt on fd:%d", event.data.fd); + log_info("Communication attempt on fd %i.", event.data.fd); if (arg_accept) { r = do_accept(argv[optind], argv + optind, envp, event.data.fd);