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 /shell | |
| 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.
Diffstat (limited to 'shell')
| -rw-r--r-- | shell/ash.c | 66 |
1 files changed, 59 insertions, 7 deletions
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 |
