diff options
author | Ron Yorston <rmy@pobox.com> | 2023-12-20 13:45:32 +0000 |
---|---|---|
committer | Ron Yorston <rmy@pobox.com> | 2023-12-20 13:59:17 +0000 |
commit | 8d85a4a5be88931978fad594b94e762313d37afc (patch) | |
tree | 0f964f6cd9b0c71b02de5011a2f815e1470c8740 /networking | |
parent | 4f9b703c31e6360d1e7ffe7d3410e42e5118b4fd (diff) | |
download | busybox-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)
Diffstat (limited to 'networking')
-rw-r--r-- | networking/httpd.c | 117 |
1 files changed, 110 insertions, 7 deletions
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 | ||
462 | struct globals { | 462 | struct 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 | ||
1579 | static 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 | ||
1567 | static void setenv1(const char *name, const char *value) | 1625 | static 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 */ |
2800 | static void mini_httpd_win32(int fg, int sock, int argc, char **argv) NORETURN; | 2889 | static void mini_httpd_win32(int sock, int argc, char **argv) NORETURN; |
2801 | static void mini_httpd_win32(int fg, int sock, int argc, char **argv) | 2890 | static 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 | } |