add CRIU support.
add support for [PATH=] and [HOST=] sections in php.ini
#include <arpa/inet.h>
#include <netinet/in.h>
+#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
+#include "lscriu.c"
+#endif
#define SAPI_LSAPI_MAX_HEADER_LENGTH 2048
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;
/* {{{ 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) {
*/
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;
}
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;
- }
-
- 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);
- }
+ 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);
+ }
}
/* {{{ sapi_lsapi_register_variables
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);
#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;
static sapi_module_struct lsapi_sapi_module =
{
"litespeed",
- "LiteSpeed V6.11",
+ "LiteSpeed V7.0",
php_lsapi_startup, /* startup */
php_module_shutdown_wrapper, /* shutdown */
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
{
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();
/* 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);
}
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 ) {
}
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;
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);
}
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;
}
/* 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,
_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;
}
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);
}
-static int processReq( void )
+static int processReq(void)
{
int ret = 0;
zend_first_try {
override_ini();
if ( engine ) {
- init_request_info( );
+ init_request_info();
if ( lsapi_module_main( source_highlight ) == -1 ) {
ret = -1;
return ret;
}
-static void cli_usage( void )
+static void cli_usage(void)
{
static const char * usage =
"Usage: php\n"
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
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 == '-' )) {
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 ) {
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;
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 );
}
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;
}
}
*/
/*
-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
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
-
+#include <limits.h>
#include <sys/stat.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
+#include <stdarg.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/resource.h>
#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;
+
+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 = { -1, -1 };
+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] =
{
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;
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__)
pHeader->m_packetLen.m_iLen = len;
}
+
static int lsapi_set_nblock( int fd, int nonblock )
{
int val = fcntl( fd, F_GETFL, 0 );
return 0;
}
+
static int lsapi_close( int fd )
{
int ret;
}
}
+
+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;
}
}
+
/*
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;
return totalLen - left;
}
+
/*
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 );
return 0;
}
+
static int allocateRespHeaderBuf( LSAPI_Request * pReq, int size )
{
char * p = (char *)realloc( pReq->m_pRespHeaderBuf, size );
return pHeader->m_packetLen.m_iLen;
}
+
static int allocateEnvList( struct LSAPI_key_value_pair ** pEnvList,
int *curSize, int newSize )
{
}
+
static inline int isPipe( int fd )
{
char achPeer[128];
return 1;
}
+
static int parseEnv( struct LSAPI_key_value_pair * pEnvList, int count,
char **pBegin, char * pEnd )
{
return 0;
}
+
static inline void swapIntEndian( int * pInteger )
{
char * p = (char *)pInteger;
}
+
static inline void fixEndian( LSAPI_Request * pReq )
{
struct lsapi_req_header *p= pReq->m_pHeader;
swapIntEndian( &p->m_cntSpecialEnv );
}
+
static void fixHeaderIndexEndian( LSAPI_Request * pReq )
{
int i;
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)
return (s_liblve)? 0 : -1;
}
-static int init_lve_ex()
+
+static int init_lve_ex(void)
{
int rc;
if ( !s_liblve )
return 0;
}
-int LSAPI_is_suEXEC_Daemon()
+
+int LSAPI_is_suEXEC_Daemon(void)
{
if (( !s_uid )&&( s_secret[0] ))
return 1;
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
return 0;
}
+
static int lsapi_lve_error( LSAPI_Request * pReq )
{
static const char * headers[] =
return 0;
}
+
static int lsapi_enterLVE( LSAPI_Request * pReq, uid_t uid )
{
#if defined(linux) || defined(__linux) || defined(__linux__) || defined(__gnu_linux__)
return 0;
}
+
static int lsapi_jailLVE( LSAPI_Request * pReq, uid_t uid, struct passwd * pw )
{
int ret = 0;
#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 )
{
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;
}
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 );
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 )
return 0;
}
+
//OPTIMIZATION
static char s_accept_notify = 0;
static char s_schedule_notify = 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 )
{
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 );
}
return 0;
}
+
static char s_conn_key_packet[16];
static inline int init_conn_key( int fd )
{
}
+
static int readReq( LSAPI_Request * pReq )
{
int len;
}
-
int LSAPI_Init(void)
{
if ( !g_inited )
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;
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];
}
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 )
{
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 */
return pReq->m_pHttpHeader + off;
}
+
static int readBodyToReqBuf( LSAPI_Request * pReq )
{
off_t bodyLeft;
}
-
int LSAPI_ReqBodyGetLine_r( LSAPI_Request * pReq, char * pBuf, size_t bufLen, int *getLF )
{
ssize_t len;
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 )
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;
}
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;
return p - pBuf;
}
+
#if defined(__FreeBSD__ ) || defined(__NetBSD__) || defined(__OpenBSD__)
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 )
{
}
#endif
+
#if defined(sun) || defined(__sun)
#include <sys/sendfile.h>
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 <sys/sendfile.h>
#define gsendfile sendfile
#endif
+
+
#if defined(HPUX)
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;
}
-
-
int LSAPI_Flush_r( LSAPI_Request * pReq )
{
int ret = 0;
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;
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 )
{
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;
return NULL;
}
+
struct _headerInfo
{
const char * _name;
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 )
{
return ret;
}
return count;
-
}
}
}
return count + pReq->m_pHeader->m_cntUnknownHeaders;
-
}
+
static int EnvForeach( struct LSAPI_key_value_pair * pEnv,
int n, LSAPI_CB_EnvHandler fn, void * arg )
{
}
-
int LSAPI_ForeachEnv_r( LSAPI_Request * pReq,
LSAPI_CB_EnvHandler fn, void * arg )
{
}
-
int LSAPI_ForeachSpecialEnv_r( LSAPI_Request * pReq,
LSAPI_CB_EnvHandler fn, void * arg )
{
}
-
int LSAPI_FinalizeRespHeaders_r( LSAPI_Request * pReq )
{
if ( !pReq || !pReq->m_pIovec )
}
-
int LSAPI_AppendRespHeader_r( LSAPI_Request * pReq, const char * pBuf, int len )
{
if ( !pReq || !pBuf || len <= 0 || len > LSAPI_RESP_HTTP_HEADER_MAX )
close(fd);
errno = ret;
return -1;
-
}
+
int LSAPI_ParseSockAddr( const char * pBind, struct sockaddr * pAddr )
{
char achAddr[256];
}
+
int LSAPI_CreateListenSock( const char * pBind, int backlog )
{
char serverAddr[128];
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;
+static fn_select_t g_fnSelect = select;
typedef struct _lsapi_prefork_server
{
int m_fd;
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 )
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 )
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;
return 0;
}
+
void LSAPI_Set_Server_fd( int fd )
{
if( g_prefork_server )
}
-
-
-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)
s_stop = signal;
}
+
static lsapi_child_status * find_child_status( int pid )
{
lsapi_child_status * pStatus = g_prefork_server->m_pChildrenStatus;
}
+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 );
{
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 )
{
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 )
}
-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 )
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];
}
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;
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;
}
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;
}
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;
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)
}
}
- 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 )
- {
- /*
- if ( pServer->m_iCurChildren >= 0 )
+ 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 >= 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 )
{
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 )
{
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 );
}
+
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;
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 );
}
{
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 )
}
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 )
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 ))
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 <crt_externs.h>
#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__)
}
}
-static int lsapi_initSuEXEC()
+
+static int lsapi_initSuEXEC(void)
{
int i;
struct passwd * pw;
}
+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" );
{
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 )
} while (--longs);
}
+
/*
* Start MD5 accumulation. Set bit count to 0 and buffer to mysterious
* initialization constants.
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)
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) */
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);
+}
+
+
*/
/*
-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
extern "C" {
#endif
-#include <stddef.h>
-#include <lsapidef.h>
+#include "lsapidef.h"
+#include <stddef.h>
#include <sys/time.h>
#include <sys/types.h>
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 );
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 )
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 )
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)
}
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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 <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+/*
+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 <stdio.h>
+
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <sys/wait.h>
+
+
+#include <sys/stat.h>
+
+#if HAVE_SYS_TYPES_H
+
+#include <sys/types.h>
+
+#endif
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <semaphore.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <signal.h>
+#include <time.h>
+#include <sys/timeb.h>
+#include <unistd.h>
+#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 <sys/param.h>
+#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;
+}
+
--- /dev/null
+/*
+ +----------------------------------------------------------------------+
+ | 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 <gwang@litespeedtech.com> |
+ +----------------------------------------------------------------------+
+*/
+/*
+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