cvs: php4 /sapi/cgi cgi_main.c /sapi/cgi/libfcgi LICENSE.TERMS fcgi_stdio.c fcgiapp.c os_unix.c os_win32.c strerror.c /sapi/cgi/libfcgi/include
fastcgi.h fcgi_config.h fcgi_config_x86.h fcgi_stdio.h fcgiapp.h fcgiappmisc.h fcgimisc.h fcgio.h fcgios.h

From: Date: Sun, 10 Mar 2002 21:39:28 +0000
Subject: cvs: php4 /sapi/cgi cgi_main.c /sapi/cgi/libfcgi LICENSE.TERMS fcgi_stdio.c fcgiapp.c os_unix.c os_win32.c strerror.c /sapi/cgi/libfcgi/include
fastcgi.h fcgi_config.h fcgi_config_x86.h fcgi_stdio.h fcgiapp.h fcgiappmisc.h fcgimisc.h fcgio.h fcgios.h
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-9870@lists.php.net to get a copy of this message
shane Sun Mar 10 16:39:28 2002 EDT Added files: /php4/sapi/cgi/libfcgi LICENSE.TERMS fcgi_stdio.c fcgiapp.c os_unix.c os_win32.c strerror.c /php4/sapi/cgi/libfcgi/include fastcgi.h fcgi_config.h fcgi_config_x86.h fcgi_stdio.h fcgiapp.h fcgiappmisc.h fcgimisc.h fcgio.h fcgios.h Modified files: /php4/sapi/cgi cgi_main.c Log: combine fastcgi capability with regular cgi binary include fastcgi library for ease of windows builds NOTE: included fastcgi library is modified for thread safety, but fastcgi support in cgi_main.c is only written for single threaded serving. This does not present any issue for using fastcgi.

