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