1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
24b6ab94eSJosh Poimboeuf #include <linux/compiler.h>
34b6ab94eSJosh Poimboeuf #include <linux/string.h>
44b6ab94eSJosh Poimboeuf #include <sys/types.h>
54b6ab94eSJosh Poimboeuf #include <sys/stat.h>
64b6ab94eSJosh Poimboeuf #include <unistd.h>
74b6ab94eSJosh Poimboeuf #include <string.h>
84b6ab94eSJosh Poimboeuf #include <stdlib.h>
94b6ab94eSJosh Poimboeuf #include <stdio.h>
104b6ab94eSJosh Poimboeuf #include "subcmd-util.h"
114b6ab94eSJosh Poimboeuf #include "exec-cmd.h"
124b6ab94eSJosh Poimboeuf #include "subcmd-config.h"
134b6ab94eSJosh Poimboeuf
144b6ab94eSJosh Poimboeuf #define MAX_ARGS 32
154b6ab94eSJosh Poimboeuf #define PATH_MAX 4096
164b6ab94eSJosh Poimboeuf
174b6ab94eSJosh Poimboeuf static const char *argv_exec_path;
184b6ab94eSJosh Poimboeuf static const char *argv0_path;
194b6ab94eSJosh Poimboeuf
exec_cmd_init(const char * exec_name,const char * prefix,const char * exec_path,const char * exec_path_env)204b6ab94eSJosh Poimboeuf void exec_cmd_init(const char *exec_name, const char *prefix,
214b6ab94eSJosh Poimboeuf const char *exec_path, const char *exec_path_env)
224b6ab94eSJosh Poimboeuf {
234b6ab94eSJosh Poimboeuf subcmd_config.exec_name = exec_name;
244b6ab94eSJosh Poimboeuf subcmd_config.prefix = prefix;
254b6ab94eSJosh Poimboeuf subcmd_config.exec_path = exec_path;
264b6ab94eSJosh Poimboeuf subcmd_config.exec_path_env = exec_path_env;
2705844393SLeo Yan
2805844393SLeo Yan /* Setup environment variable for invoked shell script. */
2905844393SLeo Yan setenv("PREFIX", prefix, 1);
304b6ab94eSJosh Poimboeuf }
314b6ab94eSJosh Poimboeuf
324b6ab94eSJosh Poimboeuf #define is_dir_sep(c) ((c) == '/')
334b6ab94eSJosh Poimboeuf
is_absolute_path(const char * path)344b6ab94eSJosh Poimboeuf static int is_absolute_path(const char *path)
354b6ab94eSJosh Poimboeuf {
364b6ab94eSJosh Poimboeuf return path[0] == '/';
374b6ab94eSJosh Poimboeuf }
384b6ab94eSJosh Poimboeuf
get_pwd_cwd(char * buf,size_t sz)39*20032376SIan Rogers static const char *get_pwd_cwd(char *buf, size_t sz)
404b6ab94eSJosh Poimboeuf {
414b6ab94eSJosh Poimboeuf char *pwd;
424b6ab94eSJosh Poimboeuf struct stat cwd_stat, pwd_stat;
43*20032376SIan Rogers if (getcwd(buf, sz) == NULL)
444b6ab94eSJosh Poimboeuf return NULL;
454b6ab94eSJosh Poimboeuf pwd = getenv("PWD");
46*20032376SIan Rogers if (pwd && strcmp(pwd, buf)) {
47*20032376SIan Rogers stat(buf, &cwd_stat);
484b6ab94eSJosh Poimboeuf if (!stat(pwd, &pwd_stat) &&
494b6ab94eSJosh Poimboeuf pwd_stat.st_dev == cwd_stat.st_dev &&
504b6ab94eSJosh Poimboeuf pwd_stat.st_ino == cwd_stat.st_ino) {
51*20032376SIan Rogers strlcpy(buf, pwd, sz);
524b6ab94eSJosh Poimboeuf }
534b6ab94eSJosh Poimboeuf }
54*20032376SIan Rogers return buf;
554b6ab94eSJosh Poimboeuf }
564b6ab94eSJosh Poimboeuf
make_nonrelative_path(char * buf,size_t sz,const char * path)57*20032376SIan Rogers static const char *make_nonrelative_path(char *buf, size_t sz, const char *path)
584b6ab94eSJosh Poimboeuf {
594b6ab94eSJosh Poimboeuf if (is_absolute_path(path)) {
60*20032376SIan Rogers if (strlcpy(buf, path, sz) >= sz)
614b6ab94eSJosh Poimboeuf die("Too long path: %.*s", 60, path);
624b6ab94eSJosh Poimboeuf } else {
63*20032376SIan Rogers const char *cwd = get_pwd_cwd(buf, sz);
64*20032376SIan Rogers
654b6ab94eSJosh Poimboeuf if (!cwd)
664b6ab94eSJosh Poimboeuf die("Cannot determine the current working directory");
67*20032376SIan Rogers
68*20032376SIan Rogers if (strlen(cwd) + strlen(path) + 2 >= sz)
694b6ab94eSJosh Poimboeuf die("Too long path: %.*s", 60, path);
70*20032376SIan Rogers
71*20032376SIan Rogers strcat(buf, "/");
72*20032376SIan Rogers strcat(buf, path);
734b6ab94eSJosh Poimboeuf }
744b6ab94eSJosh Poimboeuf return buf;
754b6ab94eSJosh Poimboeuf }
764b6ab94eSJosh Poimboeuf
system_path(const char * path)774b6ab94eSJosh Poimboeuf char *system_path(const char *path)
784b6ab94eSJosh Poimboeuf {
794b6ab94eSJosh Poimboeuf char *buf = NULL;
804b6ab94eSJosh Poimboeuf
814b6ab94eSJosh Poimboeuf if (is_absolute_path(path))
824b6ab94eSJosh Poimboeuf return strdup(path);
834b6ab94eSJosh Poimboeuf
844b6ab94eSJosh Poimboeuf astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
854b6ab94eSJosh Poimboeuf
864b6ab94eSJosh Poimboeuf return buf;
874b6ab94eSJosh Poimboeuf }
884b6ab94eSJosh Poimboeuf
extract_argv0_path(const char * argv0)894b6ab94eSJosh Poimboeuf const char *extract_argv0_path(const char *argv0)
904b6ab94eSJosh Poimboeuf {
914b6ab94eSJosh Poimboeuf const char *slash;
924b6ab94eSJosh Poimboeuf
934b6ab94eSJosh Poimboeuf if (!argv0 || !*argv0)
944b6ab94eSJosh Poimboeuf return NULL;
954b6ab94eSJosh Poimboeuf slash = argv0 + strlen(argv0);
964b6ab94eSJosh Poimboeuf
974b6ab94eSJosh Poimboeuf while (argv0 <= slash && !is_dir_sep(*slash))
984b6ab94eSJosh Poimboeuf slash--;
994b6ab94eSJosh Poimboeuf
1004b6ab94eSJosh Poimboeuf if (slash >= argv0) {
1014b6ab94eSJosh Poimboeuf argv0_path = strndup(argv0, slash - argv0);
1024b6ab94eSJosh Poimboeuf return argv0_path ? slash + 1 : NULL;
1034b6ab94eSJosh Poimboeuf }
1044b6ab94eSJosh Poimboeuf
1054b6ab94eSJosh Poimboeuf return argv0;
1064b6ab94eSJosh Poimboeuf }
1074b6ab94eSJosh Poimboeuf
set_argv_exec_path(const char * exec_path)1084b6ab94eSJosh Poimboeuf void set_argv_exec_path(const char *exec_path)
1094b6ab94eSJosh Poimboeuf {
1104b6ab94eSJosh Poimboeuf argv_exec_path = exec_path;
1114b6ab94eSJosh Poimboeuf /*
1124b6ab94eSJosh Poimboeuf * Propagate this setting to external programs.
1134b6ab94eSJosh Poimboeuf */
1144b6ab94eSJosh Poimboeuf setenv(subcmd_config.exec_path_env, exec_path, 1);
1154b6ab94eSJosh Poimboeuf }
1164b6ab94eSJosh Poimboeuf
1174b6ab94eSJosh Poimboeuf
1184b6ab94eSJosh Poimboeuf /* Returns the highest-priority location to look for subprograms. */
get_argv_exec_path(void)1194b6ab94eSJosh Poimboeuf char *get_argv_exec_path(void)
1204b6ab94eSJosh Poimboeuf {
1214b6ab94eSJosh Poimboeuf char *env;
1224b6ab94eSJosh Poimboeuf
1234b6ab94eSJosh Poimboeuf if (argv_exec_path)
1244b6ab94eSJosh Poimboeuf return strdup(argv_exec_path);
1254b6ab94eSJosh Poimboeuf
1264b6ab94eSJosh Poimboeuf env = getenv(subcmd_config.exec_path_env);
1274b6ab94eSJosh Poimboeuf if (env && *env)
1284b6ab94eSJosh Poimboeuf return strdup(env);
1294b6ab94eSJosh Poimboeuf
1304b6ab94eSJosh Poimboeuf return system_path(subcmd_config.exec_path);
1314b6ab94eSJosh Poimboeuf }
1324b6ab94eSJosh Poimboeuf
add_path(char ** out,const char * path)1334b6ab94eSJosh Poimboeuf static void add_path(char **out, const char *path)
1344b6ab94eSJosh Poimboeuf {
1354b6ab94eSJosh Poimboeuf if (path && *path) {
1364b6ab94eSJosh Poimboeuf if (is_absolute_path(path))
1374b6ab94eSJosh Poimboeuf astrcat(out, path);
138*20032376SIan Rogers else {
139*20032376SIan Rogers char buf[PATH_MAX];
140*20032376SIan Rogers
141*20032376SIan Rogers astrcat(out, make_nonrelative_path(buf, sizeof(buf), path));
142*20032376SIan Rogers }
1434b6ab94eSJosh Poimboeuf
1444b6ab94eSJosh Poimboeuf astrcat(out, ":");
1454b6ab94eSJosh Poimboeuf }
1464b6ab94eSJosh Poimboeuf }
1474b6ab94eSJosh Poimboeuf
setup_path(void)1484b6ab94eSJosh Poimboeuf void setup_path(void)
1494b6ab94eSJosh Poimboeuf {
1504b6ab94eSJosh Poimboeuf const char *old_path = getenv("PATH");
1514b6ab94eSJosh Poimboeuf char *new_path = NULL;
1524b6ab94eSJosh Poimboeuf char *tmp = get_argv_exec_path();
1534b6ab94eSJosh Poimboeuf
1544b6ab94eSJosh Poimboeuf add_path(&new_path, tmp);
1554b6ab94eSJosh Poimboeuf add_path(&new_path, argv0_path);
1564b6ab94eSJosh Poimboeuf free(tmp);
1574b6ab94eSJosh Poimboeuf
1584b6ab94eSJosh Poimboeuf if (old_path)
1594b6ab94eSJosh Poimboeuf astrcat(&new_path, old_path);
1604b6ab94eSJosh Poimboeuf else
1614b6ab94eSJosh Poimboeuf astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
1624b6ab94eSJosh Poimboeuf
1634b6ab94eSJosh Poimboeuf setenv("PATH", new_path, 1);
1644b6ab94eSJosh Poimboeuf
1654b6ab94eSJosh Poimboeuf free(new_path);
1664b6ab94eSJosh Poimboeuf }
1674b6ab94eSJosh Poimboeuf
prepare_exec_cmd(const char ** argv)1684b6ab94eSJosh Poimboeuf static const char **prepare_exec_cmd(const char **argv)
1694b6ab94eSJosh Poimboeuf {
1704b6ab94eSJosh Poimboeuf int argc;
1714b6ab94eSJosh Poimboeuf const char **nargv;
1724b6ab94eSJosh Poimboeuf
1734b6ab94eSJosh Poimboeuf for (argc = 0; argv[argc]; argc++)
1744b6ab94eSJosh Poimboeuf ; /* just counting */
1754b6ab94eSJosh Poimboeuf nargv = malloc(sizeof(*nargv) * (argc + 2));
1764b6ab94eSJosh Poimboeuf
1774b6ab94eSJosh Poimboeuf nargv[0] = subcmd_config.exec_name;
1784b6ab94eSJosh Poimboeuf for (argc = 0; argv[argc]; argc++)
1794b6ab94eSJosh Poimboeuf nargv[argc + 1] = argv[argc];
1804b6ab94eSJosh Poimboeuf nargv[argc + 1] = NULL;
1814b6ab94eSJosh Poimboeuf return nargv;
1824b6ab94eSJosh Poimboeuf }
1834b6ab94eSJosh Poimboeuf
execv_cmd(const char ** argv)1844b6ab94eSJosh Poimboeuf int execv_cmd(const char **argv) {
1854b6ab94eSJosh Poimboeuf const char **nargv = prepare_exec_cmd(argv);
1864b6ab94eSJosh Poimboeuf
1874b6ab94eSJosh Poimboeuf /* execvp() can only ever return if it fails */
1884b6ab94eSJosh Poimboeuf execvp(subcmd_config.exec_name, (char **)nargv);
1894b6ab94eSJosh Poimboeuf
1904b6ab94eSJosh Poimboeuf free(nargv);
1914b6ab94eSJosh Poimboeuf return -1;
1924b6ab94eSJosh Poimboeuf }
1934b6ab94eSJosh Poimboeuf
1944b6ab94eSJosh Poimboeuf
execl_cmd(const char * cmd,...)1954b6ab94eSJosh Poimboeuf int execl_cmd(const char *cmd,...)
1964b6ab94eSJosh Poimboeuf {
1974b6ab94eSJosh Poimboeuf int argc;
1984b6ab94eSJosh Poimboeuf const char *argv[MAX_ARGS + 1];
1994b6ab94eSJosh Poimboeuf const char *arg;
2004b6ab94eSJosh Poimboeuf va_list param;
2014b6ab94eSJosh Poimboeuf
2024b6ab94eSJosh Poimboeuf va_start(param, cmd);
2034b6ab94eSJosh Poimboeuf argv[0] = cmd;
2044b6ab94eSJosh Poimboeuf argc = 1;
2054b6ab94eSJosh Poimboeuf while (argc < MAX_ARGS) {
2064b6ab94eSJosh Poimboeuf arg = argv[argc++] = va_arg(param, char *);
2074b6ab94eSJosh Poimboeuf if (!arg)
2084b6ab94eSJosh Poimboeuf break;
2094b6ab94eSJosh Poimboeuf }
2104b6ab94eSJosh Poimboeuf va_end(param);
2114b6ab94eSJosh Poimboeuf if (MAX_ARGS <= argc) {
2124b6ab94eSJosh Poimboeuf fprintf(stderr, " Error: too many args to run %s\n", cmd);
2134b6ab94eSJosh Poimboeuf return -1;
2144b6ab94eSJosh Poimboeuf }
2154b6ab94eSJosh Poimboeuf
2164b6ab94eSJosh Poimboeuf argv[argc] = NULL;
2174b6ab94eSJosh Poimboeuf return execv_cmd(argv);
2184b6ab94eSJosh Poimboeuf }
219