18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/compiler.h>
38c2ecf20Sopenharmony_ci#include <linux/string.h>
48c2ecf20Sopenharmony_ci#include <sys/types.h>
58c2ecf20Sopenharmony_ci#include <sys/stat.h>
68c2ecf20Sopenharmony_ci#include <unistd.h>
78c2ecf20Sopenharmony_ci#include <string.h>
88c2ecf20Sopenharmony_ci#include <stdlib.h>
98c2ecf20Sopenharmony_ci#include <stdio.h>
108c2ecf20Sopenharmony_ci#include "subcmd-util.h"
118c2ecf20Sopenharmony_ci#include "exec-cmd.h"
128c2ecf20Sopenharmony_ci#include "subcmd-config.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#define MAX_ARGS	32
158c2ecf20Sopenharmony_ci#define PATH_MAX	4096
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic const char *argv_exec_path;
188c2ecf20Sopenharmony_cistatic const char *argv0_path;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_civoid exec_cmd_init(const char *exec_name, const char *prefix,
218c2ecf20Sopenharmony_ci		   const char *exec_path, const char *exec_path_env)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	subcmd_config.exec_name		= exec_name;
248c2ecf20Sopenharmony_ci	subcmd_config.prefix		= prefix;
258c2ecf20Sopenharmony_ci	subcmd_config.exec_path		= exec_path;
268c2ecf20Sopenharmony_ci	subcmd_config.exec_path_env	= exec_path_env;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define is_dir_sep(c) ((c) == '/')
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistatic int is_absolute_path(const char *path)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	return path[0] == '/';
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic const char *get_pwd_cwd(void)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	static char cwd[PATH_MAX + 1];
398c2ecf20Sopenharmony_ci	char *pwd;
408c2ecf20Sopenharmony_ci	struct stat cwd_stat, pwd_stat;
418c2ecf20Sopenharmony_ci	if (getcwd(cwd, PATH_MAX) == NULL)
428c2ecf20Sopenharmony_ci		return NULL;
438c2ecf20Sopenharmony_ci	pwd = getenv("PWD");
448c2ecf20Sopenharmony_ci	if (pwd && strcmp(pwd, cwd)) {
458c2ecf20Sopenharmony_ci		stat(cwd, &cwd_stat);
468c2ecf20Sopenharmony_ci		if (!stat(pwd, &pwd_stat) &&
478c2ecf20Sopenharmony_ci		    pwd_stat.st_dev == cwd_stat.st_dev &&
488c2ecf20Sopenharmony_ci		    pwd_stat.st_ino == cwd_stat.st_ino) {
498c2ecf20Sopenharmony_ci			strlcpy(cwd, pwd, PATH_MAX);
508c2ecf20Sopenharmony_ci		}
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci	return cwd;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic const char *make_nonrelative_path(const char *path)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	static char buf[PATH_MAX + 1];
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (is_absolute_path(path)) {
608c2ecf20Sopenharmony_ci		if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
618c2ecf20Sopenharmony_ci			die("Too long path: %.*s", 60, path);
628c2ecf20Sopenharmony_ci	} else {
638c2ecf20Sopenharmony_ci		const char *cwd = get_pwd_cwd();
648c2ecf20Sopenharmony_ci		if (!cwd)
658c2ecf20Sopenharmony_ci			die("Cannot determine the current working directory");
668c2ecf20Sopenharmony_ci		if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
678c2ecf20Sopenharmony_ci			die("Too long path: %.*s", 60, path);
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	return buf;
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_cichar *system_path(const char *path)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	char *buf = NULL;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	if (is_absolute_path(path))
778c2ecf20Sopenharmony_ci		return strdup(path);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	astrcatf(&buf, "%s/%s", subcmd_config.prefix, path);
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	return buf;
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciconst char *extract_argv0_path(const char *argv0)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	const char *slash;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (!argv0 || !*argv0)
898c2ecf20Sopenharmony_ci		return NULL;
908c2ecf20Sopenharmony_ci	slash = argv0 + strlen(argv0);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	while (argv0 <= slash && !is_dir_sep(*slash))
938c2ecf20Sopenharmony_ci		slash--;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (slash >= argv0) {
968c2ecf20Sopenharmony_ci		argv0_path = strndup(argv0, slash - argv0);
978c2ecf20Sopenharmony_ci		return argv0_path ? slash + 1 : NULL;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	return argv0;
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_civoid set_argv_exec_path(const char *exec_path)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	argv_exec_path = exec_path;
1068c2ecf20Sopenharmony_ci	/*
1078c2ecf20Sopenharmony_ci	 * Propagate this setting to external programs.
1088c2ecf20Sopenharmony_ci	 */
1098c2ecf20Sopenharmony_ci	setenv(subcmd_config.exec_path_env, exec_path, 1);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Returns the highest-priority location to look for subprograms. */
1148c2ecf20Sopenharmony_cichar *get_argv_exec_path(void)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	char *env;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (argv_exec_path)
1198c2ecf20Sopenharmony_ci		return strdup(argv_exec_path);
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	env = getenv(subcmd_config.exec_path_env);
1228c2ecf20Sopenharmony_ci	if (env && *env)
1238c2ecf20Sopenharmony_ci		return strdup(env);
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	return system_path(subcmd_config.exec_path);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void add_path(char **out, const char *path)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	if (path && *path) {
1318c2ecf20Sopenharmony_ci		if (is_absolute_path(path))
1328c2ecf20Sopenharmony_ci			astrcat(out, path);
1338c2ecf20Sopenharmony_ci		else
1348c2ecf20Sopenharmony_ci			astrcat(out, make_nonrelative_path(path));
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		astrcat(out, ":");
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_civoid setup_path(void)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	const char *old_path = getenv("PATH");
1438c2ecf20Sopenharmony_ci	char *new_path = NULL;
1448c2ecf20Sopenharmony_ci	char *tmp = get_argv_exec_path();
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	add_path(&new_path, tmp);
1478c2ecf20Sopenharmony_ci	add_path(&new_path, argv0_path);
1488c2ecf20Sopenharmony_ci	free(tmp);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (old_path)
1518c2ecf20Sopenharmony_ci		astrcat(&new_path, old_path);
1528c2ecf20Sopenharmony_ci	else
1538c2ecf20Sopenharmony_ci		astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin");
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	setenv("PATH", new_path, 1);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	free(new_path);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic const char **prepare_exec_cmd(const char **argv)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	int argc;
1638c2ecf20Sopenharmony_ci	const char **nargv;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	for (argc = 0; argv[argc]; argc++)
1668c2ecf20Sopenharmony_ci		; /* just counting */
1678c2ecf20Sopenharmony_ci	nargv = malloc(sizeof(*nargv) * (argc + 2));
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	nargv[0] = subcmd_config.exec_name;
1708c2ecf20Sopenharmony_ci	for (argc = 0; argv[argc]; argc++)
1718c2ecf20Sopenharmony_ci		nargv[argc + 1] = argv[argc];
1728c2ecf20Sopenharmony_ci	nargv[argc + 1] = NULL;
1738c2ecf20Sopenharmony_ci	return nargv;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ciint execv_cmd(const char **argv) {
1778c2ecf20Sopenharmony_ci	const char **nargv = prepare_exec_cmd(argv);
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* execvp() can only ever return if it fails */
1808c2ecf20Sopenharmony_ci	execvp(subcmd_config.exec_name, (char **)nargv);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	free(nargv);
1838c2ecf20Sopenharmony_ci	return -1;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ciint execl_cmd(const char *cmd,...)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	int argc;
1908c2ecf20Sopenharmony_ci	const char *argv[MAX_ARGS + 1];
1918c2ecf20Sopenharmony_ci	const char *arg;
1928c2ecf20Sopenharmony_ci	va_list param;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	va_start(param, cmd);
1958c2ecf20Sopenharmony_ci	argv[0] = cmd;
1968c2ecf20Sopenharmony_ci	argc = 1;
1978c2ecf20Sopenharmony_ci	while (argc < MAX_ARGS) {
1988c2ecf20Sopenharmony_ci		arg = argv[argc++] = va_arg(param, char *);
1998c2ecf20Sopenharmony_ci		if (!arg)
2008c2ecf20Sopenharmony_ci			break;
2018c2ecf20Sopenharmony_ci	}
2028c2ecf20Sopenharmony_ci	va_end(param);
2038c2ecf20Sopenharmony_ci	if (MAX_ARGS <= argc) {
2048c2ecf20Sopenharmony_ci		fprintf(stderr, " Error: too many args to run %s\n", cmd);
2058c2ecf20Sopenharmony_ci		return -1;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	argv[argc] = NULL;
2098c2ecf20Sopenharmony_ci	return execv_cmd(argv);
2108c2ecf20Sopenharmony_ci}
211