From 97ff815cbbfe524a46ab5e76312d19a4b3771079 Mon Sep 17 00:00:00 2001 From: George Wang Date: Tue, 27 Feb 2018 13:54:28 -0500 Subject: [PATCH] release LiteSpeed SAPI 7.0. add CRIU support. add support for [PATH=] and [HOST=] sections in php.ini --- sapi/litespeed/lsapi_main.c | 196 +++--- sapi/litespeed/lsapilib.c | 722 +++++++++++++++----- sapi/litespeed/lsapilib.h | 57 +- sapi/litespeed/lscriu.c | 1264 +++++++++++++++++++++++++++++++++++ sapi/litespeed/lscriu.h | 67 ++ 5 files changed, 2043 insertions(+), 263 deletions(-) create mode 100644 sapi/litespeed/lscriu.c create mode 100644 sapi/litespeed/lscriu.h diff --git a/sapi/litespeed/lsapi_main.c b/sapi/litespeed/lsapi_main.c index b111054bc02..a2f7ed8f00e 100644 --- a/sapi/litespeed/lsapi_main.c +++ b/sapi/litespeed/lsapi_main.c @@ -67,6 +67,9 @@ #include #include +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) +#include "lscriu.c" +#endif #define SAPI_LSAPI_MAX_HEADER_LENGTH 2048 @@ -91,6 +94,7 @@ static int ignore_php_ini = 0; static char * argv0 = NULL; static int engine = 1; static int parse_user_ini = 0; + #ifdef ZTS zend_compiler_globals *compiler_globals; zend_executor_globals *executor_globals; @@ -178,7 +182,7 @@ static size_t sapi_lsapi_ub_write(const char *str, size_t str_length) /* {{{ sapi_lsapi_flush */ -static void sapi_lsapi_flush( void * server_context ) +static void sapi_lsapi_flush(void * server_context) { if ( lsapi_mode ) { if ( LSAPI_Flush() == -1) { @@ -193,8 +197,7 @@ static void sapi_lsapi_flush( void * server_context ) */ static int sapi_lsapi_deactivate(void) { - if ( SG(request_info).path_translated ) - { + if ( SG(request_info).path_translated ) { efree( SG(request_info).path_translated ); SG(request_info).path_translated = NULL; } @@ -235,44 +238,44 @@ static int add_variable( const char * pKey, int keyLen, const char * pValue, int static void litespeed_php_import_environment_variables(zval *array_ptr) { - char buf[128]; - char **env, *p, *t = buf; - size_t alloc_size = sizeof(buf); - unsigned long nlen; /* ptrdiff_t is not portable */ + char buf[128]; + char **env, *p, *t = buf; + size_t alloc_size = sizeof(buf); + unsigned long nlen; /* ptrdiff_t is not portable */ if (Z_TYPE(PG(http_globals)[TRACK_VARS_ENV]) == IS_ARRAY && Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_ENV]) && zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_ENV])) > 0 - ) { + ) { zval_dtor(array_ptr); ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_ENV]); - return; + return; } else if (Z_TYPE(PG(http_globals)[TRACK_VARS_SERVER]) == IS_ARRAY && Z_ARR_P(array_ptr) != Z_ARR(PG(http_globals)[TRACK_VARS_SERVER]) && zend_hash_num_elements(Z_ARRVAL(PG(http_globals)[TRACK_VARS_SERVER])) > 0 - ) { + ) { zval_dtor(array_ptr); ZVAL_DUP(array_ptr, &PG(http_globals)[TRACK_VARS_SERVER]); - return; - } + return; + } - for (env = environ; env != NULL && *env != NULL; env++) { - p = strchr(*env, '='); - if (!p) { /* malformed entry? */ - continue; - } - nlen = p - *env; - if (nlen >= alloc_size) { - alloc_size = nlen + 64; - t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); - } - memcpy(t, *env, nlen); - t[nlen] = '\0'; - add_variable(t, nlen, p + 1, strlen( p + 1 ), array_ptr); - } - if (t != buf && t != NULL) { - efree(t); - } + for (env = environ; env != NULL && *env != NULL; env++) { + p = strchr(*env, '='); + if (!p) { /* malformed entry? */ + continue; + } + nlen = p - *env; + if (nlen >= alloc_size) { + alloc_size = nlen + 64; + t = (t == buf ? emalloc(alloc_size): erealloc(t, alloc_size)); + } + memcpy(t, *env, nlen); + t[nlen] = '\0'; + add_variable(t, nlen, p + 1, strlen( p + 1 ), array_ptr); + } + if (t != buf && t != NULL) { + efree(t); + } } /* {{{ sapi_lsapi_register_variables @@ -286,9 +289,9 @@ static void sapi_lsapi_register_variables(zval *track_vars_array) litespeed_php_import_environment_variables(track_vars_array); - LSAPI_ForeachHeader( add_variable, track_vars_array ); - LSAPI_ForeachEnv( add_variable, track_vars_array ); - add_variable("PHP_SELF", 8, php_self, strlen( php_self ), track_vars_array ); + LSAPI_ForeachHeader( add_variable, track_vars_array ); + LSAPI_ForeachEnv( add_variable, track_vars_array ); + add_variable("PHP_SELF", 8, php_self, strlen( php_self ), track_vars_array ); } else { php_import_environment_variables(track_vars_array); @@ -405,11 +408,53 @@ static void log_message (const char *fmt, ...) #define DEBUG_MESSAGE(fmt, ...) #endif -static int lsapi_activate_user_ini(TSRMLS_D); +static int lsapi_activate_user_ini(); -static int sapi_lsapi_activate(TSRMLS_D) +static int sapi_lsapi_activate() { - if (parse_user_ini && lsapi_activate_user_ini(TSRMLS_C) == FAILURE) { + char *path, *doc_root, *server_name; + size_t path_len, doc_root_len, server_name_len; + + /* PATH_TRANSLATED should be defined at this stage but better safe than sorry :) */ + if (!SG(request_info).path_translated) { + return FAILURE; + } + + if (php_ini_has_per_host_config()) { + server_name = sapi_lsapi_getenv("SERVER_NAME", 0); + /* SERVER_NAME should also be defined at this stage..but better check it anyway */ + if (server_name) { + server_name_len = strlen(server_name); + server_name = estrndup(server_name, server_name_len); + zend_str_tolower(server_name, server_name_len); + php_ini_activate_per_host_config(server_name, server_name_len); + efree(server_name); + } + } + + if (php_ini_has_per_dir_config()) { + /* Prepare search path */ + path_len = strlen(SG(request_info).path_translated); + + /* Make sure we have trailing slash! */ + if (!IS_SLASH(SG(request_info).path_translated[path_len])) { + path = emalloc(path_len + 2); + memcpy(path, SG(request_info).path_translated, path_len + 1); + path_len = zend_dirname(path, path_len); + path[path_len++] = DEFAULT_SLASH; + } else { + path = estrndup(SG(request_info).path_translated, path_len); + path_len = zend_dirname(path, path_len); + } + path[path_len] = 0; + + /* Activate per-dir-system-configuration defined in php.ini and stored into configuration_hash during startup */ + php_ini_activate_per_dir_config(path, path_len); /* Note: for global settings sake we check from root to path */ + + efree(path); + } + + if (parse_user_ini && lsapi_activate_user_ini() == FAILURE) { return FAILURE; } return SUCCESS; @@ -419,7 +464,7 @@ static int sapi_lsapi_activate(TSRMLS_D) static sapi_module_struct lsapi_sapi_module = { "litespeed", - "LiteSpeed V6.11", + "LiteSpeed V7.0", php_lsapi_startup, /* startup */ php_module_shutdown_wrapper, /* shutdown */ @@ -443,8 +488,8 @@ static sapi_module_struct lsapi_sapi_module = sapi_lsapi_register_variables, /* register server variables */ sapi_lsapi_log_message, /* Log message */ - NULL, /* Get request time */ - NULL, /* Child terminate */ + NULL, /* Get request time */ + NULL, /* Child terminate */ STANDARD_SAPI_MODULE_PROPERTIES @@ -455,7 +500,7 @@ static void init_request_info( void ) { char * pContentType = LSAPI_GetHeader( H_CONTENT_TYPE ); char * pAuth; - + SG(request_info).content_type = pContentType ? pContentType : ""; SG(request_info).request_method = LSAPI_GetRequestMethod(); SG(request_info).query_string = LSAPI_GetQueryString(); @@ -465,7 +510,7 @@ static void init_request_info( void ) /* It is not reset by zend engine, set it to 200. */ SG(sapi_headers).http_response_code = 200; - + pAuth = LSAPI_GetHeader( H_AUTHORIZATION ); php_handle_auth_data(pAuth); } @@ -521,9 +566,8 @@ static int lsapi_module_main(int show_source) static int alter_ini( const char * pKey, int keyLen, const char * pValue, int valLen, void * arg ) { -#if PHP_MAJOR_VERSION >= 7 zend_string * psKey; -#endif + int type = ZEND_INI_PERDIR; int stage = PHP_INI_STAGE_RUNTIME; if ( '\001' == *pKey ) { @@ -544,18 +588,12 @@ static int alter_ini( const char * pKey, int keyLen, const char * pValue, int va } else { -#if PHP_MAJOR_VERSION >= 7 --keyLen; psKey = zend_string_init(pKey, keyLen, 1); zend_alter_ini_entry_chars(psKey, (char *)pValue, valLen, type, stage); zend_string_release(psKey); -#else - zend_alter_ini_entry((char *)pKey, keyLen, - (char *)pValue, valLen, - type, stage); -#endif } } return 1; @@ -687,7 +725,6 @@ static int lsapi_activate_user_ini_mk_path(_lsapi_activate_user_ini_ctx *ctx, if (!path) return FAILURE; ctx->path_len = zend_dirname(path, ctx->path_len); - DEBUG_MESSAGE("dirname: %s", ctx->path); if (*fn_next) { rc = (*fn_next)(ctx, fn_next + 1); @@ -711,9 +748,7 @@ static int lsapi_activate_user_ini_mk_realpath(_lsapi_activate_user_ini_ctx *ctx } ctx->path = real_path; ctx->path_len = strlen(ctx->path); - DEBUG_MESSAGE("calculated tsrm realpath: %s", real_path); } else { - DEBUG_MESSAGE("%s is an absolute path", ctx->path); real_path = NULL; } @@ -734,10 +769,8 @@ static int lsapi_activate_user_ini_mk_user_config(_lsapi_activate_user_ini_ctx * /* Find cached config entry: If not found, create one */ ctx->entry = zend_hash_str_find_ptr(&user_config_cache, ctx->path, ctx->path_len); - if (ctx->entry) { - DEBUG_MESSAGE("found entry for %s", ctx->path); - } else { - DEBUG_MESSAGE("entry for %s not found, creating new entry", ctx->path); + if (!ctx->entry) + { ctx->entry = pemalloc(sizeof(user_config_cache_entry), 1); ctx->entry->expires = 0; zend_hash_init(&ctx->entry->user_config, 0, NULL, @@ -760,7 +793,6 @@ static void walk_down_the_path_callback(char* begin, _lsapi_activate_user_ini_ctx *ctx = data; char tmp = end[0]; end[0] = 0; - DEBUG_MESSAGE("parsing %s%c%s", begin, DEFAULT_SLASH, PG(user_ini_filename)); php_parse_user_ini_file(begin, PG(user_ini_filename), &ctx->entry->user_config); end[0] = tmp; } @@ -807,7 +839,6 @@ static int lsapi_activate_user_ini_finally(_lsapi_activate_user_ini_ctx *ctx, int rc = SUCCESS; fn_activate_user_ini_chain_t *fn_next = next; - DEBUG_MESSAGE("calling php_ini_activate_config()"); php_ini_activate_config(&ctx->entry->user_config, PHP_INI_PERDIR, PHP_INI_STAGE_HTACCESS); @@ -852,7 +883,7 @@ static void override_ini() } -static int processReq( void ) +static int processReq(void) { int ret = 0; zend_first_try { @@ -864,7 +895,7 @@ static int processReq( void ) override_ini(); if ( engine ) { - init_request_info( ); + init_request_info(); if ( lsapi_module_main( source_highlight ) == -1 ) { ret = -1; @@ -878,7 +909,7 @@ static int processReq( void ) return ret; } -static void cli_usage( void ) +static void cli_usage(void) { static const char * usage = "Usage: php\n" @@ -979,9 +1010,7 @@ static int cli_main( int argc, char * argv[] ) char ** argend= &argv[argc]; int ret = -1; int c; -#if PHP_MAJOR_VERSION >= 7 - zend_string * psKey; -#endif + zend_string *psKey; lsapi_mode = 0; /* enter CLI mode */ #ifdef PHP_WIN32 @@ -998,21 +1027,12 @@ static int cli_main( int argc, char * argv[] ) CG(in_compilation) = 0; /* not initialized but needed for several options */ SG(options) |= SAPI_OPTION_NO_CHDIR; -#if PHP_MAJOR_VERSION < 7 - EG(uninitialized_zval_ptr) = NULL; -#endif for( ini = ini_defaults; *ini; ini+=2 ) { -#if PHP_MAJOR_VERSION >= 7 - psKey = zend_string_init(*ini, strlen( *ini ), 1); + psKey = zend_string_init(*ini, strlen( *ini ), 1); zend_alter_ini_entry_chars(psKey, (char *)*(ini+1), strlen( *(ini+1) ), PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE); - zend_string_release(psKey); -#else - zend_alter_ini_entry( (char *)*ini, strlen( *ini )+1, - (char *)*(ini+1), strlen( *(ini+1) ), - PHP_INI_SYSTEM, PHP_INI_STAGE_ACTIVATE); -#endif + zend_string_release(psKey); } while (( p < argend )&&(**p == '-' )) { @@ -1234,8 +1254,12 @@ int main( int argc, char * argv[] ) tsrm_startup(1, 1, 0, NULL); #endif - zend_signal_startup(); - +#if PHP_MAJOR_VERSION >= 7 +#if defined(ZEND_SIGNALS) || PHP_MINOR_VERSION > 0 + zend_signal_startup(); +#endif +#endif + if (argc > 1 ) { if ( parse_opt( argc, argv, &climode, &php_ini_path, &php_bind ) == -1 ) { @@ -1303,6 +1327,10 @@ int main( int argc, char * argv[] ) LSAPI_Init(); +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) + int is_criu = LSCRIU_Init(); // Must be called before the regular init as it unsets the parameters. +#endif + LSAPI_Init_Env_Parameters( NULL ); lsapi_mode = 1; @@ -1314,7 +1342,15 @@ int main( int argc, char * argv[] ) php_bind = NULL; } - while( LSAPI_Prefork_Accept_r( &g_req ) >= 0 ) { + int iRequestsProcessed = 0; + int result; + + while( ( result = LSAPI_Prefork_Accept_r( &g_req )) >= 0 ) { +#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) + if (is_criu && !result) { + LSCRIU_inc_req_procssed(); + } +#endif if ( slow_script_msec ) { gettimeofday( &tv_req_begin, NULL ); } @@ -1488,11 +1524,7 @@ PHP_FUNCTION(apache_get_modules) array_init(return_value); while( *name ) { - add_next_index_string(return_value, *name -#if PHP_MAJOR_VERSION < 7 - , 1 -#endif - ); + add_next_index_string(return_value, *name); ++name; } } diff --git a/sapi/litespeed/lsapilib.c b/sapi/litespeed/lsapilib.c index 9f589014328..185435a42d4 100644 --- a/sapi/litespeed/lsapilib.c +++ b/sapi/litespeed/lsapilib.c @@ -17,7 +17,7 @@ */ /* -Copyright (c) 2002-2015, Lite Speed Technologies Inc. +Copyright (c) 2002-2018, Lite Speed Technologies Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -53,11 +53,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include - +#include #include #include #include #include +#include #include #include #include @@ -116,22 +117,50 @@ typedef struct lsapi_MD5Context lsapi_MD5_CTX; #define LSAPI_RESP_BUF_SIZE 8192 #define LSAPI_INIT_RESP_HEADER_LEN 4096 +typedef struct _lsapi_child_status +{ + int m_pid; + long m_tmStart; + volatile short m_iKillSent; + volatile char m_inProcess; + volatile char m_connected; + volatile int m_iReqCounter; + + volatile long m_tmWaitBegin; + volatile long m_tmReqBegin; + volatile long m_tmLastCheckPoint; +} +lsapi_child_status; + +static lsapi_child_status * s_worker_status = NULL; static int g_inited = 0; static int g_running = 1; static int s_ppid; +static int s_restored_ppid = 0; +static int s_pid = 0; static int s_slow_req_msecs = 0; static int s_keepListener = 0; static int s_dump_debug_info = 0; static int s_pid_dump_debug_info = 0; +static int s_req_processed = 0; -LSAPI_Request g_req = { -1, -1 }; +static int *s_busy_workers = NULL; +static int *s_accepting_workers = NULL; +static int *s_global_counter = &s_req_processed; +static int s_max_busy_workers = -1; +static char *s_stderr_log_path = NULL; + +LSAPI_Request g_req = +{ .m_fdListen = -1, .m_fd = -1 }; static char s_secret[24]; +static LSAPI_On_Timer_pf s_proc_group_timer_cb = NULL; void Flush_RespBuf_r( LSAPI_Request * pReq ); +static int lsapi_reopen_stderr(const char *p); static const char *CGI_HEADERS[H_TRANSFER_ENCODING+1] = { @@ -185,9 +214,92 @@ static int HTTP_HEADER_LEN[H_TRANSFER_ENCODING+1] = 13,17, 8, 13, 8, 19, 10, 5, 15, 3, 17 }; + +static const char *s_log_level_names[8] = +{ + "", "DEBUG","INFO", "NOTICE", "WARN", "ERROR", "CRIT", "FATAL" +}; + + +void LSAPI_Log(int flag, const char * fmt, ...) +{ + char buf[1024]; + char *p = buf; + if (flag & LSAPI_LOG_TIMESTAMP_BITS) + { + struct timeval tv; + struct tm tm; + gettimeofday(&tv, NULL); + localtime_r(&tv.tv_sec, &tm); + if (flag & LSAPI_LOG_TIMESTAMP_FULL) + { + p += snprintf(p, 1024, "%04d-%02d-%02d %02d:%02d:%02d.%06d ", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec, (int)tv.tv_usec); + } + else if (flag & LSAPI_LOG_TIMESTAMP_HMS) + { + p += snprintf(p, 1024, "%02d:%02d:%02d ", + tm.tm_hour, tm.tm_min, tm.tm_sec); + } + } + + int level = flag & LSAPI_LOG_LEVEL_BITS; + if (level && level <= LSAPI_LOG_FLAG_FATAL) + { + p += snprintf(p, 100, "[%s] ", s_log_level_names[level]); + } + + if (flag & LSAPI_LOG_PID) + { + p += snprintf(p, 100, "[%d] ", s_pid); + } + + if (p > buf) + fprintf(stderr, "%.*s", (int)(p - buf), buf); + va_list ap; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + +#ifdef LSAPI_DEBUG + +#define DBGLOG_FLAG (LSAPI_LOG_TIMESTAMP_FULL|LSAPI_LOG_FLAG_DEBUG|LSAPI_LOG_PID) +#define lsapi_dbg(...) LSAPI_Log(DBGLOG_FLAG, __VA_ARGS__) + +#else + +#define lsapi_dbg(...) + +#endif + + +static int lsapi_parent_dead() +{ + // Return non-zero if the parent is dead. 0 if still alive. + if (!s_ppid) { + // not checking, so not dead + return(0); + } + if (s_restored_ppid) { + if (kill(s_restored_ppid,0) == -1) { + if (errno == EPERM) { + return(0); // no permission, but it's still there. + } + return(1); // Dead + } + return(0); // it worked, so it's not dead + } + return(s_ppid != getppid()); +} + + static void lsapi_sigpipe( int sig ) { } + + static void lsapi_siguser1( int sig ) { g_running = 0; @@ -214,7 +326,7 @@ static void lsapi_signal(int signo, sighandler_t handler) static int s_enable_core_dump = 0; -static void lsapi_enable_core_dump() +static void lsapi_enable_core_dump(void) { #if defined(__FreeBSD__ ) || defined(__NetBSD__) || defined(__OpenBSD__) \ || defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) @@ -251,6 +363,7 @@ static inline void lsapi_buildPacketHeader( struct lsapi_packet_header * pHeader pHeader->m_packetLen.m_iLen = len; } + static int lsapi_set_nblock( int fd, int nonblock ) { int val = fcntl( fd, F_GETFL, 0 ); @@ -271,6 +384,7 @@ static int lsapi_set_nblock( int fd, int nonblock ) return 0; } + static int lsapi_close( int fd ) { int ret; @@ -283,6 +397,20 @@ static int lsapi_close( int fd ) } } + +static void lsapi_close_connection(LSAPI_Request *pReq) +{ + if (pReq->m_fd == -1) + return; + lsapi_close(pReq->m_fd); + pReq->m_fd = -1; + if (s_busy_workers) + __sync_fetch_and_sub(s_busy_workers, 1); + if (s_worker_status) + s_worker_status->m_connected = 0; +} + + static inline ssize_t lsapi_read( int fd, void * pBuf, size_t len ) { ssize_t ret; @@ -295,6 +423,7 @@ static inline ssize_t lsapi_read( int fd, void * pBuf, size_t len ) } } + /* static int lsapi_write( int fd, const void * pBuf, int len ) { @@ -317,6 +446,7 @@ static int lsapi_write( int fd, const void * pBuf, int len ) } */ + static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen ) { int ret; @@ -361,6 +491,7 @@ static int lsapi_writev( int fd, struct iovec ** pVec, int count, int totalLen ) return totalLen - left; } + /* static int getTotalLen( struct iovec * pVec, int count ) { @@ -375,6 +506,7 @@ static int getTotalLen( struct iovec * pVec, int count ) } */ + static inline int allocateBuf( LSAPI_Request * pReq, int size ) { char * pBuf = (char *)realloc( pReq->m_pReqBuf, size ); @@ -402,6 +534,7 @@ static int allocateIovec( LSAPI_Request * pReq, int n ) return 0; } + static int allocateRespHeaderBuf( LSAPI_Request * pReq, int size ) { char * p = (char *)realloc( pReq->m_pRespHeaderBuf, size ); @@ -433,6 +566,7 @@ static inline int verifyHeader( struct lsapi_packet_header * pHeader, char pktTy return pHeader->m_packetLen.m_iLen; } + static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList, int *curSize, int newSize ) { @@ -454,6 +588,7 @@ static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList, } + static inline int isPipe( int fd ) { char achPeer[128]; @@ -465,6 +600,7 @@ static inline int isPipe( int fd ) return 1; } + static int parseEnv( struct LSAPI_key_value_pair * pEnvList, int count, char **pBegin, char * pEnd ) { @@ -501,6 +637,7 @@ static int parseEnv( struct LSAPI_key_value_pair * pEnvList, int count, return 0; } + static inline void swapIntEndian( int * pInteger ) { char * p = (char *)pInteger; @@ -514,6 +651,7 @@ static inline void swapIntEndian( int * pInteger ) } + static inline void fixEndian( LSAPI_Request * pReq ) { struct lsapi_req_header *p= pReq->m_pHeader; @@ -528,6 +666,7 @@ static inline void fixEndian( LSAPI_Request * pReq ) swapIntEndian( &p->m_cntSpecialEnv ); } + static void fixHeaderIndexEndian( LSAPI_Request * pReq ) { int i; @@ -614,7 +753,7 @@ static int (*fp_lve_destroy)(struct liblve *) = NULL; static int (*fp_lve_enter)(struct liblve *, uint32_t, int32_t, int32_t, uint32_t *) = NULL; static int (*fp_lve_leave)(struct liblve *, uint32_t *) = NULL; static int (*fp_lve_jail)( struct passwd *, char *) = NULL; -static int lsapi_load_lve_lib() +static int lsapi_load_lve_lib(void) { s_liblve = dlopen("liblve.so.0", RTLD_LAZY); if (s_liblve) @@ -642,7 +781,8 @@ static int lsapi_load_lve_lib() return (s_liblve)? 0 : -1; } -static int init_lve_ex() + +static int init_lve_ex(void) { int rc; if ( !s_liblve ) @@ -714,7 +854,8 @@ static int readSecret( const char * pSecretFile ) return 0; } -int LSAPI_is_suEXEC_Daemon() + +int LSAPI_is_suEXEC_Daemon(void) { if (( !s_uid )&&( s_secret[0] )) return 1; @@ -722,10 +863,14 @@ int LSAPI_is_suEXEC_Daemon() return 0; } + static int LSAPI_perror_r( LSAPI_Request * pReq, const char * pErr1, const char *pErr2 ) { - char achError[1024]; - int n = snprintf(achError, 1024, "%s:%s: %s\n", pErr1, (pErr2)?pErr2:"", strerror( errno ) ); + char achError[4096]; + int n = snprintf(achError, sizeof(achError), "[%d] %s:%s: %s\n", getpid(), + pErr1, (pErr2)?pErr2:"", strerror(errno)); + if (n > (int)sizeof(achError)) + n = sizeof(achError); if ( pReq ) LSAPI_Write_Stderr_r( pReq, achError, n ); else @@ -733,6 +878,7 @@ static int LSAPI_perror_r( LSAPI_Request * pReq, const char * pErr1, const char return 0; } + static int lsapi_lve_error( LSAPI_Request * pReq ) { static const char * headers[] = @@ -756,6 +902,7 @@ static int lsapi_lve_error( LSAPI_Request * pReq ) return 0; } + static int lsapi_enterLVE( LSAPI_Request * pReq, uid_t uid ) { #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) @@ -777,6 +924,7 @@ static int lsapi_enterLVE( LSAPI_Request * pReq, uid_t uid ) return 0; } + static int lsapi_jailLVE( LSAPI_Request * pReq, uid_t uid, struct passwd * pw ) { int ret = 0; @@ -796,7 +944,7 @@ static int lsapi_jailLVE( LSAPI_Request * pReq, uid_t uid, struct passwd * pw ) #if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__) -static int lsapi_initLVE() +static int lsapi_initLVE(void) { const char * pEnv; if ( (pEnv = getenv( "LSAPI_LVE_ENABLE" ))!= NULL ) @@ -926,7 +1074,8 @@ static int lsapi_changeUGid( LSAPI_Request * pReq ) { int uid = s_defaultUid; int gid = s_defaultGid; - const char * pChroot = NULL; + const char *pStderrLog; + const char *pChroot = NULL; struct LSAPI_key_value_pair * pEnv; struct LSAPI_key_value_pair * pAuth; int i; @@ -991,11 +1140,16 @@ static int lsapi_changeUGid( LSAPI_Request * pReq ) } s_uid = uid; + + pStderrLog = LSAPI_GetEnv_r( pReq, "LSAPI_STDERR_LOG"); + if (pStderrLog) + lsapi_reopen_stderr(pStderrLog); return 0; } + static int parseContentLenFromHeader(LSAPI_Request * pReq) { const char * pContentLen = LSAPI_GetHeader_r( pReq, H_CONTENT_LENGTH ); @@ -1062,8 +1216,8 @@ static int parseRequest( LSAPI_Request * pReq, int totalLen ) pBegin += pReq->m_pHeader->m_httpHeaderLen; if ( pBegin != pEnd ) { - fprintf( stderr, "%d: request header does match total size, total: %d, real: %ld\n", getpid(), totalLen, - pBegin - pReq->m_pReqBuf ); + fprintf( stderr, "%d: request header does match total size, total: %d, " + "real: %ld\n", getpid(), totalLen, pBegin - pReq->m_pReqBuf ); return -1; } if ( shouldFixEndian ) @@ -1086,6 +1240,7 @@ static int parseRequest( LSAPI_Request * pReq, int totalLen ) return 0; } + //OPTIMIZATION static char s_accept_notify = 0; static char s_schedule_notify = 0; @@ -1094,27 +1249,42 @@ static char s_notified_pid = 0; static struct lsapi_packet_header s_ack = {'L', 'S', LSAPI_REQ_RECEIVED, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} }; +static struct lsapi_packet_header s_conn_close_pkt = {'L', 'S', + LSAPI_CONN_CLOSE, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} }; + - -static inline int write_req_received_notification( int fd ) +static inline int send_notification_pkt( int fd, struct lsapi_packet_header *pkt ) { - if ( write( fd, &s_ack, LSAPI_PACKET_HEADER_LEN ) - < LSAPI_PACKET_HEADER_LEN ) + if ( write( fd, pkt, LSAPI_PACKET_HEADER_LEN ) < LSAPI_PACKET_HEADER_LEN ) return -1; return 0; } -static void lsapi_sigalarm( int sig ) + +static inline int send_req_received_notification( int fd ) { - if ( s_notify_scheduled ) - { - s_notify_scheduled = 0; - if ( g_req.m_fd != -1 ) - write_req_received_notification( g_req.m_fd ); - } + return send_notification_pkt(fd, &s_ack); } -static inline int lsapi_schedule_notify() + +static inline int send_conn_close_notification( int fd ) +{ + return send_notification_pkt(fd, &s_conn_close_pkt); +} + + +//static void lsapi_sigalarm( int sig ) +//{ +// if ( s_notify_scheduled ) +// { +// s_notify_scheduled = 0; +// if ( g_req.m_fd != -1 ) +// write_req_received_notification( g_req.m_fd ); +// } +//} + + +static inline int lsapi_schedule_notify(void) { if ( !s_notify_scheduled ) { @@ -1124,11 +1294,12 @@ static inline int lsapi_schedule_notify() return 0; } + static inline int notify_req_received( int fd ) { if ( s_schedule_notify ) return lsapi_schedule_notify(); - return write_req_received_notification( fd ); + return send_req_received_notification( fd ); } @@ -1146,6 +1317,7 @@ static inline int lsapi_notify_pid( int fd ) return 0; } + static char s_conn_key_packet[16]; static inline int init_conn_key( int fd ) { @@ -1168,6 +1340,7 @@ static inline int init_conn_key( int fd ) } + static int readReq( LSAPI_Request * pReq ) { int len; @@ -1240,7 +1413,6 @@ static int readReq( LSAPI_Request * pReq ) } - int LSAPI_Init(void) { if ( !g_inited ) @@ -1264,16 +1436,25 @@ int LSAPI_Init(void) return 0; } + void LSAPI_Stop(void) { g_running = 0; } + int LSAPI_IsRunning(void) { return g_running; } + +void LSAPI_Register_Pgrp_Timer_Callback(LSAPI_On_Timer_pf cb) +{ + s_proc_group_timer_cb = cb; +} + + int LSAPI_InitRequest( LSAPI_Request * pReq, int fd ) { int newfd; @@ -1312,18 +1493,19 @@ int LSAPI_InitRequest( LSAPI_Request * pReq, int fd ) return 0; } + int LSAPI_Is_Listen( void ) { return LSAPI_Is_Listen_r( &g_req ); } + int LSAPI_Is_Listen_r( LSAPI_Request * pReq) { return pReq->m_fdListen != -1; } - int LSAPI_Accept_r( LSAPI_Request * pReq ) { char achPeer[128]; @@ -1353,6 +1535,10 @@ int LSAPI_Accept_r( LSAPI_Request * pReq ) } else { + if (s_worker_status) + s_worker_status->m_connected = 1; + if (s_busy_workers) + __sync_fetch_and_add(s_busy_workers, 1); lsapi_set_nblock( pReq->m_fd , 0 ); if (((struct sockaddr *)&achPeer)->sa_family == AF_INET ) { @@ -1372,16 +1558,17 @@ int LSAPI_Accept_r( LSAPI_Request * pReq ) if ( !readReq( pReq ) ) break; //abort(); - lsapi_close( pReq->m_fd ); - pReq->m_fd = -1; + lsapi_close_connection(pReq); LSAPI_Reset_r( pReq ); } return 0; } + static struct lsapi_packet_header finish = {'L', 'S', LSAPI_RESP_END, LSAPI_ENDIAN, {LSAPI_PACKET_HEADER_LEN} }; + int LSAPI_Finish_r( LSAPI_Request * pReq ) { /* finish req body */ @@ -1454,6 +1641,7 @@ char * LSAPI_GetHeader_r( LSAPI_Request * pReq, int headerIndex ) return pReq->m_pHttpHeader + off; } + static int readBodyToReqBuf( LSAPI_Request * pReq ) { off_t bodyLeft; @@ -1490,7 +1678,6 @@ int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq ) } - int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen, int *getLF ) { ssize_t len; @@ -1499,7 +1686,7 @@ int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen, in char * pBufCur = pBuf; char * pCur; char * p; - if (!pReq || (pReq->m_fd ==-1) ||( !pBuf )||(bufLen < 0 )|| !getLF ) + if (!pReq || pReq->m_fd == -1 || !pBuf || !getLF) return -1; *getLF = 0; while( (left = pBufEnd - pBufCur ) > 0 ) @@ -1543,21 +1730,21 @@ ssize_t LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen ) ssize_t len; off_t total; /* char *pOldBuf = pBuf; */ - if (!pReq || (pReq->m_fd ==-1) || ( !pBuf )||(bufLen < 0 )) + if (!pReq || pReq->m_fd == -1 || !pBuf || (ssize_t)bufLen < 0) return -1; total = pReq->m_reqBodyLen - pReq->m_reqBodyRead; if ( total <= 0 ) return 0; - if ( total < bufLen ) + if ( total < (ssize_t)bufLen ) bufLen = total; total = 0; len = pReq->m_bufRead - pReq->m_bufProcessed; if ( len > 0 ) { - if ( len > bufLen ) + if ( len > (ssize_t)bufLen ) len = bufLen; memmove( pBuf, pReq->m_pReqBuf + pReq->m_bufProcessed, len ); pReq->m_bufProcessed += len; @@ -1615,7 +1802,7 @@ ssize_t LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, size_t len ) } pReq->m_reqState |= LSAPI_ST_RESP_BODY; - if ( (len - skip) < pReq->m_pRespBufEnd - pReq->m_pRespBufPos ) + if ( ((ssize_t)len - skip) < pReq->m_pRespBufEnd - pReq->m_pRespBufPos ) { memmove( pReq->m_pRespBufPos, pBuf + skip, len - skip ); pReq->m_pRespBufPos += len - skip; @@ -1672,6 +1859,7 @@ ssize_t LSAPI_Write_r( LSAPI_Request * pReq, const char * pBuf, size_t len ) return p - pBuf; } + #if defined(__FreeBSD__ ) || defined(__NetBSD__) || defined(__OpenBSD__) ssize_t gsendfile( int fdOut, int fdIn, off_t* off, size_t size ) { @@ -1687,6 +1875,7 @@ ssize_t gsendfile( int fdOut, int fdIn, off_t* off, size_t size ) } #endif + #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) ssize_t gsendfile( int fdOut, int fdIn, off_t* off, size_t size ) { @@ -1702,6 +1891,7 @@ ssize_t gsendfile( int fdOut, int fdIn, off_t* off, size_t size ) } #endif + #if defined(sun) || defined(__sun) #include ssize_t gsendfile( int fdOut, int fdIn, off_t *off, size_t size ) @@ -1725,11 +1915,14 @@ ssize_t gsendfile( int fdOut, int fdIn, off_t *off, size_t size ) } #endif + #if defined(linux) || defined(__linux) || defined(__linux__) || \ defined(__gnu_linux__) #include #define gsendfile sendfile #endif + + #if defined(HPUX) ssize_t gsendfile( int fdOut, int fdIn, off_t * off, size_t size ) { @@ -1737,6 +1930,7 @@ ssize_t gsendfile( int fdOut, int fdIn, off_t * off, size_t size ) } #endif + ssize_t LSAPI_sendfile_r( LSAPI_Request * pReq, int fdIn, off_t* off, size_t size ) { struct lsapi_packet_header * pHeader = pReq->m_respPktHeader; @@ -1785,8 +1979,6 @@ void Flush_RespBuf_r( LSAPI_Request * pReq ) } - - int LSAPI_Flush_r( LSAPI_Request * pReq ) { int ret = 0; @@ -1820,8 +2012,7 @@ int LSAPI_Flush_r( LSAPI_Request * pReq ) n, pReq->m_totalLen ); if ( ret < pReq->m_totalLen ) { - lsapi_close( pReq->m_fd ); - pReq->m_fd = -1; + lsapi_close_connection(pReq); ret = -1; } pReq->m_totalLen = 0; @@ -1844,7 +2035,7 @@ ssize_t LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, size_t le if ( !pReq ) return -1; - if (( pReq->m_fd == -1 )||(pReq->m_fd == pReq->m_fdListen )) + if (s_stderr_log_path || pReq->m_fd == -1 || pReq->m_fd == pReq->m_fdListen) return write( 2, pBuf, len ); if ( pReq->m_pRespBufPos != pReq->m_pRespBuf ) { @@ -1876,14 +2067,14 @@ ssize_t LSAPI_Write_Stderr_r( LSAPI_Request * pReq, const char * pBuf, size_t le 2, totalLen ); if ( ret < totalLen ) { - lsapi_close( pReq->m_fd ); - pReq->m_fd = -1; + lsapi_close_connection(pReq); ret = -1; } } return p - pBuf; } + static char * GetHeaderVar( LSAPI_Request * pReq, const char * name ) { int i; @@ -1963,6 +2154,7 @@ char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ) return NULL; } + struct _headerInfo { const char * _name; @@ -1971,12 +2163,14 @@ struct _headerInfo int _valueLen; }; + int compareValueLocation(const void * v1, const void *v2 ) { return ((const struct _headerInfo *)v1)->_value - ((const struct _headerInfo *)v2)->_value; } + int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ) { @@ -2050,7 +2244,6 @@ int LSAPI_ForeachOrgHeader_r( LSAPI_Request * pReq, return ret; } return count; - } @@ -2119,9 +2312,9 @@ int LSAPI_ForeachHeader_r( LSAPI_Request * pReq, } } return count + pReq->m_pHeader->m_cntUnknownHeaders; - } + static int EnvForeach( struct LSAPI_key_value_pair * pEnv, int n, LSAPI_CB_EnvHandler fn, void * arg ) { @@ -2141,7 +2334,6 @@ static int EnvForeach( struct LSAPI_key_value_pair * pEnv, } - int LSAPI_ForeachEnv_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ) { @@ -2156,7 +2348,6 @@ int LSAPI_ForeachEnv_r( LSAPI_Request * pReq, } - int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq, LSAPI_CB_EnvHandler fn, void * arg ) { @@ -2173,7 +2364,6 @@ int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq, } - int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq ) { if ( !pReq || !pReq->m_pIovec ) @@ -2257,7 +2447,6 @@ int LSAPI_AppendRespHeader2_r( LSAPI_Request * pReq, const char * pHeaderName, } - int LSAPI_AppendRespHeader_r( LSAPI_Request * pReq, const char * pBuf, int len ) { if ( !pReq || !pBuf || len <= 0 || len > LSAPI_RESP_HTTP_HEADER_MAX ) @@ -2338,9 +2527,9 @@ int LSAPI_CreateListenSock2( const struct sockaddr * pServerAddr, int backlog ) close(fd); errno = ret; return -1; - } + int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr ) { char achAddr[256]; @@ -2438,6 +2627,7 @@ int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr ) } + int LSAPI_CreateListenSock( const char * pBind, int backlog ) { char serverAddr[128]; @@ -2451,25 +2641,8 @@ int LSAPI_CreateListenSock( const char * pBind, int backlog ) return fd; } + static fn_select_t g_fnSelect = select; - -typedef struct _lsapi_child_status -{ - int m_pid; - long m_tmStart; - - volatile short m_iKillSent; - volatile short m_inProcess; - volatile int m_iReqCounter; - - 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; @@ -2487,12 +2660,11 @@ typedef struct _lsapi_prefork_server lsapi_child_status * m_pChildrenStatusEnd; }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 ) { - int pid; if ( g_prefork_server ) return 0; if ( max_children <= 1 ) @@ -2500,6 +2672,8 @@ int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork ) if ( max_children >= 10000) max_children = 10000; + if (s_max_busy_workers == 0) + s_max_busy_workers = max_children / 2 + 1; g_prefork_server = (lsapi_prefork_server *)malloc( sizeof( lsapi_prefork_server ) ); if ( !g_prefork_server ) @@ -2510,8 +2684,8 @@ int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork ) g_fnSelect = fp; s_ppid = getppid(); - pid = getpid(); - setpgid( pid, pid ); + s_pid = getpid(); + setpgid( s_pid, s_pid ); g_prefork_server->m_iAvoidFork = avoidFork; g_prefork_server->m_iMaxChildren = max_children; @@ -2524,6 +2698,7 @@ int LSAPI_Init_Prefork_Server( int max_children, fn_select_t fp, int avoidFork ) return 0; } + void LSAPI_Set_Server_fd( int fd ) { if( g_prefork_server ) @@ -2557,12 +2732,8 @@ static int lsapi_accept( int fdListen ) } - - -static int s_req_processed = 0; -static int s_max_reqs = 10000; +static unsigned int s_max_reqs = UINT_MAX; static int s_max_idle_secs = 300; - static int s_stop; static void lsapi_cleanup(int signal) @@ -2570,6 +2741,7 @@ 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; @@ -2588,11 +2760,33 @@ static lsapi_child_status * find_child_status( int pid ) } +void LSAPI_reset_server_state( void ) +{ + /* + Reset child status + */ + g_prefork_server->m_iCurChildren = 0; + lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus; + lsapi_child_status * pEnd = g_prefork_server->m_pChildrenStatusEnd; + while( pStatus < pEnd ) + { + pStatus->m_pid = 0; + ++pStatus; + } + if (s_busy_workers) + __sync_lock_release(s_busy_workers); + if (s_accepting_workers) + __sync_lock_release(s_accepting_workers); + +} + static void lsapi_sigchild( int signal ) { int status, pid; lsapi_child_status * child_status; + if (g_prefork_server == NULL) + return; while( 1 ) { pid = waitpid( -1, &status, WNOHANG|WUNTRACED ); @@ -2604,7 +2798,8 @@ static void lsapi_sigchild( int signal ) { int sig_num = WTERMSIG( status ); int dump = WCOREDUMP( status ); - fprintf( stderr, "Child process with pid: %d was killed by signal: %d, core dump: %d\n", pid, sig_num, dump ); + fprintf( stderr, "Child process with pid: %d was killed by signal: " + "%d, core dump: %d\n", pid, sig_num, dump ); } if ( pid == s_pid_dump_debug_info ) { @@ -2614,9 +2809,14 @@ static void lsapi_sigchild( int signal ) child_status = find_child_status( pid ); if ( child_status ) { + if (child_status->m_connected) + { + if (s_busy_workers) + __sync_fetch_and_sub(s_busy_workers, 1); + child_status->m_connected = 0; + } child_status->m_pid = 0; --g_prefork_server->m_iCurChildren; - } } while(( g_prefork_server->m_pChildrenStatusCur > g_prefork_server->m_pChildrenStatus ) @@ -2625,13 +2825,16 @@ static void lsapi_sigchild( int signal ) } -static int lsapi_init_children_status() + +static int lsapi_init_children_status(void) { int size = 4096; - + int max_children = g_prefork_server->m_iMaxChildren + + g_prefork_server->m_iExtraChildren; + char * pBuf; - size = (g_prefork_server->m_iMaxChildren + g_prefork_server->m_iExtraChildren ) * sizeof( lsapi_child_status ) * 2; - size = (size + 4095 ) / 4096 * 4096; + size = max_children * sizeof( lsapi_child_status ) * 2 + 3 * sizeof(int); + size = (size + 4095) / 4096 * 4096; pBuf =( char*) mmap( NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0 ); if ( pBuf == MAP_FAILED ) @@ -2639,12 +2842,17 @@ static int lsapi_init_children_status() perror( "Anonymous mmap() failed" ); return -1; } + memset( pBuf, 0, size ); g_prefork_server->m_pChildrenStatus = (lsapi_child_status *)pBuf; g_prefork_server->m_pChildrenStatusCur = (lsapi_child_status *)pBuf; - g_prefork_server->m_pChildrenStatusEnd = (lsapi_child_status *)pBuf + size / sizeof( lsapi_child_status ); + g_prefork_server->m_pChildrenStatusEnd = (lsapi_child_status *)pBuf + max_children; + s_busy_workers = (int *)g_prefork_server->m_pChildrenStatusEnd; + s_accepting_workers = s_busy_workers + 1; + s_global_counter = s_accepting_workers + 1; return 0; } + static void dump_debug_info( lsapi_child_status * pStatus, long tmCur ) { char achCmd[1024]; @@ -2655,15 +2863,20 @@ static void dump_debug_info( lsapi_child_status * pStatus, long tmCur ) } s_pid_dump_debug_info = fork(); - fprintf( stderr, "[%s] Possible runaway process, PPID: %d, PID: %d, reqCount: %d, process time: %ld, checkpoint time: %ld, start time: %ld\n", - ctime(&tmCur), getpid(), pStatus->m_pid, pStatus->m_iReqCounter, - tmCur - pStatus->m_tmReqBegin, tmCur - pStatus->m_tmLastCheckPoint, tmCur - pStatus->m_tmStart ); - snprintf( achCmd, 1024, "gdb --batch -ex \"attach %d\" -ex \"set height 0\" -ex \"bt\" >&2;PATH=$PATH:/usr/sbin lsof -p %d >&2", pStatus->m_pid, pStatus->m_pid ); + fprintf( stderr, "[%s] Possible runaway process, PPID: %d, PID: %d, " + "reqCount: %d, process time: %ld, checkpoint time: %ld, start " + "time: %ld\n", ctime(&tmCur), getpid(), pStatus->m_pid, + pStatus->m_iReqCounter, tmCur - pStatus->m_tmReqBegin, + tmCur - pStatus->m_tmLastCheckPoint, tmCur - pStatus->m_tmStart ); + snprintf( achCmd, 1024, "gdb --batch -ex \"attach %d\" -ex \"set height 0\" " + "-ex \"bt\" >&2;PATH=$PATH:/usr/sbin lsof -p %d >&2", + pStatus->m_pid, pStatus->m_pid ); if ( system( achCmd ) == -1 ) perror( "system()" ); exit( 0 ); } + static void lsapi_check_child_status( long tmCur ) { int idle = 0; @@ -2681,15 +2894,17 @@ static void lsapi_check_child_status( long tmCur ) if ( !pStatus->m_inProcess ) { - if (( g_prefork_server->m_iCurChildren - dying > g_prefork_server->m_iMaxChildren)|| - ( idle > g_prefork_server->m_iMaxIdleChildren )) + if (g_prefork_server->m_iCurChildren - dying + > g_prefork_server->m_iMaxChildren + || idle > g_prefork_server->m_iMaxIdleChildren) { ++pStatus->m_iKillSent; //tobekilled = SIGUSR1; } else { - if (( s_max_idle_secs> 0)&&(tmCur - pStatus->m_tmWaitBegin > s_max_idle_secs + 5 )) + if (s_max_idle_secs> 0 + && tmCur - pStatus->m_tmWaitBegin > s_max_idle_secs + 5) { ++pStatus->m_iKillSent; //tobekilled = SIGUSR1; @@ -2700,26 +2915,29 @@ static void lsapi_check_child_status( long tmCur ) } else { - if ( tmCur - pStatus->m_tmReqBegin > - g_prefork_server->m_iMaxReqProcessTime ) + if (tmCur - pStatus->m_tmReqBegin > + g_prefork_server->m_iMaxReqProcessTime) { - if (( ( pStatus->m_iKillSent % 5 ) == 0 )&&( s_dump_debug_info )) + if ((pStatus->m_iKillSent % 5) == 0 && s_dump_debug_info) dump_debug_info( pStatus, tmCur ); if ( pStatus->m_iKillSent > 5 ) { tobekilled = SIGKILL; - fprintf( stderr, "Force killing runaway process PID: %d with SIGKILL\n", pStatus->m_pid ); + fprintf( stderr, "Force killing runaway process PID: %d" + " with SIGKILL\n", pStatus->m_pid ); } else { tobekilled = SIGTERM; - fprintf( stderr, "Killing runaway process PID: %d with SIGTERM\n", pStatus->m_pid ); + fprintf( stderr, "Killing runaway process PID: %d with " + "SIGTERM\n", pStatus->m_pid ); } } } if ( tobekilled ) { - if (( kill( pStatus->m_pid, tobekilled ) == -1 )&&( errno == ESRCH )) + if (( kill( pStatus->m_pid, tobekilled ) == -1 ) && + ( errno == ESRCH )) { pStatus->m_pid = 0; --count; @@ -2735,34 +2953,35 @@ static void lsapi_check_child_status( long tmCur ) } if ( abs( g_prefork_server->m_iCurChildren - count ) > 1 ) { - fprintf( stderr, "Children tracking is wrong: PID: %d, Cur Children: %d, count: %d, idle: %d, dying: %d\n", getpid(), - g_prefork_server->m_iCurChildren, count, idle, dying ); - + fprintf( stderr, "Children tracking is wrong: PID: %d, Cur Children: %d," + " count: %d, idle: %d, dying: %d\n", getpid(), + g_prefork_server->m_iCurChildren, count, idle, dying ); } } -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_all_children_must_die(void) +//{ +// 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 ) +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; @@ -2804,11 +3023,15 @@ static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Re s_stop = 0; while( !s_stop ) { + if (s_proc_group_timer_cb != NULL) { + s_proc_group_timer_cb(); + } + curTime = time( NULL ); if (curTime != lastTime ) { lastTime = curTime; - if (s_ppid && (getppid() != s_ppid )) + if (lsapi_parent_dead()) break; lsapi_check_child_status(curTime ); if (pServer->m_iServerMaxIdle) @@ -2824,29 +3047,20 @@ static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Re } } - if ( pServer->m_iCurChildren >= (pServer->m_iMaxChildren + pServer->m_iExtraChildren ) ) - { - fprintf( stderr, "Reached max children process limit: %d, extra: %d, current: %d, please increase LSAPI_CHILDREN.\n", - pServer->m_iMaxChildren, pServer->m_iExtraChildren, pServer->m_iCurChildren ); - 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 ) + timeout.tv_sec = 1; + timeout.tv_usec = 0; + ret = (*g_fnSelect)(pServer->m_fd+1, &readfds, NULL, NULL, &timeout); + if (ret == 1 ) { - /* - if ( pServer->m_iCurChildren >= 0 ) + if (pServer->m_iCurChildren >= pServer->m_iMaxChildren + && s_accepting_workers + && (ret = __sync_add_and_fetch(s_accepting_workers, 0)) > 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; - }*/ + usleep( 200 ); + continue; + } } else if ( ret == -1 ) { @@ -2860,6 +3074,18 @@ static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Re continue; } + if (pServer->m_iCurChildren >= + pServer->m_iMaxChildren + pServer->m_iExtraChildren) + { + fprintf( stderr, "Reached max children process limit: %d, extra: %d," + " current: %d, busy: %d, please increase LSAPI_CHILDREN.\n", + pServer->m_iMaxChildren, pServer->m_iExtraChildren, + pServer->m_iCurChildren, + s_busy_workers ? *s_busy_workers : -1 ); + usleep( 100000 ); + continue; + } + pReq->m_fd = lsapi_accept( pServer->m_fd ); if ( pReq->m_fd != -1 ) { @@ -2879,18 +3105,27 @@ static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Re if ( !pid ) { + setsid(); if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) < 0) perror( "sigprocmask( SIG_SETMASK ) to restore SIGMASK in child" ); g_prefork_server = NULL; s_ppid = getppid(); + s_pid = getpid(); s_req_processed = 0; - s_pChildStatus = child_status; + s_proc_group_timer_cb = NULL; + s_worker_status = child_status; + + s_worker_status->m_connected = 1; + if (s_busy_workers) + __sync_add_and_fetch(s_busy_workers, 1); lsapi_set_nblock( pReq->m_fd, 0 ); - if ( pReq->m_fdListen != -1 ) - { - close( pReq->m_fdListen ); - pReq->m_fdListen = -1; - } + //keep it open if busy_count is used. + s_keepListener = 1; +// if ( pReq->m_fdListen != -1 ) +// { +// close( pReq->m_fdListen ); +// pReq->m_fdListen = -1; +// } /* don't catch our signals */ sigaction( SIGCHLD, &old_child, 0 ); sigaction( SIGTERM, &old_term, 0 ); @@ -2940,11 +3175,14 @@ static int lsapi_prefork_server_accept( lsapi_prefork_server * pServer, LSAPI_Re } + void lsapi_error( const char * pMessage, int err_no ) { - fprintf( stderr, "%d: %s, errno: %d (%s)\n", getpid(), pMessage, err_no, strerror( err_no ) ); + fprintf( stderr, "%d: %s, errno: %d (%s)\n", getpid(), pMessage, err_no, + strerror( err_no ) ); } + int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) { int fd; @@ -2955,19 +3193,28 @@ int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) 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 ) + else if (s_req_processed > 0 && s_max_busy_workers > 0 && s_busy_workers) + { + ret = __sync_fetch_and_add(s_busy_workers, 0); + if (ret >= s_max_busy_workers) + { + send_conn_close_notification(pReq->m_fd); + lsapi_close_connection(pReq); + } + } + + if ( (unsigned int)s_req_processed > s_max_reqs ) return -1; - if ( s_pChildStatus ) + if ( s_worker_status ) { - s_pChildStatus->m_tmWaitBegin = time( NULL ); + s_worker_status->m_tmWaitBegin = time( NULL ); } @@ -2988,23 +3235,37 @@ int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) { if ( !g_running ) return -1; - if ((s_req_processed)&&( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) + if (s_req_processed && s_worker_status + && s_worker_status->m_iKillSent) return -1; FD_ZERO( &readfds ); FD_SET( fd, &readfds ); timeout.tv_sec = 1; timeout.tv_usec = 0; + if (fd == pReq->m_fdListen) + { + if (s_accepting_workers) + __sync_fetch_and_add(s_accepting_workers, 1); + } ret = (*g_fnSelect)(fd+1, &readfds, NULL, NULL, &timeout); + if (fd == pReq->m_fdListen) + { + if (s_accepting_workers) + __sync_fetch_and_sub(s_accepting_workers, 1); + } + if ( ret == 0 ) { - if ( s_pChildStatus ) + if ( s_worker_status ) { - s_pChildStatus->m_inProcess = 0; + s_worker_status->m_inProcess = 0; + if (fd == pReq->m_fdListen) + return -1; } ++wait_secs; if (( s_max_idle_secs > 0 )&&(wait_secs >= s_max_idle_secs )) return -1; - if ( s_ppid &&( getppid() != s_ppid)) + if ( lsapi_parent_dead() ) return -1; } else if ( ret == -1 ) @@ -3016,14 +3277,21 @@ int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) } else if ( ret >= 1 ) { - if (s_req_processed && ( s_pChildStatus )&&( s_pChildStatus->m_iKillSent )) + if (s_req_processed && s_worker_status + && s_worker_status->m_iKillSent) return -1; if ( fd == pReq->m_fdListen ) { pReq->m_fd = lsapi_accept( pReq->m_fdListen ); if ( pReq->m_fd != -1 ) { + if (s_worker_status) + s_worker_status->m_connected = 1; + if (s_busy_workers) + __sync_fetch_and_add(s_busy_workers, 1); + fd = pReq->m_fd; + lsapi_set_nblock( fd, 0 ); //init_conn_key( pReq->m_fd ); if ( !s_keepListener ) @@ -3050,42 +3318,46 @@ int LSAPI_Prefork_Accept_r( LSAPI_Request * pReq ) if ( !readReq( pReq ) ) { - if ( s_pChildStatus ) + if ( s_worker_status ) { - s_pChildStatus->m_iKillSent = 0; - s_pChildStatus->m_inProcess = 1; - ++s_pChildStatus->m_iReqCounter; - s_pChildStatus->m_tmReqBegin = s_pChildStatus->m_tmLastCheckPoint = time(NULL); + s_worker_status->m_iKillSent = 0; + s_worker_status->m_inProcess = 1; + ++s_worker_status->m_iReqCounter; + s_worker_status->m_tmReqBegin = + s_worker_status->m_tmLastCheckPoint = time(NULL); } ++s_req_processed; return 0; } - lsapi_close( pReq->m_fd ); - pReq->m_fd = -1; + lsapi_close_connection(pReq); LSAPI_Reset_r( pReq ); } return -1; } + void LSAPI_Set_Max_Reqs( int reqs ) -{ s_max_reqs = reqs; } +{ s_max_reqs = reqs - 1; } 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 )) @@ -3099,34 +3371,44 @@ void LSAPI_Set_Max_Idle_Children( int maxIdleChld ) 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_Set_Slow_Req_Msecs( int msecs ) { s_slow_req_msecs = msecs; } -int LSAPI_Get_Slow_Req_Msecs() + +int LSAPI_Get_Slow_Req_Msecs(void) { return s_slow_req_msecs; } -void LSAPI_No_Check_ppid() +void LSAPI_No_Check_ppid(void) { s_ppid = 0; } + +int LSAPI_Get_ppid() +{ + return(s_ppid); +} + + #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) #include #else extern char ** environ; #endif -static void unset_lsapi_envs() +static void unset_lsapi_envs(void) { char **env; #if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) @@ -3149,7 +3431,8 @@ static void unset_lsapi_envs() } } -static int lsapi_initSuEXEC() + +static int lsapi_initSuEXEC(void) { int i; struct passwd * pw; @@ -3204,11 +3487,88 @@ static int lsapi_initSuEXEC() } +static int lsapi_check_path(const char *p, char *final, int max_len) +{ + char resolved_path[PATH_MAX+1]; + int len = 0; + char *end; + if (*p != '/') + { + if (getcwd(final, max_len) == NULL) + return -1; + len = strlen(final); + *(final + len) = '/'; + ++len; + } + end = memccpy(&final[len], p, '\0', PATH_MAX - len); + if (!end) + { + errno = EINVAL; + return -1; + } + p = final; + if (realpath(p, resolved_path) == NULL + && errno != ENOENT && errno != EACCES) + return -1; + if (strncmp(resolved_path, "/etc/", 5) == 0) + { + errno = EPERM; + return -1; + } + return 0; +} + + +static int lsapi_reopen_stderr2(const char *full_path) +{ + int newfd = open(full_path, O_WRONLY | O_CREAT | O_APPEND, 0644); + if (newfd == -1) + { + LSAPI_perror_r(NULL, "failed to open custom stderr log", full_path); + return -1; + } + if (newfd != 2) + { + dup2(newfd, 2); + close(newfd); + dup2(2, 1); + } + if (s_stderr_log_path && full_path != s_stderr_log_path) + { + free(s_stderr_log_path); + s_stderr_log_path = NULL; + } + s_stderr_log_path = strdup(full_path); + return 0; +} + + +static int lsapi_reopen_stderr(const char *p) +{ + char full_path[PATH_MAX]; + if (s_uid == 0) + return -1; + if (lsapi_check_path(p, full_path, PATH_MAX) == -1) + { + LSAPI_perror_r(NULL, "invalid custom stderr log path", p); + return -1; + } + return lsapi_reopen_stderr2(full_path); +} + + int LSAPI_Init_Env_Parameters( fn_select_t fp ) { const char *p; int n; int avoidFork = 0; + + p = getenv("LSAPI_STDERR_LOG"); + if (p) + { + lsapi_reopen_stderr(p); + } + p = getenv( "PHP_LSAPI_MAX_REQUESTS" ); if ( !p ) p = getenv( "LSAPI_MAX_REQS" ); @@ -3301,6 +3661,16 @@ int LSAPI_Init_Env_Parameters( fn_select_t fp ) { LSAPI_No_Check_ppid(); } + + p = getenv("LSAPI_MAX_BUSY_WORKER"); + if (p) + { + n = atoi(p); + s_max_busy_workers = n; + if (n >= 0) + LSAPI_No_Check_ppid(); + } + p = getenv( "LSAPI_DUMP_DEBUG_INFO" ); if ( p ) @@ -3354,6 +3724,7 @@ static void byteReverse(unsigned char *buf, unsigned longs) } while (--longs); } + /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. @@ -3417,6 +3788,7 @@ void lsapi_MD5Update(struct lsapi_MD5Context *ctx, unsigned char const *buf, uns memmove(ctx->in, buf, len); } + /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) @@ -3462,6 +3834,7 @@ void lsapi_MD5Final(unsigned char digest[16], struct lsapi_MD5Context *ctx) memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } + /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ @@ -3562,3 +3935,18 @@ static void lsapi_MD5Transform(uint32 buf[4], uint32 const in[16]) buf[3] += d; } + +int LSAPI_Set_Restored_Parent_Pid(int pid) +{ + int old_ppid = s_ppid; + s_restored_ppid = pid; + return old_ppid; +} + + +int LSAPI_Inc_Req_Processed(int cnt) +{ + return __sync_add_and_fetch(s_global_counter, cnt); +} + + diff --git a/sapi/litespeed/lsapilib.h b/sapi/litespeed/lsapilib.h index 99145f0cb1e..d44d6c57d49 100644 --- a/sapi/litespeed/lsapilib.h +++ b/sapi/litespeed/lsapilib.h @@ -17,7 +17,7 @@ */ /* -Copyright (c) 2002-2015, Lite Speed Technologies Inc. +Copyright (c) 2002-2018, Lite Speed Technologies Inc. All rights reserved. Redistribution and use in source and binary forms, with or without @@ -56,9 +56,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. extern "C" { #endif -#include -#include +#include "lsapidef.h" +#include #include #include @@ -169,7 +169,6 @@ int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq, char * LSAPI_GetEnv_r( LSAPI_Request * pReq, const char * name ); - ssize_t LSAPI_ReadReqBody_r( LSAPI_Request * pReq, char * pBuf, size_t len ); int LSAPI_ReqBodyGetChar_r( LSAPI_Request * pReq ); @@ -294,28 +293,28 @@ static inline int LSAPI_ForeachSpecialEnv( LSAPI_CB_EnvHandler fn, void * arg ) static inline char * LSAPI_GetEnv( const char * name ) { return LSAPI_GetEnv_r( &g_req, name ); } -static inline char * LSAPI_GetQueryString() +static inline char * LSAPI_GetQueryString(void) { return LSAPI_GetQueryString_r( &g_req ); } -static inline char * LSAPI_GetScriptFileName() +static inline char * LSAPI_GetScriptFileName(void) { return LSAPI_GetScriptFileName_r( &g_req ); } -static inline char * LSAPI_GetScriptName() +static inline char * LSAPI_GetScriptName(void) { return LSAPI_GetScriptName_r( &g_req ); } -static inline char * LSAPI_GetRequestMethod() +static inline char * LSAPI_GetRequestMethod(void) { return LSAPI_GetRequestMethod_r( &g_req ); } -static inline off_t LSAPI_GetReqBodyLen() +static inline off_t LSAPI_GetReqBodyLen(void) { return LSAPI_GetReqBodyLen_r( &g_req ); } -static inline off_t LSAPI_GetReqBodyRemain() +static inline off_t LSAPI_GetReqBodyRemain(void) { return LSAPI_GetReqBodyRemain_r( &g_req ); } static inline ssize_t LSAPI_ReadReqBody( char * pBuf, size_t len ) { return LSAPI_ReadReqBody_r( &g_req, pBuf, len ); } -static inline int LSAPI_ReqBodyGetChar() +static inline int LSAPI_ReqBodyGetChar(void) { return LSAPI_ReqBodyGetChar_r( &g_req ); } static inline int LSAPI_ReqBodyGetLine( char * pBuf, int len, int *getLF ) @@ -337,7 +336,7 @@ static inline ssize_t LSAPI_sendfile( int fdIn, off_t* off, size_t size ) static inline ssize_t LSAPI_Write_Stderr( const char * pBuf, ssize_t len ) { return LSAPI_Write_Stderr_r( &g_req, pBuf, len ); } -static inline int LSAPI_Flush() +static inline int LSAPI_Flush(void) { return LSAPI_Flush_r( &g_req ); } static inline int LSAPI_AppendRespHeader( char * pBuf, int len ) @@ -379,9 +378,39 @@ int LSAPI_Init_Env_Parameters( fn_select_t fp ); void LSAPI_Set_Slow_Req_Msecs( int msecs ); -int LSAPI_Get_Slow_Req_Msecs( ); +int LSAPI_Get_Slow_Req_Msecs(void); + +int LSAPI_is_suEXEC_Daemon(void); + +int LSAPI_Set_Restored_Parent_Pid(int pid); + +typedef void (*LSAPI_On_Timer_pf)(void); +void LSAPI_Register_Pgrp_Timer_Callback(LSAPI_On_Timer_pf); + +int LSAPI_Inc_Req_Processed(int cnt); + +#define LSAPI_LOG_LEVEL_BITS 0xff +#define LSAPI_LOG_FLAG_NONE 0 +#define LSAPI_LOG_FLAG_DEBUG 1 +#define LSAPI_LOG_FLAG_INFO 2 +#define LSAPI_LOG_FLAG_NOTICE 3 +#define LSAPI_LOG_FLAG_WARN 4 +#define LSAPI_LOG_FLAG_ERROR 5 +#define LSAPI_LOG_FLAG_CRIT 6 +#define LSAPI_LOG_FLAG_FATAL 7 + +#define LSAPI_LOG_TIMESTAMP_BITS (0xff00) +#define LSAPI_LOG_TIMESTAMP_FULL (0x100) +#define LSAPI_LOG_TIMESTAMP_HMS (0x200) + +#define LSAPI_LOG_PID (0x10000) + +void LSAPI_Log(int flag, const char * fmt, ...) +#if __GNUC__ + __attribute__((format(printf, 2, 3))) +#endif +; -int LSAPI_is_suEXEC_Daemon(); #if defined (c_plusplus) || defined (__cplusplus) } diff --git a/sapi/litespeed/lscriu.c b/sapi/litespeed/lscriu.c new file mode 100644 index 00000000000..04038bb20d5 --- /dev/null +++ b/sapi/litespeed/lscriu.c @@ -0,0 +1,1264 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2018 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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: George Wang | + +----------------------------------------------------------------------+ +*/ +/* +Copyright (c) 2002-2018, Lite Speed Technologies Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Lite Speed Technologies Inc 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 COPYRIGHT HOLDERS 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 COPYRIGHT +OWNER 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. +*/ + +#define HAVE_MSGHDR_MSG_CONTROL +#include "lsapilib.h" + +#include + +#if HAVE_STDLIB_H +#include +#endif + +#if HAVE_UNISTD_H +#include +#endif + +#include + + +#include + +#if HAVE_SYS_TYPES_H + +#include + +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "lscriu.h" + +#define LSCRIU_PATH 256 + +// Begin CRIU inclusion +//CRIU inclusion +static int s_initial_start_reqs = 0; +static int s_requests_count = 0; +static int s_restored = 0; +static int (*s_lscapi_dump_me)() = NULL; +static int (*s_lscapi_prepare_me)() = NULL; +static int s_native = 0; +static int s_tried_checkpoint = 0; +static int s_criu_debug = 0; + +typedef enum +{ + CRIU_GCOUNTER_SHM, + CRIU_GCOUNTER_SIG, + CRIU_GCOUNTER_PIPE +} GlobalCounterType_t; +static GlobalCounterType_t s_global_counter_type = CRIU_GCOUNTER_SHM; + +#ifndef sighandler_t +typedef void (*sighandler_t)(int); +#endif + +void lsapi_error( const char * pMessage, int err_no ); +void LSAPI_reset_server_state( void ); +int LSAPI_Get_ppid(); + +#ifdef LSAPILIB_DEBUG_CRIU +#define lscriu_dbg(...) \ + do { if (s_criu_debug) fprintf(stderr, __VA_ARGS__); } while(0) +#else +#define lscriu_dbg(...) +#endif + +#define lscriu_err(...) fprintf(stderr, __VA_ARGS__) + + +#define SUN_PATH_MAX (sizeof(((struct sockaddr_un *)NULL)->sun_path)) + +typedef struct +{ + pid_t m_iPidToDump; + char m_chImageDirectory[1024]; + char m_chSocketDir[SUN_PATH_MAX]; + char m_chServiceAddress[SUN_PATH_MAX]; +} criu_native_dump_t; + +typedef struct +{ + int m_iDumpResult; + char m_chDumpResponseMessage[1024]; +} criu_native_dump_response_t; + +typedef sem_t * (*psem_open_t) (const char *__name, int __oflag, ...); +typedef int (*psem_post_t) (sem_t *__sem); +typedef int (*psem_close_t) (sem_t *__sem); + +psem_open_t psem_open = NULL; +psem_post_t psem_post = NULL; +psem_close_t psem_close = NULL; + +static void lsapi_criu_signal(int signo, sighandler_t handler) +{ + struct sigaction sa; + + sigaction(signo, NULL, &sa); + + if (sa.sa_handler == SIG_DFL) { + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = handler; + sigaction(signo, &sa, NULL); + } +} + + +static void lsapi_siguser2(int sig) +{ + // child requests counter for master process + ++s_requests_count; +} + + +static void LSCRIU_Set_Initial_Start_Reqs(int reqs) +{ + s_initial_start_reqs = reqs; +} + + +static void LSCRIU_Set_Global_Counter_Type(GlobalCounterType_t tp) +{ + if ((tp == CRIU_GCOUNTER_SHM) || (tp == CRIU_GCOUNTER_SIG) + || (tp == CRIU_GCOUNTER_PIPE)) { + s_global_counter_type = tp; + } else { + s_global_counter_type = CRIU_GCOUNTER_SHM; + } + +} + + + +static int LSCRIU_Get_Global_Counter_Type(void) +{ + return s_global_counter_type; +} + +#if 0 +static int *s_shm_global_counter = NULL; + + +static int LSCRIU_Init_Global_Counter(int value) +{ + if (LSCRIU_Get_Global_Counter_Type() != CRIU_GCOUNTER_SHM + || !s_initial_start_reqs) { + return 0; + } + + s_shm_global_counter = mmap(NULL, sizeof * s_shm_global_counter, + PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (!s_shm_global_counter) { + fprintf(stderr, "LSCRIU (%d): memory for global counter allocation error\n", + getpid()); + return -1; + } + __sync_lock_test_and_set(s_shm_global_counter, value); + lscriu_dbg("LSCRIU (%d): semaphore and shared memory created\n", getpid()); + return 0; +} + + +//static void LSCRIU_Destroy_Global_Counter(int only_unmap) +//{ +// if (LSCRIU_Get_Global_Counter_Type() != CRIU_GCOUNTER_SHM) { +// return; +// } +// +// if (s_shm_global_counter) { +// munmap(s_shm_global_counter, sizeof * s_shm_global_counter); +// } +//} + + +static void LSCRIU_Increase_Global_Counter(void) +{ + if (LSCRIU_Get_Global_Counter_Type() != CRIU_GCOUNTER_SHM + || !s_initial_start_reqs) { + return; + } + + if (s_shm_global_counter) { + s_requests_count = __sync_add_and_fetch(s_shm_global_counter, 1); + //lscriu_dbg("LSCRIU (%d): increase counter %d\n", getpid(), + // s_requests_count); + } else { + s_requests_count = 0; + LSCRIU_Set_Initial_Start_Reqs(0); + } +} + + +static void LSCRIU_Get_Global_Counter(void) +{ + if (!s_initial_start_reqs) { + return; + } + + if (s_shm_global_counter) { + s_requests_count = __sync_fetch_and_or(s_shm_global_counter, 0); + //lscriu_dbg("LSCRIU (%d): get counter value %d\n", getpid(), + // s_requests_count); + } else { + lscriu_dbg("LSCRIU (%d): Reset requests in get\n", getpid()); + s_requests_count = 0; + LSCRIU_Set_Initial_Start_Reqs(0); + } +} +#else + + +static int LSCRIU_Init_Global_Counter(int value) +{ + if (LSCRIU_Get_Global_Counter_Type() != CRIU_GCOUNTER_SHM + || !s_initial_start_reqs) { + return 0; + } + + return 0; +} + + +static void LSCRIU_Increase_Global_Counter(void) +{ + if (LSCRIU_Get_Global_Counter_Type() != CRIU_GCOUNTER_SHM + || !s_initial_start_reqs) { + return; + } + + s_requests_count = LSAPI_Inc_Req_Processed(1); +} + + +static void LSCRIU_Get_Global_Counter(void) +{ + if (!s_initial_start_reqs) { + return; + } + s_requests_count = LSAPI_Inc_Req_Processed(0); + +} +#endif + + +static int LSCRIU_need_checkpoint(void) +{ + if (!s_initial_start_reqs) { + return 0; + } + + if (LSCRIU_Get_Global_Counter_Type() == CRIU_GCOUNTER_SHM + && s_requests_count <= s_initial_start_reqs) { + LSCRIU_Get_Global_Counter(); + } + if (s_initial_start_reqs > 0 + && s_requests_count >= s_initial_start_reqs) { + return 1; + } + + return 0; +} + + +static int LSCRIU_load_liblscapi(void) +{ + void *lib_handle = NULL; + void *pthread_lib_handle = NULL; + char ch; + + char *pchForceNative = NULL; + char *pchNative = NULL; + + pchForceNative = getenv("LSAPI_CRIU_FORCE_NATIVE"); + pchNative = getenv("LSCAPI_CRIU_NATIVE"); + + if (pchForceNative + && ((ch = *pchForceNative | 0x20) == '1' || ch == 't' + || (ch == 'o' && ((*(pchForceNative + 1) | 0x20) == 'n')))) + s_native = 1; + else if (pchNative + && ((ch = *pchNative | 0x20) == '1' || ch == 't' + || (ch == 'o' && ((*(pchNative + 1) | 0x20) == 'n')))) + s_native = 1; + else if (!s_native) + { + // Numerical signals indicates Apache + int error = 1; + char *last; + + if (!(lib_handle = dlopen(last = "liblscapi.so", RTLD_LAZY)) /*|| + !(pthread_lib_handle = dlopen(last = "libpthread.so", RTLD_LAZY))*/) + fprintf(stderr, "LSCRIU (%d): failed to dlopen %s: %s - ignore CRIU\n", + getpid(), last, dlerror()); + else if (!(s_lscapi_dump_me = dlsym(lib_handle, last = "lscapi_dump_me")) || + !(s_lscapi_prepare_me = dlsym(lib_handle, last = "lscapi_prepare_me")) || + !(psem_open = dlsym(pthread_lib_handle, last = "sem_open")) || + !(psem_post = dlsym(pthread_lib_handle, last = "sem_post")) || + !(psem_close = dlsym(pthread_lib_handle, last = "sem_close"))) + fprintf(stderr, "LSCRIU (%d): failed to dlsym %s: %s - ignore CRIU\n", + getpid(), last, dlerror()); + else + error = 0; + if (error) + { + // close the dll handles so we release the resources + if (lib_handle) + dlclose(lib_handle); + if (pthread_lib_handle) + dlclose(pthread_lib_handle); + return -1; + } + } + return 0; +} + + +static void LSCRIU_Wink_Server_is_Ready(void) +{ + char sem_name[60]; + + if (s_native) { + // Not used for native + return; + } + if (getenv("LSAPI_UNIQE")) + snprintf(sem_name, sizeof sem_name - 1, "lsphp[hash=%s].is_ready", + getenv("LSAPI_UNIQE")); + else + snprintf(sem_name, sizeof sem_name - 1, "lsphp[euid=0x%x].is_ready", + geteuid()); + + sem_t *is_ready_sem = psem_open(sem_name, O_RDWR); + if (is_ready_sem) { + if (psem_post(is_ready_sem) < 0) + lsapi_error(sem_name, errno); + + if (psem_close(is_ready_sem) < 0) + lsapi_error(sem_name, errno); + } + else if (errno != ENOENT) + lsapi_error(sem_name, errno); + +} + + +/* + To reset server state to proper initial state after restoring server + process from CRIU image +*/ +//static void reset_server_state( void ); + + + +#if defined(__FreeBSD__) +# include +#endif + +#ifndef __CMSG_ALIGN +#define __CMSG_ALIGN(p) (((u_int)(p) + sizeof(int) - 1) &~(sizeof(int) - 1)) +#endif + +/* Length of the contents of a control message of length len */ +#ifndef CMSG_LEN +#define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +#endif + +/* Length of the space taken up by a padded control message of +length len */ +#ifndef CMSG_SPACE +#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) +#endif + +static char *LSCRIU_Error_File_Name(char *pchFile, int max_len) +{ + const char *pchDefaultSocketPath = "/tmp/"; + const char *pchDefaultLogFileName = "lsws_error.log"; + snprintf(pchFile, max_len, "%s%s", pchDefaultSocketPath, + pchDefaultLogFileName); + return pchFile; +} + +#ifdef LSAPILIB_DEBUG_CRIU +static void LSCRIU_Debugging(void) { + char *pchCRIUDebug; + pchCRIUDebug = getenv("LSAPI_CRIU_DEBUG"); + if (!pchCRIUDebug) + pchCRIUDebug = getenv("LSCAPI_CRIU_DEBUG"); + //fprintf(stderr,"(%d) LSCRIU: CRIU debug environment variable: %s\n", + // getpid(), pchCRIUDebug); + // I've made it easy to turn on debugging. CloudLinux Apache sets + // LSCAPI_CRIU_DEBUG to nothing to indicate it's on. Sigh. + if ((!pchCRIUDebug) || + ((!*pchCRIUDebug) || + (*pchCRIUDebug == '0') || + (*pchCRIUDebug == 'f') || + (*pchCRIUDebug == 'F') || + (((*pchCRIUDebug == 'O') || + (*pchCRIUDebug == 'o')) && + ((*(pchCRIUDebug + 1)) && + ((*(pchCRIUDebug + 1) == 'F') || (*(pchCRIUDebug + 1) == 'f')))))) + { + lscriu_dbg("LSCRIU (%d): CRIU Debugging disabled by environment\n", getpid()); + s_criu_debug = 0; + } + else { + s_criu_debug = 1; + lscriu_dbg("LSCRIU (%d): CRIU Debugging enabled by environment\n", getpid()); + fprintf(stderr,"LSCRIU (%d): CRIU debug environment variable: %s\n", + getpid(), pchCRIUDebug); + } +} + + +static void LSCRIU_Restored_Error(int iFatal, char *format, ...) { + // This routine deals with the awful situation of trying to get info while the stderr handle is closed on purpose. + int iOldUMask; + int iFd = -1; + char chFile[1024]; + + if (!iFatal) { + // LSCRIU_Debugging(); + if (!s_criu_debug) { + // Debugging message and debugging is off + return; + } + } + if (!LSCRIU_Error_File_Name(chFile, sizeof(chFile))) { + // We're done here...nowhere to write + return; + } + iOldUMask = umask(0); + iFd = open( chFile, O_WRONLY | O_APPEND | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + umask(iOldUMask); + if (iFd >= 0) { + char chFullMessage[0x1000]; + struct timeb sTimeb; + struct tm sTm; + ftime(&sTimeb); + localtime_r(&sTimeb.time,&sTm); + va_list ap; + va_start(ap, format); + char buf[0x1000]; + vsnprintf(buf, sizeof(buf), format, ap); + va_end(ap); + + int n = snprintf(chFullMessage, sizeof(chFullMessage), + "%04d-%02d-%02d %02d:%02d:%02d.%03d: LSCRIU (%d): %s %s\n", + sTm.tm_year + 1900, + sTm.tm_mon + 1, + sTm.tm_mday, + sTm.tm_hour, + sTm.tm_min, + sTm.tm_sec, + sTimeb.millitm, + getpid(), + iFatal ? "FATAL! " : "(debug) ", + buf); + if (n > (int)sizeof(chFullMessage)) + n = sizeof(chFullMessage); + write(iFd, chFullMessage, n); + close(iFd); + } +} +#else // no debugging +static void inline LSCRIU_Debugging(void) {} +static void inline LSCRIU_Restored_Error(int iFatal, char *format, ...) {} +#endif + + +static ssize_t read_fd(int fd, void *ptr, size_t nbytes, int *recvfd) +{ + struct msghdr msg; + struct iovec iov[1]; + ssize_t n; + +#ifdef HAVE_MSGHDR_MSG_CONTROL + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); +#else + int newfd; + msg.msg_accrights = (caddr_t) &newfd; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + if ( (n = recvmsg(fd, &msg, 0)) <= 0) + return(n); + +#ifdef HAVE_MSGHDR_MSG_CONTROL + if ( (cmptr = CMSG_FIRSTHDR(&msg)) != NULL && + cmptr->cmsg_len == CMSG_LEN(sizeof(int))) { + if (cmptr->cmsg_level != SOL_SOCKET) { + LSCRIU_Restored_Error(1, "control level != SOL_SOCKET"); + return(-1); + } + if (cmptr->cmsg_type != SCM_RIGHTS) { + LSCRIU_Restored_Error(1, "control type != SCM_RIGHTS"); + return(-1); + } + *recvfd = *((int *) CMSG_DATA(cmptr)); + } else + *recvfd = -1; /* descriptor was not passed */ +#else +/* *INDENT-OFF* */ + if (msg.msg_accrightslen == sizeof(int)) + *recvfd = newfd; + else + *recvfd = -1; /* descriptor was not passed */ +/* *INDENT-ON* */ +#endif + + return(n); +} + + +static char *CloudLinux_Socket_Name(pid_t iPidDumped, + char chFullName[], + int iMaxSize) +{ + snprintf(chFullName,iMaxSize,"/tmp/%lu.sock",iPidDumped); + return chFullName; +} + + +static int LSCRIU_Receive_Handles(char *pchSocketPath, + pid_t *piPidSender, + int iDumped) +{ + time_t iTime; + int iError = 0; + time(&iTime); + LSCRIU_Restored_Error(0, "Receive handles on %s and then Reset requests", + pchSocketPath); + { + int fdSocket = -1; + int fdConnected = -1; + int fdZero = -1; + int fdTwo = -1; + int fdSave = -1; + struct sockaddr_un sSockaddrUn, sSockaddrUnRemote; + int iRc, iRemoteLength; + char chSocketPath[SUN_PATH_MAX]; + + int path_len = strlen(pchSocketPath); + if (path_len > (int)sizeof(chSocketPath) - 1) + return -1; + memmove(chSocketPath, pchSocketPath, path_len + 1); + pchSocketPath = chSocketPath; + + fdSave = g_req.m_fdListen; + LSCRIU_Restored_Error(0, "Opening domain socket defined at " + "LSCAPI_CRIU_SOCKET_NAME: %s, comm #%d, %s", + pchSocketPath, fdSave, iDumped ? "DUMP" : "RESTORE"); + // Before listening, let's delete it, just in case + fdSocket = socket(AF_UNIX, SOCK_STREAM, 0); + if (fdSocket == -1) { + LSCRIU_Restored_Error(1, "Error performing simple socket call: %s", + strerror(errno)); + iError = 1; + } + else { + memset(&sSockaddrUn, 0, sizeof(sSockaddrUn)); + sSockaddrUn.sun_family = AF_UNIX; + memmove(sSockaddrUn.sun_path, pchSocketPath, path_len); + iRc = bind(fdSocket, (struct sockaddr *) &sSockaddrUn, + sizeof(sSockaddrUn)); + if (iRc == -1) { + LSCRIU_Restored_Error( 1, "Error binding socket %s: %s", + pchSocketPath, strerror(errno)); + iError = 1; + } + } + if (!iError) { + iRc = listen(fdSocket, 5); + if (iRc == -1) { + LSCRIU_Restored_Error( 1, "Error listening on socket %s: %s", + pchSocketPath, strerror(errno)); + iError = 1; + } + } + if (!iError) { + fdConnected = accept(fdSocket, (struct sockaddr *)&sSockaddrUnRemote, + (unsigned int *)&iRemoteLength); + if (fdConnected == -1) { + LSCRIU_Restored_Error( 1, "Error accepting on socket %s: %s", + pchSocketPath, strerror(errno)); + iError = 1; + } + } + if (!iError) { + LSCRIU_Restored_Error( 0, "Connected - now receive the file handles"); + // Zero is only used on a restore - it's the fd_listen handle on a dump. + iRc = read_fd(fdConnected, piPidSender, sizeof(pid_t), &fdZero); + if (iRc == -1) { + LSCRIU_Restored_Error( 1, "Error reading handle %d through " + "sockets: %s", + (iDumped ? fdSave : 0), strerror(errno)); + iError = 1; + } + } + if (!iError) { + LSCRIU_Restored_Error(0, "Now receive handle 2"); + iRc = read_fd(fdConnected, piPidSender, sizeof(pid_t), &fdTwo); + if (iRc == -1) { + LSCRIU_Restored_Error( 1, "Error reading handle 2 through " + "sockets: %s", strerror(errno)); + iError = 1; + } + } + if (!iError) { + LSCRIU_Restored_Error( 0, "Received handles, Dup and use them. " + "Received caller pid: %d, handles 1, 2 and " + "fdSave=%d, fdZero=%d, fdTwo=%d", + *piPidSender, fdSave, fdZero, fdTwo); + // So we don't step on ourselves. + if (fdSave == fdTwo) { + dup2(fdTwo,2); + dup2(fdZero,fdSave); + } + else { + dup2(fdZero,fdSave); + dup2(fdTwo,2); + } + dup2(2,1); + // Theoretically I can now go back to normal tracing...but keep + // logging the other way, just in case... + LSCRIU_Restored_Error( 0, "One final trace before writing to " + "stderr\n"); + lscriu_dbg("LSCRIU (%d): Restored handles - almost ready to actually resume!\n", getpid()); + LSCRIU_Restored_Error( 0, "We just wrote to stderr"); + } + if ((fdTwo > 3) && (fdTwo != fdSave)) { // So we don't close something we need! + lscriu_dbg("LSCRIU (%d): Restored handles - about to close fdTwo: %d\n", + getpid(), fdTwo); + close(fdTwo); + } + if ((fdZero > 3) && (fdZero != fdSave)) { + // We verify that we're not closing a socket we just dup-ed to! + lscriu_dbg("LSCRIU (%d): Restored handles - about to close fdZero: %d\n", + getpid(), fdZero); + close(fdZero); + } + if ((fdConnected > 3) && (fdConnected != fdSave)) { + lscriu_dbg("LSCRIU (%d): Restored handles - about to close fdConnected: %d\n", + getpid(), fdConnected); + close(fdConnected); + } + if ((fdSocket > 3) && (fdSocket != fdSave)) { + lscriu_dbg("LSCRIU (%d): Restored handles - about to close fdSocket: %d\n", + getpid(), fdSocket); + close(fdSocket); + } + /* Leave the socket path file around so we can restore it again! + lscriu_dbg("LSCRIU (%d): Closed the sockets, now delete the socket file\n", getpid()); + if (pchSocketPath) { + if (unlink(pchSocketPath) == -1) { + lscriu_err("LSCRIU (%d): Error deleting socket path: %s: %s\n", + getpid(), pchSocketPath, strerror(errno)); + } + else { + lscriu_dbg("LSCRIU (%d): Deleted %s\n", getpid(), pchSocketPath); + } + } + else { + char buf[0x1000]; + snprintf(buf, sizeof(buf) - 1, + "LSCRIU (%d): Lost the LSCAPI_CRIU_SOCKET_NAME environment" + " variable", getpid()); + fprintf(stderr, "%s\n", buf ); + LSCRIU_Restored_Error(1, buf ); + } + */ + } + return(iError ? -1 : 0); +} + + +/* This is pretty standard code taken from: + * http://www.cs.cmu.edu/afs/cs/academic/class/15213-f00/unpv12e/lib/write_fd.c + * But also in UNIX Network Programming */ +static ssize_t write_fd(int fd, void *ptr, size_t nbytes, int sendfd) +{ + struct msghdr msg; + struct iovec iov[1]; + +#ifdef HAVE_MSGHDR_MSG_CONTROL + union { + struct cmsghdr cm; + char control[CMSG_SPACE(sizeof(int))]; + } control_un; + struct cmsghdr *cmptr; + + msg.msg_control = control_un.control; + msg.msg_controllen = sizeof(control_un.control); + + cmptr = CMSG_FIRSTHDR(&msg); + cmptr->cmsg_len = CMSG_LEN(sizeof(int)); + cmptr->cmsg_level = SOL_SOCKET; + cmptr->cmsg_type = SCM_RIGHTS; + *((int *) CMSG_DATA(cmptr)) = sendfd; +#else + msg.msg_accrights = (caddr_t) &sendfd; + msg.msg_accrightslen = sizeof(int); +#endif + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + iov[0].iov_base = ptr; + iov[0].iov_len = nbytes; + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + return(sendmsg(fd, &msg, 0)); +} + + +/* +int LSCRIU_Send_File_Handles(char *pchSocketName) +{ + // Called by the child after the fork, sends the two closed handles + struct sockaddr_un sSockaddrUn; + int iFdSocket = -1; + int iLength; + int iRc; + time_t iTimeEntry = 0; + time_t iTimeNow; + int iError = 0; + int iConnected = 0; + + sSockaddrUn.sun_family = AF_UNIX; + LSCRIU_Restored_Error(0, "Send file handles using socket file: %s", + pchSocketName); + strncpy(sSockaddrUn.sun_path, pchSocketName, sizeof(sSockaddrUn.sun_path)); + iFdSocket = socket(AF_UNIX, SOCK_STREAM, 0); + if (iFdSocket == -1) { + LSCRIU_Restored_Error( 1, "Send file handles to restored images, error " + "creating initial socket: %s",strerror(errno)); + return(0); + } + iLength = strlen(sSockaddrUn.sun_path) + sizeof(sSockaddrUn.sun_family); + do { + iRc = connect(iFdSocket, (struct sockaddr *)&sSockaddrUn, iLength); + if (iRc == -1) { + time(&iTimeNow); + if (!iTimeEntry) { + iTimeEntry = iTimeNow; + } + else if (iTimeNow - iTimeEntry > 5) { + LSCRIU_Restored_Error( 1, "Timed out trying to connect to " + "restore handles"); + iError = 1; + } + else { + usleep(10); // Keep waiting + } + } + else { + LSCRIU_Restored_Error(0, "Connected to restored child to send " + "handles."); + iConnected = 1; + } + } while ((!iConnected) && (!iError)); + if (iConnected) { + pid_t iPid = LSAPI_Get_ppid(); + if (!iPid) { + iPid = getpid(); + } + if (write_fd(iFdSocket, &iPid, sizeof(iPid), g_req.m_fdListen) == -1) { + LSCRIU_Restored_Error( 1, "Error writing listen socket #%d to " + "parent: %s", g_req.m_fdListen, + strerror(errno)); + iError = 1; + } + else if (write_fd(iFdSocket, &iPid, sizeof(iPid), 2) == -1) { + LSCRIU_Restored_Error( 1, "Error writing socket #2 to parent: %s", + strerror(errno)); + iError = 1; + } + else { + LSCRIU_Restored_Error(0, "Sockets sent to child - it can now perform" + " the function"); + } + } + if (iFdSocket != -1) { + close(iFdSocket); + } + return(!iError); +} +*/ + +static int LSCRIU_Native_Dump(pid_t iPid, + char *pchSocketName, + char *pchSocketPath, + char *pchImagePath, + int iFdNative) { + criu_native_dump_t criu_native_dump; + char *pchLastSlash; + criu_native_dump_response_t criu_native_dump_response; + + criu_native_dump.m_iPidToDump = iPid; + strncpy(criu_native_dump.m_chServiceAddress, pchSocketPath, + sizeof(criu_native_dump.m_chServiceAddress)); + strncpy(criu_native_dump.m_chImageDirectory, pchImagePath, + sizeof(criu_native_dump.m_chImageDirectory)); + strncpy(criu_native_dump.m_chSocketDir, pchSocketName, + sizeof(criu_native_dump.m_chSocketDir)); + pchLastSlash = strrchr(criu_native_dump.m_chSocketDir,'/'); + if (pchLastSlash) { + pchLastSlash++; + (*pchLastSlash) = 0; + } + if (write(iFdNative, + &criu_native_dump, + sizeof(criu_native_dump)) == -1) { + lscriu_err("LSCRIU (%d): Error sending dump request to the listener: %s\n", + getpid(), strerror(errno)); + return(-1); + } + lscriu_dbg("LSCRIU (%d): Sent the dump request to the listener\n", getpid()); + //while (sleep(7200)); + if (read(iFdNative, + &criu_native_dump_response, + sizeof(criu_native_dump_response)) == -1) { + // The test will actually fail it! + //LSCRIU_Restored_Error(1, "Error reading dump socket #%d from parent: %s", + // iFdNative, strerror(errno)); + //return(-1); + } + return(0); +} + + +static void LSCRIU_CloudLinux_Checkpoint(void) +{ + int iRet; + + if ((!s_native) && (!s_lscapi_dump_me)) { + lscriu_dbg("LSCRIU (%d): Not native and unable to dump - abandon one-time " + "dump\n", getpid()); + return; + } + // NOTE - NOTE - NOTE + // In the CloudLinux implementation they do not do a prepare_me. Why, I + // don't know, so I won't either! + //iRet = s_lscapi_prepare_me(); + //if (iRet) { + //LSCRIU_Restored_Error(1, "lscapi_prepare_me failed: %s", + // strerror(errno)); + //lscriu_err("LSCRIU (%d): failed to lscapi_prepare_me: %s\n", + // getpid(), strerror(errno)); + //But keep going anyway! Not fatal yet + //} + iRet = s_lscapi_dump_me(); + if (iRet < 0) { + char *pchError; + lscriu_err("LSCRIU: CloudLinux dump of PID: %d, error: %s\n", + getpid(), strerror(errno)); + } + if (iRet == 0) { + // Dumped. To continue the child must send us the handles back + lscriu_err("LSCRIU: Successful CloudLinux dump of PID: %d\n", getpid()); + } + else { + s_restored = 1; + LSAPI_reset_server_state(); + /* + Here we have restored the php process, so we should to tell (via + semaphore) mod_lsapi that we are started and ready to receive data. + */ + LSCRIU_Wink_Server_is_Ready(); + lscriu_err("LSCRIU: Successful CloudLinux restore of PID: %d, parent: %d.\n", + getpid(), getppid()); + } + LSCRIU_Set_Initial_Start_Reqs(0); +} + + +static void LSCRIU_try_checkpoint(void) +{ + int iRet; + pid_t iPid; + pid_t iPidDump = getpid(); + int iFdNative; + char *pchImagePath; + char *pchSocketPath; + char *pchSocketName; + + if (s_tried_checkpoint) { + lscriu_dbg("LSCRIU (%d): Already tried checkpoint - one time per customer\n", + getpid()); + return; + } + lscriu_dbg("LSCRIU (%d): Trying checkpoint\n", getpid()); + s_tried_checkpoint = 1; + if (!s_native) { + LSCRIU_CloudLinux_Checkpoint(); + return; + } + char *pchFd; + pchSocketName = getenv("LSCAPI_CRIU_SOCKET_NAME"); + + if (!pchSocketName) { + lscriu_err("LSCRIU (%d): LSCAPI_CRIU_SOCKET_NAME internal environment " + "variable not set - contact Litespeed tech support\n", getpid()); + return; + } + unlink(pchSocketName); + pchFd = getenv("LSCAPI_CRIU_SYNC_FD"); + pchImagePath = getenv("LSCAPI_CRIU_IMAGE_PATH"); + pchSocketPath = getenv("LSAPI_CRIU_SOCKET_PATH"); + if (!pchSocketPath) { + pchSocketPath = getenv("LSCAPI_CRIU_SOCKET_PATH"); + } + if (!pchFd) { + lscriu_err("LSCRIU (%d): LSCAPI_CRIU_SYNC_FD internal environment " + "variable not set - contact Litespeed tech support\n", getpid()); + return; + } + if (!pchImagePath) { + lscriu_err("LSCRIU (%d): LSCAPI_CRIU_IMAGE_PATH internal environment " + "variable not set - contact Litespeed tech support\n", getpid()); + return; + } + if (!pchSocketPath) { + lscriu_err("LSCRIU (%d): LSAPI_CRIU_SOCKET_PATH internal environment " + "variable not set - contact Litespeed tech support\n", getpid()); + return; + } + lscriu_dbg("LSCRIU (%d): Checkpoint dump. ImagePath: %s, SocketName %s, " + "SocketPath: %s\n", getpid(), pchImagePath, pchSocketName, pchSocketPath); + + iFdNative = atoi(pchFd); + lscriu_dbg("LSCRIU (%d): Native checkpoint. Use filepointer %d (%s) to send " + "pid %d\n", getpid(), iFdNative, pchFd, iPidDump); + + // Handle 0 is the connected comm handle, handle 1 and 2 are the output data + // streams. We can't do a dump with 1 or 2 open. And there's no reason to + // dump 0 since it will change. So on George's advice, we'll fork here, let + // the child close the handles and do the dump. + // On restore, the recovered child will create a domain socket, and the + // parent will send it the handles it can use to continue. + + lscriu_dbg("LSCRIU (%d): fork!\n", getpid()); + iPid = fork(); + if (iPid < 0) { + lscriu_err("LSCRIU (%d): Can't checkpoint due to a fork error: %s\n", + getpid(), strerror(errno)); + return; + } + if (iPid > 0) { + // We're the parent. Close the handles, do the dump and receive them + // back from the child or the parent + //close(0); + // Let the child do the dump + usleep(1); // Get the child to run and close everything so we can dump + pid_t iPidParent = getppid(); + iRet = LSCRIU_Native_Dump(iPid, + pchSocketName, + pchSocketPath, + pchImagePath, + iFdNative); + if (iRet == 0) { + if (iPidParent != getppid()) + lscriu_dbg("LSCRIU (%d): PID %d dumped to disk; PID %d killed in " + "prior message - is this the restore?\n", getpid(), + iPidDump, iPid); + else + lscriu_err("LSCRIU (%d): PID %d dumped to disk and killed in " + "prior message\n", getpid(), iPid); + } + else + lscriu_err("LSCRIU (%d): Native dump of PID: %d FAILED\n", getpid(), + iPid); + LSCRIU_Restored_Error(1, "Restored child process - this is unexpected!"); + } + else { + int iResult; + pid_t iPidSender; + pid_t iPidParent = getppid(); + + setsid(); + close(1); + close(2); + close(g_req.m_fdListen); // If we don't do this we're dead... + close(iFdNative); + // Now get restored. We know if we're restored if the ppid changes! + // If we're dumped, we're killed (no use worrying about that!). + { + time_t iTimeStart = 0; + time_t iTimeNow; + int iRestored = 0; + do { + usleep(10); + time(&iTimeNow); + if (!iTimeStart) { + iTimeStart = iTimeNow; + } + else if ((iPidParent != getppid()) || + (iTimeNow - iTimeStart > 10)) { + iRestored = 1; + } + else if (iTimeNow - iTimeStart > 5) { + LSCRIU_Restored_Error(1, "Timed out waiting to be dumped"); + exit(1); + } + } while (!iRestored); + } + LSCRIU_Restored_Error(0, "Restored!"); + iRet = LSCRIU_Receive_Handles(pchSocketName, &iPidSender, 0); + if (iRet == 0) { + //lscriu_dbg("LSCRIU (%d): Fully restored, parent: %d.\n", getpid(), iPidSender); + LSAPI_Set_Restored_Parent_Pid(iPidSender); + lscriu_err("LSCRIU: Fully restored, pid: %d, parent: %d.\n", + getpid(), iPidSender); + LSAPI_reset_server_state(); + s_restored = 1; + /* + Here we have restored the php process, so we should to + tell(via semaphore) mod_lsapi that we are started and ready + to receive data + */ + // Not used for native! + //LSCRIU_Wink_Server_is_Ready(); + } + else { + LSCRIU_Restored_Error(1, "Restore error - terminating"); + exit(-1); + } + } + LSCRIU_Set_Initial_Start_Reqs(0); +} + + +static int LSCRIU_Init_Env_Parameters(void) +{ + const char *p; + int n; + + // LSAPI_CRIU is not passed down in Apache. So we won't require it + //p = getenv("LSAPI_CRIU"); + //if ((p) && + // (*p) && + // ((*p == '1') || + // (*p == 'T') || + // (*p == 't') || + // (*p == 'Y') || + // (*p == 'y') || + // (((*p == 'O') || + // (*p == 'o')) && + // (((*(p + 1)) == 'N') || + // ((*(p + 1)) == 'n'))))) { + // lscriu_dbg("LSCRIU (%d): Enabled by environment.\n", getpid()); + // // We're enabled! + //} + //else { + // lscriu_dbg("LSCRIU (%d): Disabled by environment.\n", getpid()); + // return 0; + //} + p = getenv("LSAPI_INITIAL_START"); + if (!p) + p = getenv("LSAPI_BACKEND_INITIAL_START"); + if (p) + { + n = atoi(p); + + if (n > 0) + { + lscriu_dbg("LSCRIU (%d): Set start requests based on environment (%d)\n", + getpid(), n); + LSCRIU_Set_Initial_Start_Reqs(n); + } + else + { + lscriu_dbg("LSCRIU (%d): LSAPI_INITIAL_START set to 0 disabled\n", + getpid()); + return 0; + } + } + else + { + lscriu_dbg("LSCRIU (%d): LSAPI_INITIAL_START NOT set - disabled\n", + getpid()); + return 0; + } + if (LSAPI_Is_Listen()) + { + lscriu_dbg("LSCRIU (%d): Listening...\n", getpid()); + GlobalCounterType_t gc_type = CRIU_GCOUNTER_SHM; + char *env; + if ((env = getenv("LSAPI_CRIU_USE_SHM"))) + { + // CloudLinux doc: Off (shared memory) or Signals. + // Litespeed doc: On (shared memory) or Signals + // So just check the first character for an 'S' and if not, then + // use shared memory. Pipe support is lost (sigh). + if ((*env == 'S') || (*env == 's')) + gc_type = CRIU_GCOUNTER_SIG; // Just assume the rest is signals + // else use the default of shared memory + } + else if ((env = getenv("LSAPI_SIGNALS"))) + { + if ((*env == '1') || + (*env == 'Y') || + (*env == 'y') || + (*env == 'T') || + (*env == 't') || + (((*env == 'O') || (*env == 'o')) && + ((*(env + 1) == 'N') || (*(env + 1) == 'n')))) + gc_type = CRIU_GCOUNTER_SIG; + else if (*env == 2) + gc_type = CRIU_GCOUNTER_PIPE; // The only case for pipe + //else use the default of shared memory + } + if (gc_type != CRIU_GCOUNTER_SHM) + { + lscriu_dbg("LSCRIU (%d): Use %s\n", getpid(), + gc_type == CRIU_GCOUNTER_SIG ? "signals" : "pipe"); + lsapi_criu_signal(SIGUSR2, lsapi_siguser2); + } + else + lscriu_dbg("LSCRIU (%d): Use shared memory\n", getpid()); + LSCRIU_Set_Global_Counter_Type(gc_type); + } + else + lscriu_dbg("LSCRIU (%d): NOT Listening\n", getpid()); + //unset_lsapi_envs(); + return 0; +} + + +void LSCRIU_inc_req_procssed() +{ + if (!LSCRIU_Get_Global_Counter_Type()) { + ++s_requests_count; + } + + lscriu_dbg("LSCRIU (%d): s_requests_count %d counter %d\n", getpid(), + s_requests_count, s_initial_start_reqs); + + if (s_initial_start_reqs > 0 && s_requests_count <= s_initial_start_reqs) { + if (LSCRIU_Get_Global_Counter_Type() == CRIU_GCOUNTER_SHM) { + LSCRIU_Increase_Global_Counter(); + if (s_requests_count >= s_initial_start_reqs) { + //Maybe this time we can stop to send signal and wait for + //1 second of select timeout + //kill( LSCRIU_Get_ppid(), SIGUSR2 ); + lscriu_dbg("LSCRIU (%d): Time to dump main process with semaphore\n", + getpid()); + } + } else { + kill(LSAPI_Get_ppid(), SIGUSR2); + lscriu_dbg("LSCRIU (%d): Send kill to main process with signals\n", + getpid()); + } + } +} + + +static void LSCRIU_on_timer(void) +{ + lscriu_dbg("LSCRIU (%d): LSCRIU_on_timer\n", getpid()); + if (LSCRIU_need_checkpoint()) { + LSCRIU_try_checkpoint(); + } +} + + +int LSCRIU_Init(void) +{ + LSCRIU_Debugging(); + LSCRIU_Init_Env_Parameters(); + if (s_initial_start_reqs) { + if (LSCRIU_load_liblscapi() == -1) + s_initial_start_reqs = 0; + } + if (s_initial_start_reqs) { + LSCRIU_Wink_Server_is_Ready(); + lscriu_dbg("LSCRIU (%d): LSAPI_Register_Pgrp_Timer_Callback\n", getpid()); + LSAPI_Register_Pgrp_Timer_Callback(LSCRIU_on_timer); + LSCRIU_Init_Global_Counter(0); + } + return s_initial_start_reqs > 0; +} + diff --git a/sapi/litespeed/lscriu.h b/sapi/litespeed/lscriu.h new file mode 100644 index 00000000000..9f40df37d50 --- /dev/null +++ b/sapi/litespeed/lscriu.h @@ -0,0 +1,67 @@ +/* + +----------------------------------------------------------------------+ + | PHP Version 7 | + +----------------------------------------------------------------------+ + | Copyright (c) 1997-2018 The PHP Group | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.01 of the PHP license, | + | that is bundled with this package in the file LICENSE, and is | + | available at through the world-wide-web at the following url: | + | http://www.php.net/license/3_01.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: George Wang | + +----------------------------------------------------------------------+ +*/ +/* +Copyright (c) 2002-2018, Lite Speed Technologies Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the Lite Speed Technologies Inc 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 COPYRIGHT HOLDERS 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 COPYRIGHT +OWNER 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. +*/ + +#ifndef _LSCRIU_H_ +#define _LSCRIU_H_ + +#if defined (c_plusplus) || defined (__cplusplus) +extern "C" { +#endif + +// return 1 if CRIU is available, return 0 if CRIU is not available +int LSCRIU_Init(void); + +void LSCRIU_inc_req_procssed(void); + + +#if defined (c_plusplus) || defined (__cplusplus) +} +#endif + +#endif // LSCRIU_ \ No newline at end of file