162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/compiler.h> 362306a36Sopenharmony_ci#include <linux/string.h> 462306a36Sopenharmony_ci#include <sys/types.h> 562306a36Sopenharmony_ci#include <sys/stat.h> 662306a36Sopenharmony_ci#include <unistd.h> 762306a36Sopenharmony_ci#include <string.h> 862306a36Sopenharmony_ci#include <stdlib.h> 962306a36Sopenharmony_ci#include <stdio.h> 1062306a36Sopenharmony_ci#include "subcmd-util.h" 1162306a36Sopenharmony_ci#include "exec-cmd.h" 1262306a36Sopenharmony_ci#include "subcmd-config.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define MAX_ARGS 32 1562306a36Sopenharmony_ci#define PATH_MAX 4096 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_cistatic const char *argv_exec_path; 1862306a36Sopenharmony_cistatic const char *argv0_path; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_civoid exec_cmd_init(const char *exec_name, const char *prefix, 2162306a36Sopenharmony_ci const char *exec_path, const char *exec_path_env) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci subcmd_config.exec_name = exec_name; 2462306a36Sopenharmony_ci subcmd_config.prefix = prefix; 2562306a36Sopenharmony_ci subcmd_config.exec_path = exec_path; 2662306a36Sopenharmony_ci subcmd_config.exec_path_env = exec_path_env; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci /* Setup environment variable for invoked shell script. */ 2962306a36Sopenharmony_ci setenv("PREFIX", prefix, 1); 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define is_dir_sep(c) ((c) == '/') 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic int is_absolute_path(const char *path) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci return path[0] == '/'; 3762306a36Sopenharmony_ci} 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic const char *get_pwd_cwd(char *buf, size_t sz) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci char *pwd; 4262306a36Sopenharmony_ci struct stat cwd_stat, pwd_stat; 4362306a36Sopenharmony_ci if (getcwd(buf, sz) == NULL) 4462306a36Sopenharmony_ci return NULL; 4562306a36Sopenharmony_ci pwd = getenv("PWD"); 4662306a36Sopenharmony_ci if (pwd && strcmp(pwd, buf)) { 4762306a36Sopenharmony_ci stat(buf, &cwd_stat); 4862306a36Sopenharmony_ci if (!stat(pwd, &pwd_stat) && 4962306a36Sopenharmony_ci pwd_stat.st_dev == cwd_stat.st_dev && 5062306a36Sopenharmony_ci pwd_stat.st_ino == cwd_stat.st_ino) { 5162306a36Sopenharmony_ci strlcpy(buf, pwd, sz); 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci return buf; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic const char *make_nonrelative_path(char *buf, size_t sz, const char *path) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci if (is_absolute_path(path)) { 6062306a36Sopenharmony_ci if (strlcpy(buf, path, sz) >= sz) 6162306a36Sopenharmony_ci die("Too long path: %.*s", 60, path); 6262306a36Sopenharmony_ci } else { 6362306a36Sopenharmony_ci const char *cwd = get_pwd_cwd(buf, sz); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!cwd) 6662306a36Sopenharmony_ci die("Cannot determine the current working directory"); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci if (strlen(cwd) + strlen(path) + 2 >= sz) 6962306a36Sopenharmony_ci die("Too long path: %.*s", 60, path); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci strcat(buf, "/"); 7262306a36Sopenharmony_ci strcat(buf, path); 7362306a36Sopenharmony_ci } 7462306a36Sopenharmony_ci return buf; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cichar *system_path(const char *path) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci char *buf = NULL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (is_absolute_path(path)) 8262306a36Sopenharmony_ci return strdup(path); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci return buf; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciconst char *extract_argv0_path(const char *argv0) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci const char *slash; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!argv0 || !*argv0) 9462306a36Sopenharmony_ci return NULL; 9562306a36Sopenharmony_ci slash = argv0 + strlen(argv0); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci while (argv0 <= slash && !is_dir_sep(*slash)) 9862306a36Sopenharmony_ci slash--; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (slash >= argv0) { 10162306a36Sopenharmony_ci argv0_path = strndup(argv0, slash - argv0); 10262306a36Sopenharmony_ci return argv0_path ? slash + 1 : NULL; 10362306a36Sopenharmony_ci } 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci return argv0; 10662306a36Sopenharmony_ci} 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_civoid set_argv_exec_path(const char *exec_path) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci argv_exec_path = exec_path; 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * Propagate this setting to external programs. 11362306a36Sopenharmony_ci */ 11462306a36Sopenharmony_ci setenv(subcmd_config.exec_path_env, exec_path, 1); 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* Returns the highest-priority location to look for subprograms. */ 11962306a36Sopenharmony_cichar *get_argv_exec_path(void) 12062306a36Sopenharmony_ci{ 12162306a36Sopenharmony_ci char *env; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci if (argv_exec_path) 12462306a36Sopenharmony_ci return strdup(argv_exec_path); 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci env = getenv(subcmd_config.exec_path_env); 12762306a36Sopenharmony_ci if (env && *env) 12862306a36Sopenharmony_ci return strdup(env); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return system_path(subcmd_config.exec_path); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void add_path(char **out, const char *path) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci if (path && *path) { 13662306a36Sopenharmony_ci if (is_absolute_path(path)) 13762306a36Sopenharmony_ci astrcat(out, path); 13862306a36Sopenharmony_ci else { 13962306a36Sopenharmony_ci char buf[PATH_MAX]; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci astrcat(out, make_nonrelative_path(buf, sizeof(buf), path)); 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci astrcat(out, ":"); 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_civoid setup_path(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci const char *old_path = getenv("PATH"); 15162306a36Sopenharmony_ci char *new_path = NULL; 15262306a36Sopenharmony_ci char *tmp = get_argv_exec_path(); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci add_path(&new_path, tmp); 15562306a36Sopenharmony_ci add_path(&new_path, argv0_path); 15662306a36Sopenharmony_ci free(tmp); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (old_path) 15962306a36Sopenharmony_ci astrcat(&new_path, old_path); 16062306a36Sopenharmony_ci else 16162306a36Sopenharmony_ci astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci setenv("PATH", new_path, 1); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci free(new_path); 16662306a36Sopenharmony_ci} 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_cistatic const char **prepare_exec_cmd(const char **argv) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci int argc; 17162306a36Sopenharmony_ci const char **nargv; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci for (argc = 0; argv[argc]; argc++) 17462306a36Sopenharmony_ci ; /* just counting */ 17562306a36Sopenharmony_ci nargv = malloc(sizeof(*nargv) * (argc + 2)); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci nargv[0] = subcmd_config.exec_name; 17862306a36Sopenharmony_ci for (argc = 0; argv[argc]; argc++) 17962306a36Sopenharmony_ci nargv[argc + 1] = argv[argc]; 18062306a36Sopenharmony_ci nargv[argc + 1] = NULL; 18162306a36Sopenharmony_ci return nargv; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ciint execv_cmd(const char **argv) { 18562306a36Sopenharmony_ci const char **nargv = prepare_exec_cmd(argv); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci /* execvp() can only ever return if it fails */ 18862306a36Sopenharmony_ci execvp(subcmd_config.exec_name, (char **)nargv); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci free(nargv); 19162306a36Sopenharmony_ci return -1; 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ciint execl_cmd(const char *cmd,...) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci int argc; 19862306a36Sopenharmony_ci const char *argv[MAX_ARGS + 1]; 19962306a36Sopenharmony_ci const char *arg; 20062306a36Sopenharmony_ci va_list param; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci va_start(param, cmd); 20362306a36Sopenharmony_ci argv[0] = cmd; 20462306a36Sopenharmony_ci argc = 1; 20562306a36Sopenharmony_ci while (argc < MAX_ARGS) { 20662306a36Sopenharmony_ci arg = argv[argc++] = va_arg(param, char *); 20762306a36Sopenharmony_ci if (!arg) 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci va_end(param); 21162306a36Sopenharmony_ci if (MAX_ARGS <= argc) { 21262306a36Sopenharmony_ci fprintf(stderr, " Error: too many args to run %s\n", cmd); 21362306a36Sopenharmony_ci return -1; 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci argv[argc] = NULL; 21762306a36Sopenharmony_ci return execv_cmd(argv); 21862306a36Sopenharmony_ci} 219