changeset 218:05592fd7a436

nginx-0.0.1-2004-01-05-23:55:48 import
author Igor Sysoev <igor@sysoev.ru>
date Mon, 05 Jan 2004 20:55:48 +0000
parents c5d1cdcb04ec
children f57597ec5249
files auto/sources auto/unix src/core/nginx.c src/core/nginx.h src/core/ngx_conf_file.c src/core/ngx_conf_file.h src/core/ngx_config.h src/core/ngx_connection.c src/core/ngx_connection.h src/core/ngx_core.h src/core/ngx_cycle.c src/core/ngx_cycle.h src/core/ngx_log.c src/core/ngx_log.h src/core/ngx_times.c src/core/ngx_times.h src/event/modules/ngx_kqueue_module.c src/event/modules/ngx_poll_module.c src/event/modules/ngx_select_module.c src/event/ngx_event.c src/event/ngx_event.h src/event/ngx_event_timer.c src/event/ngx_event_timer.h src/http/modules/ngx_http_rewrite_handler.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_log_handler.h src/http/ngx_http_request.c src/os/unix/ngx_errno.h src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_linux_sendfile_chain.c src/os/unix/ngx_os.h src/os/unix/ngx_posix_init.c src/os/unix/ngx_process.c src/os/unix/ngx_process.h src/os/unix/ngx_recv.c src/os/win32/ngx_os.h
diffstat 38 files changed, 1946 insertions(+), 507 deletions(-) [+]
line wrap: on
line diff
--- a/auto/sources
+++ b/auto/sources
@@ -19,6 +19,7 @@ CORE_DEPS="src/core/nginx.h \
             src/core/ngx_rbtree.h \
             src/core/ngx_times.h \
             src/core/ngx_connection.h \
+            src/core/ngx_cycle.h \
             src/core/ngx_conf_file.h \
             src/core/ngx_garbage_collector.h"
 
@@ -34,6 +35,8 @@ CORE_SRCS="src/core/nginx.c \
             src/core/ngx_file.c \
             src/core/ngx_rbtree.c \
             src/core/ngx_times.c \
+            src/core/ngx_connection.c \
+            src/core/ngx_cycle.c \
             src/core/ngx_conf_file.c \
             src/core/ngx_garbage_collector.c"
 
--- a/auto/unix
+++ b/auto/unix
@@ -83,10 +83,10 @@ n = pwrite(1, buf, 1, 0)"
 . auto/func
 
 
-ngx_func="strsignal()"
-ngx_func_inc="#include <string.h>"
-ngx_func_test="char *s = strsignal(1)"
-. auto/func
+#ngx_func="strsignal()"
+#ngx_func_inc="#include <string.h>"
+#ngx_func_test="char *s = strsignal(1)"
+#. auto/func
 
 
 ngx_func="strerror_r()"
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -5,18 +5,15 @@
 #include <nginx.h>
 
 
-/* STUB */
-void stub_init(ngx_cycle_t *cycle);
-
-
-
-static ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle, ngx_log_t *log);
-static int ngx_open_listening_sockets(ngx_cycle_t *cycle, ngx_log_t *log);
-static void ngx_clean_old_cycles(ngx_event_t *ev);
+static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);
+static void ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
+static ngx_int_t ngx_core_module_init(ngx_cycle_t *cycle);
 
 
 typedef struct {
+     ngx_str_t  user;
      int        daemon;
+     int        single;
      ngx_str_t  pid;
 } ngx_core_conf_t;
 
@@ -25,6 +22,13 @@ static ngx_str_t  core_name = ngx_string
 
 static ngx_command_t  ngx_core_commands[] = {
 
+    { ngx_string("user"),
+      NGX_MAIN_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_core_str_slot,
+      0,
+      offsetof(ngx_core_conf_t, user),
+      NULL },
+
     { ngx_string("daemon"),
       NGX_MAIN_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_core_flag_slot,
@@ -32,6 +36,13 @@ static ngx_command_t  ngx_core_commands[
       offsetof(ngx_core_conf_t, daemon),
       NULL },
 
+    { ngx_string("single_process"),
+      NGX_MAIN_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_core_flag_slot,
+      0,
+      offsetof(ngx_core_conf_t, single),
+      NULL },
+
       ngx_null_command
 };
 
@@ -41,44 +52,46 @@ ngx_module_t  ngx_core_module = {
     &core_name,                            /* module context */
     ngx_core_commands,                     /* module directives */
     NGX_CORE_MODULE,                       /* module type */
-    NULL,                                  /* init module */
+    ngx_core_module_init,                  /* init module */
     NULL                                   /* init child */
 };
 
 
-int                    ngx_max_module;
-ngx_os_io_t            ngx_io;
-
-volatile ngx_cycle_t  *ngx_cycle;
-ngx_array_t            ngx_old_cycles;
-
-static ngx_pool_t     *ngx_temp_pool;
-static ngx_event_t     ngx_cleaner_event;
+ngx_int_t              ngx_max_module;
 
 
-/* STUB NAME */
-static ngx_connection_t  dumb;
+/* STUB */
+uid_t      user;
 
 u_int ngx_connection_counter;
 
+ngx_int_t  ngx_master;
+ngx_int_t  ngx_single;
 
-int done;
-int restart;
-int rotate;
+
+ngx_int_t  ngx_respawn;
+ngx_int_t  ngx_terminate;
+ngx_int_t  ngx_quit;
+ngx_int_t  ngx_reconfigure;
+ngx_int_t  ngx_reopen;
+ngx_int_t  ngx_change_binary;
 
 
-int main(int argc, char *const *argv)
+int main(int argc, char *const *argv, char **envp)
 {
-    int               i;
-    ngx_fd_t          fd;
-    ngx_log_t        *log;
-    ngx_cycle_t      *cycle, init_cycle;
-    ngx_open_file_t  *file;
+    struct timeval     tv;
+    ngx_fd_t           fd;
+    ngx_int_t          i;
+    ngx_err_t          err;
+    ngx_log_t         *log;
+    ngx_cycle_t       *cycle, init_cycle;
+    ngx_open_file_t   *file;
+    ngx_core_conf_t   *ccf;
 #if !(WIN32)
-    size_t            len;
-    char              pid[/* STUB */ 10];
-    ngx_file_t        pidfile;
-    ngx_core_conf_t  *ccf;
+    size_t             len;
+    char               pid[/* STUB */ 10];
+    ngx_file_t         pidfile;
+    struct passwd     *pwd;
 #endif
 
 #if __FreeBSD__
@@ -94,7 +107,6 @@ int main(int argc, char *const *argv)
 
     log = ngx_log_init_errlog();
 
-
     /* init_cycle->log is required for signal handlers */
 
     ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
@@ -110,16 +122,46 @@ int main(int argc, char *const *argv)
         ngx_modules[i]->index = ngx_max_module++;
     }
 
-    cycle = ngx_init_cycle(NULL, log);
+    if (!(init_cycle.pool = ngx_create_pool(1024, log))) {
+        return 1;
+    }
+
+    if (ngx_set_inherited_sockets(&init_cycle, envp) == NGX_ERROR) {
+        return 1;
+    }
+
+    cycle = ngx_init_cycle(&init_cycle);
     if (cycle == NULL) {
         return 1;
     }
 
     ngx_cycle = cycle;
 
+    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+    if (ccf->single == 1) {
+        ngx_master = 0;
+        ngx_single = 1;
+
+    } else {
+        ngx_master = 1;
+        ngx_single = 0;
+    }
+
 #if !(WIN32)
 
-    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+    /* STUB */
+    if (ccf->user.len) {
+        pwd = getpwnam(ccf->user.data);
+        if (pwd == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                          "getpwnam(%s) failed", ccf->user);
+            return 1;
+        }
+
+        user = pwd->pw_uid;
+    }
+    /* */
 
     if (ccf->daemon != 0) {
         if (ngx_daemon(cycle->log) == NGX_ERROR) {
@@ -162,48 +204,64 @@ int main(int argc, char *const *argv)
 
 #endif
 
-    /* life cycle */
+    /* a life cycle */
 
     for ( ;; ) {
-#if 0
-        /* STUB */ cycle->log->log_level = NGX_LOG_DEBUG|NGX_LOG_DEBUG_HTTP;
-#endif
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "new cycle");
 
-#if 0
-
-#if !(WIN32)
-        ngx_spawn_process(cycle->log);
-#endif
+        if (ngx_master) {
+            ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
+                              "worker process", NGX_PROCESS_RESPAWN);
 
-        stub_init(cycle);
-#endif
-
-        /* TODO: forks */
+        } else {
+            ngx_init_temp_number();
 
-        ngx_init_temp_number();
-
-        for (i = 0; ngx_modules[i]; i++) {
-            if (ngx_modules[i]->init_child) {
-                if (ngx_modules[i]->init_child(cycle) == NGX_ERROR) {
-                    /* fatal */
-                    exit(1);
+            for (i = 0; ngx_modules[i]; i++) {
+                if (ngx_modules[i]->init_process) {
+                    if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
+                        /* fatal */
+                        exit(1);
+                    }
                 }
             }
         }
 
-        /* TODO: threads */
+#if 0
+        reconfigure = 0;
+        reopen = 0;
+#endif
 
-        restart = 0;
-        rotate = 0;
+        /* a cycle with the same configuration */
 
         for ( ;; ) {
 
+            /* an event loop */
+
             for ( ;; ) {
-                ngx_log_debug(cycle->log, "worker cycle");
+
+                err = 0;
+
+                if (ngx_single) {
+                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                                   "worker cycle");
+
+                    ngx_process_events(cycle->log);
 
-                ngx_process_events(cycle->log);
+                } else {
+                    ngx_set_errno(0);
+                    ngx_msleep(1000);
+                    err = ngx_errno;
+
+                    ngx_gettimeofday(&tv);
+                    ngx_time_update(tv.tv_sec);
 
-                if (done) {
+                    if (err) {
+                        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, err,
+                                       "sleep() exited");
+                    }
+                }
+
+                if (ngx_quit || ngx_terminate) {
 #if !(WIN32)
                     if (ngx_delete_file(pidfile.name.data) == NGX_FILE_ERROR) {
                         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
@@ -212,90 +270,70 @@ int main(int argc, char *const *argv)
                     }
 #endif
 
-                    ngx_log_error(NGX_LOG_INFO,
-                                  cycle->log, 0, "exiting");
+                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exiting");
+
+                    if (ngx_master) {
+                        ngx_signal_processes(cycle,
+                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
+
+                        /* TODO: wait workers */
+
+                        ngx_msleep(1000);
+
+                        ngx_gettimeofday(&tv);
+                        ngx_time_update(tv.tv_sec);
+                    }
+
+                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exit");
                     exit(0);
                 }
 
-                if (rotate) {
-                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reopen logs");
-
-                    file = cycle->open_files.elts;
-                    for (i = 0; i < cycle->open_files.nelts; i++) {
-                        if (file[i].name.data == NULL) {
-                            continue;
-                        }
-
-                        fd = ngx_open_file(file[i].name.data,
-                                      NGX_FILE_RDWR,
-                                      NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND);
-
-ngx_log_debug(log, "REOPEN: %d:%d:%s" _ fd _ file[i].fd _ file[i].name.data);
-
-                        if (fd == NGX_INVALID_FILE) {
-                            ngx_log_error(NGX_LOG_EMERG,
-                                          cycle->log, ngx_errno,
-                                          ngx_open_file_n " \"%s\" failed",
-                                          file[i].name.data);
-                            continue;
-                        }
-
-#if (WIN32)
-                        if (ngx_file_append_mode(fd) == NGX_ERROR) {
-                            ngx_log_error(NGX_LOG_EMERG,
-                                          cycle->log, ngx_errno,
-                                          ngx_file_append_mode_n
-                                          " \"%s\" failed",
-                                          file[i].name.data);
-
-                            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
-                                ngx_log_error(NGX_LOG_EMERG,
-                                              cycle->log, ngx_errno,
-                                              ngx_close_file_n " \"%s\" failed",
-                                              file[i].name.data);
-                            }
-
-                            continue;
-                        }
-#endif
-
-                        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
-                            ngx_log_error(NGX_LOG_EMERG,
-                                          cycle->log, ngx_errno,
-                                          ngx_close_file_n " \"%s\" failed",
-                                          file[i].name.data);
-                        }
-
-                        file[i].fd = fd;
-                    }
-
-                    rotate = 0;
+                if (err == NGX_EINTR) {
+                    ngx_respawn_processes(cycle);
                 }
 
-                if (restart) {
-                    ngx_log_debug(cycle->log, "restart");
+                if (ngx_change_binary) {
+                    ngx_change_binary = 0;
+                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                                  "changing binary");
+                    ngx_exec_new_binary(cycle, argv);
+                    /* TODO: quit workers */
+                }
+
+                if (ngx_reconfigure) {
+                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reconfiguring");
                     break;
                 }
 
+                if (ngx_reopen) {
+                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                                  "reopening logs");
+                    ngx_reopen_files(cycle);
+                    ngx_reopen = 0;
+                }
+
             }
 
-            cycle = ngx_init_cycle(cycle, cycle->log);
+            cycle = ngx_init_cycle(cycle);
             if (cycle == NULL) {
                 cycle = (ngx_cycle_t *) ngx_cycle;
                 continue;
             }
 
             ngx_cycle = cycle;
+            ngx_reconfigure = 0;
             break;
         }
     }
 }
 
