LCOV - code coverage report
Current view: top level - activate - activate.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 23 262 8.8 %
Date: 2019-08-22 15:41:25 Functions: 3 10 30.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <getopt.h>
       4             : #include <sys/epoll.h>
       5             : #include <sys/prctl.h>
       6             : #include <sys/socket.h>
       7             : #include <sys/wait.h>
       8             : #include <unistd.h>
       9             : 
      10             : #include "sd-daemon.h"
      11             : 
      12             : #include "alloc-util.h"
      13             : #include "errno-util.h"
      14             : #include "escape.h"
      15             : #include "fd-util.h"
      16             : #include "log.h"
      17             : #include "macro.h"
      18             : #include "pretty-print.h"
      19             : #include "process-util.h"
      20             : #include "signal-util.h"
      21             : #include "socket-util.h"
      22             : #include "string-util.h"
      23             : #include "strv.h"
      24             : #include "terminal-util.h"
      25             : #include "util.h"
      26             : 
      27             : static char **arg_listen = NULL;
      28             : static bool arg_accept = false;
      29             : static int arg_socket_type = SOCK_STREAM;
      30             : static char **arg_args = NULL;
      31             : static char **arg_setenv = NULL;
      32             : static char **arg_fdnames = NULL;
      33             : static bool arg_inetd = false;
      34             : 
      35           0 : static int add_epoll(int epoll_fd, int fd) {
      36           0 :         struct epoll_event ev = {
      37             :                 .events = EPOLLIN,
      38             :                 .data.fd = fd,
      39             :         };
      40             : 
      41           0 :         assert(epoll_fd >= 0);
      42           0 :         assert(fd >= 0);
      43             : 
      44           0 :         if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0)
      45           0 :                 return log_error_errno(errno, "Failed to add event on epoll fd:%d for fd:%d: %m", epoll_fd, fd);
      46             : 
      47           0 :         return 0;
      48             : }
      49             : 
      50           0 : static int open_sockets(int *epoll_fd, bool accept) {
      51             :         char **address;
      52           0 :         int n, fd, r, count = 0;
      53             : 
      54           0 :         n = sd_listen_fds(true);
      55           0 :         if (n < 0)
      56           0 :                 return log_error_errno(n, "Failed to read listening file descriptors from environment: %m");
      57           0 :         if (n > 0) {
      58           0 :                 log_info("Received %i descriptors via the environment.", n);
      59             : 
      60           0 :                 for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + n; fd++) {
      61           0 :                         r = fd_cloexec(fd, arg_accept);
      62           0 :                         if (r < 0)
      63           0 :                                 return r;
      64             : 
      65           0 :                         count++;
      66             :                 }
      67             :         }
      68             : 
      69             :         /* Close logging and all other descriptors */
      70           0 :         if (arg_listen) {
      71           0 :                 _cleanup_free_ int *except = NULL;
      72             :                 int i;
      73             : 
      74           0 :                 except = new(int, n);
      75           0 :                 if (!except)
      76           0 :                         return log_oom();
      77             : 
      78           0 :                 for (i = 0; i < n; i++)
      79           0 :                         except[i] = SD_LISTEN_FDS_START + i;
      80             : 
      81           0 :                 log_close();
      82           0 :                 r = close_all_fds(except, n);
      83           0 :                 if (r < 0)
      84           0 :                         return log_error_errno(r, "Failed to close all file descriptors: %m");
      85             :         }
      86             : 
      87             :         /** Note: we leak some fd's on error here. I doesn't matter
      88             :          *  much, since the program will exit immediately anyway, but
      89             :          *  would be a pain to fix.
      90             :          */
      91             : 
      92           0 :         STRV_FOREACH(address, arg_listen) {
      93           0 :                 fd = make_socket_fd(LOG_DEBUG, *address, arg_socket_type, (arg_accept * SOCK_CLOEXEC));
      94           0 :                 if (fd < 0) {
      95           0 :                         log_open();
      96           0 :                         return log_error_errno(fd, "Failed to open '%s': %m", *address);
      97             :                 }
      98             : 
      99           0 :                 assert(fd == SD_LISTEN_FDS_START + count);
     100           0 :                 count++;
     101             :         }
     102             : 
     103           0 :         if (arg_listen)
     104           0 :                 log_open();
     105             : 
     106           0 :         *epoll_fd = epoll_create1(EPOLL_CLOEXEC);
     107           0 :         if (*epoll_fd < 0)
     108           0 :                 return log_error_errno(errno, "Failed to create epoll object: %m");
     109             : 
     110           0 :         for (fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + count; fd++) {
     111           0 :                 _cleanup_free_ char *name = NULL;
     112             : 
     113           0 :                 getsockname_pretty(fd, &name);
     114           0 :                 log_info("Listening on %s as %i.", strna(name), fd);
     115             : 
     116           0 :                 r = add_epoll(*epoll_fd, fd);
     117           0 :                 if (r < 0)
     118           0 :                         return r;
     119             :         }
     120             : 
     121           0 :         return count;
     122             : }
     123             : 
     124           0 : static int exec_process(const char *name, char **argv, char **env, int start_fd, size_t n_fds) {
     125             : 
     126           0 :         _cleanup_strv_free_ char **envp = NULL;
     127           0 :         _cleanup_free_ char *joined = NULL;
     128           0 :         size_t n_env = 0, length;
     129             :         const char *tocopy;
     130             :         char **s;
     131             :         int r;
     132             : 
     133           0 :         if (arg_inetd && n_fds != 1)
     134           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     135             :                                        "--inetd only supported for single file descriptors.");
     136             : 
     137           0 :         length = strv_length(arg_setenv);
     138             : 
     139             :         /* PATH, TERM, HOME, USER, LISTEN_FDS, LISTEN_PID, LISTEN_FDNAMES, NULL */
     140           0 :         envp = new0(char *, length + 8);
     141           0 :         if (!envp)
     142           0 :                 return log_oom();
     143             : 
     144           0 :         STRV_FOREACH(s, arg_setenv) {
     145             : 
     146           0 :                 if (strchr(*s, '=')) {
     147             :                         char *k;
     148             : 
     149           0 :                         k = strdup(*s);
     150           0 :                         if (!k)
     151           0 :                                 return log_oom();
     152             : 
     153           0 :                         envp[n_env++] = k;
     154             :                 } else {
     155           0 :                         _cleanup_free_ char *p;
     156             :                         const char *n;
     157             : 
     158           0 :                         p = strjoin(*s, "=");
     159           0 :                         if (!p)
     160           0 :                                 return log_oom();
     161             : 
     162           0 :                         n = strv_find_prefix(env, p);
     163           0 :                         if (!n)
     164           0 :                                 continue;
     165             : 
     166           0 :                         envp[n_env] = strdup(n);
     167           0 :                         if (!envp[n_env])
     168           0 :                                 return log_oom();
     169             : 
     170           0 :                         n_env++;
     171             :                 }
     172             :         }
     173             : 
     174           0 :         FOREACH_STRING(tocopy, "TERM=", "PATH=", "USER=", "HOME=") {
     175             :                 const char *n;
     176             : 
     177           0 :                 n = strv_find_prefix(env, tocopy);
     178           0 :                 if (!n)
     179           0 :                         continue;
     180             : 
     181           0 :                 envp[n_env] = strdup(n);
     182           0 :                 if (!envp[n_env])
     183           0 :                         return log_oom();
     184             : 
     185           0 :                 n_env++;
     186             :         }
     187             : 
     188           0 :         if (arg_inetd) {
     189           0 :                 assert(n_fds == 1);
     190             : 
     191           0 :                 r = rearrange_stdio(start_fd, start_fd, STDERR_FILENO); /* invalidates start_fd on success + error */
     192           0 :                 if (r < 0)
     193           0 :                         return log_error_errno(r, "Failed to move fd to stdin+stdout: %m");
     194             : 
     195             :         } else {
     196           0 :                 if (start_fd != SD_LISTEN_FDS_START) {
     197           0 :                         assert(n_fds == 1);
     198             : 
     199           0 :                         if (dup2(start_fd, SD_LISTEN_FDS_START) < 0)
     200           0 :                                 return log_error_errno(errno, "Failed to dup connection: %m");
     201             : 
     202           0 :                         safe_close(start_fd);
     203           0 :                         start_fd = SD_LISTEN_FDS_START;
     204             :                 }
     205             : 
     206           0 :                 if (asprintf((char **) (envp + n_env++), "LISTEN_FDS=%zu", n_fds) < 0)
     207           0 :                         return log_oom();
     208             : 
     209           0 :                 if (asprintf((char **) (envp + n_env++), "LISTEN_PID=" PID_FMT, getpid_cached()) < 0)
     210           0 :                         return log_oom();
     211             : 
     212           0 :                 if (arg_fdnames) {
     213           0 :                         _cleanup_free_ char *names = NULL;
     214             :                         size_t len;
     215             :                         char *e;
     216             : 
     217           0 :                         len = strv_length(arg_fdnames);
     218           0 :                         if (len == 1) {
     219             :                                 size_t i;
     220             : 
     221           0 :                                 for (i = 1; i < n_fds; i++) {
     222           0 :                                         r = strv_extend(&arg_fdnames, arg_fdnames[0]);
     223           0 :                                         if (r < 0)
     224           0 :                                                 return log_error_errno(r, "Failed to extend strv: %m");
     225             :                                 }
     226           0 :                         } else if (len != n_fds)
     227           0 :                                 log_warning("The number of fd names is different than number of fds: %zu vs %zu", len, n_fds);
     228             : 
     229           0 :                         names = strv_join(arg_fdnames, ":");
     230           0 :                         if (!names)
     231           0 :                                 return log_oom();
     232             : 
     233           0 :                         e = strjoin("LISTEN_FDNAMES=", names);
     234           0 :                         if (!e)
     235           0 :                                 return log_oom();
     236             : 
     237           0 :                         envp[n_env++] = e;
     238             :                 }
     239             :         }
     240             : 
     241           0 :         joined = strv_join(argv, " ");
     242           0 :         if (!joined)
     243           0 :                 return log_oom();
     244             : 
     245           0 :         log_info("Execing %s (%s)", name, joined);
     246           0 :         execvpe(name, argv, envp);
     247             : 
     248           0 :         return log_error_errno(errno, "Failed to execp %s (%s): %m", name, joined);
     249             : }
     250             : 
     251           0 : static int fork_and_exec_process(const char *child, char **argv, char **env, int fd) {
     252           0 :         _cleanup_free_ char *joined = NULL;
     253             :         pid_t child_pid;
     254             :         int r;
     255             : 
     256           0 :         joined = strv_join(argv, " ");
     257           0 :         if (!joined)
     258           0 :                 return log_oom();
     259             : 
     260           0 :         r = safe_fork("(activate)",
     261             :                       FORK_RESET_SIGNALS | FORK_DEATHSIG | FORK_RLIMIT_NOFILE_SAFE | FORK_LOG,
     262             :                       &child_pid);
     263           0 :         if (r < 0)
     264           0 :                 return r;
     265           0 :         if (r == 0) {
     266             :                 /* In the child */
     267           0 :                 exec_process(child, argv, env, fd, 1);
     268           0 :                 _exit(EXIT_FAILURE);
     269             :         }
     270             : 
     271           0 :         log_info("Spawned %s (%s) as PID " PID_FMT ".", child, joined, child_pid);
     272           0 :         return 0;
     273             : }
     274             : 
     275           0 : static int do_accept(const char *name, char **argv, char **envp, int fd) {
     276           0 :         _cleanup_free_ char *local = NULL, *peer = NULL;
     277           0 :         _cleanup_close_ int fd_accepted = -1;
     278             : 
     279           0 :         fd_accepted = accept4(fd, NULL, NULL, 0);
     280           0 :         if (fd_accepted < 0) {
     281           0 :                 if (ERRNO_IS_ACCEPT_AGAIN(errno))
     282           0 :                         return 0;
     283             : 
     284           0 :                 return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd);
     285             :         }
     286             : 
     287           0 :         (void) getsockname_pretty(fd_accepted, &local);
     288           0 :         (void) getpeername_pretty(fd_accepted, true, &peer);
     289           0 :         log_info("Connection from %s to %s", strna(peer), strna(local));
     290             : 
     291           0 :         return fork_and_exec_process(name, argv, envp, fd_accepted);
     292             : }
     293             : 
     294             : /* SIGCHLD handler. */
     295           0 : static void sigchld_hdl(int sig) {
     296           0 :         PROTECT_ERRNO;
     297             : 
     298           0 :         for (;;) {
     299             :                 siginfo_t si;
     300             :                 int r;
     301             : 
     302           0 :                 si.si_pid = 0;
     303           0 :                 r = waitid(P_ALL, 0, &si, WEXITED | WNOHANG);
     304           0 :                 if (r < 0) {
     305           0 :                         if (errno != ECHILD)
     306           0 :                                 log_error_errno(errno, "Failed to reap children: %m");
     307           0 :                         return;
     308             :                 }
     309           0 :                 if (si.si_pid == 0)
     310           0 :                         return;
     311             : 
     312           0 :                 log_info("Child %d died with code %d", si.si_pid, si.si_status);
     313             :         }
     314             : }
     315             : 
     316           0 : static int install_chld_handler(void) {
     317             :         static const struct sigaction act = {
     318             :                 .sa_flags = SA_NOCLDSTOP | SA_RESTART,
     319             :                 .sa_handler = sigchld_hdl,
     320             :         };
     321             : 
     322           0 :         if (sigaction(SIGCHLD, &act, 0) < 0)
     323           0 :                 return log_error_errno(errno, "Failed to install SIGCHLD handler: %m");
     324             : 
     325           0 :         return 0;
     326             : }
     327             : 
     328           3 : static int help(void) {
     329           3 :         _cleanup_free_ char *link = NULL;
     330             :         int r;
     331             : 
     332           3 :         r = terminal_urlify_man("systemd-socket-activate", "1", &link);
     333           3 :         if (r < 0)
     334           0 :                 return log_oom();
     335             : 
     336           3 :         printf("%s [OPTIONS...]\n\n"
     337             :                "Listen on sockets and launch child on connection.\n\n"
     338             :                "Options:\n"
     339             :                "  -h --help                  Show this help and exit\n"
     340             :                "     --version               Print version string and exit\n"
     341             :                "  -l --listen=ADDR           Listen for raw connections at ADDR\n"
     342             :                "  -d --datagram              Listen on datagram instead of stream socket\n"
     343             :                "     --seqpacket             Listen on SOCK_SEQPACKET instead of stream socket\n"
     344             :                "  -a --accept                Spawn separate child for each connection\n"
     345             :                "  -E --setenv=NAME[=VALUE]   Pass an environment variable to children\n"
     346             :                "     --fdname=NAME[:NAME...] Specify names for file descriptors\n"
     347             :                "     --inetd                 Enable inetd file descriptor passing protocol\n"
     348             :                "\nNote: file descriptors from sd_listen_fds() will be passed through.\n"
     349             :                "\nSee the %s for details.\n"
     350             :                , program_invocation_short_name
     351             :                , link
     352             :         );
     353             : 
     354           3 :         return 0;
     355             : }
     356             : 
     357           4 : static int parse_argv(int argc, char *argv[]) {
     358             :         enum {
     359             :                 ARG_VERSION = 0x100,
     360             :                 ARG_FDNAME,
     361             :                 ARG_SEQPACKET,
     362             :                 ARG_INETD,
     363             :         };
     364             : 
     365             :         static const struct option options[] = {
     366             :                 { "help",        no_argument,       NULL, 'h'           },
     367             :                 { "version",     no_argument,       NULL, ARG_VERSION   },
     368             :                 { "datagram",    no_argument,       NULL, 'd'           },
     369             :                 { "seqpacket",   no_argument,       NULL, ARG_SEQPACKET },
     370             :                 { "listen",      required_argument, NULL, 'l'           },
     371             :                 { "accept",      no_argument,       NULL, 'a'           },
     372             :                 { "setenv",      required_argument, NULL, 'E'           },
     373             :                 { "environment", required_argument, NULL, 'E'           }, /* legacy alias */
     374             :                 { "fdname",      required_argument, NULL, ARG_FDNAME    },
     375             :                 { "inetd",       no_argument,       NULL, ARG_INETD     },
     376             :                 {}
     377             :         };
     378             : 
     379             :         int c, r;
     380             : 
     381           4 :         assert(argc >= 0);
     382           4 :         assert(argv);
     383             : 
     384           4 :         while ((c = getopt_long(argc, argv, "+hl:aE:d", options, NULL)) >= 0)
     385           4 :                 switch (c) {
     386           3 :                 case 'h':
     387           3 :                         return help();
     388             : 
     389           0 :                 case ARG_VERSION:
     390           0 :                         return version();
     391             : 
     392           0 :                 case 'l':
     393           0 :                         r = strv_extend(&arg_listen, optarg);
     394           0 :                         if (r < 0)
     395           0 :                                 return log_oom();
     396             : 
     397           0 :                         break;
     398             : 
     399           0 :                 case 'd':
     400           0 :                         if (arg_socket_type == SOCK_SEQPACKET)
     401           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     402             :                                                        "--datagram may not be combined with --seqpacket.");
     403             : 
     404           0 :                         arg_socket_type = SOCK_DGRAM;
     405           0 :                         break;
     406             : 
     407           0 :                 case ARG_SEQPACKET:
     408           0 :                         if (arg_socket_type == SOCK_DGRAM)
     409           0 :                                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     410             :                                                        "--seqpacket may not be combined with --datagram.");
     411             : 
     412           0 :                         arg_socket_type = SOCK_SEQPACKET;
     413           0 :                         break;
     414             : 
     415           0 :                 case 'a':
     416           0 :                         arg_accept = true;
     417           0 :                         break;
     418             : 
     419           0 :                 case 'E':
     420           0 :                         r = strv_extend(&arg_setenv, optarg);
     421           0 :                         if (r < 0)
     422           0 :                                 return log_oom();
     423             : 
     424           0 :                         break;
     425             : 
     426           0 :                 case ARG_FDNAME: {
     427           0 :                         _cleanup_strv_free_ char **names;
     428             :                         char **s;
     429             : 
     430           0 :                         names = strv_split(optarg, ":");
     431           0 :                         if (!names)
     432           0 :                                 return log_oom();
     433             : 
     434           0 :                         STRV_FOREACH(s, names)
     435           0 :                                 if (!fdname_is_valid(*s)) {
     436           0 :                                         _cleanup_free_ char *esc;
     437             : 
     438           0 :                                         esc = cescape(*s);
     439           0 :                                         log_warning("File descriptor name \"%s\" is not valid.", esc);
     440             :                                 }
     441             : 
     442             :                         /* Empty optargs means one empty name */
     443           0 :                         r = strv_extend_strv(&arg_fdnames,
     444           0 :                                              strv_isempty(names) ? STRV_MAKE("") : names,
     445             :                                              false);
     446           0 :                         if (r < 0)
     447           0 :                                 return log_error_errno(r, "strv_extend_strv: %m");
     448           0 :                         break;
     449             :                 }
     450             : 
     451           0 :                 case ARG_INETD:
     452           0 :                         arg_inetd = true;
     453           0 :                         break;
     454             : 
     455           1 :                 case '?':
     456           1 :                         return -EINVAL;
     457             : 
     458           0 :                 default:
     459           0 :                         assert_not_reached("Unhandled option");
     460             :                 }
     461             : 
     462           0 :         if (optind == argc)
     463           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     464             :                                        "%s: command to execute is missing.",
     465             :                                        program_invocation_short_name);
     466             : 
     467           0 :         if (arg_socket_type == SOCK_DGRAM && arg_accept)
     468           0 :                 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
     469             :                                        "Datagram sockets do not accept connections. "
     470             :                                        "The --datagram and --accept options may not be combined.");
     471             : 
     472           0 :         arg_args = argv + optind;
     473             : 
     474           0 :         return 1 /* work to do */;
     475             : }
     476             : 
     477           4 : int main(int argc, char **argv, char **envp) {
     478             :         int r, n;
     479           4 :         int epoll_fd = -1;
     480             : 
     481           4 :         log_show_color(true);
     482           4 :         log_parse_environment();
     483           4 :         log_open();
     484             : 
     485           4 :         r = parse_argv(argc, argv);
     486           4 :         if (r <= 0)
     487           4 :                 return r == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
     488             : 
     489           0 :         r = install_chld_handler();
     490           0 :         if (r < 0)
     491           0 :                 return EXIT_FAILURE;
     492             : 
     493           0 :         n = open_sockets(&epoll_fd, arg_accept);
     494           0 :         if (n < 0)
     495           0 :                 return EXIT_FAILURE;
     496           0 :         if (n == 0) {
     497           0 :                 log_error("No sockets to listen on specified or passed in.");
     498           0 :                 return EXIT_FAILURE;
     499             :         }
     500             : 
     501           0 :         for (;;) {
     502             :                 struct epoll_event event;
     503             : 
     504           0 :                 if (epoll_wait(epoll_fd, &event, 1, -1) < 0) {
     505           0 :                         if (errno == EINTR)
     506           0 :                                 continue;
     507             : 
     508           0 :                         log_error_errno(errno, "epoll_wait() failed: %m");
     509           0 :                         return EXIT_FAILURE;
     510             :                 }
     511             : 
     512           0 :                 log_info("Communication attempt on fd %i.", event.data.fd);
     513           0 :                 if (arg_accept) {
     514           0 :                         r = do_accept(argv[optind], argv + optind, envp, event.data.fd);
     515           0 :                         if (r < 0)
     516           0 :                                 return EXIT_FAILURE;
     517             :                 } else
     518           0 :                         break;
     519             :         }
     520             : 
     521           0 :         exec_process(argv[optind], argv + optind, envp, SD_LISTEN_FDS_START, (size_t) n);
     522             : 
     523           0 :         return EXIT_SUCCESS;
     524             : }

Generated by: LCOV version 1.14