linux/tools/lib/subcmd/exec-cmd.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include <linux/compiler.h>
   3#include <linux/string.h>
   4#include <sys/types.h>
   5#include <sys/stat.h>
   6#include <unistd.h>
   7#include <string.h>
   8#include <stdlib.h>
   9#include <stdio.h>
  10#include "subcmd-util.h"
  11#include "exec-cmd.h"
  12#include "subcmd-config.h"
  13
  14#define MAX_ARGS        32
  15#define PATH_MAX        4096
  16
  17static const char *argv_exec_path;
  18static const char *argv0_path;
  19
  20void exec_cmd_init(const char *exec_name, const char *prefix,
  21                   const char *exec_path, const char *exec_path_env)
  22{
  23        subcmd_config.exec_name         = exec_name;
  24        subcmd_config.prefix            = prefix;
  25        subcmd_config.exec_path         = exec_path;
  26        subcmd_config.exec_path_env     = exec_path_env;
  27}
  28
  29#define is_dir_sep(c) ((c) == '/')
  30
  31static int is_absolute_path(const char *path)
  32{
  33        return path[0] == '/';
  34}
  35
  36static const char *get_pwd_cwd(void)
  37{
  38        static char cwd[PATH_MAX + 1];
  39        char *pwd;
  40        struct stat cwd_stat, pwd_stat;
  41        if (getcwd(cwd, PATH_MAX) == NULL)
  42                return NULL;
  43        pwd = getenv("PWD");
  44        if (pwd && strcmp(pwd, cwd)) {
  45                stat(cwd, &cwd_stat);
  46                if (!stat(pwd, &pwd_stat) &&
  47                    pwd_stat.st_dev == cwd_stat.st_dev &&
  48                    pwd_stat.st_ino == cwd_stat.st_ino) {
  49                        strlcpy(cwd, pwd, PATH_MAX);
  50                }
  51        }
  52        return cwd;
  53}
  54
  55static const char *make_nonrelative_path(const char *path)
  56{
  57        static char buf[PATH_MAX + 1];
  58
  59        if (is_absolute_path(path)) {
  60                if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
  61                        die("Too long path: %.*s", 60, path);
  62        } else {
  63                const char *cwd = get_pwd_cwd();
  64                if (!cwd)
  65                        die("Cannot determine the current working directory");
  66                if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
  67                        die("Too long path: %.*s", 60, path);
  68        }
  69        return buf;
  70}
  71
  72char *system_path(const char *path)
  73{
  74        char *buf = NULL;
  75
  76        if (is_absolute_path(path))
  77                return strdup(path);
  78
  79        astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
  80
  81        return buf;
  82}
  83
  84const char *extract_argv0_path(const char *argv0)
  85{
  86        const char *slash;
  87
  88        if (!argv0 || !*argv0)
  89                return NULL;
  90        slash = argv0 + strlen(argv0);
  91
  92        while (argv0 <= slash && !is_dir_sep(*slash))
  93                slash--;
  94
  95        if (slash >= argv0) {
  96                argv0_path = strndup(argv0, slash - argv0);
  97                return argv0_path ? slash + 1 : NULL;
  98        }
  99
 100        return argv0;
 101}
 102
 103void set_argv_exec_path(const char *exec_path)
 104{
 105        argv_exec_path = exec_path;
 106        /*
 107         * Propagate this setting to external programs.
 108         */
 109        setenv(subcmd_config.exec_path_env, exec_path, 1);
 110}
 111
 112
 113/* Returns the highest-priority location to look for subprograms. */
 114char *get_argv_exec_path(void)
 115{
 116        char *env;
 117
 118        if (argv_exec_path)
 119                return strdup(argv_exec_path);
 120
 121        env = getenv(subcmd_config.exec_path_env);
 122        if (env && *env)
 123                return strdup(env);
 124
 125        return system_path(subcmd_config.exec_path);
 126}
 127
 128static void add_path(char **out, const char *path)
 129{
 130        if (path && *path) {
 131                if (is_absolute_path(path))
 132                        astrcat(out, path);
 133                else
 134                        astrcat(out, make_nonrelative_path(path));
 135
 136                astrcat(out, ":");
 137        }
 138}
 139
 140void setup_path(void)
 141{
 142        const char *old_path = getenv("PATH");
 143        char *new_path = NULL;
 144        char *tmp = get_argv_exec_path();
 145
 146        add_path(&new_path, tmp);
 147        add_path(&new_path, argv0_path);
 148        free(tmp);
 149
 150        if (old_path)
 151                astrcat(&new_path, old_path);
 152        else
 153                astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
 154
 155        setenv("PATH", new_path, 1);
 156
 157        free(new_path);
 158}
 159
 160static const char **prepare_exec_cmd(const char **argv)
 161{
 162        int argc;
 163        const char **nargv;
 164
 165        for (argc = 0; argv[argc]; argc++)
 166                ; /* just counting */
 167        nargv = malloc(sizeof(*nargv) * (argc + 2));
 168
 169        nargv[0] = subcmd_config.exec_name;
 170        for (argc = 0; argv[argc]; argc++)
 171                nargv[argc + 1] = argv[argc];
 172        nargv[argc + 1] = NULL;
 173        return nargv;
 174}
 175
 176int execv_cmd(const char **argv) {
 177        const char **nargv = prepare_exec_cmd(argv);
 178
 179        /* execvp() can only ever return if it fails */
 180        execvp(subcmd_config.exec_name, (char **)nargv);
 181
 182        free(nargv);
 183        return -1;
 184}
 185
 186
 187int execl_cmd(const char *cmd,...)
 188{
 189        int argc;
 190        const char *argv[MAX_ARGS + 1];
 191        const char *arg;
 192        va_list param;
 193
 194        va_start(param, cmd);
 195        argv[0] = cmd;
 196        argc = 1;
 197        while (argc < MAX_ARGS) {
 198                arg = argv[argc++] = va_arg(param, char *);
 199                if (!arg)
 200                        break;
 201        }
 202        va_end(param);
 203        if (MAX_ARGS <= argc) {
 204                fprintf(stderr, " Error: too many args to run %s\n", cmd);
 205                return -1;
 206        }
 207
 208        argv[argc] = NULL;
 209        return execv_cmd(argv);
 210}
 211