+#if 0
 
-static ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle, ngx_log_t *log)
+static ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle)
 {
-    int               i, n, failed;
+    ngx_int_t         i, n, failed;
     ngx_str_t         conf_file;
+    ngx_log_t        *log;
     ngx_conf_t        conf;
     ngx_pool_t       *pool;
     ngx_cycle_t      *cycle, **old;
@@ -304,6 +342,7 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
     ngx_open_file_t  *file;
     ngx_listening_t  *ls, *nls;
 
+    log = old_cycle->log;
 
     if (!(pool = ngx_create_pool(16 * 1024, log))) {
         return NULL;
@@ -318,7 +357,7 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
     cycle->old_cycle = old_cycle;
 
 
-    n = old_cycle ? old_cycle->pathes.nelts : 10;
+    n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;
     if (!(cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *)))) {
         ngx_destroy_pool(pool);
         return NULL;
@@ -329,7 +368,7 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
     cycle->pathes.pool = pool;
 
 
-    n = old_cycle ? old_cycle->open_files.nelts : 20;
+    n = old_cycle->open_files.nelts ? old_cycle->open_files.nelts : 20;
     cycle->open_files.elts = ngx_pcalloc(pool, n * sizeof(ngx_open_file_t));
     if (cycle->open_files.elts == NULL) {
         ngx_destroy_pool(pool);
@@ -347,7 +386,7 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
     }
 
 
-    n = old_cycle ? old_cycle->listening.nelts : 10;
+    n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
     cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));
     if (cycle->listening.elts == NULL) {
         ngx_destroy_pool(pool);
@@ -375,6 +414,7 @@ static ngx_cycle_t *ngx_init_cycle(ngx_c
      * ccf->pid = NULL;
      */
     ccf->daemon = -1;
+    ccf->single = -1;
     ((void **)(cycle->conf_ctx))[ngx_core_module.index] = ccf;
 
 
@@ -435,12 +475,8 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
 #endif
     }
 
-#if 0
-    /* STUB */ cycle->log->log_level = NGX_LOG_DEBUG;
-#endif
-
     if (!failed) {
-        if (old_cycle) {
+        if (old_cycle->listening.nelts) {
             ls = old_cycle->listening.elts;
             for (i = 0; i < old_cycle->listening.nelts; i++) {
                 ls[i].remain = 0;
@@ -449,6 +485,15 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
             nls = cycle->listening.elts;
             for (n = 0; n < cycle->listening.nelts; n++) {
                 for (i = 0; i < old_cycle->listening.nelts; i++) {
+                    if (ls[i].ignore) {
+                        continue;
+                    }
+
+                    ngx_log_error(NGX_LOG_INFO, log, 0,
+                                   "%X, %X",
+                                   *(int *) ls[i].sockaddr,
+                                   *(int *) nls[n].sockaddr);
+
                     if (ngx_memcmp(nls[n].sockaddr,
                                    ls[i].sockaddr, ls[i].socklen) == 0)
                     {
@@ -492,7 +537,7 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
         }
 
         if (!failed) {
-            if (ngx_open_listening_sockets(cycle, log) == NGX_ERROR) {
+            if (ngx_open_listening_sockets(cycle) == NGX_ERROR) {
                 failed = 1;
             }
         }
@@ -536,9 +581,6 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
 
     pool->log = cycle->log;
 
-#if 1
-    /* STUB */ cycle->one_process = 1;
-#endif
 
     for (i = 0; ngx_modules[i]; i++) {
         if (ngx_modules[i]->init_module) {
@@ -549,9 +591,9 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
         }
     }
 
-    if (old_cycle == NULL) {
-        return cycle;
-    }
+    /* close and delete stuff that lefts from an old cycle */
+
+    /* close the unneeded listening sockets */
 
     ls = old_cycle->listening.elts;
     for (i = 0; i < old_cycle->listening.nelts; i++) {
@@ -566,6 +608,9 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
         }
     }
 
+
+    /* close the unneeded open files */
+
     file = old_cycle->open_files.elts;
     for (i = 0; i < old_cycle->open_files.nelts; i++) {
         if (file[i].fd == NGX_INVALID_FILE) {
@@ -579,8 +624,13 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
         }
     }
 
+    if (old_cycle->connections == NULL) {
+        /* an old cycle is an init cycle */
+        ngx_destroy_pool(old_cycle->pool);
+        return cycle;
+    }
 
-    if (!old_cycle->one_process) {
+    if (master) {
         ngx_destroy_pool(old_cycle->pool);
         return cycle;
     }
@@ -626,190 +676,233 @@ ngx_log_debug(log, "OPEN: %d:%s" _ file[
     return cycle;
 }
 
-
-static int ngx_open_listening_sockets(ngx_cycle_t *cycle, ngx_log_t *log)
-{
-    int              times, failed, reuseaddr, i;
-    ngx_err_t        err;
-    ngx_socket_t     s;
-    ngx_listening_t *ls;
-
-    reuseaddr = 1;
-#if (NGX_SUPPRESS_WARN)
-    failed = 0;
 #endif
 
-    /* TODO: times configurable */
 
-    for (times = 10; times; times--) {
-        failed = 0;
+#if 0
 
-        /* for each listening socket */
+static ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle, char **envp)
+{
+    char                *p, *v;
+    ngx_socket_t         s;
+    ngx_listening_t     *ls;
+    struct sockaddr_in  *addr_in;
 
-        ls = cycle->listening.elts;
-        for (i = 0; i < cycle->listening.nelts; i++) {
+    for ( /* void */ ; *envp; envp++) {
+        if (ngx_strncmp(*envp, NGINX_VAR, NGINX_VAR_LEN) != 0) {
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                      "using inherited sockets from \"%s\"", *envp);
 
-            if (ls[i].fd != -1) {
-                continue;
-            }
-
-            if (ls[i].inherited) {
+        ngx_init_array(cycle->listening, cycle->pool,
+                       10, sizeof(ngx_listening_t), NGX_ERROR);
 
-                /* TODO: close on exit */
-                /* TODO: nonblocking */
-                /* TODO: deferred accept */
+        for (p = *envp + NGINX_VAR_LEN, v = p; *p; p++) {
+            if (*p == ':' || *p == ';') {
+                s = ngx_atoi(v, p - v);
+                if (s == NGX_ERROR) {
+                    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                                  "invalid socket number \"%s\" "
+                                  "in NGINX enviroment variable, "
+                                  "ignoring the rest of the variable", v);
+                    break;
+                }
+                v = p + 1;
 
-                continue;
-            }
+                if (!(ls = ngx_push_array(&cycle->listening))) {
+                    return NGX_ERROR;
+                }
 
-            s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol,
-                           ls[i].flags);
+                ls->fd = s;
 
-            if (s == -1) {
-                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                              ngx_socket_n " %s failed", ls[i].addr_text.data);
-                return NGX_ERROR;
-            }
+                /* AF_INET only */
+
+                ls->sockaddr = ngx_palloc(cycle->pool,
+                                          sizeof(struct sockaddr_in));
+                if (ls->sockaddr == NULL) {
+                    return NGX_ERROR;
+                }
 
-#if (WIN32)
-            /*
-             * Winsock assignes a socket number divisible by 4
-             * so to find a connection we divide a socket number by 4.
-             */
+                ls->socklen = sizeof(struct sockaddr_in);
+                if (getsockname(s, ls->sockaddr, &ls->socklen) == -1) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "getsockname() of the inherited "
+                                  "socket #%d failed", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+
+                addr_in = (struct sockaddr_in *) ls->sockaddr;
 
-            if (s % 4) {
-                ngx_log_error(NGX_LOG_EMERG, ls->log, 0,
-                              ngx_socket_n " created socket %d", s);
-                return NGX_ERROR;
-            }
-#endif
+                if (addr_in->sin_family != AF_INET) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "the inherited socket #%d has "
+                                  "unsupported family", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+                ls->addr_text_max_len = INET_ADDRSTRLEN;
 
-            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
-                           (const void *) &reuseaddr, sizeof(int)) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                              "setsockopt(SO_REUSEADDR) %s failed",
-                              ls[i].addr_text.data);
-                return NGX_ERROR;
-            }
+                ls->addr_text.data = ngx_palloc(cycle->pool,
+                                                ls->addr_text_max_len);
+                if (ls->addr_text.data == NULL) {
+                    return NGX_ERROR;
+                }
 
-            /* TODO: close on exit */
+                addr_in->sin_len = 0;
 
-            if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
-                if (ngx_nonblocking(s) == -1) {
-                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                                  ngx_nonblocking_n " %s failed",
-                                  ls[i].addr_text.data);
+                ls->family = addr_in->sin_family;
+                ls->addr_text.len = ngx_sock_ntop(ls->family, ls->sockaddr,
+                                                  ls->addr_text.data,
+                                                  ls->addr_text_max_len);
+                if (ls->addr_text.len == 0) {
                     return NGX_ERROR;
                 }
             }
-
-#if 0
-            if (ls[i].nonblocking) {
-                if (ngx_nonblocking(s) == -1) {
-                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                                  ngx_nonblocking_n " %s failed",
-                                  ls[i].addr_text.data);
-                    return NGX_ERROR;
-                }
-            }
-#endif
-
-            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
-                err = ngx_socket_errno;
-                ngx_log_error(NGX_LOG_EMERG, log, err,
-                              "bind() to %s failed", ls[i].addr_text.data);
-
-                if (err != NGX_EADDRINUSE)
-                    return NGX_ERROR;
-
-                if (ngx_close_socket(s) == -1)
-                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                                  ngx_close_socket_n " %s failed",
-                                  ls[i].addr_text.data);
-
-                failed = 1;
-                continue;
-            }
-
-            if (listen(s, ls[i].backlog) == -1) {
-                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
-                              "listen() to %s failed", ls[i].addr_text.data);
-                return NGX_ERROR;
-            }
-
-            /* TODO: deferred accept */
-
-            ls[i].fd = s;
         }
 
-        if (!failed)
-            break;
-
-        /* TODO: delay configurable */
-
-        ngx_log_error(NGX_LOG_NOTICE, log, 0,
-                      "try again to bind() after 500ms");
-        ngx_msleep(500);
-    }
-
-    if (failed) {
-        ngx_log_error(NGX_LOG_EMERG, log, 0, "still can not bind()");
-        return NGX_ERROR;
+        break;
     }
 
     return NGX_OK;
 }
 
+#endif
 
-static void ngx_clean_old_cycles(ngx_event_t *ev)
+
+static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
 {
-    int            i, n, found, live;
-    ngx_log_t     *log;
-    ngx_cycle_t  **cycle;
+    ngx_int_t         i;
+    ngx_listening_t  *ls;
 
-    log = ngx_cycle->log;
-    ngx_temp_pool->log = log;
+    if (user) {
+        if (setuid(user) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "setuid() failed");
+            /* fatal */
+            exit(1);
+        }
+    }
+
+    ngx_init_temp_number();
 
-    ngx_log_debug(log, "clean old cycles");
-
-    live = 0;
+    /*
+     * disable deleting previous events for the listening sockets because
+     * in the worker processes there are no events at all at this point
+     */ 
+    ls = cycle->listening.elts;
+    for (i = 0; i < cycle->listening.nelts; i++) {
+        ls[i].remain = 0;
+    }
 
-    cycle = ngx_old_cycles.elts;
-    for (i = 0; i < ngx_old_cycles.nelts; i++) {
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->init_process) {
+            if (ngx_modules[i]->init_process(cycle) == NGX_ERROR) {
+                /* fatal */
+                exit(1);
+            }
+        }
+    }
+
+    /* TODO: threads: start ngx_worker_thread_cycle() */
 
