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