Index: php4/sapi/cgi/cgi_main.c diff -u php4/sapi/cgi/cgi_main.c:1.150 php4/sapi/cgi/cgi_main.c:1.151 --- php4/sapi/cgi/cgi_main.c:1.150 Wed Mar 6 08:25:25 2002 +++ php4/sapi/cgi/cgi_main.c Sun Mar 10 16:39:27 2002 @@ -76,6 +76,41 @@ #include "php_getopt.h" +#ifdef PHP_FASTCGI +#include "fcgi_config.h" +#include "fcgiapp.h" +FCGX_Stream *in, *out, *err; +FCGX_ParamArray envp; + +/* Our original environment from when the FastCGI first started */ +char **orig_env; + +/* The environment given by the FastCGI */ +char **cgi_env; + +/* The manufactured environment, from merging the base environ with + * the parameters set by the per-connection environment + */ +char **merge_env; + +/** + * Number of child processes that will get created to service requests + */ +static int children = 8; + +/** + * Set to non-zero if we are the parent process + */ +static int parent = 1; + +/** + * Process group + */ +static pid_t pgroup; + + +#endif + #define PHP_MODE_STANDARD 1 #define PHP_MODE_HIGHLIGHT 2 #define PHP_MODE_INDENT 3 @@ -107,13 +142,20 @@ { #ifdef PHP_WRITE_STDOUT long ret; +#else + size_t ret; +#endif +#ifdef PHP_FASTCGI + if (!FCGX_IsCGI()) { + return FCGX_PutStr( str, str_length, out ); + } +#endif +#ifdef PHP_WRITE_STDOUT ret = write(STDOUT_FILENO, str, str_length); if (ret <= 0) return 0; return ret; #else - size_t ret; - ret = fwrite(str, 1, MIN(str_length, 16384), stdout); return ret; #endif @@ -141,6 +183,13 @@ static void sapi_cgibin_flush(void *server_context) { +#ifdef PHP_FASTCGI + if (!FCGX_IsCGI()) { + if( FCGX_FFlush( out ) == -1 ) { + php_handle_aborted_connection(); + } + } else +#endif if (fflush(stdout)==EOF) { php_handle_aborted_connection(); } @@ -159,10 +208,20 @@ static int sapi_cgi_read_post(char *buffer, uint count_bytes TSRMLS_DC) { uint read_bytes=0, tmp_read_bytes; +#ifdef PHP_FASTCGI + char *pos = buffer; +#endif count_bytes = MIN(count_bytes, (uint)SG(request_info).content_length-SG(read_post_bytes)); while (read_bytes < count_bytes) { - tmp_read_bytes = read(0, buffer+read_bytes, count_bytes-read_bytes); +#ifdef PHP_FASTCGI + if (!FCGX_IsCGI()) { + tmp_read_bytes = FCGX_GetStr( pos, count_bytes-read_bytes, in ); + pos += tmp_read_bytes; + } else +#endif + tmp_read_bytes = read(0, buffer+read_bytes, count_bytes-read_bytes); + if (tmp_read_bytes<=0) { break; } @@ -213,8 +272,12 @@ */ static sapi_module_struct cgi_sapi_module = { "cgi", /* name */ +#ifdef PHP_FASTCGI + "CGI/FastCGI", /* pretty name */ +#else "CGI", /* pretty name */ - +#endif + php_module_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ @@ -405,6 +468,28 @@ void ***tsrm_ls; #endif +#ifdef PHP_FASTCGI + int env_size, cgi_env_size; + int max_requests = 500; + int requests = 0; + int fastcgi = !FCGX_IsCGI(); + + if (fastcgi) { + /* Calculate environment size */ + env_size = 0; + while( environ[ env_size ] ) { env_size++; } + /* Also include the final NULL pointer */ + env_size++; + + /* Allocate for our environment */ + orig_env = malloc( env_size * sizeof( char *)); + if( !orig_env ) { + perror( "Can't malloc environment" ); + exit( 1 ); + } + memcpy( orig_env, environ, env_size * sizeof( char *)); + } +#endif #ifdef HAVE_SIGNAL_H #if defined(SIGPIPE) && defined(SIG_IGN) @@ -446,7 +531,11 @@ } } - if (!cgi) { + if (!cgi +#ifdef PHP_FASTCGI + /* allow ini override for fastcgi */ +#endif + ) { while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) { switch (c) { case 'c': @@ -523,9 +612,102 @@ } #endif /* FORCE_CGI_REDIRECT */ +#ifdef PHP_FASTCGI + /* How many times to run PHP scripts before dying */ + if( getenv( "PHP_FCGI_MAX_REQUESTS" )) { + max_requests = atoi( getenv( "PHP_FCGI_MAX_REQUESTS" )); + if( !max_requests ) { + fprintf( stderr, + "PHP_FCGI_MAX_REQUESTS is not valid\n" ); + exit( 1 ); + } + } + +#ifndef PHP_WIN32 + /* Pre-fork, if required */ + if( getenv( "PHP_FCGI_CHILDREN" )) { + children = atoi( getenv( "PHP_FCGI_CHILDREN" )); + if( !children ) { + fprintf( stderr, + "PHP_FCGI_CHILDREN is not valid\n" ); + exit( 1 ); + } + } + + if( children ) { + int running = 0; + int i; + pid_t pid; + + /* Create a process group for ourself & children */ + setsid(); + pgroup = getpgrp(); +#ifdef DEBUG_FASTCGI + fprintf( stderr, "Process group %d\n", pgroup ); +#endif + + /* Set up handler to kill children upon exit */ + act.sa_flags = 0; + act.sa_handler = fastcgi_cleanup; + if( sigaction( SIGTERM, &act, &old_term ) || + sigaction( SIGINT, &act, &old_int ) || + sigaction( SIGQUIT, &act, &old_quit )) { + perror( "Can't set signals" ); + exit( 1 ); + } + + while( parent ) { + do { +#ifdef DEBUG_FASTCGI + fprintf( stderr, "Forking, %d running\n", + running ); +#endif + pid = fork(); + switch( pid ) { + case 0: + /* One of the children. + * Make sure we don't go round the + * fork loop any more + */ + parent = 0; + + /* don't catch our signals */ + sigaction( SIGTERM, &old_term, 0 ); + sigaction( SIGQUIT, &old_quit, 0 ); + sigaction( SIGINT, &old_int, 0 ); + break; + case -1: + perror( "php (pre-forking)" ); + exit( 1 ); + break; + default: + /* Fine */ + running++; + break; + } + } while( parent && ( running < children )); + + if( parent ) { +#ifdef DEBUG_FASTCGI + fprintf( stderr, "Wait for kids, pid %d\n", + getpid() ); +#endif + wait( &status ); + running--; + } + } + } + +#endif /* WIN32 */ + +#endif zend_first_try { - if (!cgi) { + if (!cgi +#ifdef PHP_FASTCGI + && !fastcgi +#endif + ) { while ((c=ap_php_getopt(argc, argv, OPTSTRING))!=-1) { switch (c) { case '?': @@ -543,6 +725,27 @@ ap_php_optarg = orig_optarg; } +#ifdef PHP_FASTCGI + /* start of FAST CGI loop */ + while (!fastcgi + || FCGX_Accept( &in, &out, &err, &cgi_env ) >= 0) { + + if (fastcgi) { + /* set up environment */ + cgi_env_size = 0; + while( cgi_env[ cgi_env_size ] ) { cgi_env_size++; } + merge_env = malloc( (env_size+cgi_env_size)*sizeof(char*) ); + if( !merge_env ) { + perror( "Can't malloc environment" ); + exit( 1 ); + } + memcpy( merge_env, orig_env, (env_size-1)*sizeof(char *) ); + memcpy( merge_env + env_size - 1, + cgi_env, (cgi_env_size+1)*sizeof(char *) ); + environ = merge_env; + } +#endif + init_request_info(TSRMLS_C); SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */ @@ -550,7 +753,11 @@ zend_llist_init(&global_vars, sizeof(char *), NULL, 0); - if (!cgi) { /* never execute the arguments if you are a CGI */ + if (!cgi +#ifdef PHP_FASTCGI + && !fastcgi +#endif + ) { /* never execute the arguments if you are a CGI */ if (SG(request_info).argv0) { free(SG(request_info).argv0); SG(request_info).argv0 = NULL; @@ -676,7 +883,11 @@ CG(interactive) = interactive; - if (!cgi) { + if (!cgi +#ifdef PHP_FASTCGI + && !fastcgi +#endif + ) { if (!SG(request_info).query_string) { len = 0; if (script_file) { @@ -716,9 +927,18 @@ SG(headers_sent) = 1; SG(request_info).no_headers = 1; } - file_handle.filename = "-"; - file_handle.type = ZEND_HANDLE_FP; - file_handle.handle.fp = stdin; +#ifdef PHP_FASTCGI + if (fastcgi) { + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = SG(request_info).path_translated; + } else { +#endif + file_handle.filename = "-"; + file_handle.type = ZEND_HANDLE_FP; + file_handle.handle.fp = stdin; +#ifdef PHP_FASTCGI + } +#endif file_handle.opened_path = NULL; file_handle.free_filename = 0; @@ -726,7 +946,11 @@ zend_llist_apply(&global_vars, (llist_apply_func_t) php_register_command_line_global_vars TSRMLS_CC); zend_llist_destroy(&global_vars); - if (!cgi) { + if (!cgi +#ifdef PHP_FASTCGI + && !fastcgi +#endif + ) { if (!SG(request_info).path_translated && argc > ap_php_optind) { SG(request_info).path_translated = estrdup(argv[ap_php_optind]); } @@ -835,10 +1059,28 @@ STR_FREE(SG(request_info).path_translated); +#ifdef PHP_FASTCGI + if (!fastcgi) break; + /* only fastcgi will get here */ + + /* TODO: We should free our environment here, but + * some platforms are unhappy if they've altered our + * existing environment and we then free() the new + * environ pointer + */ + + requests++; + if( max_requests && ( requests == max_requests )) { + FCGX_Finish(); + break; + } + /* end of fastcgi loop */ + } +#endif + if (cgi_sapi_module.php_ini_path_override) { free(cgi_sapi_module.php_ini_path_override); } - } zend_catch { exit_status = 255; } zend_end_try(); Index: php4/sapi/cgi/libfcgi/LICENSE.TERMS +++ php4/sapi/cgi/libfcgi/LICENSE.TERMS This FastCGI application library source and object code (the "Software") and its documentation (the "Documentation") are copyrighted by Open Market, Inc ("Open Market"). The following terms apply to all files associated with the Software and Documentation unless explicitly disclaimed in individual files. Open Market permits you to use, copy, modify, distribute, and license this Software and the Documentation for any purpose, provided that existing copyright notices are retained in all copies and that this notice is included verbatim in any distributions. No written agreement, license, or royalty fee is required for any of the authorized uses. Modifications to this Software and Documentation may be copyrighted by their authors and need not follow the licensing terms described here. If modifications to this Software and Documentation have new licensing terms, the new terms must be clearly indicated on the first page of each file where they apply. OPEN MARKET MAKES NO EXPRESS OR IMPLIED WARRANTY WITH RESPECT TO THE SOFTWARE OR THE DOCUMENTATION, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL OPEN MARKET BE LIABLE TO YOU OR ANY THIRD PARTY FOR ANY DAMAGES ARISING FROM OR RELATING TO THIS SOFTWARE OR THE DOCUMENTATION, INCLUDING, WITHOUT LIMITATION, ANY INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES OR SIMILAR DAMAGES, INCLUDING LOST PROFITS OR LOST DATA, EVEN IF OPEN MARKET HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS". OPEN MARKET HAS NO LIABILITY IN CONTRACT, TORT, NEGLIGENCE OR OTHERWISE ARISING OUT OF THIS SOFTWARE OR THE DOCUMENTATION. Index: php4/sapi/cgi/libfcgi/fcgi_stdio.c +++ php4/sapi/cgi/libfcgi/fcgi_stdio.c /* * fcgi_stdio.c -- * * FastCGI-stdio compatibility package * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #ifndef lint static const char rcsid[] = "$Id: fcgi_stdio.c,v 1.1 2002/03/10 21:39:28 shane Exp $"; #endif /* not lint */ #include <errno.h> /* for errno */ #include <stdarg.h> /* for va_arg */ #include <stdlib.h> /* for malloc */ #include <string.h> /* for strerror */ #include "fcgi_config.h" #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef _WIN32 #define DLLAPI __declspec(dllexport) #endif #include "fcgiapp.h" #include "fcgios.h" #include "fcgimisc.h" #define NO_FCGI_DEFINES #include "fcgi_stdio.h" #undef NO_FCGI_DEFINES #ifndef _WIN32 extern char **environ; #ifdef HAVE_FILENO_PROTO #include <stdio.h> #else extern int fileno(FILE *stream); #endif extern FILE *fdopen(int fildes, const char *type); extern FILE *popen(const char *command, const char *type); extern int pclose(FILE *stream); #else /* _WIN32 */ #define popen _popen #define pclose _pclose #endif /* _WIN32 */ FCGI_FILE _fcgi_sF[3]; /* *---------------------------------------------------------------------- * * FCGI_Accept -- * * Accepts a new request from the HTTP server and creates * a conventional execution environment for the request. * * If the application was invoked as a FastCGI server, * the first call to FCGI_Accept indicates that the application * has completed its initialization and is ready to accept * a request. Subsequent calls to FCGI_Accept indicate that * the application has completed its processing of the * current request and is ready to accept a new request. * * If the application was invoked as a CGI program, the first * call to FCGI_Accept is essentially a no-op and the second * call returns EOF (-1). * * Results: * 0 for successful call, -1 for error (application should exit). * * Side effects: * If the application was invoked as a FastCGI server, * and this is not the first call to this procedure, * FCGI_Accept first performs the equivalent of FCGI_Finish. * * On every call, FCGI_Accept accepts the new request and * reads the FCGI_PARAMS stream into an environment array, * i.e. a NULL-terminated array of strings of the form * ``name=value''. It assigns a pointer to this array * to the global variable environ, used by the standard * library function getenv. It creates new FCGI_FILE *s * representing input from the HTTP server, output to the HTTP * server, and error output to the HTTP server, and assigns these * new files to stdin, stdout, and stderr respectively. * * DO NOT mutate or retain pointers to environ or any values * contained in it (e.g. to the result of calling getenv(3)), * since these are freed by the next call to FCGI_Finish or * FCGI_Accept. In particular do not use setenv(3) or putenv(3) * in conjunction with FCGI_Accept. * *---------------------------------------------------------------------- */ static int acceptCalled = FALSE; static int isCGI = FALSE; int FCGI_Accept(void) { if(!acceptCalled) { /* * First call to FCGI_Accept. Is application running * as FastCGI or as CGI? */ isCGI = FCGX_IsCGI(); acceptCalled = TRUE; atexit(&FCGI_Finish); } else if(isCGI) { /* * Not first call to FCGI_Accept and running as CGI means * application is done. */ return(EOF); } if(isCGI) { FCGI_stdin->stdio_stream = stdin; FCGI_stdin->fcgx_stream = NULL; FCGI_stdout->stdio_stream = stdout; FCGI_stdout->fcgx_stream = NULL; FCGI_stderr->stdio_stream = stderr; FCGI_stderr->fcgx_stream = NULL; } else { FCGX_Stream *in, *out, *error; FCGX_ParamArray envp; int acceptResult = FCGX_Accept(&in, &out, &error, &envp); if(acceptResult < 0) { return acceptResult; } FCGI_stdin->stdio_stream = NULL; FCGI_stdin->fcgx_stream = in; FCGI_stdout->stdio_stream = NULL; FCGI_stdout->fcgx_stream = out; FCGI_stderr->stdio_stream = NULL; FCGI_stderr->fcgx_stream = error; environ = envp; } return 0; } /* *---------------------------------------------------------------------- * * FCGI_Finish -- * * Finishes the current request from the HTTP server. * * Side effects: * * Flushes any buffered output to the HTTP server. Then frees * all storage allocated by the previous call, including all * storage reachable from the value of environ set by the previous * call to FCGI_Accept. * * DO NOT use stdin, stdout, stderr, or environ between calling * FCGI_Finish and calling FCGI_Accept. * * DO NOT mutate or retain pointers to environ or any values * contained in it (e.g. to the result of calling getenv(3)), * since these are freed by the next call to FCGI_Finish or * FCGI_Accept. In particular do not use setenv(3) or putenv(3) * in conjunction with FCGI_Accept. * *---------------------------------------------------------------------- */ void FCGI_Finish(void) { if(!acceptCalled || isCGI) { return; } FCGX_Finish(); FCGI_stdin->fcgx_stream = NULL; FCGI_stdout->fcgx_stream = NULL; FCGI_stderr->fcgx_stream = NULL; environ = NULL; } /* *---------------------------------------------------------------------- * * FCGI_StartFilterData -- * * * The current request is for the filter role, and stdin is * positioned at EOF of FCGI_STDIN. The call repositions * stdin to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF), the call sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ int FCGI_StartFilterData(void) { if(FCGI_stdin->stdio_stream) { return -1; } else { return FCGX_StartFilterData(FCGI_stdin->fcgx_stream); } } /* *---------------------------------------------------------------------- * * FCGI_SetExitStatus -- * * Sets the exit status for the current request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * FCGI_SetExitStatus several times during a request; the last call * before the request ends (by calling FCGI_Accept) determines the * value. * *---------------------------------------------------------------------- */ void FCGI_SetExitStatus(int status) { if(FCGI_stdin->fcgx_stream) { FCGX_SetExitStatus(status, FCGI_stdin->fcgx_stream); } } /* *---------------------------------------------------------------------- * * FCGI_perror -- * * Wrapper for function defined in H&S Section 11.2 * *---------------------------------------------------------------------- */ void FCGI_perror(const char *str) { FCGI_fputs(str, FCGI_stderr); FCGI_fputs(": ", FCGI_stderr); FCGI_fputs(strerror(OS_Errno), FCGI_stderr); return; } /* *---------------------------------------------------------------------- * * FCGI_OpenFromFILE -- * * Constructs a new FCGI_FILE * from the FILE *stream. * * Results: * NULL if stream == NULL or storage could not be allocated, * otherwise the new FCGI_FILE *. * *---------------------------------------------------------------------- */ static FCGI_FILE *FCGI_OpenFromFILE(FILE *stream) { FCGI_FILE *fp; if (stream == NULL) return NULL; fp = (FCGI_FILE *) malloc(sizeof(FCGI_FILE)); if (fp != NULL) { fp->stdio_stream = stream; fp->fcgx_stream = NULL; } return fp; } /* *---------------------------------------------------------------------- * * FCGI_fopen, FCGI_fclose, FCGI_fflush, FCGI_freopen -- * * Wrappers for functions defined in H&S Section 15.2 * *---------------------------------------------------------------------- */ FCGI_FILE *FCGI_fopen(const char *path, const char *mode) { FILE * file = fopen(path, mode); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } int FCGI_fclose(FCGI_FILE *fp) { int n = EOF; if(fp->stdio_stream) { n = fclose(fp->stdio_stream); fp->stdio_stream = NULL; } else if(fp->fcgx_stream) { n = FCGX_FClose(fp->fcgx_stream); fp->fcgx_stream = NULL; } if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { free(fp); } return n; } int FCGI_fflush(FCGI_FILE *fp) { if(fp == NULL) return fflush(NULL); if(fp->stdio_stream) return fflush(fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_FFlush(fp->fcgx_stream); return EOF; } FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp) { if(fp->stdio_stream) { if(freopen(path, mode, fp->stdio_stream) == NULL) return NULL; else return fp; } else if(fp->fcgx_stream) { (void) FCGX_FClose(fp->fcgx_stream); fp->stdio_stream = fopen(path, mode); if(fp->stdio_stream == NULL) return NULL; else { fp->fcgx_stream = NULL; return fp; } } return NULL; } /* *---------------------------------------------------------------------- * * FCGI_setvbuf, FCGI_setbuf -- * * Wrappers for functions defined in H&S Section 15.3 * *---------------------------------------------------------------------- */ int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size) { if(fp->stdio_stream) return setvbuf(fp->stdio_stream, buf, bufmode, size); else { return -1; } } void FCGI_setbuf(FCGI_FILE *fp, char *buf) { if(fp->stdio_stream) setbuf(fp->stdio_stream, buf); } /* *---------------------------------------------------------------------- * * FCGI_fseek, FCGI_ftell, FCGI_rewind, FCGI_fgetpos, FCGI_fsetpos -- * * Wrappers for functions defined in H&S Section 15.5 * *---------------------------------------------------------------------- */ int FCGI_fseek(FCGI_FILE *fp, long offset, int whence) { if(fp->stdio_stream) return fseek(fp->stdio_stream, offset, whence); else { OS_SetErrno(ESPIPE); return -1; } } int FCGI_ftell(FCGI_FILE *fp) { if(fp->stdio_stream) return ftell(fp->stdio_stream); else { OS_SetErrno(ESPIPE); return -1; } } void FCGI_rewind(FCGI_FILE *fp) { if(fp->stdio_stream) rewind(fp->stdio_stream); else OS_SetErrno(ESPIPE); } #ifdef HAVE_FPOS int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos) { if(fp->stdio_stream) return fgetpos(fp->stdio_stream, pos); else { OS_SetErrno(ESPIPE); return -1; } } int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos) { if(fp->stdio_stream) return fsetpos(fp->stdio_stream, pos); else { OS_SetErrno(ESPIPE); return -1; } } #endif /* *---------------------------------------------------------------------- * * FCGI_fgetc, FCGI_getchar, FCGI_ungetc -- * * Wrappers for functions defined in H&S Section 15.6 * * XXX: getc and getchar are generally defined as macros * for performance reasons * *---------------------------------------------------------------------- */ int FCGI_fgetc(FCGI_FILE *fp) { if(fp->stdio_stream) return fgetc(fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_GetChar(fp->fcgx_stream); return EOF; } int FCGI_getchar(void) { return FCGI_fgetc(FCGI_stdin); } int FCGI_ungetc(int c, FCGI_FILE *fp) { if(fp->stdio_stream) return ungetc(c, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_UnGetChar(c, fp->fcgx_stream); return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fgets, FCGI_gets -- * * Wrappers for functions defined in H&S Section 15.7 * *---------------------------------------------------------------------- */ char *FCGI_fgets(char *str, int size, FCGI_FILE *fp) { if(fp->stdio_stream) return fgets(str, size, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_GetLine(str, size, fp->fcgx_stream); return NULL; } /* * The gets() function reads characters from the standard input stream * into the array pointed to by str until a newline character is read * or an end-of-file condition is encountered. The newline character * is discarded and the string is terminated with a null character. */ char *FCGI_gets(char *str) { char *s; int c; for (s = str; ((c = FCGI_getchar()) != '\n');) { if(c == EOF) { if(s == str) return NULL; else break; } else *s++ = (char) c; } *s = 0; return str; } /* *---------------------------------------------------------------------- * * Wrappers for functions defined in H&S Section 15.8 * * XXX: missing: fscanf, scanf * *---------------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * * FCGI_fputc, FCGI_putchar -- * * Wrappers for functions defined in H&S Section 15.9 * * XXX: putc and putchar are generally defined as macros * for performance reasons * *---------------------------------------------------------------------- */ int FCGI_fputc(int c, FCGI_FILE *fp) { if(fp->stdio_stream) return fputc(c, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_PutChar(c, fp->fcgx_stream); else return EOF; } int FCGI_putchar(int c) { return FCGI_fputc(c, FCGI_stdout); } /* *---------------------------------------------------------------------- * * FCGI_fputs, FCGI_puts * * Wrappers for functions defined in H&S Section 15.10 * *---------------------------------------------------------------------- */ int FCGI_fputs(const char *str, FCGI_FILE *fp) { if(fp->stdio_stream) return fputs(str, fp->stdio_stream); else if(fp->fcgx_stream) return FCGX_PutS(str, fp->fcgx_stream); return EOF; } int FCGI_puts(const char *str) { int n; if(FCGI_stdout->stdio_stream) { n = fputs(str, FCGI_stdout->stdio_stream); if(n < 0) return n; else return fputc('\n', FCGI_stdout->stdio_stream); } else if(FCGI_stdout->fcgx_stream) { n = FCGX_PutS(str, FCGI_stdout->fcgx_stream); if(n < 0) return n; else return FCGX_PutChar('\n', FCGI_stdout->fcgx_stream); } return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fprintf, FCGI_printf -- * * Wrappers for functions defined in H&S Section 15.11 * *---------------------------------------------------------------------- */ int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...) { va_list ap; int n = 0; va_start(ap, format); if(fp->stdio_stream) n = vfprintf(fp->stdio_stream, format, ap); else if(fp->fcgx_stream) n = FCGX_VFPrintF(fp->fcgx_stream, format, ap); va_end(ap); return n; } int FCGI_printf(const char *format, ...) { va_list ap; int n; va_start(ap, format); n = FCGI_vfprintf(FCGI_stdout, format, ap); va_end(ap); return n; } /* *---------------------------------------------------------------------- * * FCGI_vfprintf, FCGI_vprintf -- * * Wrappers for functions defined in H&S Section 15.12 * *---------------------------------------------------------------------- */ int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap) { if(fp->stdio_stream) return vfprintf(fp->stdio_stream, format, ap); else if(fp->fcgx_stream) return FCGX_VFPrintF(fp->fcgx_stream, format, ap); return EOF; } int FCGI_vprintf(const char *format, va_list ap) { if(FCGI_stdout->stdio_stream) return vfprintf(FCGI_stdout->stdio_stream, format, ap); else if(FCGI_stdout->fcgx_stream) return FCGX_VFPrintF(FCGI_stdout->fcgx_stream, format, ap); return EOF; } /* *---------------------------------------------------------------------- * * FCGI_fread, FCGI_fwrite -- * * Wrappers for functions defined in H&S Section 15.13 * *---------------------------------------------------------------------- */ size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) { int n; if(fp->stdio_stream) return fread(ptr, size, nmemb, fp->stdio_stream); else if(fp->fcgx_stream) { if((size * nmemb) == 0) { return 0; } n = FCGX_GetStr((char *) ptr, size * nmemb, fp->fcgx_stream); return (n/size); } return (size_t)EOF; } size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp) { int n; if(fp->stdio_stream) return fwrite(ptr, size, nmemb, fp->stdio_stream); else if(fp->fcgx_stream) { if((size * nmemb) == 0) { return 0; } n = FCGX_PutStr((char *) ptr, size * nmemb, fp->fcgx_stream); return (n/size); } return (size_t)EOF; } /* *---------------------------------------------------------------------- * * FCGI_feof, FCGI_ferror, FCGI_clearerr -- * * Wrappers for functions defined in H&S Section 15.14 * *---------------------------------------------------------------------- */ int FCGI_feof(FCGI_FILE *fp) { if(fp->stdio_stream) { return feof(fp->stdio_stream); } else if (fp->fcgx_stream){ return FCGX_HasSeenEOF(fp->fcgx_stream); } return -1; } int FCGI_ferror(FCGI_FILE *fp) { if(fp->stdio_stream) { return ferror(fp->stdio_stream); } else if(fp->fcgx_stream) { return FCGX_GetError(fp->fcgx_stream); } return -1; } void FCGI_clearerr(FCGI_FILE *fp) { if(fp->stdio_stream) { clearerr(fp->stdio_stream); } else if(fp->fcgx_stream) { FCGX_ClearError(fp->fcgx_stream); } return; } /* *---------------------------------------------------------------------- * * FCGI_tmpfile -- * * Wrappers for function defined in H&S Section 15.16 * *---------------------------------------------------------------------- */ FCGI_FILE *FCGI_tmpfile(void) { FILE * file = tmpfile(); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } /* *---------------------------------------------------------------------- * * FCGI_fileno, FCGI_fdopen, FCGI_popen, FCGI_pclose -- * * Wrappers for POSIX, X/OPEN functions not in ISO C * *---------------------------------------------------------------------- */ int FCGI_fileno(FCGI_FILE *fp) { if(fp->stdio_stream) return fileno(fp->stdio_stream); else return -1; } FCGI_FILE *FCGI_fdopen(int fd, const char *mode) { FILE * file = fdopen(fd, mode); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) fclose(file); return fcgi_file; } FCGI_FILE *FCGI_popen(const char *cmd, const char *type) { FILE * file = popen(cmd, type); FCGI_FILE * fcgi_file = FCGI_OpenFromFILE(file); if (file && !fcgi_file) pclose(file); return fcgi_file; } int FCGI_pclose(FCGI_FILE *fp) { int n = EOF; if (fp->stdio_stream) { n = pclose(fp->stdio_stream); fp->stdio_stream = NULL; } else if(fp->fcgx_stream) { /* * The caller is deeply confused; don't free the storage. */ return EOF; } if((fp != FCGI_stdin) && (fp != FCGI_stdout) && (fp != FCGI_stderr)) { free(fp); } return n; } Index: php4/sapi/cgi/libfcgi/fcgiapp.c +++ php4/sapi/cgi/libfcgi/fcgiapp.c /* * fcgiapp.c -- * * FastCGI application library: request-at-a-time * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * */ #ifndef lint static const char rcsid[] = "$Id: fcgiapp.c,v 1.1 2002/03/10 21:39:28 shane Exp $"; #endif /* not lint */ #include <assert.h> #include <errno.h> #include <fcntl.h> /* for fcntl */ #include <math.h> #include <memory.h> /* for memchr() */ #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include "fcgi_config.h" #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> /* for getpeername */ #endif #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #ifdef HAVE_LIMITS_H #include <limits.h> #endif #ifdef _WIN32 #define DLLAPI __declspec(dllexport) #endif #include "fcgimisc.h" #include "fastcgi.h" #include "fcgios.h" #include "fcgiapp.h" /* * This is a workaround for one version of the HP C compiler * (c89 on HP-UX 9.04, also Stratus FTX), which will dump core * if given 'long double' for varargs. */ #ifdef HAVE_VA_ARG_LONG_DOUBLE_BUG #define LONG_DOUBLE double #else #define LONG_DOUBLE long double #endif /* * Globals */ static int libInitialized = 0; static int isFastCGI = -1; static char *webServerAddressList = NULL; static FCGX_Request the_request; void _FCGX_FreeStream(FCGX_Stream **streamPtr, BOOL freeData); void FCGX_ShutdownPending(void) { OS_ShutdownPending(); } static void *Malloc(size_t size) { void *result = malloc(size); ASSERT(size == 0 || result != NULL); return result; } static char *StringCopy(char *str) { int strLen = strlen(str); char *newString = (char *)Malloc(strLen + 1); memcpy(newString, str, strLen); newString[strLen] = '\000'; return newString; } /* *---------------------------------------------------------------------- * * FCGX_GetChar -- * * Reads a byte from the input stream and returns it. * * Results: * The byte, or EOF (-1) if the end of input has been reached. * *---------------------------------------------------------------------- */ int FCGX_GetChar(FCGX_Stream *stream) { if(stream->rdNext != stream->stop) return *stream->rdNext++; if(stream->isClosed || !stream->isReader) return EOF; stream->fillBuffProc(stream); stream->stopUnget = stream->rdNext; if(stream->rdNext != stream->stop) return *stream->rdNext++; ASSERT(stream->isClosed); /* bug in fillBufProc if not */ return EOF; } /* *---------------------------------------------------------------------- * * FCGX_GetStr -- * * Reads up to n consecutive bytes from the input stream * into the character array str. Performs no interpretation * of the input bytes. * * Results: * Number of bytes read. If result is smaller than n, * the end of input has been reached. * *---------------------------------------------------------------------- */ int FCGX_GetStr(char *str, int n, FCGX_Stream *stream) { int m, bytesMoved; if(n <= 0) { return 0; } /* * Fast path: n bytes are already available */ if(n <= (stream->stop - stream->rdNext)) { memcpy(str, stream->rdNext, n); stream->rdNext += n; return n; } /* * General case: stream is closed or buffer fill procedure * needs to be called */ bytesMoved = 0; for (;;) { if(stream->rdNext != stream->stop) { m = min(n - bytesMoved, stream->stop - stream->rdNext); memcpy(str, stream->rdNext, m); bytesMoved += m; stream->rdNext += m; if(bytesMoved == n) return bytesMoved; str += m; } if(stream->isClosed || !stream->isReader) return bytesMoved; stream->fillBuffProc(stream); stream->stopUnget = stream->rdNext; } } /* *---------------------------------------------------------------------- * * FCGX_GetLine -- * * Reads up to n-1 consecutive bytes from the input stream * into the character array str. Stops before n-1 bytes * have been read if '\n' or EOF is read. The terminating '\n' * is copied to str. After copying the last byte into str, * stores a '\0' terminator. * * Results: * NULL if EOF is the first thing read from the input stream, * str otherwise. * *---------------------------------------------------------------------- */ char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream) { int c; char *p = str; n--; while (n > 0) { c = FCGX_GetChar(stream); if(c == EOF) { if(p == str) return NULL; else break; } *p++ = (char) c; n--; if(c == '\n') break; } *p = '\0'; return str; } /* *---------------------------------------------------------------------- * * FCGX_UnGetChar -- * * Pushes back the character c onto the input stream. One * character of pushback is guaranteed once a character * has been read. No pushback is possible for EOF. * * Results: * Returns c if the pushback succeeded, EOF if not. * *---------------------------------------------------------------------- */ int FCGX_UnGetChar(int c, FCGX_Stream *stream) { if(c == EOF || stream->isClosed || !stream->isReader || stream->rdNext == stream->stopUnget) return EOF; --(stream->rdNext); *stream->rdNext = (unsigned char) c; return c; } /* *---------------------------------------------------------------------- * * FCGX_HasSeenEOF -- * * Returns EOF if end-of-file has been detected while reading * from stream; otherwise returns 0. * * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately * following FCGX_GetChar(s) may return EOF. This function, like * the standard C stdio function feof, does not provide the * ability to peek ahead. * * Results: * EOF if end-of-file has been detected, 0 if not. * *---------------------------------------------------------------------- */ int FCGX_HasSeenEOF(FCGX_Stream *stream) { return (stream->isClosed) ? EOF : 0; } /* *---------------------------------------------------------------------- * * FCGX_PutChar -- * * Writes a byte to the output stream. * * Results: * The byte, or EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutChar(int c, FCGX_Stream *stream) { if(stream->wrNext != stream->stop) return (*stream->wrNext++ = (unsigned char) c); if(stream->isClosed || stream->isReader) return EOF; stream->emptyBuffProc(stream, FALSE); if(stream->wrNext != stream->stop) return (*stream->wrNext++ = (unsigned char) c); ASSERT(stream->isClosed); /* bug in emptyBuffProc if not */ return EOF; } /* *---------------------------------------------------------------------- * * FCGX_PutStr -- * * Writes n consecutive bytes from the character array str * into the output stream. Performs no interpretation * of the output bytes. * * Results: * Number of bytes written (n) for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream) { int m, bytesMoved; /* * Fast path: room for n bytes in the buffer */ if(n <= (stream->stop - stream->wrNext)) { memcpy(stream->wrNext, str, n); stream->wrNext += n; return n; } /* * General case: stream is closed or buffer empty procedure * needs to be called */ bytesMoved = 0; for (;;) { if(stream->wrNext != stream->stop) { m = min(n - bytesMoved, stream->stop - stream->wrNext); memcpy(stream->wrNext, str, m); bytesMoved += m; stream->wrNext += m; if(bytesMoved == n) return bytesMoved; str += m; } if(stream->isClosed || stream->isReader) return -1; stream->emptyBuffProc(stream, FALSE); } } /* *---------------------------------------------------------------------- * * FCGX_PutS -- * * Writes a character string to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_PutS(const char *str, FCGX_Stream *stream) { return FCGX_PutStr(str, strlen(str), stream); } /* *---------------------------------------------------------------------- * * FCGX_FPrintF -- * * Performs output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...) { int result; va_list ap; va_start(ap, format); result = FCGX_VFPrintF(stream, format, ap); va_end(ap); return result; } /* *---------------------------------------------------------------------- * * FCGX_VFPrintF -- * * Performs output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ #define PRINTF_BUFFLEN 100 /* * More than sufficient space for all unmodified conversions * except %s and %f. */ #define FMT_BUFFLEN 25 /* * Max size of a format specifier is 1 + 5 + 7 + 7 + 2 + 1 + slop */ static void CopyAndAdvance(char **destPtr, char **srcPtr, int n); int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg) { char *f, *fStop, *percentPtr, *p, *fmtBuffPtr, *buffPtr; int op, performedOp, sizeModifier, buffCount = 0, buffLen, specifierLength; int fastPath, n, auxBuffLen = 0, buffReqd, minWidth, precision, exp; char *auxBuffPtr = NULL; int streamCount = 0; char fmtBuff[FMT_BUFFLEN]; char buff[PRINTF_BUFFLEN]; int intArg; short shortArg; long longArg; unsigned unsignedArg; unsigned long uLongArg; unsigned short uShortArg; char *charPtrArg = NULL; void *voidPtrArg; int *intPtrArg; long *longPtrArg; short *shortPtrArg; double doubleArg = 0.0; LONG_DOUBLE lDoubleArg = 0.0L; fmtBuff[0] = '%'; f = (char *) format; fStop = f + strlen(f); while (f != fStop) { percentPtr = (char *)memchr(f, '%', fStop - f); if(percentPtr == NULL) percentPtr = fStop; if(percentPtr != f) { if(FCGX_PutStr(f, percentPtr - f, stream) < 0) goto ErrorReturn; streamCount += percentPtr - f; f = percentPtr; if(f == fStop) break; } fastPath = TRUE; /* * The following loop always executes either once or twice. */ for (;;) { if(fastPath) { /* * Fast path: Scan optimistically, hoping that no flags, * minimum field width, or precision are specified. * Use the preallocated buffer, which is large enough * for all fast path cases. If the conversion specifier * is really more complex, run the loop a second time * using the slow path. * Note that fast path execution of %s bypasses the buffer * and %f is not attempted on the fast path due to * its large buffering requirements. */ op = *(percentPtr + 1); switch(op) { case 'l': case 'L': case 'h': sizeModifier = op; op = *(percentPtr + 2); fmtBuff[1] = (char) sizeModifier; fmtBuff[2] = (char) op; fmtBuff[3] = '\0'; specifierLength = 3; break; default: sizeModifier = ' '; fmtBuff[1] = (char) op; fmtBuff[2] = '\0'; specifierLength = 2; break; } buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { /* * Slow path: Scan the conversion specifier and construct * a new format string, compute an upper bound on the * amount of buffering that sprintf will require, * and allocate a larger buffer if necessary. */ p = percentPtr + 1; fmtBuffPtr = &fmtBuff[1]; /* * Scan flags */ n = strspn(p, "-0+ #"); if(n > 5) goto ErrorReturn; CopyAndAdvance(&fmtBuffPtr, &p, n); /* * Scan minimum field width */ n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { minWidth = va_arg(arg, int); if(abs(minWidth) > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, "%d", minWidth); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else { minWidth = 0; } } else if(n <= 6) { minWidth = strtol(p, NULL, 10); CopyAndAdvance(&fmtBuffPtr, &p, n); } else { goto ErrorReturn; } /* * Scan precision */ if(*p == '.') { CopyAndAdvance(&fmtBuffPtr, &p, 1); n = strspn(p, "0123456789"); if(n == 0) { if(*p == '*') { precision = va_arg(arg, int); if(precision < 0) precision = 0; if(precision > 999999) goto ErrorReturn; /* * The following use of strlen rather than the * value returned from sprintf is because SUNOS4 * returns a char * instead of an int count. */ sprintf(fmtBuffPtr, "%d", precision); fmtBuffPtr += strlen(fmtBuffPtr); p++; } else { precision = 0; } } else if(n <= 6) { precision = strtol(p, NULL, 10); CopyAndAdvance(&fmtBuffPtr, &p, n); } else { goto ErrorReturn; } } else { precision = -1; } /* * Scan size modifier and conversion operation */ switch(*p) { case 'l': case 'L': case 'h': sizeModifier = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); break; default: sizeModifier = ' '; break; } op = *p; CopyAndAdvance(&fmtBuffPtr, &p, 1); ASSERT(fmtBuffPtr - fmtBuff < FMT_BUFFLEN); *fmtBuffPtr = '\0'; specifierLength = p - percentPtr; /* * Bound the required buffer size. For s and f * conversions this requires examining the argument. */ switch(op) { case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'c': case 'p': buffReqd = max(precision, 46); break; case 's': charPtrArg = va_arg(arg, char *); if (!charPtrArg) charPtrArg = "(null)"; if(precision == -1) { buffReqd = strlen(charPtrArg); } else { p = (char *)memchr(charPtrArg, '\0', precision); buffReqd = (p == NULL) ? precision : p - charPtrArg; } break; case 'f': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); frexp(doubleArg, &exp); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); /* XXX Need to check for the presence of * frexpl() and use it if available */ frexp((double) lDoubleArg, &exp); break; default: goto ErrorReturn; } if(precision == -1) precision = 6; buffReqd = precision + 3 + ((exp > 0) ? exp/3 : 0); break; case 'e': case 'E': case 'g': case 'G': if(precision == -1) precision = 6; buffReqd = precision + 8; break; case 'n': case '%': default: goto ErrorReturn; break; } buffReqd = max(buffReqd + 10, minWidth); /* * Allocate the buffer */ if(buffReqd <= PRINTF_BUFFLEN) { buffPtr = buff; buffLen = PRINTF_BUFFLEN; } else { if(auxBuffPtr == NULL || buffReqd > auxBuffLen) { if(auxBuffPtr != NULL) free(auxBuffPtr); auxBuffPtr = (char *)Malloc(buffReqd); auxBuffLen = buffReqd; if(auxBuffPtr == NULL) goto ErrorReturn; } buffPtr = auxBuffPtr; buffLen = auxBuffLen; } } /* * This giant switch statement requires the following variables * to be set up: op, sizeModifier, arg, buffPtr, fmtBuff. * When fastPath == FALSE and op == 's' or 'f', the argument * has been read into charPtrArg, doubleArg, or lDoubleArg. * The statement produces the boolean performedOp, TRUE iff * the op/sizeModifier were executed and argument consumed; * if performedOp, the characters written into buffPtr[] * and the character count buffCount (== EOF meaning error). * * The switch cases are arranged in the same order as in the * description of fprintf in section 15.11 of Harbison and Steele. */ performedOp = TRUE; switch(op) { case 'd': case 'i': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': longArg = va_arg(arg, long); sprintf(buffPtr, fmtBuff, longArg); buffCount = strlen(buffPtr); break; case 'h': shortArg = (short) va_arg(arg, int); sprintf(buffPtr, fmtBuff, shortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'u': case 'o': case 'x': case 'X': switch(sizeModifier) { case ' ': unsignedArg = va_arg(arg, unsigned); sprintf(buffPtr, fmtBuff, unsignedArg); buffCount = strlen(buffPtr); break; case 'l': uLongArg = va_arg(arg, unsigned long); sprintf(buffPtr, fmtBuff, uLongArg); buffCount = strlen(buffPtr); break; case 'h': uShortArg = (unsigned short) va_arg(arg, int); sprintf(buffPtr, fmtBuff, uShortArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'c': switch(sizeModifier) { case ' ': intArg = va_arg(arg, int); sprintf(buffPtr, fmtBuff, intArg); buffCount = strlen(buffPtr); break; case 'l': /* * XXX: Allowed by ISO C Amendment 1, but * many platforms don't yet support wint_t */ goto ErrorReturn; default: goto ErrorReturn; } break; case 's': switch(sizeModifier) { case ' ': if(fastPath) { buffPtr = va_arg(arg, char *); buffCount = strlen(buffPtr); buffLen = buffCount + 1; } else { sprintf(buffPtr, fmtBuff, charPtrArg); buffCount = strlen(buffPtr); } break; case 'l': /* * XXX: Don't know how to convert a sequence * of wide characters into a byte stream, or * even how to predict the buffering required. */ goto ErrorReturn; default: goto ErrorReturn; } break; case 'p': if(sizeModifier != ' ') goto ErrorReturn; voidPtrArg = va_arg(arg, void *); sprintf(buffPtr, fmtBuff, voidPtrArg); buffCount = strlen(buffPtr); break; case 'n': switch(sizeModifier) { case ' ': intPtrArg = va_arg(arg, int *); *intPtrArg = streamCount; break; case 'l': longPtrArg = va_arg(arg, long *); *longPtrArg = streamCount; break; case 'h': shortPtrArg = (short *) va_arg(arg, short *); *shortPtrArg = (short) streamCount; break; default: goto ErrorReturn; } buffCount = 0; break; case 'f': if(fastPath) { performedOp = FALSE; break; } switch(sizeModifier) { case ' ': sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case 'e': case 'E': case 'g': case 'G': switch(sizeModifier) { case ' ': doubleArg = va_arg(arg, double); sprintf(buffPtr, fmtBuff, doubleArg); buffCount = strlen(buffPtr); break; case 'L': lDoubleArg = va_arg(arg, LONG_DOUBLE); sprintf(buffPtr, fmtBuff, lDoubleArg); buffCount = strlen(buffPtr); break; default: goto ErrorReturn; } break; case '%': if(sizeModifier != ' ') goto ErrorReturn; buff[0] = '%'; buffCount = 1; break; case '\0': goto ErrorReturn; default: performedOp = FALSE; break; } /* switch(op) */ if(performedOp) break; if(!fastPath) goto ErrorReturn; fastPath = FALSE; } /* for (;;) */ ASSERT(buffCount < buffLen); if(buffCount > 0) { if(FCGX_PutStr(buffPtr, buffCount, stream) < 0) goto ErrorReturn; streamCount += buffCount; } else if(buffCount < 0) { goto ErrorReturn; } f += specifierLength; } /* while(f != fStop) */ goto NormalReturn; ErrorReturn: streamCount = -1; NormalReturn: if(auxBuffPtr != NULL) free(auxBuffPtr); return streamCount; } /* * Copy n characters from *srcPtr to *destPtr, then increment * both *srcPtr and *destPtr by n. */ static void CopyAndAdvance(char **destPtr, char **srcPtr, int n) { char *dest = *destPtr; char *src = *srcPtr; int i; for (i = 0; i < n; i++) *dest++ = *src++; *destPtr = dest; *srcPtr = src; } /* *---------------------------------------------------------------------- * * FCGX_FFlush -- * * Flushes any buffered output. * * Server-push is a legitimate application of FCGX_FFlush. * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept * does it implicitly. FCGX_FFlush may reduce performance * by increasing the total number of operating system calls * the application makes. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FFlush(FCGX_Stream *stream) { if(!stream || stream->isClosed || stream->isReader) return 0; stream->emptyBuffProc(stream, FALSE); return (stream->isClosed) ? -1 : 0; } /* *---------------------------------------------------------------------- * * FCGX_FClose -- * * Performs FCGX_FFlush and closes the stream. * * This is not a very useful operation, since FCGX_Accept * does it implicitly. Closing the out stream before the * err stream results in an extra write if there's nothing * in the err stream, and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ int FCGX_FClose(FCGX_Stream *stream) { if (stream == NULL) return 0; if(!stream->wasFCloseCalled) { if(!stream->isReader) { stream->emptyBuffProc(stream, TRUE); } stream->wasFCloseCalled = TRUE; stream->isClosed = TRUE; if(stream->isReader) { stream->wrNext = stream->stop = stream->rdNext; } else { stream->rdNext = stream->stop = stream->wrNext; } } return (stream->FCGI_errno == 0) ? 0 : EOF; } /* *---------------------------------------------------------------------- * * SetError -- * * An error has occurred; save the error code in the stream * for diagnostic purposes and set the stream state so that * reads return EOF and writes have no effect. * *---------------------------------------------------------------------- */ static void SetError(FCGX_Stream *stream, int FCGI_errno) { /* * Preserve only the first error. */ if(stream->FCGI_errno == 0) { stream->FCGI_errno = FCGI_errno; stream->isClosed = TRUE; } } /* *---------------------------------------------------------------------- * * FCGX_GetError -- * * Return the stream error code. 0 means no error, > 0 * is an errno(2) error, < 0 is an FCGX_errno error. * *---------------------------------------------------------------------- */ int FCGX_GetError(FCGX_Stream *stream) { return stream->FCGI_errno; } /* *---------------------------------------------------------------------- * * FCGX_ClearError -- * * Clear the stream error code and end-of-file indication. * *---------------------------------------------------------------------- */ void FCGX_ClearError(FCGX_Stream *stream) { stream->FCGI_errno = 0; /* * stream->isClosed = FALSE; * XXX: should clear isClosed but work is needed to make it safe * to do so. For example, if an application calls FClose, gets * an I/O error on the write, calls ClearError and retries * the FClose, FClose (really EmptyBuffProc) will write a second * EOF record. If an application calls PutChar instead of FClose * after the ClearError, the application will write more data. * The stream's state must discriminate between various states * of the stream that are now all lumped under isClosed. */ } /* *====================================================================== * Parameters *====================================================================== */ /* * A vector of pointers representing the parameters received * by a FastCGI application server, with the vector's length * and last valid element so adding new parameters is efficient. */ typedef struct Params { FCGX_ParamArray vec; /* vector of strings */ int length; /* number of string vec can hold */ char **cur; /* current item in vec; *cur == NULL */ } Params; typedef Params *ParamsPtr; /* *---------------------------------------------------------------------- * * NewParams -- * * Creates a new Params structure. * * Results: * Pointer to the new structure. * *---------------------------------------------------------------------- */ static ParamsPtr NewParams(int length) { ParamsPtr result; result = (Params *)Malloc(sizeof(Params)); result->vec = (char **)Malloc(length * sizeof(char *)); result->length = length; result->cur = result->vec; *result->cur = NULL; return result; } /* *---------------------------------------------------------------------- * * FreeParams -- * * Frees a Params structure and all the parameters it contains. * * Side effects: * env becomes invalid. * *---------------------------------------------------------------------- */ static void FreeParams(ParamsPtr *paramsPtrPtr) { ParamsPtr paramsPtr = *paramsPtrPtr; char **p; if(paramsPtr == NULL) { return; } for (p = paramsPtr->vec; p < paramsPtr->cur; p++) { free(*p); } free(paramsPtr->vec); free(paramsPtr); *paramsPtrPtr = NULL; } /* *---------------------------------------------------------------------- * * PutParam -- * * Add a name/value pair to a Params structure. * * Results: * None. * * Side effects: * Parameters structure updated. * *---------------------------------------------------------------------- */ static void PutParam(ParamsPtr paramsPtr, char *nameValue) { int size; *paramsPtr->cur++ = nameValue; size = paramsPtr->cur - paramsPtr->vec; if(size >= paramsPtr->length) { paramsPtr->length *= 2; paramsPtr->vec = (FCGX_ParamArray)realloc(paramsPtr->vec, paramsPtr->length * sizeof(char *)); paramsPtr->cur = paramsPtr->vec + size; } *paramsPtr->cur = NULL; } /* *---------------------------------------------------------------------- * * FCGX_GetParam -- obtain value of FCGI parameter in environment * * * Results: * Value bound to name, NULL if name not present in the * environment envp. Caller must not mutate the result * or retain it past the end of this request. * *---------------------------------------------------------------------- */ char *FCGX_GetParam(const char *name, FCGX_ParamArray envp) { int len; char **p; if (name == NULL || envp == NULL) return NULL; len = strlen(name); for (p = envp; *p; ++p) { if((strncmp(name, *p, len) == 0) && ((*p)[len] == '=')) { return *p+len+1; } } return NULL; } /* *---------------------------------------------------------------------- * * Start of FastCGI-specific code * *---------------------------------------------------------------------- */ /* *---------------------------------------------------------------------- * * ReadParams -- * * Reads FastCGI name-value pairs from stream until EOF. Converts * each pair to name=value format and adds it to Params structure. * *---------------------------------------------------------------------- */ static int ReadParams(Params *paramsPtr, FCGX_Stream *stream) { int nameLen, valueLen; unsigned char lenBuff[3]; char *nameValue; while((nameLen = FCGX_GetChar(stream)) != EOF) { /* * Read name length (one or four bytes) and value length * (one or four bytes) from stream. */ if((nameLen & 0x80) != 0) { if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } nameLen = ((nameLen & 0x7f) << 24) + (lenBuff[0] << 16) + (lenBuff[1] << 8) + lenBuff[2]; } if((valueLen = FCGX_GetChar(stream)) == EOF) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } if((valueLen & 0x80) != 0) { if(FCGX_GetStr((char *) &lenBuff[0], 3, stream) != 3) { SetError(stream, FCGX_PARAMS_ERROR); return -1; } valueLen = ((valueLen & 0x7f) << 24) + (lenBuff[0] << 16) + (lenBuff[1] << 8) + lenBuff[2]; } /* * nameLen and valueLen are now valid; read the name and value * from stream and construct a standard environment entry. */ nameValue = (char *)Malloc(nameLen + valueLen + 2); if(FCGX_GetStr(nameValue, nameLen, stream) != nameLen) { SetError(stream, FCGX_PARAMS_ERROR); free(nameValue); return -1; } *(nameValue + nameLen) = '='; if(FCGX_GetStr(nameValue + nameLen + 1, valueLen, stream) != valueLen) { SetError(stream, FCGX_PARAMS_ERROR); free(nameValue); return -1; } *(nameValue + nameLen + valueLen + 1) = '\0'; PutParam(paramsPtr, nameValue); } return 0; } /* *---------------------------------------------------------------------- * * MakeHeader -- * * Constructs an FCGI_Header struct. * *---------------------------------------------------------------------- */ static FCGI_Header MakeHeader( int type, int requestId, int contentLength, int paddingLength) { FCGI_Header header; ASSERT(contentLength >= 0 && contentLength <= FCGI_MAX_LENGTH); ASSERT(paddingLength >= 0 && paddingLength <= 0xff); header.version = FCGI_VERSION_1; header.type = (unsigned char) type; header.requestIdB1 = (unsigned char) ((requestId >> 8) & 0xff); header.requestIdB0 = (unsigned char) ((requestId ) & 0xff); header.contentLengthB1 = (unsigned char) ((contentLength >> 8) & 0xff); header.contentLengthB0 = (unsigned char) ((contentLength ) & 0xff); header.paddingLength = (unsigned char) paddingLength; header.reserved = 0; return header; } /* *---------------------------------------------------------------------- * * MakeEndRequestBody -- * * Constructs an FCGI_EndRequestBody struct. * *---------------------------------------------------------------------- */ static FCGI_EndRequestBody MakeEndRequestBody( int appStatus, int protocolStatus) { FCGI_EndRequestBody body; body.appStatusB3 = (unsigned char) ((appStatus >> 24) & 0xff); body.appStatusB2 = (unsigned char) ((appStatus >> 16) & 0xff); body.appStatusB1 = (unsigned char) ((appStatus >> 8) & 0xff); body.appStatusB0 = (unsigned char) ((appStatus ) & 0xff); body.protocolStatus = (unsigned char) protocolStatus; memset(body.reserved, 0, sizeof(body.reserved)); return body; } /* *---------------------------------------------------------------------- * * MakeUnknownTypeBody -- * * Constructs an FCGI_MakeUnknownTypeBody struct. * *---------------------------------------------------------------------- */ static FCGI_UnknownTypeBody MakeUnknownTypeBody( int type) { FCGI_UnknownTypeBody body; body.type = (unsigned char) type; memset(body.reserved, 0, sizeof(body.reserved)); return body; } /* *---------------------------------------------------------------------- * * AlignInt8 -- * * Returns the smallest integer greater than or equal to n * that's a multiple of 8. * *---------------------------------------------------------------------- */ static int AlignInt8(unsigned n) { return (n + 7) & (UINT_MAX - 7); } /* *---------------------------------------------------------------------- * * AlignPtr8 -- * * Returns the smallest pointer greater than or equal to p * that's a multiple of 8. * *---------------------------------------------------------------------- */ static unsigned char *AlignPtr8(unsigned char *p) { unsigned long u = (unsigned long) p; u = ((u + 7) & (ULONG_MAX - 7)) - u; return p + u; } /* * State associated with a stream */ typedef struct FCGX_Stream_Data { unsigned char *buff; /* buffer after alignment */ int bufflen; /* number of bytes buff can store */ unsigned char *mBuff; /* buffer as returned by Malloc */ unsigned char *buffStop; /* reader: last valid byte + 1 of entire buffer. * stop generally differs from buffStop for * readers because of record structure. * writer: buff + bufflen */ int type; /* reader: FCGI_PARAMS or FCGI_STDIN * writer: FCGI_STDOUT or FCGI_STDERR */ int eorStop; /* reader: stop stream at end-of-record */ int skip; /* reader: don't deliver content bytes */ int contentLen; /* reader: bytes of unread content */ int paddingLen; /* reader: bytes of unread padding */ int isAnythingWritten; /* writer: data has been written to ipcFd */ int rawWrite; /* writer: write data without stream headers */ FCGX_Request *reqDataPtr; /* request data not specific to one stream */ } FCGX_Stream_Data; /* *---------------------------------------------------------------------- * * WriteCloseRecords -- * * Writes an EOF record for the stream content if necessary. * If this is the last writer to close, writes an FCGI_END_REQUEST * record. * *---------------------------------------------------------------------- */ static void WriteCloseRecords(struct FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; /* * Enter rawWrite mode so final records won't be encapsulated as * stream data. */ data->rawWrite = TRUE; /* * Generate EOF for stream content if needed. */ if(!(data->type == FCGI_STDERR && stream->wrNext == data->buff && !data->isAnythingWritten)) { FCGI_Header header; header = MakeHeader(data->type, data->reqDataPtr->requestId, 0, 0); FCGX_PutStr((char *) &header, sizeof(header), stream); }; /* * Generate FCGI_END_REQUEST record if needed. */ if(data->reqDataPtr->nWriters == 1) { FCGI_EndRequestRecord endRequestRecord; endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, data->reqDataPtr->requestId, sizeof(endRequestRecord.body), 0); endRequestRecord.body = MakeEndRequestBody( data->reqDataPtr->appStatus, FCGI_REQUEST_COMPLETE); FCGX_PutStr((char *) &endRequestRecord, sizeof(endRequestRecord), stream); } data->reqDataPtr->nWriters--; } static int write_it_all(int fd, char *buf, int len) { int wrote; while (len) { wrote = OS_Write(fd, buf, len); if (wrote < 0) return wrote; len -= wrote; buf += wrote; } return len; } /* *---------------------------------------------------------------------- * * EmptyBuffProc -- * * Encapsulates any buffered stream content in a FastCGI * record. Writes the data, making the buffer empty. * *---------------------------------------------------------------------- */ static void EmptyBuffProc(struct FCGX_Stream *stream, int doClose) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; int cLen, eLen; /* * If the buffer contains stream data, fill in the header. * Pad the record to a multiple of 8 bytes in length. Padding * can't overflow the buffer because the buffer is a multiple * of 8 bytes in length. If the buffer contains no stream * data, reclaim the space reserved for the header. */ if(!data->rawWrite) { cLen = stream->wrNext - data->buff - sizeof(FCGI_Header); if(cLen > 0) { eLen = AlignInt8(cLen); /* * Giving the padding a well-defined value keeps Purify happy. */ memset(stream->wrNext, 0, eLen - cLen); stream->wrNext += eLen - cLen; *((FCGI_Header *) data->buff) = MakeHeader(data->type, data->reqDataPtr->requestId, cLen, eLen - cLen); } else { stream->wrNext = data->buff; } } if(doClose) { WriteCloseRecords(stream); }; if (stream->wrNext != data->buff) { data->isAnythingWritten = TRUE; if (write_it_all(data->reqDataPtr->ipcFd, (char *)data->buff, stream->wrNext - data->buff) < 0) { SetError(stream, OS_Errno); return; } stream->wrNext = data->buff; } /* * The buffer is empty. */ if(!data->rawWrite) { stream->wrNext += sizeof(FCGI_Header); } } /* * Return codes for Process* functions */ #define STREAM_RECORD 0 #define SKIP 1 #define BEGIN_RECORD 2 #define MGMT_RECORD 3 /* *---------------------------------------------------------------------- * * ProcessManagementRecord -- * * Reads and responds to a management record. The only type of * management record this library understands is FCGI_GET_VALUES. * The only variables that this library's FCGI_GET_VALUES * understands are FCGI_MAX_CONNS, FCGI_MAX_REQS, and FCGI_MPXS_CONNS. * Ignore other FCGI_GET_VALUES variables; respond to other * management records with a FCGI_UNKNOWN_TYPE record. * *---------------------------------------------------------------------- */ static int ProcessManagementRecord(int type, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; ParamsPtr paramsPtr = NewParams(3); char **pPtr; char response[64]; /* 64 = 8 + 3*(1+1+14+1)* + padding */ char *responseP = &response[FCGI_HEADER_LEN]; char *name, value = '\0'; int len, paddedLen; if(type == FCGI_GET_VALUES) { ReadParams(paramsPtr, stream); if((FCGX_GetError(stream) != 0) || (data->contentLen != 0)) { FreeParams(&paramsPtr); return FCGX_PROTOCOL_ERROR; } for (pPtr = paramsPtr->vec; pPtr < paramsPtr->cur; pPtr++) { name = *pPtr; *(strchr(name, '=')) = '\0'; if(strcmp(name, FCGI_MAX_CONNS) == 0) { value = '1'; } else if(strcmp(name, FCGI_MAX_REQS) == 0) { value = '1'; } else if(strcmp(name, FCGI_MPXS_CONNS) == 0) { value = '0'; } else { name = NULL; } if(name != NULL) { len = strlen(name); sprintf(responseP, "%c%c%s%c", len, 1, name, value); responseP += len + 3; } } len = responseP - &response[FCGI_HEADER_LEN]; paddedLen = AlignInt8(len); *((FCGI_Header *) response) = MakeHeader(FCGI_GET_VALUES_RESULT, FCGI_NULL_REQUEST_ID, len, paddedLen - len); FreeParams(&paramsPtr); } else { paddedLen = len = sizeof(FCGI_UnknownTypeBody); ((FCGI_UnknownTypeRecord *) response)->header = MakeHeader(FCGI_UNKNOWN_TYPE, FCGI_NULL_REQUEST_ID, len, 0); ((FCGI_UnknownTypeRecord *) response)->body = MakeUnknownTypeBody(type); } if (write_it_all(data->reqDataPtr->ipcFd, response, FCGI_HEADER_LEN + paddedLen) < 0) { SetError(stream, OS_Errno); return -1; } return MGMT_RECORD; } /* *---------------------------------------------------------------------- * * ProcessBeginRecord -- * * Reads an FCGI_BEGIN_REQUEST record. * * Results: * BEGIN_RECORD for normal return. FCGX_PROTOCOL_ERROR for * protocol error. SKIP for attempt to multiplex * connection. -1 for error from write (errno in stream). * * Side effects: * In case of BEGIN_RECORD return, stores requestId, role, * keepConnection values, and sets isBeginProcessed = TRUE. * *---------------------------------------------------------------------- */ static int ProcessBeginRecord(int requestId, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; FCGI_BeginRequestBody body; if(requestId == 0 || data->contentLen != sizeof(body)) { return FCGX_PROTOCOL_ERROR; } if(data->reqDataPtr->isBeginProcessed) { /* * The Web server is multiplexing the connection. This library * doesn't know how to handle multiplexing, so respond with * FCGI_END_REQUEST{protocolStatus = FCGI_CANT_MPX_CONN} */ FCGI_EndRequestRecord endRequestRecord; endRequestRecord.header = MakeHeader(FCGI_END_REQUEST, requestId, sizeof(endRequestRecord.body), 0); endRequestRecord.body = MakeEndRequestBody(0, FCGI_CANT_MPX_CONN); if (write_it_all(data->reqDataPtr->ipcFd, (char *)&endRequestRecord, sizeof(endRequestRecord)) < 0) { SetError(stream, OS_Errno); return -1; } return SKIP; } /* * Accept this new request. Read the record body. */ data->reqDataPtr->requestId = requestId; if(FCGX_GetStr((char *) &body, sizeof(body), stream) != sizeof(body)) { return FCGX_PROTOCOL_ERROR; } data->reqDataPtr->keepConnection = (body.flags & FCGI_KEEP_CONN); data->reqDataPtr->role = (body.roleB1 << 8) + body.roleB0; data->reqDataPtr->isBeginProcessed = TRUE; return BEGIN_RECORD; } /* *---------------------------------------------------------------------- * * ProcessHeader -- * * Interprets FCGI_Header. Processes FCGI_BEGIN_REQUEST and * management records here; extracts information from stream * records (FCGI_PARAMS, FCGI_STDIN) into stream. * * Results: * >= 0 for a normal return, < 0 for error * * Side effects: * XXX: Many (more than there used to be). * If !stream->isRequestIdSet, ProcessHeader initializes * stream->requestId from header and sets stream->isRequestIdSet * to TRUE. ProcessHeader also sets stream->contentLen to header's * contentLength, and sets stream->paddingLen to the header's * paddingLength. * *---------------------------------------------------------------------- */ static int ProcessHeader(FCGI_Header header, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; int requestId; if(header.version != FCGI_VERSION_1) { return FCGX_UNSUPPORTED_VERSION; } requestId = (header.requestIdB1 << 8) + header.requestIdB0; data->contentLen = (header.contentLengthB1 << 8) + header.contentLengthB0; data->paddingLen = header.paddingLength; if(header.type == FCGI_BEGIN_REQUEST) { return ProcessBeginRecord(requestId, stream); } if(requestId == FCGI_NULL_REQUEST_ID) { return ProcessManagementRecord(header.type, stream); } if(requestId != data->reqDataPtr->requestId) { return SKIP; } if(header.type != data->type) { return FCGX_PROTOCOL_ERROR; } return STREAM_RECORD; } /* *---------------------------------------------------------------------- * * FillBuffProc -- * * Reads bytes from the ipcFd, supplies bytes to a stream client. * *---------------------------------------------------------------------- */ static void FillBuffProc(FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; FCGI_Header header; int headerLen = 0; int status, count; for (;;) { /* * If data->buff is empty, do a read. */ if(stream->rdNext == data->buffStop) { count = OS_Read(data->reqDataPtr->ipcFd, (char *)data->buff, data->bufflen); if(count <= 0) { SetError(stream, (count == 0 ? FCGX_PROTOCOL_ERROR : OS_Errno)); return; } stream->rdNext = data->buff; data->buffStop = data->buff + count; } /* * Now data->buff is not empty. If the current record contains * more content bytes, deliver all that are present in data->buff. */ if(data->contentLen > 0) { count = min(data->contentLen, data->buffStop - stream->rdNext); data->contentLen -= count; if(!data->skip) { stream->wrNext = stream->stop = stream->rdNext + count; return; } else { stream->rdNext += count; if(data->contentLen > 0) { continue; } else { data->skip = FALSE; } } } /* * If the current record (whose content has been fully consumed by * the client) was padded, skip over the padding bytes. */ if(data->paddingLen > 0) { count = min(data->paddingLen, data->buffStop - stream->rdNext); data->paddingLen -= count; stream->rdNext += count; if(data->paddingLen > 0) { continue; } } /* * All done with the current record, including the padding. * If we're in a recursive call from ProcessHeader, deliver EOF. */ if(data->eorStop) { stream->stop = stream->rdNext; stream->isClosed = TRUE; return; } /* * Fill header with bytes from the input buffer. */ count = min((int)sizeof(header) - headerLen, data->buffStop - stream->rdNext); memcpy(((char *)(&header)) + headerLen, stream->rdNext, count); headerLen += count; stream->rdNext += count; if(headerLen < sizeof(header)) { continue; }; headerLen = 0; /* * Interpret header. eorStop prevents ProcessHeader from reading * past the end-of-record when using stream to read content. */ data->eorStop = TRUE; stream->stop = stream->rdNext; status = ProcessHeader(header, stream); data->eorStop = FALSE; stream->isClosed = FALSE; switch(status) { case STREAM_RECORD: /* * If this stream record header marked the end of stream * data deliver EOF to the stream client, otherwise loop * and deliver data. * * XXX: If this is final stream and * stream->rdNext != data->buffStop, buffered * data is next request (server pipelining)? */ if(data->contentLen == 0) { stream->wrNext = stream->stop = stream->rdNext; stream->isClosed = TRUE; return; } break; case SKIP: data->skip = TRUE; break; case BEGIN_RECORD: /* * If this header marked the beginning of a new * request, return role information to caller. */ return; break; case MGMT_RECORD: break; default: ASSERT(status < 0); SetError(stream, status); return; break; } } } /* *---------------------------------------------------------------------- * * NewStream -- * * Creates a stream to read or write from an open ipcFd. * The stream performs reads/writes of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewStream( FCGX_Request *reqDataPtr, int bufflen, int isReader, int streamType) { /* * XXX: It would be a lot cleaner to have a NewStream that only * knows about the type FCGX_Stream, with all other * necessary data passed in. It appears that not just * data and the two procs are needed for initializing stream, * but also data->buff and data->buffStop. This has implications * for procs that want to swap buffers, too. */ FCGX_Stream *stream = (FCGX_Stream *)Malloc(sizeof(FCGX_Stream)); FCGX_Stream_Data *data = (FCGX_Stream_Data *)Malloc(sizeof(FCGX_Stream_Data)); data->reqDataPtr = reqDataPtr; bufflen = AlignInt8(min(max(bufflen, 32), FCGI_MAX_LENGTH + 1)); data->bufflen = bufflen; data->mBuff = (unsigned char *)Malloc(bufflen); data->buff = AlignPtr8(data->mBuff); if(data->buff != data->mBuff) { data->bufflen -= 8; } if(isReader) { data->buffStop = data->buff; } else { data->buffStop = data->buff + data->bufflen; } data->type = streamType; data->eorStop = FALSE; data->skip = FALSE; data->contentLen = 0; data->paddingLen = 0; data->isAnythingWritten = FALSE; data->rawWrite = FALSE; stream->data = data; stream->isReader = isReader; stream->isClosed = FALSE; stream->wasFCloseCalled = FALSE; stream->FCGI_errno = 0; if(isReader) { stream->fillBuffProc = FillBuffProc; stream->emptyBuffProc = NULL; stream->rdNext = data->buff; stream->stop = stream->rdNext; stream->stopUnget = data->buff; stream->wrNext = stream->stop; } else { stream->fillBuffProc = NULL; stream->emptyBuffProc = EmptyBuffProc; stream->wrNext = data->buff + sizeof(FCGI_Header); stream->stop = data->buffStop; stream->stopUnget = NULL; stream->rdNext = stream->stop; } return stream; } /* *---------------------------------------------------------------------- * * FCGX_FreeStream -- * * Frees all storage allocated when *streamPtr was created, * and nulls out *streamPtr. * *---------------------------------------------------------------------- */ void FCGX_FreeStream(FCGX_Stream **streamPtr) { _FCGX_FreeStream(streamPtr, TRUE); } void _FCGX_FreeStream(FCGX_Stream **streamPtr, BOOL freeData) { FCGX_Stream *stream = *streamPtr; FCGX_Stream_Data *data; if(stream == NULL) { return; } data = (FCGX_Stream_Data *)stream->data; if (freeData && data->reqDataPtr) free(data->reqDataPtr); data->reqDataPtr = NULL; free(data->mBuff); free(data); free(stream); *streamPtr = NULL; } /* *---------------------------------------------------------------------- * * SetReaderType -- * * Re-initializes the stream to read data of the specified type. * *---------------------------------------------------------------------- */ static FCGX_Stream *SetReaderType(FCGX_Stream *stream, int streamType) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; ASSERT(stream->isReader); data->type = streamType; data->eorStop = FALSE; data->skip = FALSE; data->contentLen = 0; data->paddingLen = 0; stream->wrNext = stream->stop = stream->rdNext; stream->isClosed = FALSE; return stream; } /* *---------------------------------------------------------------------- * * NewReader -- * * Creates a stream to read streamType records for the given * request. The stream performs OS reads of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewReader(FCGX_Request *reqDataPtr, int bufflen, int streamType) { return NewStream(reqDataPtr, bufflen, TRUE, streamType); } /* *---------------------------------------------------------------------- * * NewWriter -- * * Creates a stream to write streamType FastCGI records, using * the ipcFd and RequestId contained in *reqDataPtr. * The stream performs OS writes of up to bufflen bytes. * *---------------------------------------------------------------------- */ static FCGX_Stream *NewWriter(FCGX_Request *reqDataPtr, int bufflen, int streamType) { return NewStream(reqDataPtr, bufflen, FALSE, streamType); } /* *---------------------------------------------------------------------- * * FCGX_CreateWriter -- * * Creates a stream to write streamType FastCGI records, using * the given ipcFd and request Id. This function is provided * for use by cgi-fcgi. In order to be defensive against misuse, * this function leaks a little storage; cgi-fcgi doesn't care. * *---------------------------------------------------------------------- */ FCGX_Stream *FCGX_CreateWriter( int ipcFd, int requestId, int bufflen, int streamType) { FCGX_Request *reqDataPtr = (FCGX_Request *)Malloc(sizeof(FCGX_Request)); reqDataPtr->ipcFd = ipcFd; reqDataPtr->requestId = requestId; /* * Suppress writing an FCGI_END_REQUEST record. */ reqDataPtr->nWriters = 2; return NewWriter(reqDataPtr, bufflen, streamType); } /* *====================================================================== * Control *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_IsCGI -- * * This routine determines if the process is running as a CGI or * FastCGI process. The distinction is made by determining whether * FCGI_LISTENSOCK_FILENO is a listener ipcFd or the end of a * pipe (ie. standard in). * * Results: * TRUE if the process is a CGI process, FALSE if FastCGI. * *---------------------------------------------------------------------- */ int FCGX_IsCGI(void) { if (isFastCGI != -1) { return !isFastCGI; } if (!libInitialized) { int rc = FCGX_Init(); if (rc) { /* exit() isn't great, but hey */ //exit((rc < 0) ? rc : -rc); return 0; } } isFastCGI = OS_IsFcgi(FCGI_LISTENSOCK_FILENO); return !isFastCGI; } /* *---------------------------------------------------------------------- * * FCGX_Finish -- * * Finishes the current request from the HTTP server. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ void FCGX_Finish(void) { FCGX_Finish_r(&the_request); } /* *---------------------------------------------------------------------- * * FCGX_Finish_r -- * * Finishes the current request from the HTTP server. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ void FCGX_Finish_r(FCGX_Request *reqDataPtr) { int close; if (reqDataPtr == NULL) { return; } close = !reqDataPtr->keepConnection; /* This should probably use a 'status' member instead of 'in' */ if (reqDataPtr->in) { close |= FCGX_FClose(reqDataPtr->err); close |= FCGX_FClose(reqDataPtr->out); close |= FCGX_GetError(reqDataPtr->in); } FCGX_Free(reqDataPtr, close); } void FCGX_Free(FCGX_Request * request, int close) { if (request == NULL) return; _FCGX_FreeStream(&request->in, FALSE); _FCGX_FreeStream(&request->out, FALSE); _FCGX_FreeStream(&request->err, FALSE); FreeParams(&request->paramsPtr); if (close) { OS_IpcClose(request->ipcFd); request->ipcFd = -1; } } int FCGX_OpenSocket(const char *path, int backlog) { int rc = OS_CreateLocalIpcFd(path, backlog, 1); if (rc == FCGI_LISTENSOCK_FILENO && isFastCGI == 0) { /* XXX probably need to call OS_LibInit() again for Win */ isFastCGI = 1; } return rc; } int FCGX_InitRequest(FCGX_Request *request, int sock, int flags) { memset(request, 0, sizeof(FCGX_Request)); /* @@@ Should check that sock is open and listening */ request->listen_sock = sock; /* @@@ Should validate against "known" flags */ request->flags = flags; request->ipcFd = -1; return 0; } /* *---------------------------------------------------------------------- * * FCGX_Init -- * * Initilize the FCGX library. This is called by FCGX_Accept() * but must be called by the user when using FCGX_Accept_r(). * * Results: * 0 for successful call. * *---------------------------------------------------------------------- */ int FCGX_Init(void) { char *p; if (libInitialized) { return 0; } FCGX_InitRequest(&the_request, FCGI_LISTENSOCK_FILENO, 0); if (OS_LibInit(NULL) == -1) { return OS_Errno ? OS_Errno : -9997; } p = getenv("FCGI_WEB_SERVER_ADDRS"); webServerAddressList = p ? StringCopy(p) : NULL; libInitialized = 1; return 0; } /* *---------------------------------------------------------------------- * * FCGX_Accept -- * * Accepts a new request from the HTTP server. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ int FCGX_Accept( FCGX_Stream **in, FCGX_Stream **out, FCGX_Stream **err, FCGX_ParamArray *envp) { int rc; if (! libInitialized) { rc = FCGX_Init(); if (rc) { return rc; } } rc = FCGX_Accept_r(&the_request); *in = the_request.in; *out = the_request.out; *err = the_request.err; *envp = the_request.envp; return rc; } /* *---------------------------------------------------------------------- * * FCGX_Accept_r -- * * Accepts a new request from the HTTP server. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ int FCGX_Accept_r(FCGX_Request *reqDataPtr) { if (!libInitialized) { return -9998; } /* Finish the current request, if any. */ FCGX_Finish_r(reqDataPtr); for (;;) { /* * If a connection isn't open, accept a new connection (blocking). * If an OS error occurs in accepting the connection, * return -1 to the caller, who should exit. */ if (reqDataPtr->ipcFd < 0) { int fail_on_intr = reqDataPtr->flags & FCGI_FAIL_ACCEPT_ON_INTR; reqDataPtr->ipcFd = OS_Accept(reqDataPtr->listen_sock, fail_on_intr, webServerAddressList); if (reqDataPtr->ipcFd < 0) { return (errno > 0) ? (0 - errno) : -9999; } } /* * A connection is open. Read from the connection in order to * get the request's role and environment. If protocol or other * errors occur, close the connection and try again. */ reqDataPtr->isBeginProcessed = FALSE; reqDataPtr->in = NewReader(reqDataPtr, 8192, 0); FillBuffProc(reqDataPtr->in); if(!reqDataPtr->isBeginProcessed) { goto TryAgain; } { char *roleStr; switch(reqDataPtr->role) { case FCGI_RESPONDER: roleStr = "FCGI_ROLE=RESPONDER"; break; case FCGI_AUTHORIZER: roleStr = "FCGI_ROLE=AUTHORIZER"; break; case FCGI_FILTER: roleStr = "FCGI_ROLE=FILTER"; break; default: goto TryAgain; } reqDataPtr->paramsPtr = NewParams(30); PutParam(reqDataPtr->paramsPtr, StringCopy(roleStr)); } SetReaderType(reqDataPtr->in, FCGI_PARAMS); if(ReadParams(reqDataPtr->paramsPtr, reqDataPtr->in) >= 0) { /* * Finished reading the environment. No errors occurred, so * leave the connection-retry loop. */ break; } /* * Close the connection and try again. */ TryAgain: FCGX_Free(reqDataPtr, 1); } /* for (;;) */ /* * Build the remaining data structures representing the new * request and return successfully to the caller. */ SetReaderType(reqDataPtr->in, FCGI_STDIN); reqDataPtr->out = NewWriter(reqDataPtr, 8192, FCGI_STDOUT); reqDataPtr->err = NewWriter(reqDataPtr, 512, FCGI_STDERR); reqDataPtr->nWriters = 2; reqDataPtr->envp = reqDataPtr->paramsPtr->vec; return 0; } /* *---------------------------------------------------------------------- * * FCGX_StartFilterData -- * * stream is an input stream for a FCGI_FILTER request. * stream is positioned at EOF on FCGI_STDIN. * Repositions stream to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF) sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ int FCGX_StartFilterData(FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; if(data->reqDataPtr->role != FCGI_FILTER || !stream->isReader || !stream->isClosed || data->type != FCGI_STDIN) { SetError(stream, FCGX_CALL_SEQ_ERROR); return -1; } SetReaderType(stream, FCGI_DATA); return 0; } /* *---------------------------------------------------------------------- * * FCGX_SetExitStatus -- * * Sets the exit status for stream's request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * SetExitStatus several times during a request; the last call * before the request ends determines the value. * *---------------------------------------------------------------------- */ void FCGX_SetExitStatus(int status, FCGX_Stream *stream) { FCGX_Stream_Data *data = (FCGX_Stream_Data *)stream->data; data->reqDataPtr->appStatus = status; } Index: php4/sapi/cgi/libfcgi/os_unix.c +++ php4/sapi/cgi/libfcgi/os_unix.c /* * os_unix.c -- * * Description of file. * * * Copyright (c) 1995 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com */ #ifndef lint static const char rcsid[] = "$Id: os_unix.c,v 1.1 2002/03/10 21:39:28 shane Exp $"; #endif /* not lint */ #include "fcgi_config.h" #include <sys/types.h> #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif #include <arpa/inet.h> #include <assert.h> #include <errno.h> #include <fcntl.h> /* for fcntl */ #include <math.h> #include <memory.h> /* for memchr() */ #include <netinet/tcp.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/time.h> #include <sys/un.h> #include <signal.h> #ifdef HAVE_NETDB_H #include <netdb.h> #endif #ifdef HAVE_SYS_SOCKET_H #include <sys/socket.h> /* for getpeername */ #endif #ifdef HAVE_UNISTD_H #include <unistd.h> #endif #include "fastcgi.h" #include "fcgimisc.h" #include "fcgios.h" #ifndef INADDR_NONE #define INADDR_NONE ((unsigned long) -1) #endif /* * This structure holds an entry for each oustanding async I/O operation. */ typedef struct { OS_AsyncProc procPtr; /* callout completion procedure */ ClientData clientData; /* caller private data */ int fd; int len; int offset; void *buf; int inUse; } AioInfo; /* * Entries in the async I/O table are allocated 2 per file descriptor. * * Read Entry Index = fd * 2 * Write Entry Index = (fd * 2) + 1 */ #define AIO_RD_IX(fd) (fd * 2) #define AIO_WR_IX(fd) ((fd * 2) + 1) static int asyncIoInUse = FALSE; static int asyncIoTableSize = 16; static AioInfo *asyncIoTable = NULL; static int libInitialized = FALSE; static fd_set readFdSet; static fd_set writeFdSet; static fd_set readFdSetPost; static int numRdPosted = 0; static fd_set writeFdSetPost; static int numWrPosted = 0; static int volatile maxFd = -1; static int shutdownPending = FALSE; static int shutdownNow = FALSE; void OS_ShutdownPending() { shutdownPending = TRUE; } static void OS_Sigusr1Handler(int signo) { OS_ShutdownPending(); } static void OS_SigpipeHandler(int signo) { ; } static void installSignalHandler(int signo, const struct sigaction * act, int force) { struct sigaction sa; sigaction(signo, NULL, &sa); if (force || sa.sa_handler == SIG_DFL) { sigaction(signo, act, NULL); } } static void OS_InstallSignalHandlers(int force) { struct sigaction sa; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = OS_SigpipeHandler; installSignalHandler(SIGPIPE, &sa, force); sa.sa_handler = OS_Sigusr1Handler; installSignalHandler(SIGUSR1, &sa, force); } /* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * NOTE: This function is really only needed for application * asynchronous I/O. It will most likely change in the * future to setup the multi-threaded environment. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Async I/O table allocated and initialized. * *-------------------------------------------------------------- */ int OS_LibInit(int stdioFds[3]) { if(libInitialized) return 0; asyncIoTable = (AioInfo *)malloc(asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; return -1; } memset((char *) asyncIoTable, 0, asyncIoTableSize * sizeof(AioInfo)); FD_ZERO(&readFdSet); FD_ZERO(&writeFdSet); FD_ZERO(&readFdSetPost); FD_ZERO(&writeFdSetPost); OS_InstallSignalHandlers(FALSE); libInitialized = TRUE; return 0; } /* *-------------------------------------------------------------- * * OS_LibShutdown -- * * Shutdown the OS library. * * Results: * None. * * Side effects: * Memory freed, fds closed. * *-------------------------------------------------------------- */ void OS_LibShutdown() { if(!libInitialized) return; free(asyncIoTable); asyncIoTable = NULL; libInitialized = FALSE; return; } /* *---------------------------------------------------------------------- * * OS_BuildSockAddrUn -- * * Using the pathname bindPath, fill in the sockaddr_un structure * *servAddrPtr and the length of this structure *servAddrLen. * * The format of the sockaddr_un structure changed incompatibly in * 4.3BSD Reno. Digital UNIX supports both formats, other systems * support one or the other. * * Results: * 0 for normal return, -1 for failure (bindPath too long). * *---------------------------------------------------------------------- */ static int OS_BuildSockAddrUn(const char *bindPath, struct sockaddr_un *servAddrPtr, int *servAddrLen) { int bindPathLen = strlen(bindPath); #ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ if(bindPathLen >= sizeof(servAddrPtr->sun_path)) { return -1; } #else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ if(bindPathLen > sizeof(servAddrPtr->sun_path)) { return -1; } #endif memset((char *) servAddrPtr, 0, sizeof(*servAddrPtr)); servAddrPtr->sun_family = AF_UNIX; memcpy(servAddrPtr->sun_path, bindPath, bindPathLen); #ifdef HAVE_SOCKADDR_UN_SUN_LEN /* 4.3BSD Reno and later: BSDI, DEC */ *servAddrLen = sizeof(servAddrPtr->sun_len) + sizeof(servAddrPtr->sun_family) + bindPathLen + 1; servAddrPtr->sun_len = *servAddrLen; #else /* 4.3 BSD Tahoe: Solaris, HPUX, DEC, ... */ *servAddrLen = sizeof(servAddrPtr->sun_family) + bindPathLen; #endif return 0; } union SockAddrUnion { struct sockaddr_un unixVariant; struct sockaddr_in inetVariant; }; /* * OS_CreateLocalIpcFd -- * * This procedure is responsible for creating the listener socket * on Unix for local process communication. It will create a * domain socket or a TCP/IP socket bound to "localhost" and return * a file descriptor to it to the caller. * * Results: * Listener socket created. This call returns either a valid * file descriptor or -1 on error. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_CreateLocalIpcFd(const char *bindPath, int backlog) { int listenSock, servLen; union SockAddrUnion sa; int tcp = FALSE; unsigned long tcp_ia = 0; char *tp; short port = 0; char host[MAXPATHLEN]; strcpy(host, bindPath); if((tp = strchr(host, ':')) != 0) { *tp++ = 0; if((port = atoi(tp)) == 0) { *--tp = ':'; } else { tcp = TRUE; } } if(tcp) { if (!*host || !strcmp(host,"*")) { tcp_ia = htonl(INADDR_ANY); } else { tcp_ia = inet_addr(host); if (tcp_ia == INADDR_NONE) { struct hostent * hep; hep = gethostbyname(host); if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) { fprintf(stderr, "Cannot resolve host name %s -- exiting!\n", host); exit(1); } if (hep->h_addr_list[1]) { fprintf(stderr, "Host %s has multiple addresses ---\n", host); fprintf(stderr, "you must choose one explicitly!!!\n"); exit(1); } tcp_ia = ((struct in_addr *) (hep->h_addr))->s_addr; } } } if(tcp) { listenSock = socket(AF_INET, SOCK_STREAM, 0); if(listenSock >= 0) { int flag = 1; if(setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char *) &flag, sizeof(flag)) < 0) { fprintf(stderr, "Can't set SO_REUSEADDR.\n"); exit(1001); } } } else { listenSock = socket(AF_UNIX, SOCK_STREAM, 0); } if(listenSock < 0) { return -1; } /* * Bind the listening socket. */ if(tcp) { memset((char *) &sa.inetVariant, 0, sizeof(sa.inetVariant)); sa.inetVariant.sin_family = AF_INET; sa.inetVariant.sin_addr.s_addr = tcp_ia; sa.inetVariant.sin_port = htons(port); servLen = sizeof(sa.inetVariant); } else { unlink(bindPath); if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) { fprintf(stderr, "Listening socket's path name is too long.\n"); exit(1000); } } if(bind(listenSock, (struct sockaddr *) &sa.unixVariant, servLen) < 0 || listen(listenSock, backlog) < 0) { perror("bind/listen"); exit(errno); } return listenSock; } /* *---------------------------------------------------------------------- * * OS_FcgiConnect -- * * Create the socket and connect to the remote application if * possible. * * This was lifted from the cgi-fcgi application and was abstracted * out because Windows NT does not have a domain socket and must * use a named pipe which has a different API altogether. * * Results: * -1 if fail or a valid file descriptor if connection succeeds. * * Side effects: * Remote connection established. * *---------------------------------------------------------------------- */ int OS_FcgiConnect(char *bindPath) { union SockAddrUnion sa; int servLen, resultSock; int connectStatus; char *tp; char host[MAXPATHLEN]; short port = 0; int tcp = FALSE; strcpy(host, bindPath); if((tp = strchr(host, ':')) != 0) { *tp++ = 0; if((port = atoi(tp)) == 0) { *--tp = ':'; } else { tcp = TRUE; } } if(tcp == TRUE) { struct hostent *hp; if((hp = gethostbyname((*host ? host : "localhost"))) == NULL) { fprintf(stderr, "Unknown host: %s\n", bindPath); exit(1000); } sa.inetVariant.sin_family = AF_INET; memcpy(&sa.inetVariant.sin_addr, hp->h_addr, hp->h_length); sa.inetVariant.sin_port = htons(port); servLen = sizeof(sa.inetVariant); resultSock = socket(AF_INET, SOCK_STREAM, 0); } else { if(OS_BuildSockAddrUn(bindPath, &sa.unixVariant, &servLen)) { fprintf(stderr, "Listening socket's path name is too long.\n"); exit(1000); } resultSock = socket(AF_UNIX, SOCK_STREAM, 0); } ASSERT(resultSock >= 0); connectStatus = connect(resultSock, (struct sockaddr *) &sa.unixVariant, servLen); if(connectStatus >= 0) { return resultSock; } else { /* * Most likely (errno == ENOENT || errno == ECONNREFUSED) * and no FCGI application server is running. */ close(resultSock); return -1; } } /* *-------------------------------------------------------------- * * OS_Read -- * * Pass through to the unix read function. * * Results: * Returns number of byes read, 0, or -1 failure: errno * contains actual error. * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Read(int fd, char * buf, size_t len) { if (shutdownNow) return -1; return(read(fd, buf, len)); } /* *-------------------------------------------------------------- * * OS_Write -- * * Pass through to unix write function. * * Results: * Returns number of byes read, 0, or -1 failure: errno * contains actual error. * * Side effects: * none. * *-------------------------------------------------------------- */ int OS_Write(int fd, char * buf, size_t len) { if (shutdownNow) return -1; return(write(fd, buf, len)); } /* *---------------------------------------------------------------------- * * OS_SpawnChild -- * * Spawns a new FastCGI listener process. * * Results: * 0 if success, -1 if error. * * Side effects: * Child process spawned. * *---------------------------------------------------------------------- */ int OS_SpawnChild(char *appPath, int listenFd) { int forkResult; forkResult = fork(); if(forkResult < 0) { exit(errno); } if(forkResult == 0) { /* * Close STDIN unconditionally. It's used by the parent * process for CGI communication. The FastCGI applciation * will be replacing this with the FastCGI listenFd IF * STDIN_FILENO is the same as FCGI_LISTENSOCK_FILENO * (which it is on Unix). Regardless, STDIN, STDOUT, and * STDERR will be closed as the FastCGI process uses a * multiplexed socket in their place. */ close(STDIN_FILENO); /* * If the listenFd is already the value of FCGI_LISTENSOCK_FILENO * we're set. If not, change it so the child knows where to * get the listen socket from. */ if(listenFd != FCGI_LISTENSOCK_FILENO) { dup2(listenFd, FCGI_LISTENSOCK_FILENO); close(listenFd); } close(STDOUT_FILENO); close(STDERR_FILENO); /* * We're a child. Exec the application. * * XXX: entire environment passes through */ execl(appPath, appPath, NULL); /* * XXX: Can't do this as we've already closed STDERR!!! * * perror("exec"); */ exit(errno); } return 0; } /* *-------------------------------------------------------------- * * OS_AsyncReadStdin -- * * This initiates an asynchronous read on the standard * input handle. * * The abstraction is necessary because Windows NT does not * have a clean way of "select"ing a file descriptor for * I/O. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous bit is set in the readfd variable and * request is enqueued. * *-------------------------------------------------------------- */ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(STDIN_FILENO); asyncIoInUse = TRUE; ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = STDIN_FILENO; asyncIoTable[index].len = len; asyncIoTable[index].offset = 0; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(STDIN_FILENO, &readFdSet); if(STDIN_FILENO > maxFd) maxFd = STDIN_FILENO; return 0; } static void GrowAsyncTable(void) { int oldTableSize = asyncIoTableSize; asyncIoTableSize = asyncIoTableSize * 2; asyncIoTable = (AioInfo *)realloc(asyncIoTable, asyncIoTableSize * sizeof(AioInfo)); if(asyncIoTable == NULL) { errno = ENOMEM; exit(errno); } memset((char *) &asyncIoTable[oldTableSize], 0, oldTableSize * sizeof(AioInfo)); } /* *-------------------------------------------------------------- * * OS_AsyncRead -- * * This initiates an asynchronous read on the file * handle which may be a socket or named pipe. * * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the ReadFile may * return data if it is cached) but do all completion * processing in OS_Select when we get the io completion * port done notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_RD_IX(fd); ASSERT(asyncIoTable != NULL); asyncIoInUse = TRUE; if(fd > maxFd) maxFd = fd; if(index >= asyncIoTableSize) { GrowAsyncTable(); } ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = fd; asyncIoTable[index].len = len; asyncIoTable[index].offset = offset; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(fd, &readFdSet); return 0; } /* *-------------------------------------------------------------- * * OS_AsyncWrite -- * * This initiates an asynchronous write on the "fake" file * descriptor (which may be a file, socket, or named pipe). * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the WriteFile generally * completes immediately) but do all completion processing * in OS_DoIo when we get the io completion port done * notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { int index = AIO_WR_IX(fd); asyncIoInUse = TRUE; if(fd > maxFd) maxFd = fd; if(index >= asyncIoTableSize) { GrowAsyncTable(); } ASSERT(asyncIoTable[index].inUse == 0); asyncIoTable[index].procPtr = procPtr; asyncIoTable[index].clientData = clientData; asyncIoTable[index].fd = fd; asyncIoTable[index].len = len; asyncIoTable[index].offset = offset; asyncIoTable[index].buf = buf; asyncIoTable[index].inUse = 1; FD_SET(fd, &writeFdSet); return 0; } /* *-------------------------------------------------------------- * * OS_Close -- * * Closes the descriptor. This is a pass through to the * Unix close. * * Results: * 0 for success, -1 on failure * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Close(int fd) { if (fd == -1) return 0; if (asyncIoInUse) { int index = AIO_RD_IX(fd); FD_CLR(fd, &readFdSet); FD_CLR(fd, &readFdSetPost); if (asyncIoTable[index].inUse != 0) { asyncIoTable[index].inUse = 0; } FD_CLR(fd, &writeFdSet); FD_CLR(fd, &writeFdSetPost); index = AIO_WR_IX(fd); if (asyncIoTable[index].inUse != 0) { asyncIoTable[index].inUse = 0; } if (maxFd == fd) { maxFd--; } } return close(fd); } /* *-------------------------------------------------------------- * * OS_CloseRead -- * * Cancel outstanding asynchronous reads and prevent subsequent * reads from completing. * * Results: * Socket or file is shutdown. Return values mimic Unix shutdown: * 0 success, -1 failure * *-------------------------------------------------------------- */ int OS_CloseRead(int fd) { if(asyncIoTable[AIO_RD_IX(fd)].inUse != 0) { asyncIoTable[AIO_RD_IX(fd)].inUse = 0; FD_CLR(fd, &readFdSet); } return shutdown(fd, 0); } /* *-------------------------------------------------------------- * * OS_DoIo -- * * This function was formerly OS_Select. It's purpose is * to pull I/O completion events off the queue and dispatch * them to the appropriate place. * * Results: * Returns 0. * * Side effects: * Handlers are called. * *-------------------------------------------------------------- */ int OS_DoIo(struct timeval *tmo) { int fd, len, selectStatus; OS_AsyncProc procPtr; ClientData clientData; AioInfo *aioPtr; fd_set readFdSetCpy; fd_set writeFdSetCpy; asyncIoInUse = TRUE; FD_ZERO(&readFdSetCpy); FD_ZERO(&writeFdSetCpy); for(fd = 0; fd <= maxFd; fd++) { if(FD_ISSET(fd, &readFdSet)) { FD_SET(fd, &readFdSetCpy); } if(FD_ISSET(fd, &writeFdSet)) { FD_SET(fd, &writeFdSetCpy); } } /* * If there were no completed events from a prior call, see if there's * any work to do. */ if(numRdPosted == 0 && numWrPosted == 0) { selectStatus = select((maxFd+1), &readFdSetCpy, &writeFdSetCpy, NULL, tmo); if(selectStatus < 0) { exit(errno); } for(fd = 0; fd <= maxFd; fd++) { /* * Build up a list of completed events. We'll work off of * this list as opposed to looping through the read and write * fd sets since they can be affected by a callbacl routine. */ if(FD_ISSET(fd, &readFdSetCpy)) { numRdPosted++; FD_SET(fd, &readFdSetPost); FD_CLR(fd, &readFdSet); } if(FD_ISSET(fd, &writeFdSetCpy)) { numWrPosted++; FD_SET(fd, &writeFdSetPost); FD_CLR(fd, &writeFdSet); } } } if(numRdPosted == 0 && numWrPosted == 0) return 0; for(fd = 0; fd <= maxFd; fd++) { /* * Do reads and dispatch callback. */ if(FD_ISSET(fd, &readFdSetPost) && asyncIoTable[AIO_RD_IX(fd)].inUse) { numRdPosted--; FD_CLR(fd, &readFdSetPost); aioPtr = &asyncIoTable[AIO_RD_IX(fd)]; len = read(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; aioPtr->procPtr = NULL; clientData = aioPtr->clientData; aioPtr->inUse = 0; (*procPtr)(clientData, len); } /* * Do writes and dispatch callback. */ if(FD_ISSET(fd, &writeFdSetPost) && asyncIoTable[AIO_WR_IX(fd)].inUse) { numWrPosted--; FD_CLR(fd, &writeFdSetPost); aioPtr = &asyncIoTable[AIO_WR_IX(fd)]; len = write(aioPtr->fd, aioPtr->buf, aioPtr->len); procPtr = aioPtr->procPtr; aioPtr->procPtr = NULL; clientData = aioPtr->clientData; aioPtr->inUse = 0; (*procPtr)(clientData, len); } } return 0; } /* * Not all systems have strdup(). * @@@ autoconf should determine whether or not this is needed, but for now.. */ static char * str_dup(const char * str) { char * sdup = (char *) malloc(strlen(str) + 1); if (sdup) strcpy(sdup, str); return sdup; } /* *---------------------------------------------------------------------- * * ClientAddrOK -- * * Checks if a client address is in a list of allowed addresses * * Results: * TRUE if address list is empty or client address is present * in the list, FALSE otherwise. * *---------------------------------------------------------------------- */ static int ClientAddrOK(struct sockaddr_in *saPtr, const char *clientList) { int result = FALSE; char *clientListCopy, *cur, *next; if (clientList == NULL || *clientList == '\0') { return TRUE; } clientListCopy = str_dup(clientList); for (cur = clientListCopy; cur != NULL; cur = next) { next = strchr(cur, ','); if (next != NULL) { *next++ = '\0'; } if (inet_addr(cur) == saPtr->sin_addr.s_addr) { result = TRUE; break; } } free(clientListCopy); return result; } /* *---------------------------------------------------------------------- * * AcquireLock -- * * On platforms that implement concurrent calls to accept * on a shared listening ipcFd, returns 0. On other platforms, * acquires an exclusive lock across all processes sharing a * listening ipcFd, blocking until the lock has been acquired. * * Results: * 0 for successful call, -1 in case of system error (fatal). * * Side effects: * This process now has the exclusive lock. * *---------------------------------------------------------------------- */ static int AcquireLock(int sock, int fail_on_intr) { #ifdef USE_LOCKING do { struct flock lock; lock.l_type = F_WRLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(sock, F_SETLKW, &lock) != -1) return 0; } while (errno == EINTR && ! fail_on_intr && ! shutdownPending); return -1; #else return 0; #endif } /* *---------------------------------------------------------------------- * * ReleaseLock -- * * On platforms that implement concurrent calls to accept * on a shared listening ipcFd, does nothing. On other platforms, * releases an exclusive lock acquired by AcquireLock. * * Results: * 0 for successful call, -1 in case of system error (fatal). * * Side effects: * This process no longer holds the lock. * *---------------------------------------------------------------------- */ static int ReleaseLock(int sock) { #ifdef USE_LOCKING do { struct flock lock; lock.l_type = F_UNLCK; lock.l_start = 0; lock.l_whence = SEEK_SET; lock.l_len = 0; if (fcntl(sock, F_SETLK, &lock) != -1) return 0; } while (errno == EINTR); return -1; #else return 0; #endif } /********************************************************************** * Determine if the errno resulting from a failed accept() warrants a * retry or exit(). Based on Apache's http_main.c accept() handling * and Stevens' Unix Network Programming Vol 1, 2nd Ed, para. 15.6. */ static int is_reasonable_accept_errno (const int error) { switch (error) { #ifdef EPROTO /* EPROTO on certain older kernels really means ECONNABORTED, so * we need to ignore it for them. See discussion in new-httpd * archives nh.9701 search for EPROTO. Also see nh.9603, search * for EPROTO: There is potentially a bug in Solaris 2.x x<6, and * other boxes that implement tcp sockets in userland (i.e. on top of * STREAMS). On these systems, EPROTO can actually result in a fatal * loop. See PR#981 for example. It's hard to handle both uses of * EPROTO. */ case EPROTO: #endif #ifdef ECONNABORTED case ECONNABORTED: #endif /* Linux generates the rest of these, other tcp stacks (i.e. * bsd) tend to hide them behind getsockopt() interfaces. They * occur when the net goes sour or the client disconnects after the * three-way handshake has been done in the kernel but before * userland has picked up the socket. */ #ifdef ECONNRESET case ECONNRESET: #endif #ifdef ETIMEDOUT case ETIMEDOUT: #endif #ifdef EHOSTUNREACH case EHOSTUNREACH: #endif #ifdef ENETUNREACH case ENETUNREACH: #endif return 1; default: return 0; } } /********************************************************************** * This works around a problem on Linux 2.0.x and SCO Unixware (maybe * others?). When a connect() is made to a Unix Domain socket, but its * not accept()ed before the web server gets impatient and close()s, an * accept() results in a valid file descriptor, but no data to read. * This causes a block on the first read() - which never returns! * * Another approach to this is to write() to the socket to provoke a * SIGPIPE, but this is a pain because of the FastCGI protocol, the fact * that whatever is written has to be universally ignored by all FastCGI * web servers, and a SIGPIPE handler has to be installed which returns * (or SIGPIPE is ignored). * * READABLE_UNIX_FD_DROP_DEAD_TIMEVAL = 2,0 by default. * * Making it shorter is probably safe, but I'll leave that to you. Making * it 0,0 doesn't work reliably. The shorter you can reliably make it, * the faster your application will be able to recover (waiting 2 seconds * may _cause_ the problem when there is a very high demand). At any rate, * this is better than perma-blocking. */ static int is_af_unix_keeper(const int fd) { struct timeval tval = { READABLE_UNIX_FD_DROP_DEAD_TIMEVAL }; fd_set read_fds; FD_ZERO(&read_fds); FD_SET(fd, &read_fds); return select(fd + 1, &read_fds, NULL, NULL, &tval) >= 0 && FD_ISSET(fd, &read_fds); } /* *---------------------------------------------------------------------- * * OS_Accept -- * * Accepts a new FastCGI connection. This routine knows whether * we're dealing with TCP based sockets or NT Named Pipes for IPC. * * Results: * -1 if the operation fails, otherwise this is a valid IPC fd. * * Side effects: * New IPC connection is accepted. * *---------------------------------------------------------------------- */ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { int socket = -1; union { struct sockaddr_un un; struct sockaddr_in in; } sa; for (;;) { if (AcquireLock(listen_sock, fail_on_intr)) return -1; for (;;) { do { #ifdef HAVE_SOCKLEN socklen_t len = sizeof(sa); #else int len = sizeof(sa); #endif if (shutdownPending) break; /* There's a window here */ socket = accept(listen_sock, (struct sockaddr *)&sa, &len); } while (socket < 0 && errno == EINTR && ! fail_on_intr && ! shutdownPending); if (socket < 0) { if (shutdownPending || ! is_reasonable_accept_errno(errno)) { int errnoSave = errno; ReleaseLock(listen_sock); if (! shutdownPending) { errno = errnoSave; } return (-1); } errno = 0; } else { /* socket >= 0 */ int set = 1; if (sa.in.sin_family != AF_INET) break; #ifdef TCP_NODELAY /* No replies to outgoing data, so disable Nagle */ setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set)); #endif /* Check that the client IP address is approved */ if (ClientAddrOK(&sa.in, webServerAddrs)) break; close(socket); } /* socket >= 0 */ } /* for(;;) */ if (ReleaseLock(listen_sock)) return (-1); if (sa.in.sin_family != AF_UNIX || is_af_unix_keeper(socket)) break; close(socket); } /* while(1) - lock */ return (socket); } /* *---------------------------------------------------------------------- * * OS_IpcClose * * OS IPC routine to close an IPC connection. * * Results: * * * Side effects: * IPC connection is closed. * *---------------------------------------------------------------------- */ int OS_IpcClose(int ipcFd) { return OS_Close(ipcFd); } /* *---------------------------------------------------------------------- * * OS_IsFcgi -- * * Determines whether this process is a FastCGI process or not. * * Results: * Returns 1 if FastCGI, 0 if not. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_IsFcgi(int sock) { union { struct sockaddr_in in; struct sockaddr_un un; } sa; #ifdef HAVE_SOCKLEN socklen_t len = sizeof(sa); #else int len = sizeof(sa); #endif errno = 0; if (getpeername(sock, (struct sockaddr *)&sa, &len) != 0 && errno == ENOTCONN) { return TRUE; } else { return FALSE; } } /* *---------------------------------------------------------------------- * * OS_SetFlags -- * * Sets selected flag bits in an open file descriptor. * *---------------------------------------------------------------------- */ void OS_SetFlags(int fd, int flags) { int val; if((val = fcntl(fd, F_GETFL, 0)) < 0) { exit(errno); } val |= flags; if(fcntl(fd, F_SETFL, val) < 0) { exit(errno); } } Index: php4/sapi/cgi/libfcgi/os_win32.c +++ php4/sapi/cgi/libfcgi/os_win32.c /* * os_win32.c -- * * * Copyright (c) 1995 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com * * (Special thanks to Karen and Bill. They made my job much easier and * significantly more enjoyable.) */ #ifndef lint static const char rcsid[] = "$Id: os_win32.c,v 1.1 2002/03/10 21:39:28 shane Exp $"; #endif /* not lint */ #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <stdlib.h> #include <assert.h> #include <stdio.h> #include <sys/timeb.h> #define DLLAPI __declspec(dllexport) #include "fcgimisc.h" #include "fcgios.h" #define WIN32_OPEN_MAX 128 /* XXX: Small hack */ /* * millisecs to wait for a client connection before checking the * shutdown flag (then go back to waiting for a connection, etc). */ #define ACCEPT_TIMEOUT 1000 #define LOCALHOST "localhost" static HANDLE hIoCompPort = INVALID_HANDLE_VALUE; static HANDLE hStdinCompPort = INVALID_HANDLE_VALUE; static HANDLE hStdinThread = INVALID_HANDLE_VALUE; static HANDLE stdioHandles[3] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; static HANDLE acceptMutex = INVALID_HANDLE_VALUE; static BOOLEAN shutdownPending = FALSE; static BOOLEAN shutdownNow = FALSE; /* * An enumeration of the file types * supported by the FD_TABLE structure. * * XXX: Not all currently supported. This allows for future * functionality. */ typedef enum { FD_UNUSED, FD_FILE_SYNC, FD_FILE_ASYNC, FD_SOCKET_SYNC, FD_SOCKET_ASYNC, FD_PIPE_SYNC, FD_PIPE_ASYNC } FILE_TYPE; typedef union { HANDLE fileHandle; SOCKET sock; unsigned int value; } DESCRIPTOR; /* * Structure used to map file handle and socket handle * values into values that can be used to create unix-like * select bitmaps, read/write for both sockets/files. */ struct FD_TABLE { DESCRIPTOR fid; FILE_TYPE type; char *path; DWORD Errno; unsigned long instance; int status; int offset; /* only valid for async file writes */ LPDWORD offsetHighPtr; /* pointers to offset high and low words */ LPDWORD offsetLowPtr; /* only valid for async file writes (logs) */ HANDLE hMapMutex; /* mutex handle for multi-proc offset update */ LPVOID ovList; /* List of associated OVERLAPPED_REQUESTs */ }; /* * XXX Note there is no dyanmic sizing of this table, so if the * number of open file descriptors exceeds WIN32_OPEN_MAX the * app will blow up. */ static struct FD_TABLE fdTable[WIN32_OPEN_MAX]; static CRITICAL_SECTION fdTableCritical; struct OVERLAPPED_REQUEST { OVERLAPPED overlapped; unsigned long instance; /* file instance (won't match after a close) */ OS_AsyncProc procPtr; /* callback routine */ ClientData clientData; /* callback argument */ ClientData clientData1; /* additional clientData */ }; typedef struct OVERLAPPED_REQUEST *POVERLAPPED_REQUEST; static const char *bindPathPrefix = "\\\\.\\pipe\\FastCGI\\"; static FILE_TYPE listenType = FD_UNUSED; // XXX This should be a DESCRIPTOR static HANDLE hListen = INVALID_HANDLE_VALUE; static OVERLAPPED listenOverlapped; static BOOLEAN libInitialized = FALSE; /* *-------------------------------------------------------------- * * Win32NewDescriptor -- * * Set up for I/O descriptor masquerading. * * Results: * Returns "fake id" which masquerades as a UNIX-style "small * non-negative integer" file/socket descriptor. * Win32_* routine below will "do the right thing" based on the * descriptor's actual type. -1 indicates failure. * * Side effects: * Entry in fdTable is reserved to represent the socket/file. * *-------------------------------------------------------------- */ static int Win32NewDescriptor(FILE_TYPE type, int fd, int desiredFd) { int index = -1; EnterCriticalSection(&fdTableCritical); /* * If desiredFd is set, try to get this entry (this is used for * mapping stdio handles). Otherwise try to get the fd entry. * If this is not available, find a the first empty slot. . */ if (desiredFd >= 0 && desiredFd < WIN32_OPEN_MAX) { if (fdTable[desiredFd].type == FD_UNUSED) { index = desiredFd; } } else if (fd > 0) { if (fd < WIN32_OPEN_MAX && fdTable[fd].type == FD_UNUSED) { index = fd; } else { int i; for (i = 1; i < WIN32_OPEN_MAX; ++i) { if (fdTable[i].type == FD_UNUSED) { index = i; break; } } } } if (index != -1) { fdTable[index].fid.value = fd; fdTable[index].type = type; fdTable[index].path = NULL; fdTable[index].Errno = NO_ERROR; fdTable[index].status = 0; fdTable[index].offset = -1; fdTable[index].offsetHighPtr = fdTable[index].offsetLowPtr = NULL; fdTable[index].hMapMutex = NULL; fdTable[index].ovList = NULL; } LeaveCriticalSection(&fdTableCritical); return index; } /* *-------------------------------------------------------------- * * StdinThread-- * * This thread performs I/O on stadard input. It is needed * because you can't guarantee that all applications will * create standard input with sufficient access to perform * asynchronous I/O. Since we don't want to block the app * reading from stdin we make it look like it's using I/O * completion ports to perform async I/O. * * Results: * Data is read from stdin and posted to the io completion * port. * * Side effects: * None. * *-------------------------------------------------------------- */ static void StdinThread(LPDWORD startup){ int doIo = TRUE; unsigned long fd; unsigned long bytesRead; POVERLAPPED_REQUEST pOv; // Touch the arg to prevent warning startup = NULL; while(doIo) { /* * Block until a request to read from stdin comes in or a * request to terminate the thread arrives (fd = -1). */ if (!GetQueuedCompletionStatus(hStdinCompPort, &bytesRead, &fd, (LPOVERLAPPED *)&pOv, (DWORD)-1) && !pOv) { doIo = 0; break; } ASSERT((fd == STDIN_FILENO) || (fd == -1)); if(fd == -1) { doIo = 0; break; } ASSERT(pOv->clientData1 != NULL); if(ReadFile(stdioHandles[STDIN_FILENO], pOv->clientData1, bytesRead, &bytesRead, NULL)) { PostQueuedCompletionStatus(hIoCompPort, bytesRead, STDIN_FILENO, (LPOVERLAPPED)pOv); } else { doIo = 0; break; } } ExitThread(0); } void OS_ShutdownPending(void) { shutdownPending = TRUE; } /* XXX Need a shutdown now event */ static DWORD WINAPI ShutdownRequestThread(LPVOID arg) { HANDLE shutdownEvent = (HANDLE) arg; if (WaitForSingleObject(shutdownEvent, INFINITE) == WAIT_FAILED) { // Assuming it will happen again, all we can do is exit the thread return 1; } else { // "Simple reads and writes to properly-aligned 32-bit variables are atomic" shutdownPending = TRUE; // Before an accept() is entered the shutdownPending flag is checked. // If set, OS_Accept() will return -1. If not, it waits // on a connection request for one second, checks the flag, & repeats. // Only one process/thread is allowed to do this at time by // wrapping the accept() with mutex. return 0; } } /* *-------------------------------------------------------------- * * OS_LibInit -- * * Set up the OS library for use. * * Results: * Returns 0 if success, -1 if not. * * Side effects: * Sockets initialized, pseudo file descriptors setup, etc. * *-------------------------------------------------------------- */ int OS_LibInit(int stdioFds[3]) { WORD wVersion; WSADATA wsaData; int err; int fakeFd; DWORD threadId; char *cLenPtr = NULL; char *val = NULL; if(libInitialized) return 0; InitializeCriticalSection(&fdTableCritical); /* * Initialize windows sockets library. */ wVersion = MAKEWORD(2,0); err = WSAStartup( wVersion, &wsaData ); if (err) { fprintf(stderr, "Error starting Windows Sockets. Error: %d", WSAGetLastError()); //exit(111); return -1; } /* * Create the I/O completion port to be used for our I/O queue. */ if (hIoCompPort == INVALID_HANDLE_VALUE) { hIoCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hIoCompPort == INVALID_HANDLE_VALUE) { printf("<H2>OS_LibInit Failed CreateIoCompletionPort! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * If a shutdown event is in the env, save it (I don't see any to * remove it from the environment out from under the application). * Spawn a thread to wait on the shutdown request. */ val = getenv(SHUTDOWN_EVENT_NAME); if (val != NULL) { HANDLE shutdownEvent = (HANDLE) atoi(val); if (! CreateThread(NULL, 0, ShutdownRequestThread, shutdownEvent, 0, NULL)) { return -1; } } /* * If an accept mutex is in the env, save it and remove it. */ val = getenv(MUTEX_VARNAME); if (val != NULL) { acceptMutex = (HANDLE) atoi(val); } /* * Determine if this library is being used to listen for FastCGI * connections. This is communicated by STDIN containing a * valid handle to a listener object. In this case, both the * "stdout" and "stderr" handles will be INVALID (ie. closed) by * the starting process. * * The trick is determining if this is a pipe or a socket... * * XXX: Add the async accept test to determine socket or handle to a * pipe!!! */ if((GetStdHandle(STD_OUTPUT_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_ERROR_HANDLE) == INVALID_HANDLE_VALUE) && (GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE) ) { DWORD pipeMode = PIPE_READMODE_BYTE | PIPE_WAIT; HANDLE oldStdIn = GetStdHandle(STD_INPUT_HANDLE); // Move the handle to a "low" number if (! DuplicateHandle(GetCurrentProcess(), oldStdIn, GetCurrentProcess(), &hListen, 0, TRUE, DUPLICATE_SAME_ACCESS)) { return -1; } if (! SetStdHandle(STD_INPUT_HANDLE, hListen)) { return -1; } CloseHandle(oldStdIn); /* * Set the pipe handle state so that it operates in wait mode. * * NOTE: The listenFd is not mapped to a pseudo file descriptor * as all work done on it is contained to the OS library. * * XXX: Initial assumption is that SetNamedPipeHandleState will * fail if this is an IP socket... */ if (SetNamedPipeHandleState(hListen, &pipeMode, NULL, NULL)) { listenType = FD_PIPE_SYNC; listenOverlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); } else { listenType = FD_SOCKET_SYNC; } } /* * If there are no stdioFds passed in, we're done. */ if(stdioFds == NULL) { libInitialized = 1; return 0; } /* * Setup standard input asynchronous I/O. There is actually a separate * thread spawned for this purpose. The reason for this is that some * web servers use anonymous pipes for the connection between itself * and a CGI application. Anonymous pipes can't perform asynchronous * I/O or use I/O completion ports. Therefore in order to present a * consistent I/O dispatch model to an application we emulate I/O * completion port behavior by having the standard input thread posting * messages to the hIoCompPort which look like a complete overlapped * I/O structure. This keeps the event dispatching simple from the * application perspective. */ stdioHandles[STDIN_FILENO] = GetStdHandle(STD_INPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDIN_FILENO], HANDLE_FLAG_INHERIT, 0)) { /* * XXX: Causes error when run from command line. Check KB err = GetLastError(); DebugBreak(); exit(99); */ return -1; } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDIN_FILENO], STDIN_FILENO)) == -1) { return -1; } else { /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ stdioFds[STDIN_FILENO] = fakeFd; } /* * Create the I/O completion port to be used for communicating with * the thread doing I/O on standard in. This port will carry read * and possibly thread termination requests to the StdinThread. */ if (hStdinCompPort == INVALID_HANDLE_VALUE) { hStdinCompPort = CreateIoCompletionPort (INVALID_HANDLE_VALUE, NULL, 0, 1); if(hStdinCompPort == INVALID_HANDLE_VALUE) { printf("<H2>OS_LibInit Failed CreateIoCompletionPort: STDIN! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * Create the thread that will read stdin if the CONTENT_LENGTH * is non-zero. */ if((cLenPtr = getenv("CONTENT_LENGTH")) != NULL && atoi(cLenPtr) > 0) { hStdinThread = CreateThread(NULL, 8192, (LPTHREAD_START_ROUTINE)&StdinThread, NULL, 0, &threadId); if (hStdinThread == NULL) { printf("<H2>OS_LibInit Failed to create STDIN thread! ERROR: %d</H2>\r\n\r\n", GetLastError()); return -1; } } /* * STDOUT will be used synchronously. * * XXX: May want to convert this so that it could be used for OVERLAPPED * I/O later. If so, model it after the Stdin I/O as stdout is * also incapable of async I/O on some servers. */ stdioHandles[STDOUT_FILENO] = GetStdHandle(STD_OUTPUT_HANDLE); if(!SetHandleInformation(stdioHandles[STDOUT_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); //exit(99); return -1; } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDOUT_FILENO], STDOUT_FILENO)) == -1) { return -1; } else { /* * Set stdout equal to our pseudo FD */ stdioFds[STDOUT_FILENO] = fakeFd; } stdioHandles[STDERR_FILENO] = GetStdHandle(STD_ERROR_HANDLE); if(!SetHandleInformation(stdioHandles[STDERR_FILENO], HANDLE_FLAG_INHERIT, FALSE)) { DebugBreak(); //exit(99); return -1; } if ((fakeFd = Win32NewDescriptor(FD_PIPE_SYNC, (int)stdioHandles[STDERR_FILENO], STDERR_FILENO)) == -1) { return -1; } else { /* * Set stderr equal to our pseudo FD */ stdioFds[STDERR_FILENO] = fakeFd; } return 0; } /* *-------------------------------------------------------------- * * OS_LibShutdown -- * * Shutdown the OS library. * * Results: * None. * * Side effects: * Memory freed, handles closed. * *-------------------------------------------------------------- */ void OS_LibShutdown() { if (hIoCompPort != INVALID_HANDLE_VALUE) { CloseHandle(hIoCompPort); hIoCompPort = INVALID_HANDLE_VALUE; } if (hStdinCompPort != INVALID_HANDLE_VALUE) { CloseHandle(hStdinCompPort); hStdinCompPort = INVALID_HANDLE_VALUE; } if (acceptMutex != INVALID_HANDLE_VALUE) { ReleaseMutex(acceptMutex); CloseHandle(acceptMutex); } /* we only want to do this if we're not a web server */ if (stdioHandles[0] != INVALID_HANDLE_VALUE) { DisconnectNamedPipe(hListen); CancelIo(hListen); } DeleteCriticalSection(&fdTableCritical); WSACleanup(); } /* *-------------------------------------------------------------- * * Win32FreeDescriptor -- * * Free I/O descriptor entry in fdTable. * * Results: * Frees I/O descriptor entry in fdTable. * * Side effects: * None. * *-------------------------------------------------------------- */ static void Win32FreeDescriptor(int fd) { /* Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); EnterCriticalSection(&fdTableCritical); if (fdTable[fd].type != FD_UNUSED) { switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: /* Free file path string */ ASSERT(fdTable[fd].path != NULL); free(fdTable[fd].path); fdTable[fd].path = NULL; break; case FD_PIPE_ASYNC: CloseHandle((HANDLE)fdTable[fd].fid.value); break; default: break; } ASSERT(fdTable[fd].path == NULL); fdTable[fd].type = FD_UNUSED; fdTable[fd].path = NULL; fdTable[fd].Errno = NO_ERROR; fdTable[fd].offsetHighPtr = fdTable[fd].offsetLowPtr = NULL; if (fdTable[fd].hMapMutex != NULL) { CloseHandle(fdTable[fd].hMapMutex); fdTable[fd].hMapMutex = NULL; } } LeaveCriticalSection(&fdTableCritical); return; } static short getPort(const char * bindPath) { short port = 0; char * p = strchr(bindPath, ':'); if (p && *++p) { char buf[6]; strncpy(buf, p, 6); buf[5] = '\0'; port = (short) atoi(buf); } return port; } /* * OS_CreateLocalIpcFd -- * * This procedure is responsible for creating the listener pipe * on Windows NT for local process communication. It will create a * named pipe and return a file descriptor to it to the caller. * * Results: * Listener pipe created. This call returns either a valid * pseudo file descriptor or -1 on error. * * Side effects: * Listener pipe and IPC address are stored in the FCGI info * structure. * 'errno' will set on errors (-1 is returned). * *---------------------------------------------------------------------- */ int OS_CreateLocalIpcFd(const char *bindPath, int backlog, int bCreateMutex) { int pseudoFd = -1; short port = getPort(bindPath); HANDLE mutex = INVALID_HANDLE_VALUE; char mutexEnvString[100]; if (mutex == NULL) { return -2; } if (bCreateMutex) { mutex = CreateMutex(NULL, FALSE, NULL); if (! SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE)) { CloseHandle(mutex); return -3; } // This is a nail for listening to more than one port.. // This should really be handled by the caller. _snprintf(mutexEnvString, sizeof(mutexEnvString)-1, MUTEX_VARNAME "=%d", (int) mutex); putenv(mutexEnvString); } // There's nothing to be gained (at the moment) by a shutdown Event if (port && *bindPath != ':' && strncmp(bindPath, LOCALHOST, strlen(LOCALHOST))) { fprintf(stderr, "To start a service on a TCP port can not " "specify a host name.\n" "You should either use \"localhost:<port>\" or " " just use \":<port>.\"\n"); //exit(1); if (bCreateMutex) CloseHandle(mutexEnvString); return -1; } listenType = (port) ? FD_SOCKET_SYNC : FD_PIPE_ASYNC; if (port) { SOCKET listenSock; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; sockAddr.sin_addr.s_addr = htonl(INADDR_ANY); sockAddr.sin_port = htons(port); listenSock = socket(AF_INET, SOCK_STREAM, 0); if (listenSock == INVALID_SOCKET) { if (bCreateMutex)CloseHandle(mutexEnvString); return -4; } if (bind(listenSock, (struct sockaddr *) &sockAddr, sockLen) ) { if (bCreateMutex)CloseHandle(mutexEnvString); return -12; } if (listen(listenSock, backlog)) { if (bCreateMutex)CloseHandle(mutexEnvString); return -5; } pseudoFd = Win32NewDescriptor(listenType, listenSock, -1); if (pseudoFd == -1) { if (bCreateMutex)CloseHandle(mutexEnvString); closesocket(listenSock); return -6; } hListen = (HANDLE) listenSock; } else { HANDLE hListenPipe = INVALID_HANDLE_VALUE; char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); if (! pipePath) { if (bCreateMutex)CloseHandle(mutexEnvString); return -7; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hListenPipe = CreateNamedPipe(pipePath, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT | PIPE_READMODE_BYTE, PIPE_UNLIMITED_INSTANCES, 4096, 4096, 0, NULL); free(pipePath); if (hListenPipe == INVALID_HANDLE_VALUE) { if (bCreateMutex)CloseHandle(mutexEnvString); return -8; } if (! SetHandleInformation(hListenPipe, HANDLE_FLAG_INHERIT, TRUE)) { if (bCreateMutex)CloseHandle(mutexEnvString); return -9; } pseudoFd = Win32NewDescriptor(listenType, (int) hListenPipe, -1); if (pseudoFd == -1) { if (bCreateMutex)CloseHandle(mutexEnvString); CloseHandle(hListenPipe); return -10; } hListen = (HANDLE) hListenPipe; } return pseudoFd; } /* *---------------------------------------------------------------------- * * OS_FcgiConnect -- * * Create the pipe pathname connect to the remote application if * possible. * * Results: * -1 if fail or a valid handle if connection succeeds. * * Side effects: * Remote connection established. * *---------------------------------------------------------------------- */ int OS_FcgiConnect(char *bindPath) { short port = getPort(bindPath); int pseudoFd = -1; if (port) { struct hostent *hp; char *host = NULL; struct sockaddr_in sockAddr; int sockLen = sizeof(sockAddr); SOCKET sock; if (*bindPath != ':') { char * p = strchr(bindPath, ':'); int len = p - bindPath + 1; host = malloc(len); strncpy(host, bindPath, len); host[len] = '\0'; } hp = gethostbyname(host ? host : LOCALHOST); if (host) { free(host); } if (hp == NULL) { fprintf(stderr, "Unknown host: %s\n", bindPath); return -1; } memset(&sockAddr, 0, sizeof(sockAddr)); sockAddr.sin_family = AF_INET; memcpy(&sockAddr.sin_addr, hp->h_addr, hp->h_length); sockAddr.sin_port = htons(port); sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == INVALID_SOCKET) { return -1; } if (! connect(sock, (struct sockaddr *) &sockAddr, sockLen)) { closesocket(sock); return -1; } pseudoFd = Win32NewDescriptor(FD_SOCKET_SYNC, sock, -1); if (pseudoFd == -1) { closesocket(sock); return -1; } } else { char *pipePath = malloc(strlen(bindPathPrefix) + strlen(bindPath) + 1); HANDLE hPipe; if (! pipePath) { return -1; } strcpy(pipePath, bindPathPrefix); strcat(pipePath, bindPath); hPipe = CreateFile(pipePath, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); free(pipePath); if( hPipe == INVALID_HANDLE_VALUE || hPipe == 0) { return -1; } pseudoFd = Win32NewDescriptor(FD_PIPE_ASYNC, (int) hPipe, -1); if (pseudoFd == -1) { CloseHandle(hPipe); return -1; } /* * Set stdin equal to our pseudo FD and create the I/O completion * port to be used for async I/O. */ if (! CreateIoCompletionPort(hPipe, hIoCompPort, pseudoFd, 1)) { Win32FreeDescriptor(pseudoFd); CloseHandle(hPipe); return -1; } } return pseudoFd; } /* *-------------------------------------------------------------- * * OS_Read -- * * Pass through to the appropriate NT read function. * * Results: * Returns number of byes read. Mimics unix read:. * n bytes read, 0 or -1 failure: errno contains actual error * * Side effects: * None. * *-------------------------------------------------------------- */ int OS_Read(int fd, char * buf, size_t len) { DWORD bytesRead; int ret = -1; ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); if (shutdownNow) return -1; switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: case FD_PIPE_SYNC: case FD_PIPE_ASYNC: if (ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, NULL)) { ret = bytesRead; } else { fdTable[fd].Errno = GetLastError(); } break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: ret = recv(fdTable[fd].fid.sock, buf, len, 0); if (ret == SOCKET_ERROR) { fdTable[fd].Errno = WSAGetLastError(); ret = -1; } break; default: ASSERT(0); } return ret; } /* *-------------------------------------------------------------- * * OS_Write -- * * Perform a synchronous OS write. * * Results: * Returns number of bytes written. Mimics unix write: * n bytes written, 0 or -1 failure (??? couldn't find man page). * * Side effects: * none. * *-------------------------------------------------------------- */ int OS_Write(int fd, char * buf, size_t len) { DWORD bytesWritten; int ret = -1; ASSERT(fd >= 0 && fd < WIN32_OPEN_MAX); if (shutdownNow) return -1; switch (fdTable[fd].type) { case FD_FILE_SYNC: case FD_FILE_ASYNC: case FD_PIPE_SYNC: case FD_PIPE_ASYNC: if (WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, NULL)) { ret = bytesWritten; } else { fdTable[fd].Errno = GetLastError(); } break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: ret = send(fdTable[fd].fid.sock, buf, len, 0); if (ret == SOCKET_ERROR) { fdTable[fd].Errno = WSAGetLastError(); ret = -1; } break; default: ASSERT(0); } return ret; } /* *---------------------------------------------------------------------- * * OS_SpawnChild -- * * Spawns a new server listener process, and stores the information * relating to the child in the supplied record. A wait handler is * registered on the child's completion. This involves creating * a process on NT and preparing a command line with the required * state (currently a -childproc flag and the server socket to use * for accepting connections). * * Results: * 0 if success, -1 if error. * * Side effects: * Child process spawned. * *---------------------------------------------------------------------- */ int OS_SpawnChild(char *execPath, int listenFd, PROCESS_INFORMATION *pInfo, char *env) { STARTUPINFO StartupInfo; BOOL success; memset((void *)&StartupInfo, 0, sizeof(STARTUPINFO)); StartupInfo.cb = sizeof (STARTUPINFO); StartupInfo.lpReserved = NULL; StartupInfo.lpReserved2 = NULL; StartupInfo.cbReserved2 = 0; StartupInfo.lpDesktop = NULL; StartupInfo.wShowWindow = SW_HIDE; /* * FastCGI on NT will set the listener pipe HANDLE in the stdin of * the new process. The fact that there is a stdin and NULL handles * for stdout and stderr tells the FastCGI process that this is a * FastCGI process and not a CGI process. */ StartupInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; /* * XXX: Do I have to dup the handle before spawning the process or is * it sufficient to use the handle as it's reference counted * by NT anyway? */ StartupInfo.hStdInput = fdTable[listenFd].fid.fileHandle; StartupInfo.hStdOutput = INVALID_HANDLE_VALUE; StartupInfo.hStdError = INVALID_HANDLE_VALUE; /* * Make the listener socket inheritable. */ success = SetHandleInformation(StartupInfo.hStdInput, HANDLE_FLAG_INHERIT, TRUE); if(!success) { //exit(99); return -1; } /* * XXX: Might want to apply some specific security attributes to the * processes. */ success = CreateProcess(execPath, /* LPCSTR address of module name */ NULL, /* LPCSTR address of command line */ NULL, /* Process security attributes */ NULL, /* Thread security attributes */ TRUE, /* Inheritable Handes inherited. */ 0, /* DWORD creation flags */ env, /* Use parent environment block */ NULL, /* Address of current directory name */ &StartupInfo, /* Address of STARTUPINFO */ pInfo); /* Address of PROCESS_INFORMATION */ if(success) { return 0; } else { return -1; } } /* *-------------------------------------------------------------- * * OS_AsyncReadStdin -- * * This initiates an asynchronous read on the standard * input handle. This handle is not guaranteed to be * capable of performing asynchronous I/O so we send a * message to the StdinThread to do the synchronous read. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous message is queued to the StdinThread and an * overlapped structure is allocated/initialized. * *-------------------------------------------------------------- */ int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { POVERLAPPED_REQUEST pOv; ASSERT(fdTable[STDIN_FILENO].type != FD_UNUSED); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); pOv->clientData1 = (ClientData)buf; pOv->instance = fdTable[STDIN_FILENO].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; PostQueuedCompletionStatus(hStdinCompPort, len, STDIN_FILENO, (LPOVERLAPPED)pOv); return 0; } /* *-------------------------------------------------------------- * * OS_AsyncRead -- * * This initiates an asynchronous read on the file * handle which may be a socket or named pipe. * * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the ReadFile may * return data if it is cached) but do all completion * processing in OS_Select when we get the io completion * port done notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { DWORD bytesRead; POVERLAPPED_REQUEST pOv; /* * Catch any bogus fd values */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* * Confirm that this is an async fd */ ASSERT(fdTable[fd].type != FD_UNUSED); ASSERT(fdTable[fd].type != FD_FILE_SYNC); ASSERT(fdTable[fd].type != FD_PIPE_SYNC); ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); /* * Only file offsets should be non-zero, but make sure. */ if (fdTable[fd].type == FD_FILE_ASYNC) if (fdTable[fd].offset >= 0) pOv->overlapped.Offset = fdTable[fd].offset; else pOv->overlapped.Offset = offset; pOv->instance = fdTable[fd].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; bytesRead = fd; /* * ReadFile returns: TRUE success, FALSE failure */ if (!ReadFile(fdTable[fd].fid.fileHandle, buf, len, &bytesRead, (LPOVERLAPPED)pOv)) { fdTable[fd].Errno = GetLastError(); if(fdTable[fd].Errno == ERROR_NO_DATA || fdTable[fd].Errno == ERROR_PIPE_NOT_CONNECTED) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return 0; } if(fdTable[fd].Errno != ERROR_IO_PENDING) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return -1; } fdTable[fd].Errno = 0; } return 0; } /* *-------------------------------------------------------------- * * OS_AsyncWrite -- * * This initiates an asynchronous write on the "fake" file * descriptor (which may be a file, socket, or named pipe). * We also must save the ProcPtr and ClientData, so later * when the io completes, we know who to call. * * We don't look at any results here (the WriteFile generally * completes immediately) but do all completion processing * in OS_DoIo when we get the io completion port done * notifications. Then we call the callback. * * Results: * -1 if error, 0 otherwise. * * Side effects: * Asynchronous I/O operation is queued for completion. * *-------------------------------------------------------------- */ int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData) { DWORD bytesWritten; POVERLAPPED_REQUEST pOv; /* * Catch any bogus fd values */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* * Confirm that this is an async fd */ ASSERT(fdTable[fd].type != FD_UNUSED); ASSERT(fdTable[fd].type != FD_FILE_SYNC); ASSERT(fdTable[fd].type != FD_PIPE_SYNC); ASSERT(fdTable[fd].type != FD_SOCKET_SYNC); pOv = (POVERLAPPED_REQUEST)malloc(sizeof(struct OVERLAPPED_REQUEST)); ASSERT(pOv); memset((void *)pOv, 0, sizeof(struct OVERLAPPED_REQUEST)); /* * Only file offsets should be non-zero, but make sure. */ if (fdTable[fd].type == FD_FILE_ASYNC) /* * Only file opened via OS_AsyncWrite with * O_APPEND will have an offset != -1. */ if (fdTable[fd].offset >= 0) /* * If the descriptor has a memory mapped file * handle, take the offsets from there. */ if (fdTable[fd].hMapMutex != NULL) { /* * Wait infinitely; this *should* not cause problems. */ WaitForSingleObject(fdTable[fd].hMapMutex, INFINITE); /* * Retrieve the shared offset values. */ pOv->overlapped.OffsetHigh = *(fdTable[fd].offsetHighPtr); pOv->overlapped.Offset = *(fdTable[fd].offsetLowPtr); /* * Update the shared offset values for the next write */ *(fdTable[fd].offsetHighPtr) += 0; /* XXX How do I handle overflow */ *(fdTable[fd].offsetLowPtr) += len; ReleaseMutex(fdTable[fd].hMapMutex); } else pOv->overlapped.Offset = fdTable[fd].offset; else pOv->overlapped.Offset = offset; pOv->instance = fdTable[fd].instance; pOv->procPtr = procPtr; pOv->clientData = clientData; bytesWritten = fd; /* * WriteFile returns: TRUE success, FALSE failure */ if (!WriteFile(fdTable[fd].fid.fileHandle, buf, len, &bytesWritten, (LPOVERLAPPED)pOv)) { fdTable[fd].Errno = GetLastError(); if(fdTable[fd].Errno != ERROR_IO_PENDING) { PostQueuedCompletionStatus(hIoCompPort, 0, fd, (LPOVERLAPPED)pOv); return -1; } fdTable[fd].Errno = 0; } if (fdTable[fd].offset >= 0) fdTable[fd].offset += len; return 0; } /* *-------------------------------------------------------------- * * OS_Close -- * * Closes the descriptor with routine appropriate for * descriptor's type. * * Results: * Socket or file is closed. Return values mimic Unix close: * 0 success, -1 failure * * Side effects: * Entry in fdTable is marked as free. * *-------------------------------------------------------------- */ int OS_Close(int fd) { int ret = 0; /* * Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); ASSERT(fdTable[fd].type != FD_UNUSED); switch (fdTable[fd].type) { case FD_PIPE_SYNC: case FD_PIPE_ASYNC: case FD_FILE_SYNC: case FD_FILE_ASYNC: break; case FD_SOCKET_SYNC: case FD_SOCKET_ASYNC: /* * Closing a socket that has an async read outstanding causes a * tcp reset and possible data loss. The shutdown call seems to * prevent this. */ shutdown(fdTable[fd].fid.sock, 2); /* * closesocket returns: 0 success, SOCKET_ERROR failure */ if (closesocket(fdTable[fd].fid.sock) == SOCKET_ERROR) ret = -1; break; default: return -1; /* fake failure */ } Win32FreeDescriptor(fd); return ret; } /* *-------------------------------------------------------------- * * OS_CloseRead -- * * Cancel outstanding asynchronous reads and prevent subsequent * reads from completing. * * Results: * Socket or file is shutdown. Return values mimic Unix shutdown: * 0 success, -1 failure * *-------------------------------------------------------------- */ int OS_CloseRead(int fd) { int ret = 0; /* * Catch it if fd is a bogus value */ ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); ASSERT(fdTable[fd].type == FD_SOCKET_ASYNC || fdTable[fd].type == FD_SOCKET_SYNC); if (shutdown(fdTable[fd].fid.sock,0) == SOCKET_ERROR) ret = -1; return ret; } /* *-------------------------------------------------------------- * * OS_DoIo -- * * This function was formerly OS_Select. It's purpose is * to pull I/O completion events off the queue and dispatch * them to the appropriate place. * * Results: * Returns 0. * * Side effects: * Handlers are called. * *-------------------------------------------------------------- */ int OS_DoIo(struct timeval *tmo) { unsigned long fd; unsigned long bytes; POVERLAPPED_REQUEST pOv; struct timeb tb; int ms; int ms_last; int err; /* XXX * We can loop in here, but not too long, as wait handlers * must run. * For cgi stdin, apparently select returns when io completion * ports don't, so don't wait the full timeout. */ if(tmo) ms = (tmo->tv_sec*1000 + tmo->tv_usec/1000) / 2; else ms = 1000; ftime(&tb); ms_last = tb.time*1000 + tb.millitm; while (ms >= 0) { if(tmo && (ms = tmo->tv_sec*1000 + tmo->tv_usec/1000)> 100) ms = 100; if (!GetQueuedCompletionStatus(hIoCompPort, &bytes, &fd, (LPOVERLAPPED *)&pOv, ms) && !pOv) { err = WSAGetLastError(); return 0; /* timeout */ } ASSERT((fd >= 0) && (fd < WIN32_OPEN_MAX)); /* call callback if descriptor still valid */ ASSERT(pOv); if(pOv->instance == fdTable[fd].instance) (*pOv->procPtr)(pOv->clientData, bytes); free(pOv); ftime(&tb); ms -= (tb.time*1000 + tb.millitm - ms_last); ms_last = tb.time*1000 + tb.millitm; } return 0; } static int isAddrOK(struct sockaddr_in * inet_sockaddr, const char * okAddrs) { static const char *token = " ,;:\t"; char *ipaddr; char *p; if (okAddrs == NULL) return TRUE; ipaddr = inet_ntoa(inet_sockaddr->sin_addr); p = strstr(okAddrs, ipaddr); if (p == NULL) return FALSE; if (p == okAddrs) { p += strlen(ipaddr); return (strchr(token, *p) != NULL); } if (strchr(token, *--p) != NULL) { p += strlen(ipaddr) + 1; return (strchr(token, *p) != NULL); } return FALSE; } #ifndef NO_WSAACEPT static int CALLBACK isAddrOKCallback(LPWSABUF lpCallerId, LPWSABUF dc0, LPQOS dc1, LPQOS dc2, LPWSABUF dc3, LPWSABUF dc4, GROUP *dc5, DWORD data) { struct sockaddr_in *sockaddr = (struct sockaddr_in *) lpCallerId->buf; // Touch the args to avoid warnings dc0 = NULL; dc1 = NULL; dc2 = NULL; dc3 = NULL; dc4 = NULL; dc5 = NULL; if ((void *) data == NULL) return CF_ACCEPT; if (sockaddr->sin_family != AF_INET) return CF_ACCEPT; return isAddrOK(sockaddr, (const char *) data) ? CF_ACCEPT : CF_REJECT; } #endif static printLastError(const char * text) { LPVOID buf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, (LPTSTR) &buf, 0, NULL ); fprintf(stderr, "%s: %s\n", text, (LPCTSTR) buf); LocalFree(buf); } static int acceptNamedPipe() { int ipcFd = -1; if (! ConnectNamedPipe(hListen, &listenOverlapped)) { switch (GetLastError()) { case ERROR_PIPE_CONNECTED: // A client connected after CreateNamedPipe but // before ConnectNamedPipe. Its a good connection. break; case ERROR_IO_PENDING: // Wait for a connection to complete. while (WaitForSingleObject(listenOverlapped.hEvent, ACCEPT_TIMEOUT) == WAIT_TIMEOUT) { if (shutdownPending) { OS_LibShutdown(); return -1; } } break; case ERROR_PIPE_LISTENING: // The pipe handle is in nonblocking mode. case ERROR_NO_DATA: // The previous client closed its handle (and we failed // to call DisconnectNamedPipe) default: printLastError("unexpected ConnectNamedPipe() error"); } } ipcFd = Win32NewDescriptor(FD_PIPE_SYNC, (int) hListen, -1); if (ipcFd == -1) { DisconnectNamedPipe(hListen); } return ipcFd; } static int acceptSocket(const char *webServerAddrs) { SOCKET hSock; int ipcFd = -1; for (;;) { struct sockaddr sockaddr; int sockaddrLen = sizeof(sockaddr); for (;;) { const struct timeval timeout = {1, 0}; fd_set readfds; FD_ZERO(&readfds); FD_SET((unsigned int) hListen, &readfds); if (select(0, &readfds, NULL, NULL, &timeout) == 0) { if (shutdownPending) { OS_LibShutdown(); return -1; } } else { break; } } #if NO_WSAACEPT hSock = accept((SOCKET) hListen, &sockaddr, &sockaddrLen); if (hSock == INVALID_SOCKET) { break; } if (isAddrOK((struct sockaddr_in *) &sockaddr, webServerAddrs)) { break; } closesocket(hSock); #else hSock = WSAAccept((unsigned int) hListen, &sockaddr, &sockaddrLen, isAddrOKCallback, (DWORD) webServerAddrs); if (hSock != INVALID_SOCKET) { break; } if (WSAGetLastError() != WSAECONNREFUSED) { break; } #endif } if (hSock == INVALID_SOCKET) { /* Use FormatMessage() */ fprintf(stderr, "accept()/WSAAccept() failed: %d", WSAGetLastError()); return -1; } ipcFd = Win32NewDescriptor(FD_SOCKET_SYNC, hSock, -1); if (ipcFd == -1) { closesocket(hSock); } return ipcFd; } /* *---------------------------------------------------------------------- * * OS_Accept -- * * Accepts a new FastCGI connection. This routine knows whether * we're dealing with TCP based sockets or NT Named Pipes for IPC. * * fail_on_intr is ignored in the Win lib. * * Results: * -1 if the operation fails, otherwise this is a valid IPC fd. * *---------------------------------------------------------------------- */ int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs) { int ipcFd = -1; // Touch args to prevent warnings listen_sock = 0; fail_on_intr = 0; // @todo Muliple listen sockets and sockets other than 0 are not // supported due to the use of globals. if (shutdownPending) { OS_LibShutdown(); return -1; } // The mutex is to keep other processes (and threads, when supported) // from going into the accept cycle. The accept cycle needs to // periodically break out to check the state of the shutdown flag // and there's no point to having more than one thread do that. if (acceptMutex != INVALID_HANDLE_VALUE) { if (WaitForSingleObject(acceptMutex, INFINITE) == WAIT_FAILED) { printLastError("WaitForSingleObject() failed"); return -1; } } if (shutdownPending) { OS_LibShutdown(); } else if (listenType == FD_PIPE_SYNC) { ipcFd = acceptNamedPipe(); } else if (listenType == FD_SOCKET_SYNC) { ipcFd = acceptSocket(webServerAddrs); } else { fprintf(stderr, "unknown listenType (%d)\n", listenType); } if (acceptMutex != INVALID_HANDLE_VALUE) { ReleaseMutex(acceptMutex); } return ipcFd; } /* *---------------------------------------------------------------------- * * OS_IpcClose * * OS IPC routine to close an IPC connection. * * Results: * * * Side effects: * IPC connection is closed. * *---------------------------------------------------------------------- */ int OS_IpcClose(int ipcFd) { if (ipcFd == -1) return 0; /* * Catch it if fd is a bogus value */ ASSERT((ipcFd >= 0) && (ipcFd < WIN32_OPEN_MAX)); ASSERT(fdTable[ipcFd].type != FD_UNUSED); switch(listenType) { case FD_PIPE_SYNC: /* * Make sure that the client (ie. a Web Server in this case) has * read all data from the pipe before we disconnect. */ if(!FlushFileBuffers(fdTable[ipcFd].fid.fileHandle)) return -1; if(DisconnectNamedPipe(fdTable[ipcFd].fid.fileHandle)) { OS_Close(ipcFd); return 0; } else { return -1; } break; case FD_SOCKET_SYNC: OS_Close(ipcFd); return 0; break; case FD_UNUSED: default: //exit(106); return -1; break; } return -1; } /* *---------------------------------------------------------------------- * * OS_IsFcgi -- * * Determines whether this process is a FastCGI process or not. * * Results: * Returns 1 if FastCGI, 0 if not. * * Side effects: * None. * *---------------------------------------------------------------------- */ int OS_IsFcgi(int sock) { // Touch args to prevent warnings sock = 0; /* XXX This is broken for sock */ return (listenType != FD_UNUSED); } /* *---------------------------------------------------------------------- * * OS_SetFlags -- * * Sets selected flag bits in an open file descriptor. Currently * this is only to put a SOCKET into non-blocking mode. * *---------------------------------------------------------------------- */ void OS_SetFlags(int fd, int flags) { unsigned long pLong = 1L; int err; if (fdTable[fd].type == FD_SOCKET_SYNC && flags == O_NONBLOCK) { if (ioctlsocket(fdTable[fd].fid.sock, FIONBIO, &pLong) == SOCKET_ERROR) { //exit(WSAGetLastError()); SetLastError(WSAGetLastError()); return; } if (!CreateIoCompletionPort((HANDLE)fdTable[fd].fid.sock, hIoCompPort, fd, 1)) { //err = GetLastError(); //exit(err); return; } fdTable[fd].type = FD_SOCKET_ASYNC; } return; } Index: php4/sapi/cgi/libfcgi/strerror.c +++ php4/sapi/cgi/libfcgi/strerror.c /* * The terms in the file "LICENSE.TERMS" do not apply to this file. * See terms below. * * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) /*static char *sccsid = "from: @(#)strerror.c 5.6 (Berkeley) 5/4/91";*/ static char *rcsid = "$Id: strerror.c,v 1.1 2002/03/10 21:39:28 shane Exp $"; #endif /* LIBC_SCCS and not lint */ #include "fcgi_config.h" #if ! defined (HAVE_STRERROR) #include <string.h> /* * Since perror() is not allowed to change the contents of strerror()'s * static buffer, both functions supply their own buffers to the * internal function __strerror(). */ char * __strerror(int num, char *buf) { #define UPREFIX "Unknown error: " extern char *sys_errlist[]; extern int sys_nerr; register unsigned int errnum; register char *p, *t; char tmp[40]; errnum = num; /* convert to unsigned */ if (errnum < sys_nerr) return(sys_errlist[errnum]); /* Do this by hand, so we don't include stdio(3). */ t = tmp; do { *t++ = "0123456789"[errnum % 10]; } while (errnum /= 10); strcpy (buf, UPREFIX); for (p = buf + sizeof(UPREFIX) -1;;) { *p++ = *--t; if (t <= tmp) break; } return buf; } char * strerror(int num) { static char buf[40]; /* 64-bit number + slop */ return __strerror(num, buf); } #endif Index: php4/sapi/cgi/libfcgi/include/fastcgi.h +++ php4/sapi/cgi/libfcgi/include/fastcgi.h /* * fastcgi.h -- * * Defines for the FastCGI protocol. * * * Copyright (c) 1995-1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fastcgi.h,v 1.1 2002/03/10 21:39:28 shane Exp $ */ #ifndef _FASTCGI_H #define _FASTCGI_H /* * Listening socket file number */ #define FCGI_LISTENSOCK_FILENO 0 typedef struct { unsigned char version; unsigned char type; unsigned char requestIdB1; unsigned char requestIdB0; unsigned char contentLengthB1; unsigned char contentLengthB0; unsigned char paddingLength; unsigned char reserved; } FCGI_Header; #define FCGI_MAX_LENGTH 0xffff /* * Number of bytes in a FCGI_Header. Future versions of the protocol * will not reduce this number. */ #define FCGI_HEADER_LEN 8 /* * Value for version component of FCGI_Header */ #define FCGI_VERSION_1 1 /* * Values for type component of FCGI_Header */ #define FCGI_BEGIN_REQUEST 1 #define FCGI_ABORT_REQUEST 2 #define FCGI_END_REQUEST 3 #define FCGI_PARAMS 4 #define FCGI_STDIN 5 #define FCGI_STDOUT 6 #define FCGI_STDERR 7 #define FCGI_DATA 8 #define FCGI_GET_VALUES 9 #define FCGI_GET_VALUES_RESULT 10 #define FCGI_UNKNOWN_TYPE 11 #define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE) /* * Value for requestId component of FCGI_Header */ #define FCGI_NULL_REQUEST_ID 0 typedef struct { unsigned char roleB1; unsigned char roleB0; unsigned char flags; unsigned char reserved[5]; } FCGI_BeginRequestBody; typedef struct { FCGI_Header header; FCGI_BeginRequestBody body; } FCGI_BeginRequestRecord; /* * Mask for flags component of FCGI_BeginRequestBody */ #define FCGI_KEEP_CONN 1 /* * Values for role component of FCGI_BeginRequestBody */ #define FCGI_RESPONDER 1 #define FCGI_AUTHORIZER 2 #define FCGI_FILTER 3 typedef struct { unsigned char appStatusB3; unsigned char appStatusB2; unsigned char appStatusB1; unsigned char appStatusB0; unsigned char protocolStatus; unsigned char reserved[3]; } FCGI_EndRequestBody; typedef struct { FCGI_Header header; FCGI_EndRequestBody body; } FCGI_EndRequestRecord; /* * Values for protocolStatus component of FCGI_EndRequestBody */ #define FCGI_REQUEST_COMPLETE 0 #define FCGI_CANT_MPX_CONN 1 #define FCGI_OVERLOADED 2 #define FCGI_UNKNOWN_ROLE 3 /* * Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT records */ #define FCGI_MAX_CONNS "FCGI_MAX_CONNS" #define FCGI_MAX_REQS "FCGI_MAX_REQS" #define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS" typedef struct { unsigned char type; unsigned char reserved[7]; } FCGI_UnknownTypeBody; typedef struct { FCGI_Header header; FCGI_UnknownTypeBody body; } FCGI_UnknownTypeRecord; #endif /* _FASTCGI_H */ Index: php4/sapi/cgi/libfcgi/include/fcgi_config.h +++ php4/sapi/cgi/libfcgi/include/fcgi_config.h /* fcgi_config.h. Generated automatically by configure. */ /* fcgi_config.h.in. Generated automatically from configure.in by autoheader. */ /* Define if you have the <arpa/inet.h> header file. */ #define HAVE_ARPA_INET_H 1 /* Define if you have the <dlfcn.h> header file. */ #define HAVE_DLFCN_H 1 /* Define if there's a fileno() prototype in stdio.h */ #define HAVE_FILENO_PROTO 1 /* Define if the fpos_t typedef is in stdio.h */ #define HAVE_FPOS 1 /* Define if you have the <inttypes.h> header file. */ #define HAVE_INTTYPES_H 1 /* Define if you have the `dnet_stub' library (-ldnet_stub). */ /* #undef HAVE_LIBDNET_STUB */ /* Define if you have the `ieee' library (-lieee). */ /* #undef HAVE_LIBIEEE */ /* Define if you have the `nsl' library (-lnsl). */ #define HAVE_LIBNSL 1 /* Define if you have the pthread library */ #define HAVE_LIBPTHREAD 1 /* Define if you have the `resolv' library (-lresolv). */ #define HAVE_LIBRESOLV 1 /* Define if you have the `socket' library (-lsocket). */ #define HAVE_LIBSOCKET 1 /* Define if you have the <limits.h> header file. */ #define HAVE_LIMITS_H 1 /* Define if you have the <memory.h> header file. */ #define HAVE_MEMORY_H 1 /* Define if you have the <netdb.h> header file. */ /* #define HAVE_NETDB_H 1 */ /* Define if you have the <netinet/in.h> header file. */ #define HAVE_NETINET_IN_H 1 /* Define if sockaddr_un in sys/un.h contains a sun_len component */ /* #undef HAVE_SOCKADDR_UN_SUN_LEN */ /* Define if the socklen_t typedef is in sys/socket.h */ /* #undef HAVE_SOCKLEN */ /* Define if you have the <stdint.h> header file. */ /* #undef HAVE_STDINT_H */ /* Define if you have the <stdlib.h> header file. */ #define HAVE_STDLIB_H 1 /* Define if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define if you have the <strings.h> header file. */ #define HAVE_STRINGS_H 1 /* Define if you have the <string.h> header file. */ #define HAVE_STRING_H 1 /* Define if you have the <sys/param.h> header file. */ /* #define HAVE_SYS_PARAM_H 1 */ /* Define if you have the <sys/socket.h> header file. */ /*#define HAVE_SYS_SOCKET_H 1*/ /* Define if you have the <sys/stat.h> header file. */ #define HAVE_SYS_STAT_H 1 /* Define if you have the <sys/time.h> header file. */ /*#define HAVE_SYS_TIME_H 1*/ /* Define if you have the <sys/types.h> header file. */ #define HAVE_SYS_TYPES_H 1 /* Define if you have the <unistd.h> header file. */ /*#define HAVE_UNISTD_H 1*/ /* Define if va_arg(arg, long double) crashes the compiler */ /* #undef HAVE_VA_ARG_LONG_DOUBLE_BUG */ /* Name of package */ #define PACKAGE "fcgi" /* Define if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if cross-process locking is required by accept() */ #define USE_LOCKING 1 /* Version number of package */ #define VERSION "2.2.2" /* Define to empty if `const' does not conform to ANSI C. */ /* #undef const */ /* Define as `__inline' if that's what the C compiler calls it, or to nothing if it is not supported. */ /* #undef inline */ /* Define to `int' if <sys/types.h> does not define. */ #define ssize_t int Index: php4/sapi/cgi/libfcgi/include/fcgi_config_x86.h +++ php4/sapi/cgi/libfcgi/include/fcgi_config_x86.h /* * Default fcgi_config.h when building on WinNT (configure is not run). */ /* Define if you have the <arpa/inet.h> header file. */ #undef HAVE_ARPA_INET_H /* Define if there's a fileno() prototype in stdio.h */ #undef HAVE_FILENO_PROTO /* Define if you have the <inttypes.h> header file. */ #undef HAVE_INTTYPES_H /* Define if we have f{set,get}pos functions */ #define HAVE_FPOS 1 /* Define if you have the `dnet_stub' library (-ldnet_stub). */ #undef HAVE_LIBDNET_STUB /* Define if you have the `ieee' library (-lieee). */ #undef HAVE_LIBIEEE /* Define if you have the `nsl' library (-lnsl). */ #undef HAVE_LIBNSL /* Define if you have the pthread library */ #undef HAVE_LIBPTHREAD /* Define if you have the `resolv' library (-lresolv). */ #undef HAVE_LIBRESOLV /* Define if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET /* Define if you have the <limits.h> header file. */ #define HAVE_LIMITS_H 1 /* Define if we need cross-process locking */ #undef USE_LOCKING /* Define if you have the <memory.h> header file. */ #undef HAVE_MEMORY_H /* Define if you have the <netdb.h> header file. */ #undef HAVE_NETDB_H /* Define if you have the <netinet/in.h> header file. */ #undef HAVE_NETINET_IN_H /* Define if sockaddr_un in sys/un.h contains a sun_len component */ #undef HAVE_SOCKADDR_UN_SUN_LEN /* Define if the socklen_t typedef is in sys/socket.h */ #undef HAVE_SOCKLEN /* Define if you have the <stdint.h> header file. */ #undef HAVE_STDINT_H /* Define if you have the <stdlib.h> header file. */ #undef HAVE_STDLIB_H /* Define if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define if you have the <strings.h> header file. */ #undef HAVE_STRINGS_H /* Define if you have the <string.h> header file. */ #undef HAVE_STRING_H /* Define if you have the <sys/param.h> header file. */ #undef HAVE_SYS_PARAM_H /* Define if you have the <sys/socket.h> header file. */ #undef HAVE_SYS_SOCKET_H /* Define if you have the <sys/stat.h> header file. */ #undef HAVE_SYS_STAT_H /* Define if you have the <sys/time.h> header file. */ #undef HAVE_SYS_TIME_H /* Define if you have the <sys/types.h> header file. */ #undef HAVE_SYS_TYPES_H /* Define if you have the <unistd.h> header file. */ #undef HAVE_UNISTD_H /* Define if va_arg(arg, long double) crashes the compiler */ #undef HAVE_VA_ARG_LONG_DOUBLE_BUG /* Define if you have the ANSI C header files. */ #undef STDC_HEADERS /* Define to empty if `const' does not conform to ANSI C. */ #undef const /* Define as `__inline' if that's what the C compiler calls it, or to nothing if it is not supported. */ #undef inline /* Define to `int' if <sys/types.h> does not define. */ #undef ssize_t Index: php4/sapi/cgi/libfcgi/include/fcgi_stdio.h +++ php4/sapi/cgi/libfcgi/include/fcgi_stdio.h /* * fcgi_stdio.h -- * * FastCGI-stdio compatibility package * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgi_stdio.h,v 1.1 2002/03/10 21:39:28 shane Exp $ */ #ifndef _FCGI_STDIO #define _FCGI_STDIO 1 #include <stdio.h> #include <sys/types.h> #include "fcgiapp.h" #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif #ifndef DLLAPI #ifdef _WIN32 #if defined(_LIB) || defined(FCGI_STATIC) #define DLLAPI #else #define DLLAPI __declspec(dllimport) #endif #else #define DLLAPI #endif #endif /* * Wrapper type for FILE */ typedef struct { FILE *stdio_stream; FCGX_Stream *fcgx_stream; } FCGI_FILE; /* * The four new functions and two new macros */ DLLAPI int FCGI_Accept(void); DLLAPI void FCGI_Finish(void); DLLAPI int FCGI_StartFilterData(void); DLLAPI void FCGI_SetExitStatus(int status); #define FCGI_ToFILE(fcgi_file) (fcgi_file->stdio_stream) #define FCGI_ToFcgiStream(fcgi_file) (fcgi_file->fcgx_stream) /* * Wrapper stdin, stdout, and stderr variables, set up by FCGI_Accept() */ DLLAPI extern FCGI_FILE _fcgi_sF[]; #define FCGI_stdin (&_fcgi_sF[0]) #define FCGI_stdout (&_fcgi_sF[1]) #define FCGI_stderr (&_fcgi_sF[2]) /* * Wrapper function prototypes, grouped according to sections * of Harbison & Steele, "C: A Reference Manual," fourth edition, * Prentice-Hall, 1995. */ DLLAPI void FCGI_perror(const char *str); DLLAPI FCGI_FILE *FCGI_fopen(const char *path, const char *mode); DLLAPI int FCGI_fclose(FCGI_FILE *fp); DLLAPI int FCGI_fflush(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_freopen(const char *path, const char *mode, FCGI_FILE *fp); DLLAPI int FCGI_setvbuf(FCGI_FILE *fp, char *buf, int bufmode, size_t size); DLLAPI void FCGI_setbuf(FCGI_FILE *fp, char *buf); DLLAPI int FCGI_fseek(FCGI_FILE *fp, long offset, int whence); DLLAPI int FCGI_ftell(FCGI_FILE *fp); DLLAPI void FCGI_rewind(FCGI_FILE *fp); #ifdef HAVE_FPOS DLLAPI int FCGI_fgetpos(FCGI_FILE *fp, fpos_t *pos); DLLAPI int FCGI_fsetpos(FCGI_FILE *fp, const fpos_t *pos); #endif DLLAPI int FCGI_fgetc(FCGI_FILE *fp); DLLAPI int FCGI_getchar(void); DLLAPI int FCGI_ungetc(int c, FCGI_FILE *fp); DLLAPI char *FCGI_fgets(char *str, int size, FCGI_FILE *fp); DLLAPI char *FCGI_gets(char *str); /* * Not yet implemented * * int FCGI_fscanf(FCGI_FILE *fp, const char *format, ...); * int FCGI_scanf(const char *format, ...); * */ DLLAPI int FCGI_fputc(int c, FCGI_FILE *fp); DLLAPI int FCGI_putchar(int c); DLLAPI int FCGI_fputs(const char *str, FCGI_FILE *fp); DLLAPI int FCGI_puts(const char *str); DLLAPI int FCGI_fprintf(FCGI_FILE *fp, const char *format, ...); DLLAPI int FCGI_printf(const char *format, ...); DLLAPI int FCGI_vfprintf(FCGI_FILE *fp, const char *format, va_list ap); DLLAPI int FCGI_vprintf(const char *format, va_list ap); DLLAPI size_t FCGI_fread(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); DLLAPI size_t FCGI_fwrite(void *ptr, size_t size, size_t nmemb, FCGI_FILE *fp); DLLAPI int FCGI_feof(FCGI_FILE *fp); DLLAPI int FCGI_ferror(FCGI_FILE *fp); DLLAPI void FCGI_clearerr(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_tmpfile(void); DLLAPI int FCGI_fileno(FCGI_FILE *fp); DLLAPI FCGI_FILE *FCGI_fdopen(int fd, const char *mode); DLLAPI FCGI_FILE *FCGI_popen(const char *cmd, const char *type); DLLAPI int FCGI_pclose(FCGI_FILE *); /* * The remaining definitions are for application programs, * not for fcgi_stdio.c */ #ifndef NO_FCGI_DEFINES /* * Replace standard types, variables, and functions with FastCGI wrappers. * Use undef in case a macro is already defined. */ #undef FILE #define FILE FCGI_FILE #undef stdin #define stdin FCGI_stdin #undef stdout #define stdout FCGI_stdout #undef stderr #define stderr FCGI_stderr #undef perror #define perror FCGI_perror #undef fopen #define fopen FCGI_fopen #undef fclose #define fclose FCGI_fclose #undef fflush #define fflush FCGI_fflush #undef freopen #define freopen FCGI_freopen #undef setvbuf #define setvbuf FCGI_setvbuf #undef setbuf #define setbuf FCGI_setbuf #undef fseek #define fseek FCGI_fseek #undef ftell #define ftell FCGI_ftell #undef rewind #define rewind FCGI_rewind #undef fgetpos #define fgetpos FCGI_fgetpos #undef fsetpos #define fsetpos FCGI_fsetpos #undef fgetc #define fgetc FCGI_fgetc #undef getc #define getc FCGI_fgetc #undef getchar #define getchar FCGI_getchar #undef ungetc #define ungetc FCGI_ungetc #undef fgets #define fgets FCGI_fgets #undef gets #define gets FCGI_gets #undef fputc #define fputc FCGI_fputc #undef putc #define putc FCGI_fputc #undef putchar #define putchar FCGI_putchar #undef fputs #define fputs FCGI_fputs #undef puts #define puts FCGI_puts #undef fprintf #define fprintf FCGI_fprintf #undef printf #define printf FCGI_printf #undef vfprintf #define vfprintf FCGI_vfprintf #undef vprintf #define vprintf FCGI_vprintf #undef fread #define fread FCGI_fread #undef fwrite #define fwrite FCGI_fwrite #undef feof #define feof FCGI_feof #undef ferror #define ferror FCGI_ferror #undef clearerr #define clearerr FCGI_clearerr #undef tmpfile #define tmpfile FCGI_tmpfile #undef fileno #define fileno FCGI_fileno #undef fdopen #define fdopen FCGI_fdopen #undef popen #define popen FCGI_popen #undef pclose #define pclose FCGI_pclose #endif /* NO_FCGI_DEFINES */ #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGI_STDIO */ Index: php4/sapi/cgi/libfcgi/include/fcgiapp.h +++ php4/sapi/cgi/libfcgi/include/fcgiapp.h /* * fcgiapp.h -- * * Definitions for FastCGI application server programs * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgiapp.h,v 1.1 2002/03/10 21:39:28 shane Exp $ */ #ifndef _FCGIAPP_H #define _FCGIAPP_H /* Hack to see if we are building TCL - TCL needs varargs not stdarg */ #ifndef TCL_LIBRARY #include <stdarg.h> #else #include <varargs.h> #endif #ifndef DLLAPI #ifdef _WIN32 #if defined(_LIB) || defined(FCGI_STATIC) #define DLLAPI #else #define DLLAPI __declspec(dllimport) #endif #else #define DLLAPI #endif #endif #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif /* * Error codes. Assigned to avoid conflict with EOF and errno(2). */ #define FCGX_UNSUPPORTED_VERSION -2 #define FCGX_PROTOCOL_ERROR -3 #define FCGX_PARAMS_ERROR -4 #define FCGX_CALL_SEQ_ERROR -5 /* * This structure defines the state of a FastCGI stream. * Streams are modeled after the FILE type defined in stdio.h. * (We wouldn't need our own if platform vendors provided a * standard way to subclass theirs.) * The state of a stream is private and should only be accessed * by the procedures defined below. */ typedef struct FCGX_Stream { unsigned char *rdNext; /* reader: first valid byte * writer: equals stop */ unsigned char *wrNext; /* writer: first free byte * reader: equals stop */ unsigned char *stop; /* reader: last valid byte + 1 * writer: last free byte + 1 */ unsigned char *stopUnget; /* reader: first byte of current buffer * fragment, for ungetc * writer: undefined */ int isReader; int isClosed; int wasFCloseCalled; int FCGI_errno; /* error status */ void (*fillBuffProc) (struct FCGX_Stream *stream); void (*emptyBuffProc) (struct FCGX_Stream *stream, int doClose); void *data; } FCGX_Stream; /* * An environment (as defined by environ(7)): A NULL-terminated array * of strings, each string having the form name=value. */ typedef char **FCGX_ParamArray; /* * FCGX_Request Flags * * Setting FCGI_FAIL_ACCEPT_ON_INTR prevents FCGX_Accept() from * restarting upon being interrupted. */ #define FCGI_FAIL_ACCEPT_ON_INTR 1 /* * FCGX_Request -- State associated with a request. * * Its exposed for API simplicity, I expect parts of it to change! */ typedef struct FCGX_Request { int requestId; /* valid if isBeginProcessed */ int role; FCGX_Stream *in; FCGX_Stream *out; FCGX_Stream *err; char **envp; /* Don't use anything below here */ struct Params *paramsPtr; int ipcFd; /* < 0 means no connection */ int isBeginProcessed; /* FCGI_BEGIN_REQUEST seen */ int keepConnection; /* don't close ipcFd at end of request */ int appStatus; int nWriters; /* number of open writers (0..2) */ int flags; int listen_sock; } FCGX_Request; /* *====================================================================== * Control *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_IsCGI -- * * Returns TRUE iff this process appears to be a CGI process * rather than a FastCGI process. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_IsCGI(void); /* *---------------------------------------------------------------------- * * FCGX_Init -- * * Initialize the FCGX library. Call in multi-threaded apps * before calling FCGX_Accept_r(). * * Returns 0 upon success. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Init(void); /* *---------------------------------------------------------------------- * * FCGX_OpenSocket -- * * Create a FastCGI listen socket. * * path is the Unix domain socket (named pipe for WinNT), or a colon * followed by a port number. e.g. "/tmp/fastcgi/mysocket", ":5000" * * backlog is the listen queue depth used in the listen() call. * * Returns the socket's file descriptor or -1 on error. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_OpenSocket(const char *path, int backlog); /* *---------------------------------------------------------------------- * * FCGX_InitRequest -- * * Initialize a FCGX_Request for use with FCGX_Accept_r(). * * sock is a file descriptor returned by FCGX_OpenSocket() or 0 (default). * The only supported flag at this time is FCGI_FAIL_ON_INTR. * * Returns 0 upon success. *---------------------------------------------------------------------- */ DLLAPI int FCGX_InitRequest(FCGX_Request *request, int sock, int flags); /* *---------------------------------------------------------------------- * * FCGX_Accept_r -- * * Accept a new request (multi-thread safe). Be sure to call * FCGX_Init() first. * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * * DON'T use the FCGX_Request, its structure WILL change. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Accept_r(FCGX_Request *request); /* *---------------------------------------------------------------------- * * FCGX_Finish_r -- * * Finish the request (multi-thread safe). * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Finish_r(FCGX_Request *request); /* *---------------------------------------------------------------------- * * FCGX_Free -- * * Free the memory and, if close is true, * IPC FD associated with the request (multi-thread safe). * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Free(FCGX_Request * request, int close); /* *---------------------------------------------------------------------- * * FCGX_Accept -- * * Accept a new request (NOT multi-thread safe). * * Results: * 0 for successful call, -1 for error. * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * Creates input, output, and error streams and * assigns them to *in, *out, and *err respectively. * Creates a parameters data structure to be accessed * via getenv(3) (if assigned to environ) or by FCGX_GetParam * and assigns it to *envp. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_Accept( FCGX_Stream **in, FCGX_Stream **out, FCGX_Stream **err, FCGX_ParamArray *envp); /* *---------------------------------------------------------------------- * * FCGX_Finish -- * * Finish the current request (NOT multi-thread safe). * * Side effects: * * Finishes the request accepted by (and frees any * storage allocated by) the previous call to FCGX_Accept. * * DO NOT retain pointers to the envp array or any strings * contained in it (e.g. to the result of calling FCGX_GetParam), * since these will be freed by the next call to FCGX_Finish * or FCGX_Accept. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_Finish(void); /* *---------------------------------------------------------------------- * * FCGX_StartFilterData -- * * stream is an input stream for a FCGI_FILTER request. * stream is positioned at EOF on FCGI_STDIN. * Repositions stream to the start of FCGI_DATA. * If the preconditions are not met (e.g. FCGI_STDIN has not * been read to EOF) sets the stream error code to * FCGX_CALL_SEQ_ERROR. * * Results: * 0 for a normal return, < 0 for error * *---------------------------------------------------------------------- */ DLLAPI int FCGX_StartFilterData(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_SetExitStatus -- * * Sets the exit status for stream's request. The exit status * is the status code the request would have exited with, had * the request been run as a CGI program. You can call * SetExitStatus several times during a request; the last call * before the request ends determines the value. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_SetExitStatus(int status, FCGX_Stream *stream); /* *====================================================================== * Parameters *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_GetParam -- obtain value of FCGI parameter in environment * * * Results: * Value bound to name, NULL if name not present in the * environment envp. Caller must not mutate the result * or retain it past the end of this request. * *---------------------------------------------------------------------- */ DLLAPI char *FCGX_GetParam(const char *name, FCGX_ParamArray envp); /* *====================================================================== * Readers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_GetChar -- * * Reads a byte from the input stream and returns it. * * Results: * The byte, or EOF (-1) if the end of input has been reached. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetChar(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_UnGetChar -- * * Pushes back the character c onto the input stream. One * character of pushback is guaranteed once a character * has been read. No pushback is possible for EOF. * * Results: * Returns c if the pushback succeeded, EOF if not. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_UnGetChar(int c, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetStr -- * * Reads up to n consecutive bytes from the input stream * into the character array str. Performs no interpretation * of the input bytes. * * Results: * Number of bytes read. If result is smaller than n, * the end of input has been reached. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetStr(char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetLine -- * * Reads up to n-1 consecutive bytes from the input stream * into the character array str. Stops before n-1 bytes * have been read if '\n' or EOF is read. The terminating '\n' * is copied to str. After copying the last byte into str, * stores a '\0' terminator. * * Results: * NULL if EOF is the first thing read from the input stream, * str otherwise. * *---------------------------------------------------------------------- */ DLLAPI char *FCGX_GetLine(char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_HasSeenEOF -- * * Returns EOF if end-of-file has been detected while reading * from stream; otherwise returns 0. * * Note that FCGX_HasSeenEOF(s) may return 0, yet an immediately * following FCGX_GetChar(s) may return EOF. This function, like * the standard C stdio function feof, does not provide the * ability to peek ahead. * * Results: * EOF if end-of-file has been detected, 0 if not. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_HasSeenEOF(FCGX_Stream *stream); /* *====================================================================== * Writers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_PutChar -- * * Writes a byte to the output stream. * * Results: * The byte, or EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutChar(int c, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_PutStr -- * * Writes n consecutive bytes from the character array str * into the output stream. Performs no interpretation * of the output bytes. * * Results: * Number of bytes written (n) for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutStr(const char *str, int n, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_PutS -- * * Writes a null-terminated character string to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_PutS(const char *str, FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_FPrintF, FCGX_VFPrintF -- * * Performs printf-style output formatting and writes the results * to the output stream. * * Results: * number of bytes written for normal return, * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FPrintF(FCGX_Stream *stream, const char *format, ...); DLLAPI int FCGX_VFPrintF(FCGX_Stream *stream, const char *format, va_list arg); /* *---------------------------------------------------------------------- * * FCGX_FFlush -- * * Flushes any buffered output. * * Server-push is a legitimate application of FCGX_FFlush. * Otherwise, FCGX_FFlush is not very useful, since FCGX_Accept * does it implicitly. Calling FCGX_FFlush in non-push applications * results in extra writes and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FFlush(FCGX_Stream *stream); /* *====================================================================== * Both Readers and Writers *====================================================================== */ /* *---------------------------------------------------------------------- * * FCGX_FClose -- * * Closes the stream. For writers, flushes any buffered * output. * * Close is not a very useful operation since FCGX_Accept * does it implicitly. Closing the out stream before the * err stream results in an extra write if there's nothing * in the err stream, and therefore reduces performance. * * Results: * EOF (-1) if an error occurred. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_FClose(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_GetError -- * * Return the stream error code. 0 means no error, > 0 * is an errno(2) error, < 0 is an FastCGI error. * *---------------------------------------------------------------------- */ DLLAPI int FCGX_GetError(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_ClearError -- * * Clear the stream error code and end-of-file indication. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_ClearError(FCGX_Stream *stream); /* *---------------------------------------------------------------------- * * FCGX_CreateWriter -- * * Create a FCGX_Stream (used by cgi-fcgi). This shouldn't * be needed by a FastCGI applictaion. * *---------------------------------------------------------------------- */ DLLAPI FCGX_Stream *FCGX_CreateWriter( int socket, int requestId, int bufflen, int streamType); /* *---------------------------------------------------------------------- * * FCGX_FreeStream -- * * Free a FCGX_Stream (used by cgi-fcgi). This shouldn't * be needed by a FastCGI applictaion. * *---------------------------------------------------------------------- */ DLLAPI void FCGX_FreeStream(FCGX_Stream **stream); /* ---------------------------------------------------------------------- * * Prevent the lib from accepting any new requests. Signal handler safe. * * ---------------------------------------------------------------------- */ DLLAPI void FCGX_ShutdownPending(void); #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGIAPP_H */ Index: php4/sapi/cgi/libfcgi/include/fcgiappmisc.h +++ php4/sapi/cgi/libfcgi/include/fcgiappmisc.h /* * fcgiappmisc.h -- * * Functions implemented by fcgiapp.h that aren't needed * by normal applications, but may be useful to special * applications. * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgiappmisc.h,v 1.1 2002/03/10 21:39:28 shane Exp $ */ #ifndef _FCGIAPPMISC_H #define _FCGIAPPMISC_H #include "fcgiapp.h" /* for FCGX_Stream */ #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif #ifdef _WIN32 #ifndef DLLAPI #ifdef FCGI_STATIC #define DLLAPI #else #define DLLAPI __declspec(dllimport) #endif #endif #else #define DLLAPI #endif DLLAPI FCGX_Stream *CreateWriter( int socket, int requestId, int bufflen, int streamType); DLLAPI void FreeStream(FCGX_Stream **stream); #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGIAPPMISC_H */ Index: php4/sapi/cgi/libfcgi/include/fcgimisc.h +++ php4/sapi/cgi/libfcgi/include/fcgimisc.h /* * fcgimisc.h -- * * Miscellaneous definitions * * * Copyright (c) 1996 Open Market, Inc. * * See the file "LICENSE.TERMS" for information on usage and redistribution * of this file, and for a DISCLAIMER OF ALL WARRANTIES. * * $Id: fcgimisc.h,v 1.1 2002/03/10 21:39:28 shane Exp $ */ #ifndef _FCGIMISC_H #define _FCGIMISC_H #ifndef FALSE #define FALSE (0) #endif #ifndef TRUE #define TRUE (1) #endif #ifndef min #define min(a,b) ((a) < (b) ? (a) : (b)) #endif #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif #ifndef ASSERT #define ASSERT(assertion) assert(assertion) #endif #endif /* _FCGIMISC_H */ Index: php4/sapi/cgi/libfcgi/include/fcgio.h +++ php4/sapi/cgi/libfcgi/include/fcgio.h // // Provides support for FastCGI via C++ iostreams. // // $Id: fcgio.h,v 1.1 2002/03/10 21:39:28 shane Exp $ // // This work is based on routines written by George Feinberg. They // have been mostly re-written and extensively changed by // Michael Richards. // // Rewritten again with bug fixes and numerous enhancements by // Michael Shell. // // And rewritten again by Rob Saccoccio. // // Special Thanks to Dietmar Kuehl for his help and the numerous custom // streambuf examples on his web site. // // Copyright (c) 2000 Tux the Linux Penguin // Copyright (c) 2001 Rob Saccoccio and Chelsea Networks // // You are free to use this software without charge or royalty // as long as this notice is not removed or altered, and recognition // is given to the author(s) // // This code is offered as-is without any warranty either expressed or // implied; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. If it breaks, you get to keep // both halves. #ifndef FCGIO_H #define FCGIO_H #include <iostream.h> #include "fcgiapp.h" #ifndef DLLAPI #ifdef _WIN32 #define DLLAPI __declspec(dllimport) #else #define DLLAPI #endif #endif /* * fcgi_streambuf */ class fcgi_streambuf : public streambuf { public: // Note that if no buf is assigned (the default), iostream methods // such as peek(), unget() and putback() will fail. If a buf is // assigned, I/O is a bit less effecient and output streams will // have to be flushed (or the streambuf destroyed) before the next // call to "accept". DLLAPI fcgi_streambuf(FCGX_Stream * fcgx, char * buf, int len); DLLAPI fcgi_streambuf(char * buf, int len); DLLAPI fcgi_streambuf(FCGX_Stream * fcgx = NULL); DLLAPI ~fcgi_streambuf(void); DLLAPI int attach(FCGX_Stream * fcgx); protected: // Consume the put area (if buffered) and c (if c is not EOF). DLLAPI virtual int overflow(int); // Flush the put area (if buffered) and the FCGX buffer to the client. DLLAPI virtual int sync(); // Remove and return the current character. DLLAPI virtual int uflow(); // Fill the get area (if buffered) and return the current character. DLLAPI virtual int underflow(); // Use a buffer. The only reasons that a buffer would be useful is // to support the use of the unget()/putback() or seek() methods. Using // a buffer will result in less efficient I/O. Note: the underlying // FastCGI library (FCGX) maintains its own input and output buffers. DLLAPI virtual streambuf * setbuf(char * buf, int len); DLLAPI virtual int xsgetn(char * s, int n); DLLAPI virtual int xsputn(const char * s, int n); private: FCGX_Stream * fcgx; // buf is just handy to have around char * buf; // this isn't kept by the base class int bufsize; void init(FCGX_Stream * fcgx, char * buf, int bufsize); void reset(void); }; /* * fcgi_istream - deprecated */ class fcgi_istream : public istream { public: // deprecated DLLAPI fcgi_istream(FCGX_Stream * fcgx = NULL); // deprecated DLLAPI ~fcgi_istream(void) {} // deprecated DLLAPI virtual void attach(FCGX_Stream * fcgx); private: fcgi_streambuf fcgi_strmbuf; }; /* * fcgi_ostream - deprecated */ class fcgi_ostream : public ostream { public: // deprecated DLLAPI fcgi_ostream(FCGX_Stream * fcgx = NULL); // deprecated DLLAPI ~fcgi_ostream(void) {} // deprecated DLLAPI virtual void attach(FCGX_Stream *fcgx); private: fcgi_streambuf fcgi_strmbuf; }; #endif /* FCGIO_H */ Index: php4/sapi/cgi/libfcgi/include/fcgios.h +++ php4/sapi/cgi/libfcgi/include/fcgios.h /* * fcgios.h -- * * Description of file. * * * Copyright (c) 1996 Open Market, Inc. * All rights reserved. * * This file contains proprietary and confidential information and * remains the unpublished property of Open Market, Inc. Use, * disclosure, or reproduction is prohibited except as permitted by * express written license agreement with Open Market, Inc. * * Bill Snapper * snapper@openmarket.com */ #ifndef _FCGIOS_H #define _FCGIOS_H #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #endif #include "fcgi_config.h" #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif #if defined (c_plusplus) || defined (__cplusplus) extern "C" { #endif #ifdef _WIN32 #define OS_Errno GetLastError() #define OS_SetErrno(err) SetLastError(err) #ifndef O_NONBLOCK #define O_NONBLOCK 0x0004 /* no delay */ #endif #else /* !_WIN32 */ #define OS_Errno errno #define OS_SetErrno(err) errno = (err) #endif /* !_WIN32 */ #ifndef DLLAPI #ifdef _WIN32 #if defined(_LIB) || defined(FCGI_STATIC) #define DLLAPI #else #define DLLAPI __declspec(dllimport) #endif #else #define DLLAPI #endif #endif /* This is the initializer for a "struct timeval" used in a select() call * right after a new request is accept()ed to determine readablity. Its * a drop-dead timer. Its only used for AF_UNIX sockets (not TCP sockets). * Its a workaround for a kernel bug in Linux 2.0.x and SCO Unixware. * Making this as small as possible, yet remain reliable would be best. * 2 seconds is very conservative. 0,0 is not reliable. The shorter the * timeout, the faster request processing will recover. The longer the * timeout, the more likely this application being "busy" will cause other * requests to abort and cause more dead sockets that need this timeout. */ #define READABLE_UNIX_FD_DROP_DEAD_TIMEVAL 2,0 #ifndef STDIN_FILENO #define STDIN_FILENO 0 #endif #ifndef STDOUT_FILENO #define STDOUT_FILENO 1 #endif #ifndef STDERR_FILENO #define STDERR_FILENO 2 #endif #ifndef MAXPATHLEN #define MAXPATHLEN 1024 #endif #ifndef X_OK #define X_OK 0x01 #endif #ifndef _CLIENTDATA # if defined(__STDC__) || defined(__cplusplus) typedef void *ClientData; # else typedef int *ClientData; # endif /* __STDC__ */ #define _CLIENTDATA #endif #define MUTEX_VARNAME "_FCGI_MUTEX_" #define SHUTDOWN_EVENT_NAME "_FCGI_SHUTDOWN_EVENT_" typedef void (*OS_AsyncProc) (ClientData clientData, int len); DLLAPI int OS_LibInit(int stdioFds[3]); DLLAPI void OS_LibShutdown(void); DLLAPI int OS_CreateLocalIpcFd(const char *bindPath, int backlog, int bCreateMutex); DLLAPI int OS_FcgiConnect(char *bindPath); DLLAPI int OS_Read(int fd, char * buf, size_t len); DLLAPI int OS_Write(int fd, char * buf, size_t len); DLLAPI int OS_SpawnChild(char *execPath, int listenFd, PROCESS_INFORMATION *pInfo, char *env); DLLAPI int OS_AsyncReadStdin(void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_AsyncRead(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_AsyncWrite(int fd, int offset, void *buf, int len, OS_AsyncProc procPtr, ClientData clientData); DLLAPI int OS_Close(int fd); DLLAPI int OS_CloseRead(int fd); DLLAPI int OS_DoIo(struct timeval *tmo); DLLAPI int OS_Accept(int listen_sock, int fail_on_intr, const char *webServerAddrs); DLLAPI int OS_IpcClose(int ipcFd); DLLAPI int OS_IsFcgi(int sock); DLLAPI void OS_SetFlags(int fd, int flags); DLLAPI void OS_ShutdownPending(void); #if defined (__cplusplus) || defined (c_plusplus) } /* terminate extern "C" { */ #endif #endif /* _FCGIOS_H */
« previous php.cvs (#9870) next »