-        if (cycle[i] == NULL) {
-            continue;
+    for ( ;; ) {
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
+
+        ngx_process_events(cycle->log);
+
+        if (ngx_terminate) {
+            ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exiting");
+            exit(0);
+        }
+
+        if (ngx_quit) {
+            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                          "gracefully shutdowning");
+            break;
         }
 
-        found = 0;
+        if (ngx_reopen) {
+            ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "reopen logs");
+            ngx_reopen_files(cycle);
+            ngx_reopen = 0;
+        }
+    }
 
-        for (n = 0; n < cycle[i]->connection_n; n++) {
-            if (cycle[i]->connections[n].fd != -1) {
-                found = 1;
-                ngx_log_debug(log, "live fd: %d" _ n);
-                break;
-            }
-        }
+    ngx_close_listening_sockets(cycle);
 
-        if (found) {
-            live = 1;
-            continue;
+    for ( ;; ) {
+        if (ngx_event_timer_rbtree == &ngx_event_timer_sentinel) {
+            ngx_log_error(NGX_LOG_INFO, cycle->log, 0, "exiting");
+            exit(0);
         }
 
-        ngx_log_debug(log, "clean old cycle: %d" _ i);
-        ngx_destroy_pool(cycle[i]->pool);
-        cycle[i] = NULL;
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
+
+        ngx_process_events(cycle->log);
+    }
+}
+
+
+static void ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
+{
+    char             *env[2], *var, *p;
+    ngx_int_t         i;
+    ngx_exec_ctx_t    ctx;
+    ngx_listening_t  *ls;
+
+    ctx.path = argv[0];
+    ctx.name = "new binary process";
+    ctx.argv = argv;
+
+    var = ngx_alloc(NGINX_VAR_LEN
+                            + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 1,
+                    cycle->log);
+
+    p = ngx_cpymem(var, NGINX_VAR, NGINX_VAR_LEN);
+
+    ls = cycle->listening.elts;
+    for (i = 0; i < cycle->listening.nelts; i++) {
+        p += ngx_snprintf(p, NGX_INT32_LEN + 2, "%u;", ls[i].fd);
     }
 
-    ngx_log_debug(log, "old cycles status: %d" _ live);
+    env[0] = var;
+    env[1] = NULL;
+    ctx.envp = (char *const *) &env;
+
+    ngx_exec(cycle, &ctx);
 
-    if (live) {
-        ngx_log_debug(log, "TIMER");
-        ngx_add_timer(ev, 30000);
+    ngx_free(var);
+}
+
+
+static ngx_int_t ngx_core_module_init(ngx_cycle_t *cycle)
+{
+    ngx_core_conf_t  *ccf;
 
-    } else {
-        ngx_destroy_pool(ngx_temp_pool);
-        ngx_temp_pool = NULL;
-        ngx_old_cycles.nelts = 0;
+    /*
+     * ngx_core_module has a special init procedure: it is called by
+     * ngx_init_cycle() before the configuration file parsing to create
+     * ngx_core_module configuration and to set its default parameters
+     */
+
+    if (((void **)(cycle->conf_ctx))[ngx_core_module.index] != NULL) {
+        return NGX_OK;
     }
+
+    if (!(ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t)))) {
+        return NGX_ERROR;
+    }
+    /* set by pcalloc()
+     *
+     * ccf->pid = NULL;
+     */
+    ccf->daemon = -1;
+    ccf->single = -1;
+
+    ((void **)(cycle->conf_ctx))[ngx_core_module.index] = ccf;
+
+    return NGX_OK;
 }
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -2,16 +2,19 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define  NGINX_VER   "nginx/0.0.1"
-#define  NGINX_CONF  "nginx.conf"
-#define  NGINX_PID   "nginx.pid"
+#define NGINX_VER      "nginx/0.0.1"
+#define NGINX_CONF     "nginx.conf"
+#define NGINX_PID      "nginx.pid"
 
+#define NGINX_VAR      "NGINX="
+#define NGINX_VAR_LEN  (sizeof(NGINX_VAR) - 1)
 
-extern int           ngx_max_module;
-extern u_int         ngx_connection_counter;
+extern ngx_module_t    ngx_core_module;
 
-extern ngx_module_t  ngx_core_module;
+extern ngx_uint_t      ngx_connection_counter;
 
+extern ngx_int_t       ngx_master;
+extern ngx_int_t       ngx_single;
 
 
 #endif /* _NGINX_H_INCLUDED_ */
--- a/src/core/ngx_conf_file.c
+++ b/src/core/ngx_conf_file.c
@@ -571,6 +571,13 @@ char *ngx_conf_set_core_flag_slot(ngx_co
 }
 
 
+char *ngx_conf_set_core_str_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+                                 void *conf)
+{
+    return ngx_conf_set_str_slot(cf, cmd, *(void **)conf);
+}
+
+
 char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     char  *p = conf;
--- a/src/core/ngx_conf_file.h
+++ b/src/core/ngx_conf_file.h
@@ -85,26 +85,6 @@ struct ngx_open_file_s {
 };
 
 
-struct ngx_cycle_s {
-    void           ****conf_ctx;
-    ngx_pool_t        *pool;
-    ngx_log_t         *log;
-    ngx_array_t        listening;
-    ngx_array_t        open_files;
-    ngx_array_t        pathes;
-
-    int                connection_n;
-    ngx_connection_t  *connections;
-    ngx_event_t       *read_events;
-    ngx_event_t       *write_events;
-
-    ngx_cycle_t       *cycle;
-    ngx_cycle_t       *old_cycle;
-
-    unsigned           one_process:1;
-};
-
-
 struct ngx_module_s {
     int             ctx_index;
     int             index;
@@ -112,7 +92,10 @@ struct ngx_module_s {
     ngx_command_t  *commands;
     int             type;
     int           (*init_module)(ngx_cycle_t *cycle);
-    int           (*init_child)(ngx_cycle_t *cycle);
+    int           (*init_process)(ngx_cycle_t *cycle);
+#if 0
+    int           (*init_thread)(ngx_cycle_t *cycle);
+#endif
 };
 
 
@@ -271,11 +254,12 @@ char *ngx_conf_set_bitmask_slot(ngx_conf
 
 char *ngx_conf_set_core_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd,
                                   void *conf);
+char *ngx_conf_set_core_str_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+                                 void *conf);
 
 
-extern ngx_module_t          *ngx_modules[];
-extern volatile ngx_cycle_t  *ngx_cycle;
-extern ngx_array_t            ngx_old_cycles;
+extern ngx_int_t      ngx_max_module;
+extern ngx_module_t  *ngx_modules[];
 
 
 #endif /* _NGX_HTTP_CONF_FILE_H_INCLUDED_ */
--- a/src/core/ngx_config.h
+++ b/src/core/ngx_config.h
@@ -59,14 +59,16 @@ typedef u_int  ngx_uint_t;
 
 #if !(WIN32)
 
-#define ngx_signal_helper(n)  SIG##n
-#define ngx_signal_value(n)   ngx_signal_helper(n)
+#define ngx_signal_helper(n)     SIG##n
+#define ngx_signal_value(n)      ngx_signal_helper(n)
 
 /* TODO: #ifndef */
-#define NGX_RESTART_SIGNAL    HUP
-#define NGX_ROTATE_SIGNAL     USR1
-#define NGX_SHUTDOWN_SIGNAL   TERM
-#define NGX_INTERRUPT_SIGNAL  INT
+#define NGX_RECONFIGURE_SIGNAL   HUP
+#define NGX_REOPEN_SIGNAL        USR1
+#define NGX_SHUTDOWN_SIGNAL      QUIT
+#define NGX_TERMINATE_SIGNAL     TERM
+#define NGX_INTERRUPT_SIGNAL     INT
+#define NGX_CHANGEBIN_SIGNAL     USR2
 
 #endif
 
@@ -76,6 +78,13 @@ typedef u_int  ngx_uint_t;
 #define NGX_INVALID_ARRAY_INDEX 0x80000000
 
 
+/* TODO: auto */
+#define NGX_INT32_LEN      sizeof("-2147483648") - 1
+#define NGX_TIME_T_LEN     sizeof("-2147483648") - 1
+#define NGX_OFF_T_LEN      sizeof("-9223372036854775808") - 1
+
+
+
 /* TODO: auto_conf */
 #define NGX_ALIGN       (sizeof(unsigned long) - 1)  /* platform word */
 #define NGX_ALIGN_CAST  (unsigned long)              /* size of the pointer */
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_connection.c
@@ -0,0 +1,275 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+/* STUB */
+#include <nginx.h>
+
+
+ngx_os_io_t            ngx_io;
+
+
+ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle, char **envp)
+{
+    char                *p, *v;
+    ngx_socket_t         s;
+    ngx_listening_t     *ls;
+    struct sockaddr_in  *addr_in;
+
+    for ( /* void */ ; *envp; envp++) {
+        if (ngx_strncmp(*envp, NGINX_VAR, NGINX_VAR_LEN) != 0) {
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                      "using inherited sockets from \"%s\"", *envp);
+
+        ngx_init_array(cycle->listening, cycle->pool,
+                       10, sizeof(ngx_listening_t), NGX_ERROR);
+
+        for (p = *envp + NGINX_VAR_LEN, v = p; *p; p++) {
+            if (*p == ':' || *p == ';') {
+                s = ngx_atoi(v, p - v);
+                if (s == NGX_ERROR) {
+                    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                                  "invalid socket number \"%s\" "
+                                  "in NGINX enviroment variable, "
+                                  "ignoring the rest of the variable", v);
+                    break;
+                }
+                v = p + 1;
+
+                if (!(ls = ngx_push_array(&cycle->listening))) {
+                    return NGX_ERROR;
+                }
+
+                ls->fd = s;
+
+                /* AF_INET only */
+
+                ls->sockaddr = ngx_palloc(cycle->pool,
+                                          sizeof(struct sockaddr_in));
+                if (ls->sockaddr == NULL) {
+                    return NGX_ERROR;
+                }
+
+                ls->socklen = sizeof(struct sockaddr_in);
+                if (getsockname(s, ls->sockaddr, &ls->socklen) == -1) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "getsockname() of the inherited "
+                                  "socket #%d failed", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+
+                addr_in = (struct sockaddr_in *) ls->sockaddr;
+
+                if (addr_in->sin_family != AF_INET) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "the inherited socket #%d has "
+                                  "unsupported family", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+                ls->addr_text_max_len = INET_ADDRSTRLEN;
+
+                ls->addr_text.data = ngx_palloc(cycle->pool,
+                                                ls->addr_text_max_len);
+                if (ls->addr_text.data == NULL) {
+                    return NGX_ERROR;
+                }
+
+                addr_in->sin_len = 0;
+
+                ls->family = addr_in->sin_family;
+                ls->addr_text.len = ngx_sock_ntop(ls->family, ls->sockaddr,
+                                                  ls->addr_text.data,
+                                                  ls->addr_text_max_len);
+                if (ls->addr_text.len == 0) {
+                    return NGX_ERROR;
+                }
+            }
+        }
+
+        break;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle)
+{
+    int              tries, failed, reuseaddr, i;
+    ngx_err_t        err;
+    ngx_log_t       *log;
+    ngx_socket_t     s;
+    ngx_listening_t *ls;
+
+    reuseaddr = 1;
+#if (NGX_SUPPRESS_WARN)
+    failed = 0;
+#endif
+
+    log = cycle->log;
+
+    /* TODO: tries configurable */
+
+    for (tries = 10; tries; tries--) {
+        failed = 0;
+
+        /* for each listening socket */
+
+        ls = cycle->listening.elts;
+        for (i = 0; i < cycle->listening.nelts; i++) {
+
+            if (ls[i].ignore) {
+                continue;
+            }
+
+            if (ls[i].fd != -1) {
+                continue;
+            }
+
+            if (ls[i].inherited) {
+
+                /* TODO: close on exit */
+                /* TODO: nonblocking */
+                /* TODO: deferred accept */
+
+                continue;
+            }
+
+            s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol,
+                           ls[i].flags);
+
+            if (s == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              ngx_socket_n " %s failed", ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+#if (WIN32)
+            /*
+             * Winsock assignes a socket number divisible by 4
+             * so to find a connection we divide a socket number by 4.
+             */
+
+            if (s % 4) {
+                ngx_log_error(NGX_LOG_EMERG, ls->log, 0,
+                              ngx_socket_n " created socket %d", s);
+                return NGX_ERROR;
+            }
+#endif
+
+            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                           (const void *) &reuseaddr, sizeof(int)) == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              "setsockopt(SO_REUSEADDR) %s failed",
+                              ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+            /* TODO: close on exit */
+
+            if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
+                if (ngx_nonblocking(s) == -1) {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_nonblocking_n " %s failed",
+                                  ls[i].addr_text.data);
+                    return NGX_ERROR;
+                }
+            }
+
+#if 0
+            if (ls[i].nonblocking) {
+                if (ngx_nonblocking(s) == -1) {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_nonblocking_n " %s failed",
+                                  ls[i].addr_text.data);
+                    return NGX_ERROR;
+                }
+            }
+#endif
+
+            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
+                err = ngx_socket_errno;
+                ngx_log_error(NGX_LOG_EMERG, log, err,
+                              "bind() to %s failed", ls[i].addr_text.data);
+
+                if (err != NGX_EADDRINUSE)
+                    return NGX_ERROR;
+
+                if (ngx_close_socket(s) == -1)
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_close_socket_n " %s failed",
+                                  ls[i].addr_text.data);
+
+                failed = 1;
+                continue;
+            }
+
+            if (listen(s, ls[i].backlog) == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              "listen() to %s failed", ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+            /* TODO: deferred accept */
+
+            ls[i].fd = s;
+        }
+
+        if (!failed)
+            break;
+
+        /* TODO: delay configurable */
+
+        ngx_log_error(NGX_LOG_NOTICE, log, 0,
+                      "try again to bind() after 500ms");
+        ngx_msleep(500);
+    }
+
+    if (failed) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0, "still can not bind()");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+void ngx_close_listening_sockets(ngx_cycle_t *cycle)
+{
+    ngx_int_t         i;
+    ngx_socket_t      fd;
+    ngx_listening_t  *ls;
+
+    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+        return;
+    }
+
+    ls = cycle->listening.elts;
+    for (i = 0; i < cycle->listening.nelts; i++) {
+        fd = ls[i].fd;
+
+#if (WIN32)
+        /*
+         * Winsock assignes a socket number divisible by 4
+         * so to find a connection we divide a socket number by 4.
+         */
+
+        fd /= 4;
+#endif
+
+        ngx_del_event(&cycle->read_events[fd], NGX_READ_EVENT, NGX_CLOSE_EVENT);
+
+        if (ngx_close_socket(ls[i].fd) == -1) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+                          ngx_close_socket_n " %s failed",
+                          ls[i].addr_text.data);
+        }
+
+        cycle->connections[fd].fd = -1;
+    }
+}
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -36,6 +36,7 @@ typedef struct {
 
     unsigned          new:1;
     unsigned          remain:1;
+    unsigned          ignore:1;
 
     unsigned          bound:1;       /* already bound */
     unsigned          inherited:1;   /* inherited from previous process */
@@ -85,13 +86,18 @@ struct ngx_connection_s {
 
     unsigned          pipeline:1;
     unsigned          unexpected_eof:1;
-    unsigned          tcp_nopush:1;
+    signed            tcp_nopush:2;
 #if (HAVE_IOCP)
     unsigned          accept_context_updated:1;
 #endif
 };
 
 
+ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle, char **envp);
+ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);
+void ngx_close_listening_sockets(ngx_cycle_t *cycle);
+
+
 extern ngx_os_io_t  ngx_io;
 
 
