LCOV - code coverage report
Current view: top level - shared - exec-util.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 148 220 67.3 %
Date: 2019-08-23 13:36:53 Functions: 9 10 90.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 103 214 48.1 %

           Branch data     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                 :         84 : 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         [ -  + ]:         84 :         if (null_or_empty_path(path)) {
      41         [ #  # ]:          0 :                 log_debug("%s is empty (a mask).", path);
      42                 :          0 :                 return 0;
      43                 :            :         }
      44                 :            : 
      45                 :         84 :         r = safe_fork("(direxec)", FORK_DEATHSIG|FORK_LOG, &_pid);
      46         [ -  + ]:        140 :         if (r < 0)
      47                 :          0 :                 return r;
      48         [ +  + ]:        140 :         if (r == 0) {
      49                 :            :                 char *_argv[2];
      50                 :            : 
      51         [ +  + ]:         84 :                 if (stdout_fd >= 0) {
      52                 :         64 :                         r = rearrange_stdio(STDIN_FILENO, stdout_fd, STDERR_FILENO);
      53         [ -  + ]:         64 :                         if (r < 0)
      54                 :          0 :                                 _exit(EXIT_FAILURE);
      55                 :            :                 }
      56                 :            : 
      57                 :         84 :                 (void) rlimit_nofile_safe();
      58                 :            : 
      59         [ +  - ]:         84 :                 if (!argv) {
      60                 :         84 :                         _argv[0] = (char*) path;
      61                 :         84 :                         _argv[1] = NULL;
      62                 :         84 :                         argv = _argv;
      63                 :            :                 } else
      64                 :          0 :                         argv[0] = (char*) path;
      65                 :            : 
      66                 :         84 :                 execv(path, argv);
      67         [ -  + ]:         84 :                 log_error_errno(errno, "Failed to execute %s: %m", path);
      68                 :         84 :                 _exit(EXIT_FAILURE);
      69                 :            :         }
      70                 :            : 
      71                 :         56 :         *pid = _pid;
      72                 :         56 :         return 1;
      73                 :            : }
      74                 :            : 
      75                 :         28 : 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   [ +  +  +  + ]:         28 :         parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks;
      98                 :            : 
      99                 :         28 :         r = conf_files_list_strv(&paths, NULL, NULL, CONF_FILES_EXECUTABLE|CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED, (const char* const*) directories);
     100         [ -  + ]:         28 :         if (r < 0)
     101         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to enumerate executables: %m");
     102                 :            : 
     103         [ +  + ]:         28 :         if (parallel_execution) {
     104                 :          4 :                 pids = hashmap_new(NULL);
     105         [ -  + ]:          4 :                 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         [ +  - ]:         28 :         if (timeout != USEC_INFINITY)
     113                 :         28 :                 alarm(DIV_ROUND_UP(timeout, USEC_PER_SEC));
     114                 :            : 
     115   [ +  +  +  + ]:         32 :         STRV_FOREACH(e, envp)
     116         [ -  + ]:          4 :                 if (putenv(*e) != 0)
     117         [ #  # ]:          0 :                         return log_error_errno(errno, "Failed to set environment variable: %m");
     118                 :            : 
     119   [ +  -  +  - ]:         84 :         STRV_FOREACH(path, paths) {
     120      [ +  -  - ]:         56 :                 _cleanup_free_ char *t = NULL;
     121      [ +  -  - ]:         56 :                 _cleanup_close_ int fd = -1;
     122                 :            :                 pid_t pid;
     123                 :            : 
     124                 :         84 :                 t = strdup(*path);
     125         [ -  + ]:         84 :                 if (!t)
     126                 :          0 :                         return log_oom();
     127                 :            : 
     128         [ +  + ]:         84 :                 if (callbacks) {
     129                 :         64 :                         fd = open_serialization_fd(basename(*path));
     130         [ -  + ]:         64 :                         if (fd < 0)
     131         [ #  # ]:          0 :                                 return log_error_errno(fd, "Failed to open serialization file: %m");
     132                 :            :                 }
     133                 :            : 
     134                 :         84 :                 r = do_spawn(t, argv, fd, &pid);
     135         [ -  + ]:         56 :                 if (r <= 0)
     136                 :          0 :                         continue;
     137                 :            : 
     138         [ +  + ]:         56 :                 if (parallel_execution) {
     139                 :          8 :                         r = hashmap_put(pids, PID_TO_PTR(pid), t);
     140         [ -  + ]:          8 :                         if (r < 0)
     141                 :          0 :                                 return log_oom();
     142                 :          8 :                         t = NULL;
     143                 :            :                 } else {
     144                 :         48 :                         r = wait_for_terminate_and_check(t, pid, WAIT_LOG);
     145         [ +  + ]:         48 :                         if (FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS)) {
     146         [ -  + ]:         44 :                                 if (r < 0)
     147                 :          0 :                                         continue;
     148         [ -  + ]:          4 :                         } else if (r > 0)
     149                 :          0 :                                 return r;
     150                 :            : 
     151         [ +  + ]:         48 :                         if (callbacks) {
     152         [ -  + ]:         44 :                                 if (lseek(fd, 0, SEEK_SET) < 0)
     153         [ #  # ]:          0 :                                         return log_error_errno(errno, "Failed to seek on serialization fd: %m");
     154                 :            : 
     155                 :         44 :                                 r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]);
     156                 :         44 :                                 fd = -1;
     157         [ -  + ]:         44 :                                 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                 :         28 : 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                 :         28 :         char **dirs = (char**) directories;
     197                 :         28 :         _cleanup_close_ int fd = -1;
     198                 :            :         char *name;
     199                 :            :         int r;
     200                 :            :         pid_t executor_pid;
     201                 :            : 
     202         [ -  + ]:         28 :         assert(!strv_isempty(dirs));
     203                 :            : 
     204                 :         28 :         name = basename(dirs[0]);
     205         [ -  + ]:         28 :         assert(!isempty(name));
     206                 :            : 
     207         [ +  + ]:         28 :         if (callbacks) {
     208         [ -  + ]:         20 :                 assert(callback_args);
     209         [ -  + ]:         20 :                 assert(callbacks[STDOUT_GENERATE]);
     210         [ -  + ]:         20 :                 assert(callbacks[STDOUT_COLLECT]);
     211         [ -  + ]:         20 :                 assert(callbacks[STDOUT_CONSUME]);
     212                 :            : 
     213                 :         20 :                 fd = open_serialization_fd(name);
     214         [ -  + ]:         20 :                 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                 :         28 :         r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid);
     223         [ -  + ]:         56 :         if (r < 0)
     224                 :          0 :                 return r;
     225         [ +  + ]:         56 :         if (r == 0) {
     226                 :         28 :                 r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags);
     227         [ #  # ]:          0 :                 _exit(r < 0 ? EXIT_FAILURE : r);
     228                 :            :         }
     229                 :            : 
     230                 :         28 :         r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0);
     231         [ -  + ]:         28 :         if (r < 0)
     232                 :          0 :                 return r;
     233   [ +  +  +  - ]:         28 :         if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0)
     234                 :          4 :                 return r;
     235                 :            : 
     236         [ +  + ]:         24 :         if (!callbacks)
     237                 :          4 :                 return 0;
     238                 :            : 
     239         [ -  + ]:         20 :         if (lseek(fd, 0, SEEK_SET) < 0)
     240         [ #  # ]:          0 :                 return log_error_errno(errno, "Failed to rewind serialization fd: %m");
     241                 :            : 
     242                 :         20 :         r = callbacks[STDOUT_CONSUME](fd, callback_args[STDOUT_CONSUME]);
     243                 :         20 :         fd = -1;
     244         [ -  + ]:         20 :         if (r < 0)
     245         [ #  # ]:          0 :                 return log_error_errno(r, "Failed to parse returned data: %m");
     246                 :         20 :         return 0;
     247                 :            : }
     248                 :            : 
     249                 :         16 : static int gather_environment_generate(int fd, void *arg) {
     250                 :         16 :         char ***env = arg, **x, **y;
     251                 :         16 :         _cleanup_fclose_ FILE *f = NULL;
     252                 :         16 :         _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         [ -  + ]:         16 :         assert(env);
     262                 :            : 
     263                 :         16 :         f = fdopen(fd, "r");
     264         [ -  + ]:         16 :         if (!f) {
     265                 :          0 :                 safe_close(fd);
     266                 :          0 :                 return -errno;
     267                 :            :         }
     268                 :            : 
     269                 :         16 :         r = load_env_file_pairs(f, NULL, &new);
     270         [ -  + ]:         16 :         if (r < 0)
     271                 :          0 :                 return r;
     272                 :            : 
     273   [ +  -  +  +  :         32 :         STRV_FOREACH_PAIR(x, y, new) {
                   +  - ]
     274                 :            :                 char *p;
     275                 :            : 
     276         [ -  + ]:         16 :                 if (!env_name_is_valid(*x)) {
     277         [ #  # ]:          0 :                         log_warning("Invalid variable assignment \"%s=...\", ignoring.", *x);
     278                 :          0 :                         continue;
     279                 :            :                 }
     280                 :            : 
     281                 :         16 :                 p = strjoin(*x, "=", *y);
     282         [ -  + ]:         16 :                 if (!p)
     283                 :          0 :                         return -ENOMEM;
     284                 :            : 
     285                 :         16 :                 r = strv_env_replace(env, p);
     286         [ -  + ]:         16 :                 if (r < 0)
     287                 :          0 :                         return r;
     288                 :            : 
     289         [ -  + ]:         16 :                 if (setenv(*x, *y, true) < 0)
     290                 :          0 :                         return -errno;
     291                 :            :         }
     292                 :            : 
     293                 :         16 :         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                 :          8 : static int gather_environment_consume(int fd, void *arg) {
     323                 :          8 :         _cleanup_fclose_ FILE *f = NULL;
     324                 :          8 :         char ***env = arg;
     325                 :          8 :         int r = 0;
     326                 :            : 
     327                 :            :         /* Read a series of env=cescape(VAR=value) assignments from fd into env. */
     328                 :            : 
     329         [ -  + ]:          8 :         assert(env);
     330                 :            : 
     331                 :          8 :         f = fdopen(fd, "r");
     332         [ -  + ]:          8 :         if (!f) {
     333                 :          0 :                 safe_close(fd);
     334                 :          0 :                 return -errno;
     335                 :            :         }
     336                 :            : 
     337                 :         32 :         for (;;) {
     338   [ +  -  +  - ]:         40 :                 _cleanup_free_ char *line = NULL;
     339                 :            :                 const char *v;
     340                 :            :                 int k;
     341                 :            : 
     342                 :         40 :                 k = read_line(f, LONG_LINE_MAX, &line);
     343         [ -  + ]:         40 :                 if (k < 0)
     344                 :          0 :                         return k;
     345         [ +  + ]:         40 :                 if (k == 0)
     346                 :          8 :                         break;
     347                 :            : 
     348                 :         32 :                 v = startswith(line, "env=");
     349         [ -  + ]:         32 :                 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                 :         32 :                 k = deserialize_environment(v, env);
     358         [ -  + ]:         32 :                 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                 :          8 :         return r;
     367                 :            : }
     368                 :            : 
     369                 :          8 : int exec_command_flags_from_strv(char **ex_opts, ExecCommandFlags *flags) {
     370                 :          8 :         ExecCommandFlags ex_flag, ret_flags = 0;
     371                 :            :         char **opt;
     372                 :            : 
     373         [ -  + ]:          8 :         assert(flags);
     374                 :            : 
     375   [ +  -  +  + ]:         28 :         STRV_FOREACH(opt, ex_opts) {
     376                 :         24 :                 ex_flag = exec_command_flags_from_string(*opt);
     377         [ +  + ]:         24 :                 if (ex_flag >= 0)
     378                 :         20 :                         ret_flags |= ex_flag;
     379                 :            :                 else
     380                 :          4 :                         return -EINVAL;
     381                 :            :         }
     382                 :            : 
     383                 :          4 :         *flags = ret_flags;
     384                 :            : 
     385                 :          4 :         return 0;
     386                 :            : }
     387                 :            : 
     388                 :         12 : int exec_command_flags_to_strv(ExecCommandFlags flags, char ***ex_opts) {
     389                 :         12 :         _cleanup_strv_free_ char **ret_opts = NULL;
     390                 :         12 :         ExecCommandFlags it = flags;
     391                 :            :         const char *str;
     392                 :            :         int i, r;
     393                 :            : 
     394         [ -  + ]:         12 :         assert(ex_opts);
     395                 :            : 
     396         [ +  + ]:         52 :         for (i = 0; it != 0; it &= ~(1 << i), i++) {
     397         [ +  + ]:         44 :                 if (FLAGS_SET(flags, (1 << i))) {
     398                 :         36 :                         str = exec_command_flags_to_string(1 << i);
     399         [ +  + ]:         36 :                         if (!str)
     400                 :          4 :                                 return -EINVAL;
     401                 :            : 
     402                 :         32 :                         r = strv_extend(&ret_opts, str);
     403         [ -  + ]:         32 :                         if (r < 0)
     404                 :          0 :                                 return r;
     405                 :            :                 }
     406                 :            :         }
     407                 :            : 
     408                 :          8 :         *ex_opts = TAKE_PTR(ret_opts);
     409                 :            : 
     410                 :          8 :         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                 :         36 : const char* exec_command_flags_to_string(ExecCommandFlags i) {
     428                 :            :         size_t idx;
     429                 :            : 
     430         [ +  + ]:        124 :         for (idx = 0; idx < ELEMENTSOF(exec_command_strings); idx++)
     431         [ +  + ]:        120 :                 if (i == (1 << idx))
     432                 :         32 :                         return exec_command_strings[idx];
     433                 :            : 
     434                 :          4 :         return NULL;
     435                 :            : }
     436                 :            : 
     437                 :         24 : ExecCommandFlags exec_command_flags_from_string(const char *s) {
     438                 :            :         ssize_t idx;
     439                 :            : 
     440                 :         24 :         idx = string_table_lookup(exec_command_strings, ELEMENTSOF(exec_command_strings), s);
     441                 :            : 
     442         [ +  + ]:         24 :         if (idx < 0)
     443                 :          4 :                 return _EXEC_COMMAND_FLAGS_INVALID;
     444                 :            :         else
     445                 :         20 :                 return 1 << idx;
     446                 :            : }

Generated by: LCOV version 1.14