ben 98/05/16 09:34:51
Modified: src CHANGES src/include alloc.h buff.h util_script.h src/main alloc.c buff.c http_log.c util_script.c src/modules/standard mod_cgi.c mod_include.c Log: Fix Win32 CGI. Revision Changes Path 1.849 +5 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /export/home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.848 retrieving revision 1.849 diff -u -r1.848 -r1.849 --- CHANGES 1998/05/15 09:01:34 1.848 +++ CHANGES 1998/05/16 16:34:44 1.849 @@ -1,5 +1,10 @@ Changes with Apache 1.3b7 + *) WIN32: CGIs could cause a hang (because of a deadlock in the standard C + library), so CGI handling has been changed to use Win32 native handles + instead of C file descriptors. + [Ben Laurie and Bill Stoddard <[EMAIL PROTECTED]>] PR#1129, 1607 + *) The proxy cache would store an incorrect content-length in the cached file copy after a cache update. That resulted in repeated fetching of the original copy instead of using the cached copy. 1.57 +6 -0 apache-1.3/src/include/alloc.h Index: alloc.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/alloc.h,v retrieving revision 1.56 retrieving revision 1.57 diff -u -r1.56 -r1.57 --- alloc.h 1998/05/03 17:31:06 1.56 +++ alloc.h 1998/05/16 16:34:46 1.57 @@ -256,6 +256,9 @@ API_EXPORT(void) ap_note_cleanups_for_file(pool *, FILE *); API_EXPORT(void) ap_note_cleanups_for_fd(pool *, int); +#ifdef WIN32 +API_EXPORT(void) ap_note_cleanups_for_h(pool *, HANDLE); +#endif API_EXPORT(void) ap_kill_cleanups_for_fd(pool *p, int fd); API_EXPORT(void) ap_note_cleanups_for_socket(pool *, int); @@ -272,6 +275,9 @@ API_EXPORT(int) ap_pfclose(struct pool *, FILE *); API_EXPORT(int) ap_pclosef(struct pool *, int fd); +#ifdef WIN32 +API_EXPORT(int) ap_pcloseh(struct pool *, HANDLE hDevice); +#endif /* routines to deal with directories */ API_EXPORT(DIR *) ap_popendir(pool *p, const char *name); 1.40 +20 -3 apache-1.3/src/include/buff.h Index: buff.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/buff.h,v retrieving revision 1.39 retrieving revision 1.40 diff -u -r1.39 -r1.40 --- buff.h 1998/05/03 17:31:07 1.39 +++ buff.h 1998/05/16 16:34:46 1.40 @@ -117,6 +117,9 @@ /* could also put pointers to the basic I/O routines here */ int fd; /* the file descriptor */ int fd_in; /* input file descriptor, if different */ +#ifdef WIN32 + HANDLE hFH; /* Windows filehandle */ +#endif /* transport handle, for RPC binding handle or some such */ void *t_handle; @@ -142,6 +145,9 @@ /* Stream creation and modification */ API_EXPORT(BUFF *) ap_bcreate(pool *p, int flags); API_EXPORT(void) ap_bpushfd(BUFF *fb, int fd_in, int fd_out); +#ifdef WIN32 +API_EXPORT(void) ap_bpushh(BUFF *fb, HANDLE hFH); +#endif API_EXPORT(int) ap_bsetopt(BUFF *fb, int optname, const void *optval); API_EXPORT(int) ap_bgetopt(BUFF *fb, int optname, void *optval); API_EXPORT(int) ap_bsetflag(BUFF *fb, int flag, int value); @@ -191,9 +197,20 @@ ?os_toascii[(unsigned char)c]:(c), 0)) #endif /*CHARSET_EBCDIC*/ -API_EXPORT(int) ap_spawn_child_err_buff(pool *, int (*)(void *), void *, - enum kill_conditions, BUFF **pipe_in, BUFF **pipe_out, - BUFF **pipe_err); +typedef struct { +#ifdef WIN32 + /* + * These handles are used by ap_call_exec to call + * create process with pipe handles. + */ + HANDLE hPipeInputRead; + HANDLE hPipeOutputWrite; + HANDLE hPipeErrorWrite; +#endif +} child_info; +API_EXPORT(int) ap_spawn_child_err_buff(pool *, int (*)(void *, child_info *), void *, + enum kill_conditions, BUFF **pipe_in, BUFF **pipe_out, + BUFF **pipe_err); /* enable non-blocking operations */ API_EXPORT(int) ap_bnonblock(BUFF *fb, int direction); 1.32 +1 -1 apache-1.3/src/include/util_script.h Index: util_script.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/util_script.h,v retrieving revision 1.31 retrieving revision 1.32 diff -u -r1.31 -r1.32 --- util_script.h 1998/05/03 17:31:11 1.31 +++ util_script.h 1998/05/16 16:34:47 1.32 @@ -79,7 +79,7 @@ API_EXPORT(int) ap_scan_script_header_err_buff(request_rec *r, BUFF *f, char *buffer); API_EXPORT(void) ap_send_size(size_t size, request_rec *r); -API_EXPORT(int) ap_call_exec(request_rec *r, char *argv0, char **env, +API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, char **env, int shellcmd); #ifdef __cplusplus 1.92 +162 -4 apache-1.3/src/main/alloc.c Index: alloc.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/alloc.c,v retrieving revision 1.91 retrieving revision 1.92 diff -u -r1.91 -r1.92 --- alloc.c 1998/05/06 19:49:50 1.91 +++ alloc.c 1998/05/16 16:34:47 1.92 @@ -1533,6 +1533,36 @@ return res; } +#ifdef WIN32 +static void h_cleanup(void *fdv) +{ + CloseHandle((HANDLE) fdv); +} + +API_EXPORT(void) ap_note_cleanups_for_h(pool *p, HANDLE hDevice) +{ + ap_register_cleanup(p, (void *) hDevice, h_cleanup, h_cleanup); +} + +API_EXPORT(int) ap_pcloseh(pool *a, HANDLE hDevice) +{ + int res=0; + int save_errno; + + ap_block_alarms(); + + if (!CloseHandle(hDevice)) { + res = GetLastError(); + } + + save_errno = errno; + ap_kill_cleanup(a, (void *) hDevice, h_cleanup); + ap_unblock_alarms(); + errno = save_errno; + return res; +} +#endif + /* Note that we have separate plain_ and child_ cleanups for FILE *s, * since fclose() would flush I/O buffers, which is extremely undesirable; * we just close the descriptor. @@ -2011,12 +2041,139 @@ ap_unblock_alarms(); return pid; } - -API_EXPORT(int) ap_spawn_child_err_buff(pool *p, int (*func) (void *), void *data, - enum kill_conditions kill_how, - BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err) +API_EXPORT(int) ap_spawn_child_err_buff(pool *p, int (*func) (void *, child_info *), void *data, + enum kill_conditions kill_how, + BUFF **pipe_in, BUFF **pipe_out, BUFF **pipe_err) { +#ifdef WIN32 + SECURITY_ATTRIBUTES sa = {0}; + HANDLE hPipeOutputRead = NULL; + HANDLE hPipeOutputWrite = NULL; + HANDLE hPipeInputRead = NULL; + HANDLE hPipeInputWrite = NULL; + HANDLE hPipeErrorRead = NULL; + HANDLE hPipeErrorWrite = NULL; + int pid = 0; + child_info info; + + + ap_block_alarms(); + + /* + * First thing to do is to create the pipes that we will use for stdin, stdout, and + * stderr in the child process. + */ + sa.nLength = sizeof(sa); + sa.bInheritHandle = TRUE; + sa.lpSecurityDescriptor = NULL; + + + /* Create pipes for standard input/output/error redirection. */ + if (pipe_in && !CreatePipe(&hPipeInputRead, &hPipeInputWrite, &sa, 0)) + return 0; + + if (pipe_out && !CreatePipe(&hPipeOutputRead, &hPipeOutputWrite, &sa, 0)) { + if(pipe_in) { + CloseHandle(hPipeInputRead); + CloseHandle(hPipeInputWrite); + } + return 0; + } + + if (pipe_err && !CreatePipe(&hPipeErrorRead, &hPipeErrorWrite, &sa, 0)) { + if(pipe_in) { + CloseHandle(hPipeInputRead); + CloseHandle(hPipeInputWrite); + } + if(pipe_out) { + CloseHandle(hPipeOutputRead); + CloseHandle(hPipeOutputWrite); + } + return 0; + } + + /* The script writes stdout to this pipe handle */ + info.hPipeOutputWrite = hPipeOutputWrite; + + /* The script reads stdin from this pipe handle */ + info.hPipeInputRead = hPipeInputRead; + + /* The script writes stderr to this pipe handle */ + info.hPipeErrorWrite = hPipeErrorWrite; + + /* + * Try to launch the CGI. Under the covers, this call + * will try to pick up the appropriate interpreter if + * one is needed. + */ + pid = func(data, &info); + if (pid == -1) { + /* Things didn't work, so cleanup */ + pid = 0; /* map Win32 error code onto Unix default */ + CloseHandle(hPipeOutputRead); + CloseHandle(hPipeInputWrite); + CloseHandle(hPipeErrorRead); + } + else { + if (pipe_out) { + /* + * This pipe represents stdout for the script, + * so we read from this pipe. + */ + /* Create a read buffer */ + *pipe_out = ap_bcreate(p, B_RD); + + /* Setup the cleanup routine for the handle */ + ap_note_cleanups_for_h(p, hPipeOutputRead); + + /* Associate the handle with the new buffer */ + ap_bpushh(*pipe_out, hPipeOutputRead); + } + + if (pipe_in) { + /* + * This pipe represents stdin for the script, so we + * write to this pipe. + */ + /* Create a write buffer */ + *pipe_in = ap_bcreate(p, B_WR); + + /* Setup the cleanup routine for the handle */ + ap_note_cleanups_for_h(p, hPipeInputWrite); + + /* Associate the handle with the new buffer */ + ap_bpushh(*pipe_in, hPipeInputWrite); + + } + + if (pipe_err) { + /* + * This pipe represents stderr for the script, so + * we read from this pipe. + */ + /* Create a read buffer */ + *pipe_err = ap_bcreate(p, B_RD); + + /* Setup the cleanup routine for the handle */ + ap_note_cleanups_for_h(p, hPipeErrorRead); + + /* Associate the handle with the new buffer */ + ap_bpushh(*pipe_err, hPipeErrorRead); + } + } + + + /* + * Now that handles have been inherited, close them to be safe. + * You don't want to read or write to them accidentally, and we + * sure don't want to have a handle leak. + */ + CloseHandle(hPipeOutputWrite); + CloseHandle(hPipeInputRead); + CloseHandle(hPipeErrorWrite); + +#else int fd_in, fd_out, fd_err; int pid, save_errno; @@ -2051,6 +2208,7 @@ ap_note_cleanups_for_fd(p, fd_err); ap_bpushfd(*pipe_err, fd_err, fd_err); } +#endif ap_unblock_alarms(); return pid; 1.76 +34 -3 apache-1.3/src/main/buff.c Index: buff.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/buff.c,v retrieving revision 1.75 retrieving revision 1.76 diff -u -r1.75 -r1.76 --- buff.c 1998/05/08 22:54:52 1.75 +++ buff.c 1998/05/16 16:34:48 1.76 @@ -235,7 +235,14 @@ { int rv; - rv = read(fb->fd_in, buf, nbyte); +#ifdef WIN32 + if (fb->hFH != INVALID_HANDLE_VALUE) { + if (!ReadFile(fb->hFH,buf,nbyte,&rv,NULL)) + rv = -1; + } + else +#endif + rv = read(fb->fd_in, buf, nbyte); return rv; } @@ -263,10 +270,17 @@ { int rv; +#ifdef WIN32 + if (fb->hFH != INVALID_HANDLE_VALUE) { + if (!WriteFile(fb->hFH,buf,nbyte,&rv,NULL)) + rv = -1; + } + else +#endif #if defined (B_SFIO) - rv = sfwrite(fb->sf_out, buf, nbyte); + rv = sfwrite(fb->sf_out, buf, nbyte); #else - rv = write(fb->fd, buf, nbyte); + rv = write(fb->fd, buf, nbyte); #endif return rv; @@ -340,6 +354,9 @@ fb->fd = -1; fb->fd_in = -1; +#ifdef WIN32 + fb->hFH = INVALID_HANDLE_VALUE; +#endif #ifdef B_SFIO fb->sf_in = NULL; @@ -362,6 +379,16 @@ fb->fd_in = fd_in; } +#ifdef WIN32 +/* + * Push some Win32 handles onto the stream. + */ +API_EXPORT(void) ap_bpushh(BUFF *fb, HANDLE hFH) +{ + fb->hFH = hFH; +} +#endif + API_EXPORT(int) ap_bsetopt(BUFF *fb, int optname, const void *optval) { if (optname == BO_BYTECT) { @@ -1373,6 +1400,10 @@ else { rc3 = 0; } + } + else if (fb->hFH != INVALID_HANDLE_VALUE) { + rc2 = ap_pcloseh(fb->pool, fb->hFH); + rc3 = 0; } else { #endif 1.57 +2 -1 apache-1.3/src/main/http_log.c Index: http_log.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_log.c,v retrieving revision 1.56 retrieving revision 1.57 diff -u -r1.56 -r1.57 --- http_log.c 1998/05/04 16:28:47 1.56 +++ http_log.c 1998/05/16 16:34:48 1.57 @@ -651,7 +651,7 @@ { piped_log *pl; FILE *dummy; - + if (!spawn_child (p, piped_log_child, (void *)program, kill_after_timeout, &dummy, NULL)) { perror ("spawn_child"); @@ -661,6 +661,7 @@ pl = ap_palloc (p, sizeof (*pl)); pl->p = p; pl->write_f = dummy; + return pl; } 1.110 +110 -1 apache-1.3/src/main/util_script.c Index: util_script.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/util_script.c,v retrieving revision 1.109 retrieving revision 1.110 diff -u -r1.109 -r1.110 --- util_script.c 1998/05/10 02:20:12 1.109 +++ util_script.c 1998/05/16 16:34:48 1.110 @@ -586,7 +586,7 @@ #endif -API_EXPORT(int) ap_call_exec(request_rec *r, char *argv0, char **env, int shellcmd) +API_EXPORT(int) ap_call_exec(request_rec *r, child_info *pinfo, char *argv0, char **env, int shellcmd) { int pid = 0; #if defined(RLIMIT_CPU) || defined(RLIMIT_NPROC) || \ @@ -715,7 +715,13 @@ char *dot; char *exename; int is_exe = 0; + STARTUPINFO si; + PROCESS_INFORMATION pi; + char *pCommand; + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + interpreter[0] = 0; exename = strrchr(r->filename, '/'); @@ -770,6 +776,108 @@ } } + /* + * Make child process use hPipeOutputWrite as standard out, + * and make sure it does not show on screen. + */ + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; + si.wShowWindow = SW_HIDE; + si.hStdInput = pinfo->hPipeInputRead; + si.hStdOutput = pinfo->hPipeOutputWrite; + si.hStdError = pinfo->hPipeErrorWrite; + + pid = -1; + if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { + if (is_exe || is_binary) { + /* + * When the CGI is a straight binary executable, + * we can run it as is + */ + pCommand = r->filename; + } + else if (is_script) { + /* When an interpreter is needed, we need to create + * a command line that has the interpreter name + * followed by the CGI script name. + */ + pCommand = ap_pstrcat(r->pool, interpreter + 2, " ", + r->filename, NULL); + } + else { + /* If not an executable or script, just execute it + * from a command prompt. + */ + pCommand = ap_pstrcat(r->pool, "CMD.EXE", " /C ", + r->filename, NULL); + } + } + else { + + /* If we are in this leg, there are some other arguments + * that we must include in the execution of the CGI. + * Because CreateProcess is the way it is, we have to + * create a command line like format for the execution + * of the CGI. This means we need to create on long + * string with the executable and arguments. + * + * The arguments string comes in the request structure, + * and each argument is separated by a '+'. We'll replace + * these pluses with spaces. + */ + char *arguments=NULL; + int iStringSize = 0; + int x; + + /* + * Duplicate the request structure string so we don't change it. + */ + arguments = ap_pstrdup(r->pool, r->args); + + /* + * Change the '+' to ' ' + */ + for (x=0; arguments[x]; x++) { + if ('+' == arguments[x]) { + arguments[x] = ' '; + } + } + + /* + * We need to unescape any characters that are + * in the arguments list. + */ + ap_unescape_url(arguments); + arguments = ap_escape_shell_cmd(r->pool, arguments); + + /* + * The argument list should now be good to use, + * so now build the command line. + */ + if (is_exe || is_binary) { + pCommand = ap_pstrcat(r->pool, r->filename, " ", + arguments, NULL); + } + else if (is_script) { + pCommand = ap_pstrcat(r->pool, interpreter + 2, " ", + r->filename, " ", arguments, NULL); + } + else { + pCommand = ap_pstrcat(r->pool, "CMD.EXE", " /C ", + r->filename, " ", arguments, NULL); + } + } + + if (CreateProcess(NULL, pCommand, NULL, NULL, TRUE, 0, env, NULL, &si, &pi)) { + pid = pi.dwProcessId; + /* + * We must close the handles to the new process and its main thread + * to prevent handle and memory leaks. + */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + } +#if 0 if ((!r->args) || (!r->args[0]) || strchr(r->args, '=')) { if (is_exe || is_binary) { pid = spawnle(_P_NOWAIT, r->filename, r->filename, NULL, env); @@ -800,6 +908,7 @@ r->filename), env); } } +#endif return (pid); } #else 1.78 +5 -5 apache-1.3/src/modules/standard/mod_cgi.c Index: mod_cgi.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_cgi.c,v retrieving revision 1.77 retrieving revision 1.78 diff -u -r1.77 -r1.78 --- mod_cgi.c 1998/05/09 02:28:22 1.77 +++ mod_cgi.c 1998/05/16 16:34:50 1.78 @@ -281,7 +281,7 @@ char *argv0; }; -static int cgi_child(void *child_stuff) +static int cgi_child(void *child_stuff, child_info *pinfo) { struct cgi_child_stuff *cld = (struct cgi_child_stuff *) child_stuff; request_rec *r = cld->r; @@ -327,7 +327,7 @@ ap_cleanup_for_exec(); - child_pid = ap_call_exec(r, argv0, env, 0); + child_pid = ap_call_exec(r, pinfo, argv0, env, 0); #ifdef WIN32 return (child_pid); #else @@ -429,9 +429,9 @@ * SSI request -djg */ if (!ap_spawn_child_err_buff(r->main ? r->main->pool : r->pool, cgi_child, - (void *) &cld, - kill_after_timeout, - &script_out, &script_in, &script_err)) { + (void *) &cld, + kill_after_timeout, + &script_out, &script_in, &script_err)) { ap_log_error(APLOG_MARK, APLOG_ERR, r->server, "couldn't spawn child process: %s", r->filename); return SERVER_ERROR; 1.86 +7 -1 apache-1.3/src/modules/standard/mod_include.c Index: mod_include.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_include.c,v retrieving revision 1.85 retrieving revision 1.86 diff -u -r1.85 -r1.86 --- mod_include.c 1998/05/09 02:28:22 1.85 +++ mod_include.c 1998/05/16 16:34:50 1.86 @@ -729,6 +729,7 @@ } } +#ifndef WIN32 typedef struct { request_rec *r; char *s; @@ -825,7 +826,7 @@ */ return 0; } - +#endif static int handle_exec(FILE *in, request_rec *r, const char *error) { @@ -839,6 +840,10 @@ return 1; } if (!strcmp(tag, "cmd")) { +#ifdef WIN32 + ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, + "cmd in SSI temporarily disabled"); +#else parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 1); if (include_cmd(parsed_string, r) == -1) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, @@ -850,6 +855,7 @@ /* just in case some stooge changed directories */ #ifndef WIN32 ap_chdir_file(r->filename); +#endif #endif } else if (!strcmp(tag, "cgi")) {