aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2024-08-19 08:06:19 +0100
committerRon Yorston <rmy@pobox.com>2024-08-19 08:06:19 +0100
commit4b7b4a960bab5b3e331e130b257fe8280fd9da43 (patch)
tree78aa6a26f38c4a279ab7310992e2259be3f5ece5
parentb21899038683bd646446d3db9e84f64ea669d2ed (diff)
downloadbusybox-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.h8
-rw-r--r--shell/ash.c66
-rw-r--r--win32/process.c9
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
569typedef struct {
570 char *path;
571 char *name;
572 char *opts;
573 char buf[100];
574} interp_t;
575
576int FAST_FUNC parse_interpreter(const char *cmd, interp_t *interp);
569char ** FAST_FUNC grow_argv(char **argv, int n); 577char ** FAST_FUNC grow_argv(char **argv, int n);
570pid_t FAST_FUNC mingw_spawn(char **argv); 578pid_t FAST_FUNC mingw_spawn(char **argv);
571intptr_t FAST_FUNC mingw_spawn_detach(char **argv); 579intptr_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
9093static void 9093static void
9094#if ENABLE_PLATFORM_MINGW32
9095tryexec(IF_FEATURE_SH_STANDALONE(int applet_no, const char *nfpath,)
9096 const char *cmd, char **argv, char **envp)
9097#else
9094tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp) 9098tryexec(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 */
9190static struct builtincmd *find_builtin(const char *name); 9225static struct builtincmd *find_builtin(const char *name);
9191static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN; 9226static void shellexec(char *prog, char **argv, const char *path, int idx,
9192static void shellexec(char *prog, char **argv, const char *path, int idx) 9227 int nofork) NORETURN;
9228static 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
16498static void 16550static 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
56typedef struct { 56int FAST_FUNC
57 char *path;
58 char *name;
59 char *opts;
60 char buf[100];
61} interp_t;
62
63static int
64parse_interpreter(const char *cmd, interp_t *interp) 57parse_interpreter(const char *cmd, interp_t *interp)
65{ 58{
66 char *path, *t; 59 char *path, *t;