--- a/src/core/ngx_core.h
+++ b/src/core/ngx_core.h
@@ -37,6 +37,7 @@ typedef struct ngx_connection_s  ngx_con
 #include <ngx_rbtree.h>
 #include <ngx_times.h>
 #include <ngx_inet.h>
+#include <ngx_cycle.h>
 #include <ngx_conf_file.h>
 #include <ngx_os.h>
 #include <ngx_connection.h>
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_cycle.c
@@ -0,0 +1,743 @@
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+/* STUB */
+#include <nginx.h>
+
+
+static void ngx_clean_old_cycles(ngx_event_t *ev);
+
+
+volatile ngx_cycle_t  *ngx_cycle;
+ngx_array_t            ngx_old_cycles;
+
+static ngx_pool_t     *ngx_temp_pool;
+static ngx_event_t     ngx_cleaner_event;
+
+
+/* STUB NAME */
+static ngx_connection_t  dumb;
+/* STUB */
+
+
+ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle)
+{
+    ngx_int_t         i, n, failed;
+    ngx_str_t         conf_file;
+    ngx_log_t        *log;
+    ngx_conf_t        conf;
+    ngx_pool_t       *pool;
+    ngx_cycle_t      *cycle, **old;
+    ngx_socket_t      fd;
+    ngx_open_file_t  *file;
+    ngx_listening_t  *ls, *nls;
+
+    log = old_cycle->log;
+
+    if (!(pool = ngx_create_pool(16 * 1024, log))) {
+        return NULL;
+    }
+
+    if (!(cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t)))) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    cycle->pool = pool;
+
+    cycle->old_cycle = old_cycle;
+
+
+    n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;
+    if (!(cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *)))) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    cycle->pathes.nelts = 0;
+    cycle->pathes.size = sizeof(ngx_path_t *);
+    cycle->pathes.nalloc = n;
+    cycle->pathes.pool = pool;
+
+
+    n = old_cycle->open_files.nelts ? old_cycle->open_files.nelts : 20;
+    cycle->open_files.elts = ngx_pcalloc(pool, n * sizeof(ngx_open_file_t));
+    if (cycle->open_files.elts == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    cycle->open_files.nelts = 0;
+    cycle->open_files.size = sizeof(ngx_open_file_t);
+    cycle->open_files.nalloc = n;
+    cycle->open_files.pool = pool;
+
+
+    if (!(cycle->log = ngx_log_create_errlog(cycle, NULL))) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+
+    n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;
+    cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));
+    if (cycle->listening.elts == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+    cycle->listening.nelts = 0;
+    cycle->listening.size = sizeof(ngx_listening_t);
+    cycle->listening.nalloc = n;
+    cycle->listening.pool = pool;
+
+
+    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
+    if (cycle->conf_ctx == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+
+    if (ngx_core_module.init_module(cycle) == NGX_ERROR) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+
+    ngx_memzero(&conf, sizeof(ngx_conf_t));
+    /* STUB: init array ? */
+    conf.args = ngx_create_array(pool, 10, sizeof(ngx_str_t));
+    if (conf.args == NULL) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+    conf.ctx = cycle->conf_ctx;
+    conf.cycle = cycle;
+    /* STUB */ conf.pool = cycle->pool;
+    conf.log = log;
+    conf.module_type = NGX_CORE_MODULE;
+    conf.cmd_type = NGX_MAIN_CONF;
+
+    conf_file.len = sizeof(NGINX_CONF) - 1;
+    conf_file.data = NGINX_CONF;
+
+    if (ngx_conf_parse(&conf, &conf_file) != NGX_CONF_OK) {
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+
+    failed = 0;
+
+    file = cycle->open_files.elts;
+    for (i = 0; i < cycle->open_files.nelts; i++) {
+        if (file[i].name.data == NULL) {
+            continue;
+        }
+
+        file[i].fd = ngx_open_file(file[i].name.data,
+                                   NGX_FILE_RDWR,
+                                   NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND);
+
+ngx_log_debug(log, "OPEN: %d:%s" _ file[i].fd _ file[i].name.data);
+
+        if (file[i].fd == NGX_INVALID_FILE) {
+            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                          ngx_open_file_n " \"%s\" failed",
+                          file[i].name.data);
+            failed = 1;
+            break;
+        }
+
+#if (WIN32)
+        if (ngx_file_append_mode(file[i].fd) == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                          ngx_file_append_mode_n " \"%s\" failed",
+                          file[i].name.data);
+            failed = 1;
+            break;
+        }
+#endif
+    }
+
+    if (!failed) {
+        if (old_cycle->listening.nelts) {
+            ls = old_cycle->listening.elts;
+            for (i = 0; i < old_cycle->listening.nelts; i++) {
+                ls[i].remain = 0;
+            }
+
+            nls = cycle->listening.elts;
+            for (n = 0; n < cycle->listening.nelts; n++) {
+                for (i = 0; i < old_cycle->listening.nelts; i++) {
+                    if (ls[i].ignore) {
+                        continue;
+                    }
+
+                    ngx_log_error(NGX_LOG_INFO, log, 0,
+                                   "%X, %X",
+                                   *(int *) ls[i].sockaddr,
+                                   *(int *) nls[n].sockaddr);
+
+                    if (ngx_memcmp(nls[n].sockaddr,
+                                   ls[i].sockaddr, ls[i].socklen) == 0)
+                    {
+                        fd = ls[i].fd;
+#if (WIN32)
+                        /*
+                         * Winsock assignes a socket number divisible by 4 so
+                         * to find a connection we divide a socket number by 4.
+                         */
+
+                        fd /= 4;
+#endif
+                        if (fd >= (ngx_socket_t) cycle->connection_n) {
+                            ngx_log_error(NGX_LOG_EMERG, log, 0,
+                                        "%d connections is not enough to hold "
+                                        "an open listening socket on %s, "
+                                        "required at least %d connections",
+                                        cycle->connection_n,
+                                        ls[i].addr_text.data, fd);
+                            failed = 1;
+                            break;
+                        }
+
+                        nls[n].fd = ls[i].fd;
+                        nls[i].remain = 1;
+                        ls[i].remain = 1;
+                        break;
+                    }
+                }
+
+                if (nls[n].fd == -1) {
+                    nls[n].new = 1;
+                }
+            }
+
+        } else {
+            ls = cycle->listening.elts;
+            for (i = 0; i < cycle->listening.nelts; i++) {
+                ls[i].new = 1;
+            }
+        }
+
+        if (!failed) {
+            if (ngx_open_listening_sockets(cycle) == NGX_ERROR) {
+                failed = 1;
+            }
+        }
+    }
+
+    if (failed) {
+
+        /* rollback the new cycle configuration */
+
+        file = cycle->open_files.elts;
+        for (i = 0; i < cycle->open_files.nelts; i++) {
+            if (file[i].fd == NGX_INVALID_FILE) {
+                continue;
+            }
+
+            if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                              ngx_close_file_n " \"%s\" failed",
+                              file[i].name.data);
+            }
+        }
+
+        ls = cycle->listening.elts;
+        for (i = 0; i < cycle->listening.nelts; i++) {
+            if (ls[i].new && ls[i].fd == -1) {
+                continue;
+            }
+
+            if (ngx_close_socket(ls[i].fd) == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              ngx_close_socket_n " %s failed",
+                              ls[i].addr_text.data);
+            }
+        }
+
+        ngx_destroy_pool(pool);
+        return NULL;
+    }
+
+    /* commit the new cycle configuration */
+
+    pool->log = cycle->log;
+
+
+    for (i = 0; ngx_modules[i]; i++) {
+        if (ngx_modules[i]->init_module) {
+            if (ngx_modules[i]->init_module(cycle) == NGX_ERROR) {
+                /* fatal */
+                exit(1);
+            }
+        }
+    }
+
+    /* close and delete stuff that lefts from an old cycle */
+
+    /* close the unneeded listening sockets */
+
+    ls = old_cycle->listening.elts;
+    for (i = 0; i < old_cycle->listening.nelts; i++) {
+        if (ls[i].remain) {
+            continue;
+        }
+
+        if (ngx_close_socket(ls[i].fd) == -1) {
+            ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                          ngx_close_socket_n " %s failed",
+                          ls[i].addr_text.data);
+        }
+    }
+
+
+    /* close the unneeded open files */
+
+    file = old_cycle->open_files.elts;
+    for (i = 0; i < old_cycle->open_files.nelts; i++) {
+        if (file[i].fd == NGX_INVALID_FILE) {
+            continue;
+        }
+
+        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed",
+                          file[i].name.data);
+        }
+    }
+
+    if (old_cycle->connections == NULL) {
+        /* an old cycle is an init cycle */
+        ngx_destroy_pool(old_cycle->pool);
+        return cycle;
+    }
+
+    if (ngx_master) {
+        ngx_destroy_pool(old_cycle->pool);
+        return cycle;
+    }
+
+    if (ngx_temp_pool == NULL) {
+        ngx_temp_pool = ngx_create_pool(128, cycle->log);
+        if (ngx_temp_pool == NULL) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                          "can not create ngx_temp_pool");
+            exit(1);
+        }
+
+        n = 10;
+        ngx_old_cycles.elts = ngx_pcalloc(ngx_temp_pool,
+                                          n * sizeof(ngx_cycle_t *));
+        if (ngx_old_cycles.elts == NULL) {
+            exit(1);
+        }
+        ngx_old_cycles.nelts = 0;
+        ngx_old_cycles.size = sizeof(ngx_cycle_t *);
+        ngx_old_cycles.nalloc = n;
+        ngx_old_cycles.pool = ngx_temp_pool;
+
+        ngx_cleaner_event.event_handler = ngx_clean_old_cycles;
+        ngx_cleaner_event.log = cycle->log;
+        ngx_cleaner_event.data = &dumb;
+        dumb.fd = (ngx_socket_t) -1;
+    }
+
+    ngx_temp_pool->log = cycle->log;
+
+    old = ngx_push_array(&ngx_old_cycles);
+    if (old == NULL) {
+        exit(1);
+    }
+    *old = old_cycle;
+
+    if (!ngx_cleaner_event.timer_set) {
+        ngx_add_timer(&ngx_cleaner_event, 30000);
+        ngx_cleaner_event.timer_set = 1;
+    }
+
+    return cycle;
+}
+
+
+#if 0
+
+
+static ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle, char **envp)
+{
+    char                *p, *v;
+    ngx_socket_t         s;
+    ngx_listening_t     *ls;
+    struct sockaddr_in  *addr_in;
+
+    for ( /* void */ ; *envp; envp++) {
+        if (ngx_strncmp(*envp, NGINX_VAR, NGINX_VAR_LEN) != 0) {
+            continue;
+        }
+
+        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+                      "using inherited sockets from \"%s\"", *envp);
+
+        ngx_init_array(cycle->listening, cycle->pool,
+                       10, sizeof(ngx_listening_t), NGX_ERROR);
+
+        for (p = *envp + NGINX_VAR_LEN, v = p; *p; p++) {
+            if (*p == ':' || *p == ';') {
+                s = ngx_atoi(v, p - v);
+                if (s == NGX_ERROR) {
+                    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+                                  "invalid socket number \"%s\" "
+                                  "in NGINX enviroment variable, "
+                                  "ignoring the rest of the variable", v);
+                    break;
+                }
+                v = p + 1;
+
+                if (!(ls = ngx_push_array(&cycle->listening))) {
+                    return NGX_ERROR;
+                }
+
+                ls->fd = s;
+
+                /* AF_INET only */
+
+                ls->sockaddr = ngx_palloc(cycle->pool,
+                                          sizeof(struct sockaddr_in));
+                if (ls->sockaddr == NULL) {
+                    return NGX_ERROR;
+                }
+
+                ls->socklen = sizeof(struct sockaddr_in);
+                if (getsockname(s, ls->sockaddr, &ls->socklen) == -1) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "getsockname() of the inherited "
+                                  "socket #%d failed", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+
+                addr_in = (struct sockaddr_in *) ls->sockaddr;
+
+                if (addr_in->sin_family != AF_INET) {
+                    ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+                                  "the inherited socket #%d has "
+                                  "unsupported family", s);
+                    ls->ignore = 1;
+                    continue;
+                }
+                ls->addr_text_max_len = INET_ADDRSTRLEN;
+
+                ls->addr_text.data = ngx_palloc(cycle->pool,
+                                                ls->addr_text_max_len);
+                if (ls->addr_text.data == NULL) {
+                    return NGX_ERROR;
+                }
+
+                addr_in->sin_len = 0;
+
+                ls->family = addr_in->sin_family;
+                ls->addr_text.len = ngx_sock_ntop(ls->family, ls->sockaddr,
+                                                  ls->addr_text.data,
+                                                  ls->addr_text_max_len);
+                if (ls->addr_text.len == 0) {
+                    return NGX_ERROR;
+                }
+            }
+        }
+
+        break;
+    }
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle)
+{
+    int              tries, failed, reuseaddr, i;
+    ngx_err_t        err;
+    ngx_log_t       *log;
+    ngx_socket_t     s;
+    ngx_listening_t *ls;
+
+    reuseaddr = 1;
+#if (NGX_SUPPRESS_WARN)
+    failed = 0;
+#endif
+
+    log = cycle->log;
+
+    /* TODO: tries configurable */
+
+    for (tries = 10; tries; tries--) {
+        failed = 0;
+
+        /* for each listening socket */
+
+        ls = cycle->listening.elts;
+        for (i = 0; i < cycle->listening.nelts; i++) {
+
+            if (ls[i].ignore) {
+                continue;
+            }
+
+            if (ls[i].fd != -1) {
+                continue;
+            }
+
+            if (ls[i].inherited) {
+
+                /* TODO: close on exit */
+                /* TODO: nonblocking */
+                /* TODO: deferred accept */
+
+                continue;
+            }
+
+            s = ngx_socket(ls[i].family, ls[i].type, ls[i].protocol,
+                           ls[i].flags);
+
+            if (s == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              ngx_socket_n " %s failed", ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+#if (WIN32)
+            /*
+             * Winsock assignes a socket number divisible by 4
+             * so to find a connection we divide a socket number by 4.
+             */
+
+            if (s % 4) {
+                ngx_log_error(NGX_LOG_EMERG, ls->log, 0,
+                              ngx_socket_n " created socket %d", s);
+                return NGX_ERROR;
+            }
+#endif
+
+            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
+                           (const void *) &reuseaddr, sizeof(int)) == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              "setsockopt(SO_REUSEADDR) %s failed",
+                              ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+            /* TODO: close on exit */
+
+            if (!(ngx_event_flags & NGX_USE_AIO_EVENT)) {
+                if (ngx_nonblocking(s) == -1) {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_nonblocking_n " %s failed",
+                                  ls[i].addr_text.data);
+                    return NGX_ERROR;
+                }
+            }
+
+#if 0
+            if (ls[i].nonblocking) {
+                if (ngx_nonblocking(s) == -1) {
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_nonblocking_n " %s failed",
+                                  ls[i].addr_text.data);
+                    return NGX_ERROR;
+                }
+            }
+#endif
+
+            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
+                err = ngx_socket_errno;
+                ngx_log_error(NGX_LOG_EMERG, log, err,
+                              "bind() to %s failed", ls[i].addr_text.data);
+
+                if (err != NGX_EADDRINUSE)
+                    return NGX_ERROR;
+
+                if (ngx_close_socket(s) == -1)
+                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                                  ngx_close_socket_n " %s failed",
+                                  ls[i].addr_text.data);
+
+                failed = 1;
+                continue;
+            }
+
+            if (listen(s, ls[i].backlog) == -1) {
+                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+                              "listen() to %s failed", ls[i].addr_text.data);
+                return NGX_ERROR;
+            }
+
+            /* TODO: deferred accept */
+
+            ls[i].fd = s;
+        }
+
+        if (!failed)
+            break;
+
+        /* TODO: delay configurable */
+
+        ngx_log_error(NGX_LOG_NOTICE, log, 0,
+                      "try again to bind() after 500ms");
+        ngx_msleep(500);
+    }
+
+    if (failed) {
+        ngx_log_error(NGX_LOG_EMERG, log, 0, "still can not bind()");
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+#endif
+
+
+void ngx_reopen_files(ngx_cycle_t *cycle)
+{
+    ngx_fd_t          fd;
+    ngx_int_t         i;
+    ngx_open_file_t  *file;
+
+    file = cycle->open_files.elts;
+    for (i = 0; i < cycle->open_files.nelts; i++) {
+        if (file[i].name.data == NULL) {
+            continue;
+        }
+
+        fd = ngx_open_file(file[i].name.data, NGX_FILE_RDWR,
+                           NGX_FILE_CREATE_OR_OPEN|NGX_FILE_APPEND);
+
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
+                       "reopen file \"%s\", old:%d new:%d",
+                       file[i].name.data, file[i].fd, fd);
+
+        if (fd == NGX_INVALID_FILE) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                          ngx_open_file_n " \"%s\" failed", file[i].name.data);
+            continue;
+        }
+
+#if (WIN32)
+        if (ngx_file_append_mode(fd) == NGX_ERROR) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                          ngx_file_append_mode_n " \"%s\" failed",
+                          file[i].name.data);
+
+            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                              ngx_close_file_n " \"%s\" failed",
+                              file[i].name.data);
+            }
+
+            continue;
+        }
+#endif
+
+        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+                          ngx_close_file_n " \"%s\" failed",
+                          file[i].name.data);
+        }
+
+        file[i].fd = fd;
+    }
+}
+
+
+#if 0
+
+static void ngx_close_listening_sockets(ngx_cycle_t *cycle)
+{
+    ngx_int_t         i;
+    ngx_socket_t      fd;
+    ngx_listening_t  *ls;
+
+    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+        return;
+    }
+
+    ls = cycle->listening.elts;
+    for (i = 0; i < cycle->listening.nelts; i++) {
+        fd = ls[i].fd;
+
+#if (WIN32)
+        /*
+         * Winsock assignes a socket number divisible by 4
+         * so to find a connection we divide a socket number by 4.
+         */
+
+        fd /= 4;
+#endif
+
+        ngx_del_event(&cycle->read_events[fd], NGX_READ_EVENT, NGX_CLOSE_EVENT);
+
+        if (ngx_close_socket(ls[i].fd) == -1) {
+            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+                          ngx_close_socket_n " %s failed",
+                          ls[i].addr_text.data);
+        }
+
+        cycle->connections[fd].fd = -1;
+    }
+}
+
+#endif
+
+
+static void ngx_clean_old_cycles(ngx_event_t *ev)
+{
+    int            i, n, found, live;
+    ngx_log_t     *log;
+    ngx_cycle_t  **cycle;
+
+    log = ngx_cycle->log;
+    ngx_temp_pool->log = log;
+
+    ngx_log_debug(log, "clean old cycles");
+
+    live = 0;
+
+    cycle = ngx_old_cycles.elts;
+    for (i = 0; i < ngx_old_cycles.nelts; i++) {
+
+        if (cycle[i] == NULL) {
+            continue;
+        }
+
+        found = 0;
+
+        for (n = 0; n < cycle[i]->connection_n; n++) {
+            if (cycle[i]->connections[n].fd != -1) {
+                found = 1;
+                ngx_log_debug(log, "live fd: %d" _ n);
+                break;
+            }
+        }
+
+        if (found) {
+            live = 1;
+            continue;
+        }
+
+        ngx_log_debug(log, "clean old cycle: %d" _ i);
+        ngx_destroy_pool(cycle[i]->pool);
+        cycle[i] = NULL;
+    }
+
+    ngx_log_debug(log, "old cycles status: %d" _ live);
+
+    if (live) {
+        ngx_log_debug(log, "TIMER");
+        ngx_add_timer(ev, 30000);
+
+    } else {
+        ngx_destroy_pool(ngx_temp_pool);
+        ngx_temp_pool = NULL;
+        ngx_old_cycles.nelts = 0;
+    }
+}
new file mode 100644
--- /dev/null
+++ b/src/core/ngx_cycle.h
@@ -0,0 +1,36 @@
+#ifndef _NGX_CYCLE_H_INCLUDED_
+#define _NGX_CYCLE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+struct ngx_cycle_s {
+    void           ****conf_ctx;
+    ngx_pool_t        *pool;
+    ngx_log_t         *log;
+    ngx_array_t        listening;
+    ngx_array_t        open_files;
+    ngx_array_t        pathes;
+
+    int                connection_n;
+    ngx_connection_t  *connections;
+    ngx_event_t       *read_events;
+    ngx_event_t       *write_events;
+
+    ngx_cycle_t       *old_cycle;
+
+    unsigned           one_process:1;
+};
+
+
+ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);
+void ngx_reopen_files(ngx_cycle_t *cycle);
+
+
+extern volatile ngx_cycle_t  *ngx_cycle;
+extern ngx_array_t            ngx_old_cycles;
+
+
+#endif /* _NGX_CYCLE_H_INCLUDED_ */
--- a/src/core/ngx_log.c
+++ b/src/core/ngx_log.c
@@ -52,7 +52,7 @@ static const char *err_levels[] = {
 };
 
 static const char *debug_levels[] = {
-    "debug", "debug_alloc", "debug_event", "debug_http"
+    "debug", "debug_core", "debug_alloc", "debug_event", "debug_http"
 };
 
 
