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