xref: /linux/tools/lib/subcmd/exec-cmd.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
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