1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/compiler.h> 3 #include <linux/string.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <unistd.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <stdio.h> 10 #include "subcmd-util.h" 11 #include "exec-cmd.h" 12 #include "subcmd-config.h" 13 14 #define MAX_ARGS 32 15 #define PATH_MAX 4096 16 17 static const char *argv_exec_path; 18 static const char *argv0_path; 19 20 void exec_cmd_init(const char *exec_name, const char *prefix, 21 const char *exec_path, const char *exec_path_env) 22 { 23 subcmd_config.exec_name = exec_name; 24 subcmd_config.prefix = prefix; 25 subcmd_config.exec_path = exec_path; 26 subcmd_config.exec_path_env = exec_path_env; 27 } 28 29 #define is_dir_sep(c) ((c) == '/') 30 31 static int is_absolute_path(const char *path) 32 { 33 return path[0] == '/'; 34 } 35 36 static const char *get_pwd_cwd(void) 37 { 38 static char cwd[PATH_MAX + 1]; 39 char *pwd; 40 struct stat cwd_stat, pwd_stat; 41 if (getcwd(cwd, PATH_MAX) == NULL) 42 return NULL; 43 pwd = getenv("PWD"); 44 if (pwd && strcmp(pwd, cwd)) { 45 stat(cwd, &cwd_stat); 46 if (!stat(pwd, &pwd_stat) && 47 pwd_stat.st_dev == cwd_stat.st_dev && 48 pwd_stat.st_ino == cwd_stat.st_ino) { 49 strlcpy(cwd, pwd, PATH_MAX); 50 } 51 } 52 return cwd; 53 } 54 55 static const char *make_nonrelative_path(const char *path) 56 { 57 static char buf[PATH_MAX + 1]; 58 59 if (is_absolute_path(path)) { 60 if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX) 61 die("Too long path: %.*s", 60, path); 62 } else { 63 const char *cwd = get_pwd_cwd(); 64 if (!cwd) 65 die("Cannot determine the current working directory"); 66 if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX) 67 die("Too long path: %.*s", 60, path); 68 } 69 return buf; 70 } 71 72 char *system_path(const char *path) 73 { 74 char *buf = NULL; 75 76 if (is_absolute_path(path)) 77 return strdup(path); 78 79 astrcatf(&buf, "%s/%s", subcmd_config.prefix, path); 80 81 return buf; 82 } 83 84 const char *extract_argv0_path(const char *argv0) 85 { 86 const char *slash; 87 88 if (!argv0 || !*argv0) 89 return NULL; 90 slash = argv0 + strlen(argv0); 91 92 while (argv0 <= slash && !is_dir_sep(*slash)) 93 slash--; 94 95 if (slash >= argv0) { 96 argv0_path = strndup(argv0, slash - argv0); 97 return argv0_path ? slash + 1 : NULL; 98 } 99 100 return argv0; 101 } 102 103 void set_argv_exec_path(const char *exec_path) 104 { 105 argv_exec_path = exec_path; 106 /* 107 * Propagate this setting to external programs. 108 */ 109 setenv(subcmd_config.exec_path_env, exec_path, 1); 110 } 111 112 113 /* Returns the highest-priority location to look for subprograms. */ 114 char *get_argv_exec_path(void) 115 { 116 char *env; 117 118 if (argv_exec_path) 119 return strdup(argv_exec_path); 120 121 env = getenv(subcmd_config.exec_path_env); 122 if (env && *env) 123 return strdup(env); 124 125 return system_path(subcmd_config.exec_path); 126 } 127 128 static void add_path(char **out, const char *path) 129 { 130 if (path && *path) { 131 if (is_absolute_path(path)) 132 astrcat(out, path); 133 else 134 astrcat(out, make_nonrelative_path(path)); 135 136 astrcat(out, ":"); 137 } 138 } 139 140 void setup_path(void) 141 { 142 const char *old_path = getenv("PATH"); 143 char *new_path = NULL; 144 char *tmp = get_argv_exec_path(); 145 146 add_path(&new_path, tmp); 147 add_path(&new_path, argv0_path); 148 free(tmp); 149 150 if (old_path) 151 astrcat(&new_path, old_path); 152 else 153 astrcat(&new_path, "/usr/local/bin:/usr/bin:/bin"); 154 155 setenv("PATH", new_path, 1); 156 157 free(new_path); 158 } 159 160 static const char **prepare_exec_cmd(const char **argv) 161 { 162 int argc; 163 const char **nargv; 164 165 for (argc = 0; argv[argc]; argc++) 166 ; /* just counting */ 167 nargv = malloc(sizeof(*nargv) * (argc + 2)); 168 169 nargv[0] = subcmd_config.exec_name; 170 for (argc = 0; argv[argc]; argc++) 171 nargv[argc + 1] = argv[argc]; 172 nargv[argc + 1] = NULL; 173 return nargv; 174 } 175 176 int execv_cmd(const char **argv) { 177 const char **nargv = prepare_exec_cmd(argv); 178 179 /* execvp() can only ever return if it fails */ 180 execvp(subcmd_config.exec_name, (char **)nargv); 181 182 free(nargv); 183 return -1; 184 } 185 186 187 int execl_cmd(const char *cmd,...) 188 { 189 int argc; 190 const char *argv[MAX_ARGS + 1]; 191 const char *arg; 192 va_list param; 193 194 va_start(param, cmd); 195 argv[0] = cmd; 196 argc = 1; 197 while (argc < MAX_ARGS) { 198 arg = argv[argc++] = va_arg(param, char *); 199 if (!arg) 200 break; 201 } 202 va_end(param); 203 if (MAX_ARGS <= argc) { 204 fprintf(stderr, " Error: too many args to run %s\n", cmd); 205 return -1; 206 } 207 208 argv[argc] = NULL; 209 return execv_cmd(argv); 210 } 211