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