LiteSpeed SAPI module is a dedicated interface for PHP integration with
LiteSpeed Web Server. LiteSpeed SAPI has similar architecture to the
-FastCGI SAPI with two major enhancements: better performance and
-support for dynamic PHP configuration changes through web server
+FastCGI SAPI with there major enhancements: better performance, dynamic
+spawning and PHP configuration modification through web server
configuration and .htaccess files.
Our simple benchmark test ("hello world") shows that PHP with
LiteSpeed SAPI has 30% better performance over PHP with FastCGI SAPI,
which is nearly twice the performance that Apache mod_php can deliver.
-A major drawback of FastCGI PHP comparing to mod_php is that "php.ini"
-is the only way to set PHP configuration, and cannot be changed at
-runtime via configuration files like .htaccess files or web server's
-virtual host configuration. As FastCGI PHP is usually shared at server
-level by all virtual hosts, it is big security concern to use it in a
-shared hosting environment. LiteSpeed SAPI is carefully designed to
-address this issue. It accepts same flexible configuration overridden
-methods as those supported in mod_php. LiteSpeed SAPI also uses the
-same configuration directives as Apache mod_php.
+A major drawback of FastCGI PHP comparing to Apache mod_php is lacking
+the flexibilities in PHP configurations. PHP configurations cannot be
+changed at runtime via configuration files like .htaccess files or web
+server's virtual host configuration. In shared hosting environment,
+each hosting account will has its own "open_basedir" overridden in
+server configuration to enhance server security when mod_php is used.
+usually, FastCGI PHP is not an option in shared hosting environment
+due to lacking of this flexibility. LiteSpeed SAPI is carefully designed
+to address this issue. PHP configurations can be modified the same way
+as that in mod_php with the the same configuration directives.
-Therefore, with above enhancements, LiteSpeed SAPI is highly
-recommended over FastCGI SAPI for using PHP with LiteSpeed web
-server.
+PHP with LiteSpeed SAPI is highly recommended over FastCGI PHP for
+PHP scripting with LiteSpeed web server.
Building PHP with LiteSpeed SAPI
need to overwrite the old executable with this one and you are all
set.
+Start PHP from command line
+===========================
+
+Usually, lsphp is managed by LiteSpeed web server in a single server
+installation. lsphp can be used in clustered environment with one
+LiteSpeed web server at the front, load balancing lsphp processes
+running on multiple backend servers. In such environment, lsphp can be
+start manually from command with option "-b <socket_address>", socket
+address can be IPv4, IPv6 or Unix Domain Socket address.
+for example:
+
+ ./lsphp -b [::]:3000
+
+have lsphp bind to port 3000 on all IPv4 and IPv6 address,
+
+ ./lsphp -b *:3000
+
+have lsphp bind to port 300 on all IPv4 address.
+
+ ./lsphp -b 192.168.0.2:3000
+
+have lsphp bind to address 192.168.0.2:3000.
+
+ ./lsphp -b /tmp/lsphp_manual.sock
+
+have lsphp accept request on Unix domain socket "/tmp/lsphp_manual.sock"
+
Using LiteSpeed PHP with LiteSpeed Web Server
=============================================
Brief instructions are as follow:
-1) Login to web admin interface, go to 'Server'->'Ext App' tab, add an
- external application of type "LSAPI app", "Command" should be set
- to a shell command that executes the PHP binary you just
- built. "Instances" should be set to match the value of "Max
- Connections".
+1) Login to web administration interface, go to 'Server'->'Ext App' tab,
+ add an external application of type "LSAPI app", "Command" should be
+ set to a shell command that executes the PHP binary you just built.
+ "Instances" should be set to "1". Add "LSAPI_CHILDREN" environment
+ variable to match the value of "Max Connections". More tunable
+ environment variable described below can be added.
2) Go to 'Server'->'Script Handler' tab, add a script handler
configuration: set 'suffix' to 'php', 'Handler Type' to 'LiteSpeed
3) Click 'Apply Changes' link on the top left of the page, then click
'graceful restart'. Now PHP is running with LiteSpeed SAPI.
-Tuning
-------
+Tunings
+-------
-There are two environment variables that can be tweaked to control the
-behavior of LiteSpeed PHP.
+There are a few environment variables that can be tweaked to control the
+behavior of LSAPI application.
-PHP_LSAPI_CHILDREN (no default)
+* LSAPI_CHILDREN or PHP_LSAPI_CHILDREN (default: 0)
-In order to handle multiple requests concurrently, LiteSpeed web
-server can either spawn multiple PHP processes; or spawn one process,
-and this process will create a number of child processes to handle
-multiple requests simultaneously.
+There are two ways to let PHP handle multiple requests concurrently,
+Server Managed Mode and Self Managed Mode. In Server Managed Mode,
+LiteSpeed web server dynamically spawn/stop PHP processes, in this mode
+"Instances" should match "Max Connections" configuration for PHP
+external application. To start PHP in Self Managed Mode, "Instances"
+should be set to "1", while "LSAPI_CHILDREN" environment variable should
+be set to match the value of "Max Connections" and >1. Web Server will
+start one PHP process, this process will start/stop children PHP processes
+dynamically based on on demand. If "LSAPI_CHILDREN" <=1, PHP will be
+started in server managed mode.
-The web server will create PHP processes specified by "Instance" in
-LSAPI application configuration. For one PHP process launched by the
-server, if PHP_LSAPI_CHILDREN is not set, it will not create any child
-process; if PHP_LSAPI_CHILDREN is set, it will spawn a number of child
-processes specified by PHP_LSAPI_CHILDREN. Usually, it should match
-"Max Connections" configured for the LSAPI application, and both
-values should not be set over 100 in most cases.
+Self Managed Mode is preferred because all PHP processes can share one
+shared memory block for the opcode cache.
-PHP_LSAPI_MAX_REQUESTS (default value: 500)
+Usually, there is no need to set value of LSAPI_CHILDREN over 100 in
+most server environment.
+
+
+* LSAPI_AVOID_FORK (default: 0)
+
+LSAPI_AVOID_FORK specifies the policy of the internal process manager in
+"Self Managed Mode". When set to 0, the internal process manager will stop
+and start children process on demand to save system resource. This is
+preferred in a shared hosting environment. When set to 1, the internal
+process manager will try to avoid freqently stopping and starting children
+process. This might be preferred in a dedicate hosting environment.
+
+
+* LSAPI_EXTRA_CHILDREN (default: 1/3 of LSAPI_CHILDREN or 0)
+
+LSAPI_EXTRA_CHILDREN controls the maximum number of extra children processes
+can be started when some or all existing children processes are in
+malfunctioning state. Total number of children processes will be reduced to
+LSAPI_CHILDREN level as soon as service is back to normal.
+When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
+LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value is 0.
+
+
+* LSAPI_MAX_REQS or PHP_LSAPI_MAX_REQUESTS (default value: 10000)
This controls how many requests each child process will handle before
-exit. When one process exits, another will be created. This tuning is
-necessary because several PHP functions have been identified having
-memory leaks. If the PHP processes were left around forever, they
-could become very inefficient.
+it exits automatically. Several PHP functions have been identified
+having memory leaks. This parameter can help reducing memory usage
+of leaky PHP functions.
+
+
+* LSAPI_MAX_IDLE (default value: 300 seconds)
+
+In Self Managed Mode, LSAPI_MAX_IDLE controls how long a idle child
+process will wait for a new request before it exits. This option help
+releasing system resources taken by idle processes.
+
+
+* LSAPI_MAX_IDLE_CHILDREN
+ (default value: 1/3 of LSAPI_CHILDREN or LSAPI_CHILDREN)
+
+In Self Managed Mode, LSAI_MAX_IDLE_CHILDREN controls how many idle
+children processes are allowed. Excessive idle children processes
+will be killed by the parent process immediately.
+When LSAPI_AVOID_FORK is set to 0, the default value is 1/3 of
+LSAPI_CHIDLREN, When LSAPI_AVOID_FORK is set to 1, the default value
+is LSAPI_CHILDREN.
+
+
+* LSAPI_MAX_PROCESS_TIME (default value: 300 seconds)
+
+In Self Managed Mode, LSAPI_MAX_PROCESS_TIME controls the maximum
+processing time allowed when processing a request. If a child process
+can not finish processing of a request in the given time period, it
+will be killed by the parent process. This option can help getting rid
+of dead or runaway child process.
+
+
+* LSAPI_PGRP_MAX_IDLE (default value: FOREVER )
+
+In Self Managed Mode, LSAPI_PGRP_MAX_IDLE controls how long the parent
+process will wait before exiting when there is no child process.
+This option help releasing system resources taken by an idle parent
+process.
+
+
+* LSAPI_PPID_NO_CHECK
+
+By default a LSAPI application check the existence of its parent process
+and exits automatically if the parent process died. This is to reduce
+orphan process when web server is restarted. However, it is desireable
+to disable this feature, such as when a LSAPI process was started
+manually from command line. LSAPI_PPID_NO_CHECK should be set when
+you want to disable the checking of existence of parent process.
+When PHP started by "-b" option, it is disabled automatically.
+
+
+Compatibility with Apache mod_php
+=================================
+
+LSAPI PHP supports PHP configuration overridden via web server configuration
+as well as .htaccess.
+Since 4.0 release "apache_response_headers" function is supported.
+
Contact
#include "php_ini.h"
#include "php_variables.h"
#include "zend_highlight.h"
+#include "zend.h"
#include "lsapilib.h"
#define SAPI_LSAPI_MAX_HEADER_LENGTH 2048
-static char s_headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
static int lsapi_mode = 1;
static char *php_self = "";
static char *script_filename = "";
+static int source_highlight = 0;
#ifdef ZTS
zend_compiler_globals *compiler_globals;
void ***tsrm_ls;
#endif
+zend_module_entry litespeed_module_entry;
/* {{{ php_lsapi_startup
*/
h = zend_llist_get_first_ex(&sapi_headers->headers, &pos);
while (h) {
- LSAPI_AppendRespHeader(h->header, h->header_len);
+ if ( h->header_len > 0 )
+ LSAPI_AppendRespHeader(h->header, h->header_len);
h = zend_llist_get_next_ex(&sapi_headers->headers, &pos);
}
if (SG(sapi_headers).send_default_content_type)
{
char *hd;
int len;
+ char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
hd = sapi_get_default_content_type(TSRMLS_C);
- len = snprintf( s_headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1,
+ len = snprintf( headerBuf, SAPI_LSAPI_MAX_HEADER_LENGTH - 1,
"Content-type: %s", hd );
efree(hd);
- LSAPI_AppendRespHeader( s_headerBuf, len );
+ LSAPI_AppendRespHeader( headerBuf, len );
}
}
LSAPI_FinalizeRespHeaders();
SG(request_info).request_uri = LSAPI_GetScriptName();
SG(request_info).content_length = LSAPI_GetReqBodyLen();
SG(request_info).path_translated = LSAPI_GetScriptFileName();
- SG(sapi_headers).http_response_code = 0; //It is not reset by zend engine, set it to 0.
+
+ /* It is not reset by zend engine, set it to 0. */
+ SG(sapi_headers).http_response_code = 0;
pAuth = LSAPI_GetHeader( H_AUTHORIZATION );
php_handle_auth_data(pAuth TSRMLS_CC);
override_ini();
- if ( lsapi_module_main( 0 TSRMLS_CC ) == -1 )
+ if ( lsapi_module_main( source_highlight TSRMLS_CC ) == -1 )
ret = -1;
} zend_end_try();
{
static const char * usage =
"Usage: php\n"
- " php -[h|i|q|v|?] [<file>] [args...]\n"
- " Run in LSAPI mode when no parameter or only '-c' is specified\n"
- " Run in Command Line Interpreter mode when parameters are specified"
+ " php -[b|c|h|i|q|s|v|?] [<file>] [args...]\n"
+ " Run in LSAPI mode, only '-b', '-s' and '-c' are effective\n"
+ " Run in Command Line Interpreter mode when parameters are specified\n"
"\n"
+ " -b <address:port>|<port> Bind Path for external LSAPI Server mode\n"
" -c <path>|<file> Look for php.ini file in this directory\n"
" -h This help\n"
" -i PHP information\n"
" -q Quiet-mode. Suppress HTTP Header output.\n"
+ " -s Display colour syntax highlighted source.\n"
" -v Version number\n"
" -? This help\n"
"\n"
" args... Arguments passed to script.\n";
php_output_startup();
php_output_activate(TSRMLS_C);
-// SG(headers_sent) = 1;
php_printf( usage );
php_end_ob_buffers(1 TSRMLS_CC);
}
static int parse_opt( int argc, char * argv[], int *climode,
- char **php_ini_path )
+ char **php_ini_path, char ** php_bind )
{
char ** p = &argv[1];
char ** argend= &argv[argc];
++p;
switch( c )
{
+ case 'b':
+ if ( p >= argend )
+ {
+ fprintf( stderr, "TCP or socket address must be specified following '-b' option.\n");
+ return -1;
+ }
+ *php_bind = *p++;
+ break;
+
case 'c':
if ( p >= argend )
{
}
*php_ini_path = *p++;
break;
+ case 's':
+ source_highlight = 1;
+ break;
case 'h':
case 'i':
case 'q':
switch( c )
{
case 'q':
-// SG(headers_sent) = 1;
-// SG(request_info).no_headers = 1;
break;
case 'i':
if (php_request_startup(TSRMLS_C) != FAILURE)
{
-// SG(headers_sent) = 1;
-// SG(request_info).no_headers = 1;
php_print_info(0xFFFFFFFF TSRMLS_CC);
php_end_ob_buffers(1 TSRMLS_CC);
php_request_shutdown( NULL );
case 'v':
if (php_request_startup(TSRMLS_C) != FAILURE)
{
-// SG(headers_sent) = 1;
-// SG(request_info).no_headers = 1;
#if ZEND_DEBUG
php_printf("PHP %s (%s) (built: %s %s) (DEBUG)\nCopyright (c) 1997-2004 The PHP Group\n%s", PHP_VERSION, sapi_module.name, __DATE__, __TIME__, get_zend_version());
#else
break;
case 'c':
++p;
+ /* fall through */
+ case 's':
break;
+
case 'h':
case '?':
default:
}
else
{
+ if (source_highlight)
+ {
+ zend_syntax_highlighter_ini syntax_highlighter_ini;
+
+ php_get_highlight_struct(&syntax_highlighter_ini);
+ highlight_file(SG(request_info).path_translated, &syntax_highlighter_ini TSRMLS_CC);
+ }
+ else
+ {
+ file_handle.filename = *p;
+ file_handle.free_filename = 0;
+ file_handle.opened_path = NULL;
+
+ php_execute_script(&file_handle TSRMLS_CC);
+ }
- file_handle.filename = *p;
- file_handle.free_filename = 0;
- file_handle.opened_path = NULL;
-
- php_execute_script(&file_handle TSRMLS_CC);
php_request_shutdown( NULL );
}
}
pid = wait( &status );
running--;
}
- kill( -getpgrp(), s_stop );
+ kill( -getpgrp(), SIGUSR1 );
exit( 0 );
}
int main( int argc, char * argv[] )
{
int ret;
- int max_requests = 500;
- int requests = 0;
+ int bindFd;
char * php_ini_path = NULL;
+ char * php_bind = NULL;
+ char * p;
+ int n;
int climode = 0;
-
+
#ifdef HAVE_SIGNAL_H
#if defined(SIGPIPE) && defined(SIG_IGN)
signal(SIGPIPE, SIG_IGN);
if (argc > 1 )
{
- if ( parse_opt( argc, argv, &climode, &php_ini_path ) == -1 )
+ if ( parse_opt( argc, argv, &climode, &php_ini_path, &php_bind ) == -1 )
return 1;
}
if ( climode )
if ( php_ini_path )
lsapi_sapi_module.php_ini_path_override = php_ini_path;
- if (php_module_startup(&lsapi_sapi_module, NULL, 0) == FAILURE) {
+ if (php_module_startup(&lsapi_sapi_module, &litespeed_module_entry, 1) == FAILURE) {
#ifdef ZTS
tsrm_shutdown();
#endif
return cli_main(argc, argv);
}
- if( getenv( "PHP_LSAPI_MAX_REQUESTS" ))
+
+ if ( php_bind )
{
- max_requests = atoi( getenv( "PHP_LSAPI_MAX_REQUESTS" ));
- if( !max_requests )
+ bindFd = LSAPI_CreateListenSock( php_bind, 10 );
+ if ( bindFd == -1 )
{
fprintf( stderr,
- "PHP_LSAPI_MAX_REQUESTS is not valid\n" );
- exit( 1 );
+ "Failed to bind socket [%s]: %s\n", php_bind, strerror( errno ) );
+ exit( 2 );
+ }
+ if ( bindFd != 0 )
+ {
+ dup2( bindFd, 0 );
+ close( bindFd );
}
}
-
+
LSAPI_Init();
+
+ LSAPI_Init_Env_Parameters( NULL );
- if (( getenv( "PHP_LSAPI_CHILDREN" ) )&& LSAPI_Is_Listen() )
- {
- int children = atoi( getenv( "PHP_LSAPI_CHILDREN" ));
- if ( children > 0 )
- start_children( children );
- }
+ if ( php_bind )
+ LSAPI_No_Check_ppid();
- while( LSAPI_Accept() >= 0 )
+ while( LSAPI_Prefork_Accept_r( &g_req ) >= 0 )
{
ret = processReq(TSRMLS_C);
LSAPI_Finish();
if ( ret )
break;
- requests++;
- if( max_requests && ( requests == max_requests ))
- {
- break;
- }
}
php_module_shutdown(TSRMLS_C);
return ret;
}
+
+/* LiteSpeed PHP module starts here */
+
+
+
+PHP_FUNCTION(litespeed_request_headers);
+PHP_FUNCTION(litespeed_response_headers);
+
+PHP_MINFO_FUNCTION(litespeed);
+
+zend_function_entry litespeed_functions[] = {
+ PHP_FE(litespeed_request_headers, NULL)
+ PHP_FE(litespeed_response_headers, NULL)
+ PHP_FALIAS(getallheaders, litespeed_request_headers, NULL)
+ PHP_FALIAS(apache_request_headers, litespeed_request_headers, NULL)
+ PHP_FALIAS(apache_response_headers, litespeed_response_headers, NULL)
+ {NULL, NULL, NULL}
+};
+
+static PHP_MINIT_FUNCTION(litespeed)
+{
+ //REGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+
+static PHP_MSHUTDOWN_FUNCTION(litespeed)
+{
+ //UNREGISTER_INI_ENTRIES();
+ return SUCCESS;
+}
+
+zend_module_entry litespeed_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "litespeed",
+ litespeed_functions,
+ PHP_MINIT(litespeed),
+ PHP_MSHUTDOWN(litespeed),
+ NULL,
+ NULL,
+ NULL,
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+static int add_associate_array( const char * pKey, int keyLen, const char * pValue, int valLen,
+ void * arg )
+{
+ add_assoc_string_ex( (zval *)arg, (char *)pKey, keyLen+1, (char *)pValue, 1 );
+ return 1;
+}
+
+
+/* {{{ proto array litespeed_request_headers(void)
+ Fetch all HTTP request headers */
+PHP_FUNCTION(litespeed_request_headers)
+{
+ //TODO:
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+ array_init(return_value);
+
+ LSAPI_ForeachOrgHeader( add_associate_array, return_value );
+
+}
+/* }}} */
+
+
+
+/* {{{ proto array litespeed_response_headers(void)
+ Fetch all HTTP response headers */
+PHP_FUNCTION(litespeed_response_headers)
+{
+ sapi_header_struct *h;
+ zend_llist_position pos;
+ char * p;
+ int len;
+ char headerBuf[SAPI_LSAPI_MAX_HEADER_LENGTH];
+
+ if (ZEND_NUM_ARGS() > 0) {
+ WRONG_PARAM_COUNT;
+ }
+
+ if (!&SG(sapi_headers).headers) {
+ RETURN_FALSE;
+ }
+ array_init(return_value);
+
+ h = zend_llist_get_first_ex(&SG(sapi_headers).headers, &pos);
+ while (h)
+ {
+ if ( h->header_len > 0 )
+ {
+ p = strchr( h->header, ':' );
+ len = p - h->header;
+ if (( p )&&( len > 0 ))
+ {
+ memmove( headerBuf, h->header, len );
+ while( len > 0 && (isspace( headerBuf[len-1])) )
+ --len;
+ headerBuf[len] = 0;
+ if ( len )
+ {
+ while( isspace(*++p));
+ add_assoc_string_ex(return_value, headerBuf, len+1, p, 1 );
+ }
+ }
+ }
+ h = zend_llist_get_next_ex(&SG(sapi_headers).headers, &pos);
+ }
+}
+
+/* }}} */
+
+
/*
* Local variables:
* tab-width: 4
#include <errno.h>
#include <fcntl.h>
-//#include <arpa/inet.h>
-#include <sys/types.h>
+#include <arpa/inet.h>
+#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <sys/un.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/uio.h>
+#include <sys/wait.h>
+#include <time.h>
#include <unistd.h>
#define LSAPI_ST_REQ_HEADER 1
static int g_inited = 0;
static int g_running = 1;
-LSAPI_Request g_req;
+static int s_ppid;
+LSAPI_Request g_req = { -1, -1 };
void Flush_RespBuf_r( LSAPI_Request * pReq );
{
"HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET",
"HTTP_ACCEPT_ENCODING",
- "HTTP_ACCEPT_LANG", "HTTP_AUTHORIZATION",
+ "HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHORIZATION",
"HTTP_CONNECTION", "CONTENT_TYPE",
"CONTENT_LENGTH", "HTTP_COOKIE", "HTTP_COOKIE2",
"HTTP_HOST", "HTTP_PRAGMA",
"HTTP_REFERER", "HTTP_USER_AGENT",
- "HTTP_CACHE_CTRL",
+ "HTTP_CACHE_CONTROL",
"HTTP_IF_MODIFIED_SINCE", "HTTP_IF_MATCH",
"HTTP_IF_NONE_MATCH",
"HTTP_IF_RANGE",
"HTTP_IF_UNMODIFIED_SINCE",
- "HTTP_KEEPALIVE",
+ "HTTP_KEEP_ALIVE",
"HTTP_RANGE",
"HTTP_X_FORWARDED_FOR",
"HTTP_VIA",
};
static int CGI_HEADER_LEN[H_TRANSFER_ENCODING+1] =
-{ 11, 19, 20, 16, 18, 15, 12, 14, 11, 12, 9, 11, 12, 15, 15,
- 22, 13, 18, 13, 24, 14, 10, 20, 8, 22 };
+{ 11, 19, 20, 20, 18, 15, 12, 14, 11, 12, 9, 11, 12, 15, 18,
+ 22, 13, 18, 13, 24, 15, 10, 20, 8, 22 };
+
+static const char *HTTP_HEADERS[H_TRANSFER_ENCODING+1] =
+{
+ "Accept", "Accept-Charset",
+ "Accept-Encoding",
+ "Accept-Language", "Authorization",
+ "Connection", "Content-Type",
+ "Content-Length", "Cookie", "Cookie2",
+ "Host", "Pragma",
+ "Referer", "User-Agent",
+ "Cache-Control",
+ "If-Modified-Since", "If-Match",
+ "If-None-Match",
+ "If-Range",
+ "If-Unmodified-Since",
+ "Keep-Alive",
+ "Range",
+ "X-Forwarded-For",
+ "Via",
+ "Transfer-Encoding"
+};
+
+static int HTTP_HEADER_LEN[H_TRANSFER_ENCODING+1] =
+{ 6, 14, 15, 15, 13, 10, 12, 14, 6, 7, 4, 6, 7, 10, //user-agent
+ 13,17, 8, 13, 8, 19, 10, 5, 15, 3, 17
+};
+
static void lsapi_sigpipe( int sig )
{
}
static inline void lsapi_buildPacketHeader( struct lsapi_packet_header * pHeader,
char type, int len )
{
- pHeader->m_versionB0 = LSAPI_VERSION_B0; //LSAPI protocol version
+ pHeader->m_versionB0 = LSAPI_VERSION_B0; /* LSAPI protocol version */
pHeader->m_versionB1 = LSAPI_VERSION_B1;
pHeader->m_type = type;
pHeader->m_flag = LSAPI_ENDIAN;
}
}
-//static int lsapi_write( int fd, const void * pBuf, int len )
-//{
-// int ret;
-// const char * pCur;
-// const char * pEnd;
-// if ( len == 0 )
-// return 0;
-// pCur = (const char *)pBuf;
-// pEnd = pCur + len;
-// while( g_running && (pCur < pEnd) )
-// {
-// ret = write( fd, pCur, pEnd - pCur );
-// if ( ret >= 0)
-// pCur += ret;
-// else if (( ret == -1 )&&( errno != EINTR ))
-// return ret;
-// }
-// return pCur - (const char *)pBuf;
-//}
+/*
+static int lsapi_write( int fd, const void * pBuf, int len )
+{
+ int ret;
+ const char * pCur;
+ const char * pEnd;
+ if ( len == 0 )
+ return 0;
+ pCur = (const char *)pBuf;
+ pEnd = pCur + len;
+ while( g_running && (pCur < pEnd) )
+ {
+ ret = write( fd, pCur, pEnd - pCur );
+ if ( ret >= 0)
+ pCur += ret;
+ else if (( ret == -1 )&&( errno != EINTR ))
+ return ret;
+ }
+ return pCur - (const char *)pBuf;
+}
+*/
static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen )
{
}
}
}
- else if (( ret == -1 )&&( errno != EINTR ))
- return ret;
+ else if ( ret == -1 )
+ {
+ if ( errno == EAGAIN )
+ {
+ if ( totalLen - left > 0 )
+ return totalLen - left;
+ else
+ return -1;
+ }
+ else if ( errno != EINTR )
+ return ret;
+ }
}
return totalLen - left;
}
-//static int getTotalLen( struct iovec * pVec, int count )
-//{
-// struct iovec * pEnd = pVec + count;
-// int total = 0;
-// while( pVec < pEnd )
-// {
-// total += pVec->iov_len;
-// ++pVec;
-// }
-// return total;
-//}
-
+/*
+static int getTotalLen( struct iovec * pVec, int count )
+{
+ struct iovec * pEnd = pVec + count;
+ int total = 0;
+ while( pVec < pEnd )
+ {
+ total += pVec->iov_len;
+ ++pVec;
+ }
+ return total;
+}
+*/
static inline int allocateBuf( LSAPI_Request * pReq, int size )
{
static inline int isPipe( int fd )
{
- char achPeer[128];
- int len = 128;
+ char achPeer[128];
+ socklen_t len = 128;
if (( getpeername( fd, (struct sockaddr *)achPeer, &len ) != 0 )&&
( errno == ENOTCONN ))
return 0;
if ( packetLen > LSAPI_MAX_HEADER_LEN )
return -1;
- if ( packetLen > pReq->m_reqBufSize )
+ if ( packetLen + 1024 > pReq->m_reqBufSize )
{
- if ( allocateBuf( pReq, packetLen ) == -1 )
+ if ( allocateBuf( pReq, packetLen + 1024 ) == -1 )
return -1;
}
while( packetLen > pReq->m_bufRead )
if ( LSAPI_InitRequest( &g_req, LSAPI_SOCK_FILENO ) == -1 )
return -1;
g_inited = 1;
+ s_ppid = getppid();
}
return 0;
}
-void LSAPI_stop(void)
+void LSAPI_Stop(void)
{
g_running = 0;
}
+int LSAPI_IsRunning(void)
+{
+ return g_running;
+}
+
int LSAPI_InitRequest( LSAPI_Request * pReq, int fd )
{
if ( !pReq )
int LSAPI_Accept_r( LSAPI_Request * pReq )
{
- char achPeer[128];
- int len;
- int nodelay = 1;
+ char achPeer[128];
+ socklen_t len;
+ int nodelay = 1;
+
if ( !pReq )
return -1;
if ( LSAPI_Finish_r( pReq ) == -1 )
len = sizeof( achPeer );
pReq->m_fd = accept( pReq->m_fdListen,
(struct sockaddr *)&achPeer, &len );
- if (( pReq->m_fd == -1 )&&( errno == EINTR ))
- continue;
- if (( pReq->m_fd != -1 )&&
- (((struct sockaddr *)&achPeer)->sa_family == AF_INET ))
+ if ( pReq->m_fd == -1 )
+ {
+ if (( errno == EINTR )||( errno == EAGAIN))
+ continue;
+ else
+ return -1;
+ }
+ else if (((struct sockaddr *)&achPeer)->sa_family == AF_INET )
{
setsockopt(pReq->m_fd, IPPROTO_TCP, TCP_NODELAY,
(char *)&nodelay, sizeof(nodelay));
int LSAPI_Finish_r( LSAPI_Request * pReq )
{
- //finish req body
+ /* finish req body */
if ( !pReq )
return -1;
if (pReq->m_reqState)
return pReq->m_pHttpHeader + off;
}
+static int readBodyToReqBuf( LSAPI_Request * pReq )
+{
+ int bodyLeft;
+ int len = pReq->m_bufRead - pReq->m_bufProcessed;
+ if ( len > 0 )
+ return len;
+ pReq->m_bufRead = pReq->m_bufProcessed = pReq->m_pHeader->m_pktHeader.m_packetLen.m_iLen;
+
+ bodyLeft = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead;
+ len = pReq->m_reqBufSize - pReq->m_bufRead;
+ if ( len < 0 )
+ return -1;
+ if ( len > bodyLeft )
+ len = bodyLeft;
+
+ len = lsapi_read( pReq->m_fd, pReq->m_pReqBuf + pReq->m_bufRead, len );
+ if ( len > 0 )
+ pReq->m_bufRead += len;
+ return len;
+}
+
+
+int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq )
+{
+ if (!pReq || (pReq->m_fd ==-1) )
+ return EOF;
+ if ( pReq->m_bufProcessed >= pReq->m_bufRead )
+ {
+ if ( readBodyToReqBuf( pReq ) <= 0 )
+ return EOF;
+ }
+ ++pReq->m_reqBodyRead;
+ return (unsigned char)*(pReq->m_pReqBuf + pReq->m_bufProcessed++);
+}
+
+
+
+int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, int bufLen, int *getLF )
+{
+ int len;
+ int left;
+ char * pBufEnd = pBuf + bufLen - 1;
+ char * pBufCur = pBuf;
+ char * pCur;
+ char * p;
+ if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF )
+ return -1;
+ *getLF = 0;
+ while( (left = pBufEnd - pBufCur ) > 0 )
+ {
+
+ len = pReq->m_bufRead - pReq->m_bufProcessed;
+ if ( len <= 0 )
+ {
+ if ( (len = readBodyToReqBuf( pReq )) <= 0 )
+ {
+ *getLF = 1;
+ break;
+ }
+ }
+ if ( len > left )
+ len = left;
+ pCur = pReq->m_pReqBuf + pReq->m_bufProcessed;
+ p = memchr( pCur, '\n', len );
+ if ( p )
+ len = p - pCur + 1;
+ memmove( pBufCur, pCur, len );
+ pBufCur += len;
+ pReq->m_bufProcessed += len;
+
+ pReq->m_reqBodyRead += len;
+
+ if ( p )
+ {
+ *getLF = 1;
+ break;
+ }
+ }
+ *pBufCur = 0;
+
+ return pBufCur - pBuf;
+}
int LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, int bufLen )
{
int len;
int total;
- //char *pOldBuf = pBuf;
- if (!pReq || ( !pBuf )||(bufLen < 0 ))
+ /* char *pOldBuf = pBuf; */
+ if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 ))
return -1;
total = pReq->m_pHeader->m_reqBodyLen - pReq->m_reqBodyRead;
pBuf += len;
bufLen -= len;
}
- else if ( len < 0 )
- return -1;
+ else if ( len <= 0 )
+ {
+ if ( !total)
+ return -1;
+ break;
+ }
}
pReq->m_reqBodyRead += total;
return total;
}
-//int LSAPI_Write( const char * pBuf, int len )
-//{
-// return LSAPI_Write_r( &g_req, pBuf, len );
-//}
-
int LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, int len )
{
struct lsapi_packet_header * pHeader;
int toWrite;
int packetLen;
- if ( !pReq || !pBuf )
+ if ( !pReq || !pBuf || (pReq->m_fd == -1) )
return -1;
if ( len < pReq->m_pRespBufEnd - pReq->m_pRespBufPos )
{
int n;
if ( !pReq )
return -1;
+ n = pReq->m_pIovecCur - pReq->m_pIovecToWrite;
+ if (( 0 == n )&&( pReq->m_pRespBufPos == pReq->m_pRespBuf ))
+ return 0;
+ if ( pReq->m_fd == -1 )
+ {
+ pReq->m_pRespBufPos = pReq->m_pRespBuf;
+ pReq->m_totalLen = 0;
+ pReq->m_pIovecCur = pReq->m_pIovecToWrite = pReq->m_pIovec;
+ return -1;
+ }
if ( pReq->m_reqState & LSAPI_ST_RESP_HEADER )
{
LSAPI_FinalizeRespHeaders_r( pReq );
{
Flush_RespBuf_r( pReq );
}
+
n = pReq->m_pIovecCur - pReq->m_pIovecToWrite;
if ( n > 0 )
{
struct iovec iov[2];
struct iovec *pIov;
+ if ( !pReq )
+ return -1;
+ if (( pReq->m_fd == -1 )||(pReq->m_fd == pReq->m_fdListen ))
+ return write( 2, pBuf, len );
if ( pReq->m_pRespBufPos != pReq->m_pRespBuf )
{
LSAPI_Flush_r( pReq );
return p - pBuf;
}
+static char * GetHeaderVar( LSAPI_Request * pReq, const char * name )
+{
+ int i;
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i )
+ {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] )
+ {
+ if ( strcmp( name, CGI_HEADERS[i] ) == 0 )
+ return pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i];
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 )
+ {
+ const char *p;
+ char *pKey;
+ char *pKeyEnd;
+ int keyLen;
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd )
+ {
+ pKey = pReq->m_pHttpHeader + pCur->nameOff;
+ keyLen = pCur->nameLen;
+ pKeyEnd = pKey + keyLen;
+ p = &name[5];
+
+ while(( pKey < pKeyEnd )&&( *p ))
+ {
+ char ch = toupper( *pKey );
+ if ((ch != *p )||(( *p == '_' )&&( ch != '-')))
+ break;
+ ++p; ++pKey;
+ }
+ if (( pKey == pKeyEnd )&& (!*p ))
+ return pReq->m_pHttpHeader + pCur->valueOff;
+ ++pCur;
+ }
+ }
+ return NULL;
+}
+
char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name )
{
struct LSAPI_key_value_pair * pEnd = pBegin + pReq->m_pHeader->m_cntEnv;
if ( !pReq || !name )
return NULL;
+ if ( strncmp( name, "HTTP_", 5 ) == 0 )
+ {
+ return GetHeaderVar( pReq, name );
+ }
while( pBegin < pEnd )
{
if ( strcmp( name, pBegin->pKey ) == 0 )
return NULL;
}
+int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq,
+ LSAPI_CB_EnvHandler fn, void * arg )
+{
+ int i;
+ int len = 0;
+ char * pValue;
+ int ret;
+ int count = 0;
+ if ( !pReq || !fn )
+ return -1;
+ for( i = 0; i < H_TRANSFER_ENCODING; ++i )
+ {
+ if ( pReq->m_pHeaderIndex->m_headerOff[i] )
+ {
+ len = pReq->m_pHeaderIndex->m_headerLen[i];
+ pValue = pReq->m_pHttpHeader + pReq->m_pHeaderIndex->m_headerOff[i];
+ *(pValue + len ) = 0;
+ ret = (*fn)( HTTP_HEADERS[i], HTTP_HEADER_LEN[i],
+ pValue, len, arg );
+ ++count;
+ if ( ret <= 0 )
+ return ret;
+ }
+ }
+ if ( pReq->m_pHeader->m_cntUnknownHeaders > 0 )
+ {
+ char *pKey;
+ int keyLen;
+ struct lsapi_header_offset * pCur, *pEnd;
+ pCur = pReq->m_pUnknownHeader;
+ pEnd = pCur + pReq->m_pHeader->m_cntUnknownHeaders;
+ while( pCur < pEnd )
+ {
+ pKey = pReq->m_pHttpHeader + pCur->nameOff;
+ keyLen = pCur->nameLen;
+
+ pValue = pReq->m_pHttpHeader + pCur->valueOff;
+ *(pValue + pCur->valueLen ) = 0;
+ ret = (*fn)( pKey, keyLen,
+ pValue, pCur->valueLen, arg );
+ if ( ret <= 0 )
+ return ret;
+ ++pCur;
+ }
+ }
+ return count + pReq->m_pHeader->m_cntUnknownHeaders;
+
+}
+
int LSAPI_ForeachHeader_r( LSAPI_Request * pReq,
LSAPI_CB_EnvHandler fn, void * arg )
pValue = pReq->m_pHttpHeader + pCur->valueOff;
*(pValue + pCur->valueLen ) = 0;
- ret = (*fn)( achHeaderName, pCur->valueLen,
- pValue, len, arg );
+ ret = (*fn)( achHeaderName, keyLen,
+ pValue, pCur->valueLen, arg );
if ( ret <= 0 )
return ret;
++pCur;
memmove( pReq->m_pRespHeaderBufPos, pBuf, len );
pReq->m_pRespHeaderBufPos += len;
*pReq->m_pRespHeaderBufPos++ = 0;
- ++len; //add one byte padding for \0
+ ++len; /* add one byte padding for \0 */
pReq->m_respHeaderLen[pReq->m_respHeader.m_respInfo.m_cntHeaders] = len;
++pReq->m_respHeader.m_respInfo.m_cntHeaders;
return 0;
}
+int LSAPI_CreateListenSock2( const struct sockaddr * pServerAddr, int backlog )
+{
+ int ret;
+ int fd;
+ int flag = 1;
+ int addr_len;
+
+ switch( pServerAddr->sa_family )
+ {
+ case AF_INET:
+ addr_len = 16;
+ break;
+ case AF_INET6:
+ addr_len = sizeof( struct sockaddr_in6 );
+ break;
+ case AF_UNIX:
+ addr_len = sizeof( struct sockaddr_un );
+ unlink( ((struct sockaddr_un *)pServerAddr)->sun_path );
+ break;
+ default:
+ return -1;
+ }
+
+ fd = socket( pServerAddr->sa_family, SOCK_STREAM, 0 );
+ if ( fd == -1 )
+ return -1;
+
+ fcntl( fd, F_SETFD, FD_CLOEXEC );
+
+ if(setsockopt( fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)( &flag ), sizeof(flag)) == 0)
+ {
+ ret = bind( fd, pServerAddr, addr_len );
+ if ( !ret )
+ {
+ ret = listen( fd, backlog );
+ if ( !ret )
+ return fd;
+ }
+ }
+
+ ret = errno;
+ close(fd);
+ errno = ret;
+ return -1;
+
+}
+
+int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr )
+{
+ char achAddr[256];
+ char * p = achAddr;
+ char * pEnd;
+ struct addrinfo *res, hints;
+ int doAddrInfo = 0;
+ int port;
+
+ if ( !pBind )
+ return -1;
+ while( isspace( *p ) )
+ ++pBind;
+
+ strncpy( achAddr, pBind, 256 );
+
+ switch( *p )
+ {
+ case '/':
+ pAddr->sa_family = AF_UNIX;
+ strncpy( ((struct sockaddr_un *)pAddr)->sun_path, p,
+ sizeof(((struct sockaddr_un *)pAddr)->sun_path) );
+ return 0;
+
+ case '[':
+ pAddr->sa_family = AF_INET6;
+ ++p;
+ pEnd = strchr( p, ']' );
+ if ( !pEnd )
+ return -1;
+ *pEnd++ = 0;
+
+ if ( *p == '*' )
+ {
+ strcpy( achAddr, "::" );
+ p = achAddr;
+ }
+ doAddrInfo = 1;
+ break;
+
+ default:
+ pAddr->sa_family = AF_INET;
+ pEnd = strchr( p, ':' );
+ if ( !pEnd )
+ return -1;
+ *pEnd++ = 0;
+
+ doAddrInfo = 0;
+ if ( *p == '*' )
+ {
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl(INADDR_ANY);
+ }
+ else if (!strcasecmp( p, "localhost" ) )
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = htonl( INADDR_LOOPBACK );
+ else
+ {
+ ((struct sockaddr_in *)pAddr)->sin_addr.s_addr = inet_addr( p );
+ if ( ((struct sockaddr_in *)pAddr)->sin_addr.s_addr == INADDR_BROADCAST)
+ {
+ doAddrInfo = 1;
+ }
+ }
+ break;
+ }
+ if ( *pEnd == ':' )
+ ++pEnd;
+
+ port = atoi( pEnd );
+ if (( port <= 0 )||( port > 655535 ))
+ return -1;
+ if ( doAddrInfo )
+ {
+
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_family = pAddr->sa_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ if ( getaddrinfo(p, NULL, &hints, &res) )
+ {
+ return -1;
+ }
+
+ memcpy(pAddr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ }
+
+ if ( pAddr->sa_family == AF_INET )
+ ((struct sockaddr_in *)pAddr)->sin_port = htons( port );
+ else
+ ((struct sockaddr_in6 *)pAddr)->sin6_port = htons( port );
+ return 0;
+
+}
+
+int LSAPI_CreateListenSock( const char * pBind, int backlog )
+{
+ char serverAddr[128];
+ int ret;
+ int fd = -1;
+ ret = LSAPI_ParseSockAddr( pBind, (struct sockaddr *)serverAddr );
+ if ( !ret )
+ {
+ fd = LSAPI_CreateListenSock2( (struct sockaddr *)serverAddr, backlog );
+ }
+ return fd;
+}
+
+static fn_select_t g_fnSelect = select;
+
+typedef struct _lsapi_child_status
+{
+ int m_pid;
+
+ volatile short m_iKillSent;
+ volatile short m_inProcess;
+
+ volatile long m_tmWaitBegin;
+ volatile long m_tmReqBegin;
+ volatile long m_tmLastCheckPoint;
+}
+lsapi_child_status;
+
+static lsapi_child_status * s_pChildStatus = NULL;
+
+typedef struct _lsapi_prefork_server
+{
+ int m_fd;
+ int m_iMaxChildren;
+ int m_iExtraChildren;
+ int m_iCurChildren;
+ int m_iMaxIdleChildren;
+ int m_iServerMaxIdle;
+ int m_iChildrenMaxIdleTime;
+ int m_iMaxReqProcessTime;
+ int m_iAvoidFork;
+
+ lsapi_child_status * m_pChildrenStatus;
+
+}lsapi_prefork_server;
+
+static lsapi_prefork_server * g_prefork_server = NULL;
+
+int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork )
+{
+ if ( g_prefork_server )
+ return 0;
+ if ( max_children <= 1 )
+ return -1;
+ if ( max_children >= 10000)
+ max_children = 10000;
+
+
+ g_prefork_server = (lsapi_prefork_server *)malloc( sizeof( lsapi_prefork_server ) );
+ if ( !g_prefork_server )
+ return -1;
+ memset( g_prefork_server, 0, sizeof( lsapi_prefork_server ) );
+ if ( fp != NULL )
+ g_fnSelect = fp;
+
+ s_ppid = getppid();
+ g_prefork_server->m_iAvoidFork = avoidFork;
+ g_prefork_server->m_iMaxChildren = max_children;
+
+ g_prefork_server->m_iExtraChildren = ( avoidFork ) ? 0 : (max_children / 3) ;
+ g_prefork_server->m_iMaxIdleChildren = ( avoidFork ) ? (max_children + 1) : (max_children / 3);
+ g_prefork_server->m_iChildrenMaxIdleTime = 300;
+ g_prefork_server->m_iMaxReqProcessTime = 300;
+ return 0;
+}
+
+void LSAPI_Set_Server_fd( int fd )
+{
+ if( g_prefork_server )
+ g_prefork_server->m_fd = fd;
+}
+
+
+static int lsapi_accept( int fdListen )
+{
+ int fd;
+ int nodelay = 1;
+ socklen_t len;
+ char achPeer[128];
+
+ len = sizeof( achPeer );
+ fd = accept( fdListen, (struct sockaddr *)&achPeer, &len );
+ if ( fd != -1 )
+ {
+ if (((struct sockaddr *)&achPeer)->sa_family == AF_INET )
+ {
+ setsockopt( fd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&nodelay, sizeof(nodelay));
+ }
+ }
+ return fd;
+
+}
+
+
+
+
+static int s_req_processed = 0;
+static int s_max_reqs = 10000;
+static int s_max_idle_secs = 300;
+
+static int s_stop;
+
+static void lsapi_cleanup(int signal)
+{
+ s_stop = signal;
+}
+
+static lsapi_child_status * find_child_status( int pid )
+{
+ lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus;
+ lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2;
+ while( pStatus < pEnd )
+ {
+ if ( pStatus->m_pid == pid )
+ return pStatus;
+ ++pStatus;
+ }
+ return NULL;
+}
+
+
+
+static void lsapi_sigchild( int signal )
+{
+ int status, pid;
+ lsapi_child_status * child_status;
+ while( 1 )
+ {
+ pid = waitpid( -1, &status, WNOHANG|WUNTRACED );
+ if ( pid <= 0 )
+ {
+ break;
+ }
+ child_status = find_child_status( pid );
+ if ( child_status )
+ child_status->m_pid = 0;
+ --g_prefork_server->m_iCurChildren;
+ }
+
+}
+
+static int lsapi_init_children_status()
+{
+ int size = 4096;
+
+ char * pBuf;
+ size = g_prefork_server->m_iMaxChildren * sizeof( lsapi_child_status ) * 2;
+ size = (size + 4095 ) / 4096 * 4096;
+ pBuf =( char*) mmap( NULL, size, PROT_READ | PROT_WRITE,
+ MAP_ANON | MAP_SHARED, -1, 0 );
+ if ( pBuf == MAP_FAILED )
+ {
+ perror( "Anonymous mmap() failed" );
+ return -1;
+ }
+ memset( pBuf, 0, size );
+ g_prefork_server->m_pChildrenStatus = (lsapi_child_status *)pBuf;
+ return 0;
+}
+
+static void lsapi_check_child_status( long tmCur )
+{
+ int idle = 0;
+ int tobekilled;
+ int dying = 0;
+ lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus;
+ lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatus + g_prefork_server->m_iMaxChildren * 2;
+ while( pStatus < pEnd )
+ {
+ tobekilled = pStatus->m_iKillSent;
+ if ( pStatus->m_pid != 0 )
+ {
+ if ( !tobekilled )
+ {
+ if ( !pStatus->m_inProcess )
+ {
+
+ if (( g_prefork_server->m_iCurChildren - dying > g_prefork_server->m_iMaxChildren)||
+ ( idle >= g_prefork_server->m_iMaxIdleChildren ))
+ {
+ tobekilled = 1;
+ }
+ else
+ {
+ if (( s_max_idle_secs> 0)&&(tmCur - pStatus->m_tmWaitBegin > s_max_idle_secs + 5 ))
+ tobekilled = 1;
+ }
+ if ( !tobekilled )
+ ++idle;
+ }
+ else
+ {
+ if ( tmCur - pStatus->m_tmReqBegin >
+ g_prefork_server->m_iMaxReqProcessTime )
+ tobekilled = 1;
+ }
+ }
+ else
+ {
+ if ( pStatus->m_inProcess )
+ tobekilled = pStatus->m_iKillSent = 0;
+ }
+ if ( tobekilled )
+ {
+ tobekilled = 0;
+ if ( pStatus->m_iKillSent > 5 )
+ tobekilled = SIGKILL;
+ else if ( pStatus->m_iKillSent == 3 )
+ tobekilled = SIGTERM;
+ else if ( pStatus->m_iKillSent == 1 )
+ {
+ tobekilled = SIGUSR1;
+ }
+ if ( tobekilled )
+ kill( pStatus->m_pid, tobekilled );
+ ++pStatus->m_iKillSent;
+ ++dying;
+ }
+
+ }
+ else
+ ++dying;
+ ++pStatus;
+ }
+}
+
+static int lsapi_all_children_must_die()
+{
+ int maxWait;
+ int sec =0;
+ g_prefork_server->m_iMaxReqProcessTime = 10;
+ g_prefork_server->m_iMaxIdleChildren = -1;
+ maxWait = 15;
+
+ while( g_prefork_server->m_iCurChildren && (sec < maxWait) )
+ {
+ lsapi_check_child_status(time(NULL));
+ sleep( 1 );
+ sec++;
+ }
+ if ( g_prefork_server->m_iCurChildren != 0 )
+ kill( -getpgrp(), SIGKILL );
+ return 0;
+}
+
+
+
+static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Request * pReq )
+{
+ struct sigaction act, old_term, old_quit, old_int,
+ old_usr1, old_child;
+ lsapi_child_status * child_status;
+ int wait_secs = 0;
+ int ret = 0;
+ int pid;
+ time_t lastTime = 0;
+ time_t curTime = 0;
+ fd_set readfds;
+ struct timeval timeout;
+
+ lsapi_init_children_status();
+
+ setsid();
+
+ act.sa_flags = 0;
+ act.sa_handler = lsapi_sigchild;
+ if( sigaction( SIGCHLD, &act, &old_child ) )
+ {
+ perror( "Can't set signal handler for SIGCHILD" );
+ return -1;
+ }
+
+ /* Set up handler to kill children upon exit */
+ act.sa_flags = 0;
+ act.sa_handler = lsapi_cleanup;
+ if( sigaction( SIGTERM, &act, &old_term ) ||
+ sigaction( SIGINT, &act, &old_int ) ||
+ sigaction( SIGUSR1, &act, &old_usr1 ) ||
+ sigaction( SIGQUIT, &act, &old_quit ))
+ {
+ perror( "Can't set signals" );
+ return -1;
+ }
+ s_stop = 0;
+ while( !s_stop )
+ {
+ if ( ret )
+ curTime = time( NULL );
+ else
+ ++curTime;
+ if (curTime != lastTime )
+ {
+ lastTime = curTime;
+ if (s_ppid && (getppid() != s_ppid ))
+ break;
+ lsapi_check_child_status(curTime );
+ if (pServer->m_iServerMaxIdle)
+ {
+ if ( pServer->m_iCurChildren <= 0 )
+ {
+ ++wait_secs;
+ if ( wait_secs > pServer->m_iServerMaxIdle )
+ return -1;
+ }
+ else
+ wait_secs = 0;
+ }
+ }
+
+ if ( pServer->m_iCurChildren >= (pServer->m_iMaxChildren + pServer->m_iExtraChildren ) )
+ {
+ usleep( 100000 );
+ continue;
+ }
+
+ FD_ZERO( &readfds );
+ FD_SET( pServer->m_fd, &readfds );
+ timeout.tv_sec = 1; timeout.tv_usec = 0;
+ if ((ret = (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout)) == 1 )
+ {
+ if ( pServer->m_iCurChildren >= 0 )
+ {
+ usleep( 10 );
+ FD_ZERO( &readfds );
+ FD_SET( pServer->m_fd, &readfds );
+ timeout.tv_sec = 0; timeout.tv_usec = 0;
+ if ( (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout) == 0 )
+ continue;
+ }
+ }
+ else if ( ret == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ /* perror( "select()" ); */
+ break;
+ }
+ else
+ {
+ continue;
+ }
+
+ pReq->m_fd = lsapi_accept( pServer->m_fd );
+ if ( pReq->m_fd != -1 )
+ {
+ child_status = find_child_status( 0 );
+ pid = fork();
+ if ( !pid )
+ {
+ g_prefork_server = NULL;
+ s_ppid = getppid();
+ s_req_processed = 0;
+ s_pChildStatus = child_status;
+ child_status->m_iKillSent = 0;
+
+ /* don't catch our signals */
+ sigaction( SIGCHLD, &old_child, 0 );
+ sigaction( SIGTERM, &old_term, 0 );
+ sigaction( SIGQUIT, &old_quit, 0 );
+ sigaction( SIGINT, &old_int, 0 );
+ sigaction( SIGUSR1, &old_usr1, 0 );
+ return 0;
+ }
+ else if ( pid == -1 )
+ {
+ perror( "fork() failed, please increase process limit" );
+ }
+ else
+ {
+ ++pServer->m_iCurChildren;
+ if ( child_status )
+ {
+ child_status->m_pid = pid;
+ child_status->m_iKillSent = 0;
+ child_status->m_tmWaitBegin = time(NULL);
+ }
+ }
+ close( pReq->m_fd );
+ pReq->m_fd = -1;
+
+ }
+ else
+ {
+ if (( errno == EINTR )||( errno == EAGAIN))
+ continue;
+ perror( "accept() failed" );
+ return -1;
+ }
+ }
+ sigaction( SIGUSR1, &old_usr1, 0 );
+ kill( -getpgrp(), SIGUSR1 );
+ lsapi_all_children_must_die(); /* Sorry, children ;-) */
+ return -1;
+
+}
+
+int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq )
+{
+ int fd;
+ int ret;
+ int wait_secs;
+ fd_set readfds;
+ struct timeval timeout;
+
+ LSAPI_Finish_r( pReq );
+
+
+ if ( g_prefork_server )
+ {
+ if ( g_prefork_server->m_fd != -1 )
+ if ( lsapi_prefork_server_accept( g_prefork_server, pReq ) == -1 )
+ return -1;
+ }
+ if ( s_req_processed >= s_max_reqs )
+ return -1;
+
+ if ( s_pChildStatus )
+ {
+ s_pChildStatus->m_tmWaitBegin = time( NULL );
+ }
+
+ while( g_running )
+ {
+ if ( pReq->m_fd != -1 )
+ {
+ fd = pReq->m_fd;
+ }
+ else if ( pReq->m_fdListen != -1 )
+ fd = pReq->m_fdListen;
+ else
+ return -1;
+ wait_secs = 0;
+ while( 1 )
+ {
+ if ( !g_running )
+ return -1;
+ if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent ))
+ return -1;
+ FD_ZERO( &readfds );
+ FD_SET( fd, &readfds );
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 0;
+ ret = (*g_fnSelect)(fd+1, &readfds, NULL, NULL, &timeout);
+ if ( ret == 0 )
+ {
+ if ( s_pChildStatus )
+ {
+ s_pChildStatus->m_inProcess = 0;
+ }
+ ++wait_secs;
+ if (( s_max_idle_secs > 0 )&&(wait_secs >= s_max_idle_secs ))
+ return -1;
+ if ( s_ppid &&( getppid() != s_ppid))
+ return -1;
+ }
+ else if ( ret == -1 )
+ {
+ if ( errno == EINTR )
+ continue;
+ else
+ return -1;
+ }
+ else if ( ret >= 1 )
+ {
+ if (( s_pChildStatus )&&( s_pChildStatus->m_iKillSent ))
+ return -1;
+ if ( fd == pReq->m_fdListen )
+ {
+ pReq->m_fd = lsapi_accept( pReq->m_fdListen );
+ if ( pReq->m_fd != -1 )
+ {
+ fd = pReq->m_fd;
+ }
+ else
+ return -1;
+ }
+ else
+ break;
+ }
+ }
+
+ if ( !readReq( pReq ) )
+ {
+ if ( s_pChildStatus )
+ {
+ s_pChildStatus->m_inProcess = 1;
+ s_pChildStatus->m_tmReqBegin = s_pChildStatus->m_tmLastCheckPoint = time(NULL);
+ }
+ ++s_req_processed;
+ return 0;
+ }
+ lsapi_close( pReq->m_fd );
+ pReq->m_fd = -1;
+ LSAPI_Reset_r( pReq );
+ }
+ return -1;
+
+}
+
+void LSAPI_Set_Max_Reqs( int reqs )
+{ s_max_reqs = reqs; }
+
+void LSAPI_Set_Max_Idle( int secs )
+{ s_max_idle_secs = secs; }
+
+void LSAPI_Set_Max_Children( int maxChildren )
+{
+ if ( g_prefork_server )
+ g_prefork_server->m_iMaxChildren = maxChildren;
+}
+
+void LSAPI_Set_Extra_Children( int extraChildren )
+{
+ if (( g_prefork_server )&&( extraChildren >= 0 ))
+ g_prefork_server->m_iExtraChildren = extraChildren;
+}
+
+void LSAPI_Set_Max_Process_Time( int secs )
+{
+ if (( g_prefork_server )&&( secs > 0 ))
+ g_prefork_server->m_iMaxReqProcessTime = secs;
+}
+
+
+void LSAPI_Set_Max_Idle_Children( int maxIdleChld )
+{
+ if (( g_prefork_server )&&( maxIdleChld > 0 ))
+ g_prefork_server->m_iMaxIdleChildren = maxIdleChld;
+}
+
+void LSAPI_Set_Server_Max_Idle_Secs( int serverMaxIdle )
+{
+ if ( g_prefork_server )
+ g_prefork_server->m_iServerMaxIdle = serverMaxIdle;
+}
+
+
+void LSAPI_No_Check_ppid()
+{
+ s_ppid = 0;
+}
+
+#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
+#include <crt_externs.h>
+#else
+extern char ** environ;
+#endif
+static void unset_lsapi_envs()
+{
+ char **env;
+#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
+ env = *_NSGetEnviron();
+#else
+ env = environ;
+#endif
+ while( env != NULL && *env != NULL )
+ {
+ if (!strncmp(*env, "LSAPI_", 6) || !strncmp( *env, "PHP_LSAPI_", 10 ) )
+ {
+ char ** del = env;
+ do
+ *del = del[1];
+ while( *del++ );
+ }
+ else
+ ++env;
+ }
+}
+
+void LSAPI_Init_Env_Parameters( fn_select_t fp )
+{
+ const char *p;
+ int n;
+ int avoidFork = 0;
+ p = getenv( "PHP_LSAPI_MAX_REQUESTS" );
+ if ( !p )
+ p = getenv( "LSAPI_MAX_REQS" );
+ if ( p )
+ {
+ n = atoi( p );
+ if ( n > 0 )
+ LSAPI_Set_Max_Reqs( n );
+ }
+
+ p = getenv( "LSAPI_AVOID_FORK" );
+ if ( p )
+ {
+ avoidFork = atoi( p );
+ }
+
+ p = getenv( "LSAPI_MAX_IDLE" );
+ if ( p )
+ {
+ n = atoi( p );
+ LSAPI_Set_Max_Idle( n );
+ }
+
+ if ( LSAPI_Is_Listen() )
+ {
+ n = 0;
+ p = getenv( "PHP_LSAPI_CHILDREN" );
+ if ( !p )
+ p = getenv( "LSAPI_CHILDREN" );
+ if ( p )
+ n = atoi( p );
+ if ( n > 1 )
+ {
+ LSAPI_Init_Prefork_Server( n, fp, avoidFork );
+ LSAPI_Set_Server_fd( g_req.m_fdListen );
+ }
+
+ p = getenv( "LSAPI_EXTRA_CHILDREN" );
+ if ( p )
+ LSAPI_Set_Extra_Children( atoi( p ) );
+
+ p = getenv( "LSAPI_MAX_IDLE_CHILDREN" );
+ if ( p )
+ LSAPI_Set_Max_Idle_Children( atoi( p ) );
+
+ p = getenv( "LSAPI_PGRP_MAX_IDLE" );
+ if ( p )
+ {
+ LSAPI_Set_Server_Max_Idle_Secs( atoi( p ) );
+ }
+
+ p = getenv( "LSAPI_MAX_PROCESS_TIME" );
+ if ( p )
+ LSAPI_Set_Max_Process_Time( atoi( p ) );
+
+ if ( getenv( "LSAPI_PPID_NO_CHECK" ) )
+ {
+ LSAPI_No_Check_ppid();
+ }
+ }
+ unset_lsapi_envs();
+}