--- a/src/core/ngx_log.h
+++ b/src/core/ngx_log.h
@@ -16,9 +16,10 @@
 #define NGX_LOG_INFO            7
 #define NGX_LOG_DEBUG           8
 
-#define NGX_LOG_DEBUG_ALLOC     0x10
-#define NGX_LOG_DEBUG_EVENT     0x20
-#define NGX_LOG_DEBUG_HTTP      0x40
+#define NGX_LOG_DEBUG_CORE      0x10
+#define NGX_LOG_DEBUG_ALLOC     0x20
+#define NGX_LOG_DEBUG_EVENT     0x40
+#define NGX_LOG_DEBUG_HTTP      0x80
 
 #define NGX_LOG_DEBUG_FIRST     NGX_LOG_DEBUG
 #define NGX_LOG_DEBUG_LAST      NGX_LOG_DEBUG_HTTP
--- a/src/core/ngx_times.c
+++ b/src/core/ngx_times.c
@@ -39,19 +39,25 @@ void ngx_time_init()
     ngx_cached_http_log_time.data = cached_http_log_time;
 
     ngx_gettimeofday(&tv);
-    ngx_cached_time = tv.tv_sec;
+    ngx_cached_time = 0;
     ngx_start_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000;
     ngx_old_elapsed_msec = 0;
     ngx_elapsed_msec = 0;
 
