diff options
-rw-r--r-- | Makefile.in | 2 | ||||
-rw-r--r-- | configure.in | 5 | ||||
-rw-r--r-- | sapi/fastcgi/CREDITS | 4 | ||||
-rw-r--r-- | sapi/fastcgi/Makefile.in | 5 | ||||
-rw-r--r-- | sapi/fastcgi/README.FastCGI | 17 | ||||
-rw-r--r-- | sapi/fastcgi/config.m4 | 22 | ||||
-rw-r--r-- | sapi/fastcgi/fastcgi.c | 457 | ||||
-rw-r--r-- | sapi/fastcgi/php.sym | 0 | ||||
-rw-r--r-- | sapi/fastcgi/php_fastcgi.h | 28 |
9 files changed, 539 insertions, 1 deletions
diff --git a/Makefile.in b/Makefile.in index 81d2b7a600..6968ba80fc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -18,7 +18,7 @@ LTLIBRARY_LIBADD = $(LTLIBRARY_DEPENDENCIES) $(EXTRA_LIBS) PROGRAM_NAME = php PROGRAM_SOURCES = stub.c -PROGRAM_LDADD = libphp4.la +PROGRAM_LDADD = libphp4.la $(EXT_PROGRAM_LDADD) PROGRAM_LDFLAGS = -export-dynamic PROGRAM_DEPENDENCIES = $(PROGRAM_LDADD) diff --git a/configure.in b/configure.in index a22c290946..d596970246 100644 --- a/configure.in +++ b/configure.in @@ -645,6 +645,11 @@ if test "$PHP_SAPI" = "cgi"; then PHP_PROGRAM=php fi +if test "$PHP_SAPI" = "fastcgi"; then + PHP_PROGRAM=php +fi + + PHP_REGEX PHP_CONFIGURE_PART(Configuring Zend) diff --git a/sapi/fastcgi/CREDITS b/sapi/fastcgi/CREDITS new file mode 100644 index 0000000000..eb9cece428 --- /dev/null +++ b/sapi/fastcgi/CREDITS @@ -0,0 +1,4 @@ +fastcgi +Ben Mansell +Stephen Landamore +Daniel Silverstone diff --git a/sapi/fastcgi/Makefile.in b/sapi/fastcgi/Makefile.in new file mode 100644 index 0000000000..0bea9c31b1 --- /dev/null +++ b/sapi/fastcgi/Makefile.in @@ -0,0 +1,5 @@ + +LTLIBRARY_NAME = libsapi.la +LTLIBRARY_SOURCES = fastcgi.c + +include $(top_srcdir)/build/ltlib.mk diff --git a/sapi/fastcgi/README.FastCGI b/sapi/fastcgi/README.FastCGI new file mode 100644 index 0000000000..65dbc44ea0 --- /dev/null +++ b/sapi/fastcgi/README.FastCGI @@ -0,0 +1,17 @@ +FastCGI module +-------------- + +This module requires the FastCGI development kit, available from +http://www.fastcgi.com/ + +Before building PHP, please enter the dev kit, and run: + +./configure +make +make export + +This will compile the library code required for the FastCGI module. All +that is then required is to configure PHP with the '--with-fastcgi' option. +After making the code, you will end up with a binary file called 'php'. +Installation of this file will depend on the web server being used, please +see their documentation for details. diff --git a/sapi/fastcgi/config.m4 b/sapi/fastcgi/config.m4 new file mode 100644 index 0000000000..7e9c39e0b5 --- /dev/null +++ b/sapi/fastcgi/config.m4 @@ -0,0 +1,22 @@ +AC_MSG_CHECKING(for FastCGI support) +AC_ARG_WITH(fastcgi, +[ --with-fastcgi=SRCDIR Build PHP as FastCGI application],[ + if test "$withval" = "yes"; then + FASTCGIPATH=/usr/local + else + FASTCGIPATH=$withval + fi + test -f "$FASTCGIPATH/lib/libfcgi.a" || AC_MSG_ERROR(Unable to find libfcgi.a in $FASTCGIPATH/lib) + test -f "$FASTCGIPATH/include/fastcgi.h" || AC_MSG_ERROR(Unable to find fastcgi.h in $FASTCGIPATH/include) + PHP_SAPI=fastcgi + PHP_LIBS=$FASTCGIPATH/lib/libfcgi.a + AC_ADD_INCLUDE($FASTCGIPATH/include) + EXT_PROGRAM_LDADD="$EXT_PROGRAM_LDADD $FASTCGIPATH/lib/libfcgi.a" + INSTALL_IT="\$(INSTALL) -m 0755 $SAPI_PROGRAM \$(bindir)/$SAPI_FASTCGI" + RESULT="yes" + PHP_SUBST(FASTCGI_LIBADD) + PHP_SUBST(EXT_PROGRAM_LDADD) +],[ + RESULT="no" +]) +AC_MSG_RESULT($RESULT) diff --git a/sapi/fastcgi/fastcgi.c b/sapi/fastcgi/fastcgi.c new file mode 100644 index 0000000000..8db43ae792 --- /dev/null +++ b/sapi/fastcgi/fastcgi.c @@ -0,0 +1,457 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2001 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Ben Mansell <php@slimyhorror.com> | + +----------------------------------------------------------------------+ +*/ + +/* Debugging */ +/* #define DEBUG_FASTCGI 1 */ + +/* Two configurables for the FastCGI runner. + * + * PHP_FCGI_CHILDREN - if set, the FastCGI will pre-fork this many processes + * which will accept requests. + * + * PHP_FCGI_MAX_REQUESTS - if set, the runner will kill itself after handling + * the given number of requests. This is to curb any + * memory leaks in PHP. + */ + + +/* The following code is based mainly on the thttpd sapi and the original + * CGI code, no doubt with many new and interesting bugs created... :) + */ + +#include "php.h" +#include "SAPI.h" +#include "php_main.h" +#include "php_fastcgi.h" +#include "php_variables.h" + +#include "fcgi_config.h" +#include "fcgiapp.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/stat.h> + + +#define TLS_D +#define TLS_DC +#define TLS_C +#define TLS_CC +#define TLS_FETCH() + + +FCGX_Stream *in, *out, *err; +FCGX_ParamArray envp; +char *path_info = NULL; + +/* 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; + + +static int sapi_fastcgi_ub_write(const char *str, uint str_length) +{ + uint sent = FCGX_PutStr( str, str_length, out ); + return sent; +} + + +static void sapi_fastcgi_flush( void *server_context ) +{ + if( FCGX_FFlush( out ) == -1 ) { + php_handle_aborted_connection(); + } +} + + +static int sapi_fastcgi_send_headers(sapi_headers_struct *sapi_headers SLS_DC) +{ + char buf[1024]; + int n = 0; + zend_llist_position pos; + sapi_header_struct *h; + + switch( sapi_headers->http_response_code ) { + case 200: + /* Default, assumed by FastCGI */ + break; + case 302: + FCGX_PutS( "Status: 302 Moved Temporarily\r\n", out ); + break; + case 401: + FCGX_PutS( "Status: 401 Authorization Required\r\n", out ); + break; + default: + FCGX_FPrintF( out, "Status: %d Undescribed\r\\n", + sapi_headers->http_response_code ); + } + + h = zend_llist_get_first_ex(&sapi_headers->headers, &pos); + while (h) { + /* TODO: Buffer headers together into one big Put? */ +#ifdef DEBUG_FASTCGI + fprintf( stderr, "Printing header %s\n", h->header ); +#endif + FCGX_PutStr( h->header, h->header_len, out ); + FCGX_PutStr( "\r\n", 2, out ); + h = zend_llist_get_next_ex(&sapi_headers->headers, &pos); + } + FCGX_PutStr( "\r\n", 2, out ); + + return SAPI_HEADER_SENT_SUCCESSFULLY; +} + +static int sapi_fastcgi_read_post(char *buffer, uint count_bytes SLS_DC) +{ + size_t read_bytes = 0, tmp; + int c; + char *pos = buffer; + TLS_FETCH(); + + while( count_bytes ) { + c = FCGX_GetStr( pos, count_bytes, in ); + read_bytes += c; + count_bytes -= c; + pos += c; + if( !c ) break; + } + return read_bytes; +} + +static char *sapi_fastcgi_read_cookies(SLS_D) +{ + return getenv( "HTTP_COOKIE" ); +} + + +static void sapi_fastcgi_register_variables(zval *track_vars_array ELS_DC SLS_DC PLS_DC) +{ + char *self = getenv("REQUEST_URI"); + char *ptr = strchr( self, '?' ); + + /* In CGI mode, we consider the environment to be a part of the server + * variables + */ + php_import_environment_variables(track_vars_array ELS_CC PLS_CC); + + /* strip query string off this */ + if ( ptr ) *ptr = 0; + php_register_variable( "PHP_SELF", getenv("REQUEST_URI"), track_vars_array ELS_CC PLS_CC); + if ( ptr ) *ptr = '?'; +} + + +static sapi_module_struct fastcgi_sapi_module = { + "fastcgi", + "FastCGI", + + php_module_startup, + php_module_shutdown_wrapper, + + NULL, /* activate */ + NULL, /* deactivate */ + + sapi_fastcgi_ub_write, + sapi_fastcgi_flush, + NULL, /* get uid */ + NULL, /* getenv */ + + php_error, + + NULL, + sapi_fastcgi_send_headers, + NULL, + sapi_fastcgi_read_post, + sapi_fastcgi_read_cookies, + + sapi_fastcgi_register_variables, + NULL, /* Log message */ + + NULL, /* Block interruptions */ + NULL, /* Unblock interruptions */ + + STANDARD_SAPI_MODULE_PROPERTIES +}; + +static void fastcgi_module_main(TLS_D SLS_DC) +{ + zend_file_handle file_handle; + CLS_FETCH(); + ELS_FETCH(); + PLS_FETCH(); + + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = SG(request_info).path_translated; + file_handle.free_filename = 0; + file_handle.opened_path = NULL; + + if (php_request_startup(CLS_C ELS_CC PLS_CC SLS_CC) == SUCCESS) { + php_execute_script(&file_handle CLS_CC ELS_CC PLS_CC); + } + php_request_shutdown(NULL); +} + + +static void init_request_info( SLS_D ) +{ + char *content_length = getenv("CONTENT_LENGTH"); + const char *auth; + struct stat st; + char *pi = getenv( "PATH_INFO" ); + char *pt = getenv( "PATH_TRANSLATED" ); + path_info = strdup( pi ); + + SG(request_info).request_method = getenv("REQUEST_METHOD"); + SG(request_info).query_string = getenv("QUERY_STRING"); + SG(request_info).request_uri = path_info; + SG(request_info).content_type = getenv("CONTENT_TYPE"); + SG(request_info).content_length = (content_length?atoi(content_length):0); + SG(sapi_headers).http_response_code = 200; + + SG(request_info).path_translated = pt; + /* + * if the file doesn't exist, try to extract PATH_INFO out + * of it by stat'ing back through the '/' + */ + if ( stat( pt, &st ) == -1 ) { + int len = strlen(pt); + char *ptr; + while( ptr = strrchr(pt,'/') ) { + *ptr = 0; + if ( stat(pt,&st) == 0 && S_ISREG(st.st_mode) ) { + /* + * okay, we found the base script! + * work out how many chars we had to strip off; + * then we can modify PATH_INFO + * accordingly + */ + int slen = len - strlen(pt); + if ( pi ) { + int pilen = strlen( pi ); + strcpy( pi, pi + pilen - slen ); + } + break; + } + } + /* + * if we stripped out all the '/' and still didn't find + * a valid path... we will fail, badly. of course we would + * have failed anyway... is there a nice way to error? + */ + } else { + /* the first stat succeeded... */ + if ( pi ) *pi = 0; + } + + /* The CGI RFC allows servers to pass on unvalidated Authorization data */ + auth = getenv("HTTP_AUTHORIZATION"); +#ifdef DEBUG_FASTCGI + fprintf( stderr, "Authorization: %s\n", auth ); +#endif + php_handle_auth_data(auth SLS_CC); + + +} + + +void fastcgi_php_init(void) +{ + sapi_startup(&fastcgi_sapi_module); + fastcgi_sapi_module.startup(&fastcgi_sapi_module); + SG(server_context) = (void *) 1; +} + +void fastcgi_php_shutdown(void) +{ + if (SG(server_context) != NULL) { + fastcgi_sapi_module.shutdown(&fastcgi_sapi_module); + sapi_shutdown(); + } +} + + +int main(int argc, char *argv[]) +{ + int exit_status = SUCCESS; + int c, i, len; + zend_file_handle file_handle; + char *s; + char *argv0=NULL; + char *script_file=NULL; + zend_llist global_vars; + int children = 8; + int max_requests = 500; + int requests = 0; + int status; + int env_size; + +#ifdef FASTCGI_DEBUG + fprintf( stderr, "Initialising now!\n" ); +#endif + + /* 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 *)); + +#ifdef HAVE_SIGNAL_H +#if defined(SIGPIPE) && defined(SIG_IGN) + signal(SIGPIPE,SIG_IGN); /* ignore SIGPIPE in standalone mode so + that sockets created via fsockopen() + don't kill PHP if the remote site + closes it. in apache|apxs mode apache + does that for us! thies@thieso.net + 20000419 */ +#endif +#endif + + sapi_startup(&fastcgi_sapi_module); + + if (php_module_startup(&fastcgi_sapi_module)==FAILURE) { + return FAILURE; + } + + /* 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 ); + } + } + + /* 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 parent = 1; + int running = 0; + while( parent ) { + do { +#ifdef FASTCGI_DEBUG + fprintf( stderr, "Forking, %d running\n", + running ); +#endif + switch( fork() ) { + case 0: + /* One of the children. + * Make sure we don't go round the + * fork loop any more + */ + parent = 0; + break; + case -1: + perror( "php (pre-forking)" ); + exit( 1 ); + break; + default: + /* Fine */ + running++; + break; + } + } while( parent && ( running < children )); + + if( parent ) { + wait( &status ); + running--; + } + } + } + + /* Main FastCGI loop */ +#ifdef FASTCGI_DEBUG + fprintf( stderr, "Going into accept loop\n" ); +#endif + + while( FCGX_Accept( &in, &out, &err, &cgi_env ) >= 0 ) { + +#ifdef FASTCGI_DEBUG + fprintf( stderr, "Got accept\n" ); +#endif + + /* Allocate for our environment */ + merge_env = malloc( env_size * sizeof( char *)); + if( !merge_env ) { + perror( "Can't malloc environment" ); + exit( 1 ); + } + memcpy( merge_env, orig_env, env_size * sizeof( char *)); + + /* Use the new environment */ + environ = merge_env; + + /* Populate our environment with the CGI's */ + for( i = 0; cgi_env[ i ]; i++ ) { + putenv( cgi_env[ i ] ); + } + + init_request_info( TLS_C SLS_CC ); + SG(server_context) = (void *) 1; /* avoid server_context==NULL checks */ + CG(extended_info) = 0; + SG(request_info).argv0 = argv0; + zend_llist_init(&global_vars, sizeof(char *), NULL, 0); + + fastcgi_module_main( TLS_C SLS_CC ); + if( path_info ) { + free( path_info ); + path_info = NULL; + } + + /* 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; + } + } + +#ifdef FASTCGI_DEBUG + fprintf( stderr, "Exiting...\n" ); +#endif + return 0; +} diff --git a/sapi/fastcgi/php.sym b/sapi/fastcgi/php.sym new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/sapi/fastcgi/php.sym diff --git a/sapi/fastcgi/php_fastcgi.h b/sapi/fastcgi/php_fastcgi.h new file mode 100644 index 0000000000..9031d7bd11 --- /dev/null +++ b/sapi/fastcgi/php_fastcgi.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | PHP version 4.0 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2001 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 2.02 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at | + | http://www.php.net/license/2_02.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Author: Sascha Schumann <sascha@schumann.cx> | + +----------------------------------------------------------------------+ +*/ + +#ifndef PHP_FASTCGI_H +#define PHP_FASTCGI_H + +#include <sys/types.h> +#include <sys/stat.h> + +void fastcgi_php_shutdown(void); +void fastcgi_php_init(void); + +#endif |