LCOV - code coverage report
Current view: top level - shared - exec-util.c (source / functions) Hit Total Coverage
Test: main_coverage.info Lines: 148 220 67.3 %
Date: 2019-08-22 15:41:25 Functions: 9 10 90.0 %

          Line data    Source code
       1             : /* SPDX-License-Identifier: LGPL-2.1+ */
       2             : 
       3             : #include <dirent.h>
       4             : #include <errno.h>
       5             : #include <sys/prctl.h>
       6             : #include <sys/types.h>
       7             : #include <unistd.h>
       8             : #include <stdio.h>
       9             : 
      10             : #include "alloc-util.h"
      11             : #include "conf-files.h"
      12             : #include "env-file.h"
      13             : #include "env-util.h"
      14             : #include "exec-util.h"
      15             : #include "fd-util.h"
      16             : #include "fileio.h"
      17             : #include "hashmap.h"
      18             : #include "macro.h"
      19             : #include "process-util.h"
      20             : #include "rlimit-util.h"
      21             : #include "serialize.h"
      22             : #include "set.h"
      23             : #include "signal-util.h"
      24             : #include "stat-util.h"
      25             : #include "string-table.h"
      26             : #include "string-util.h"
      27             : #include "strv.h"
      28             : #include "terminal-util.h"
      29             : #include "tmpfile-util.h"
      30             : #include "util.h"
      31             : 
      32             : /* Put this test here for a lack of better place */
      33             : assert_cc(EAGAIN == EWOULDBLOCK);
      34             : 
      35          21 : static int do_spawn(const char *path, char *argv[], int stdout_fd, pid_t *pid) {
      36             : 
      37             :         pid_t _pid;
      38             :         int r;
      39             : 
      40          21 :         if (null_or_empty_path(path)) {
      41           0 :                 log_debug("%s is empty (a mask).", path);
      42           0 :                 return 0;
      43             :         }
      44             : 
      45          21 :         r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
      46          35 :         if (r < 0)
      47           0 :                 return r;
      48          35 :         if (r == 0) {
      49             :                 char *_argv[2];
      50             : 
      51          21 :                 if (stdout_fd >= 0) {
      52          16 :                         r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
      53          16 :                         if (r < 0)
      54           0 :                                 _exit(EXIT_FAILURE);
      55             :                 }
      56             : 
      57          21 :                 (void) rlimit_nofile_safe();
      58             : 
      59          21 :                 if (!argv) {
      60          21 :                         _argv[0] = (char*) path;
      61          21 :                         _argv[1] = NULL;
      62          21 :                         argv = _argv;
      63             :                 } else
      64           0 :                         argv[0] = (char*) path;
      65             : 
      66          21 :                 execv(path, argv);
      67          21 :                 log_error_errno(errno, "Failed to execute %s: %m", path);
      68          21 :                 _exit(EXIT_FAILURE);
      69             :         }
      70             : 
      71          14 :         *pid = _pid;
      72          14 :         return 1;
      73             : }
      74             : 
      75           7 : static int do_execute(
      76             :                 char **directories,
      77             :                 usec_t timeout,
      78             :                 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
      79             :                 void* const callback_args[_STDOUT_CONSUME_MAX],
      80             :                 int output_fd,
      81             :                 char *argv[],
      82             :                 char *envp[],
      83             :                 ExecDirFlags flags) {
      84             : 
      85           0 :         _cleanup_hashmap_free_free_ Hashmap *pids = NULL;
      86           0 :         _cleanup_strv_free_ char **paths = NULL;
      87             :         char **path, **e;
      88             :         int r;
      89             :         bool parallel_execution;
      90             : 
      91             :         /* We fork this all off from a child process so that we can somewhat cleanly make
      92             :          * use of SIGALRM to set a time limit.
      93             :          *
      94             :          * We attempt to perform parallel execution if configured by the user, however
      95             :          * if `callbacks` is nonnull, execution must be serial.
      96             :          */
      97           7 :         parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
      98             : 
      99           7 :         r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
     100           7 :         if (r < 0)
     101           0 :                 return log_error_errno(r, "Failed to enumerate executables: %m");
     102             : 
     103           7 :         if (parallel_execution) {
     104           1 :                 pids = hashmap_new(NULL);
     105           1 :                 if (!pids)
     106           0 :                         return log_oom();
     107             :         }
     108             : 
     109             :         /* Abort execution of this process after the timeout. We simply rely on SIGALRM as
     110             :          * default action terminating the process, and turn on alarm(). */
     111             : 
     112           7 :         if (timeout != USEC_INFINITY)
     113           7 :                 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
     114             : 
     115           8 :         STRV_FOREACH(e, envp)
     116           1 :                 if (putenv(*e) != 0)
     117           0 :                         return log_error_errno(errno, "Failed to set environment variable: %m");
     118             : 
     119          21 :         STRV_FOREACH(path, paths) {
     120          14 :                 _cleanup_free_ char *t = NULL;
     121          14 :                 _cleanup_close_ int fd = -1;
     122             :                 pid_t pid;
     123             : 
     124          21 :                 t = strdup(*path);
     125          21 :                 if (!t)
     126           0 :                         return log_oom();
     127             : 
     128          21 :                 if (callbacks) {
     129          16 :                         fd = open_serialization_fd(basename(*path));
     130          16 :                         if (fd < 0)
     131           0 :                                 return log_error_errno(fd, "Failed to open serialization file: %m");
     132             :                 }
     133             : 
     134          21 :                 r = do_spawn(t, argv, fd, &pid);
     135          14 :                 if (r <= 0)
     136           0 :                         continue;
     137             : 
     138          14 :                 if (parallel_execution) {
     139           2 :                         r = hashmap_put(pids, PID_TO_PTR(pid), t);
     140           2 :                         if (r < 0)
     141           0 :                                 return log_oom();
     142           2 :                         t = NULL;
     143             :                 } else {
     144          12 :                         r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
     145          12 :                         if (FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS)) {
     146          11 :                                 if (r < 0)
     147           0 :                                         continue;
     148           1 :                         } else if (r > 0)
     149           0 :                                 return r;
     150             : 
     151          12 :                         if (callbacks) {
     152          11 :                                 if (lseek(fd, 0, SEEK_SET) < 0)
     153           0 :                                         return log_error_errno(errno, "Failed to seek on serialization fd: %m");
     154             : 
     155          11 :                                 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
     156          11 :                                 fd = -1;
     157          11 :                                 if (r < 0)
     158           0 :                                         return log_error_errno(r, "Failed to process output from %s: %m", *path);
     159             :                         }
     160             :                 }
     161             :         }
     162             : 
     163           0 :         if (callbacks) {
     164           0 :                 r = callbacks[STDOUT_COLLECT](output_fd, callback_args[STDOUT_COLLECT]);
     165           0 :                 if (r < 0)
     166           0 :                         return log_error_errno(r, "Callback two failed: %m");
     167             :         }
     168             : 
     169           0 :         while (!hashmap_isempty(pids)) {
     170           0 :                 _cleanup_free_ char *t = NULL;
     171             :                 pid_t pid;
     172             : 
     173           0 :                 pid = PTR_TO_PID(hashmap_first_key(pids));
     174           0 :                 assert(pid > 0);
     175             : 
     176           0 :                 t = hashmap_remove(pids, PID_TO_PTR(pid));
     177           0 :                 assert(t);
     178             : 
     179           0 :                 r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
     180           0 :                 if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
     181           0 :                         return r;
     182             :         }
     183             : 
     184           0 :         return 0;
     185             : }
     186             : 
     187           7 : int execute_directories(
     188             :                 const char* const* directories,
     189             :                 usec_t timeout,
     190             :                 gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX],
     191             :                 void* const callback_args[_STDOUT_CONSUME_MAX],
     192             :                 char *argv[],
     193             :                 char *envp[],
     194             :                 ExecDirFlags flags) {
     195             : 
     196           7 :         char **dirs = (char**) directories;
     197           7 :         _cleanup_close_ int fd = -1;
     198             :         char *name;
     199             :         int r;
     200             :         pid_t executor_pid;
     201             : 
     202           7 :         assert(!strv_isempty(dirs));
     203             : 
     204           7 :         name = basename(dirs[0]);
     205           7 :         assert(!isempty(name));
     206             : 
     207           7 :         if (callbacks) {
     208           5 :                 assert(callback_args);
     209           5 :                 assert(callbacks[STDOUT_GENERATE]);
     210           5 :                 assert(callbacks[STDOUT_COLLECT]);
     211           5 :                 assert(callbacks[STDOUT_CONSUME]);
     212             : 
     213           5 :                 fd = open_serialization_fd(name);
     214           5 :                 if (fd < 0)
     215           0 :                         return log_error_errno(fd, "Failed to open serialization file: %m");
     216             :         }
     217             : 
     218             :         /* Executes all binaries in the directories serially or in parallel and waits for
     219             :          * them to finish. Optionally a timeout is applied. If a file with the same name
     220             :          * exists in more than one directory, the earliest one wins. */
     221             : 
     222           7 :         r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid);
     223          14 :         if (r < 0)
     224           0 :                 return r;
     225          14 :         if (r == 0) {
     226           7 :                 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags);
     227           0 :                 _exit(r < 0 ? EXIT_FAILURE : r);
     228             :         }
     229             : 
     230           7 :         r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0);
     231           7 :         if (r < 0)
     232           0 :                 return r;
     233           7 :         if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
     234           1 :                 return r;
     235             : 
     236           6 :         if (!callbacks)
     237           1 :                 return 0;
     238             : 
     239           5 :         if (lseek(fd, 0, SEEK_SET) < 0)
     240           0 :                 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
     241             : 
     242           5 :         r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
     243           5 :         fd = -1;
     244           5 :         if (r < 0)
     245           0 :                 return log_error_errno(r, "Failed to parse returned data: %m");
     246           5 :         return 0;
     247             : }
     248             : 
     249           4 : static int gather_environment_generate(int fd, void *arg) {
     250           4 :         char ***env = arg, **x, **y;
     251           4 :         _cleanup_fclose_ FILE *f = NULL;
     252           4 :         _cleanup_strv_free_ char **new = NULL;
     253             :         int r;
     254             : 
     255             :         /* Read a series of VAR=value assignments from fd, use them to update the list of
     256             :          * variables in env. Also update the exported environment.
     257             :          *
     258             :          * fd is always consumed, even on error.
     259             :          */
     260             : 
     261           4 :         assert(env);
     262             : 
     263           4 :         f = fdopen(fd, "r");
     264           4 :         if (!f) {
     265           0 :                 safe_close(fd);
     266           0 :                 return -errno;
     267             :         }
     268             : 
     269           4 :         r = load_env_file_pairs(f, NULL, &new);
     270           4 :         if (r < 0)
     271           0 :                 return r;
     272             : 
     273           8 :         STRV_FOREACH_PAIR(x, y, new) {
     274             :                 char *p;
     275             : 
     276           4 :                 if (!env_name_is_valid(*x)) {
     277           0 :                         log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
     278           0 :                         continue;
     279             :                 }
     280             : 
     281           4 :                 p = strjoin(*x, "=", *y);
     282           4 :                 if (!p)
     283           0 :                         return -ENOMEM;
     284             : 
     285           4 :                 r = strv_env_replace(env, p);
     286           4 :                 if (r < 0)
     287           0 :                         return r;
     288             : 
     289           4 :                 if (setenv(*x, *y, true) < 0)
     290           0 :                         return -errno;
     291             :         }
     292             : 
     293           4 :         return r;
     294             : }
     295             : 
     296           0 : static int gather_environment_collect(int fd, void *arg) {
     297           0 :         _cleanup_fclose_ FILE *f = NULL;
     298           0 :         char ***env = arg;
     299             :         int r;
     300             : 
     301             :         /* Write out a series of env=cescape(VAR=value) assignments to fd. */
     302             : 
     303           0 :         assert(env);
     304             : 
     305           0 :         f = fdopen(fd, "w");
     306           0 :         if (!f) {
     307           0 :                 safe_close(fd);
     308           0 :                 return -errno;
     309             :         }
     310             : 
     311           0 :         r = serialize_strv(f, "env", *env);
     312           0 :         if (r < 0)
     313           0 :                 return r;
     314             : 
     315           0 :         r = fflush_and_check(f);
     316           0 :         if (r < 0)
     317           0 :                 return r;
     318             : 
     319           0 :         return 0;
     320             : }
     321             : 
     322           2 : static int gather_environment_consume(int fd, void *arg) {
     323           2 :         _cleanup_fclose_ FILE *f = NULL;
     324           2 :         char ***env = arg;
     325           2 :         int r = 0;
     326             : 
     327             :         /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
     328             : 
     329           2 :         assert(env);
     330             : 
     331           2 :         f = fdopen(fd, "r");
     332           2 :         if (!f) {
     333           0 :                 safe_close(fd);
     334           0 :                 return -errno;
     335             :         }
     336             : 
     337           8 :         for (;;) {
     338          10 :                 _cleanup_free_ char *line = NULL;
     339             :                 const char *v;
     340             :                 int k;
     341             : 
     342          10 :                 k = read_line(f, LONG_LINE_MAX, &line);
     343          10 :                 if (k < 0)
     344           0 :                         return k;
     345          10 :                 if (k == 0)
     346           2 :                         break;
     347             : 
     348           8 :                 v = startswith(line, "env=");
     349           8 :                 if (!v) {
     350           0 :                         log_debug("Serialization line \"%s\" unexpectedly didn't start with \"env=\".", line);
     351           0 :                         if (r == 0)
     352           0 :                                 r = -EINVAL;
     353             : 
     354           0 :                         continue;
     355             :                 }
     356             : 
     357           8 :                 k = deserialize_environment(v, env);
     358           8 :                 if (k < 0) {
     359           0 :                         log_debug_errno(k, "Invalid serialization line \"%s\": %m", line);
     360             : 
     361           0 :                         if (r == 0)
     362           0 :                                 r = k;
     363             :                 }
     364             :         }
     365             : 
     366           2 :         return r;
     367             : }
     368             : 
     369           2 : int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
     370           2 :         ExecCommandFlags ex_flag, ret_flags = 0;
     371             :         char **opt;
     372             : 
     373           2 :         assert(flags);
     374             : 
     375           7 :         STRV_FOREACH(opt, ex_opts) {
     376           6 :                 ex_flag = exec_command_flags_from_string(*opt);
     377           6 :                 if (ex_flag >= 0)
     378           5 :                         ret_flags |= ex_flag;
     379             :                 else
     380           1 :                         return -EINVAL;
     381             :         }
     382             : 
     383           1 :         *flags = ret_flags;
     384             : 
     385           1 :         return 0;
     386             : }
     387             : 
     388           3 : int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
     389           3 :         _cleanup_strv_free_ char **ret_opts = NULL;
     390           3 :         ExecCommandFlags it = flags;
     391             :         const char *str;
     392             :         int i, r;
     393             : 
     394           3 :         assert(ex_opts);
     395             : 
     396          13 :         for (i = 0; it != 0; it &= ~(1 << i), i++) {
     397          11 :                 if (FLAGS_SET(flags, (1 << i))) {
     398           9 :                         str = exec_command_flags_to_string(1 << i);
     399           9 :                         if (!str)
     400           1 :                                 return -EINVAL;
     401             : 
     402           8 :                         r = strv_extend(&ret_opts, str);
     403           8 :                         if (r < 0)
     404           0 :                                 return r;
     405             :                 }
     406             :         }
     407             : 
     408           2 :         *ex_opts = TAKE_PTR(ret_opts);
     409             : 
     410           2 :         return 0;
     411             : }
     412             : 
     413             : const gather_stdout_callback_t gather_environment[] = {
     414             :         gather_environment_generate,
     415             :         gather_environment_collect,
     416             :         gather_environment_consume,
     417             : };
     418             : 
     419             : static const char* const exec_command_strings[] = {
     420             :         "ignore-failure", /* EXEC_COMMAND_IGNORE_FAILURE */
     421             :         "privileged",     /* EXEC_COMMAND_FULLY_PRIVILEGED */
     422             :         "no-setuid",      /* EXEC_COMMAND_NO_SETUID */
     423             :         "ambient",        /* EXEC_COMMAND_AMBIENT_MAGIC */
     424             :         "no-env-expand",  /* EXEC_COMMAND_NO_ENV_EXPAND */
     425             : };
     426             : 
     427           9 : const char* exec_command_flags_to_string(ExecCommandFlags i) {
     428             :         size_t idx;
     429             : 
     430          31 :         for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
     431          30 :                 if (i == (1 << idx))
     432           8 :                         return exec_command_strings[idx];
     433             : 
     434           1 :         return NULL;
     435             : }
     436             : 
     437           6 : ExecCommandFlags exec_command_flags_from_string(const char *s) {
     438             :         ssize_t idx;
     439             : 
     440           6 :         idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
     441             : 
     442           6 :         if (idx < 0)
     443           1 :                 return _EXEC_COMMAND_FLAGS_INVALID;
     444             :         else
     445           5 :                 return 1 << idx;
     446             : }

Generated by: LCOV version 1.14