-    ngx_time_update();
+    ngx_time_update(tv.tv_sec);
 }
 
 
-void ngx_time_update()
+void ngx_time_update(time_t s)
 {
     ngx_tm_t  tm;
 
+    if (ngx_cached_time == s) {
+        return;
+    }
+
+    ngx_cached_time = s;
+
     ngx_gmtime(ngx_cached_time, &ngx_cached_gmtime);
 
     ngx_cached_http_time.len = ngx_snprintf(ngx_cached_http_time.data,
--- a/src/core/ngx_times.h
+++ b/src/core/ngx_times.h
@@ -7,7 +7,7 @@
 
 
 void ngx_time_init();
-void ngx_time_update();
+void ngx_time_update(time_t s);
 size_t ngx_http_time(char *buf, time_t t);
 void ngx_gmtime(time_t t, ngx_tm_t *tp);
 
--- a/src/event/modules/ngx_kqueue_module.c
+++ b/src/event/modules/ngx_kqueue_module.c
@@ -92,9 +92,6 @@ static int ngx_kqueue_init(ngx_cycle_t *
 
     kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module);
 
-ngx_log_debug(cycle->log, "CH: %d" _ kcf->changes);
-ngx_log_debug(cycle->log, "EV: %d" _ kcf->events);
-
     if (ngx_kqueue == -1) {
         ngx_kqueue = kqueue();
 
@@ -196,6 +193,7 @@ static int ngx_kqueue_add_event(ngx_even
     ngx_connection_t  *c;
 
     ev->active = 1;
+    ev->disabled = 0;
     ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;
 
     if (nchanges > 0
@@ -205,16 +203,14 @@ static int ngx_kqueue_add_event(ngx_even
     {
         if (change_list[ev->index].flags == EV_DISABLE) {
 
-#if (NGX_DEBUG_EVENT)
-            ngx_connection_t *c = (ngx_connection_t *) ev->data;
-            ngx_log_debug(ev->log, "kqueue event activated: %d: ft:%d" _
-                          c->fd _ event);
-#endif
+            /*
+             * if the EV_DISABLE is still not passed to a kernel
+             * we will not pass it
+             */
 
-           /*
-            * if the EV_DISABLE is still not passed to a kernel
-            * we will not pass it
-            */
+            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                           "kevent activated: %d: ft:%d",
+                           ngx_event_ident(ev->data), event);
 
             if (ev->index < (u_int) --nchanges) {
                 e = (ngx_event_t *) change_list[nchanges].udata;
@@ -241,17 +237,16 @@ static int ngx_kqueue_del_event(ngx_even
     ngx_event_t  *e;
 
     ev->active = 0;
+    ev->disabled = 0;
 
     if (nchanges > 0
         && ev->index < (u_int) nchanges
         && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)
                                                              == (uintptr_t) ev)
     {
-#if (NGX_DEBUG_EVENT)
-        ngx_connection_t *c = (ngx_connection_t *) ev->data;
-        ngx_log_debug(ev->log, "kqueue event deleted: %d: ft:%d" _
-                      c->fd _ event);
-#endif
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
+                       "kevent deleted: %d: ft:%d",
+                       ngx_event_ident(ev->data), event);
 
         /* if the event is still not passed to a kernel we will not pass it */
 
@@ -274,6 +269,10 @@ static int ngx_kqueue_del_event(ngx_even
         return NGX_OK;
     }
 
+    if (flags & NGX_DISABLE_EVENT) {
+        ev->disabled = 1;
+    }
+
     return ngx_kqueue_set_event(ev, event,
                            flags & NGX_DISABLE_EVENT ? EV_DISABLE : EV_DELETE);
 }
@@ -346,7 +345,7 @@ static int ngx_kqueue_set_event(ngx_even
 
 static int ngx_kqueue_process_events(ngx_log_t *log)
 {
-    int                events, instance, i;
+    ngx_int_t          events, instance, i;
     ngx_err_t          err;
     ngx_msec_t         timer;
     ngx_event_t       *ev;
@@ -391,10 +390,7 @@ static int ngx_kqueue_process_events(ngx
 #endif
     ngx_elapsed_msec = tv.tv_sec * 1000 + tv.tv_usec / 1000 - ngx_start_msec;
 
-    if (ngx_cached_time != tv.tv_sec) {
-        ngx_cached_time = tv.tv_sec;
-        ngx_time_update();
-    }
+    ngx_time_update(tv.tv_sec);
 
     if (timer) {
         delta = ngx_elapsed_msec - delta;
@@ -407,12 +403,12 @@ static int ngx_kqueue_process_events(ngx
         }
     }
 
-#if (NGX_DEBUG_EVENT)
-        ngx_log_debug(log, "kevent timer: %d, delta: %d" _ timer _ (int) delta);
-#endif
+    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,
+                   "kevent timer: %d, delta: %d", timer, (int) delta);
 
     if (err) {
-        ngx_log_error(NGX_LOG_ALERT, log, err, "kevent() failed");
+        ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
+                      log, err, "kevent() failed");
         return NGX_ERROR;
     }
 
--- a/src/event/modules/ngx_poll_module.c
+++ b/src/event/modules/ngx_poll_module.c
@@ -262,11 +262,7 @@ static int ngx_poll_process_events(ngx_l
     ngx_log_debug(log, "poll ready %d" _ ready);
 
     ngx_gettimeofday(&tv);
-
-    if (ngx_cached_time != tv.tv_sec) {
-        ngx_cached_time = tv.tv_sec;
-        ngx_time_update();
-    }
+    ngx_time_update(tv.tv_sec);
 
     if ((int) timer != INFTIM) {
         delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta;
--- a/src/event/modules/ngx_select_module.c
+++ b/src/event/modules/ngx_select_module.c
@@ -370,11 +370,7 @@ static int ngx_select_process_events(ngx
 #else /* HAVE_SELECT_CHANGE_TIMEOUT */
 
     ngx_gettimeofday(&tv);
-
-    if (ngx_cached_time != tv.tv_sec) {
-        ngx_cached_time = tv.tv_sec;
-        ngx_time_update();
-    }
+    ngx_time_update(tv.tv_sec);
 
     if (timer) {
         delta = tv.tv_sec * 1000 + tv.tv_usec / 1000 - delta;
--- a/src/event/ngx_event.c
+++ b/src/event/ngx_event.c
@@ -222,7 +222,8 @@ static int ngx_event_init(ngx_cycle_t *c
                  */
 
                 if (ngx_del_event(&cycle->old_cycle->read_events[fd],
-                                             NGX_READ_EVENT, 0) == NGX_ERROR) {
+                                 NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR)
+                {
                     return NGX_ERROR;
                 }
 
--- a/src/event/ngx_event.h
+++ b/src/event/ngx_event.h
@@ -20,6 +20,14 @@ typedef struct {
 #endif
 
 
+typedef enum {
+     NGX_ERROR_CRIT = 0,
+     NGX_ERROR_ERR,
+     NGX_ERROR_INFO,
+     NGX_ERROR_IGNORE_ECONNRESET
+} ngx_event_log_error_e;
+
+
 struct ngx_event_s {
     void            *data;
     /* TODO rename to handler */
@@ -36,8 +44,8 @@ struct ngx_event_s {
     /*
      * The inline of "ngx_rbtree_t  rbtree;".
      *
-     * It allows to pack the rbtree_color and the variuos event bit flags into
-     * the single "int".  We also use "unsigned char" and then "usigned short"
+     * It allows to pack the rbtree_color and the various event bit flags into
+     * the single "int".  We also use "unsigned char" and then "unsigned short"
      * because otherwise MSVC 6.0 uses an additional "int" for the bit flags.
      * We use "char rbtree_color" instead of "unsigned int rbtree_color:1"
      * because it preserves the bits order on the big endian platforms.
@@ -62,14 +70,16 @@ struct ngx_event_s {
      */
     unsigned char    active:1;
 
+    unsigned char    disabled:1;
+
     /* the ready event; in aio mode 0 means that no operation can be posted */
     unsigned char    ready:1;
 
     /* aio operation is complete */
     unsigned char    complete:1;
 
-    unsigned char    eof:1;
-    unsigned char    error:1;
+    unsigned short   eof:1;
+    unsigned short   error:1;
 
     unsigned short   timedout:1;
     unsigned short   timer_set:1;
@@ -78,7 +88,7 @@ struct ngx_event_s {
 
     unsigned short   read_discarded:1;
 
-    unsigned short   ignore_econnreset:1;
+    unsigned short   log_error:2;  /* ngx_event_log_error_e */
     unsigned short   unexpected_eof:1;
 
     unsigned short   deferred_accept:1;
@@ -371,6 +381,9 @@ int ngx_event_post_acceptex(ngx_listenin
 #endif
 
 
+/* used in ngx_log_debugX() */
+#define ngx_event_ident(p)  ((ngx_connection_t *) (p))->fd
+
 
 #include <ngx_event_timer.h>
 
--- a/src/event/ngx_event_timer.c
+++ b/src/event/ngx_event_timer.c
@@ -16,7 +16,7 @@ ngx_rbtree_t   ngx_event_timer_sentinel;
 
 int ngx_event_timer_init(ngx_cycle_t *cycle)
 {
-    if (cycle->old_cycle) {
+    if (ngx_event_timer_rbtree) {
         return NGX_OK;
     }
 
--- a/src/event/ngx_event_timer.h
+++ b/src/event/ngx_event_timer.h
@@ -33,9 +33,6 @@ extern ngx_rbtree_t  *ngx_event_timer_rb
 extern ngx_rbtree_t   ngx_event_timer_sentinel;
 
 
-#define ngx_event_ident(p)  ((ngx_connection_t *) (p))->fd
-
-
 ngx_inline static void ngx_event_del_timer(ngx_event_t *ev)
 {
     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
--- a/src/http/modules/ngx_http_rewrite_handler.c
+++ b/src/http/modules/ngx_http_rewrite_handler.c
@@ -113,7 +113,7 @@ static ngx_int_t ngx_http_rewrite_handle
         if (rc == NGX_DECLINED) {
             if (scf->log) {
                 ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
-                              "\"%s\" is not matched", rule[i].re_name.data);
+                              "\"%s\" does not match", rule[i].re_name.data);
             }
 
             continue;
@@ -129,7 +129,7 @@ static ngx_int_t ngx_http_rewrite_handle
 
         if (scf->log) {
             ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,
-                          "\"%s\" matched", rule[i].re_name.data);
+                          "\"%s\" matches", rule[i].re_name.data);
         }
 
         uri.len = rule[i].size;
@@ -301,8 +301,10 @@ static char *ngx_http_rewrite_rule(ngx_c
             }
         }
 
-        rule->msize++;
-        rule->msize *= 3;
+        if (rule->msize) {
+            rule->msize++;
+            rule->msize *= 3;
+        }
 
         if (cf->args->nelts > 3) {
             if (ngx_strcmp(value[3].data, "last") == 0) {
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -148,6 +148,13 @@ static ngx_command_t  ngx_http_core_comm
       offsetof(ngx_http_core_loc_conf_t, sendfile),
       NULL },
 
+    { ngx_string("tcp_nopush"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_core_loc_conf_t, tcp_nopush),
+      NULL },
+
     { ngx_string("send_timeout"),
       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_msec_slot,
@@ -505,6 +512,11 @@ int ngx_http_find_location_config(ngx_ht
         r->sendfile = 1;
     }
 
+    if (!clcf->tcp_nopush) {
+        /* disable TCP_NOPUSH/TCP_CORK use */
+        r->connection->tcp_nopush = -1;
+    }
+
     if (auto_redirect) {
         if (!(r->headers_out.location =
                    ngx_http_add_header(&r->headers_out, ngx_http_headers_out)))
@@ -1128,6 +1140,7 @@ static void *ngx_http_core_create_loc_co
 
     lcf->client_body_timeout = NGX_CONF_UNSET;
     lcf->sendfile = NGX_CONF_UNSET;
+    lcf->tcp_nopush = NGX_CONF_UNSET;
     lcf->send_timeout = NGX_CONF_UNSET;
     lcf->send_lowat = NGX_CONF_UNSET;
     lcf->discarded_buffer_size = NGX_CONF_UNSET;
@@ -1206,6 +1219,7 @@ static char *ngx_http_core_merge_loc_con
     ngx_conf_merge_msec_value(conf->client_body_timeout,
                               prev->client_body_timeout, 10000);
     ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);
+    ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);
     ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 10000);
     ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);
     ngx_conf_merge_size_value(conf->discarded_buffer_size,
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -122,6 +122,7 @@ typedef struct {
 
     ngx_msec_t    client_body_timeout;     /* client_body_timeout */
     int           sendfile;                /* sendfile */
+    int           tcp_nopush;              /* tcp_nopush */
     ngx_msec_t    send_timeout;            /* send_timeout */
     ssize_t       send_lowat;              /* send_lowat */
     ssize_t       discarded_buffer_size;   /* discarded_buffer_size */
--- a/src/http/ngx_http_log_handler.h
+++ b/src/http/ngx_http_log_handler.h
@@ -15,11 +15,6 @@ typedef char *(*ngx_http_log_op_pt) (ngx
 
 #define NGX_HTTP_LOG_ARG         (u_int) -1
 
-/* STUB */
-#define NGX_INT32_LEN      sizeof("-2147483648") - 1
-#define NGX_TIME_T_LEN     sizeof("-2147483648") - 1
-#define NGX_OFF_T_LEN      sizeof("-9223372036854775808") - 1
-
 
 typedef struct {
     size_t               len;
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -82,6 +82,7 @@ void ngx_http_init_connection(ngx_connec
 
     rev = c->read;
     rev->event_handler = ngx_http_init_request;
+    rev->log_error = NGX_ERROR_INFO;
 
     if (rev->ready) {
         /* deferred accept, aio, iocp, epoll */
@@ -896,6 +897,17 @@ void ngx_http_finalize_request(ngx_http_
         ngx_del_timer(r->connection->write);
     }
 
+    if (r->connection->read->kq_eof) {
+#if (NGX_KQUEUE)
+        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log,
+                       r->connection->read->kq_errno,
+                       "kevent reported about closed connection by client");
+#endif
+        ngx_http_close_request(r, 0);
+        ngx_http_close_connection(r->connection);
+        return;
+    }
+
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
     if (r->keepalive != 0 && clcf->keepalive_timeout > 0) {
@@ -1203,7 +1215,7 @@ static void ngx_http_set_keepalive(ngx_h
 
     ctx->action = "keepalive";
 
-    if (c->tcp_nopush) {
+    if (c->tcp_nopush == 1) {
         if (ngx_tcp_push(c->fd) == NGX_ERROR) {
             ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,
                           ngx_tcp_push_n " failed");
@@ -1239,10 +1251,10 @@ static void ngx_http_keepalive_handler(n
      * so we ignore ECONNRESET here.
      */
 
-    rev->ignore_econnreset = 1;
+    rev->log_error = NGX_ERROR_IGNORE_ECONNRESET;
     ngx_set_socket_errno(0);
     n = ngx_recv(c, c->buffer->last, c->buffer->end - c->buffer->last);
-    rev->ignore_econnreset = 0;
+    rev->log_error = NGX_ERROR_INFO;
 
     if (n == NGX_AGAIN) {
         return;
@@ -1506,11 +1518,11 @@ void ngx_http_close_connection(ngx_conne
         ngx_del_conn(c);
 
     } else {
-        if (c->read->active) {
+        if (c->read->active || c->read->disabled) {
             ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
         }
 
-        if (c->write->active) {
+        if (c->write->active || c->write->disabled) {
             ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
         }
     }
--- a/src/os/unix/ngx_errno.h
+++ b/src/os/unix/ngx_errno.h
@@ -20,6 +20,7 @@ typedef int               ngx_err_t;
 #define NGX_EINPROGRESS   EINPROGRESS
 #define NGX_EADDRINUSE    EADDRINUSE
 #define NGX_ECONNRESET    ECONNRESET
+#define NGX_ENOTCONN      ENOTCONN
 #define NGX_ETIMEDOUT     ETIMEDOUT
 #define NGX_ECANCELED     ECANCELED
 #define NGX_ENOMOREFILES  0
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -24,6 +24,7 @@
 #include <dirent.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#include <pwd.h>
 #include <osreldate.h>
 
 
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -151,7 +151,7 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 
         if (file) {
 
-            if (ngx_freebsd_use_tcp_nopush && !c->tcp_nopush) {
+            if (ngx_freebsd_use_tcp_nopush && c->tcp_nopush == 0) {
                 c->tcp_nopush = 1;
 
                 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "tcp_nopush");
@@ -191,7 +191,7 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
                 } else if (err == NGX_EAGAIN) {
                     eagain = 1;
 
-                } else if (err == NGX_EPIPE) {
+                } else if (err == NGX_EPIPE || err == NGX_ENOTCONN) {
                     level = NGX_LOG_INFO;
                 }
 
@@ -202,8 +202,13 @@ ngx_chain_t *ngx_freebsd_sendfile_chain(
 
                 } else {
                     wev->error = 1;
+#if 0
                     ngx_log_error(level, c->log, err,
                                   "sendfile() failed");
+#else
+                    ngx_log_error(level, c->log, err,
+                                  "sendfile(#%d) failed", c->fd);
+#endif
                     return NGX_CHAIN_ERROR;
                 }
             }
--- a/src/os/unix/ngx_linux_sendfile_chain.c
+++ b/src/os/unix/ngx_linux_sendfile_chain.c
@@ -6,7 +6,7 @@
 
 /*
  * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit
- * offsets only and the including <sys/sendfile.h> breaks building if
+ * offsets only and the including <sys/sendfile.h> breaks the compiling if
  * off_t is 64 bit wide.  So we use own sendfile() definition where offset
  * parameter is int32_t and use sendfile() with the file parts below 2G.
  *
@@ -75,14 +75,14 @@ ngx_chain_t *ngx_linux_sendfile_chain(ng
 
         /* set TCP_CORK if there is a header before a file */
 
-        if (!c->tcp_nopush
+        if (!c->tcp_nopush == 0
             && header.nelts != 0
             && cl
             && cl->hunk->type & NGX_HUNK_FILE)
         {
             c->tcp_nopush = 1;
 
-ngx_log_debug(c->log, "CORK");
+            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "tcp_nopush");
 
             if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
                 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -49,9 +49,13 @@ extern int          ngx_max_sockets;
 extern int          ngx_inherited_nonblocking;
 
 
-extern int          done;
-extern int          restart;
-extern int          rotate;
+extern ngx_int_t    ngx_master;
+
+extern ngx_int_t    ngx_quit;
+extern ngx_int_t    ngx_terminate;
+extern ngx_int_t    ngx_reconfigure;
+extern ngx_int_t    ngx_reopen;
+extern ngx_int_t    ngx_change_binary;
 
 
 #ifdef __FreeBSD__
--- a/src/os/unix/ngx_posix_init.c
+++ b/src/os/unix/ngx_posix_init.c
@@ -8,9 +8,6 @@ int  ngx_inherited_nonblocking;
 
 
 void ngx_signal_handler(int signo);
-void ngx_exit_signal_handler(int signo);
-void ngx_restart_signal_handler(int signo);
-void ngx_rotate_signal_handler(int signo);
 
 
 typedef struct {
@@ -22,27 +19,37 @@ typedef struct {
 
 
 ngx_signal_t  signals[] = {
-    { ngx_signal_value(NGX_RESTART_SIGNAL),
-      "SIG" ngx_value(NGX_RESTART_SIGNAL),
-      "restarting",
+    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
+      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
+      ", reconfiguring",
       ngx_signal_handler },
 
-    { ngx_signal_value(NGX_ROTATE_SIGNAL),
-      "SIG" ngx_value(NGX_ROTATE_SIGNAL),
-      "reopen logs",
+    { ngx_signal_value(NGX_REOPEN_SIGNAL),
+      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
+      ", reopen logs",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_INTERRUPT_SIGNAL),
       "SIG" ngx_value(NGX_INTERRUPT_SIGNAL),
-      "exiting",
+      ", exiting",
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
+      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
+      ", exiting",
       ngx_signal_handler },
 
     { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
       "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
-      "shutdowning",
+      ", shutdowning",
       ngx_signal_handler },
 
-    { SIGCHLD, "SIGCHLD", NULL, ngx_sigchld_handler },
+    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
+      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
+      ", changing binary",
+      ngx_signal_handler },
+
+    { SIGCHLD, "SIGCHLD", "", ngx_signal_handler },
 
     { SIGPIPE, "SIGPIPE, SIG_IGN", NULL, SIG_IGN },
 
@@ -91,7 +98,8 @@ int ngx_posix_init(ngx_log_t *log)
 
 void ngx_signal_handler(int signo)
 {
-    ngx_signal_t  *sig;
+    struct timeval   tv;
+    ngx_signal_t    *sig;
 
     for (sig = signals; sig->signo != 0; sig++) {
         if (sig->signo == signo) {
@@ -99,64 +107,43 @@ void ngx_signal_handler(int signo)
         }
     }
 
-#if (HAVE_STRSIGNAL)
+    ngx_gettimeofday(&tv);
+    ngx_time_update(tv.tv_sec);
 
     ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
-                  "signal #%d (%s: %s) received, %s",
-                  signo, sig->signame, strsignal(signo), sig->action);
-#else
-
-    ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
-                  "signal #%d (%s) received, %s",
+                  "signal %d (%s) received%s",
                   signo, sig->signame, sig->action);
 
-#endif
-
     switch (signo) {
 
-    /* STUB */
-    case SIGQUIT:
-    case SIGABRT:
+    case SIGCHLD:
+        ngx_process_get_status();
+        break;
 
     case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+        ngx_quit = 1;
+        break;
+
+    case ngx_signal_value(NGX_TERMINATE_SIGNAL):
     case ngx_signal_value(NGX_INTERRUPT_SIGNAL):
-        done = 1;
+        ngx_terminate = 1;
         break;
 
-    case ngx_signal_value(NGX_RESTART_SIGNAL):
-        restart = 1;
+    case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+        ngx_reconfigure = 1;
         break;
 
-    case ngx_signal_value(NGX_ROTATE_SIGNAL):
-        rotate = 1;
+    case ngx_signal_value(NGX_REOPEN_SIGNAL):
+        ngx_reopen = 1;
+        break;
+
+    case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+        ngx_change_binary = 1;
         break;
     }
 }
 
 
-void ngx_exit_signal_handler(int signo)
-{
-    char *s;
-
-    s = strsignal(signo);
-    ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
-                  "%s signal received, exiting", s);
-    done = 1;
-}
-
-
-void ngx_restart_signal_handler(int signo)
-{
-    restart = 1;
-}
-
-
-void ngx_rotate_signal_handler(int signo)
-{
-    rotate = 1;
-}
-
-
 int ngx_posix_post_conf_init(ngx_log_t *log)
 {
     ngx_fd_t  pp[2];
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -3,73 +3,241 @@
 #include <ngx_core.h>
 
 
-void testone(ngx_log_t *log)
-{
-    ngx_log_debug(log, "child process");
-    ngx_msleep(5000);
-    exit(0);
-}
+static void ngx_exec_proc(ngx_cycle_t *cycle, void *data);
+
+ngx_uint_t     ngx_last_process;
+ngx_process_t  ngx_processes[NGX_MAX_PROCESSES];
 
 
-int ngx_spawn_process(ngx_log_t *log)
+ngx_int_t ngx_spawn_process(ngx_cycle_t *cycle,
+                            ngx_spawn_proc_pt proc, void *data,
+                            char *name, ngx_int_t respawn)
 {
-    pid_t     pid;
-    sigset_t  set, oset; 
+    sigset_t   set, oset;
+    ngx_pid_t  pid;
 
-    sigemptyset(&set);
-    sigaddset(&set, SIGCHLD);
-    if (sigprocmask(SIG_BLOCK, &set, &oset) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "sigprocmask() failed");
+    if (respawn < 0) {
+        sigemptyset(&set);
+        sigaddset(&set, SIGCHLD);
+        if (sigprocmask(SIG_BLOCK, &set, &oset) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "sigprocmask() failed while spawning %s", name);
+            return NGX_ERROR;
+        }
     }
 
     pid = fork();
 
+    if (pid == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "fork() failed while spawning \"%s\"", name);
+    }
+
     if (pid == -1 || pid == 0) {
         if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) {
-            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
-                          "sigprocmask() failed");
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "sigprocmask() failed while spawning %s", name);
+            return NGX_ERROR;
         }
     }
 
     switch (pid) {
     case -1:
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "fork() failed");
         return NGX_ERROR;
 
     case 0:
-        testone(log);
+        proc(cycle, data);
         break;
 
     default:
         break;
     }
 
-ngx_log_debug(log, "parent process, child: " PID_T_FMT _ pid);
+    ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                   "spawn %s: " PID_T_FMT, name, pid);
+
+    if (respawn >= 0) {
+        ngx_processes[respawn].pid = pid;
+        ngx_processes[respawn].exited = 0;
+        return NGX_OK;
+    }
 
-    /* book keeping */
+    ngx_processes[ngx_last_process].pid = pid;
+    ngx_processes[ngx_last_process].proc = proc;
+    ngx_processes[ngx_last_process].data = data;
+    ngx_processes[ngx_last_process].name = name;
+    ngx_processes[ngx_last_process].respawn =
+                                      (respawn == NGX_PROCESS_RESPAWN) ? 1 : 0;
+    ngx_processes[ngx_last_process].detached =
+                                     (respawn == NGX_PROCESS_DETACHED) ? 1 : 0;
+    ngx_processes[ngx_last_process].exited = 0;
+    ngx_processes[ngx_last_process].exiting = 0;
+    ngx_last_process++;
 
     if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) {
-        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, "sigprocmask() failed");
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "sigprocmask() failed while spawning %s", name);
+        return NGX_ERROR;
+    }
+
+    return NGX_OK;
+}
+
+
+ngx_int_t ngx_exec(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
+{
+    if (ngx_spawn_process(cycle, ngx_exec_proc, ctx, ctx->name,
+                                            NGX_PROCESS_DETACHED) == NGX_ERROR)
+    {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                      "can not spawn %s", ctx->name);
+        return NGX_ERROR;
     }
 
     return NGX_OK;
 }
 
 
+static void ngx_exec_proc(ngx_cycle_t *cycle, void *data)
+{
+    ngx_exec_ctx_t  *ctx = data;
+
+    if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "execve() failed while executing %s \"%s\"",
+                      ctx->name, ctx->path);
+    }
+
+    exit(1);
+}
+
+
+void ngx_signal_processes(ngx_cycle_t *cycle, ngx_int_t signal)
+{
+    sigset_t    set, oset;
+    ngx_uint_t  i;
+
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    if (sigprocmask(SIG_BLOCK, &set, &oset) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "sigprocmask() failed while signaling processes");
+        return;
+    }
+
+    for (i = 0; i < ngx_last_process; i++) {
+
+        if (ngx_processes[i].detached) {
+            continue;
+        }
+
+        if (ngx_processes[i].exited) {
+            if (i != --ngx_last_process) {
+                ngx_processes[i--] = ngx_processes[ngx_last_process];
+            }
+            continue;
+        }
+
+        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+                       "kill (" PID_T_FMT ", %d)" ,
+                       ngx_processes[i].pid, signal);
+
+        if (kill(ngx_processes[i].pid, signal) == -1) {
+            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                          "kill(%d, %d) failed", ngx_processes[i].pid, signal);
+            continue;
+        }
+
+        if (signal != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
+            ngx_processes[i].exiting = 1;
+        }
+    }
+
+    if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "sigprocmask() failed while signaling processes");
+    }
+}
+
+
+void ngx_respawn_processes(ngx_cycle_t *cycle)
+{
+    sigset_t    set, oset;
+    ngx_uint_t  i;
+
+    sigemptyset(&set);
+    sigaddset(&set, SIGCHLD);
+    if (sigprocmask(SIG_BLOCK, &set, &oset) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "sigprocmask() failed while respawning processes");
+        return;
+    }
+
+    /*
+     * to avoid a race condition we can check and set value of ngx_respawn
+     * only in signal handler or while SIGCHLD is blocked
+     */
+
+    if (ngx_respawn) {
+
+        for (i = 0; i < ngx_last_process; i++) {
+            if (!ngx_processes[i].exited) {
+                continue;
+            }
+
+            if (!ngx_processes[i].respawn) {
+                if (i != --ngx_last_process) {
+                    ngx_processes[i--] = ngx_processes[ngx_last_process];
+                }
+                continue;
+            }
+
+            if (ngx_spawn_process(cycle,
+                                  ngx_processes[i].proc, ngx_processes[i].data,
+                                  ngx_processes[i].name, i) == NGX_ERROR)
+            {
+                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+                              "can not respawn %s", ngx_processes[i].name);
+            }
+        }
+
+        ngx_respawn = 0;
+    }
+
+    if (sigprocmask(SIG_SETMASK, &oset, &set) == -1) {
+        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+                      "sigprocmask() failed while respawning processes");
+    }
+}
+
+
+#if 0
 void ngx_sigchld_handler(int signo)
 {
-    int             status, one;
-    pid_t           pid;
-    ngx_err_t       err;
-    struct timeval  tv;
+    int              status;
+    char            *process;
+    ngx_pid_t        pid;
+    ngx_err_t        err;
+    ngx_uint_t       i, one;
+    struct timeval   tv;
 
     ngx_gettimeofday(&tv);
+    ngx_time_update(tv.tv_sec);
 
-    if (ngx_cached_time != tv.tv_sec) {
-        ngx_cached_time = tv.tv_sec;
-        ngx_time_update();
-    }
+    ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
+                  "signal #%d (SIGCHLD) received", signo);
+}
+#endif
+
 
+void ngx_process_get_status()
+{
+    int              status;
+    char            *process;
+    ngx_pid_t        pid;
+    ngx_err_t        err;
+    ngx_uint_t       i, one;
+    struct timeval   tv;
     one = 0;
 
     for ( ;; ) {
@@ -96,18 +264,39 @@ void ngx_sigchld_handler(int signo)
         }
 
         one = 1;
+        process = "";
 
-        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
-                      "process " PID_T_FMT " exited with code %d", pid, status);
+        for (i = 0; i < ngx_last_process; i++) {
+            if (ngx_processes[i].pid == pid) {
+                ngx_processes[i].status = status;
 
-        /* TODO: restart handler */
+                if (!ngx_processes[i].exiting) {
+                    ngx_processes[i].exited = 1;
+
+                    if (ngx_processes[i].respawn) {
+                        ngx_respawn = 1;
+                    }
+                }
 
-#if 0
-        ngx_msleep(2000);
-#endif
+                process = ngx_processes[i].name;
+                break;
+            }
+        }
+
+        if (i == ngx_last_process) {
+            process = "unknown process";
+        }
 
-#if 0
-        ngx_spawn_process(ngx_cycle->log);
-#endif
+        if (WTERMSIG(status)) {
+            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+                          "%s " PID_T_FMT " exited on signal %d%s",
+                          process, pid, WTERMSIG(status),
+                          WCOREDUMP(status) ? " (core dumped)" : "");
+
+        } else {
+            ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
+                          "%s " PID_T_FMT " exited with code %d",
+                          process, pid, WEXITSTATUS(status));
+        }
     }
 }
--- a/src/os/unix/ngx_process.h
+++ b/src/os/unix/ngx_process.h
@@ -4,11 +4,51 @@
 
 typedef pid_t       ngx_pid_t;
 
-#define ngx_getpid  getpid
+typedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data);
+
+typedef struct {
+    ngx_pid_t           pid;
+    int                 status;
+
+    ngx_spawn_proc_pt   proc;
+    void               *data;
+    char               *name;
+
+    unsigned            respawn:1;
+    unsigned            detached:1;
+    unsigned            exited:1;
+    unsigned            exiting:1;
+} ngx_process_t;
 
 
-int ngx_spawn_process(ngx_log_t *log);
-void ngx_sigchld_handler(int signo);
+typedef struct {
+    char         *path;
+    char         *name;
+    char *const  *argv;
+    char *const  *envp;
+} ngx_exec_ctx_t;
+
+
+#define ngx_getpid  getpid
+
+#define NGX_MAX_PROCESSES  1024
+
+#define NGX_PROCESS_RESPAWN    -1
+#define NGX_PROCESS_NORESPAWN  -2
+#define NGX_PROCESS_DETACHED   -3
+
+
+ngx_int_t ngx_spawn_process(ngx_cycle_t *cycle,
+                            ngx_spawn_proc_pt proc, void *data,
+                            char *name, ngx_int_t respawn);
+ngx_int_t ngx_exec(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);
+void ngx_signal_processes(ngx_cycle_t *cycle, ngx_int_t signal);
+void ngx_respawn_processes(ngx_cycle_t *cycle);
+void ngx_process_get_status(void);
+
+extern ngx_int_t      ngx_respawn;
+extern ngx_uint_t     ngx_last_process;
+extern ngx_process_t  ngx_processes[NGX_MAX_PROCESSES];
 
 
 #endif /* _NGX_PROCESS_H_INCLUDED_ */
--- a/src/os/unix/ngx_recv.c
+++ b/src/os/unix/ngx_recv.c
@@ -17,8 +17,9 @@ ssize_t ngx_unix_recv(ngx_connection_t *
     rev = c->read;
 
     if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
-        ngx_log_debug(c->log, "recv: eof:%d, avail:%d, err:%d" _
-                      rev->kq_eof _ rev->available _ rev->kq_errno);
+        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                       "recv: eof:%d, avail:%d, err:%d",
+                       rev->kq_eof, rev->available, rev->kq_errno);
 
         if (rev->available == 0) {
             if (rev->kq_eof) {
@@ -28,7 +29,16 @@ ssize_t ngx_unix_recv(ngx_connection_t *
                 if (rev->kq_errno) {
                     rev->error = 1;
                     ngx_set_socket_errno(rev->kq_errno);
-                    return ngx_unix_recv_error(rev, rev->kq_errno);
+                    ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,
+                                  "kevent() reported about closed connection");
+
+                    if (rev->kq_errno == NGX_ECONNRESET
+                        && rev->log_error == NGX_ERROR_IGNORE_ECONNRESET)
+                    {
+                        return 0;
+                    }
+
+                    return NGX_ERROR;
                 }
 
                 return 0;
@@ -42,7 +52,7 @@ ssize_t ngx_unix_recv(ngx_connection_t *
     do {
         n = recv(c->fd, buf, size, 0);
 
-        ngx_log_debug(c->log, "recv: %d:%d" _ n _ size);
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,"recv: %d:%d", n, size);
 
         if (n >= 0) {
             if (ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) {
@@ -104,7 +114,7 @@ ssize_t ngx_unix_recv(ngx_connection_t *
     do {
         n = recv(c->fd, buf, size, 0);
 
-        ngx_log_debug(c->log, "recv: %d:%d" _ n _ size);
+        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,"recv: %d:%d", n, size);
 
         if (n >= 0) {
             if ((size_t) n < size) {
@@ -138,21 +148,33 @@ ssize_t ngx_unix_recv(ngx_connection_t *
 
 static int ngx_unix_recv_error(ngx_event_t *rev, ngx_err_t err)
 {
-    if (err == NGX_ECONNRESET && rev->ignore_econnreset) {
-        return 0;
-    }
+    ngx_int_t  level;
 
-    if (err == NGX_EAGAIN) {
-        ngx_log_error(NGX_LOG_INFO, rev->log, err, "recv() returned EAGAIN");
+    if (err == NGX_EAGAIN || err == NGX_EINTR) {
+        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, err, "recv() not ready");
         return NGX_AGAIN;
     }
 
-    if (err == NGX_EINTR) {
-        ngx_log_error(NGX_LOG_INFO, rev->log, err, "recv() returned EINTR");
-        return NGX_EINTR;
+    if (err == NGX_ECONNRESET) {
+
+        switch (rev->log_error) {
+        case NGX_ERROR_IGNORE_ECONNRESET:
+            return 0;
+        case NGX_ERROR_INFO:
+            level = NGX_LOG_INFO;
+            break;
+        case NGX_ERROR_ERR:
+            level = NGX_LOG_ERR;
+            break;
+        default:
+            level = NGX_LOG_CRIT;
+        }
+
+    } else {
+        level = NGX_LOG_CRIT;
     }
 
-    ngx_log_error(NGX_LOG_ERR, rev->log, err, "recv() failed");
+    ngx_log_error(level, rev->log, err, "recv() failed");
 
     return NGX_ERROR;
 }
--- a/src/os/win32/ngx_os.h
+++ b/src/os/win32/ngx_os.h
@@ -46,8 +46,8 @@ extern int          ngx_inherited_nonblo
 extern int          ngx_win32_version;
 
 
-extern int          restart;
-extern int          rotate;
+extern int          reconfigure;
+extern int          reopen;