diff options
author | Ron Yorston <rmy@pobox.com> | 2024-08-19 08:06:19 +0100 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2024-08-19 08:06:19 +0100 |
commit | 4b7b4a960bab5b3e331e130b257fe8280fd9da43 (patch) | |
tree | 78aa6a26f38c4a279ab7310992e2259be3f5ece5 | |
parent | b21899038683bd646446d3db9e84f64ea669d2ed (diff) | |
download | busybox-w32-4b7b4a960bab5b3e331e130b257fe8280fd9da43.tar.gz busybox-w32-4b7b4a960bab5b3e331e130b257fe8280fd9da43.tar.bz2 busybox-w32-4b7b4a960bab5b3e331e130b257fe8280fd9da43.zip |
ash: optimise running of scripts
The BusyBox shell detects certain cases where forking a command is
unnecessary (last command in a script or subshell, for example) and
calls execve(2) instead. This doesn't help in the Windows port
because execve(2) is implemented by creating a process.
There is one case where it is possible to apply this optimisation:
if the command is a script and the script interpreter is an applet.
- Have evalcommand() pass a flag to indicate this situation to
shellexec(). Also, allocate two spare elements before the start
of the argv array.
- If the flag is TRUE shellexec() passes the shell's PATH variable
down to tryexec() so it can perform a test for applet override.
- If tryexec() finds that all the necessary conditions apply it
can run a script by directly invoking the interpreter's main().
Adds 192-224 bytes.
-rw-r--r-- | include/mingw.h | 8 | ||||
-rw-r--r-- | shell/ash.c | 66 | ||||
-rw-r--r-- | win32/process.c | 9 |
3 files changed, 68 insertions, 15 deletions
diff --git a/include/mingw.h b/include/mingw.h index adb810ec5..ed7884e39 100644 --- a/include/mingw.h +++ b/include/mingw.h | |||
@@ -566,6 +566,14 @@ int utimes(const char *file_name, const struct timeval times[2]); | |||
566 | #define is_unc_path(x) (strlen(x) > 4 && is_dir_sep(x[0]) && \ | 566 | #define is_unc_path(x) (strlen(x) > 4 && is_dir_sep(x[0]) && \ |
567 | is_dir_sep(x[1]) && !is_dir_sep(x[2])) | 567 | is_dir_sep(x[1]) && !is_dir_sep(x[2])) |
568 | 568 | ||
569 | typedef struct { | ||
570 | char *path; | ||
571 | char *name; | ||
572 | char *opts; | ||
573 | char buf[100]; | ||
574 | } interp_t; | ||
575 | |||
576 | int FAST_FUNC parse_interpreter(const char *cmd, interp_t *interp); | ||
569 | char ** FAST_FUNC grow_argv(char **argv, int n); | 577 | char ** FAST_FUNC grow_argv(char **argv, int n); |
570 | pid_t FAST_FUNC mingw_spawn(char **argv); | 578 | pid_t FAST_FUNC mingw_spawn(char **argv); |
571 | intptr_t FAST_FUNC mingw_spawn_detach(char **argv); | 579 | intptr_t FAST_FUNC mingw_spawn_detach(char **argv); |
diff --git a/shell/ash.c b/shell/ash.c index aa291b99d..2e4181b3f 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -9091,8 +9091,16 @@ static int builtinloc = -1; /* index in path of %builtin, or -1 */ | |||
9091 | 9091 | ||
9092 | 9092 | ||
9093 | static void | 9093 | static void |
9094 | #if ENABLE_PLATFORM_MINGW32 | ||
9095 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *nfpath,) | ||
9096 | const char *cmd, char **argv, char **envp) | ||
9097 | #else | ||
9094 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) | 9098 | tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) |
9099 | #endif | ||
9095 | { | 9100 | { |
9101 | #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE | ||
9102 | interp_t interp; | ||
9103 | #endif | ||
9096 | #if ENABLE_FEATURE_SH_STANDALONE | 9104 | #if ENABLE_FEATURE_SH_STANDALONE |
9097 | if (applet_no >= 0) { | 9105 | if (applet_no >= 0) { |
9098 | # if ENABLE_PLATFORM_MINGW32 | 9106 | # if ENABLE_PLATFORM_MINGW32 |
@@ -9101,6 +9109,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
9101 | struct forkshell *fs = (struct forkshell *)sticky_mem_start; | 9109 | struct forkshell *fs = (struct forkshell *)sticky_mem_start; |
9102 | if (applet_main[applet_no] != ash_main || | 9110 | if (applet_main[applet_no] != ash_main || |
9103 | (fs && fs->fpid == FS_SHELLEXEC)) { | 9111 | (fs && fs->fpid == FS_SHELLEXEC)) { |
9112 | run_noexec: | ||
9104 | /* mingw-w64's getopt() uses __argv[0] as the program name */ | 9113 | /* mingw-w64's getopt() uses __argv[0] as the program name */ |
9105 | __argv[0] = (char *)cmd; | 9114 | __argv[0] = (char *)cmd; |
9106 | /* 'which' wants to know if it was invoked from a standalone | 9115 | /* 'which' wants to know if it was invoked from a standalone |
@@ -9141,6 +9150,28 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
9141 | 9150 | ||
9142 | /* cmd was allocated on the stack with room for an extension */ | 9151 | /* cmd was allocated on the stack with room for an extension */ |
9143 | add_win32_extension((char *)cmd); | 9152 | add_win32_extension((char *)cmd); |
9153 | |||
9154 | # if ENABLE_FEATURE_SH_STANDALONE | ||
9155 | /* If nfpath is non-NULL, evalcommand() has determined this | ||
9156 | * command doesn't need a fork(). If the command is a script | ||
9157 | * with an interpreter which is an applet we can run it as if | ||
9158 | * it were a noexec applet. */ | ||
9159 | if (nfpath && parse_interpreter(cmd, &interp)) { | ||
9160 | applet_no = find_applet_by_name_for_sh(interp.name, nfpath); | ||
9161 | if (applet_no >= 0) { | ||
9162 | argv[0] = (char *)cmd; | ||
9163 | /* evalcommand() has added two elements before argv */ | ||
9164 | if (interp.opts) { | ||
9165 | argv--; | ||
9166 | argv[0] = (char *)interp.opts; | ||
9167 | } | ||
9168 | argv--; | ||
9169 | cmd = argv[0] = (char *)interp.name; | ||
9170 | goto run_noexec; | ||
9171 | } | ||
9172 | } | ||
9173 | # endif | ||
9174 | |||
9144 | execve(cmd, argv, envp); | 9175 | execve(cmd, argv, envp); |
9145 | /* skip POSIX-mandated retry on ENOEXEC */ | 9176 | /* skip POSIX-mandated retry on ENOEXEC */ |
9146 | #else /* !ENABLE_PLATFORM_MINGW32 */ | 9177 | #else /* !ENABLE_PLATFORM_MINGW32 */ |
@@ -9182,20 +9213,29 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c | |||
9182 | #endif /* !ENABLE_PLATFORM_MINGW32 */ | 9213 | #endif /* !ENABLE_PLATFORM_MINGW32 */ |
9183 | } | 9214 | } |
9184 | 9215 | ||
9216 | #if !ENABLE_PLATFORM_MINGW32 || !ENABLE_FEATURE_SH_STANDALONE | ||
9217 | # define shellexec(prg, a, pth, i, n) shellexec(prg, a, pth, i) | ||
9218 | #endif | ||
9219 | |||
9185 | /* | 9220 | /* |
9186 | * Exec a program. Never returns. If you change this routine, you may | 9221 | * Exec a program. Never returns. If you change this routine, you may |
9187 | * have to change the find_command routine as well. | 9222 | * have to change the find_command routine as well. |
9188 | * argv[-1] must exist and be writable! See tryexec() for why. | 9223 | * argv[-1] must exist and be writable! See tryexec() for why. |
9189 | */ | 9224 | */ |
9190 | static struct builtincmd *find_builtin(const char *name); | 9225 | static struct builtincmd *find_builtin(const char *name); |
9191 | static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; | 9226 | static void shellexec(char *prog, char **argv, const char *path, int idx, |
9192 | static void shellexec(char *prog, char **argv, const char *path, int idx) | 9227 | int nofork) NORETURN; |
9228 | static void shellexec(char *prog, char **argv, const char *path, int idx, | ||
9229 | int nofork) | ||
9193 | { | 9230 | { |
9194 | char *cmdname; | 9231 | char *cmdname; |
9195 | int e; | 9232 | int e; |
9196 | char **envp; | 9233 | char **envp; |
9197 | int exerrno; | 9234 | int exerrno; |
9198 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ | 9235 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ |
9236 | #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE | ||
9237 | const char *nfpath = nofork ? path : NULL; | ||
9238 | #endif | ||
9199 | 9239 | ||
9200 | envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); | 9240 | envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); |
9201 | #if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT) | 9241 | #if ENABLE_FEATURE_SH_STANDALONE && ENABLE_PLATFORM_MINGW32 && defined(_UCRT) |
@@ -9217,7 +9257,8 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
9217 | ) { | 9257 | ) { |
9218 | #if ENABLE_PLATFORM_MINGW32 | 9258 | #if ENABLE_PLATFORM_MINGW32 |
9219 | char *progext = stack_add_ext_space(prog); | 9259 | char *progext = stack_add_ext_space(prog); |
9220 | tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) progext, argv, envp); | 9260 | tryexec(IF_FEATURE_SH_STANDALONE(applet_no, nfpath,) |
9261 | progext, argv, envp); | ||
9221 | #else | 9262 | #else |
9222 | tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); | 9263 | tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); |
9223 | #endif | 9264 | #endif |
@@ -9234,7 +9275,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
9234 | const char *name = bb_basename(prog); | 9275 | const char *name = bb_basename(prog); |
9235 | # if ENABLE_FEATURE_SH_STANDALONE | 9276 | # if ENABLE_FEATURE_SH_STANDALONE |
9236 | if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) { | 9277 | if ((applet_no = find_applet_by_name_for_sh(name, path)) >= 0) { |
9237 | tryexec(applet_no, name, argv, envp); | 9278 | tryexec(applet_no, nfpath, name, argv, envp); |
9238 | e = errno; | 9279 | e = errno; |
9239 | } | 9280 | } |
9240 | # endif | 9281 | # endif |
@@ -9250,7 +9291,12 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
9250 | while (padvance(&path, argv[0]) >= 0) { | 9291 | while (padvance(&path, argv[0]) >= 0) { |
9251 | cmdname = stackblock(); | 9292 | cmdname = stackblock(); |
9252 | if (--idx < 0 && pathopt == NULL) { | 9293 | if (--idx < 0 && pathopt == NULL) { |
9294 | #if ENABLE_PLATFORM_MINGW32 | ||
9295 | tryexec(IF_FEATURE_SH_STANDALONE(-1, nfpath,) | ||
9296 | cmdname, argv, envp); | ||
9297 | #else | ||
9253 | tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); | 9298 | tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp); |
9299 | #endif | ||
9254 | if (errno != ENOENT && errno != ENOTDIR) | 9300 | if (errno != ENOENT && errno != ENOTDIR) |
9255 | e = errno; | 9301 | e = errno; |
9256 | } | 9302 | } |
@@ -11223,7 +11269,7 @@ execcmd(int argc UNUSED_PARAM, char **argv) | |||
11223 | prog = argv[0]; | 11269 | prog = argv[0]; |
11224 | if (optionarg) | 11270 | if (optionarg) |
11225 | argv[0] = optionarg; | 11271 | argv[0] = optionarg; |
11226 | shellexec(prog, argv, pathval(), 0); | 11272 | shellexec(prog, argv, pathval(), 0, FALSE); |
11227 | /* NOTREACHED */ | 11273 | /* NOTREACHED */ |
11228 | } | 11274 | } |
11229 | return 0; | 11275 | return 0; |
@@ -11581,7 +11627,13 @@ evalcommand(union node *cmd, int flags) | |||
11581 | localvar_stop = pushlocalvars(vlocal); | 11627 | localvar_stop = pushlocalvars(vlocal); |
11582 | 11628 | ||
11583 | #if ENABLE_PLATFORM_MINGW32 | 11629 | #if ENABLE_PLATFORM_MINGW32 |
11630 | # if ENABLE_FEATURE_SH_STANDALONE | ||
11631 | /* Reserve two extra spots at the front for shellexec. */ | ||
11632 | nargv = stalloc(sizeof(char *) * (argc + 3)); | ||
11633 | argv = nargv = nargv + 2; | ||
11634 | # else | ||
11584 | argv = nargv = stalloc(sizeof(char *) * (argc + 1)); | 11635 | argv = nargv = stalloc(sizeof(char *) * (argc + 1)); |
11636 | # endif | ||
11585 | #else | 11637 | #else |
11586 | /* Reserve one extra spot at the front for shellexec. */ | 11638 | /* Reserve one extra spot at the front for shellexec. */ |
11587 | nargv = stalloc(sizeof(char *) * (argc + 2)); | 11639 | nargv = stalloc(sizeof(char *) * (argc + 2)); |
@@ -11773,7 +11825,7 @@ evalcommand(union node *cmd, int flags) | |||
11773 | /* fall through to exec'ing external program */ | 11825 | /* fall through to exec'ing external program */ |
11774 | } | 11826 | } |
11775 | #endif | 11827 | #endif |
11776 | shellexec(argv[0], argv, path, cmdentry.u.index); | 11828 | shellexec(argv[0], argv, path, cmdentry.u.index, TRUE); |
11777 | /* NOTREACHED */ | 11829 | /* NOTREACHED */ |
11778 | } /* default */ | 11830 | } /* default */ |
11779 | case CMDBUILTIN: | 11831 | case CMDBUILTIN: |
@@ -16492,7 +16544,7 @@ forkshell_shellexec(struct forkshell *fs) | |||
16492 | char *path = fs->path; | 16544 | char *path = fs->path; |
16493 | 16545 | ||
16494 | FORCE_INT_ON; | 16546 | FORCE_INT_ON; |
16495 | shellexec(argv[0], argv, path, idx); | 16547 | shellexec(argv[0], argv, path, idx, FALSE); |
16496 | } | 16548 | } |
16497 | 16549 | ||
16498 | static void | 16550 | static void |
diff --git a/win32/process.c b/win32/process.c index 097a1d71c..e3ce95ca0 100644 --- a/win32/process.c +++ b/win32/process.c | |||
@@ -53,14 +53,7 @@ pid_t mingw_wait3(pid_t pid, int *status, int options, struct rusage *rusage) | |||
53 | return -1; | 53 | return -1; |
54 | } | 54 | } |
55 | 55 | ||
56 | typedef struct { | 56 | int FAST_FUNC |
57 | char *path; | ||
58 | char *name; | ||
59 | char *opts; | ||
60 | char buf[100]; | ||
61 | } interp_t; | ||
62 | |||
63 | static int | ||
64 | parse_interpreter(const char *cmd, interp_t *interp) | 57 | parse_interpreter(const char *cmd, interp_t *interp) |
65 | { | 58 | { |
66 | char *path, *t; | 59 | char *path, *t; |