]> granicus.if.org Git - php/commitdiff
release LiteSpeed SAPI 7.0.
authorGeorge Wang <gwang@php.net>
Tue, 27 Feb 2018 18:54:28 +0000 (13:54 -0500)
committerGeorge Wang <gwang@php.net>
Tue, 27 Feb 2018 18:54:28 +0000 (13:54 -0500)
add CRIU support.
add support for [PATH=] and [HOST=] sections in php.ini

sapi/litespeed/lsapi_main.c
sapi/litespeed/lsapilib.c
sapi/litespeed/lsapilib.h
sapi/litespeed/lscriu.c [new file with mode: 0644]
sapi/litespeed/lscriu.h [new file with mode: 0644]

index b111054bc02ef3a6c307712c4ba4a7ea86649bd2..a2f7ed8f00e9093d88f26ddaedaf07936878d196 100644 (file)
@@ -67,6 +67,9 @@
 #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
 
@@ -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;
-       }
-
-       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
@@ -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;
     }
 }
index 9f589014328a9ec2da89e23a9bc64832d6752a9f..185435a42d499800b35abed86e0ac69b44aea053 100644 (file)
@@ -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 <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>
@@ -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;
+
+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] =
 {
@@ -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 <sys/sendfile.h>
 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 <sys/sendfile.h>
 #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;
 
+static fn_select_t g_fnSelect = select;
 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 )
-        {
-            /*
-            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 )
         {
@@ -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 <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__)
@@ -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);
+}
+
+
index 99145f0cb1e35e34e9b386729fc7f87dc27963fe..d44d6c57d4916b4682c4904ed8edc5446edbac18 100644 (file)
@@ -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 <stddef.h>
-#include <lsapidef.h>
+#include "lsapidef.h"
 
+#include <stddef.h>
 #include <sys/time.h>
 #include <sys/types.h>
 
@@ -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 (file)
index 0000000..04038bb
--- /dev/null
@@ -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 <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;
+}
+
diff --git a/sapi/litespeed/lscriu.h b/sapi/litespeed/lscriu.h
new file mode 100644 (file)
index 0000000..9f40df3
--- /dev/null
@@ -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 <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