diff options
author | Ron Yorston <rmy@pobox.com> | 2012-04-17 11:47:16 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2012-04-17 11:47:16 +0100 |
commit | 7209ab16b8ef7ac9d7a298434aaf456d1cc6a982 (patch) | |
tree | cb0ca0df2bb4d71ff4096eea5c7c8fa8ad2d66e5 | |
parent | 3f092d0d50a8212dbc3af096f952c34cba128302 (diff) | |
download | busybox-w32-7209ab16b8ef7ac9d7a298434aaf456d1cc6a982.tar.gz busybox-w32-7209ab16b8ef7ac9d7a298434aaf456d1cc6a982.tar.bz2 busybox-w32-7209ab16b8ef7ac9d7a298434aaf456d1cc6a982.zip |
Detect and execute shell scripts based on presence of '#!'
-rw-r--r-- | libbb/execable.c | 30 | ||||
-rw-r--r-- | shell/ash.c | 15 | ||||
-rw-r--r-- | win32/process.c | 74 |
3 files changed, 91 insertions, 28 deletions
diff --git a/libbb/execable.c b/libbb/execable.c index e35dad208..afb957e43 100644 --- a/libbb/execable.c +++ b/libbb/execable.c | |||
@@ -16,15 +16,31 @@ | |||
16 | int FAST_FUNC execable_file(const char *name) | 16 | int FAST_FUNC execable_file(const char *name) |
17 | { | 17 | { |
18 | struct stat s; | 18 | struct stat s; |
19 | if (ENABLE_PLATFORM_MINGW32) { | 19 | #if ENABLE_PLATFORM_MINGW32 |
20 | int len = strlen(name); | 20 | if (!stat(name, &s) && S_ISREG(s.st_mode)) { |
21 | return len > 4 && | 21 | int len, fd, n; |
22 | char buf[100]; | ||
23 | |||
24 | if ((len=strlen(name)) > 4 && | ||
22 | (!strcasecmp(name+len-4, ".exe") || | 25 | (!strcasecmp(name+len-4, ".exe") || |
23 | !strcasecmp(name+len-4, ".com")) && | 26 | !strcasecmp(name+len-4, ".com"))) { |
24 | !stat(name, &s) && | 27 | return 1; |
25 | S_ISREG(s.st_mode); | 28 | } |
29 | |||
30 | fd = open(name, O_RDONLY); | ||
31 | if (fd < 0) | ||
32 | return 0; | ||
33 | n = read(fd, buf, sizeof(buf)-1); | ||
34 | close(fd); | ||
35 | if (n < 4) /* at least '#!/x' and not error */ | ||
36 | return 0; | ||
37 | |||
38 | return (buf[0] == '#' && buf[1] == '!'); | ||
26 | } | 39 | } |
40 | return 0; | ||
41 | #else | ||
27 | return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); | 42 | return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); |
43 | #endif | ||
28 | } | 44 | } |
29 | 45 | ||
30 | /* search (*PATHp) for an executable file; | 46 | /* search (*PATHp) for an executable file; |
@@ -67,11 +83,13 @@ char* FAST_FUNC find_execable(const char *filename, char **PATHp) | |||
67 | memcpy(np+len, ".exe", 5); | 83 | memcpy(np+len, ".exe", 5); |
68 | if (execable_file(np)) { | 84 | if (execable_file(np)) { |
69 | *PATHp = n; | 85 | *PATHp = n; |
86 | free(p); | ||
70 | return np; | 87 | return np; |
71 | } | 88 | } |
72 | memcpy(np+len, ".com", 5); | 89 | memcpy(np+len, ".com", 5); |
73 | if (execable_file(np)) { | 90 | if (execable_file(np)) { |
74 | *PATHp = n; | 91 | *PATHp = n; |
92 | free(p); | ||
75 | return np; | 93 | return np; |
76 | } | 94 | } |
77 | } | 95 | } |
diff --git a/shell/ash.c b/shell/ash.c index a809bf181..54c299d7e 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -21,7 +21,9 @@ | |||
21 | * | 21 | * |
22 | * - Environment variables from Windows will all be turned to uppercase. | 22 | * - Environment variables from Windows will all be turned to uppercase. |
23 | * - PATH accepts both ; and : as separator, but can't be mixed | 23 | * - PATH accepts both ; and : as separator, but can't be mixed |
24 | * - command without ".exe" is still understood as executable (option to turn off?) | 24 | * - command without ".exe" extension is still understood as executable |
25 | * - shell scripts on the path are detected by the presence of '#!'; | ||
26 | * the path to the interpreter is ignored, PATH is searched to find it | ||
25 | * - both / and \ are supported in PATH. Usually you must use / | 27 | * - both / and \ are supported in PATH. Usually you must use / |
26 | * - trap/job does not work | 28 | * - trap/job does not work |
27 | * - /dev/null is supported for redirection | 29 | * - /dev/null is supported for redirection |
@@ -12947,7 +12949,16 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
12947 | if (stat(fullname, &statb) < 0) { | 12949 | if (stat(fullname, &statb) < 0) { |
12948 | if (errno != ENOENT && errno != ENOTDIR) | 12950 | if (errno != ENOENT && errno != ENOTDIR) |
12949 | e = errno; | 12951 | e = errno; |
12950 | goto loop; | 12952 | fullname[len] = '\0'; |
12953 | if (stat(fullname, &statb) < 0) { | ||
12954 | if (errno != ENOENT && errno != ENOTDIR) | ||
12955 | e = errno; | ||
12956 | goto loop; | ||
12957 | } | ||
12958 | if (!execable_file(fullname)) { | ||
12959 | e = ENOEXEC; | ||
12960 | goto loop; | ||
12961 | } | ||
12951 | } | 12962 | } |
12952 | } | 12963 | } |
12953 | fullname[len] = '\0'; | 12964 | fullname[len] = '\0'; |
diff --git a/win32/process.c b/win32/process.c index 3ce152c45..2cfec4fdf 100644 --- a/win32/process.c +++ b/win32/process.c | |||
@@ -30,13 +30,18 @@ next_path_sep(const char *path) | |||
30 | return strchr(has_dos_drive_prefix(path) ? path+2 : path, ':'); | 30 | return strchr(has_dos_drive_prefix(path) ? path+2 : path, ':'); |
31 | } | 31 | } |
32 | 32 | ||
33 | #define MAX_OPT 10 | ||
34 | |||
33 | static const char * | 35 | static const char * |
34 | parse_interpreter(const char *cmd) | 36 | parse_interpreter(const char *cmd, char ***opts, int *nopts) |
35 | { | 37 | { |
36 | static char buf[100]; | 38 | static char buf[100]; |
37 | char *p, *opt; | 39 | char *p, *s, *t, *opt[MAX_OPT]; |
38 | int n, fd; | 40 | int n, fd; |
39 | 41 | ||
42 | *nopts = 0; | ||
43 | *opts = opt; | ||
44 | |||
40 | /* don't even try a .exe */ | 45 | /* don't even try a .exe */ |
41 | n = strlen(cmd); | 46 | n = strlen(cmd); |
42 | if (n >= 4 && | 47 | if (n >= 4 && |
@@ -58,13 +63,34 @@ parse_interpreter(const char *cmd) | |||
58 | p = strchr(buf, '\n'); | 63 | p = strchr(buf, '\n'); |
59 | if (!p) | 64 | if (!p) |
60 | return NULL; | 65 | return NULL; |
61 | |||
62 | *p = '\0'; | 66 | *p = '\0'; |
67 | |||
68 | /* remove trailing whitespace */ | ||
69 | while ( isspace(*--p) ) { | ||
70 | *p = '\0'; | ||
71 | } | ||
72 | |||
63 | if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\'))) | 73 | if (!(p = strrchr(buf+2, '/')) && !(p = strrchr(buf+2, '\\'))) |
64 | return NULL; | 74 | return NULL; |
65 | /* strip options */ | 75 | |
66 | if ((opt = strchr(p+1, ' '))) | 76 | /* move to end of interpreter name */ |
67 | *opt = '\0'; | 77 | for ( s=p; *s && !isspace(*s); ++s ) { |
78 | } | ||
79 | |||
80 | n = 0; | ||
81 | if ( *s != '\0' ) { | ||
82 | /* there are options */ | ||
83 | *s++ = '\0'; | ||
84 | |||
85 | while ( (t=strtok(s, " \t")) && n < MAX_OPT ) { | ||
86 | s = NULL; | ||
87 | opt[n++] = t; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | *nopts = n; | ||
92 | *opts = opt; | ||
93 | |||
68 | return p+1; | 94 | return p+1; |
69 | } | 95 | } |
70 | 96 | ||
@@ -179,35 +205,43 @@ static pid_t | |||
179 | mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, const char *const *envp) | 205 | mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, const char *const *envp) |
180 | { | 206 | { |
181 | int ret; | 207 | int ret; |
182 | const char *interpr = parse_interpreter(prog); | 208 | char **opts; |
209 | int nopts; | ||
210 | const char *interpr = parse_interpreter(prog, &opts, &nopts); | ||
211 | const char **new_argv; | ||
212 | int argc = 0; | ||
183 | 213 | ||
184 | if (!interpr) | 214 | if (!interpr) |
185 | return spawnveq(mode, prog, argv, envp); | 215 | return spawnveq(mode, prog, argv, envp); |
186 | 216 | ||
187 | if (ENABLE_FEATURE_PREFER_APPLETS && !strcmp(interpr, "sh")) { | 217 | |
188 | const char **new_argv; | 218 | while (argv[argc]) |
189 | int argc = 0; | 219 | argc++; |
190 | 220 | new_argv = malloc(sizeof(*argv)*(argc+nopts+2)); | |
191 | while (argv[argc]) | 221 | memcpy(new_argv+1, opts, sizeof(*opts)*nopts); |
192 | argc++; | 222 | memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); |
193 | new_argv = malloc(sizeof(*argv)*(argc+2)); | 223 | new_argv[nopts+1] = prog; /* pass absolute path */ |
194 | memcpy(new_argv+1, argv, sizeof(*argv)*(argc+1)); | 224 | |
195 | new_argv[0] = prog; /* pass absolute path */ | 225 | if (ENABLE_FEATURE_PREFER_APPLETS && find_applet_by_name(interpr) >= 0) { |
196 | ret = mingw_spawn_applet(mode, "sh", new_argv, envp); | 226 | new_argv[0] = interpr; |
197 | free(new_argv); | 227 | ret = mingw_spawn_applet(mode, interpr, new_argv, envp); |
198 | } | 228 | } |
199 | else { | 229 | else { |
200 | char *path = xstrdup(getenv("PATH")); | 230 | char *path = xstrdup(getenv("PATH")); |
201 | char *tmp = path; | 231 | char *tmp = path; |
202 | char *iprog = find_execable(interpr, &tmp); | 232 | char *iprog = find_execable(interpr, &tmp); |
203 | free(path); | 233 | free(path); |
204 | if (!prog) { | 234 | if (!iprog) { |
235 | free(new_argv); | ||
205 | errno = ENOENT; | 236 | errno = ENOENT; |
206 | return -1; | 237 | return -1; |
207 | } | 238 | } |
208 | ret = spawnveq(mode, iprog, argv, envp); | 239 | new_argv[0] = iprog; |
240 | ret = spawnveq(mode, iprog, new_argv, envp); | ||
209 | free(iprog); | 241 | free(iprog); |
210 | } | 242 | } |
243 | |||
244 | free(new_argv); | ||
211 | return ret; | 245 | return ret; |
212 | } | 246 | } |
213 | 247 | ||