aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2023-12-20 13:45:32 +0000
committerRon Yorston <rmy@pobox.com>2023-12-20 13:59:17 +0000
commit8d85a4a5be88931978fad594b94e762313d37afc (patch)
tree0f964f6cd9b0c71b02de5011a2f815e1470c8740
parent4f9b703c31e6360d1e7ffe7d3410e42e5118b4fd (diff)
downloadbusybox-w32-8d85a4a5be88931978fad594b94e762313d37afc.tar.gz
busybox-w32-8d85a4a5be88931978fad594b94e762313d37afc.tar.bz2
busybox-w32-8d85a4a5be88931978fad594b94e762313d37afc.zip
httpd: enable support for CGI
The upstream code uses fork/exec when running a CGI process. Emulate this by: - Spawning a child httpd process with the special '-I 0' option, along with the options provided on the server command line. This sets up the proper state then calls the cgi_handler() function. - The cgi_handler() function fixes the pipe file descriptors and starts another child process to run the CGI script. These processes are detached from the console on creation. When spawn() functions are run in P_DETACH mode they don't connect to the standard file descriptors. Normally this doesn't matter but the process which runs the CGI scripts needs to inherit the pipe endpoints. The create_detached_process() function handles this. See: https://github.com/rprichard/win32-console-docs/blob/master/README.md Adds about 2.9Kb to the size of the binary. (GitHub issue #266)
-rw-r--r--configs/mingw32_defconfig4
-rw-r--r--configs/mingw64_defconfig4
-rw-r--r--configs/mingw64u_defconfig4
-rw-r--r--include/mingw.h2
-rw-r--r--networking/httpd.c117
-rw-r--r--win32/process.c60
6 files changed, 176 insertions, 15 deletions
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
index ee01330d1..0f674d45c 100644
--- a/configs/mingw32_defconfig
+++ b/configs/mingw32_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.37.0.git
4# Sat Dec 9 09:38:58 2023 4# Wed Dec 20 13:23:38 2023
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -916,7 +916,7 @@ CONFIG_FEATURE_HTTPD_RANGES=y
916# CONFIG_FEATURE_HTTPD_SETUID is not set 916# CONFIG_FEATURE_HTTPD_SETUID is not set
917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y 917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set 918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
919# CONFIG_FEATURE_HTTPD_CGI is not set 919CONFIG_FEATURE_HTTPD_CGI=y
920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set 920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set 921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y 922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
index 3fed1c99b..37f95f4c8 100644
--- a/configs/mingw64_defconfig
+++ b/configs/mingw64_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.37.0.git
4# Sat Dec 9 09:38:58 2023 4# Wed Dec 20 13:23:38 2023
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -916,7 +916,7 @@ CONFIG_FEATURE_HTTPD_RANGES=y
916# CONFIG_FEATURE_HTTPD_SETUID is not set 916# CONFIG_FEATURE_HTTPD_SETUID is not set
917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y 917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set 918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
919# CONFIG_FEATURE_HTTPD_CGI is not set 919CONFIG_FEATURE_HTTPD_CGI=y
920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set 920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set 921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y 922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
diff --git a/configs/mingw64u_defconfig b/configs/mingw64u_defconfig
index f3391a206..f26b17ec9 100644
--- a/configs/mingw64u_defconfig
+++ b/configs/mingw64u_defconfig
@@ -1,7 +1,7 @@
1# 1#
2# Automatically generated make config: don't edit 2# Automatically generated make config: don't edit
3# Busybox version: 1.37.0.git 3# Busybox version: 1.37.0.git
4# Sat Dec 9 09:38:58 2023 4# Wed Dec 20 13:23:38 2023
5# 5#
6CONFIG_HAVE_DOT_CONFIG=y 6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set 7# CONFIG_PLATFORM_POSIX is not set
@@ -916,7 +916,7 @@ CONFIG_FEATURE_HTTPD_RANGES=y
916# CONFIG_FEATURE_HTTPD_SETUID is not set 916# CONFIG_FEATURE_HTTPD_SETUID is not set
917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y 917CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set 918# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
919# CONFIG_FEATURE_HTTPD_CGI is not set 919CONFIG_FEATURE_HTTPD_CGI=y
920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set 920# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set 921# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y 922CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
diff --git a/include/mingw.h b/include/mingw.h
index 46cbe03f8..88da85243 100644
--- a/include/mingw.h
+++ b/include/mingw.h
@@ -552,12 +552,14 @@ pid_t FAST_FUNC mingw_spawn(char **argv);
552intptr_t FAST_FUNC mingw_spawn_detach(char **argv); 552intptr_t FAST_FUNC mingw_spawn_detach(char **argv);
553intptr_t FAST_FUNC mingw_spawn_proc(const char **argv); 553intptr_t FAST_FUNC mingw_spawn_proc(const char **argv);
554int mingw_execv(const char *cmd, char *const *argv); 554int mingw_execv(const char *cmd, char *const *argv);
555int httpd_execv_detach(const char *cmd, char *const *argv);
555int mingw_execvp(const char *cmd, char *const *argv); 556int mingw_execvp(const char *cmd, char *const *argv);
556int mingw_execve(const char *cmd, char *const *argv, char *const *envp); 557int mingw_execve(const char *cmd, char *const *argv, char *const *envp);
557#define spawn mingw_spawn 558#define spawn mingw_spawn
558#define execvp mingw_execvp 559#define execvp mingw_execvp
559#define execve mingw_execve 560#define execve mingw_execve
560#define execv mingw_execv 561#define execv mingw_execv
562#define HTTPD_DETACH (8)
561 563
562#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') 564#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
563 565
diff --git a/networking/httpd.c b/networking/httpd.c
index 0de56a47a..d049e3842 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -461,6 +461,13 @@ static const struct {
461 461
462struct globals { 462struct globals {
463 int verbose; /* must be int (used by getopt32) */ 463 int verbose; /* must be int (used by getopt32) */
464#if ENABLE_PLATFORM_MINGW32
465 smallint foreground;
466# if ENABLE_FEATURE_HTTPD_CGI
467 int server_argc;
468 char **server_argv;
469# endif
470#endif
464 smallint flg_deny_all; 471 smallint flg_deny_all;
465#if ENABLE_FEATURE_HTTPD_GZIP 472#if ENABLE_FEATURE_HTTPD_GZIP
466 /* client can handle gzip / we are going to send gzip */ 473 /* client can handle gzip / we are going to send gzip */
@@ -518,6 +525,11 @@ struct globals {
518}; 525};
519#define G (*ptr_to_globals) 526#define G (*ptr_to_globals)
520#define verbose (G.verbose ) 527#define verbose (G.verbose )
528#if ENABLE_PLATFORM_MINGW32
529#define foreground (G.foreground )
530#define server_argc (G.server_argc )
531#define server_argv (G.server_argv )
532#endif
521#define flg_deny_all (G.flg_deny_all ) 533#define flg_deny_all (G.flg_deny_all )
522#if ENABLE_FEATURE_HTTPD_GZIP 534#if ENABLE_FEATURE_HTTPD_GZIP
523# define content_gzip (G.content_gzip ) 535# define content_gzip (G.content_gzip )
@@ -1563,6 +1575,52 @@ static NOINLINE void cgi_io_loop_and_exit(int fromCgi_rd, int toCgi_wr, int post
1563#endif 1575#endif
1564 1576
1565#if ENABLE_FEATURE_HTTPD_CGI 1577#if ENABLE_FEATURE_HTTPD_CGI
1578# if ENABLE_PLATFORM_MINGW32
1579static void cgi_handler(char **argv)
1580{
1581 struct fd_pair fromCgi; /* CGI -> httpd pipe */
1582 struct fd_pair toCgi; /* httpd -> CGI pipe */
1583 char *dir, *script;
1584
1585 xfunc_error_retval = 242;
1586
1587 if (sscanf(argv[0], "%d:%d:%d:%d", &toCgi.wr, &toCgi.rd,
1588 &fromCgi.wr, &fromCgi.rd) != 4) {
1589 exit(242);
1590 }
1591
1592 /* NB: close _first_, then move fds! */
1593 close(toCgi.wr);
1594 close(fromCgi.rd);
1595 xmove_fd(toCgi.rd, 0); /* replace stdin with the pipe */
1596 xmove_fd(fromCgi.wr, 1); /* replace stdout with the pipe */
1597
1598 dir = argv[1];
1599 script = argv[2];
1600
1601 if (chdir_or_warn(dir) != 0) {
1602 goto error_execing_cgi;
1603 }
1604
1605 /* set argv[0] to name without path */
1606 argv[0] = script;
1607 argv[1] = NULL;
1608
1609 /* _NOT_ execvp. We do not search PATH. argv[0] is a filename
1610 * without any dir components and will only match a file
1611 * in the current directory */
1612 if (foreground)
1613 execv(argv[0], argv);
1614 else
1615 httpd_execv_detach(argv[0], argv);
1616 if (verbose)
1617 bb_perror_msg("can't execute '%s'", argv[0]);
1618 error_execing_cgi:
1619 /* send to stdout */
1620 iobuf = xmalloc(IOBUF_SIZE);
1621 send_headers_and_exit(HTTP_NOT_FOUND);
1622}
1623# endif
1566 1624
1567static void setenv1(const char *name, const char *value) 1625static void setenv1(const char *name, const char *value)
1568{ 1626{
@@ -1596,6 +1654,10 @@ static void send_cgi_and_exit(
1596 struct fd_pair toCgi; /* httpd -> CGI pipe */ 1654 struct fd_pair toCgi; /* httpd -> CGI pipe */
1597 char *script, *last_slash; 1655 char *script, *last_slash;
1598 int pid; 1656 int pid;
1657#if ENABLE_PLATFORM_MINGW32
1658 char **argv;
1659 int i;
1660#endif
1599 1661
1600 /* Make a copy. NB: caller guarantees: 1662 /* Make a copy. NB: caller guarantees:
1601 * url[0] == '/', url[1] != '/' */ 1663 * url[0] == '/', url[1] != '/' */
@@ -1682,6 +1744,32 @@ static void send_cgi_and_exit(
1682 xpiped_pair(fromCgi); 1744 xpiped_pair(fromCgi);
1683 xpiped_pair(toCgi); 1745 xpiped_pair(toCgi);
1684 1746
1747#if ENABLE_PLATFORM_MINGW32
1748 /* Find script's dir */
1749 script = last_slash;
1750 if (script != url) { /* paranoia */
1751 *script = '\0';
1752 }
1753 script++;
1754
1755 argv = xzalloc((server_argc + 9) * sizeof(char *));
1756 argv[0] = (char *)bb_busybox_exec_path;
1757 argv[1] = (char *)"--busybox";
1758 argv[2] = (char *)"-httpd" + 1; // skip '-'
1759 argv[3] = (char *)"-I";
1760 argv[4] = (char *)"0";
1761 for (i = 0; i < server_argc; ++i)
1762 argv[i + 5] = server_argv[i];
1763 argv[server_argc + 5] = xasprintf("%d:%d:%d:%d", toCgi.wr, toCgi.rd,
1764 fromCgi.wr, fromCgi.rd);
1765 argv[server_argc + 6] = (char *)url + 1; // script directory
1766 argv[server_argc + 7] = (char *)script; // script name
1767 /* argv[server_argc + 8] = NULL; - xzalloc did it */
1768
1769 pid = foreground ? mingw_spawn(argv) : mingw_spawn_detach(argv);
1770 if (pid == -1)
1771 log_and_exit();
1772#else
1685 pid = vfork(); 1773 pid = vfork();
1686 if (pid < 0) { 1774 if (pid < 0) {
1687 /* TODO: log perror? */ 1775 /* TODO: log perror? */
@@ -1759,6 +1847,7 @@ static void send_cgi_and_exit(
1759 1847
1760 /* Restore variables possibly changed by child */ 1848 /* Restore variables possibly changed by child */
1761 xfunc_error_retval = 0; 1849 xfunc_error_retval = 0;
1850#endif
1762 1851
1763 /* Pump data */ 1852 /* Pump data */
1764 close(fromCgi.wr); 1853 close(fromCgi.wr);
@@ -2797,8 +2886,8 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2797} 2886}
2798#endif 2887#endif
2799#else /* ENABLE_PLATFORM_MINGW32 */ 2888#else /* ENABLE_PLATFORM_MINGW32 */
2800static void mini_httpd_win32(int fg, int sock, int argc, char **argv) NORETURN; 2889static void mini_httpd_win32(int sock, int argc, char **argv) NORETURN;
2801static void mini_httpd_win32(int fg, int sock, int argc, char **argv) 2890static void mini_httpd_win32(int sock, int argc, char **argv)
2802{ 2891{
2803 char *argv_copy[argc + 5]; 2892 char *argv_copy[argc + 5];
2804 2893
@@ -2820,7 +2909,8 @@ static void mini_httpd_win32(int fg, int sock, int argc, char **argv)
2820 setsockopt_keepalive(n); 2909 setsockopt_keepalive(n);
2821 2910
2822 argv_copy[4] = itoa(n); 2911 argv_copy[4] = itoa(n);
2823 if ((fg ? spawn(argv_copy) : mingw_spawn_detach(argv_copy)) == -1) 2912 if ((foreground ?
2913 spawn(argv_copy) : mingw_spawn_detach(argv_copy)) == -1)
2824 bb_perror_msg_and_die("can't execute 'httpd'"); 2914 bb_perror_msg_and_die("can't execute 'httpd'");
2825 2915
2826 /* parent, or spawn failed */ 2916 /* parent, or spawn failed */
@@ -2936,7 +3026,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2936 "\0" 3026 "\0"
2937 /* -v counts, -i implies -f */ 3027 /* -v counts, -i implies -f */
2938 IF_NOT_PLATFORM_MINGW32("vv:if",) 3028 IF_NOT_PLATFORM_MINGW32("vv:if",)
2939 IF_PLATFORM_MINGW32("vv:If",) 3029 IF_PLATFORM_MINGW32("vv:",)
2940 &opt_c_configFile, &url_for_decode, &home_httpd 3030 &opt_c_configFile, &url_for_decode, &home_httpd
2941 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) 3031 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2942 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) 3032 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
@@ -2981,7 +3071,8 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2981 } 3071 }
2982#endif 3072#endif
2983#else /* ENABLE_PLATFORM_MINGW32 */ 3073#else /* ENABLE_PLATFORM_MINGW32 */
2984 if (!(opt & OPT_FOREGROUND) && argv[0][0] != '-') 3074 foreground = (opt & OPT_FOREGROUND) == OPT_FOREGROUND;
3075 if (!foreground && argv[0][0] != '-')
2985 mingw_daemonize(argv); 3076 mingw_daemonize(argv);
2986#endif 3077#endif
2987 3078
@@ -3032,7 +3123,14 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
3032#endif 3123#endif
3033 3124
3034 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); 3125 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
3035#if !ENABLE_PLATFORM_MINGW32 3126#if ENABLE_PLATFORM_MINGW32
3127# if ENABLE_FEATURE_HTTPD_CGI
3128 if ((opt & OPT_INETD) && fd == 0) {
3129 cgi_handler(argv + optind);
3130 return 0;
3131 }
3132# endif
3133#else
3036 if (!(opt & OPT_INETD)) 3134 if (!(opt & OPT_INETD))
3037 signal(SIGHUP, sighup_handler); 3135 signal(SIGHUP, sighup_handler);
3038#endif 3136#endif
@@ -3044,6 +3142,11 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
3044 xdup2(0, 1); 3142 xdup2(0, 1);
3045 while (--fd > 2) 3143 while (--fd > 2)
3046 close(fd); 3144 close(fd);
3145# if ENABLE_FEATURE_HTTPD_CGI
3146 /* Skip 'httpd -I N' and omit any non-option arguments */
3147 server_argc = optind - 3;
3148 server_argv = argv + 3;
3149# endif
3047 } 3150 }
3048#endif 3151#endif
3049 if (opt & OPT_INETD) 3152 if (opt & OPT_INETD)
@@ -3057,7 +3160,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
3057 mini_httpd_nommu(server_socket, argc, argv); /* never returns */ 3160 mini_httpd_nommu(server_socket, argc, argv); /* never returns */
3058#endif 3161#endif
3059#else /* ENABLE_PLATFORM_MINGW32 */ 3162#else /* ENABLE_PLATFORM_MINGW32 */
3060 mini_httpd_win32(opt & OPT_FOREGROUND, server_socket, argc, argv); 3163 mini_httpd_win32(server_socket, argc, argv);
3061#endif 3164#endif
3062 /* return 0; */ 3165 /* return 0; */
3063} 3166}
diff --git a/win32/process.c b/win32/process.c
index b51f25d5c..35480e08d 100644
--- a/win32/process.c
+++ b/win32/process.c
@@ -248,6 +248,51 @@ grow_argv(char **argv, int n)
248 return new_argv; 248 return new_argv;
249} 249}
250 250
251#if ENABLE_FEATURE_HTTPD_CGI
252static int
253create_detached_process(const char *prog, char *const *argv)
254{
255 int argc, i;
256 char *command = NULL;
257 STARTUPINFO siStartInfo;
258 PROCESS_INFORMATION piProcInfo;
259 int success;
260
261 argc = string_array_len((char **)argv);
262 for (i = 0; i < argc; i++)
263 command = xappendword(command, quote_arg(argv[i]));
264
265 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
266 siStartInfo.cb = sizeof(STARTUPINFO);
267 siStartInfo.hStdInput = (HANDLE)_get_osfhandle(STDIN_FILENO);
268 siStartInfo.hStdOutput = (HANDLE)_get_osfhandle(STDOUT_FILENO);
269 siStartInfo.dwFlags = STARTF_USESTDHANDLES;
270
271 success = CreateProcess((LPCSTR)prog,
272 (LPSTR)command, /* command line */
273 NULL, /* process security attributes */
274 NULL, /* primary thread security attributes */
275 TRUE, /* handles are inherited */
276 DETACHED_PROCESS, /* creation flags */
277 NULL, /* use parent's environment */
278 NULL, /* use parent's current directory */
279 &siStartInfo, /* STARTUPINFO pointer */
280 &piProcInfo); /* receives PROCESS_INFORMATION */
281
282 free(command);
283
284 if (!success)
285 return -1;
286 exit(0);
287}
288
289# define SPAWNVEQ(m, p, a, e) \
290 ((m != HTTPD_DETACH) ? spawnveq(m, p, a, e) : \
291 create_detached_process(p, a))
292#else
293# define SPAWNVEQ(m, p, a, e) spawnveq(m, p, a, e)
294#endif
295
251static intptr_t 296static intptr_t
252mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, 297mingw_spawn_interpreter(int mode, const char *prog, char *const *argv,
253 char *const *envp, int level) 298 char *const *envp, int level)
@@ -259,7 +304,7 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv,
259 char *path = NULL; 304 char *path = NULL;
260 305
261 if (!parse_interpreter(prog, &interp)) 306 if (!parse_interpreter(prog, &interp))
262 return spawnveq(mode, prog, argv, envp); 307 return SPAWNVEQ(mode, prog, argv, envp);
263 308
264 if (++level > 4) { 309 if (++level > 4) {
265 errno = ELOOP; 310 errno = ELOOP;
@@ -275,7 +320,7 @@ mingw_spawn_interpreter(int mode, const char *prog, char *const *argv,
275 if (unix_path(interp.path) && find_applet_by_name(interp.name) >= 0) { 320 if (unix_path(interp.path) && find_applet_by_name(interp.name) >= 0) {
276 /* the fake path indicates the index of the script */ 321 /* the fake path indicates the index of the script */
277 new_argv[0] = path = xasprintf("%d:/%s", nopts+1, interp.name); 322 new_argv[0] = path = xasprintf("%d:/%s", nopts+1, interp.name);
278 ret = mingw_spawn_applet(mode, new_argv, envp); 323 ret = SPAWNVEQ(mode, bb_busybox_exec_path, new_argv, envp);
279 goto done; 324 goto done;
280 } 325 }
281#endif 326#endif
@@ -428,6 +473,17 @@ mingw_execv(const char *cmd, char *const *argv)
428 return mingw_execve(cmd, argv, NULL); 473 return mingw_execve(cmd, argv, NULL);
429} 474}
430 475
476#if ENABLE_FEATURE_HTTPD_CGI
477int httpd_execv_detach(const char *script, char *const *argv)
478{
479 intptr_t ret = mingw_spawn_interpreter(HTTPD_DETACH, script,
480 (char *const *)argv, NULL, 0);
481 if (ret != -1)
482 exit(0);
483 return ret;
484}
485#endif
486
431static inline long long filetime_to_ticks(const FILETIME *ft) 487static inline long long filetime_to_ticks(const FILETIME *ft)
432{ 488{
433 return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/ 489 return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/