#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
{
#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
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();
}
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;
}
*/
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 */
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)
}
}
- 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':
}
#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 '?':
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 */
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;
CG(interactive) = interactive;
- if (!cgi) {
+ if (!cgi
+#ifdef PHP_FASTCGI
+ && !fastcgi
+#endif
+ ) {
if (!SG(request_info).query_string) {
len = 0;
if (script_file) {
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;
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]);
}
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();
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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$";
+#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;
+}
--- /dev/null
+/*
+ * 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$";
+#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(¶msPtr);
+ 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(¶msPtr);
+ } 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;
+}
+
--- /dev/null
+/*
+ * 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$
+ */
+
+#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 */
+
--- /dev/null
+/* 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
\ No newline at end of file
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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$
+ */
+
+#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 */
+
--- /dev/null
+/*
+ * 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$
+ */
+
+#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;
+
+\f
+/*
+ *======================================================================
+ * 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);
+\f
+/*
+ *======================================================================
+ * 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);
+\f
+/*
+ *======================================================================
+ * 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);
+\f
+/*
+ *======================================================================
+ * 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);
+\f
+/*
+ *======================================================================
+ * 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 */
--- /dev/null
+/*
+ * 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$
+ */
+
+#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 */
--- /dev/null
+/*
+ * 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$
+ */
+
+#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 */
--- /dev/null
+//
+// Provides support for FastCGI via C++ iostreams.
+//
+// $Id$
+//
+// 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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$";
+#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);
+ }
+}
--- /dev/null
+/*
+ * 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$";
+#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;
+}
+
--- /dev/null
+/*
+ * 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$";
+#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