summaryrefslogtreecommitdiffstats
path: root/usr.sbin/nginx/src
diff options
context:
space:
mode:
authorrobert <robert@openbsd.org>2011-09-22 23:38:25 +0000
committerrobert <robert@openbsd.org>2011-09-22 23:38:25 +0000
commitd2bbe503305ff6b033a901010508488faf7e9c70 (patch)
tree60b5dc346eedf872ca3ca83e9969454dd0120c0a /usr.sbin/nginx/src
parentimport of nginx 1.0.6 with a bundled libpcre needed for pcre to work (diff)
downloadwireguard-openbsd-d2bbe503305ff6b033a901010508488faf7e9c70.tar.xz
wireguard-openbsd-d2bbe503305ff6b033a901010508488faf7e9c70.zip
thank you CVS for missing the "core" directory
Diffstat (limited to 'usr.sbin/nginx/src')
-rw-r--r--usr.sbin/nginx/src/core/nginx.c1326
-rw-r--r--usr.sbin/nginx/src/core/nginx.h19
-rw-r--r--usr.sbin/nginx/src/core/ngx_array.c146
-rw-r--r--usr.sbin/nginx/src/core/ngx_array.h52
-rw-r--r--usr.sbin/nginx/src/core/ngx_buf.c217
-rw-r--r--usr.sbin/nginx/src/core/ngx_buf.h161
-rw-r--r--usr.sbin/nginx/src/core/ngx_conf_file.c1505
-rw-r--r--usr.sbin/nginx/src/core/ngx_conf_file.h347
-rw-r--r--usr.sbin/nginx/src/core/ngx_config.h133
-rw-r--r--usr.sbin/nginx/src/core/ngx_connection.c1073
-rw-r--r--usr.sbin/nginx/src/core/ngx_connection.h194
-rw-r--r--usr.sbin/nginx/src/core/ngx_core.h94
-rw-r--r--usr.sbin/nginx/src/core/ngx_cpuinfo.c138
-rw-r--r--usr.sbin/nginx/src/core/ngx_crc.h38
-rw-r--r--usr.sbin/nginx/src/core/ngx_crc32.c128
-rw-r--r--usr.sbin/nginx/src/core/ngx_crc32.h78
-rw-r--r--usr.sbin/nginx/src/core/ngx_crypt.c238
-rw-r--r--usr.sbin/nginx/src/core/ngx_crypt.h19
-rw-r--r--usr.sbin/nginx/src/core/ngx_cycle.c1383
-rw-r--r--usr.sbin/nginx/src/core/ngx_cycle.h141
-rw-r--r--usr.sbin/nginx/src/core/ngx_file.c992
-rw-r--r--usr.sbin/nginx/src/core/ngx_file.h150
-rw-r--r--usr.sbin/nginx/src/core/ngx_hash.c975
-rw-r--r--usr.sbin/nginx/src/core/ngx_hash.h121
-rw-r--r--usr.sbin/nginx/src/core/ngx_inet.c1007
-rw-r--r--usr.sbin/nginx/src/core/ngx_inet.h119
-rw-r--r--usr.sbin/nginx/src/core/ngx_list.c70
-rw-r--r--usr.sbin/nginx/src/core/ngx_list.h82
-rw-r--r--usr.sbin/nginx/src/core/ngx_log.c454
-rw-r--r--usr.sbin/nginx/src/core/ngx_log.h210
-rw-r--r--usr.sbin/nginx/src/core/ngx_md5.c288
-rw-r--r--usr.sbin/nginx/src/core/ngx_md5.h59
-rw-r--r--usr.sbin/nginx/src/core/ngx_murmurhash.c50
-rw-r--r--usr.sbin/nginx/src/core/ngx_murmurhash.h18
-rw-r--r--usr.sbin/nginx/src/core/ngx_open_file_cache.c888
-rw-r--r--usr.sbin/nginx/src/core/ngx_open_file_cache.h118
-rw-r--r--usr.sbin/nginx/src/core/ngx_output_chain.c672
-rw-r--r--usr.sbin/nginx/src/core/ngx_palloc.c432
-rw-r--r--usr.sbin/nginx/src/core/ngx_palloc.h94
-rw-r--r--usr.sbin/nginx/src/core/ngx_parse.c248
-rw-r--r--usr.sbin/nginx/src/core/ngx_parse.h23
-rw-r--r--usr.sbin/nginx/src/core/ngx_queue.c79
-rw-r--r--usr.sbin/nginx/src/core/ngx_queue.h111
-rw-r--r--usr.sbin/nginx/src/core/ngx_radix_tree.c290
-rw-r--r--usr.sbin/nginx/src/core/ngx_radix_tree.h45
-rw-r--r--usr.sbin/nginx/src/core/ngx_rbtree.c382
-rw-r--r--usr.sbin/nginx/src/core/ngx_rbtree.h83
-rw-r--r--usr.sbin/nginx/src/core/ngx_regex.c222
-rw-r--r--usr.sbin/nginx/src/core/ngx_regex.h55
-rw-r--r--usr.sbin/nginx/src/core/ngx_resolver.c2204
-rw-r--r--usr.sbin/nginx/src/core/ngx_resolver.h148
-rw-r--r--usr.sbin/nginx/src/core/ngx_sha1.h30
-rw-r--r--usr.sbin/nginx/src/core/ngx_shmtx.c283
-rw-r--r--usr.sbin/nginx/src/core/ngx_shmtx.h37
-rw-r--r--usr.sbin/nginx/src/core/ngx_slab.c700
-rw-r--r--usr.sbin/nginx/src/core/ngx_slab.h53
-rw-r--r--usr.sbin/nginx/src/core/ngx_spinlock.c52
-rw-r--r--usr.sbin/nginx/src/core/ngx_string.c1807
-rw-r--r--usr.sbin/nginx/src/core/ngx_string.h229
-rw-r--r--usr.sbin/nginx/src/core/ngx_times.c406
-rw-r--r--usr.sbin/nginx/src/core/ngx_times.h50
61 files changed, 21766 insertions, 0 deletions
diff --git a/usr.sbin/nginx/src/core/nginx.c b/usr.sbin/nginx/src/core/nginx.c
new file mode 100644
index 00000000000..b8bab37e3f5
--- /dev/null
+++ b/usr.sbin/nginx/src/core/nginx.c
@@ -0,0 +1,1326 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <nginx.h>
+
+
+static ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);
+static ngx_int_t ngx_get_options(int argc, char *const *argv);
+static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);
+static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);
+static void *ngx_core_module_create_conf(ngx_cycle_t *cycle);
+static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf);
+static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_enum_t ngx_debug_points[] = {
+ { ngx_string("stop"), NGX_DEBUG_POINTS_STOP },
+ { ngx_string("abort"), NGX_DEBUG_POINTS_ABORT },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_command_t ngx_core_commands[] = {
+
+ { ngx_string("daemon"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ 0,
+ offsetof(ngx_core_conf_t, daemon),
+ NULL },
+
+ { ngx_string("master_process"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ 0,
+ offsetof(ngx_core_conf_t, master),
+ NULL },
+
+ { ngx_string("timer_resolution"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ 0,
+ offsetof(ngx_core_conf_t, timer_resolution),
+ NULL },
+
+ { ngx_string("pid"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ 0,
+ offsetof(ngx_core_conf_t, pid),
+ NULL },
+
+ { ngx_string("lock_file"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ 0,
+ offsetof(ngx_core_conf_t, lock_file),
+ NULL },
+
+ { ngx_string("worker_processes"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_core_conf_t, worker_processes),
+ NULL },
+
+ { ngx_string("debug_points"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ 0,
+ offsetof(ngx_core_conf_t, debug_points),
+ &ngx_debug_points },
+
+ { ngx_string("user"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12,
+ ngx_set_user,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("worker_priority"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_set_priority,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("worker_cpu_affinity"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,
+ ngx_set_cpu_affinity,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("worker_rlimit_nofile"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_core_conf_t, rlimit_nofile),
+ NULL },
+
+ { ngx_string("worker_rlimit_core"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_off_slot,
+ 0,
+ offsetof(ngx_core_conf_t, rlimit_core),
+ NULL },
+
+ { ngx_string("worker_rlimit_sigpending"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_core_conf_t, rlimit_sigpending),
+ NULL },
+
+ { ngx_string("working_directory"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_str_slot,
+ 0,
+ offsetof(ngx_core_conf_t, working_directory),
+ NULL },
+
+ { ngx_string("env"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_set_env,
+ 0,
+ 0,
+ NULL },
+
+#if (NGX_THREADS)
+
+ { ngx_string("worker_threads"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_num_slot,
+ 0,
+ offsetof(ngx_core_conf_t, worker_threads),
+ NULL },
+
+ { ngx_string("thread_stack_size"),
+ NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ 0,
+ offsetof(ngx_core_conf_t, thread_stack_size),
+ NULL },
+
+#endif
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_core_module_ctx = {
+ ngx_string("core"),
+ ngx_core_module_create_conf,
+ ngx_core_module_init_conf
+};
+
+
+ngx_module_t ngx_core_module = {
+ NGX_MODULE_V1,
+ &ngx_core_module_ctx, /* module context */
+ ngx_core_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+ngx_uint_t ngx_max_module;
+
+static ngx_uint_t ngx_show_help;
+static ngx_uint_t ngx_show_version;
+static ngx_uint_t ngx_show_configure;
+static u_char *ngx_prefix;
+static u_char *ngx_conf_file;
+static u_char *ngx_conf_params;
+static char *ngx_signal;
+
+
+static char **ngx_os_environ;
+
+
+int ngx_cdecl
+main(int argc, char *const *argv)
+{
+ ngx_int_t i;
+ ngx_log_t *log;
+ ngx_cycle_t *cycle, init_cycle;
+ ngx_core_conf_t *ccf;
+
+ if (ngx_strerror_init() != NGX_OK) {
+ return 1;
+ }
+
+ if (ngx_get_options(argc, argv) != NGX_OK) {
+ return 1;
+ }
+
+ if (ngx_show_version) {
+ ngx_log_stderr(0, "nginx version: " NGINX_VER);
+
+ if (ngx_show_help) {
+ ngx_log_stderr(0,
+ "Usage: nginx [-?hvVtq] [-s signal] [-c filename] "
+ "[-p prefix] [-g directives]" CRLF CRLF
+ "Options:" CRLF
+ " -?,-h : this help" CRLF
+ " -v : show version and exit" CRLF
+ " -V : show version and configure options then exit"
+ CRLF
+ " -t : test configuration and exit" CRLF
+ " -q : suppress non-error messages "
+ "during configuration testing" CRLF
+ " -s signal : send signal to a master process: "
+ "stop, quit, reopen, reload" CRLF
+#ifdef NGX_PREFIX
+ " -p prefix : set prefix path (default: "
+ NGX_PREFIX ")" CRLF
+#else
+ " -p prefix : set prefix path (default: NONE)" CRLF
+#endif
+ " -c filename : set configuration file (default: "
+ NGX_CONF_PATH ")" CRLF
+ " -g directives : set global directives out of configuration "
+ "file" CRLF
+ );
+ }
+
+ if (ngx_show_configure) {
+#ifdef NGX_COMPILER
+ ngx_log_stderr(0, "built by " NGX_COMPILER);
+#endif
+#if (NGX_SSL)
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ ngx_log_stderr(0, "TLS SNI support enabled");
+#else
+ ngx_log_stderr(0, "TLS SNI support disabled");
+#endif
+#endif
+ ngx_log_stderr(0, "configure arguments:" NGX_CONFIGURE);
+ }
+
+ if (!ngx_test_config) {
+ return 0;
+ }
+ }
+
+#if (NGX_FREEBSD)
+ ngx_debug_init();
+#endif
+
+ /* TODO */ ngx_max_sockets = -1;
+
+ ngx_time_init();
+
+#if (NGX_PCRE)
+ ngx_regex_init();
+#endif
+
+ ngx_pid = ngx_getpid();
+
+ log = ngx_log_init(ngx_prefix);
+ if (log == NULL) {
+ return 1;
+ }
+
+ /* STUB */
+#if (NGX_OPENSSL)
+ ngx_ssl_init(log);
+#endif
+
+ /*
+ * init_cycle->log is required for signal handlers and
+ * ngx_process_options()
+ */
+
+ ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
+ init_cycle.log = log;
+ ngx_cycle = &init_cycle;
+
+ init_cycle.pool = ngx_create_pool(1024, log);
+ if (init_cycle.pool == NULL) {
+ return 1;
+ }
+
+ if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {
+ return 1;
+ }
+
+ if (ngx_process_options(&init_cycle) != NGX_OK) {
+ return 1;
+ }
+
+ if (ngx_os_init(log) != NGX_OK) {
+ return 1;
+ }
+
+ /*
+ * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
+ */
+
+ if (ngx_crc32_table_init() != NGX_OK) {
+ return 1;
+ }
+
+ if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
+ return 1;
+ }
+
+ ngx_max_module = 0;
+ for (i = 0; ngx_modules[i]; i++) {
+ ngx_modules[i]->index = ngx_max_module++;
+ }
+
+ cycle = ngx_init_cycle(&init_cycle);
+ if (cycle == NULL) {
+ if (ngx_test_config) {
+ ngx_log_stderr(0, "configuration file %s test failed",
+ init_cycle.conf_file.data);
+ }
+
+ return 1;
+ }
+
+ if (ngx_test_config) {
+ if (!ngx_quiet_mode) {
+ ngx_log_stderr(0, "configuration file %s test is successful",
+ cycle->conf_file.data);
+ }
+
+ return 0;
+ }
+
+ if (ngx_signal) {
+ return ngx_signal_process(cycle, ngx_signal);
+ }
+
+ ngx_os_status(cycle->log);
+
+ ngx_cycle = cycle;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {
+ ngx_process = NGX_PROCESS_MASTER;
+ }
+
+#if !(NGX_WIN32)
+
+ if (ngx_init_signals(cycle->log) != NGX_OK) {
+ return 1;
+ }
+
+ if (!ngx_inherited && ccf->daemon) {
+ if (ngx_daemon(cycle->log) != NGX_OK) {
+ return 1;
+ }
+
+ ngx_daemonized = 1;
+ }
+
+#endif
+
+ if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
+ return 1;
+ }
+
+ if (cycle->log->file->fd != ngx_stderr) {
+
+ if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ ngx_set_stderr_n " failed");
+ return 1;
+ }
+ }
+
+ if (log->file->fd != ngx_stderr) {
+ if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_close_file_n " built-in log failed");
+ }
+ }
+
+ ngx_use_stderr = 0;
+
+ if (ngx_process == NGX_PROCESS_SINGLE) {
+ ngx_single_process_cycle(cycle);
+
+ } else {
+ ngx_master_process_cycle(cycle);
+ }
+
+ return 0;
+}
+
+
+static ngx_int_t
+ngx_add_inherited_sockets(ngx_cycle_t *cycle)
+{
+ u_char *p, *v, *inherited;
+ ngx_int_t s;
+ ngx_listening_t *ls;
+
+ inherited = (u_char *) getenv(NGINX_VAR);
+
+ if (inherited == NULL) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
+ "using inherited sockets from \"%s\"", inherited);
+
+ if (ngx_array_init(&cycle->listening, cycle->pool, 10,
+ sizeof(ngx_listening_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (p = inherited, 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_VAR
+ " environment variable, ignoring the rest"
+ " of the variable", v);
+ break;
+ }
+
+ v = p + 1;
+
+ ls = ngx_array_push(&cycle->listening);
+ if (ls == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_listening_t));
+
+ ls->fd = (ngx_socket_t) s;
+ }
+ }
+
+ ngx_inherited = 1;
+
+ return ngx_set_inherited_sockets(cycle);
+}
+
+
+char **
+ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)
+{
+ char **p, **env;
+ ngx_str_t *var;
+ ngx_uint_t i, n;
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (last == NULL && ccf->environment) {
+ return ccf->environment;
+ }
+
+ var = ccf->env.elts;
+
+ for (i = 0; i < ccf->env.nelts; i++) {
+ if (ngx_strcmp(var[i].data, "TZ") == 0
+ || ngx_strncmp(var[i].data, "TZ=", 3) == 0)
+ {
+ goto tz_found;
+ }
+ }
+
+ var = ngx_array_push(&ccf->env);
+ if (var == NULL) {
+ return NULL;
+ }
+
+ var->len = 2;
+ var->data = (u_char *) "TZ";
+
+ var = ccf->env.elts;
+
+tz_found:
+
+ n = 0;
+
+ for (i = 0; i < ccf->env.nelts; i++) {
+
+ if (var[i].data[var[i].len] == '=') {
+ n++;
+ continue;
+ }
+
+ for (p = ngx_os_environ; *p; p++) {
+
+ if (ngx_strncmp(*p, var[i].data, var[i].len) == 0
+ && (*p)[var[i].len] == '=')
+ {
+ n++;
+ break;
+ }
+ }
+ }
+
+ if (last) {
+ env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log);
+ *last = n;
+
+ } else {
+ env = ngx_palloc(cycle->pool, (n + 1) * sizeof(char *));
+ }
+
+ if (env == NULL) {
+ return NULL;
+ }
+
+ n = 0;
+
+ for (i = 0; i < ccf->env.nelts; i++) {
+
+ if (var[i].data[var[i].len] == '=') {
+ env[n++] = (char *) var[i].data;
+ continue;
+ }
+
+ for (p = ngx_os_environ; *p; p++) {
+
+ if (ngx_strncmp(*p, var[i].data, var[i].len) == 0
+ && (*p)[var[i].len] == '=')
+ {
+ env[n++] = *p;
+ break;
+ }
+ }
+ }
+
+ env[n] = NULL;
+
+ if (last == NULL) {
+ ccf->environment = env;
+ environ = env;
+ }
+
+ return env;
+}
+
+
+ngx_pid_t
+ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
+{
+ char **env, *var;
+ u_char *p;
+ ngx_uint_t i, n;
+ ngx_pid_t pid;
+ ngx_exec_ctx_t ctx;
+ ngx_core_conf_t *ccf;
+ ngx_listening_t *ls;
+
+ ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));
+
+ ctx.path = argv[0];
+ ctx.name = "new binary process";
+ ctx.argv = argv;
+
+ n = 2;
+ env = ngx_set_environment(cycle, &n);
+ if (env == NULL) {
+ return NGX_INVALID_PID;
+ }
+
+ var = ngx_alloc(sizeof(NGINX_VAR)
+ + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,
+ cycle->log);
+
+ p = ngx_cpymem(var, NGINX_VAR "=", sizeof(NGINX_VAR));
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+ p = ngx_sprintf(p, "%ud;", ls[i].fd);
+ }
+
+ *p = '\0';
+
+ env[n++] = var;
+
+#if (NGX_SETPROCTITLE_USES_ENV)
+
+ /* allocate the spare 300 bytes for the new binary process title */
+
+ env[n++] = "SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
+ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
+
+#endif
+
+ env[n] = NULL;
+
+#if (NGX_DEBUG)
+ {
+ char **e;
+ for (e = env; *e; e++) {
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, "env: %s", *e);
+ }
+ }
+#endif
+
+ ctx.envp = (char *const *) env;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_rename_file_n " %s to %s failed "
+ "before executing new binary process \"%s\"",
+ ccf->pid.data, ccf->oldpid.data, argv[0]);
+
+ ngx_free(env);
+ ngx_free(var);
+
+ return NGX_INVALID_PID;
+ }
+
+ pid = ngx_execute(cycle, &ctx);
+
+ if (pid == NGX_INVALID_PID) {
+ if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_rename_file_n " %s back to %s failed after "
+ "the try to execute the new binary process \"%s\"",
+ ccf->oldpid.data, ccf->pid.data, argv[0]);
+ }
+ }
+
+ ngx_free(env);
+ ngx_free(var);
+
+ return pid;
+}
+
+
+static ngx_int_t
+ngx_get_options(int argc, char *const *argv)
+{
+ u_char *p;
+ ngx_int_t i;
+
+ for (i = 1; i < argc; i++) {
+
+ p = (u_char *) argv[i];
+
+ if (*p++ != '-') {
+ ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
+ return NGX_ERROR;
+ }
+
+ while (*p) {
+
+ switch (*p++) {
+
+ case '?':
+ case 'h':
+ ngx_show_version = 1;
+ ngx_show_help = 1;
+ break;
+
+ case 'v':
+ ngx_show_version = 1;
+ break;
+
+ case 'V':
+ ngx_show_version = 1;
+ ngx_show_configure = 1;
+ break;
+
+ case 't':
+ ngx_test_config = 1;
+ break;
+
+ case 'q':
+ ngx_quiet_mode = 1;
+ break;
+
+ case 'p':
+ if (*p) {
+ ngx_prefix = p;
+ goto next;
+ }
+
+ if (argv[++i]) {
+ ngx_prefix = (u_char *) argv[i];
+ goto next;
+ }
+
+ ngx_log_stderr(0, "option \"-p\" requires directory name");
+ return NGX_ERROR;
+
+ case 'c':
+ if (*p) {
+ ngx_conf_file = p;
+ goto next;
+ }
+
+ if (argv[++i]) {
+ ngx_conf_file = (u_char *) argv[i];
+ goto next;
+ }
+
+ ngx_log_stderr(0, "option \"-c\" requires file name");
+ return NGX_ERROR;
+
+ case 'g':
+ if (*p) {
+ ngx_conf_params = p;
+ goto next;
+ }
+
+ if (argv[++i]) {
+ ngx_conf_params = (u_char *) argv[i];
+ goto next;
+ }
+
+ ngx_log_stderr(0, "option \"-g\" requires parameter");
+ return NGX_ERROR;
+
+ case 's':
+ if (*p) {
+ ngx_signal = (char *) p;
+
+ } else if (argv[++i]) {
+ ngx_signal = argv[i];
+
+ } else {
+ ngx_log_stderr(0, "option \"-s\" requires parameter");
+ return NGX_ERROR;
+ }
+
+ if (ngx_strcmp(ngx_signal, "stop") == 0
+ || ngx_strcmp(ngx_signal, "quit") == 0
+ || ngx_strcmp(ngx_signal, "reopen") == 0
+ || ngx_strcmp(ngx_signal, "reload") == 0)
+ {
+ ngx_process = NGX_PROCESS_SIGNALLER;
+ goto next;
+ }
+
+ ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
+ return NGX_ERROR;
+
+ default:
+ ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
+ return NGX_ERROR;
+ }
+ }
+
+ next:
+
+ continue;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv)
+{
+#if (NGX_FREEBSD)
+
+ ngx_os_argv = (char **) argv;
+ ngx_argc = argc;
+ ngx_argv = (char **) argv;
+
+#else
+ size_t len;
+ ngx_int_t i;
+
+ ngx_os_argv = (char **) argv;
+ ngx_argc = argc;
+
+ ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log);
+ if (ngx_argv == NULL) {
+ return NGX_ERROR;
+ }
+
+ for (i = 0; i < argc; i++) {
+ len = ngx_strlen(argv[i]) + 1;
+
+ ngx_argv[i] = ngx_alloc(len, cycle->log);
+ if (ngx_argv[i] == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len);
+ }
+
+ ngx_argv[i] = NULL;
+
+#endif
+
+ ngx_os_environ = environ;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_process_options(ngx_cycle_t *cycle)
+{
+ u_char *p;
+ size_t len;
+
+ if (ngx_prefix) {
+ len = ngx_strlen(ngx_prefix);
+ p = ngx_prefix;
+
+ if (!ngx_path_separator(*p)) {
+ p = ngx_pnalloc(cycle->pool, len + 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(p, ngx_prefix, len);
+ p[len++] = '/';
+ }
+
+ cycle->conf_prefix.len = len;
+ cycle->conf_prefix.data = p;
+ cycle->prefix.len = len;
+ cycle->prefix.data = p;
+
+ } else {
+
+#ifndef NGX_PREFIX
+
+ p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ngx_getcwd(p, NGX_MAX_PATH) == 0) {
+ ngx_log_stderr(ngx_errno, "[emerg]: " ngx_getcwd_n " failed");
+ return NGX_ERROR;
+ }
+
+ len = ngx_strlen(p);
+
+ p[len++] = '/';
+
+ cycle->conf_prefix.len = len;
+ cycle->conf_prefix.data = p;
+ cycle->prefix.len = len;
+ cycle->prefix.data = p;
+
+#else
+
+#ifdef NGX_CONF_PREFIX
+ ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);
+#else
+ ngx_str_set(&cycle->conf_prefix, NGX_PREFIX);
+#endif
+ ngx_str_set(&cycle->prefix, NGX_PREFIX);
+
+#endif
+ }
+
+ if (ngx_conf_file) {
+ cycle->conf_file.len = ngx_strlen(ngx_conf_file);
+ cycle->conf_file.data = ngx_conf_file;
+
+ } else {
+ ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);
+ }
+
+ if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ for (p = cycle->conf_file.data + cycle->conf_file.len - 1;
+ p > cycle->conf_file.data;
+ p--)
+ {
+ if (ngx_path_separator(*p)) {
+ cycle->conf_prefix.len = p - ngx_cycle->conf_file.data + 1;
+ cycle->conf_prefix.data = ngx_cycle->conf_file.data;
+ break;
+ }
+ }
+
+ if (ngx_conf_params) {
+ cycle->conf_param.len = ngx_strlen(ngx_conf_params);
+ cycle->conf_param.data = ngx_conf_params;
+ }
+
+ if (ngx_test_config) {
+ cycle->log->log_level = NGX_LOG_INFO;
+ }
+
+ return NGX_OK;
+}
+
+
+static void *
+ngx_core_module_create_conf(ngx_cycle_t *cycle)
+{
+ ngx_core_conf_t *ccf;
+
+ ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));
+ if (ccf == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_pcalloc()
+ *
+ * ccf->pid = NULL;
+ * ccf->oldpid = NULL;
+ * ccf->priority = 0;
+ * ccf->cpu_affinity_n = 0;
+ * ccf->cpu_affinity = NULL;
+ */
+
+ ccf->daemon = NGX_CONF_UNSET;
+ ccf->master = NGX_CONF_UNSET;
+ ccf->timer_resolution = NGX_CONF_UNSET_MSEC;
+
+ ccf->worker_processes = NGX_CONF_UNSET;
+ ccf->debug_points = NGX_CONF_UNSET;
+
+ ccf->rlimit_nofile = NGX_CONF_UNSET;
+ ccf->rlimit_core = NGX_CONF_UNSET;
+ ccf->rlimit_sigpending = NGX_CONF_UNSET;
+
+ ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;
+ ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;
+
+#if (NGX_THREADS)
+ ccf->worker_threads = NGX_CONF_UNSET;
+ ccf->thread_stack_size = NGX_CONF_UNSET_SIZE;
+#endif
+
+ if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return ccf;
+}
+
+
+static char *
+ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)
+{
+ ngx_core_conf_t *ccf = conf;
+
+ ngx_conf_init_value(ccf->daemon, 1);
+ ngx_conf_init_value(ccf->master, 1);
+ ngx_conf_init_msec_value(ccf->timer_resolution, 0);
+
+ ngx_conf_init_value(ccf->worker_processes, 1);
+ ngx_conf_init_value(ccf->debug_points, 0);
+
+#if (NGX_HAVE_SCHED_SETAFFINITY)
+
+ if (ccf->cpu_affinity_n
+ && ccf->cpu_affinity_n != 1
+ && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes)
+ {
+ ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
+ "number of the \"worker_processes\" is not equal to "
+ "the number of the \"worker_cpu_affinity\" mask, "
+ "using last mask for remaining worker processes");
+ }
+
+#endif
+
+#if (NGX_THREADS)
+
+ ngx_conf_init_value(ccf->worker_threads, 0);
+ ngx_threads_n = ccf->worker_threads;
+ ngx_conf_init_size_value(ccf->thread_stack_size, 2 * 1024 * 1024);
+
+#endif
+
+
+ if (ccf->pid.len == 0) {
+ ngx_str_set(&ccf->pid, NGX_PID_PATH);
+ }
+
+ if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);
+
+ ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);
+ if (ccf->oldpid.data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len),
+ NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT));
+
+
+#if !(NGX_WIN32)
+
+ if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) {
+ struct group *grp;
+ struct passwd *pwd;
+
+ ngx_set_errno(0);
+ pwd = getpwnam(NGX_USER);
+ if (pwd == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "getpwnam(\"" NGX_USER "\") failed");
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->username = NGX_USER;
+ ccf->user = pwd->pw_uid;
+
+ ngx_set_errno(0);
+ grp = getgrnam(NGX_GROUP);
+ if (grp == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "getgrnam(\"" NGX_GROUP "\") failed");
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->group = grp->gr_gid;
+ }
+
+
+ if (ccf->lock_file.len == 0) {
+ ngx_str_set(&ccf->lock_file, NGX_LOCK_PATH);
+ }
+
+ if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ {
+ ngx_str_t lock_file;
+
+ lock_file = cycle->old_cycle->lock_file;
+
+ if (lock_file.len) {
+ lock_file.len--;
+
+ if (ccf->lock_file.len != lock_file.len
+ || ngx_strncmp(ccf->lock_file.data, lock_file.data, lock_file.len)
+ != 0)
+ {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "\"lock_file\" could not be changed, ignored");
+ }
+
+ cycle->lock_file.len = lock_file.len + 1;
+ lock_file.len += sizeof(".accept");
+
+ cycle->lock_file.data = ngx_pstrdup(cycle->pool, &lock_file);
+ if (cycle->lock_file.data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ } else {
+ cycle->lock_file.len = ccf->lock_file.len + 1;
+ cycle->lock_file.data = ngx_pnalloc(cycle->pool,
+ ccf->lock_file.len + sizeof(".accept"));
+ if (cycle->lock_file.data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memcpy(ngx_cpymem(cycle->lock_file.data, ccf->lock_file.data,
+ ccf->lock_file.len),
+ ".accept", sizeof(".accept"));
+ }
+ }
+
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_WIN32)
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"user\" is not supported, ignored");
+
+ return NGX_CONF_OK;
+
+#else
+
+ ngx_core_conf_t *ccf = conf;
+
+ char *group;
+ struct passwd *pwd;
+ struct group *grp;
+ ngx_str_t *value;
+
+ if (ccf->user != (uid_t) NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ if (geteuid() != 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"user\" directive makes sense only "
+ "if the master process runs "
+ "with super-user privileges, ignored");
+ return NGX_CONF_OK;
+ }
+
+ value = (ngx_str_t *) cf->args->elts;
+
+ ccf->username = (char *) value[1].data;
+
+ ngx_set_errno(0);
+ pwd = getpwnam((const char *) value[1].data);
+ if (pwd == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ "getpwnam(\"%s\") failed", value[1].data);
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->user = pwd->pw_uid;
+
+ group = (char *) ((cf->args->nelts == 2) ? value[1].data : value[2].data);
+
+ ngx_set_errno(0);
+ grp = getgrnam(group);
+ if (grp == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ "getgrnam(\"%s\") failed", group);
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->group = grp->gr_gid;
+
+ return NGX_CONF_OK;
+
+#endif
+}
+
+
+static char *
+ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_core_conf_t *ccf = conf;
+
+ ngx_str_t *value, *var;
+ ngx_uint_t i;
+
+ var = ngx_array_push(&ccf->env);
+ if (var == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+ *var = value[1];
+
+ for (i = 0; i < value[1].len; i++) {
+
+ if (value[1].data[i] == '=') {
+
+ var->len = i;
+
+ return NGX_CONF_OK;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_core_conf_t *ccf = conf;
+
+ ngx_str_t *value;
+ ngx_uint_t n, minus;
+
+ if (ccf->priority != 0) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (value[1].data[0] == '-') {
+ n = 1;
+ minus = 1;
+
+ } else if (value[1].data[0] == '+') {
+ n = 1;
+ minus = 0;
+
+ } else {
+ n = 0;
+ minus = 0;
+ }
+
+ ccf->priority = ngx_atoi(&value[1].data[n], value[1].len - n);
+ if (ccf->priority == NGX_ERROR) {
+ return "invalid number";
+ }
+
+ if (minus) {
+ ccf->priority = -ccf->priority;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+#if (NGX_HAVE_SCHED_SETAFFINITY)
+ ngx_core_conf_t *ccf = conf;
+
+ u_char ch;
+ u_long *mask;
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+
+ if (ccf->cpu_affinity) {
+ return "is duplicate";
+ }
+
+ mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(long));
+ if (mask == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ccf->cpu_affinity_n = cf->args->nelts - 1;
+ ccf->cpu_affinity = mask;
+
+ value = cf->args->elts;
+
+ for (n = 1; n < cf->args->nelts; n++) {
+
+ if (value[n].len > 32) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"worker_cpu_affinity\" supports up to 32 CPU only");
+ return NGX_CONF_ERROR;
+ }
+
+ mask[n - 1] = 0;
+
+ for (i = 0; i < value[n].len; i++) {
+
+ ch = value[n].data[i];
+
+ if (ch == ' ') {
+ continue;
+ }
+
+ mask[n - 1] <<= 1;
+
+ if (ch == '0') {
+ continue;
+ }
+
+ if (ch == '1') {
+ mask[n - 1] |= 1;
+ continue;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid character \"%c\" in \"worker_cpu_affinity\"",
+ ch);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+#else
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "\"worker_cpu_affinity\" is not supported "
+ "on this platform, ignored");
+#endif
+
+ return NGX_CONF_OK;
+}
+
+
+u_long
+ngx_get_cpu_affinity(ngx_uint_t n)
+{
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,
+ ngx_core_module);
+
+ if (ccf->cpu_affinity == NULL) {
+ return 0;
+ }
+
+ if (ccf->cpu_affinity_n > n) {
+ return ccf->cpu_affinity[n];
+ }
+
+ return ccf->cpu_affinity[ccf->cpu_affinity_n - 1];
+}
diff --git a/usr.sbin/nginx/src/core/nginx.h b/usr.sbin/nginx/src/core/nginx.h
new file mode 100644
index 00000000000..89cd6843098
--- /dev/null
+++ b/usr.sbin/nginx/src/core/nginx.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGINX_H_INCLUDED_
+#define _NGINX_H_INCLUDED_
+
+
+#define nginx_version 1000006
+#define NGINX_VERSION "1.0.6"
+#define NGINX_VER "nginx/" NGINX_VERSION
+
+#define NGINX_VAR "NGINX"
+#define NGX_OLDPID_EXT ".oldbin"
+
+
+#endif /* _NGINX_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_array.c b/usr.sbin/nginx/src/core/ngx_array.c
new file mode 100644
index 00000000000..a536d87b561
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_array.c
@@ -0,0 +1,146 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_array_t *
+ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)
+{
+ ngx_array_t *a;
+
+ a = ngx_palloc(p, sizeof(ngx_array_t));
+ if (a == NULL) {
+ return NULL;
+ }
+
+ a->elts = ngx_palloc(p, n * size);
+ if (a->elts == NULL) {
+ return NULL;
+ }
+
+ a->nelts = 0;
+ a->size = size;
+ a->nalloc = n;
+ a->pool = p;
+
+ return a;
+}
+
+
+void
+ngx_array_destroy(ngx_array_t *a)
+{
+ ngx_pool_t *p;
+
+ p = a->pool;
+
+ if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {
+ p->d.last -= a->size * a->nalloc;
+ }
+
+ if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {
+ p->d.last = (u_char *) a;
+ }
+}
+
+
+void *
+ngx_array_push(ngx_array_t *a)
+{
+ void *elt, *new;
+ size_t size;
+ ngx_pool_t *p;
+
+ if (a->nelts == a->nalloc) {
+
+ /* the array is full */
+
+ size = a->size * a->nalloc;
+
+ p = a->pool;
+
+ if ((u_char *) a->elts + size == p->d.last
+ && p->d.last + a->size <= p->d.end)
+ {
+ /*
+ * the array allocation is the last in the pool
+ * and there is space for new allocation
+ */
+
+ p->d.last += a->size;
+ a->nalloc++;
+
+ } else {
+ /* allocate a new array */
+
+ new = ngx_palloc(p, 2 * size);
+ if (new == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(new, a->elts, size);
+ a->elts = new;
+ a->nalloc *= 2;
+ }
+ }
+
+ elt = (u_char *) a->elts + a->size * a->nelts;
+ a->nelts++;
+
+ return elt;
+}
+
+
+void *
+ngx_array_push_n(ngx_array_t *a, ngx_uint_t n)
+{
+ void *elt, *new;
+ size_t size;
+ ngx_uint_t nalloc;
+ ngx_pool_t *p;
+
+ size = n * a->size;
+
+ if (a->nelts + n > a->nalloc) {
+
+ /* the array is full */
+
+ p = a->pool;
+
+ if ((u_char *) a->elts + a->size * a->nalloc == p->d.last
+ && p->d.last + size <= p->d.end)
+ {
+ /*
+ * the array allocation is the last in the pool
+ * and there is space for new allocation
+ */
+
+ p->d.last += size;
+ a->nalloc += n;
+
+ } else {
+ /* allocate a new array */
+
+ nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);
+
+ new = ngx_palloc(p, nalloc * a->size);
+ if (new == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(new, a->elts, a->nelts * a->size);
+ a->elts = new;
+ a->nalloc = nalloc;
+ }
+ }
+
+ elt = (u_char *) a->elts + a->size * a->nelts;
+ a->nelts += n;
+
+ return elt;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_array.h b/usr.sbin/nginx/src/core/ngx_array.h
new file mode 100644
index 00000000000..00206cb1be3
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_array.h
@@ -0,0 +1,52 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_ARRAY_H_INCLUDED_
+#define _NGX_ARRAY_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+struct ngx_array_s {
+ void *elts;
+ ngx_uint_t nelts;
+ size_t size;
+ ngx_uint_t nalloc;
+ ngx_pool_t *pool;
+};
+
+
+ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
+void ngx_array_destroy(ngx_array_t *a);
+void *ngx_array_push(ngx_array_t *a);
+void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
+
+
+static ngx_inline ngx_int_t
+ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)
+{
+ /*
+ * set "array->nelts" before "array->elts", otherwise MSVC thinks
+ * that "array->nelts" may be used without having been initialized
+ */
+
+ array->nelts = 0;
+ array->size = size;
+ array->nalloc = n;
+ array->pool = pool;
+
+ array->elts = ngx_palloc(pool, n * size);
+ if (array->elts == NULL) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+#endif /* _NGX_ARRAY_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_buf.c b/usr.sbin/nginx/src/core/ngx_buf.c
new file mode 100644
index 00000000000..2f2c4372185
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_buf.c
@@ -0,0 +1,217 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_buf_t *
+ngx_create_temp_buf(ngx_pool_t *pool, size_t size)
+{
+ ngx_buf_t *b;
+
+ b = ngx_calloc_buf(pool);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->start = ngx_palloc(pool, size);
+ if (b->start == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_calloc_buf():
+ *
+ * b->file_pos = 0;
+ * b->file_last = 0;
+ * b->file = NULL;
+ * b->shadow = NULL;
+ * b->tag = 0;
+ * and flags
+ */
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->last + size;
+ b->temporary = 1;
+
+ return b;
+}
+
+
+ngx_chain_t *
+ngx_alloc_chain_link(ngx_pool_t *pool)
+{
+ ngx_chain_t *cl;
+
+ cl = pool->chain;
+
+ if (cl) {
+ pool->chain = cl->next;
+ return cl;
+ }
+
+ cl = ngx_palloc(pool, sizeof(ngx_chain_t));
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ return cl;
+}
+
+
+ngx_chain_t *
+ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)
+{
+ u_char *p;
+ ngx_int_t i;
+ ngx_buf_t *b;
+ ngx_chain_t *chain, *cl, **ll;
+
+ p = ngx_palloc(pool, bufs->num * bufs->size);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ ll = &chain;
+
+ for (i = 0; i < bufs->num; i++) {
+
+ b = ngx_calloc_buf(pool);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ /*
+ * set by ngx_calloc_buf():
+ *
+ * b->file_pos = 0;
+ * b->file_last = 0;
+ * b->file = NULL;
+ * b->shadow = NULL;
+ * b->tag = 0;
+ * and flags
+ *
+ */
+
+ b->pos = p;
+ b->last = p;
+ b->temporary = 1;
+
+ b->start = p;
+ p += bufs->size;
+ b->end = p;
+
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = b;
+ *ll = cl;
+ ll = &cl->next;
+ }
+
+ *ll = NULL;
+
+ return chain;
+}
+
+
+ngx_int_t
+ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)
+{
+ ngx_chain_t *cl, **ll;
+
+ ll = chain;
+
+ for (cl = *chain; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ while (in) {
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = in->buf;
+ *ll = cl;
+ ll = &cl->next;
+ in = in->next;
+ }
+
+ *ll = NULL;
+
+ return NGX_OK;
+}
+
+
+ngx_chain_t *
+ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)
+{
+ ngx_chain_t *cl;
+
+ if (*free) {
+ cl = *free;
+ *free = cl->next;
+ cl->next = NULL;
+ return cl;
+ }
+
+ cl = ngx_alloc_chain_link(p);
+ if (cl == NULL) {
+ return NULL;
+ }
+
+ cl->buf = ngx_calloc_buf(p);
+ if (cl->buf == NULL) {
+ return NULL;
+ }
+
+ cl->next = NULL;
+
+ return cl;
+}
+
+
+void
+ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
+ ngx_chain_t **out, ngx_buf_tag_t tag)
+{
+ ngx_chain_t *cl;
+
+ if (*busy == NULL) {
+ *busy = *out;
+
+ } else {
+ for (cl = *busy; cl->next; cl = cl->next) { /* void */ }
+
+ cl->next = *out;
+ }
+
+ *out = NULL;
+
+ while (*busy) {
+ if (ngx_buf_size((*busy)->buf) != 0) {
+ break;
+ }
+
+ if ((*busy)->buf->tag != tag) {
+ *busy = (*busy)->next;
+ continue;
+ }
+
+ (*busy)->buf->pos = (*busy)->buf->start;
+ (*busy)->buf->last = (*busy)->buf->start;
+
+ cl = *busy;
+ *busy = cl->next;
+ cl->next = *free;
+ *free = cl;
+ }
+}
diff --git a/usr.sbin/nginx/src/core/ngx_buf.h b/usr.sbin/nginx/src/core/ngx_buf.h
new file mode 100644
index 00000000000..847eaad05d6
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_buf.h
@@ -0,0 +1,161 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_BUF_H_INCLUDED_
+#define _NGX_BUF_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef void * ngx_buf_tag_t;
+
+typedef struct ngx_buf_s ngx_buf_t;
+
+struct ngx_buf_s {
+ u_char *pos;
+ u_char *last;
+ off_t file_pos;
+ off_t file_last;
+
+ u_char *start; /* start of buffer */
+ u_char *end; /* end of buffer */
+ ngx_buf_tag_t tag;
+ ngx_file_t *file;
+ ngx_buf_t *shadow;
+
+
+ /* the buf's content could be changed */
+ unsigned temporary:1;
+
+ /*
+ * the buf's content is in a memory cache or in a read only memory
+ * and must not be changed
+ */
+ unsigned memory:1;
+
+ /* the buf's content is mmap()ed and must not be changed */
+ unsigned mmap:1;
+
+ unsigned recycled:1;
+ unsigned in_file:1;
+ unsigned flush:1;
+ unsigned sync:1;
+ unsigned last_buf:1;
+ unsigned last_in_chain:1;
+
+ unsigned last_shadow:1;
+ unsigned temp_file:1;
+
+ /* STUB */ int num;
+};
+
+
+struct ngx_chain_s {
+ ngx_buf_t *buf;
+ ngx_chain_t *next;
+};
+
+
+typedef struct {
+ ngx_int_t num;
+ size_t size;
+} ngx_bufs_t;
+
+
+typedef struct ngx_output_chain_ctx_s ngx_output_chain_ctx_t;
+
+typedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in);
+
+#if (NGX_HAVE_FILE_AIO)
+typedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx,
+ ngx_file_t *file);
+#endif
+
+struct ngx_output_chain_ctx_s {
+ ngx_buf_t *buf;
+ ngx_chain_t *in;
+ ngx_chain_t *free;
+ ngx_chain_t *busy;
+
+ unsigned sendfile:1;
+ unsigned directio:1;
+#if (NGX_HAVE_ALIGNED_DIRECTIO)
+ unsigned unaligned:1;
+#endif
+ unsigned need_in_memory:1;
+ unsigned need_in_temp:1;
+#if (NGX_HAVE_FILE_AIO)
+ unsigned aio:1;
+
+ ngx_output_chain_aio_pt aio_handler;
+#endif
+
+ off_t alignment;
+
+ ngx_pool_t *pool;
+ ngx_int_t allocated;
+ ngx_bufs_t bufs;
+ ngx_buf_tag_t tag;
+
+ ngx_output_chain_filter_pt output_filter;
+ void *filter_ctx;
+};
+
+
+typedef struct {
+ ngx_chain_t *out;
+ ngx_chain_t **last;
+ ngx_connection_t *connection;
+ ngx_pool_t *pool;
+ off_t limit;
+} ngx_chain_writer_ctx_t;
+
+
+#define NGX_CHAIN_ERROR (ngx_chain_t *) NGX_ERROR
+
+
+#define ngx_buf_in_memory(b) (b->temporary || b->memory || b->mmap)
+#define ngx_buf_in_memory_only(b) (ngx_buf_in_memory(b) && !b->in_file)
+
+#define ngx_buf_special(b) \
+ ((b->flush || b->last_buf || b->sync) \
+ && !ngx_buf_in_memory(b) && !b->in_file)
+
+#define ngx_buf_sync_only(b) \
+ (b->sync \
+ && !ngx_buf_in_memory(b) && !b->in_file && !b->flush && !b->last_buf)
+
+#define ngx_buf_size(b) \
+ (ngx_buf_in_memory(b) ? (off_t) (b->last - b->pos): \
+ (b->file_last - b->file_pos))
+
+ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
+ngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);
+
+
+#define ngx_alloc_buf(pool) ngx_palloc(pool, sizeof(ngx_buf_t))
+#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))
+
+ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);
+#define ngx_free_chain(pool, cl) \
+ cl->next = pool->chain; \
+ pool->chain = cl
+
+
+
+ngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in);
+ngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in);
+
+ngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
+ ngx_chain_t *in);
+ngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free);
+void ngx_chain_update_chains(ngx_chain_t **free, ngx_chain_t **busy,
+ ngx_chain_t **out, ngx_buf_tag_t tag);
+
+
+#endif /* _NGX_BUF_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_conf_file.c b/usr.sbin/nginx/src/core/ngx_conf_file.c
new file mode 100644
index 00000000000..689b7e7a21d
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_conf_file.c
@@ -0,0 +1,1505 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#define NGX_CONF_BUFFER 4096
+
+static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);
+static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);
+static char *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+static ngx_int_t ngx_conf_test_full_name(ngx_str_t *name);
+static void ngx_conf_flush_files(ngx_cycle_t *cycle);
+
+
+static ngx_command_t ngx_conf_commands[] = {
+
+ { ngx_string("include"),
+ NGX_ANY_CONF|NGX_CONF_TAKE1,
+ ngx_conf_include,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+ngx_module_t ngx_conf_module = {
+ NGX_MODULE_V1,
+ NULL, /* module context */
+ ngx_conf_commands, /* module directives */
+ NGX_CONF_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ ngx_conf_flush_files, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+/* The eight fixed arguments */
+
+static ngx_uint_t argument_number[] = {
+ NGX_CONF_NOARGS,
+ NGX_CONF_TAKE1,
+ NGX_CONF_TAKE2,
+ NGX_CONF_TAKE3,
+ NGX_CONF_TAKE4,
+ NGX_CONF_TAKE5,
+ NGX_CONF_TAKE6,
+ NGX_CONF_TAKE7
+};
+
+
+char *
+ngx_conf_param(ngx_conf_t *cf)
+{
+ char *rv;
+ ngx_str_t *param;
+ ngx_buf_t b;
+ ngx_conf_file_t conf_file;
+
+ param = &cf->cycle->conf_param;
+
+ if (param->len == 0) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));
+
+ ngx_memzero(&b, sizeof(ngx_buf_t));
+
+ b.start = param->data;
+ b.pos = param->data;
+ b.last = param->data + param->len;
+ b.end = b.last;
+ b.temporary = 1;
+
+ conf_file.file.fd = NGX_INVALID_FILE;
+ conf_file.file.name.data = NULL;
+ conf_file.line = 0;
+
+ cf->conf_file = &conf_file;
+ cf->conf_file->buffer = &b;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ cf->conf_file = NULL;
+
+ return rv;
+}
+
+
+char *
+ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
+{
+ char *rv;
+ ngx_fd_t fd;
+ ngx_int_t rc;
+ ngx_buf_t buf;
+ ngx_conf_file_t *prev, conf_file;
+ enum {
+ parse_file = 0,
+ parse_block,
+ parse_param
+ } type;
+
+#if (NGX_SUPPRESS_WARN)
+ fd = NGX_INVALID_FILE;
+ prev = NULL;
+#endif
+
+ if (filename) {
+
+ /* open configuration file */
+
+ fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+ if (fd == NGX_INVALID_FILE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ ngx_open_file_n " \"%s\" failed",
+ filename->data);
+ return NGX_CONF_ERROR;
+ }
+
+ prev = cf->conf_file;
+
+ cf->conf_file = &conf_file;
+
+ if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", filename->data);
+ }
+
+ cf->conf_file->buffer = &buf;
+
+ buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
+ if (buf.start == NULL) {
+ goto failed;
+ }
+
+ buf.pos = buf.start;
+ buf.last = buf.start;
+ buf.end = buf.last + NGX_CONF_BUFFER;
+ buf.temporary = 1;
+
+ cf->conf_file->file.fd = fd;
+ cf->conf_file->file.name.len = filename->len;
+ cf->conf_file->file.name.data = filename->data;
+ cf->conf_file->file.offset = 0;
+ cf->conf_file->file.log = cf->log;
+ cf->conf_file->line = 1;
+
+ type = parse_file;
+
+ } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {
+
+ type = parse_block;
+
+ } else {
+ type = parse_param;
+ }
+
+
+ for ( ;; ) {
+ rc = ngx_conf_read_token(cf);
+
+ /*
+ * ngx_conf_read_token() may return
+ *
+ * NGX_ERROR there is error
+ * NGX_OK the token terminated by ";" was found
+ * NGX_CONF_BLOCK_START the token terminated by "{" was found
+ * NGX_CONF_BLOCK_DONE the "}" was found
+ * NGX_CONF_FILE_DONE the configuration file is done
+ */
+
+ if (rc == NGX_ERROR) {
+ goto done;
+ }
+
+ if (rc == NGX_CONF_BLOCK_DONE) {
+
+ if (type != parse_block) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
+ goto failed;
+ }
+
+ goto done;
+ }
+
+ if (rc == NGX_CONF_FILE_DONE) {
+
+ if (type == parse_block) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected end of file, expecting \"}\"");
+ goto failed;
+ }
+
+ goto done;
+ }
+
+ if (rc == NGX_CONF_BLOCK_START) {
+
+ if (type == parse_param) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "block directives are not supported "
+ "in -g option");
+ goto failed;
+ }
+ }
+
+ /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */
+
+ if (cf->handler) {
+
+ /*
+ * the custom handler, i.e., that is used in the http's
+ * "types { ... }" directive
+ */
+
+ rv = (*cf->handler)(cf, NULL, cf->handler_conf);
+ if (rv == NGX_CONF_OK) {
+ continue;
+ }
+
+ if (rv == NGX_CONF_ERROR) {
+ goto failed;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);
+
+ goto failed;
+ }
+
+
+ rc = ngx_conf_handler(cf, rc);
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+ }
+
+failed:
+
+ rc = NGX_ERROR;
+
+done:
+
+ if (filename) {
+ if (cf->conf_file->buffer->start) {
+ ngx_free(cf->conf_file->buffer->start);
+ }
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " %s failed",
+ filename->data);
+ return NGX_CONF_ERROR;
+ }
+
+ cf->conf_file = prev;
+ }
+
+ if (rc == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static ngx_int_t
+ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
+{
+ char *rv;
+ void *conf, **confp;
+ ngx_uint_t i, multi;
+ ngx_str_t *name;
+ ngx_command_t *cmd;
+
+ name = cf->args->elts;
+
+ multi = 0;
+
+ for (i = 0; ngx_modules[i]; i++) {
+
+ /* look up the directive in the appropriate modules */
+
+ if (ngx_modules[i]->type != NGX_CONF_MODULE
+ && ngx_modules[i]->type != cf->module_type)
+ {
+ continue;
+ }
+
+ cmd = ngx_modules[i]->commands;
+ if (cmd == NULL) {
+ continue;
+ }
+
+ for ( /* void */ ; cmd->name.len; cmd++) {
+
+ if (name->len != cmd->name.len) {
+ continue;
+ }
+
+ if (ngx_strcmp(name->data, cmd->name.data) != 0) {
+ continue;
+ }
+
+
+ /* is the directive's location right ? */
+
+ if (!(cmd->type & cf->cmd_type)) {
+ if (cmd->type & NGX_CONF_MULTI) {
+ multi = 1;
+ continue;
+ }
+
+ goto not_allowed;
+ }
+
+ if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "directive \"%s\" is not terminated by \";\"",
+ name->data);
+ return NGX_ERROR;
+ }
+
+ if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "directive \"%s\" has no opening \"{\"",
+ name->data);
+ return NGX_ERROR;
+ }
+
+ /* is the directive's argument count right ? */
+
+ if (!(cmd->type & NGX_CONF_ANY)) {
+
+ if (cmd->type & NGX_CONF_FLAG) {
+
+ if (cf->args->nelts != 2) {
+ goto invalid;
+ }
+
+ } else if (cmd->type & NGX_CONF_1MORE) {
+
+ if (cf->args->nelts < 2) {
+ goto invalid;
+ }
+
+ } else if (cmd->type & NGX_CONF_2MORE) {
+
+ if (cf->args->nelts < 3) {
+ goto invalid;
+ }
+
+ } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {
+
+ goto invalid;
+
+ } else if (!(cmd->type & argument_number[cf->args->nelts - 1]))
+ {
+ goto invalid;
+ }
+ }
+
+ /* set up the directive's configuration context */
+
+ conf = NULL;
+
+ if (cmd->type & NGX_DIRECT_CONF) {
+ conf = ((void **) cf->ctx)[ngx_modules[i]->index];
+
+ } else if (cmd->type & NGX_MAIN_CONF) {
+ conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);
+
+ } else if (cf->ctx) {
+ confp = *(void **) ((char *) cf->ctx + cmd->conf);
+
+ if (confp) {
+ conf = confp[ngx_modules[i]->ctx_index];
+ }
+ }
+
+ rv = cmd->set(cf, cmd, conf);
+
+ if (rv == NGX_CONF_OK) {
+ return NGX_OK;
+ }
+
+ if (rv == NGX_CONF_ERROR) {
+ return NGX_ERROR;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%s\" directive %s", name->data, rv);
+
+ return NGX_ERROR;
+ }
+ }
+
+ if (multi == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown directive \"%s\"", name->data);
+
+ return NGX_ERROR;
+ }
+
+not_allowed:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "\"%s\" directive is not allowed here", name->data);
+ return NGX_ERROR;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid number of arguments in \"%s\" directive",
+ name->data);
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_conf_read_token(ngx_conf_t *cf)
+{
+ u_char *start, ch, *src, *dst;
+ off_t file_size;
+ size_t len;
+ ssize_t n, size;
+ ngx_uint_t found, need_space, last_space, sharp_comment, variable;
+ ngx_uint_t quoted, s_quoted, d_quoted, start_line;
+ ngx_str_t *word;
+ ngx_buf_t *b;
+
+ found = 0;
+ need_space = 0;
+ last_space = 1;
+ sharp_comment = 0;
+ variable = 0;
+ quoted = 0;
+ s_quoted = 0;
+ d_quoted = 0;
+
+ cf->args->nelts = 0;
+ b = cf->conf_file->buffer;
+ start = b->pos;
+ start_line = cf->conf_file->line;
+
+ file_size = ngx_file_size(&cf->conf_file->file.info);
+
+ for ( ;; ) {
+
+ if (b->pos >= b->last) {
+
+ if (cf->conf_file->file.offset >= file_size) {
+
+ if (cf->args->nelts > 0) {
+
+ if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected end of parameter, "
+ "expecting \";\"");
+ return NGX_ERROR;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected end of file, "
+ "expecting \";\" or \"}\"");
+ return NGX_ERROR;
+ }
+
+ return NGX_CONF_FILE_DONE;
+ }
+
+ len = b->pos - start;
+
+ if (len == NGX_CONF_BUFFER) {
+ cf->conf_file->line = start_line;
+
+ if (d_quoted) {
+ ch = '"';
+
+ } else if (s_quoted) {
+ ch = '\'';
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "too long parameter \"%*s...\" started",
+ 10, start);
+ return NGX_ERROR;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "too long parameter, probably "
+ "missing terminating \"%c\" character", ch);
+ return NGX_ERROR;
+ }
+
+ if (len) {
+ ngx_memmove(b->start, start, len);
+ }
+
+ size = (ssize_t) (file_size - cf->conf_file->file.offset);
+
+ if (size > b->end - (b->start + len)) {
+ size = b->end - (b->start + len);
+ }
+
+ n = ngx_read_file(&cf->conf_file->file, b->start + len, size,
+ cf->conf_file->file.offset);
+
+ if (n == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (n != size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ ngx_read_file_n " returned "
+ "only %z bytes instead of %z",
+ n, size);
+ return NGX_ERROR;
+ }
+
+ b->pos = b->start + len;
+ b->last = b->pos + n;
+ start = b->start;
+ }
+
+ ch = *b->pos++;
+
+ if (ch == LF) {
+ cf->conf_file->line++;
+
+ if (sharp_comment) {
+ sharp_comment = 0;
+ }
+ }
+
+ if (sharp_comment) {
+ continue;
+ }
+
+ if (quoted) {
+ quoted = 0;
+ continue;
+ }
+
+ if (need_space) {
+ if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
+ last_space = 1;
+ need_space = 0;
+ continue;
+ }
+
+ if (ch == ';') {
+ return NGX_OK;
+ }
+
+ if (ch == '{') {
+ return NGX_CONF_BLOCK_START;
+ }
+
+ if (ch == ')') {
+ last_space = 1;
+ need_space = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected \"%c\"", ch);
+ return NGX_ERROR;
+ }
+ }
+
+ if (last_space) {
+ if (ch == ' ' || ch == '\t' || ch == CR || ch == LF) {
+ continue;
+ }
+
+ start = b->pos - 1;
+ start_line = cf->conf_file->line;
+
+ switch (ch) {
+
+ case ';':
+ case '{':
+ if (cf->args->nelts == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected \"%c\"", ch);
+ return NGX_ERROR;
+ }
+
+ if (ch == '{') {
+ return NGX_CONF_BLOCK_START;
+ }
+
+ return NGX_OK;
+
+ case '}':
+ if (cf->args->nelts != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unexpected \"}\"");
+ return NGX_ERROR;
+ }
+
+ return NGX_CONF_BLOCK_DONE;
+
+ case '#':
+ sharp_comment = 1;
+ continue;
+
+ case '\\':
+ quoted = 1;
+ last_space = 0;
+ continue;
+
+ case '"':
+ start++;
+ d_quoted = 1;
+ last_space = 0;
+ continue;
+
+ case '\'':
+ start++;
+ s_quoted = 1;
+ last_space = 0;
+ continue;
+
+ default:
+ last_space = 0;
+ }
+
+ } else {
+ if (ch == '{' && variable) {
+ continue;
+ }
+
+ variable = 0;
+
+ if (ch == '\\') {
+ quoted = 1;
+ continue;
+ }
+
+ if (ch == '$') {
+ variable = 1;
+ continue;
+ }
+
+ if (d_quoted) {
+ if (ch == '"') {
+ d_quoted = 0;
+ need_space = 1;
+ found = 1;
+ }
+
+ } else if (s_quoted) {
+ if (ch == '\'') {
+ s_quoted = 0;
+ need_space = 1;
+ found = 1;
+ }
+
+ } else if (ch == ' ' || ch == '\t' || ch == CR || ch == LF
+ || ch == ';' || ch == '{')
+ {
+ last_space = 1;
+ found = 1;
+ }
+
+ if (found) {
+ word = ngx_array_push(cf->args);
+ if (word == NULL) {
+ return NGX_ERROR;
+ }
+
+ word->data = ngx_pnalloc(cf->pool, b->pos - start + 1);
+ if (word->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ for (dst = word->data, src = start, len = 0;
+ src < b->pos - 1;
+ len++)
+ {
+ if (*src == '\\') {
+ switch (src[1]) {
+ case '"':
+ case '\'':
+ case '\\':
+ src++;
+ break;
+
+ case 't':
+ *dst++ = '\t';
+ src += 2;
+ continue;
+
+ case 'r':
+ *dst++ = '\r';
+ src += 2;
+ continue;
+
+ case 'n':
+ *dst++ = '\n';
+ src += 2;
+ continue;
+ }
+
+ }
+ *dst++ = *src++;
+ }
+ *dst = '\0';
+ word->len = len;
+
+ if (ch == ';') {
+ return NGX_OK;
+ }
+
+ if (ch == '{') {
+ return NGX_CONF_BLOCK_START;
+ }
+
+ found = 0;
+ }
+ }
+ }
+}
+
+
+static char *
+ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_int_t n;
+ ngx_str_t *value, file, name;
+ ngx_glob_t gl;
+
+ value = cf->args->elts;
+ file = value[1];
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (strpbrk((char *) file.data, "*?[") == NULL) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ return ngx_conf_parse(cf, &file);
+ }
+
+ ngx_memzero(&gl, sizeof(ngx_glob_t));
+
+ gl.pattern = file.data;
+ gl.log = cf->log;
+ gl.test = 1;
+
+ if (ngx_open_glob(&gl) != NGX_OK) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
+ ngx_open_glob_n " \"%s\" failed", file.data);
+ return NGX_CONF_ERROR;
+ }
+
+ rv = NGX_CONF_OK;
+
+ for ( ;; ) {
+ n = ngx_read_glob(&gl, &name);
+
+ if (n != NGX_OK) {
+ break;
+ }
+
+ file.len = name.len++;
+ file.data = ngx_pstrdup(cf->pool, &name);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, "include %s", file.data);
+
+ rv = ngx_conf_parse(cf, &file);
+
+ if (rv != NGX_CONF_OK) {
+ break;
+ }
+ }
+
+ ngx_close_glob(&gl);
+
+ return rv;
+}
+
+
+ngx_int_t
+ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix)
+{
+ size_t len;
+ u_char *p, *n, *prefix;
+ ngx_int_t rc;
+
+ rc = ngx_conf_test_full_name(name);
+
+ if (rc == NGX_OK) {
+ return rc;
+ }
+
+ if (conf_prefix) {
+ len = cycle->conf_prefix.len;
+ prefix = cycle->conf_prefix.data;
+
+ } else {
+ len = cycle->prefix.len;
+ prefix = cycle->prefix.data;
+ }
+
+#if (NGX_WIN32)
+
+ if (rc == 2) {
+ len = rc;
+ }
+
+#endif
+
+ n = ngx_pnalloc(cycle->pool, len + name->len + 1);
+ if (n == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(n, prefix, len);
+ ngx_cpystrn(p, name->data, name->len + 1);
+
+ name->len += len;
+ name->data = n;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_conf_test_full_name(ngx_str_t *name)
+{
+#if (NGX_WIN32)
+ u_char c0, c1;
+
+ c0 = name->data[0];
+
+ if (name->len < 2) {
+ if (c0 == '/') {
+ return 2;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ c1 = name->data[1];
+
+ if (c1 == ':') {
+ c0 |= 0x20;
+
+ if ((c0 >= 'a' && c0 <= 'z')) {
+ return NGX_OK;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ if (c1 == '/') {
+ return NGX_OK;
+ }
+
+ if (c0 == '/') {
+ return 2;
+ }
+
+ return NGX_DECLINED;
+
+#else
+
+ if (name->data[0] == '/') {
+ return NGX_OK;
+ }
+
+ return NGX_DECLINED;
+
+#endif
+}
+
+
+ngx_open_file_t *
+ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name)
+{
+ ngx_str_t full;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_open_file_t *file;
+
+#if (NGX_SUPPRESS_WARN)
+ ngx_str_null(&full);
+#endif
+
+ if (name->len) {
+ full = *name;
+
+ if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {
+ return NULL;
+ }
+
+ part = &cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ if (full.len != file[i].name.len) {
+ continue;
+ }
+
+ if (ngx_strcmp(full.data, file[i].name.data) == 0) {
+ return &file[i];
+ }
+ }
+ }
+
+ file = ngx_list_push(&cycle->open_files);
+ if (file == NULL) {
+ return NULL;
+ }
+
+ if (name->len) {
+ file->fd = NGX_INVALID_FILE;
+ file->name = full;
+
+ } else {
+ file->fd = ngx_stderr;
+ file->name = *name;
+ }
+
+ file->buffer = NULL;
+
+ return file;
+}
+
+
+static void
+ngx_conf_flush_files(ngx_cycle_t *cycle)
+{
+ ssize_t n, len;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_open_file_t *file;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, "flush files");
+
+ part = &cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ len = file[i].pos - file[i].buffer;
+
+ if (file[i].buffer == NULL || len == 0) {
+ continue;
+ }
+
+ n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+ if (n == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_write_fd_n " to \"%s\" failed",
+ file[i].name.data);
+
+ } else if (n != len) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+ file[i].name.data, n, len);
+ }
+ }
+}
+
+
+void ngx_cdecl
+ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err,
+ const char *fmt, ...)
+{
+ u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last;
+ va_list args;
+
+ last = errstr + NGX_MAX_CONF_ERRSTR;
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(errstr, last, fmt, args);
+ va_end(args);
+
+ if (err) {
+ p = ngx_log_errno(p, last, err);
+ }
+
+ if (cf->conf_file == NULL) {
+ ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr);
+ return;
+ }
+
+ if (cf->conf_file->file.fd == NGX_INVALID_FILE) {
+ ngx_log_error(level, cf->log, 0, "%*s in command line",
+ p - errstr, errstr);
+ return;
+ }
+
+ ngx_log_error(level, cf->log, 0, "%*s in %s:%ui",
+ p - errstr, errstr,
+ cf->conf_file->file.name.data, cf->conf_file->line);
+}
+
+
+char *
+ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_flag_t *fp;
+ ngx_conf_post_t *post;
+
+ fp = (ngx_flag_t *) (p + cmd->offset);
+
+ if (*fp != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) {
+ *fp = 1;
+
+ } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) {
+ *fp = 0;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%s\" in \"%s\" directive, "
+ "it must be \"on\" or \"off\"",
+ value[1].data, cmd->name.data);
+ return NGX_CONF_ERROR;
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, fp);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *field, *value;
+ ngx_conf_post_t *post;
+
+ field = (ngx_str_t *) (p + cmd->offset);
+
+ if (field->data) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *field = value[1];
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, field);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value, *s;
+ ngx_array_t **a;
+ ngx_conf_post_t *post;
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NGX_CONF_UNSET_PTR) {
+ *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ s = ngx_array_push(*a);
+ if (s == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ *s = value[1];
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, s);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_array_t **a;
+ ngx_keyval_t *kv;
+ ngx_conf_post_t *post;
+
+ a = (ngx_array_t **) (p + cmd->offset);
+
+ if (*a == NULL) {
+ *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t));
+ if (*a == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ kv = ngx_array_push(*a);
+ if (kv == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ kv->key = value[1];
+ kv->value = value[2];
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, kv);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_int_t *np;
+ ngx_str_t *value;
+ ngx_conf_post_t *post;
+
+
+ np = (ngx_int_t *) (p + cmd->offset);
+
+ if (*np != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+ *np = ngx_atoi(value[1].data, value[1].len);
+ if (*np == NGX_ERROR) {
+ return "invalid number";
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, np);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ size_t *sp;
+ ngx_str_t *value;
+ ngx_conf_post_t *post;
+
+
+ sp = (size_t *) (p + cmd->offset);
+ if (*sp != NGX_CONF_UNSET_SIZE) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *sp = ngx_parse_size(&value[1]);
+ if (*sp == (size_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, sp);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ off_t *op;
+ ngx_str_t *value;
+ ngx_conf_post_t *post;
+
+
+ op = (off_t *) (p + cmd->offset);
+ if (*op != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *op = ngx_parse_offset(&value[1]);
+ if (*op == (off_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, op);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_msec_t *msp;
+ ngx_str_t *value;
+ ngx_conf_post_t *post;
+
+
+ msp = (ngx_msec_t *) (p + cmd->offset);
+ if (*msp != NGX_CONF_UNSET_MSEC) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *msp = ngx_parse_time(&value[1], 0);
+ if (*msp == (ngx_msec_t) NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (*msp == (ngx_msec_t) NGX_PARSE_LARGE_TIME) {
+ return "value must be less than 597 hours";
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, msp);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ time_t *sp;
+ ngx_str_t *value;
+ ngx_conf_post_t *post;
+
+
+ sp = (time_t *) (p + cmd->offset);
+ if (*sp != NGX_CONF_UNSET) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *sp = ngx_parse_time(&value[1], 1);
+ if (*sp == NGX_ERROR) {
+ return "invalid value";
+ }
+
+ if (*sp == NGX_PARSE_LARGE_TIME) {
+ return "value must be less than 68 years";
+ }
+
+ if (cmd->post) {
+ post = cmd->post;
+ return post->post_handler(cf, post, sp);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *value;
+ ngx_bufs_t *bufs;
+
+
+ bufs = (ngx_bufs_t *) (p + cmd->offset);
+ if (bufs->num) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ bufs->num = ngx_atoi(value[1].data, value[1].len);
+ if (bufs->num == NGX_ERROR || bufs->num == 0) {
+ return "invalid value";
+ }
+
+ bufs->size = ngx_parse_size(&value[2]);
+ if (bufs->size == (size_t) NGX_ERROR || bufs->size == 0) {
+ return "invalid value";
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_uint_t *np, i;
+ ngx_str_t *value;
+ ngx_conf_enum_t *e;
+
+ np = (ngx_uint_t *) (p + cmd->offset);
+
+ if (*np != NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+ e = cmd->post;
+
+ for (i = 0; e[i].name.len != 0; i++) {
+ if (e[i].name.len != value[1].len
+ || ngx_strcasecmp(e[i].name.data, value[1].data) != 0)
+ {
+ continue;
+ }
+
+ *np = e[i].value;
+
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "invalid value \"%s\"", value[1].data);
+
+ return NGX_CONF_ERROR;
+}
+
+
+char *
+ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_uint_t *np, i, m;
+ ngx_str_t *value;
+ ngx_conf_bitmask_t *mask;
+
+
+ np = (ngx_uint_t *) (p + cmd->offset);
+ value = cf->args->elts;
+ mask = cmd->post;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ for (m = 0; mask[m].name.len != 0; m++) {
+
+ if (mask[m].name.len != value[i].len
+ || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)
+ {
+ continue;
+ }
+
+ if (*np & mask[m].mask) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "duplicate value \"%s\"", value[i].data);
+
+ } else {
+ *np |= mask[m].mask;
+ }
+
+ break;
+ }
+
+ if (mask[m].name.len == 0) {
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "invalid value \"%s\"", value[i].data);
+
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_unsupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ return "unsupported on this platform";
+}
+
+
+char *
+ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_conf_deprecated_t *d = post;
+
+ ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
+ "the \"%s\" directive is deprecated, "
+ "use the \"%s\" directive instead",
+ d->old_name, d->new_name);
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data)
+{
+ ngx_conf_num_bounds_t *bounds = post;
+ ngx_int_t *np = data;
+
+ if (bounds->high == -1) {
+ if (*np >= bounds->low) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "value must be equal or more than %i", bounds->low);
+
+ return NGX_CONF_ERROR;
+ }
+
+ if (*np >= bounds->low && *np <= bounds->high) {
+ return NGX_CONF_OK;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "value must be between %i and %i",
+ bounds->low, bounds->high);
+
+ return NGX_CONF_ERROR;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_conf_file.h b/usr.sbin/nginx/src/core/ngx_conf_file.h
new file mode 100644
index 00000000000..86c60a43c2c
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_conf_file.h
@@ -0,0 +1,347 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_HTTP_CONF_FILE_H_INCLUDED_
+#define _NGX_HTTP_CONF_FILE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * AAAA number of agruments
+ * FF command flags
+ * TT command type, i.e. HTTP "location" or "server" command
+ */
+
+#define NGX_CONF_NOARGS 0x00000001
+#define NGX_CONF_TAKE1 0x00000002
+#define NGX_CONF_TAKE2 0x00000004
+#define NGX_CONF_TAKE3 0x00000008
+#define NGX_CONF_TAKE4 0x00000010
+#define NGX_CONF_TAKE5 0x00000020
+#define NGX_CONF_TAKE6 0x00000040
+#define NGX_CONF_TAKE7 0x00000080
+
+#define NGX_CONF_MAX_ARGS 8
+
+#define NGX_CONF_TAKE12 (NGX_CONF_TAKE1|NGX_CONF_TAKE2)
+#define NGX_CONF_TAKE13 (NGX_CONF_TAKE1|NGX_CONF_TAKE3)
+
+#define NGX_CONF_TAKE23 (NGX_CONF_TAKE2|NGX_CONF_TAKE3)
+
+#define NGX_CONF_TAKE123 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)
+#define NGX_CONF_TAKE1234 (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3 \
+ |NGX_CONF_TAKE4)
+
+#define NGX_CONF_ARGS_NUMBER 0x000000ff
+#define NGX_CONF_BLOCK 0x00000100
+#define NGX_CONF_FLAG 0x00000200
+#define NGX_CONF_ANY 0x00000400
+#define NGX_CONF_1MORE 0x00000800
+#define NGX_CONF_2MORE 0x00001000
+#define NGX_CONF_MULTI 0x00002000
+
+#define NGX_DIRECT_CONF 0x00010000
+
+#define NGX_MAIN_CONF 0x01000000
+#define NGX_ANY_CONF 0x0F000000
+
+
+
+#define NGX_CONF_UNSET -1
+#define NGX_CONF_UNSET_UINT (ngx_uint_t) -1
+#define NGX_CONF_UNSET_PTR (void *) -1
+#define NGX_CONF_UNSET_SIZE (size_t) -1
+#define NGX_CONF_UNSET_MSEC (ngx_msec_t) -1
+
+
+#define NGX_CONF_OK NULL
+#define NGX_CONF_ERROR (void *) -1
+
+#define NGX_CONF_BLOCK_START 1
+#define NGX_CONF_BLOCK_DONE 2
+#define NGX_CONF_FILE_DONE 3
+
+#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */
+#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */
+
+
+#define NGX_MAX_CONF_ERRSTR 1024
+
+
+struct ngx_command_s {
+ ngx_str_t name;
+ ngx_uint_t type;
+ char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+ ngx_uint_t conf;
+ ngx_uint_t offset;
+ void *post;
+};
+
+#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
+
+
+struct ngx_open_file_s {
+ ngx_fd_t fd;
+ ngx_str_t name;
+
+ u_char *buffer;
+ u_char *pos;
+ u_char *last;
+
+#if 0
+ /* e.g. append mode, error_log */
+ ngx_uint_t flags;
+ /* e.g. reopen db file */
+ ngx_uint_t (*handler)(void *data, ngx_open_file_t *file);
+ void *data;
+#endif
+};
+
+
+#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
+#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0
+
+struct ngx_module_s {
+ ngx_uint_t ctx_index;
+ ngx_uint_t index;
+
+ ngx_uint_t spare0;
+ ngx_uint_t spare1;
+ ngx_uint_t spare2;
+ ngx_uint_t spare3;
+
+ ngx_uint_t version;
+
+ void *ctx;
+ ngx_command_t *commands;
+ ngx_uint_t type;
+
+ ngx_int_t (*init_master)(ngx_log_t *log);
+
+ ngx_int_t (*init_module)(ngx_cycle_t *cycle);
+
+ ngx_int_t (*init_process)(ngx_cycle_t *cycle);
+ ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
+ void (*exit_thread)(ngx_cycle_t *cycle);
+ void (*exit_process)(ngx_cycle_t *cycle);
+
+ void (*exit_master)(ngx_cycle_t *cycle);
+
+ uintptr_t spare_hook0;
+ uintptr_t spare_hook1;
+ uintptr_t spare_hook2;
+ uintptr_t spare_hook3;
+ uintptr_t spare_hook4;
+ uintptr_t spare_hook5;
+ uintptr_t spare_hook6;
+ uintptr_t spare_hook7;
+};
+
+
+typedef struct {
+ ngx_str_t name;
+ void *(*create_conf)(ngx_cycle_t *cycle);
+ char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
+} ngx_core_module_t;
+
+
+typedef struct {
+ ngx_file_t file;
+ ngx_buf_t *buffer;
+ ngx_uint_t line;
+} ngx_conf_file_t;
+
+
+typedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf,
+ ngx_command_t *dummy, void *conf);
+
+
+struct ngx_conf_s {
+ char *name;
+ ngx_array_t *args;
+
+ ngx_cycle_t *cycle;
+ ngx_pool_t *pool;
+ ngx_pool_t *temp_pool;
+ ngx_conf_file_t *conf_file;
+ ngx_log_t *log;
+
+ void *ctx;
+ ngx_uint_t module_type;
+ ngx_uint_t cmd_type;
+
+ ngx_conf_handler_pt handler;
+ char *handler_conf;
+};
+
+
+typedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf,
+ void *data, void *conf);
+
+typedef struct {
+ ngx_conf_post_handler_pt post_handler;
+} ngx_conf_post_t;
+
+
+typedef struct {
+ ngx_conf_post_handler_pt post_handler;
+ char *old_name;
+ char *new_name;
+} ngx_conf_deprecated_t;
+
+
+typedef struct {
+ ngx_conf_post_handler_pt post_handler;
+ ngx_int_t low;
+ ngx_int_t high;
+} ngx_conf_num_bounds_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t value;
+} ngx_conf_enum_t;
+
+
+#define NGX_CONF_BITMASK_SET 1
+
+typedef struct {
+ ngx_str_t name;
+ ngx_uint_t mask;
+} ngx_conf_bitmask_t;
+
+
+
+char * ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data);
+char *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data);
+
+
+#define ngx_get_conf(conf_ctx, module) conf_ctx[module.index]
+
+
+
+#define ngx_conf_init_value(conf, default) \
+ if (conf == NGX_CONF_UNSET) { \
+ conf = default; \
+ }
+
+#define ngx_conf_init_ptr_value(conf, default) \
+ if (conf == NGX_CONF_UNSET_PTR) { \
+ conf = default; \
+ }
+
+#define ngx_conf_init_uint_value(conf, default) \
+ if (conf == NGX_CONF_UNSET_UINT) { \
+ conf = default; \
+ }
+
+#define ngx_conf_init_size_value(conf, default) \
+ if (conf == NGX_CONF_UNSET_SIZE) { \
+ conf = default; \
+ }
+
+#define ngx_conf_init_msec_value(conf, default) \
+ if (conf == NGX_CONF_UNSET_MSEC) { \
+ conf = default; \
+ }
+
+#define ngx_conf_merge_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET) { \
+ conf = (prev == NGX_CONF_UNSET) ? default : prev; \
+ }
+
+#define ngx_conf_merge_ptr_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET_PTR) { \
+ conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev; \
+ }
+
+#define ngx_conf_merge_uint_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET_UINT) { \
+ conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev; \
+ }
+
+#define ngx_conf_merge_msec_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET_MSEC) { \
+ conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev; \
+ }
+
+#define ngx_conf_merge_sec_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET) { \
+ conf = (prev == NGX_CONF_UNSET) ? default : prev; \
+ }
+
+#define ngx_conf_merge_size_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET_SIZE) { \
+ conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev; \
+ }
+
+#define ngx_conf_merge_off_value(conf, prev, default) \
+ if (conf == NGX_CONF_UNSET) { \
+ conf = (prev == NGX_CONF_UNSET) ? default : prev; \
+ }
+
+#define ngx_conf_merge_str_value(conf, prev, default) \
+ if (conf.data == NULL) { \
+ if (prev.data) { \
+ conf.len = prev.len; \
+ conf.data = prev.data; \
+ } else { \
+ conf.len = sizeof(default) - 1; \
+ conf.data = (u_char *) default; \
+ } \
+ }
+
+#define ngx_conf_merge_bufs_value(conf, prev, default_num, default_size) \
+ if (conf.num == 0) { \
+ if (prev.num) { \
+ conf.num = prev.num; \
+ conf.size = prev.size; \
+ } else { \
+ conf.num = default_num; \
+ conf.size = default_size; \
+ } \
+ }
+
+#define ngx_conf_merge_bitmask_value(conf, prev, default) \
+ if (conf == 0) { \
+ conf = (prev == 0) ? default : prev; \
+ }
+
+
+char *ngx_conf_param(ngx_conf_t *cf);
+char *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);
+
+
+ngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name,
+ ngx_uint_t conf_prefix);
+ngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name);
+void ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf,
+ ngx_err_t err, const char *fmt, ...);
+
+
+char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+char *ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+extern ngx_uint_t ngx_max_module;
+extern ngx_module_t *ngx_modules[];
+
+
+#endif /* _NGX_HTTP_CONF_FILE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_config.h b/usr.sbin/nginx/src/core/ngx_config.h
new file mode 100644
index 00000000000..9762b18ca06
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_config.h
@@ -0,0 +1,133 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CONFIG_H_INCLUDED_
+#define _NGX_CONFIG_H_INCLUDED_
+
+
+#include <ngx_auto_headers.h>
+
+
+#if defined __DragonFly__ && !defined __FreeBSD__
+#define __FreeBSD__ 4
+#define __FreeBSD_version 480101
+#endif
+
+
+#if (NGX_FREEBSD)
+#include <ngx_freebsd_config.h>
+
+
+#elif (NGX_LINUX)
+#include <ngx_linux_config.h>
+
+
+#elif (NGX_SOLARIS)
+#include <ngx_solaris_config.h>
+
+
+#elif (NGX_DARWIN)
+#include <ngx_darwin_config.h>
+
+
+#elif (NGX_WIN32)
+#include <ngx_win32_config.h>
+
+
+#else /* POSIX */
+#include <ngx_posix_config.h>
+
+#endif
+
+
+#ifndef NGX_HAVE_SO_SNDLOWAT
+#define NGX_HAVE_SO_SNDLOWAT 1
+#endif
+
+
+#if !(NGX_WIN32)
+
+#define ngx_signal_helper(n) SIG##n
+#define ngx_signal_value(n) ngx_signal_helper(n)
+
+#define ngx_random random
+
+/* TODO: #ifndef */
+#define NGX_SHUTDOWN_SIGNAL QUIT
+#define NGX_TERMINATE_SIGNAL TERM
+#define NGX_NOACCEPT_SIGNAL WINCH
+#define NGX_RECONFIGURE_SIGNAL HUP
+
+#if (NGX_LINUXTHREADS)
+#define NGX_REOPEN_SIGNAL INFO
+#define NGX_CHANGEBIN_SIGNAL XCPU
+#else
+#define NGX_REOPEN_SIGNAL USR1
+#define NGX_CHANGEBIN_SIGNAL USR2
+#endif
+
+#define ngx_cdecl
+#define ngx_libc_cdecl
+
+#endif
+
+typedef intptr_t ngx_int_t;
+typedef uintptr_t ngx_uint_t;
+typedef intptr_t ngx_flag_t;
+
+
+#define NGX_INT32_LEN sizeof("-2147483648") - 1
+#define NGX_INT64_LEN sizeof("-9223372036854775808") - 1
+
+#if (NGX_PTR_SIZE == 4)
+#define NGX_INT_T_LEN NGX_INT32_LEN
+#else
+#define NGX_INT_T_LEN NGX_INT64_LEN
+#endif
+
+
+#ifndef NGX_ALIGNMENT
+#define NGX_ALIGNMENT sizeof(unsigned long) /* platform word */
+#endif
+
+#define ngx_align(d, a) (((d) + (a - 1)) & ~(a - 1))
+#define ngx_align_ptr(p, a) \
+ (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))
+
+
+#define ngx_abort abort
+
+
+/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */
+#define NGX_INVALID_ARRAY_INDEX 0x80000000
+
+
+/* TODO: auto_conf: ngx_inline inline __inline __inline__ */
+#ifndef ngx_inline
+#define ngx_inline inline
+#endif
+
+#ifndef INADDR_NONE /* Solaris */
+#define INADDR_NONE ((unsigned int) -1)
+#endif
+
+#ifdef MAXHOSTNAMELEN
+#define NGX_MAXHOSTNAMELEN MAXHOSTNAMELEN
+#else
+#define NGX_MAXHOSTNAMELEN 256
+#endif
+
+
+#if ((__GNU__ == 2) && (__GNUC_MINOR__ < 8))
+#define NGX_MAX_UINT32_VALUE (uint32_t) 0xffffffffLL
+#else
+#define NGX_MAX_UINT32_VALUE (uint32_t) 0xffffffff
+#endif
+
+#define NGX_MAX_INT32_VALUE (uint32_t) 0x7fffffff
+
+
+#endif /* _NGX_CONFIG_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_connection.c b/usr.sbin/nginx/src/core/ngx_connection.c
new file mode 100644
index 00000000000..9f19fcc4f76
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_connection.c
@@ -0,0 +1,1073 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+ngx_os_io_t ngx_io;
+
+
+static void ngx_drain_connections(void);
+
+
+ngx_listening_t *
+ngx_create_listening(ngx_conf_t *cf, void *sockaddr, socklen_t socklen)
+{
+ size_t len;
+ ngx_listening_t *ls;
+ struct sockaddr *sa;
+ u_char text[NGX_SOCKADDR_STRLEN];
+
+ ls = ngx_array_push(&cf->cycle->listening);
+ if (ls == NULL) {
+ return NULL;
+ }
+
+ ngx_memzero(ls, sizeof(ngx_listening_t));
+
+ sa = ngx_palloc(cf->pool, socklen);
+ if (sa == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(sa, sockaddr, socklen);
+
+ ls->sockaddr = sa;
+ ls->socklen = socklen;
+
+ len = ngx_sock_ntop(sa, text, NGX_SOCKADDR_STRLEN, 1);
+ ls->addr_text.len = len;
+
+ switch (ls->sockaddr->sa_family) {
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+ break;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
+ len++;
+ break;
+#endif
+ case AF_INET:
+ ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;
+ break;
+ default:
+ ls->addr_text_max_len = NGX_SOCKADDR_STRLEN;
+ break;
+ }
+
+ ls->addr_text.data = ngx_pnalloc(cf->pool, len);
+ if (ls->addr_text.data == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(ls->addr_text.data, text, len);
+
+ ls->fd = (ngx_socket_t) -1;
+ ls->type = SOCK_STREAM;
+
+ ls->backlog = NGX_LISTEN_BACKLOG;
+ ls->rcvbuf = -1;
+ ls->sndbuf = -1;
+
+#if (NGX_HAVE_SETFIB)
+ ls->setfib = -1;
+#endif
+
+ return ls;
+}
+
+
+ngx_int_t
+ngx_set_inherited_sockets(ngx_cycle_t *cycle)
+{
+ size_t len;
+ ngx_uint_t i;
+ ngx_listening_t *ls;
+ socklen_t olen;
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ ngx_err_t err;
+ struct accept_filter_arg af;
+#endif
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ int timeout;
+#endif
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ ls[i].sockaddr = ngx_palloc(cycle->pool, NGX_SOCKADDRLEN);
+ if (ls[i].sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ ls[i].socklen = NGX_SOCKADDRLEN;
+ if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {
+ ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+ "getsockname() of the inherited "
+ "socket #%d failed", ls[i].fd);
+ ls[i].ignore = 1;
+ continue;
+ }
+
+ switch (ls[i].sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;
+ len = NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1;
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;
+ len = NGX_UNIX_ADDRSTRLEN;
+ break;
+#endif
+
+ case AF_INET:
+ ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;
+ len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
+ break;
+
+ default:
+ ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,
+ "the inherited socket #%d has "
+ "an unsupported protocol family", ls[i].fd);
+ ls[i].ignore = 1;
+ continue;
+ }
+
+ ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);
+ if (ls[i].addr_text.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = ngx_sock_ntop(ls[i].sockaddr, ls[i].addr_text.data, len, 1);
+ if (len == 0) {
+ return NGX_ERROR;
+ }
+
+ ls[i].addr_text.len = len;
+
+ ls[i].backlog = NGX_LISTEN_BACKLOG;
+
+ olen = sizeof(int);
+
+ if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,
+ &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "getsockopt(SO_RCVBUF) %V failed, ignored",
+ &ls[i].addr_text);
+
+ ls[i].rcvbuf = -1;
+ }
+
+ olen = sizeof(int);
+
+ if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,
+ &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "getsockopt(SO_SNDBUF) %V failed, ignored",
+ &ls[i].addr_text);
+
+ ls[i].sndbuf = -1;
+ }
+
+#if 0
+ /* SO_SETFIB is currently a set only option */
+
+#if (NGX_HAVE_SETFIB)
+
+ if (getsockopt(ls[i].setfib, SOL_SOCKET, SO_SETFIB,
+ (void *) &ls[i].setfib, &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "getsockopt(SO_SETFIB) %V failed, ignored",
+ &ls[i].addr_text);
+
+ ls[i].setfib = -1;
+ }
+
+#endif
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+
+ ngx_memzero(&af, sizeof(struct accept_filter_arg));
+ olen = sizeof(struct accept_filter_arg);
+
+ if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)
+ == -1)
+ {
+ err = ngx_errno;
+
+ if (err == NGX_EINVAL) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,
+ "getsockopt(SO_ACCEPTFILTER) for %V failed, ignored",
+ &ls[i].addr_text);
+ continue;
+ }
+
+ if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\0') {
+ continue;
+ }
+
+ ls[i].accept_filter = ngx_palloc(cycle->pool, 16);
+ if (ls[i].accept_filter == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn((u_char *) ls[i].accept_filter,
+ (u_char *) af.af_name, 16);
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+
+ timeout = 0;
+ olen = sizeof(int);
+
+ if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, ngx_errno,
+ "getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored",
+ &ls[i].addr_text);
+ continue;
+ }
+
+ if (olen < sizeof(int) || timeout == 0) {
+ continue;
+ }
+
+ ls[i].deferred_accept = 1;
+#endif
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_open_listening_sockets(ngx_cycle_t *cycle)
+{
+ int reuseaddr;
+ ngx_uint_t i, tries, failed;
+ 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: configurable try number */
+
+ for (tries = 5; 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].sockaddr->sa_family, ls[i].type, 0);
+
+ if (s == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_socket_n " %V failed", &ls[i].addr_text);
+ return NGX_ERROR;
+ }
+
+ 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) %V failed",
+ &ls[i].addr_text);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ return NGX_ERROR;
+ }
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+
+ if (ls[i].sockaddr->sa_family == AF_INET6 && ls[i].ipv6only) {
+ int ipv6only;
+
+ ipv6only = (ls[i].ipv6only == 1);
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (const void *) &ipv6only, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ "setsockopt(IPV6_V6ONLY) %V failed, ignored",
+ &ls[i].addr_text);
+ }
+ }
+#endif
+ /* 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 " %V failed",
+ &ls[i].addr_text);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ return NGX_ERROR;
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,
+ "bind() %V #%d ", &ls[i].addr_text, s);
+
+ if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
+ err = ngx_socket_errno;
+
+ if (err == NGX_EADDRINUSE && ngx_test_config) {
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, log, err,
+ "bind() to %V failed", &ls[i].addr_text);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ if (err != NGX_EADDRINUSE) {
+ return NGX_ERROR;
+ }
+
+ failed = 1;
+
+ continue;
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (ls[i].sockaddr->sa_family == AF_UNIX) {
+ mode_t mode;
+ u_char *name;
+
+ name = ls[i].addr_text.data + sizeof("unix:") - 1;
+ mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+ if (chmod((char *) name, mode) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "chmod() \"%s\" failed", name);
+ }
+
+ if (ngx_test_config) {
+ if (ngx_delete_file(name) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ ngx_delete_file_n " %s failed", name);
+ }
+ }
+ }
+#endif
+
+ if (listen(s, ls[i].backlog) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ "listen() to %V, backlog %d failed",
+ &ls[i].addr_text, ls[i].backlog);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+
+ return NGX_ERROR;
+ }
+
+ ls[i].listen = 1;
+
+ 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 could not bind()");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_configure_listening_sockets(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+ ngx_listening_t *ls;
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ struct accept_filter_arg af;
+#endif
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ int timeout;
+#endif
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ ls[i].log = *ls[i].logp;
+
+ if (ls[i].rcvbuf != -1) {
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF,
+ (const void *) &ls[i].rcvbuf, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(SO_RCVBUF, %d) %V failed, ignored",
+ ls[i].rcvbuf, &ls[i].addr_text);
+ }
+ }
+
+ if (ls[i].sndbuf != -1) {
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF,
+ (const void *) &ls[i].sndbuf, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(SO_SNDBUF, %d) %V failed, ignored",
+ ls[i].sndbuf, &ls[i].addr_text);
+ }
+ }
+
+#if (NGX_HAVE_SETFIB)
+ if (ls[i].setfib != -1) {
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,
+ (const void *) &ls[i].setfib, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(SO_SETFIB, %d) %V failed, ignored",
+ ls[i].setfib, &ls[i].addr_text);
+ }
+ }
+#endif
+
+#if 0
+ if (1) {
+ int tcp_nodelay = 1;
+
+ if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_NODELAY,
+ (const void *) &tcp_nodelay, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "setsockopt(TCP_NODELAY) %V failed, ignored",
+ &ls[i].addr_text);
+ }
+ }
+#endif
+
+ if (ls[i].listen) {
+
+ /* change backlog via listen() */
+
+ if (listen(ls[i].fd, ls[i].backlog) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,
+ "listen() to %V, backlog %d failed, ignored",
+ &ls[i].addr_text, ls[i].backlog);
+ }
+ }
+
+ /*
+ * setting deferred mode should be last operation on socket,
+ * because code may prematurely continue cycle on failure
+ */
+
+#if (NGX_HAVE_DEFERRED_ACCEPT)
+
+#ifdef SO_ACCEPTFILTER
+
+ if (ls[i].delete_deferred) {
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setsockopt(SO_ACCEPTFILTER, NULL) "
+ "for %V failed, ignored",
+ &ls[i].addr_text);
+
+ if (ls[i].accept_filter) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ "could not change the accept filter "
+ "to \"%s\" for %V, ignored",
+ ls[i].accept_filter, &ls[i].addr_text);
+ }
+
+ continue;
+ }
+
+ ls[i].deferred_accept = 0;
+ }
+
+ if (ls[i].add_deferred) {
+ ngx_memzero(&af, sizeof(struct accept_filter_arg));
+ (void) ngx_cpystrn((u_char *) af.af_name,
+ (u_char *) ls[i].accept_filter, 16);
+
+ if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER,
+ &af, sizeof(struct accept_filter_arg))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setsockopt(SO_ACCEPTFILTER, \"%s\") "
+ " for %V failed, ignored",
+ ls[i].accept_filter, &ls[i].addr_text);
+ continue;
+ }
+
+ ls[i].deferred_accept = 1;
+ }
+
+#endif
+
+#ifdef TCP_DEFER_ACCEPT
+
+ if (ls[i].add_deferred || ls[i].delete_deferred) {
+
+ if (ls[i].add_deferred) {
+ timeout = (int) (ls[i].post_accept_timeout / 1000);
+
+ } else {
+ timeout = 0;
+ }
+
+ if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT,
+ &timeout, sizeof(int))
+ == -1)
+ {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ "setsockopt(TCP_DEFER_ACCEPT, %d) for %V failed, "
+ "ignored",
+ timeout, &ls[i].addr_text);
+
+ continue;
+ }
+ }
+
+ if (ls[i].add_deferred) {
+ ls[i].deferred_accept = 1;
+ }
+
+#endif
+
+#endif /* NGX_HAVE_DEFERRED_ACCEPT */
+ }
+
+ return;
+}
+
+
+void
+ngx_close_listening_sockets(ngx_cycle_t *cycle)
+{
+ ngx_uint_t i;
+ ngx_listening_t *ls;
+ ngx_connection_t *c;
+
+ if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
+ return;
+ }
+
+ ngx_accept_mutex_held = 0;
+ ngx_use_accept_mutex = 0;
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+
+ c = ls[i].connection;
+
+ if (c) {
+ if (c->read->active) {
+ if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
+ ngx_del_conn(c, NGX_CLOSE_EVENT);
+
+ } else if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {
+
+ /*
+ * it seems that Linux-2.6.x OpenVZ sends events
+ * for closed shared listening sockets unless
+ * the events was explicity deleted
+ */
+
+ ngx_del_event(c->read, NGX_READ_EVENT, 0);
+
+ } else {
+ ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+ }
+ }
+
+ ngx_free_connection(c);
+
+ c->fd = (ngx_socket_t) -1;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
+ "close listening %V #%d ", &ls[i].addr_text, ls[i].fd);
+
+ if (ngx_close_socket(ls[i].fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed", &ls[i].addr_text);
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (ls[i].sockaddr->sa_family == AF_UNIX
+ && ngx_process <= NGX_PROCESS_MASTER
+ && ngx_new_binary == 0)
+ {
+ u_char *name = ls[i].addr_text.data + sizeof("unix:") - 1;
+
+ if (ngx_delete_file(name) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+ ngx_delete_file_n " %s failed", name);
+ }
+ }
+
+#endif
+
+ ls[i].fd = (ngx_socket_t) -1;
+ }
+}
+
+
+ngx_connection_t *
+ngx_get_connection(ngx_socket_t s, ngx_log_t *log)
+{
+ ngx_uint_t instance;
+ ngx_event_t *rev, *wev;
+ ngx_connection_t *c;
+
+ /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */
+
+ if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "the new socket has number %d, "
+ "but only %ui files are available",
+ s, ngx_cycle->files_n);
+ return NULL;
+ }
+
+ /* ngx_mutex_lock */
+
+ c = ngx_cycle->free_connections;
+
+ if (c == NULL) {
+ ngx_drain_connections();
+ c = ngx_cycle->free_connections;
+ }
+
+ if (c == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ "%ui worker_connections are not enough",
+ ngx_cycle->connection_n);
+
+ /* ngx_mutex_unlock */
+
+ return NULL;
+ }
+
+ ngx_cycle->free_connections = c->data;
+ ngx_cycle->free_connection_n--;
+
+ /* ngx_mutex_unlock */
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[s] = c;
+ }
+
+ rev = c->read;
+ wev = c->write;
+
+ ngx_memzero(c, sizeof(ngx_connection_t));
+
+ c->read = rev;
+ c->write = wev;
+ c->fd = s;
+ c->log = log;
+
+ instance = rev->instance;
+
+ ngx_memzero(rev, sizeof(ngx_event_t));
+ ngx_memzero(wev, sizeof(ngx_event_t));
+
+ rev->instance = !instance;
+ wev->instance = !instance;
+
+ rev->index = NGX_INVALID_INDEX;
+ wev->index = NGX_INVALID_INDEX;
+
+ rev->data = c;
+ wev->data = c;
+
+ wev->write = 1;
+
+ return c;
+}
+
+
+void
+ngx_free_connection(ngx_connection_t *c)
+{
+ /* ngx_mutex_lock */
+
+ c->data = ngx_cycle->free_connections;
+ ngx_cycle->free_connections = c;
+ ngx_cycle->free_connection_n++;
+
+ /* ngx_mutex_unlock */
+
+ if (ngx_cycle->files) {
+ ngx_cycle->files[c->fd] = NULL;
+ }
+}
+
+
+void
+ngx_close_connection(ngx_connection_t *c)
+{
+ ngx_err_t err;
+ ngx_uint_t log_error, level;
+ ngx_socket_t fd;
+
+ if (c->fd == -1) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, 0, "connection already closed");
+ return;
+ }
+
+ if (c->read->timer_set) {
+ ngx_del_timer(c->read);
+ }
+
+ if (c->write->timer_set) {
+ ngx_del_timer(c->write);
+ }
+
+ if (ngx_del_conn) {
+ ngx_del_conn(c, NGX_CLOSE_EVENT);
+
+ } else {
+ if (c->read->active || c->read->disabled) {
+ ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);
+ }
+
+ if (c->write->active || c->write->disabled) {
+ ngx_del_event(c->write, NGX_WRITE_EVENT, NGX_CLOSE_EVENT);
+ }
+ }
+
+#if (NGX_THREADS)
+
+ /*
+ * we have to clean the connection information before the closing
+ * because another thread may reopen the same file descriptor
+ * before we clean the connection
+ */
+
+ ngx_mutex_lock(ngx_posted_events_mutex);
+
+ if (c->read->prev) {
+ ngx_delete_posted_event(c->read);
+ }
+
+ if (c->write->prev) {
+ ngx_delete_posted_event(c->write);
+ }
+
+ c->read->closed = 1;
+ c->write->closed = 1;
+
+ if (c->single_connection) {
+ ngx_unlock(&c->lock);
+ c->read->locked = 0;
+ c->write->locked = 0;
+ }
+
+ ngx_mutex_unlock(ngx_posted_events_mutex);
+
+#else
+
+ if (c->read->prev) {
+ ngx_delete_posted_event(c->read);
+ }
+
+ if (c->write->prev) {
+ ngx_delete_posted_event(c->write);
+ }
+
+ c->read->closed = 1;
+ c->write->closed = 1;
+
+#endif
+
+ ngx_reusable_connection(c, 0);
+
+ log_error = c->log_error;
+
+ ngx_free_connection(c);
+
+ fd = c->fd;
+ c->fd = (ngx_socket_t) -1;
+
+ if (ngx_close_socket(fd) == -1) {
+
+ err = ngx_socket_errno;
+
+ if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {
+
+ switch (log_error) {
+
+ 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;
+ }
+
+ /* we use ngx_cycle->log because c->log was in c->pool */
+
+ ngx_log_error(level, ngx_cycle->log, err,
+ ngx_close_socket_n " %d failed", fd);
+ }
+}
+
+
+void
+ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)
+{
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "reusable connection: %ui", reusable);
+
+ if (c->reusable) {
+ ngx_queue_remove(&c->queue);
+ }
+
+ c->reusable = reusable;
+
+ if (reusable) {
+ /* need cast as ngx_cycle is volatile */
+
+ ngx_queue_insert_head(
+ (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);
+ }
+}
+
+
+static void
+ngx_drain_connections(void)
+{
+ ngx_int_t i;
+ ngx_queue_t *q;
+ ngx_connection_t *c;
+
+ for (i = 0; i < 32; i++) {
+ if (ngx_queue_empty(&ngx_cycle->reusable_connections_queue)) {
+ break;
+ }
+
+ q = ngx_queue_last(&ngx_cycle->reusable_connections_queue);
+ c = ngx_queue_data(q, ngx_connection_t, queue);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "reusing connection");
+
+ c->close = 1;
+ c->read->handler(c->read);
+ }
+}
+
+
+ngx_int_t
+ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,
+ ngx_uint_t port)
+{
+ socklen_t len;
+ ngx_uint_t addr;
+ u_char sa[NGX_SOCKADDRLEN];
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ ngx_uint_t i;
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (c->local_sockaddr->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) c->local_sockaddr;
+
+ for (addr = 0, i = 0; addr == 0 && i < 16; i++) {
+ addr |= sin6->sin6_addr.s6_addr[i];
+ }
+
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) c->local_sockaddr;
+ addr = sin->sin_addr.s_addr;
+ break;
+ }
+
+ if (addr == 0) {
+
+ len = NGX_SOCKADDRLEN;
+
+ if (getsockname(c->fd, (struct sockaddr *) &sa, &len) == -1) {
+ ngx_connection_error(c, ngx_socket_errno, "getsockname() failed");
+ return NGX_ERROR;
+ }
+
+ c->local_sockaddr = ngx_palloc(c->pool, len);
+ if (c->local_sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(c->local_sockaddr, &sa, len);
+ }
+
+ if (s == NULL) {
+ return NGX_OK;
+ }
+
+ s->len = ngx_sock_ntop(c->local_sockaddr, s->data, s->len, port);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)
+{
+ ngx_uint_t level;
+
+ /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */
+
+ if ((err == NGX_ECONNRESET
+#if (NGX_WIN32)
+ || err == NGX_ECONNABORTED
+#endif
+ ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)
+ {
+ return 0;
+ }
+
+#if (NGX_SOLARIS)
+ if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) {
+ return 0;
+ }
+#endif
+
+ if (err == 0
+ || err == NGX_ECONNRESET
+#if (NGX_WIN32)
+ || err == NGX_ECONNABORTED
+#else
+ || err == NGX_EPIPE
+#endif
+ || err == NGX_ENOTCONN
+ || err == NGX_ETIMEDOUT
+ || err == NGX_ECONNREFUSED
+ || err == NGX_ENETDOWN
+ || err == NGX_ENETUNREACH
+ || err == NGX_EHOSTDOWN
+ || err == NGX_EHOSTUNREACH)
+ {
+ switch (c->log_error) {
+
+ case NGX_ERROR_IGNORE_EINVAL:
+ case NGX_ERROR_IGNORE_ECONNRESET:
+ case NGX_ERROR_INFO:
+ level = NGX_LOG_INFO;
+ break;
+
+ default:
+ level = NGX_LOG_ERR;
+ }
+
+ } else {
+ level = NGX_LOG_ALERT;
+ }
+
+ ngx_log_error(level, c->log, err, text);
+
+ return NGX_ERROR;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_connection.h b/usr.sbin/nginx/src/core/ngx_connection.h
new file mode 100644
index 00000000000..f9bf5040105
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_connection.h
@@ -0,0 +1,194 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CONNECTION_H_INCLUDED_
+#define _NGX_CONNECTION_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct ngx_listening_s ngx_listening_t;
+
+struct ngx_listening_s {
+ ngx_socket_t fd;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen; /* size of sockaddr */
+ size_t addr_text_max_len;
+ ngx_str_t addr_text;
+
+ int type;
+
+ int backlog;
+ int rcvbuf;
+ int sndbuf;
+
+ /* handler of accepted connection */
+ ngx_connection_handler_pt handler;
+
+ void *servers; /* array of ngx_http_in_addr_t, for example */
+
+ ngx_log_t log;
+ ngx_log_t *logp;
+
+ size_t pool_size;
+ /* should be here because of the AcceptEx() preread */
+ size_t post_accept_buffer_size;
+ /* should be here because of the deferred accept */
+ ngx_msec_t post_accept_timeout;
+
+ ngx_listening_t *previous;
+ ngx_connection_t *connection;
+
+ unsigned open:1;
+ unsigned remain:1;
+ unsigned ignore:1;
+
+ unsigned bound:1; /* already bound */
+ unsigned inherited:1; /* inherited from previous process */
+ unsigned nonblocking_accept:1;
+ unsigned listen:1;
+ unsigned nonblocking:1;
+ unsigned shared:1; /* shared between threads or processes */
+ unsigned addr_ntop:1;
+
+#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)
+ unsigned ipv6only:2;
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT)
+ unsigned deferred_accept:1;
+ unsigned delete_deferred:1;
+ unsigned add_deferred:1;
+#ifdef SO_ACCEPTFILTER
+ char *accept_filter;
+#endif
+#endif
+#if (NGX_HAVE_SETFIB)
+ int setfib;
+#endif
+
+};
+
+
+typedef enum {
+ NGX_ERROR_ALERT = 0,
+ NGX_ERROR_ERR,
+ NGX_ERROR_INFO,
+ NGX_ERROR_IGNORE_ECONNRESET,
+ NGX_ERROR_IGNORE_EINVAL
+} ngx_connection_log_error_e;
+
+
+typedef enum {
+ NGX_TCP_NODELAY_UNSET = 0,
+ NGX_TCP_NODELAY_SET,
+ NGX_TCP_NODELAY_DISABLED
+} ngx_connection_tcp_nodelay_e;
+
+
+typedef enum {
+ NGX_TCP_NOPUSH_UNSET = 0,
+ NGX_TCP_NOPUSH_SET,
+ NGX_TCP_NOPUSH_DISABLED
+} ngx_connection_tcp_nopush_e;
+
+
+#define NGX_LOWLEVEL_BUFFERED 0x0f
+#define NGX_SSL_BUFFERED 0x01
+
+
+struct ngx_connection_s {
+ void *data;
+ ngx_event_t *read;
+ ngx_event_t *write;
+
+ ngx_socket_t fd;
+
+ ngx_recv_pt recv;
+ ngx_send_pt send;
+ ngx_recv_chain_pt recv_chain;
+ ngx_send_chain_pt send_chain;
+
+ ngx_listening_t *listening;
+
+ off_t sent;
+
+ ngx_log_t *log;
+
+ ngx_pool_t *pool;
+
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t addr_text;
+
+#if (NGX_SSL)
+ ngx_ssl_connection_t *ssl;
+#endif
+
+ struct sockaddr *local_sockaddr;
+
+ ngx_buf_t *buffer;
+
+ ngx_queue_t queue;
+
+ ngx_atomic_uint_t number;
+
+ ngx_uint_t requests;
+
+ unsigned buffered:8;
+
+ unsigned log_error:3; /* ngx_connection_log_error_e */
+
+ unsigned single_connection:1;
+ unsigned unexpected_eof:1;
+ unsigned timedout:1;
+ unsigned error:1;
+ unsigned destroyed:1;
+
+ unsigned idle:1;
+ unsigned reusable:1;
+ unsigned close:1;
+
+ unsigned sendfile:1;
+ unsigned sndlowat:1;
+ unsigned tcp_nodelay:2; /* ngx_connection_tcp_nodelay_e */
+ unsigned tcp_nopush:2; /* ngx_connection_tcp_nopush_e */
+
+#if (NGX_HAVE_IOCP)
+ unsigned accept_context_updated:1;
+#endif
+
+#if (NGX_HAVE_AIO_SENDFILE)
+ unsigned aio_sendfile:1;
+ ngx_buf_t *busy_sendfile;
+#endif
+
+#if (NGX_THREADS)
+ ngx_atomic_t lock;
+#endif
+};
+
+
+ngx_listening_t *ngx_create_listening(ngx_conf_t *cf, void *sockaddr,
+ socklen_t socklen);
+ngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);
+ngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);
+void ngx_configure_listening_sockets(ngx_cycle_t *cycle);
+void ngx_close_listening_sockets(ngx_cycle_t *cycle);
+void ngx_close_connection(ngx_connection_t *c);
+ngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,
+ ngx_uint_t port);
+ngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text);
+
+ngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log);
+void ngx_free_connection(ngx_connection_t *c);
+
+void ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable);
+
+#endif /* _NGX_CONNECTION_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_core.h b/usr.sbin/nginx/src/core/ngx_core.h
new file mode 100644
index 00000000000..9f179015341
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_core.h
@@ -0,0 +1,94 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CORE_H_INCLUDED_
+#define _NGX_CORE_H_INCLUDED_
+
+
+typedef struct ngx_module_s ngx_module_t;
+typedef struct ngx_conf_s ngx_conf_t;
+typedef struct ngx_cycle_s ngx_cycle_t;
+typedef struct ngx_pool_s ngx_pool_t;
+typedef struct ngx_chain_s ngx_chain_t;
+typedef struct ngx_log_s ngx_log_t;
+typedef struct ngx_array_s ngx_array_t;
+typedef struct ngx_open_file_s ngx_open_file_t;
+typedef struct ngx_command_s ngx_command_t;
+typedef struct ngx_file_s ngx_file_t;
+typedef struct ngx_event_s ngx_event_t;
+typedef struct ngx_event_aio_s ngx_event_aio_t;
+typedef struct ngx_connection_s ngx_connection_t;
+
+typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);
+typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
+
+
+#define NGX_OK 0
+#define NGX_ERROR -1
+#define NGX_AGAIN -2
+#define NGX_BUSY -3
+#define NGX_DONE -4
+#define NGX_DECLINED -5
+#define NGX_ABORT -6
+
+
+#include <ngx_errno.h>
+#include <ngx_atomic.h>
+#include <ngx_thread.h>
+#include <ngx_rbtree.h>
+#include <ngx_time.h>
+#include <ngx_socket.h>
+#include <ngx_string.h>
+#include <ngx_files.h>
+#include <ngx_shmem.h>
+#include <ngx_process.h>
+#include <ngx_user.h>
+#include <ngx_parse.h>
+#include <ngx_log.h>
+#include <ngx_alloc.h>
+#include <ngx_palloc.h>
+#include <ngx_buf.h>
+#include <ngx_queue.h>
+#include <ngx_array.h>
+#include <ngx_list.h>
+#include <ngx_hash.h>
+#include <ngx_file.h>
+#include <ngx_crc.h>
+#include <ngx_crc32.h>
+#include <ngx_murmurhash.h>
+#if (NGX_PCRE)
+#include <ngx_regex.h>
+#endif
+#include <ngx_radix_tree.h>
+#include <ngx_times.h>
+#include <ngx_shmtx.h>
+#include <ngx_slab.h>
+#include <ngx_inet.h>
+#include <ngx_cycle.h>
+#if (NGX_OPENSSL)
+#include <ngx_event_openssl.h>
+#endif
+#include <ngx_process_cycle.h>
+#include <ngx_conf_file.h>
+#include <ngx_resolver.h>
+#include <ngx_open_file_cache.h>
+#include <ngx_os.h>
+#include <ngx_connection.h>
+
+
+#define LF (u_char) 10
+#define CR (u_char) 13
+#define CRLF "\x0d\x0a"
+
+
+#define ngx_abs(value) (((value) >= 0) ? (value) : - (value))
+#define ngx_max(val1, val2) ((val1 < val2) ? (val2) : (val1))
+#define ngx_min(val1, val2) ((val1 > val2) ? (val2) : (val1))
+
+void ngx_cpuinfo(void);
+
+
+#endif /* _NGX_CORE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_cpuinfo.c b/usr.sbin/nginx/src/core/ngx_cpuinfo.c
new file mode 100644
index 00000000000..39a6b05a056
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_cpuinfo.c
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (( __i386__ || __amd64__ ) && ( __GNUC__ || __INTEL_COMPILER ))
+
+
+static ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);
+
+
+#if ( __i386__ )
+
+static ngx_inline void
+ngx_cpuid(uint32_t i, uint32_t *buf)
+{
+
+ /*
+ * we could not use %ebx as output parameter if gcc builds PIC,
+ * and we could not save %ebx on stack, because %esp is used,
+ * when the -fomit-frame-pointer optimization is specified.
+ */
+
+ __asm__ (
+
+ " mov %%ebx, %%esi; "
+
+ " cpuid; "
+ " mov %%eax, (%1); "
+ " mov %%ebx, 4(%1); "
+ " mov %%edx, 8(%1); "
+ " mov %%ecx, 12(%1); "
+
+ " mov %%esi, %%ebx; "
+
+ : : "a" (i), "D" (buf) : "ecx", "edx", "esi", "memory" );
+}
+
+
+#else /* __amd64__ */
+
+
+static ngx_inline void
+ngx_cpuid(uint32_t i, uint32_t *buf)
+{
+ uint32_t eax, ebx, ecx, edx;
+
+ __asm__ (
+
+ "cpuid"
+
+ : "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx) : "a" (i) );
+
+ buf[0] = eax;
+ buf[1] = ebx;
+ buf[2] = edx;
+ buf[3] = ecx;
+}
+
+
+#endif
+
+
+/* auto detect the L2 cache line size of modern and widespread CPUs */
+
+void
+ngx_cpuinfo(void)
+{
+ u_char *vendor;
+ uint32_t vbuf[5], cpu[4], model;
+
+ vbuf[0] = 0;
+ vbuf[1] = 0;
+ vbuf[2] = 0;
+ vbuf[3] = 0;
+ vbuf[4] = 0;
+
+ ngx_cpuid(0, vbuf);
+
+ vendor = (u_char *) &vbuf[1];
+
+ if (vbuf[0] == 0) {
+ return;
+ }
+
+ ngx_cpuid(1, cpu);
+
+ if (ngx_strcmp(vendor, "GenuineIntel") == 0) {
+
+ switch ((cpu[0] & 0xf00) >> 8) {
+
+ /* Pentium */
+ case 5:
+ ngx_cacheline_size = 32;
+ break;
+
+ /* Pentium Pro, II, III */
+ case 6:
+ ngx_cacheline_size = 32;
+
+ model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0);
+
+ if (model >= 0xd0) {
+ /* Intel Core, Core 2, Atom */
+ ngx_cacheline_size = 64;
+ }
+
+ break;
+
+ /*
+ * Pentium 4, although its cache line size is 64 bytes,
+ * it prefetches up to two cache lines during memory read
+ */
+ case 15:
+ ngx_cacheline_size = 128;
+ break;
+ }
+
+ } else if (ngx_strcmp(vendor, "AuthenticAMD") == 0) {
+ ngx_cacheline_size = 64;
+ }
+}
+
+#else
+
+
+void
+ngx_cpuinfo(void)
+{
+}
+
+
+#endif
diff --git a/usr.sbin/nginx/src/core/ngx_crc.h b/usr.sbin/nginx/src/core/ngx_crc.h
new file mode 100644
index 00000000000..c77f037abcd
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_crc.h
@@ -0,0 +1,38 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CRC_H_INCLUDED_
+#define _NGX_CRC_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/* 32-bit crc16 */
+
+static ngx_inline uint32_t
+ngx_crc(u_char *data, size_t len)
+{
+ uint32_t sum;
+
+ for (sum = 0; len; len--) {
+
+ /*
+ * gcc 2.95.2 x86 and icc 7.1.006 compile
+ * that operator into the single "rol" opcode,
+ * msvc 6.0sp2 compiles it into four opcodes.
+ */
+ sum = sum >> 1 | sum << 31;
+
+ sum += *data++;
+ }
+
+ return sum;
+}
+
+
+#endif /* _NGX_CRC_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_crc32.c b/usr.sbin/nginx/src/core/ngx_crc32.c
new file mode 100644
index 00000000000..624510cee32
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_crc32.c
@@ -0,0 +1,128 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * The code and lookup tables are based on the algorithm
+ * described at http://www.w3.org/TR/PNG/
+ *
+ * The 256 element lookup table takes 1024 bytes, and it may be completely
+ * cached after processing about 30-60 bytes of data. So for short data
+ * we use the 16 element lookup table that takes only 64 bytes and align it
+ * to CPU cache line size. Of course, the small table adds code inside
+ * CRC32 loop, but the cache misses overhead is bigger than overhead of
+ * the additional code. For example, ngx_crc32_short() of 16 bytes of data
+ * takes half as much CPU clocks than ngx_crc32_long().
+ */
+
+
+static uint32_t ngx_crc32_table16[] = {
+ 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,
+ 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,
+ 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,
+ 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c
+};
+
+
+uint32_t ngx_crc32_table256[] = {
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
+};
+
+
+uint32_t *ngx_crc32_table_short = ngx_crc32_table16;
+
+
+ngx_int_t
+ngx_crc32_table_init(void)
+{
+ void *p;
+
+ if (((uintptr_t) ngx_crc32_table_short
+ & ~((uintptr_t) ngx_cacheline_size - 1))
+ == (uintptr_t) ngx_crc32_table_short)
+ {
+ return NGX_OK;
+ }
+
+ p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_align_ptr(p, ngx_cacheline_size);
+
+ ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));
+
+ ngx_crc32_table_short = p;
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_crc32.h b/usr.sbin/nginx/src/core/ngx_crc32.h
new file mode 100644
index 00000000000..4435e4517e2
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_crc32.h
@@ -0,0 +1,78 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CRC32_H_INCLUDED_
+#define _NGX_CRC32_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+extern uint32_t *ngx_crc32_table_short;
+extern uint32_t ngx_crc32_table256[];
+
+
+static ngx_inline uint32_t
+ngx_crc32_short(u_char *p, size_t len)
+{
+ u_char c;
+ uint32_t crc;
+
+ crc = 0xffffffff;
+
+ while (len--) {
+ c = *p++;
+ crc = ngx_crc32_table_short[(crc ^ (c & 0xf)) & 0xf] ^ (crc >> 4);
+ crc = ngx_crc32_table_short[(crc ^ (c >> 4)) & 0xf] ^ (crc >> 4);
+ }
+
+ return crc ^ 0xffffffff;
+}
+
+
+static ngx_inline uint32_t
+ngx_crc32_long(u_char *p, size_t len)
+{
+ uint32_t crc;
+
+ crc = 0xffffffff;
+
+ while (len--) {
+ crc = ngx_crc32_table256[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+ }
+
+ return crc ^ 0xffffffff;
+}
+
+
+#define ngx_crc32_init(crc) \
+ crc = 0xffffffff
+
+
+static ngx_inline void
+ngx_crc32_update(uint32_t *crc, u_char *p, size_t len)
+{
+ uint32_t c;
+
+ c = *crc;
+
+ while (len--) {
+ c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8);
+ }
+
+ *crc = c;
+}
+
+
+#define ngx_crc32_final(crc) \
+ crc ^= 0xffffffff
+
+
+ngx_int_t ngx_crc32_table_init(void);
+
+
+#endif /* _NGX_CRC32_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_crypt.c b/usr.sbin/nginx/src/core/ngx_crypt.c
new file mode 100644
index 00000000000..9564c3618cb
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_crypt.c
@@ -0,0 +1,238 @@
+
+/*
+ * Copyright (C) Maxim Dounin
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_md5.h>
+#if (NGX_HAVE_SHA1)
+#include <ngx_sha1.h>
+#endif
+
+
+#if (NGX_CRYPT)
+
+static ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+static ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+#endif
+
+
+static u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n);
+
+
+ngx_int_t
+ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ if (ngx_strncmp(salt, "$apr1$", sizeof("$apr1$") - 1) == 0) {
+ return ngx_crypt_apr1(pool, key, salt, encrypted);
+
+ } else if (ngx_strncmp(salt, "{PLAIN}", sizeof("{PLAIN}") - 1) == 0) {
+ return ngx_crypt_plain(pool, key, salt, encrypted);
+
+#if (NGX_HAVE_SHA1)
+ } else if (ngx_strncmp(salt, "{SSHA}", sizeof("{SSHA}") - 1) == 0) {
+ return ngx_crypt_ssha(pool, key, salt, encrypted);
+#endif
+ }
+
+ /* fallback to libc crypt() */
+
+ return ngx_libc_crypt(pool, key, salt, encrypted);
+}
+
+
+static ngx_int_t
+ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ ngx_int_t n;
+ ngx_uint_t i;
+ u_char *p, *last, final[16];
+ size_t saltlen, keylen;
+ ngx_md5_t md5, ctx1;
+
+ /* Apache's apr1 crypt is Paul-Henning Kamp's md5 crypt with $apr1$ magic */
+
+ keylen = ngx_strlen(key);
+
+ /* true salt: no magic, max 8 chars, stop at first $ */
+
+ salt += sizeof("$apr1$") - 1;
+ last = salt + 8;
+ for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }
+ saltlen = p - salt;
+
+ /* hash key and salt */
+
+ ngx_md5_init(&md5);
+ ngx_md5_update(&md5, key, keylen);
+ ngx_md5_update(&md5, (u_char *) "$apr1$", sizeof("$apr1$") - 1);
+ ngx_md5_update(&md5, salt, saltlen);
+
+ ngx_md5_init(&ctx1);
+ ngx_md5_update(&ctx1, key, keylen);
+ ngx_md5_update(&ctx1, salt, saltlen);
+ ngx_md5_update(&ctx1, key, keylen);
+ ngx_md5_final(final, &ctx1);
+
+ for (n = keylen; n > 0; n -= 16) {
+ ngx_md5_update(&md5, final, n > 16 ? 16 : n);
+ }
+
+ ngx_memzero(final, sizeof(final));
+
+ for (i = keylen; i; i >>= 1) {
+ if (i & 1) {
+ ngx_md5_update(&md5, final, 1);
+
+ } else {
+ ngx_md5_update(&md5, key, 1);
+ }
+ }
+
+ ngx_md5_final(final, &md5);
+
+ for (i = 0; i < 1000; i++) {
+ ngx_md5_init(&ctx1);
+
+ if (i & 1) {
+ ngx_md5_update(&ctx1, key, keylen);
+
+ } else {
+ ngx_md5_update(&ctx1, final, 16);
+ }
+
+ if (i % 3) {
+ ngx_md5_update(&ctx1, salt, saltlen);
+ }
+
+ if (i % 7) {
+ ngx_md5_update(&ctx1, key, keylen);
+ }
+
+ if (i & 1) {
+ ngx_md5_update(&ctx1, final, 16);
+
+ } else {
+ ngx_md5_update(&ctx1, key, keylen);
+ }
+
+ ngx_md5_final(final, &ctx1);
+ }
+
+ /* output */
+
+ *encrypted = ngx_pnalloc(pool, sizeof("$apr1$") - 1 + saltlen + 16 + 1);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(*encrypted, "$apr1$", sizeof("$apr1$") - 1);
+ p = ngx_copy(p, salt, saltlen);
+ *p++ = '$';
+
+ p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4);
+ p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4);
+ p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4);
+ p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4);
+ p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4);
+ p = ngx_crypt_to64(p, final[11], 2);
+ *p = '\0';
+
+ return NGX_OK;
+}
+
+
+static u_char *
+ngx_crypt_to64(u_char *p, uint32_t v, size_t n)
+{
+ static u_char itoa64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ while (n--) {
+ *p++ = itoa64[v & 0x3f];
+ v >>= 6;
+ }
+
+ return p;
+}
+
+
+static ngx_int_t
+ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ u_char *p;
+
+ len = ngx_strlen(key);
+
+ *encrypted = ngx_pnalloc(pool, sizeof("{PLAIN}") - 1 + len + 1);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ p = ngx_cpymem(*encrypted, "{PLAIN}", sizeof("{PLAIN}") - 1);
+ ngx_memcpy(p, key, len + 1);
+
+ return NGX_OK;
+}
+
+
+#if (NGX_HAVE_SHA1)
+
+static ngx_int_t
+ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
+{
+ size_t len;
+ ngx_str_t encoded, decoded;
+ ngx_sha1_t sha1;
+
+ /* "{SSHA}" base64(SHA1(key salt) salt) */
+
+ /* decode base64 salt to find out true salt */
+
+ encoded.data = salt + sizeof("{SSHA}") - 1;
+ encoded.len = ngx_strlen(encoded.data);
+
+ decoded.data = ngx_pnalloc(pool, ngx_base64_decoded_length(encoded.len));
+ if (decoded.data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_decode_base64(&decoded, &encoded);
+
+ /* update SHA1 from key and salt */
+
+ ngx_sha1_init(&sha1);
+ ngx_sha1_update(&sha1, key, ngx_strlen(key));
+ ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20);
+ ngx_sha1_final(decoded.data, &sha1);
+
+ /* encode it back to base64 */
+
+ len = sizeof("{SSHA}") - 1 + ngx_base64_encoded_length(decoded.len) + 1;
+
+ *encrypted = ngx_pnalloc(pool, len);
+ if (*encrypted == NULL) {
+ return NGX_ERROR;
+ }
+
+ encoded.data = ngx_cpymem(*encrypted, "{SSHA}", sizeof("{SSHA}") - 1);
+ ngx_encode_base64(&encoded, &decoded);
+ encoded.data[encoded.len] = '\0';
+
+ return NGX_OK;
+}
+
+#endif /* NGX_HAVE_SHA1 */
+
+#endif /* NGX_CRYPT */
diff --git a/usr.sbin/nginx/src/core/ngx_crypt.h b/usr.sbin/nginx/src/core/ngx_crypt.h
new file mode 100644
index 00000000000..45ef81d4b0e
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_crypt.h
@@ -0,0 +1,19 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CRYPT_H_INCLUDED_
+#define _NGX_CRYPT_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,
+ u_char **encrypted);
+
+
+#endif /* _NGX_CRYPT_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_cycle.c b/usr.sbin/nginx/src/core/ngx_cycle.c
new file mode 100644
index 00000000000..79867079d5e
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_cycle.c
@@ -0,0 +1,1383 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+static void ngx_destroy_cycle_pools(ngx_conf_t *conf);
+static ngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2);
+static ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle,
+ ngx_shm_zone_t *shm_zone);
+static ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);
+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;
+
+ngx_uint_t ngx_test_config;
+ngx_uint_t ngx_quiet_mode;
+
+#if (NGX_THREADS)
+ngx_tls_key_t ngx_core_tls_key;
+#endif
+
+
+/* STUB NAME */
+static ngx_connection_t dumb;
+/* STUB */
+
+static ngx_str_t error_log = ngx_string(NGX_ERROR_LOG_PATH);
+
+
+ngx_cycle_t *
+ngx_init_cycle(ngx_cycle_t *old_cycle)
+{
+ void *rv;
+ char **senv, **env;
+ ngx_uint_t i, n;
+ ngx_log_t *log;
+ ngx_time_t *tp;
+ ngx_conf_t conf;
+ ngx_pool_t *pool;
+ ngx_cycle_t *cycle, **old;
+ ngx_shm_zone_t *shm_zone, *oshm_zone;
+ ngx_list_part_t *part, *opart;
+ ngx_open_file_t *file;
+ ngx_listening_t *ls, *nls;
+ ngx_core_conf_t *ccf, *old_ccf;
+ ngx_core_module_t *module;
+ char hostname[NGX_MAXHOSTNAMELEN];
+
+ ngx_timezone_update();
+
+ /* force localtime update with a new timezone */
+
+ tp = ngx_timeofday();
+ tp->sec = 0;
+
+ ngx_time_update();
+
+
+ log = old_cycle->log;
+
+ pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
+ if (pool == NULL) {
+ return NULL;
+ }
+ pool->log = log;
+
+ cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
+ if (cycle == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ cycle->pool = pool;
+ cycle->log = log;
+ cycle->new_log.log_level = NGX_LOG_ERR;
+ cycle->old_cycle = old_cycle;
+
+ cycle->conf_prefix.len = old_cycle->conf_prefix.len;
+ cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);
+ if (cycle->conf_prefix.data == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ cycle->prefix.len = old_cycle->prefix.len;
+ cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);
+ if (cycle->prefix.data == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ cycle->conf_file.len = old_cycle->conf_file.len;
+ cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);
+ if (cycle->conf_file.data == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+ ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,
+ old_cycle->conf_file.len + 1);
+
+ cycle->conf_param.len = old_cycle->conf_param.len;
+ cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);
+ if (cycle->conf_param.data == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+
+ n = old_cycle->pathes.nelts ? old_cycle->pathes.nelts : 10;
+
+ cycle->pathes.elts = ngx_pcalloc(pool, n * sizeof(ngx_path_t *));
+ if (cycle->pathes.elts == NULL) {
+ 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;
+
+
+ if (old_cycle->open_files.part.nelts) {
+ n = old_cycle->open_files.part.nelts;
+ for (part = old_cycle->open_files.part.next; part; part = part->next) {
+ n += part->nelts;
+ }
+
+ } else {
+ n = 20;
+ }
+
+ if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))
+ != NGX_OK)
+ {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+
+ if (old_cycle->shared_memory.part.nelts) {
+ n = old_cycle->shared_memory.part.nelts;
+ for (part = old_cycle->shared_memory.part.next; part; part = part->next)
+ {
+ n += part->nelts;
+ }
+
+ } else {
+ n = 1;
+ }
+
+ if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))
+ != NGX_OK)
+ {
+ 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;
+
+
+ ngx_queue_init(&cycle->reusable_connections_queue);
+
+
+ cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));
+ if (cycle->conf_ctx == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+
+ if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "gethostname() failed");
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ /* on Linux gethostname() silently truncates name that does not fit */
+
+ hostname[NGX_MAXHOSTNAMELEN - 1] = '\0';
+ cycle->hostname.len = ngx_strlen(hostname);
+
+ cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);
+ if (cycle->hostname.data == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);
+
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_CORE_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->create_conf) {
+ rv = module->create_conf(cycle);
+ if (rv == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+ cycle->conf_ctx[ngx_modules[i]->index] = rv;
+ }
+ }
+
+
+ senv = environ;
+
+
+ ngx_memzero(&conf, sizeof(ngx_conf_t));
+ /* STUB: init array ? */
+ conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));
+ if (conf.args == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+ conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
+ if (conf.temp_pool == NULL) {
+ ngx_destroy_pool(pool);
+ return NULL;
+ }
+
+
+ conf.ctx = cycle->conf_ctx;
+ conf.cycle = cycle;
+ conf.pool = pool;
+ conf.log = log;
+ conf.module_type = NGX_CORE_MODULE;
+ conf.cmd_type = NGX_MAIN_CONF;
+
+#if 0
+ log->log_level = NGX_LOG_DEBUG_ALL;
+#endif
+
+ if (ngx_conf_param(&conf) != NGX_CONF_OK) {
+ environ = senv;
+ ngx_destroy_cycle_pools(&conf);
+ return NULL;
+ }
+
+ if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
+ environ = senv;
+ ngx_destroy_cycle_pools(&conf);
+ return NULL;
+ }
+
+ if (ngx_test_config && !ngx_quiet_mode) {
+ ngx_log_stderr(0, "the configuration file %s syntax is ok",
+ cycle->conf_file.data);
+ }
+
+ for (i = 0; ngx_modules[i]; i++) {
+ if (ngx_modules[i]->type != NGX_CORE_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[i]->ctx;
+
+ if (module->init_conf) {
+ if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
+ == NGX_CONF_ERROR)
+ {
+ environ = senv;
+ ngx_destroy_cycle_pools(&conf);
+ return NULL;
+ }
+ }
+ }
+
+ if (ngx_process == NGX_PROCESS_SIGNALLER) {
+ return cycle;
+ }
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ if (ngx_test_config) {
+
+ if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
+ goto failed;
+ }
+
+ } else if (!ngx_is_init_cycle(old_cycle)) {
+
+ /*
+ * we do not create the pid file in the first ngx_init_cycle() call
+ * because we need to write the demonized process pid
+ */
+
+ old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,
+ ngx_core_module);
+ if (ccf->pid.len != old_ccf->pid.len
+ || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)
+ {
+ /* new pid file name */
+
+ if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
+ goto failed;
+ }
+
+ ngx_delete_pidfile(old_cycle);
+ }
+ }
+
+
+ if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {
+ goto failed;
+ }
+
+
+ if (ngx_create_pathes(cycle, ccf->user) != NGX_OK) {
+ goto failed;
+ }
+
+
+ if (cycle->new_log.file == NULL) {
+ cycle->new_log.file = ngx_conf_open_file(cycle, &error_log);
+ if (cycle->new_log.file == NULL) {
+ goto failed;
+ }
+ }
+
+ /* open the new files */
+
+ part = &cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ if (file[i].name.len == 0) {
+ continue;
+ }
+
+ file[i].fd = ngx_open_file(file[i].name.data,
+ NGX_FILE_APPEND,
+ NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,
+ "log: %p %d \"%s\"",
+ &file[i], 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);
+ goto failed;
+ }
+
+#if !(NGX_WIN32)
+ if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ "fcntl(FD_CLOEXEC) \"%s\" failed",
+ file[i].name.data);
+ goto failed;
+ }
+#endif
+ }
+
+ cycle->log = &cycle->new_log;
+ pool->log = &cycle->new_log;
+
+
+ /* create shared memory */
+
+ part = &cycle->shared_memory.part;
+ shm_zone = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ shm_zone = part->elts;
+ i = 0;
+ }
+
+ if (shm_zone[i].shm.size == 0) {
+ ngx_log_error(NGX_LOG_EMERG, log, 0,
+ "zero size shared memory zone \"%V\"",
+ &shm_zone[i].shm.name);
+ goto failed;
+ }
+
+ if (shm_zone[i].init == NULL) {
+ /* unused shared zone */
+ continue;
+ }
+
+ shm_zone[i].shm.log = cycle->log;
+
+ opart = &old_cycle->shared_memory.part;
+ oshm_zone = opart->elts;
+
+ for (n = 0; /* void */ ; n++) {
+
+ if (n >= opart->nelts) {
+ if (opart->next == NULL) {
+ break;
+ }
+ opart = opart->next;
+ oshm_zone = opart->elts;
+ n = 0;
+ }
+
+ if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
+ continue;
+ }
+
+ if (ngx_strncmp(shm_zone[i].shm.name.data,
+ oshm_zone[n].shm.name.data,
+ shm_zone[i].shm.name.len)
+ != 0)
+ {
+ continue;
+ }
+
+ if (shm_zone[i].shm.size == oshm_zone[n].shm.size) {
+ shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
+
+ if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
+ != NGX_OK)
+ {
+ goto failed;
+ }
+
+ goto shm_zone_found;
+ }
+
+ ngx_shm_free(&oshm_zone[n].shm);
+
+ break;
+ }
+
+ if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
+ goto failed;
+ }
+
+ if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
+ goto failed;
+ }
+
+ if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
+ goto failed;
+ }
+
+ shm_zone_found:
+
+ continue;
+ }
+
+
+ /* handle the listening sockets */
+
+ 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;
+ }
+
+ if (ngx_cmp_sockaddr(nls[n].sockaddr, ls[i].sockaddr) == NGX_OK)
+ {
+ nls[n].fd = ls[i].fd;
+ nls[n].previous = &ls[i];
+ ls[i].remain = 1;
+
+ if (ls[n].backlog != nls[i].backlog) {
+ nls[n].listen = 1;
+ }
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+
+ /*
+ * FreeBSD, except the most recent versions,
+ * could not remove accept filter
+ */
+ nls[n].deferred_accept = ls[i].deferred_accept;
+
+ if (ls[i].accept_filter && nls[n].accept_filter) {
+ if (ngx_strcmp(ls[i].accept_filter,
+ nls[n].accept_filter)
+ != 0)
+ {
+ nls[n].delete_deferred = 1;
+ nls[n].add_deferred = 1;
+ }
+
+ } else if (ls[i].accept_filter) {
+ nls[n].delete_deferred = 1;
+
+ } else if (nls[n].accept_filter) {
+ nls[n].add_deferred = 1;
+ }
+#endif
+
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+
+ if (ls[n].deferred_accept && !nls[n].deferred_accept) {
+ nls[n].delete_deferred = 1;
+
+ } else if (ls[i].deferred_accept != nls[n].deferred_accept)
+ {
+ nls[n].add_deferred = 1;
+ }
+#endif
+ break;
+ }
+ }
+
+ if (nls[n].fd == -1) {
+ nls[n].open = 1;
+ }
+ }
+
+ } else {
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+ ls[i].open = 1;
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
+ if (ls[i].accept_filter) {
+ ls[i].add_deferred = 1;
+ }
+#endif
+#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)
+ if (ls[i].deferred_accept) {
+ ls[i].add_deferred = 1;
+ }
+#endif
+ }
+ }
+
+ if (ngx_open_listening_sockets(cycle) != NGX_OK) {
+ goto failed;
+ }
+
+ if (!ngx_test_config) {
+ ngx_configure_listening_sockets(cycle);
+ }
+
+
+ /* commit the new cycle configuration */
+
+ if (!ngx_use_stderr && cycle->log->file->fd != ngx_stderr) {
+
+ if (ngx_set_stderr(cycle->log->file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_set_stderr_n " failed");
+ }
+ }
+
+ 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_OK) {
+ /* fatal */
+ exit(1);
+ }
+ }
+ }
+
+
+ /* close and delete stuff that lefts from an old cycle */
+
+ /* free the unnecessary shared memory */
+
+ opart = &old_cycle->shared_memory.part;
+ oshm_zone = opart->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= opart->nelts) {
+ if (opart->next == NULL) {
+ goto old_shm_zone_done;
+ }
+ opart = opart->next;
+ oshm_zone = opart->elts;
+ i = 0;
+ }
+
+ part = &cycle->shared_memory.part;
+ shm_zone = part->elts;
+
+ for (n = 0; /* void */ ; n++) {
+
+ if (n >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ shm_zone = part->elts;
+ n = 0;
+ }
+
+ if (oshm_zone[i].shm.name.len == shm_zone[n].shm.name.len
+ && ngx_strncmp(oshm_zone[i].shm.name.data,
+ shm_zone[n].shm.name.data,
+ oshm_zone[i].shm.name.len)
+ == 0)
+ {
+ goto live_shm_zone;
+ }
+ }
+
+ ngx_shm_free(&oshm_zone[i].shm);
+
+ live_shm_zone:
+
+ continue;
+ }
+
+old_shm_zone_done:
+
+
+ /* close the unnecessary listening sockets */
+
+ ls = old_cycle->listening.elts;
+ for (i = 0; i < old_cycle->listening.nelts; i++) {
+
+ if (ls[i].remain || 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 " listening socket on %V failed",
+ &ls[i].addr_text);
+ }
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ if (ls[i].sockaddr->sa_family == AF_UNIX) {
+ u_char *name;
+
+ name = ls[i].addr_text.data + sizeof("unix:") - 1;
+
+ ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
+ "deleting socket %s", name);
+
+ if (ngx_delete_file(name) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
+ ngx_delete_file_n " %s failed", name);
+ }
+ }
+
+#endif
+ }
+
+
+ /* close the unnecessary open files */
+
+ part = &old_cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
+ 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);
+ }
+ }
+
+ ngx_destroy_pool(conf.temp_pool);
+
+ if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {
+
+ /*
+ * perl_destruct() frees environ, if it is not the same as it was at
+ * perl_construct() time, therefore we save the previous cycle
+ * environment before ngx_conf_parse() where it will be changed.
+ */
+
+ env = environ;
+ environ = senv;
+
+ ngx_destroy_pool(old_cycle->pool);
+ cycle->old_cycle = NULL;
+
+ environ = env;
+
+ 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.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_array_push(&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;
+
+
+failed:
+
+ if (!ngx_is_init_cycle(old_cycle)) {
+ old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,
+ ngx_core_module);
+ if (old_ccf->environment) {
+ environ = old_ccf->environment;
+ }
+ }
+
+ /* rollback the new cycle configuration */
+
+ part = &cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {
+ 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 (ngx_test_config) {
+ ngx_destroy_cycle_pools(&conf);
+ return NULL;
+ }
+
+ ls = cycle->listening.elts;
+ for (i = 0; i < cycle->listening.nelts; i++) {
+ if (ls[i].fd == -1 || !ls[i].open) {
+ continue;
+ }
+
+ if (ngx_close_socket(ls[i].fd) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,
+ ngx_close_socket_n " %V failed",
+ &ls[i].addr_text);
+ }
+ }
+
+ ngx_destroy_cycle_pools(&conf);
+
+ return NULL;
+}
+
+
+static void
+ngx_destroy_cycle_pools(ngx_conf_t *conf)
+{
+ ngx_destroy_pool(conf->temp_pool);
+ ngx_destroy_pool(conf->pool);
+}
+
+
+static ngx_int_t
+ngx_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2)
+{
+ struct sockaddr_in *sin1, *sin2;
+#if (NGX_HAVE_INET6)
+ struct sockaddr_in6 *sin61, *sin62;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un *saun1, *saun2;
+#endif
+
+ if (sa1->sa_family != sa2->sa_family) {
+ return NGX_DECLINED;
+ }
+
+ switch (sa1->sa_family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin61 = (struct sockaddr_in6 *) sa1;
+ sin62 = (struct sockaddr_in6 *) sa2;
+
+ if (sin61->sin6_port != sin62->sin6_port) {
+ return NGX_DECLINED;
+ }
+
+ if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) {
+ return NGX_DECLINED;
+ }
+
+ break;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+ case AF_UNIX:
+ saun1 = (struct sockaddr_un *) sa1;
+ saun2 = (struct sockaddr_un *) sa2;
+
+ if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path,
+ sizeof(saun1->sun_path))
+ != 0)
+ {
+ return NGX_DECLINED;
+ }
+
+ break;
+#endif
+
+ default: /* AF_INET */
+
+ sin1 = (struct sockaddr_in *) sa1;
+ sin2 = (struct sockaddr_in *) sa2;
+
+ if (sin1->sin_port != sin2->sin_port) {
+ return NGX_DECLINED;
+ }
+
+ if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {
+ return NGX_DECLINED;
+ }
+
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)
+{
+ u_char *file;
+ ngx_slab_pool_t *sp;
+
+ sp = (ngx_slab_pool_t *) zn->shm.addr;
+
+ if (zn->shm.exists) {
+
+ if (sp == sp->addr) {
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
+ "shared zone \"%V\" has no equal addresses: %p vs %p",
+ &zn->shm.name, sp->addr, sp);
+ return NGX_ERROR;
+ }
+
+ sp->end = zn->shm.addr + zn->shm.size;
+ sp->min_shift = 3;
+ sp->addr = zn->shm.addr;
+
+#if (NGX_HAVE_ATOMIC_OPS)
+
+ file = NULL;
+
+#else
+
+ file = ngx_pnalloc(cycle->pool, cycle->lock_file.len + zn->shm.name.len);
+ if (file == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_sprintf(file, "%V%V%Z", &cycle->lock_file, &zn->shm.name);
+
+#endif
+
+ if (ngx_shmtx_create(&sp->mutex, (void *) &sp->lock, file) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ ngx_slab_init(sp);
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)
+{
+ size_t len;
+ ngx_uint_t create;
+ ngx_file_t file;
+ u_char pid[NGX_INT64_LEN + 2];
+
+ if (ngx_process > NGX_PROCESS_MASTER) {
+ return NGX_OK;
+ }
+
+ ngx_memzero(&file, sizeof(ngx_file_t));
+
+ file.name = *name;
+ file.log = log;
+
+ create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;
+
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR,
+ create, NGX_FILE_DEFAULT_ACCESS);
+
+ if (file.fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", file.name.data);
+ return NGX_ERROR;
+ }
+
+ if (!ngx_test_config) {
+ len = ngx_snprintf(pid, NGX_INT64_LEN + 2, "%P%N", ngx_pid) - pid;
+
+ if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file.name.data);
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_delete_pidfile(ngx_cycle_t *cycle)
+{
+ u_char *name;
+ ngx_core_conf_t *ccf;
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data;
+
+ if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", name);
+ }
+}
+
+
+ngx_int_t
+ngx_signal_process(ngx_cycle_t *cycle, char *sig)
+{
+ ssize_t n;
+ ngx_int_t pid;
+ ngx_file_t file;
+ ngx_core_conf_t *ccf;
+ u_char buf[NGX_INT64_LEN + 2];
+
+ ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");
+
+ ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
+
+ file.name = ccf->pid;
+ file.log = cycle->log;
+
+ file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
+ NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);
+
+ if (file.fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", file.name.data);
+ return 1;
+ }
+
+ n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);
+
+ if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file.name.data);
+ }
+
+ if (n == NGX_ERROR) {
+ return 1;
+ }
+
+ while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
+
+ pid = ngx_atoi(buf, ++n);
+
+ if (pid == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
+ "invalid PID number \"%*s\" in \"%s\"",
+ n, buf, file.name.data);
+ return 1;
+ }
+
+ return ngx_os_signal_process(cycle, sig, pid);
+
+}
+
+
+static ngx_int_t
+ngx_test_lockfile(u_char *file, ngx_log_t *log)
+{
+#if !(NGX_HAVE_ATOMIC_OPS)
+ ngx_fd_t fd;
+
+ fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", file);
+ return NGX_ERROR;
+ }
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file);
+ }
+
+ if (ngx_delete_file(file) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", file);
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+void
+ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)
+{
+ ssize_t n, len;
+ ngx_fd_t fd;
+ ngx_uint_t i;
+ ngx_list_part_t *part;
+ ngx_open_file_t *file;
+
+ part = &cycle->open_files.part;
+ file = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ file = part->elts;
+ i = 0;
+ }
+
+ if (file[i].name.len == 0) {
+ continue;
+ }
+
+ len = file[i].pos - file[i].buffer;
+
+ if (file[i].buffer && len != 0) {
+
+ n = ngx_write_fd(file[i].fd, file[i].buffer, len);
+
+ if (n == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
+ ngx_write_fd_n " to \"%s\" failed",
+ file[i].name.data);
+
+ } else if (n != len) {
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
+ ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
+ file[i].name.data, n, len);
+ }
+
+ file[i].pos = file[i].buffer;
+ }
+
+ fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND,
+ NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);
+
+ 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 !(NGX_WIN32)
+ if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {
+ ngx_file_info_t fi;
+
+ if (ngx_file_info((const char *) file[i].name.data, &fi)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ ngx_file_info_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);
+ }
+ }
+
+ if (fi.st_uid != user) {
+ if (chown((const char *) file[i].name.data, user, -1) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "chown(\"%s\", %d) failed",
+ file[i].name.data, user);
+
+ 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);
+ }
+ }
+ }
+
+ if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) {
+
+ fi.st_mode |= (S_IRUSR|S_IWUSR);
+
+ if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "chmod() \"%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);
+ }
+ }
+ }
+ }
+
+ if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "fcntl(FD_CLOEXEC) \"%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 !(NGX_WIN32)
+
+ if (cycle->log->file->fd != STDERR_FILENO) {
+ if (dup2(cycle->log->file->fd, STDERR_FILENO) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "dup2(STDERR) failed");
+ }
+ }
+
+#endif
+}
+
+
+ngx_shm_zone_t *
+ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)
+{
+ ngx_uint_t i;
+ ngx_shm_zone_t *shm_zone;
+ ngx_list_part_t *part;
+
+ part = &cf->cycle->shared_memory.part;
+ shm_zone = part->elts;
+
+ for (i = 0; /* void */ ; i++) {
+
+ if (i >= part->nelts) {
+ if (part->next == NULL) {
+ break;
+ }
+ part = part->next;
+ shm_zone = part->elts;
+ i = 0;
+ }
+
+ if (name->len != shm_zone[i].shm.name.len) {
+ continue;
+ }
+
+ if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)
+ != 0)
+ {
+ continue;
+ }
+
+ if (size && size != shm_zone[i].shm.size) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the size %uz of shared memory zone \"%V\" "
+ "conflicts with already declared size %uz",
+ size, &shm_zone[i].shm.name, shm_zone[i].shm.size);
+ return NULL;
+ }
+
+ if (tag != shm_zone[i].tag) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the shared memory zone \"%V\" is "
+ "already declared for a different use",
+ &shm_zone[i].shm.name);
+ return NULL;
+ }
+
+ return &shm_zone[i];
+ }
+
+ shm_zone = ngx_list_push(&cf->cycle->shared_memory);
+
+ if (shm_zone == NULL) {
+ return NULL;
+ }
+
+ shm_zone->data = NULL;
+ shm_zone->shm.log = cf->cycle->log;
+ shm_zone->shm.size = size;
+ shm_zone->shm.name = *name;
+ shm_zone->shm.exists = 0;
+ shm_zone->init = NULL;
+ shm_zone->tag = tag;
+
+ return shm_zone;
+}
+
+
+static void
+ngx_clean_old_cycles(ngx_event_t *ev)
+{
+ ngx_uint_t i, n, found, live;
+ ngx_log_t *log;
+ ngx_cycle_t **cycle;
+
+ log = ngx_cycle->log;
+ ngx_temp_pool->log = log;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "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 != (ngx_socket_t) -1) {
+ found = 1;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "live fd:%d", n);
+
+ break;
+ }
+ }
+
+ if (found) {
+ live = 1;
+ continue;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "clean old cycle: %d", i);
+
+ ngx_destroy_pool(cycle[i]->pool);
+ cycle[i] = NULL;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, "old cycles status: %d", live);
+
+ if (live) {
+ ngx_add_timer(ev, 30000);
+
+ } else {
+ ngx_destroy_pool(ngx_temp_pool);
+ ngx_temp_pool = NULL;
+ ngx_old_cycles.nelts = 0;
+ }
+}
diff --git a/usr.sbin/nginx/src/core/ngx_cycle.h b/usr.sbin/nginx/src/core/ngx_cycle.h
new file mode 100644
index 00000000000..e14983d6f1b
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_cycle.h
@@ -0,0 +1,141 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_CYCLE_H_INCLUDED_
+#define _NGX_CYCLE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef NGX_CYCLE_POOL_SIZE
+#define NGX_CYCLE_POOL_SIZE 16384
+#endif
+
+
+#define NGX_DEBUG_POINTS_STOP 1
+#define NGX_DEBUG_POINTS_ABORT 2
+
+
+typedef struct ngx_shm_zone_s ngx_shm_zone_t;
+
+typedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);
+
+struct ngx_shm_zone_s {
+ void *data;
+ ngx_shm_t shm;
+ ngx_shm_zone_init_pt init;
+ void *tag;
+};
+
+
+struct ngx_cycle_s {
+ void ****conf_ctx;
+ ngx_pool_t *pool;
+
+ ngx_log_t *log;
+ ngx_log_t new_log;
+
+ ngx_connection_t **files;
+ ngx_connection_t *free_connections;
+ ngx_uint_t free_connection_n;
+
+ ngx_queue_t reusable_connections_queue;
+
+ ngx_array_t listening;
+ ngx_array_t pathes;
+ ngx_list_t open_files;
+ ngx_list_t shared_memory;
+
+ ngx_uint_t connection_n;
+ ngx_uint_t files_n;
+
+ ngx_connection_t *connections;
+ ngx_event_t *read_events;
+ ngx_event_t *write_events;
+
+ ngx_cycle_t *old_cycle;
+
+ ngx_str_t conf_file;
+ ngx_str_t conf_param;
+ ngx_str_t conf_prefix;
+ ngx_str_t prefix;
+ ngx_str_t lock_file;
+ ngx_str_t hostname;
+};
+
+
+typedef struct {
+ ngx_flag_t daemon;
+ ngx_flag_t master;
+
+ ngx_msec_t timer_resolution;
+
+ ngx_int_t worker_processes;
+ ngx_int_t debug_points;
+
+ ngx_int_t rlimit_nofile;
+ ngx_int_t rlimit_sigpending;
+ off_t rlimit_core;
+
+ int priority;
+
+ ngx_uint_t cpu_affinity_n;
+ u_long *cpu_affinity;
+
+ char *username;
+ ngx_uid_t user;
+ ngx_gid_t group;
+
+ ngx_str_t working_directory;
+ ngx_str_t lock_file;
+
+ ngx_str_t pid;
+ ngx_str_t oldpid;
+
+ ngx_array_t env;
+ char **environment;
+
+#if (NGX_THREADS)
+ ngx_int_t worker_threads;
+ size_t thread_stack_size;
+#endif
+
+} ngx_core_conf_t;
+
+
+typedef struct {
+ ngx_pool_t *pool; /* pcre's malloc() pool */
+} ngx_core_tls_t;
+
+
+#define ngx_is_init_cycle(cycle) (cycle->conf_ctx == NULL)
+
+
+ngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);
+ngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log);
+void ngx_delete_pidfile(ngx_cycle_t *cycle);
+ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig);
+void ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);
+char **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last);
+ngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);
+u_long ngx_get_cpu_affinity(ngx_uint_t n);
+ngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,
+ size_t size, void *tag);
+
+
+extern volatile ngx_cycle_t *ngx_cycle;
+extern ngx_array_t ngx_old_cycles;
+extern ngx_module_t ngx_core_module;
+extern ngx_uint_t ngx_test_config;
+extern ngx_uint_t ngx_quiet_mode;
+#if (NGX_THREADS)
+extern ngx_tls_key_t ngx_core_tls_key;
+#endif
+
+
+#endif /* _NGX_CYCLE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_file.c b/usr.sbin/nginx/src/core/ngx_file.c
new file mode 100644
index 00000000000..7704c3e1736
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_file.c
@@ -0,0 +1,992 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static ngx_atomic_t temp_number = 0;
+ngx_atomic_t *ngx_temp_number = &temp_number;
+ngx_atomic_int_t ngx_random_number = 123456;
+
+
+ssize_t
+ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)
+{
+ ngx_int_t rc;
+
+ if (tf->file.fd == NGX_INVALID_FILE) {
+ rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,
+ tf->persistent, tf->clean, tf->access);
+
+ if (rc == NGX_ERROR || rc == NGX_AGAIN) {
+ return rc;
+ }
+
+ if (tf->log_level) {
+ ngx_log_error(tf->log_level, tf->file.log, 0, "%s %V",
+ tf->warn, &tf->file.name);
+ }
+ }
+
+ return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);
+}
+
+
+ngx_int_t
+ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,
+ ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)
+{
+ uint32_t n;
+ ngx_err_t err;
+ ngx_pool_cleanup_t *cln;
+ ngx_pool_cleanup_file_t *clnf;
+
+ file->name.len = path->name.len + 1 + path->len + 10;
+
+ file->name.data = ngx_pnalloc(pool, file->name.len + 1);
+ if (file->name.data == NULL) {
+ return NGX_ERROR;
+ }
+
+#if 0
+ for (i = 0; i < file->name.len; i++) {
+ file->name.data[i] = 'X';
+ }
+#endif
+
+ ngx_memcpy(file->name.data, path->name.data, path->name.len);
+
+ n = (uint32_t) ngx_next_temp_number(0);
+
+ cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ for ( ;; ) {
+ (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
+ "%010uD%Z", n);
+
+ ngx_create_hashed_filename(path, file->name.data, file->name.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "hashed path: %s", file->name.data);
+
+ file->fd = ngx_open_tempfile(file->name.data, persistent, access);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "temp fd:%d", file->fd);
+
+ if (file->fd != NGX_INVALID_FILE) {
+
+ cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;
+ clnf = cln->data;
+
+ clnf->fd = file->fd;
+ clnf->name = file->name.data;
+ clnf->log = pool->log;
+
+ return NGX_OK;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_EEXIST) {
+ n = (uint32_t) ngx_next_temp_number(1);
+ continue;
+ }
+
+ if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ ngx_open_tempfile_n " \"%s\" failed",
+ file->name.data);
+ return NGX_ERROR;
+ }
+
+ if (ngx_create_path(file, path) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+}
+
+
+void
+ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)
+{
+ size_t i, level;
+ ngx_uint_t n;
+
+ i = path->name.len + 1;
+
+ file[path->name.len + path->len] = '/';
+
+ for (n = 0; n < 3; n++) {
+ level = path->level[n];
+
+ if (level == 0) {
+ break;
+ }
+
+ len -= level;
+ file[i - 1] = '/';
+ ngx_memcpy(&file[i], &file[len], level);
+ i += level + 1;
+ }
+}
+
+
+ngx_int_t
+ngx_create_path(ngx_file_t *file, ngx_path_t *path)
+{
+ size_t pos;
+ ngx_err_t err;
+ ngx_uint_t i;
+
+ pos = path->name.len;
+
+ for (i = 0; i < 3; i++) {
+ if (path->level[i] == 0) {
+ break;
+ }
+
+ pos += path->level[i] + 1;
+
+ file->name.data[pos] = '\0';
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+ "temp file: \"%s\"", file->name.data);
+
+ if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+ if (err != NGX_EEXIST) {
+ ngx_log_error(NGX_LOG_CRIT, file->log, err,
+ ngx_create_dir_n " \"%s\" failed",
+ file->name.data);
+ return NGX_ERROR;
+ }
+ }
+
+ file->name.data[pos] = '/';
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_err_t
+ngx_create_full_path(u_char *dir, ngx_uint_t access)
+{
+ u_char *p, ch;
+ ngx_err_t err;
+
+ err = 0;
+
+#if (NGX_WIN32)
+ p = dir + 3;
+#else
+ p = dir + 1;
+#endif
+
+ for ( /* void */ ; *p; p++) {
+ ch = *p;
+
+ if (ch != '/') {
+ continue;
+ }
+
+ *p = '\0';
+
+ if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ switch (err) {
+ case NGX_EEXIST:
+ err = 0;
+ case NGX_EACCES:
+ break;
+
+ default:
+ return err;
+ }
+ }
+
+ *p = '/';
+ }
+
+ return err;
+}
+
+
+ngx_atomic_uint_t
+ngx_next_temp_number(ngx_uint_t collision)
+{
+ ngx_atomic_uint_t n, add;
+
+ add = collision ? ngx_random_number : 1;
+
+ n = ngx_atomic_fetch_add(ngx_temp_number, add);
+
+ return n + add;
+}
+
+
+char *
+ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ssize_t level;
+ ngx_str_t *value;
+ ngx_uint_t i, n;
+ ngx_path_t *path, **slot;
+
+ slot = (ngx_path_t **) (p + cmd->offset);
+
+ if (*slot) {
+ return "is duplicate";
+ }
+
+ path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
+ if (path == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ value = cf->args->elts;
+
+ path->name = value[1];
+
+ if (path->name.data[path->name.len - 1] == '/') {
+ path->name.len--;
+ }
+
+ if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {
+ return NULL;
+ }
+
+ path->len = 0;
+ path->manager = NULL;
+ path->loader = NULL;
+ path->conf_file = cf->conf_file->file.name.data;
+ path->line = cf->conf_file->line;
+
+ for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
+ level = ngx_atoi(value[n].data, value[n].len);
+ if (level == NGX_ERROR || level == 0) {
+ return "invalid value";
+ }
+
+ path->level[i] = level;
+ path->len += level + 1;
+ }
+
+ while (i < 3) {
+ path->level[i++] = 0;
+ }
+
+ *slot = path;
+
+ if (ngx_add_path(cf, slot) == NGX_ERROR) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,
+ ngx_path_init_t *init)
+{
+ if (*path) {
+ return NGX_CONF_OK;
+ }
+
+ if (prev) {
+ *path = prev;
+ return NGX_CONF_OK;
+ }
+
+ *path = ngx_palloc(cf->pool, sizeof(ngx_path_t));
+ if (*path == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ (*path)->name = init->name;
+
+ if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ (*path)->level[0] = init->level[0];
+ (*path)->level[1] = init->level[1];
+ (*path)->level[2] = init->level[2];
+
+ (*path)->len = init->level[0] + (init->level[0] ? 1 : 0)
+ + init->level[1] + (init->level[1] ? 1 : 0)
+ + init->level[2] + (init->level[2] ? 1 : 0);
+
+ (*path)->manager = NULL;
+ (*path)->loader = NULL;
+ (*path)->conf_file = NULL;
+
+ if (ngx_add_path(cf, path) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+char *
+ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *confp = conf;
+
+ u_char *p;
+ ngx_str_t *value;
+ ngx_uint_t i, right, shift, *access;
+
+ access = (ngx_uint_t *) (confp + cmd->offset);
+
+ if (*access != NGX_CONF_UNSET_UINT) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ *access = 0600;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ p = value[i].data;
+
+ if (ngx_strncmp(p, "user:", sizeof("user:") - 1) == 0) {
+ shift = 6;
+ p += sizeof("user:") - 1;
+
+ } else if (ngx_strncmp(p, "group:", sizeof("group:") - 1) == 0) {
+ shift = 3;
+ p += sizeof("group:") - 1;
+
+ } else if (ngx_strncmp(p, "all:", sizeof("all:") - 1) == 0) {
+ shift = 0;
+ p += sizeof("all:") - 1;
+
+ } else {
+ goto invalid;
+ }
+
+ if (ngx_strcmp(p, "rw") == 0) {
+ right = 6;
+
+ } else if (ngx_strcmp(p, "r") == 0) {
+ right = 4;
+
+ } else {
+ goto invalid;
+ }
+
+ *access |= right << shift;
+ }
+
+ return NGX_CONF_OK;
+
+invalid:
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid value \"%V\"", &value[i]);
+
+ return NGX_CONF_ERROR;
+}
+
+
+ngx_int_t
+ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)
+{
+ ngx_uint_t i, n;
+ ngx_path_t *path, **p;
+
+ path = *slot;
+
+ p = cf->cycle->pathes.elts;
+ for (i = 0; i < cf->cycle->pathes.nelts; i++) {
+ if (p[i]->name.len == path->name.len
+ && ngx_strcmp(p[i]->name.data, path->name.data) == 0)
+ {
+ for (n = 0; n < 3; n++) {
+ if (p[i]->level[n] != path->level[n]) {
+ if (path->conf_file == NULL) {
+ if (p[i]->conf_file == NULL) {
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "the default path name \"%V\" has "
+ "the same name as another default path, "
+ "but the different levels, you need to "
+ "redefine one of them in http section",
+ &p[i]->name);
+ return NGX_ERROR;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, cf->log, 0,
+ "the path name \"%V\" in %s:%ui has "
+ "the same name as default path, but "
+ "the different levels, you need to "
+ "define default path in http section",
+ &p[i]->name, p[i]->conf_file, p[i]->line);
+ return NGX_ERROR;
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the same path name \"%V\" in %s:%ui "
+ "has the different levels than",
+ &p[i]->name, p[i]->conf_file, p[i]->line);
+ return NGX_ERROR;
+ }
+
+ if (p[i]->level[n] == 0) {
+ break;
+ }
+ }
+
+ *slot = p[i];
+
+ return NGX_OK;
+ }
+ }
+
+ p = ngx_array_push(&cf->cycle->pathes);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ *p = path;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user)
+{
+ ngx_err_t err;
+ ngx_uint_t i;
+ ngx_path_t **path;
+
+ path = cycle->pathes.elts;
+ for (i = 0; i < cycle->pathes.nelts; i++) {
+
+ if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+ if (err != NGX_EEXIST) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, err,
+ ngx_create_dir_n " \"%s\" failed",
+ path[i]->name.data);
+ return NGX_ERROR;
+ }
+ }
+
+ if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {
+ continue;
+ }
+
+#if !(NGX_WIN32)
+ {
+ ngx_file_info_t fi;
+
+ if (ngx_file_info((const char *) path[i]->name.data, &fi)
+ == NGX_FILE_ERROR)
+ {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ ngx_file_info_n " \"%s\" failed", path[i]->name.data);
+ return NGX_ERROR;
+ }
+
+ if (fi.st_uid != user) {
+ if (chown((const char *) path[i]->name.data, user, -1) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "chown(\"%s\", %d) failed",
+ path[i]->name.data, user);
+ return NGX_ERROR;
+ }
+ }
+
+ if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))
+ != (S_IRUSR|S_IWUSR|S_IXUSR))
+ {
+ fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);
+
+ if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {
+ ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
+ "chmod() \"%s\" failed", path[i]->name.data);
+ return NGX_ERROR;
+ }
+ }
+ }
+#endif
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)
+{
+ u_char *name;
+ ngx_err_t err;
+ ngx_copy_file_t cf;
+
+#if !(NGX_WIN32)
+
+ if (ext->access) {
+ if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_change_file_access_n " \"%s\" failed", src->data);
+ err = 0;
+ goto failed;
+ }
+ }
+
+#endif
+
+ if (ext->time != -1) {
+ if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_set_file_time_n " \"%s\" failed", src->data);
+ err = 0;
+ goto failed;
+ }
+ }
+
+ if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
+ return NGX_OK;
+ }
+
+ err = ngx_errno;
+
+ if (err == NGX_ENOPATH) {
+
+ if (!ext->create_path) {
+ goto failed;
+ }
+
+ err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));
+
+ if (err) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, err,
+ ngx_create_dir_n " \"%s\" failed", to->data);
+ err = 0;
+ goto failed;
+ }
+
+ if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {
+ return NGX_OK;
+ }
+
+ err = ngx_errno;
+ }
+
+#if (NGX_WIN32)
+
+ if (err == NGX_EEXIST) {
+ err = ngx_win32_rename_file(src, to, ext->log);
+
+ if (err == 0) {
+ return NGX_OK;
+ }
+ }
+
+#endif
+
+ if (err == NGX_EXDEV) {
+
+ cf.size = -1;
+ cf.buf_size = 0;
+ cf.access = ext->access;
+ cf.time = ext->time;
+ cf.log = ext->log;
+
+ name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_sprintf(name, "%*s.%010uD%Z", to->len, to->data,
+ (uint32_t) ngx_next_temp_number(0));
+
+ if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {
+
+ if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) {
+ ngx_free(name);
+
+ if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed",
+ src->data);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+ }
+
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_rename_file_n " \"%s\" to \"%s\" failed",
+ name, to->data);
+
+ if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", name);
+
+ }
+ }
+
+ ngx_free(name);
+
+ err = 0;
+ }
+
+failed:
+
+ if (ext->delete_file) {
+ if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", src->data);
+ }
+ }
+
+ if (err) {
+ ngx_log_error(NGX_LOG_CRIT, ext->log, err,
+ ngx_rename_file_n " \"%s\" to \"%s\" failed",
+ src->data, to->data);
+ }
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)
+{
+ char *buf;
+ off_t size;
+ size_t len;
+ ssize_t n;
+ ngx_fd_t fd, nfd;
+ ngx_int_t rc;
+ ngx_file_info_t fi;
+
+ rc = NGX_ERROR;
+ buf = NULL;
+ nfd = NGX_INVALID_FILE;
+
+ fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);
+
+ if (fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", from);
+ goto failed;
+ }
+
+ if (cf->size != -1) {
+ size = cf->size;
+
+ } else {
+ if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", from);
+
+ goto failed;
+ }
+
+ size = ngx_file_size(&fi);
+ }
+
+ len = cf->buf_size ? cf->buf_size : 65536;
+
+ if ((off_t) len > size) {
+ len = (size_t) size;
+ }
+
+ buf = ngx_alloc(len, cf->log);
+ if (buf == NULL) {
+ goto failed;
+ }
+
+ nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN,
+ cf->access);
+
+ if (nfd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", to);
+ goto failed;
+ }
+
+ while (size > 0) {
+
+ if ((off_t) len > size) {
+ len = (size_t) size;
+ }
+
+ n = ngx_read_fd(fd, buf, len);
+
+ if (n == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_read_fd_n " \"%s\" failed", from);
+ goto failed;
+ }
+
+ if ((size_t) n != len) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_read_fd_n " has read only %z of %uz from %s",
+ n, size, from);
+ goto failed;
+ }
+
+ n = ngx_write_fd(nfd, buf, len);
+
+ if (n == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_write_fd_n " \"%s\" failed", to);
+ goto failed;
+ }
+
+ if ((size_t) n != len) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_write_fd_n " has written only %z of %uz to %s",
+ n, size, to);
+ goto failed;
+ }
+
+ size -= n;
+ }
+
+ if (cf->time != -1) {
+ if (ngx_set_file_time(to, nfd, cf->time) != NGX_OK) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_set_file_time_n " \"%s\" failed", to);
+ goto failed;
+ }
+ }
+
+ rc = NGX_OK;
+
+failed:
+
+ if (nfd != NGX_INVALID_FILE) {
+ if (ngx_close_file(nfd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", to);
+ }
+ }
+
+ if (fd != NGX_INVALID_FILE) {
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", from);
+ }
+ }
+
+ if (buf) {
+ ngx_free(buf);
+ }
+
+ return rc;
+}
+
+
+/*
+ * ctx->init_handler() - see ctx->alloc
+ * ctx->file_handler() - file handler
+ * ctx->pre_tree_handler() - handler is called before entering directory
+ * ctx->post_tree_handler() - handler is called after leaving directory
+ * ctx->spec_handler() - special (socket, FIFO, etc.) file handler
+ *
+ * ctx->data - some data structure, it may be the same on all levels, or
+ * reallocated if ctx->alloc is nonzero
+ *
+ * ctx->alloc - a size of data structure that is allocated at every level
+ * and is initilialized by ctx->init_handler()
+ *
+ * ctx->log - a log
+ *
+ * on fatal (memory) error handler must return NGX_ABORT to stop walking tree
+ */
+
+ngx_int_t
+ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)
+{
+ void *data, *prev;
+ u_char *p, *name;
+ size_t len;
+ ngx_int_t rc;
+ ngx_err_t err;
+ ngx_str_t file, buf;
+ ngx_dir_t dir;
+
+ ngx_str_null(&buf);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "walk tree \"%V\"", tree);
+
+ if (ngx_open_dir(tree, &dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+ ngx_open_dir_n " \"%s\" failed", tree->data);
+ return NGX_ERROR;
+ }
+
+ prev = ctx->data;
+
+ if (ctx->alloc) {
+ data = ngx_alloc(ctx->alloc, ctx->log);
+ if (data == NULL) {
+ goto failed;
+ }
+
+ if (ctx->init_handler(data, prev) == NGX_ABORT) {
+ goto failed;
+ }
+
+ ctx->data = data;
+
+ } else {
+ data = NULL;
+ }
+
+ for ( ;; ) {
+
+ ngx_set_errno(0);
+
+ if (ngx_read_dir(&dir) == NGX_ERROR) {
+ err = ngx_errno;
+
+ if (err == NGX_ENOMOREFILES) {
+ rc = NGX_OK;
+
+ } else {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, err,
+ ngx_read_dir_n " \"%s\" failed", tree->data);
+ rc = NGX_ERROR;
+ }
+
+ goto done;
+ }
+
+ len = ngx_de_namelen(&dir);
+ name = ngx_de_name(&dir);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "tree name %uz:\"%s\"", len, name);
+
+ if (len == 1 && name[0] == '.') {
+ continue;
+ }
+
+ if (len == 2 && name[0] == '.' && name[1] == '.') {
+ continue;
+ }
+
+ file.len = tree->len + 1 + len;
+
+ if (file.len + NGX_DIR_MASK_LEN > buf.len) {
+
+ if (buf.len) {
+ ngx_free(buf.data);
+ }
+
+ buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;
+
+ buf.data = ngx_alloc(buf.len + 1, ctx->log);
+ if (buf.data == NULL) {
+ goto failed;
+ }
+ }
+
+ p = ngx_cpymem(buf.data, tree->data, tree->len);
+ *p++ = '/';
+ ngx_memcpy(p, name, len + 1);
+
+ file.data = buf.data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "tree path \"%s\"", file.data);
+
+ if (!dir.valid_info) {
+ if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+ ngx_de_info_n " \"%s\" failed", file.data);
+ continue;
+ }
+ }
+
+ if (ngx_de_is_file(&dir)) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "tree file \"%s\"", file.data);
+
+ ctx->size = ngx_de_size(&dir);
+ ctx->fs_size = ngx_de_fs_size(&dir);
+ ctx->access = ngx_de_access(&dir);
+ ctx->mtime = ngx_de_mtime(&dir);
+
+ if (ctx->file_handler(ctx, &file) == NGX_ABORT) {
+ goto failed;
+ }
+
+ } else if (ngx_de_is_dir(&dir)) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "tree enter dir \"%s\"", file.data);
+
+ ctx->access = ngx_de_access(&dir);
+ ctx->mtime = ngx_de_mtime(&dir);
+
+ if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {
+ goto failed;
+ }
+
+ if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {
+ goto failed;
+ }
+
+ ctx->access = ngx_de_access(&dir);
+ ctx->mtime = ngx_de_mtime(&dir);
+
+ if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {
+ goto failed;
+ }
+
+ } else {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,
+ "tree special \"%s\"", file.data);
+
+ if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {
+ goto failed;
+ }
+ }
+ }
+
+failed:
+
+ rc = NGX_ABORT;
+
+done:
+
+ if (buf.len) {
+ ngx_free(buf.data);
+ }
+
+ if (data) {
+ ngx_free(data);
+ ctx->data = prev;
+ }
+
+ if (ngx_close_dir(&dir) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,
+ ngx_close_dir_n " \"%s\" failed", tree->data);
+ }
+
+ return rc;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_file.h b/usr.sbin/nginx/src/core/ngx_file.h
new file mode 100644
index 00000000000..88035172d7f
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_file.h
@@ -0,0 +1,150 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_FILE_H_INCLUDED_
+#define _NGX_FILE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+struct ngx_file_s {
+ ngx_fd_t fd;
+ ngx_str_t name;
+ ngx_file_info_t info;
+
+ off_t offset;
+ off_t sys_offset;
+
+ ngx_log_t *log;
+
+#if (NGX_HAVE_FILE_AIO)
+ ngx_event_aio_t *aio;
+#endif
+
+ unsigned valid_info:1;
+ unsigned directio:1;
+};
+
+
+#define NGX_MAX_PATH_LEVEL 3
+
+
+typedef time_t (*ngx_path_manager_pt) (void *data);
+typedef void (*ngx_path_loader_pt) (void *data);
+
+
+typedef struct {
+ ngx_str_t name;
+ size_t len;
+ size_t level[3];
+
+ ngx_path_manager_pt manager;
+ ngx_path_loader_pt loader;
+ void *data;
+
+ u_char *conf_file;
+ ngx_uint_t line;
+} ngx_path_t;
+
+
+typedef struct {
+ ngx_str_t name;
+ size_t level[3];
+} ngx_path_init_t;
+
+
+typedef struct {
+ ngx_file_t file;
+ off_t offset;
+ ngx_path_t *path;
+ ngx_pool_t *pool;
+ char *warn;
+
+ ngx_uint_t access;
+
+ unsigned log_level:8;
+ unsigned persistent:1;
+ unsigned clean:1;
+} ngx_temp_file_t;
+
+
+typedef struct {
+ ngx_uint_t access;
+ ngx_uint_t path_access;
+ time_t time;
+ ngx_fd_t fd;
+
+ unsigned create_path:1;
+ unsigned delete_file:1;
+
+ ngx_log_t *log;
+} ngx_ext_rename_file_t;
+
+
+typedef struct {
+ off_t size;
+ size_t buf_size;
+
+ ngx_uint_t access;
+ time_t time;
+
+ ngx_log_t *log;
+} ngx_copy_file_t;
+
+
+typedef struct ngx_tree_ctx_s ngx_tree_ctx_t;
+
+typedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);
+typedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name);
+
+struct ngx_tree_ctx_s {
+ off_t size;
+ off_t fs_size;
+ ngx_uint_t access;
+ time_t mtime;
+
+ ngx_tree_init_handler_pt init_handler;
+ ngx_tree_handler_pt file_handler;
+ ngx_tree_handler_pt pre_tree_handler;
+ ngx_tree_handler_pt post_tree_handler;
+ ngx_tree_handler_pt spec_handler;
+
+ void *data;
+ size_t alloc;
+
+ ngx_log_t *log;
+};
+
+
+ssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain);
+ngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,
+ ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean,
+ ngx_uint_t access);
+void ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len);
+ngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path);
+ngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);
+ngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);
+ngx_int_t ngx_create_pathes(ngx_cycle_t *cycle, ngx_uid_t user);
+ngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,
+ ngx_ext_rename_file_t *ext);
+ngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);
+ngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);
+
+ngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision);
+
+char *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+char *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path,
+ ngx_path_t *prev, ngx_path_init_t *init);
+char *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+extern ngx_atomic_t *ngx_temp_number;
+extern ngx_atomic_int_t ngx_random_number;
+
+
+#endif /* _NGX_FILE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_hash.c b/usr.sbin/nginx/src/core/ngx_hash.c
new file mode 100644
index 00000000000..0c7285202fb
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_hash.c
@@ -0,0 +1,975 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+void *
+ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)
+{
+ ngx_uint_t i;
+ ngx_hash_elt_t *elt;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "hf:\"%*s\"", len, name);
+#endif
+
+ elt = hash->buckets[key % hash->size];
+
+ if (elt == NULL) {
+ return NULL;
+ }
+
+ while (elt->value) {
+ if (len != (size_t) elt->len) {
+ goto next;
+ }
+
+ for (i = 0; i < len; i++) {
+ if (name[i] != elt->name[i]) {
+ goto next;
+ }
+ }
+
+ return elt->value;
+
+ next:
+
+ elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
+ sizeof(void *));
+ continue;
+ }
+
+ return NULL;
+}
+
+
+void *
+ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
+{
+ void *value;
+ ngx_uint_t i, n, key;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wch:\"%*s\"", len, name);
+#endif
+
+ n = len;
+
+ while (n) {
+ if (name[n - 1] == '.') {
+ break;
+ }
+
+ n--;
+ }
+
+ key = 0;
+
+ for (i = n; i < len; i++) {
+ key = ngx_hash(key, name[i]);
+ }
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
+#endif
+
+ value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);
+#endif
+
+ if (value) {
+
+ /*
+ * the 2 low bits of value have the special meaning:
+ * 00 - value is data pointer for both "example.com"
+ * and "*.example.com";
+ * 01 - value is data pointer for "*.example.com" only;
+ * 10 - value is pointer to wildcard hash allowing
+ * both "example.com" and "*.example.com";
+ * 11 - value is pointer to wildcard hash allowing
+ * "*.example.com" only.
+ */
+
+ if ((uintptr_t) value & 2) {
+
+ if (n == 0) {
+
+ /* "example.com" */
+
+ if ((uintptr_t) value & 1) {
+ return NULL;
+ }
+
+ hwc = (ngx_hash_wildcard_t *)
+ ((uintptr_t) value & (uintptr_t) ~3);
+ return hwc->value;
+ }
+
+ hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
+
+ value = ngx_hash_find_wc_head(hwc, name, n - 1);
+
+ if (value) {
+ return value;
+ }
+
+ return hwc->value;
+ }
+
+ if ((uintptr_t) value & 1) {
+
+ if (n == 0) {
+
+ /* "example.com" */
+
+ return NULL;
+ }
+
+ return (void *) ((uintptr_t) value & (uintptr_t) ~3);
+ }
+
+ return value;
+ }
+
+ return hwc->value;
+}
+
+
+void *
+ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)
+{
+ void *value;
+ ngx_uint_t i, key;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "wct:\"%*s\"", len, name);
+#endif
+
+ key = 0;
+
+ for (i = 0; i < len; i++) {
+ if (name[i] == '.') {
+ break;
+ }
+
+ key = ngx_hash(key, name[i]);
+ }
+
+ if (i == len) {
+ return NULL;
+ }
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "key:\"%ui\"", key);
+#endif
+
+ value = ngx_hash_find(&hwc->hash, key, name, i);
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "value:\"%p\"", value);
+#endif
+
+ if (value) {
+
+ /*
+ * the 2 low bits of value have the special meaning:
+ * 00 - value is data pointer;
+ * 11 - value is pointer to wildcard hash allowing "example.*".
+ */
+
+ if ((uintptr_t) value & 2) {
+
+ i++;
+
+ hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);
+
+ value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);
+
+ if (value) {
+ return value;
+ }
+
+ return hwc->value;
+ }
+
+ return value;
+ }
+
+ return hwc->value;
+}
+
+
+void *
+ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,
+ size_t len)
+{
+ void *value;
+
+ if (hash->hash.buckets) {
+ value = ngx_hash_find(&hash->hash, key, name, len);
+
+ if (value) {
+ return value;
+ }
+ }
+
+ if (len == 0) {
+ return NULL;
+ }
+
+ if (hash->wc_head && hash->wc_head->hash.buckets) {
+ value = ngx_hash_find_wc_head(hash->wc_head, name, len);
+
+ if (value) {
+ return value;
+ }
+ }
+
+ if (hash->wc_tail && hash->wc_tail->hash.buckets) {
+ value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);
+
+ if (value) {
+ return value;
+ }
+ }
+
+ return NULL;
+}
+
+
+#define NGX_HASH_ELT_SIZE(name) \
+ (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))
+
+ngx_int_t
+ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)
+{
+ u_char *elts;
+ size_t len;
+ u_short *test;
+ ngx_uint_t i, n, key, size, start, bucket_size;
+ ngx_hash_elt_t *elt, **buckets;
+
+ for (n = 0; n < nelts; n++) {
+ if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))
+ {
+ ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+ "could not build the %s, you should "
+ "increase %s_bucket_size: %i",
+ hinit->name, hinit->name, hinit->bucket_size);
+ return NGX_ERROR;
+ }
+ }
+
+ test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);
+ if (test == NULL) {
+ return NGX_ERROR;
+ }
+
+ bucket_size = hinit->bucket_size - sizeof(void *);
+
+ start = nelts / (bucket_size / (2 * sizeof(void *)));
+ start = start ? start : 1;
+
+ if (hinit->max_size > 10000 && hinit->max_size / nelts < 100) {
+ start = hinit->max_size - 1000;
+ }
+
+ for (size = start; size < hinit->max_size; size++) {
+
+ ngx_memzero(test, size * sizeof(u_short));
+
+ for (n = 0; n < nelts; n++) {
+ if (names[n].key.data == NULL) {
+ continue;
+ }
+
+ key = names[n].key_hash % size;
+ test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "%ui: %ui %ui \"%V\"",
+ size, key, test[key], &names[n].key);
+#endif
+
+ if (test[key] > (u_short) bucket_size) {
+ goto next;
+ }
+ }
+
+ goto found;
+
+ next:
+
+ continue;
+ }
+
+ ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,
+ "could not build the %s, you should increase "
+ "either %s_max_size: %i or %s_bucket_size: %i",
+ hinit->name, hinit->name, hinit->max_size,
+ hinit->name, hinit->bucket_size);
+
+ ngx_free(test);
+
+ return NGX_ERROR;
+
+found:
+
+ for (i = 0; i < size; i++) {
+ test[i] = sizeof(void *);
+ }
+
+ for (n = 0; n < nelts; n++) {
+ if (names[n].key.data == NULL) {
+ continue;
+ }
+
+ key = names[n].key_hash % size;
+ test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+ }
+
+ len = 0;
+
+ for (i = 0; i < size; i++) {
+ if (test[i] == sizeof(void *)) {
+ continue;
+ }
+
+ test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));
+
+ len += test[i];
+ }
+
+ if (hinit->hash == NULL) {
+ hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)
+ + size * sizeof(ngx_hash_elt_t *));
+ if (hinit->hash == NULL) {
+ ngx_free(test);
+ return NGX_ERROR;
+ }
+
+ buckets = (ngx_hash_elt_t **)
+ ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));
+
+ } else {
+ buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));
+ if (buckets == NULL) {
+ ngx_free(test);
+ return NGX_ERROR;
+ }
+ }
+
+ elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);
+ if (elts == NULL) {
+ ngx_free(test);
+ return NGX_ERROR;
+ }
+
+ elts = ngx_align_ptr(elts, ngx_cacheline_size);
+
+ for (i = 0; i < size; i++) {
+ if (test[i] == sizeof(void *)) {
+ continue;
+ }
+
+ buckets[i] = (ngx_hash_elt_t *) elts;
+ elts += test[i];
+
+ }
+
+ for (i = 0; i < size; i++) {
+ test[i] = 0;
+ }
+
+ for (n = 0; n < nelts; n++) {
+ if (names[n].key.data == NULL) {
+ continue;
+ }
+
+ key = names[n].key_hash % size;
+ elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);
+
+ elt->value = names[n].value;
+ elt->len = (u_short) names[n].key.len;
+
+ ngx_strlow(elt->name, names[n].key.data, names[n].key.len);
+
+ test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));
+ }
+
+ for (i = 0; i < size; i++) {
+ if (buckets[i] == NULL) {
+ continue;
+ }
+
+ elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);
+
+ elt->value = NULL;
+ }
+
+ ngx_free(test);
+
+ hinit->hash->buckets = buckets;
+ hinit->hash->size = size;
+
+#if 0
+
+ for (i = 0; i < size; i++) {
+ ngx_str_t val;
+ ngx_uint_t key;
+
+ elt = buckets[i];
+
+ if (elt == NULL) {
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "%ui: NULL", i);
+ continue;
+ }
+
+ while (elt->value) {
+ val.len = elt->len;
+ val.data = &elt->name[0];
+
+ key = hinit->key(val.data, val.len);
+
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "%ui: %p \"%V\" %ui", i, elt, &val, key);
+
+ elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,
+ sizeof(void *));
+ }
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
+ ngx_uint_t nelts)
+{
+ size_t len, dot_len;
+ ngx_uint_t i, n, dot;
+ ngx_array_t curr_names, next_names;
+ ngx_hash_key_t *name, *next_name;
+ ngx_hash_init_t h;
+ ngx_hash_wildcard_t *wdc;
+
+ if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,
+ sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&next_names, hinit->temp_pool, nelts,
+ sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ for (n = 0; n < nelts; n = i) {
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "wc0: \"%V\"", &names[n].key);
+#endif
+
+ dot = 0;
+
+ for (len = 0; len < names[n].key.len; len++) {
+ if (names[n].key.data[len] == '.') {
+ dot = 1;
+ break;
+ }
+ }
+
+ name = ngx_array_push(&curr_names);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ name->key.len = len;
+ name->key.data = names[n].key.data;
+ name->key_hash = hinit->key(name->key.data, name->key.len);
+ name->value = names[n].value;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "wc1: \"%V\" %ui", &name->key, dot);
+#endif
+
+ dot_len = len + 1;
+
+ if (dot) {
+ len++;
+ }
+
+ next_names.nelts = 0;
+
+ if (names[n].key.len != len) {
+ next_name = ngx_array_push(&next_names);
+ if (next_name == NULL) {
+ return NGX_ERROR;
+ }
+
+ next_name->key.len = names[n].key.len - len;
+ next_name->key.data = names[n].key.data + len;
+ next_name->key_hash = 0;
+ next_name->value = names[n].value;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "wc2: \"%V\"", &next_name->key);
+#endif
+ }
+
+ for (i = n + 1; i < nelts; i++) {
+ if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {
+ break;
+ }
+
+ if (!dot
+ && names[i].key.len > len
+ && names[i].key.data[len] != '.')
+ {
+ break;
+ }
+
+ next_name = ngx_array_push(&next_names);
+ if (next_name == NULL) {
+ return NGX_ERROR;
+ }
+
+ next_name->key.len = names[i].key.len - dot_len;
+ next_name->key.data = names[i].key.data + dot_len;
+ next_name->key_hash = 0;
+ next_name->value = names[i].value;
+
+#if 0
+ ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,
+ "wc3: \"%V\"", &next_name->key);
+#endif
+ }
+
+ if (next_names.nelts) {
+
+ h = *hinit;
+ h.hash = NULL;
+
+ if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,
+ next_names.nelts)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ wdc = (ngx_hash_wildcard_t *) h.hash;
+
+ if (names[n].key.len == len) {
+ wdc->value = names[n].value;
+ }
+
+ name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));
+
+ } else if (dot) {
+ name->value = (void *) ((uintptr_t) name->value | 1);
+ }
+ }
+
+ if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,
+ curr_names.nelts)
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_uint_t
+ngx_hash_key(u_char *data, size_t len)
+{
+ ngx_uint_t i, key;
+
+ key = 0;
+
+ for (i = 0; i < len; i++) {
+ key = ngx_hash(key, data[i]);
+ }
+
+ return key;
+}
+
+
+ngx_uint_t
+ngx_hash_key_lc(u_char *data, size_t len)
+{
+ ngx_uint_t i, key;
+
+ key = 0;
+
+ for (i = 0; i < len; i++) {
+ key = ngx_hash(key, ngx_tolower(data[i]));
+ }
+
+ return key;
+}
+
+
+ngx_uint_t
+ngx_hash_strlow(u_char *dst, u_char *src, size_t n)
+{
+ ngx_uint_t key;
+
+ key = 0;
+
+ while (n--) {
+ *dst = ngx_tolower(*src);
+ key = ngx_hash(key, *dst);
+ dst++;
+ src++;
+ }
+
+ return key;
+}
+
+
+ngx_int_t
+ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)
+{
+ ngx_uint_t asize;
+
+ if (type == NGX_HASH_SMALL) {
+ asize = 4;
+ ha->hsize = 107;
+
+ } else {
+ asize = NGX_HASH_LARGE_ASIZE;
+ ha->hsize = NGX_HASH_LARGE_HSIZE;
+ }
+
+ if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,
+ sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,
+ sizeof(ngx_hash_key_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+
+ ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);
+ if (ha->keys_hash == NULL) {
+ return NGX_ERROR;
+ }
+
+ ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,
+ sizeof(ngx_array_t) * ha->hsize);
+ if (ha->dns_wc_head_hash == NULL) {
+ return NGX_ERROR;
+ }
+
+ ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,
+ sizeof(ngx_array_t) * ha->hsize);
+ if (ha->dns_wc_tail_hash == NULL) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,
+ ngx_uint_t flags)
+{
+ size_t len;
+ u_char *p;
+ ngx_str_t *name;
+ ngx_uint_t i, k, n, skip, last;
+ ngx_array_t *keys, *hwc;
+ ngx_hash_key_t *hk;
+
+ last = key->len;
+
+ if (flags & NGX_HASH_WILDCARD_KEY) {
+
+ /*
+ * supported wildcards:
+ * "*.example.com", ".example.com", and "www.example.*"
+ */
+
+ n = 0;
+
+ for (i = 0; i < key->len; i++) {
+
+ if (key->data[i] == '*') {
+ if (++n > 1) {
+ return NGX_DECLINED;
+ }
+ }
+
+ if (key->data[i] == '.' && key->data[i + 1] == '.') {
+ return NGX_DECLINED;
+ }
+ }
+
+ if (key->len > 1 && key->data[0] == '.') {
+ skip = 1;
+ goto wildcard;
+ }
+
+ if (key->len > 2) {
+
+ if (key->data[0] == '*' && key->data[1] == '.') {
+ skip = 2;
+ goto wildcard;
+ }
+
+ if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {
+ skip = 0;
+ last -= 2;
+ goto wildcard;
+ }
+ }
+
+ if (n) {
+ return NGX_DECLINED;
+ }
+ }
+
+ /* exact hash */
+
+ k = 0;
+
+ for (i = 0; i < last; i++) {
+ if (!(flags & NGX_HASH_READONLY_KEY)) {
+ key->data[i] = ngx_tolower(key->data[i]);
+ }
+ k = ngx_hash(k, key->data[i]);
+ }
+
+ k %= ha->hsize;
+
+ /* check conflicts in exact hash */
+
+ name = ha->keys_hash[k].elts;
+
+ if (name) {
+ for (i = 0; i < ha->keys_hash[k].nelts; i++) {
+ if (last != name[i].len) {
+ continue;
+ }
+
+ if (ngx_strncmp(key->data, name[i].data, last) == 0) {
+ return NGX_BUSY;
+ }
+ }
+
+ } else {
+ if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
+ sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ name = ngx_array_push(&ha->keys_hash[k]);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ *name = *key;
+
+ hk = ngx_array_push(&ha->keys);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key = *key;
+ hk->key_hash = ngx_hash_key(key->data, last);
+ hk->value = value;
+
+ return NGX_OK;
+
+
+wildcard:
+
+ /* wildcard hash */
+
+ k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);
+
+ k %= ha->hsize;
+
+ if (skip == 1) {
+
+ /* check conflicts in exact hash for ".example.com" */
+
+ name = ha->keys_hash[k].elts;
+
+ if (name) {
+ len = last - skip;
+
+ for (i = 0; i < ha->keys_hash[k].nelts; i++) {
+ if (len != name[i].len) {
+ continue;
+ }
+
+ if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {
+ return NGX_BUSY;
+ }
+ }
+
+ } else {
+ if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,
+ sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ name = ngx_array_push(&ha->keys_hash[k]);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ name->len = last - 1;
+ name->data = ngx_pnalloc(ha->temp_pool, name->len);
+ if (name->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(name->data, &key->data[1], name->len);
+ }
+
+
+ if (skip) {
+
+ /*
+ * convert "*.example.com" to "com.example.\0"
+ * and ".example.com" to "com.example\0"
+ */
+
+ p = ngx_pnalloc(ha->temp_pool, last);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = 0;
+ n = 0;
+
+ for (i = last - 1; i; i--) {
+ if (key->data[i] == '.') {
+ ngx_memcpy(&p[n], &key->data[i + 1], len);
+ n += len;
+ p[n++] = '.';
+ len = 0;
+ continue;
+ }
+
+ len++;
+ }
+
+ if (len) {
+ ngx_memcpy(&p[n], &key->data[1], len);
+ n += len;
+ }
+
+ p[n] = '\0';
+
+ hwc = &ha->dns_wc_head;
+ keys = &ha->dns_wc_head_hash[k];
+
+ } else {
+
+ /* convert "www.example.*" to "www.example\0" */
+
+ last++;
+
+ p = ngx_pnalloc(ha->temp_pool, last);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_cpystrn(p, key->data, last);
+
+ hwc = &ha->dns_wc_tail;
+ keys = &ha->dns_wc_tail_hash[k];
+ }
+
+
+ hk = ngx_array_push(hwc);
+ if (hk == NULL) {
+ return NGX_ERROR;
+ }
+
+ hk->key.len = last - 1;
+ hk->key.data = p;
+ hk->key_hash = 0;
+ hk->value = value;
+
+
+ /* check conflicts in wildcard hash */
+
+ name = keys->elts;
+
+ if (name) {
+ len = last - skip;
+
+ for (i = 0; i < keys->nelts; i++) {
+ if (len != name[i].len) {
+ continue;
+ }
+
+ if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {
+ return NGX_BUSY;
+ }
+ }
+
+ } else {
+ if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)
+ {
+ return NGX_ERROR;
+ }
+ }
+
+ name = ngx_array_push(keys);
+ if (name == NULL) {
+ return NGX_ERROR;
+ }
+
+ name->len = last - skip;
+ name->data = ngx_pnalloc(ha->temp_pool, name->len);
+ if (name->data == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(name->data, key->data + skip, name->len);
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_hash.h b/usr.sbin/nginx/src/core/ngx_hash.h
new file mode 100644
index 00000000000..beabcb7c306
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_hash.h
@@ -0,0 +1,121 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_HASH_H_INCLUDED_
+#define _NGX_HASH_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+ void *value;
+ u_short len;
+ u_char name[1];
+} ngx_hash_elt_t;
+
+
+typedef struct {
+ ngx_hash_elt_t **buckets;
+ ngx_uint_t size;
+} ngx_hash_t;
+
+
+typedef struct {
+ ngx_hash_t hash;
+ void *value;
+} ngx_hash_wildcard_t;
+
+
+typedef struct {
+ ngx_str_t key;
+ ngx_uint_t key_hash;
+ void *value;
+} ngx_hash_key_t;
+
+
+typedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len);
+
+
+typedef struct {
+ ngx_hash_t hash;
+ ngx_hash_wildcard_t *wc_head;
+ ngx_hash_wildcard_t *wc_tail;
+} ngx_hash_combined_t;
+
+
+typedef struct {
+ ngx_hash_t *hash;
+ ngx_hash_key_pt key;
+
+ ngx_uint_t max_size;
+ ngx_uint_t bucket_size;
+
+ char *name;
+ ngx_pool_t *pool;
+ ngx_pool_t *temp_pool;
+} ngx_hash_init_t;
+
+
+#define NGX_HASH_SMALL 1
+#define NGX_HASH_LARGE 2
+
+#define NGX_HASH_LARGE_ASIZE 16384
+#define NGX_HASH_LARGE_HSIZE 10007
+
+#define NGX_HASH_WILDCARD_KEY 1
+#define NGX_HASH_READONLY_KEY 2
+
+
+typedef struct {
+ ngx_uint_t hsize;
+
+ ngx_pool_t *pool;
+ ngx_pool_t *temp_pool;
+
+ ngx_array_t keys;
+ ngx_array_t *keys_hash;
+
+ ngx_array_t dns_wc_head;
+ ngx_array_t *dns_wc_head_hash;
+
+ ngx_array_t dns_wc_tail;
+ ngx_array_t *dns_wc_tail_hash;
+} ngx_hash_keys_arrays_t;
+
+
+typedef struct {
+ ngx_uint_t hash;
+ ngx_str_t key;
+ ngx_str_t value;
+ u_char *lowcase_key;
+} ngx_table_elt_t;
+
+
+void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);
+void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
+void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);
+void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key,
+ u_char *name, size_t len);
+
+ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
+ ngx_uint_t nelts);
+ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,
+ ngx_uint_t nelts);
+
+#define ngx_hash(key, c) ((ngx_uint_t) key * 31 + c)
+ngx_uint_t ngx_hash_key(u_char *data, size_t len);
+ngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);
+ngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n);
+
+
+ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);
+ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key,
+ void *value, ngx_uint_t flags);
+
+
+#endif /* _NGX_HASH_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_inet.c b/usr.sbin/nginx/src/core/ngx_inet.c
new file mode 100644
index 00000000000..ac1ca8bf202
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_inet.c
@@ -0,0 +1,1007 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u);
+static ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u);
+static ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u);
+
+
+in_addr_t
+ngx_inet_addr(u_char *text, size_t len)
+{
+ u_char *p, c;
+ in_addr_t addr;
+ ngx_uint_t octet, n;
+
+ addr = 0;
+ octet = 0;
+ n = 0;
+
+ for (p = text; p < text + len; p++) {
+
+ c = *p;
+
+ if (c >= '0' && c <= '9') {
+ octet = octet * 10 + (c - '0');
+ continue;
+ }
+
+ if (c == '.' && octet < 256) {
+ addr = (addr << 8) + octet;
+ octet = 0;
+ n++;
+ continue;
+ }
+
+ return INADDR_NONE;
+ }
+
+ if (n != 3) {
+ return INADDR_NONE;
+ }
+
+ if (octet < 256) {
+ addr = (addr << 8) + octet;
+ return htonl(addr);
+ }
+
+ return INADDR_NONE;
+}
+
+
+#if (NGX_HAVE_INET6)
+
+ngx_int_t
+ngx_inet6_addr(u_char *p, size_t len, u_char *addr)
+{
+ u_char c, *zero, *digit, *s, *d;
+ size_t len4;
+ ngx_uint_t n, nibbles, word;
+
+ if (len == 0) {
+ return NGX_ERROR;
+ }
+
+ zero = NULL;
+ digit = NULL;
+ len4 = 0;
+ nibbles = 0;
+ word = 0;
+ n = 8;
+
+ if (p[0] == ':') {
+ p++;
+ len--;
+ }
+
+ for (/* void */; len; len--) {
+ c = *p++;
+
+ if (c == ':') {
+ if (nibbles) {
+ digit = p;
+ len4 = len;
+ *addr++ = (u_char) (word >> 8);
+ *addr++ = (u_char) (word & 0xff);
+
+ if (--n) {
+ nibbles = 0;
+ word = 0;
+ continue;
+ }
+
+ } else {
+ if (zero == NULL) {
+ digit = p;
+ len4 = len;
+ zero = addr;
+ continue;
+ }
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (c == '.' && nibbles) {
+ if (n < 2 || digit == NULL) {
+ return NGX_ERROR;
+ }
+
+ word = ngx_inet_addr(digit, len4 - 1);
+ if (word == INADDR_NONE) {
+ return NGX_ERROR;
+ }
+
+ word = ntohl(word);
+ *addr++ = (u_char) ((word >> 24) & 0xff);
+ *addr++ = (u_char) ((word >> 16) & 0xff);
+ n--;
+ break;
+ }
+
+ if (++nibbles > 4) {
+ return NGX_ERROR;
+ }
+
+ if (c >= '0' && c <= '9') {
+ word = word * 16 + (c - '0');
+ continue;
+ }
+
+ c |= 0x20;
+
+ if (c >= 'a' && c <= 'f') {
+ word = word * 16 + (c - 'a') + 10;
+ continue;
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (nibbles == 0 && zero == NULL) {
+ return NGX_ERROR;
+ }
+
+ *addr++ = (u_char) (word >> 8);
+ *addr++ = (u_char) (word & 0xff);
+
+ if (--n) {
+ if (zero) {
+ n *= 2;
+ s = addr - 1;
+ d = s + n;
+ while (s >= zero) {
+ *d-- = *s--;
+ }
+ ngx_memzero(zero, n);
+ return NGX_OK;
+ }
+
+ } else {
+ if (zero == NULL) {
+ return NGX_OK;
+ }
+ }
+
+ return NGX_ERROR;
+}
+
+#endif
+
+
+size_t
+ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len, ngx_uint_t port)
+{
+ u_char *p;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ size_t n;
+ struct sockaddr_in6 *sin6;
+#endif
+#if (NGX_HAVE_UNIX_DOMAIN)
+ struct sockaddr_un *saun;
+#endif
+
+ switch (sa->sa_family) {
+
+ case AF_INET:
+
+ sin = (struct sockaddr_in *) sa;
+ p = (u_char *) &sin->sin_addr;
+
+ if (port) {
+ p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud:%d",
+ p[0], p[1], p[2], p[3], ntohs(sin->sin_port));
+ } else {
+ p = ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
+ p[0], p[1], p[2], p[3]);
+ }
+
+ return (p - text);
+
+#if (NGX_HAVE_INET6)
+
+ case AF_INET6:
+
+ sin6 = (struct sockaddr_in6 *) sa;
+
+ n = 0;
+
+ if (port) {
+ text[n++] = '[';
+ }
+
+ n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len);
+
+ if (port) {
+ n = ngx_sprintf(&text[1 + n], "]:%d",
+ ntohs(sin6->sin6_port)) - text;
+ }
+
+ return n;
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ case AF_UNIX:
+ saun = (struct sockaddr_un *) sa;
+
+ /* we do not include trailing zero in address length */
+
+ return ngx_snprintf(text, len, "unix:%s%Z", saun->sun_path) - text - 1;
+
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+
+size_t
+ngx_inet_ntop(int family, void *addr, u_char *text, size_t len)
+{
+ u_char *p;
+
+ switch (family) {
+
+ case AF_INET:
+
+ p = addr;
+
+ return ngx_snprintf(text, len, "%ud.%ud.%ud.%ud",
+ p[0], p[1], p[2], p[3])
+ - text;
+
+#if (NGX_HAVE_INET6)
+
+ case AF_INET6:
+ return ngx_inet6_ntop(addr, text, len);
+
+#endif
+
+ default:
+ return 0;
+ }
+}
+
+
+#if (NGX_HAVE_INET6)
+
+size_t
+ngx_inet6_ntop(u_char *p, u_char *text, size_t len)
+{
+ u_char *dst;
+ size_t max, n;
+ ngx_uint_t i, zero, last;
+
+ if (len < NGX_INET6_ADDRSTRLEN) {
+ return 0;
+ }
+
+ zero = (ngx_uint_t) -1;
+ last = (ngx_uint_t) -1;
+ max = 1;
+ n = 0;
+
+ for (i = 0; i < 16; i += 2) {
+
+ if (p[i] || p[i + 1]) {
+
+ if (max < n) {
+ zero = last;
+ max = n;
+ }
+
+ n = 0;
+ continue;
+ }
+
+ if (n++ == 0) {
+ last = i;
+ }
+ }
+
+ if (max < n) {
+ zero = last;
+ max = n;
+ }
+
+ dst = text;
+ n = 16;
+
+ if (zero == 0) {
+
+ if ((max == 5 && p[10] == 0xff && p[11] == 0xff)
+ || (max == 6)
+ || (max == 7 && p[14] != 0 && p[15] != 1))
+ {
+ n = 12;
+ }
+
+ *dst++ = ':';
+ }
+
+ for (i = 0; i < n; i += 2) {
+
+ if (i == zero) {
+ *dst++ = ':';
+ i += (max - 1) * 2;
+ continue;
+ }
+
+ dst = ngx_sprintf(dst, "%uxi", p[i] * 256 + p[i + 1]);
+
+ if (i < 14) {
+ *dst++ = ':';
+ }
+ }
+
+ if (n == 12) {
+ dst = ngx_sprintf(dst, "%ud.%ud.%ud.%ud", p[12], p[13], p[14], p[15]);
+ }
+
+ return dst - text;
+}
+
+#endif
+
+
+ngx_int_t
+ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr)
+{
+ u_char *addr, *mask, *last;
+ size_t len;
+ ngx_int_t shift;
+#if (NGX_HAVE_INET6)
+ ngx_int_t rc;
+ ngx_uint_t s, i;
+#endif
+
+ addr = text->data;
+ last = addr + text->len;
+
+ mask = ngx_strlchr(addr, last, '/');
+ len = (mask ? mask : last) - addr;
+
+ cidr->u.in.addr = ngx_inet_addr(addr, len);
+
+ if (cidr->u.in.addr != INADDR_NONE) {
+ cidr->family = AF_INET;
+
+ if (mask == NULL) {
+ cidr->u.in.mask = 0xffffffff;
+ return NGX_OK;
+ }
+
+#if (NGX_HAVE_INET6)
+ } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) {
+ cidr->family = AF_INET6;
+
+ if (mask == NULL) {
+ ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16);
+ return NGX_OK;
+ }
+
+#endif
+ } else {
+ return NGX_ERROR;
+ }
+
+ mask++;
+
+ shift = ngx_atoi(mask, last - mask);
+ if (shift == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ switch (cidr->family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ addr = cidr->u.in6.addr.s6_addr;
+ mask = cidr->u.in6.mask.s6_addr;
+ rc = NGX_OK;
+
+ for (i = 0; i < 16; i++) {
+
+ s = (shift > 8) ? 8 : shift;
+ shift -= s;
+
+ mask[i] = (u_char) (0 - (1 << (8 - s)));
+
+ if (addr[i] != (addr[i] & mask[i])) {
+ rc = NGX_DONE;
+ addr[i] &= mask[i];
+ }
+ }
+
+ return rc;
+#endif
+
+ default: /* AF_INET */
+
+ if (shift) {
+ cidr->u.in.mask = htonl((ngx_uint_t) (0 - (1 << (32 - shift))));
+
+ } else {
+ /* x86 compilers use a shl instruction that shifts by modulo 32 */
+ cidr->u.in.mask = 0;
+ }
+
+ if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) {
+ return NGX_OK;
+ }
+
+ cidr->u.in.addr &= cidr->u.in.mask;
+
+ return NGX_DONE;
+ }
+}
+
+
+ngx_int_t
+ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len)
+{
+ in_addr_t inaddr;
+ ngx_uint_t family;
+ struct sockaddr_in *sin;
+#if (NGX_HAVE_INET6)
+ struct in6_addr inaddr6;
+ struct sockaddr_in6 *sin6;
+
+ /*
+ * prevent MSVC8 waring:
+ * potentially uninitialized local variable 'inaddr6' used
+ */
+ ngx_memzero(inaddr6.s6_addr, sizeof(struct in6_addr));
+#endif
+
+ inaddr = ngx_inet_addr(text, len);
+
+ if (inaddr != INADDR_NONE) {
+ family = AF_INET;
+ len = sizeof(struct sockaddr_in);
+
+#if (NGX_HAVE_INET6)
+ } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) {
+ family = AF_INET6;
+ len = sizeof(struct sockaddr_in6);
+
+#endif
+ } else {
+ return NGX_DECLINED;
+ }
+
+ addr->sockaddr = ngx_pcalloc(pool, len);
+ if (addr->sockaddr == NULL) {
+ return NGX_ERROR;
+ }
+
+ addr->sockaddr->sa_family = (u_char) family;
+ addr->socklen = len;
+
+ switch (family) {
+
+#if (NGX_HAVE_INET6)
+ case AF_INET6:
+ sin6 = (struct sockaddr_in6 *) addr->sockaddr;
+ ngx_memcpy(sin6->sin6_addr.s6_addr, inaddr6.s6_addr, 16);
+ break;
+#endif
+
+ default: /* AF_INET */
+ sin = (struct sockaddr_in *) addr->sockaddr;
+ sin->sin_addr.s_addr = inaddr;
+ break;
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+ u_char *p;
+
+ p = u->url.data;
+
+ if (ngx_strncasecmp(p, (u_char *) "unix:", 5) == 0) {
+ return ngx_parse_unix_domain_url(pool, u);
+ }
+
+ if ((p[0] == ':' || p[0] == '/') && !u->listen) {
+ u->err = "invalid host";
+ return NGX_ERROR;
+ }
+
+ if (p[0] == '[') {
+ return ngx_parse_inet6_url(pool, u);
+ }
+
+ return ngx_parse_inet_url(pool, u);
+}
+
+
+static ngx_int_t
+ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+#if (NGX_HAVE_UNIX_DOMAIN)
+ u_char *path, *uri, *last;
+ size_t len;
+ struct sockaddr_un *saun;
+
+ len = u->url.len;
+ path = u->url.data;
+
+ path += 5;
+ len -= 5;
+
+ if (u->uri_part) {
+
+ last = path + len;
+ uri = ngx_strlchr(path, last, ':');
+
+ if (uri) {
+ len = uri - path;
+ uri++;
+ u->uri.len = last - uri;
+ u->uri.data = uri;
+ }
+ }
+
+ if (len == 0) {
+ u->err = "no path in the unix domain socket";
+ return NGX_ERROR;
+ }
+
+ u->host.len = len++;
+ u->host.data = path;
+
+ if (len > sizeof(saun->sun_path)) {
+ u->err = "too long path in the unix domain socket";
+ return NGX_ERROR;
+ }
+
+ u->socklen = sizeof(struct sockaddr_un);
+ saun = (struct sockaddr_un *) &u->sockaddr;
+ saun->sun_family = AF_UNIX;
+ (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);
+
+ u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
+ if (u->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un));
+ if (saun == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->family = AF_UNIX;
+ u->naddrs = 1;
+
+ saun->sun_family = AF_UNIX;
+ (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);
+
+ u->addrs[0].sockaddr = (struct sockaddr *) saun;
+ u->addrs[0].socklen = sizeof(struct sockaddr_un);
+ u->addrs[0].name.len = len + 4;
+ u->addrs[0].name.data = u->url.data;
+
+ return NGX_OK;
+
+#else
+
+ u->err = "the unix domain sockets are not supported on this platform";
+
+ return NGX_ERROR;
+
+#endif
+}
+
+
+static ngx_int_t
+ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+ u_char *p, *host, *port, *last, *uri, *args;
+ size_t len;
+ ngx_int_t n;
+ struct hostent *h;
+ struct sockaddr_in *sin;
+
+ u->socklen = sizeof(struct sockaddr_in);
+ sin = (struct sockaddr_in *) &u->sockaddr;
+ sin->sin_family = AF_INET;
+
+ u->family = AF_INET;
+
+ host = u->url.data;
+
+ last = host + u->url.len;
+
+ port = ngx_strlchr(host, last, ':');
+
+ uri = ngx_strlchr(host, last, '/');
+
+ args = ngx_strlchr(host, last, '?');
+
+ if (args) {
+ if (uri == NULL) {
+ uri = args;
+
+ } else if (args < uri) {
+ uri = args;
+ }
+ }
+
+ if (uri) {
+ if (u->listen || !u->uri_part) {
+ u->err = "invalid host";
+ return NGX_ERROR;
+ }
+
+ u->uri.len = last - uri;
+ u->uri.data = uri;
+
+ last = uri;
+
+ if (uri < port) {
+ port = NULL;
+ }
+ }
+
+ if (port) {
+ port++;
+
+ len = last - port;
+
+ if (len == 0) {
+ u->err = "invalid port";
+ return NGX_ERROR;
+ }
+
+ n = ngx_atoi(port, len);
+
+ if (n < 1 || n > 65536) {
+ u->err = "invalid port";
+ return NGX_ERROR;
+ }
+
+ u->port = (in_port_t) n;
+ sin->sin_port = htons((in_port_t) n);
+
+ u->port_text.len = len;
+ u->port_text.data = port;
+
+ last = port - 1;
+
+ } else {
+ if (uri == NULL) {
+
+ if (u->listen) {
+
+ /* test value as port only */
+
+ n = ngx_atoi(host, last - host);
+
+ if (n != NGX_ERROR) {
+
+ if (n < 1 || n > 65536) {
+ u->err = "invalid port";
+ return NGX_ERROR;
+ }
+
+ u->port = (in_port_t) n;
+ sin->sin_port = htons((in_port_t) n);
+
+ u->port_text.len = last - host;
+ u->port_text.data = host;
+
+ u->wildcard = 1;
+
+ return NGX_OK;
+ }
+ }
+ }
+
+ u->no_port = 1;
+ }
+
+ len = last - host;
+
+ if (len == 0) {
+ u->err = "no host";
+ return NGX_ERROR;
+ }
+
+ if (len == 1 && *host == '*') {
+ len = 0;
+ }
+
+ u->host.len = len;
+ u->host.data = host;
+
+ if (u->no_resolve) {
+ return NGX_OK;
+ }
+
+ if (len) {
+ sin->sin_addr.s_addr = ngx_inet_addr(host, len);
+
+ if (sin->sin_addr.s_addr == INADDR_NONE) {
+ p = ngx_alloc(++len, pool->log);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(p, host, len);
+
+ h = gethostbyname((const char *) p);
+
+ ngx_free(p);
+
+ if (h == NULL || h->h_addr_list[0] == NULL) {
+ u->err = "host not found";
+ return NGX_ERROR;
+ }
+
+ sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[0]);
+ }
+
+ if (sin->sin_addr.s_addr == INADDR_ANY) {
+ u->wildcard = 1;
+ }
+
+ } else {
+ sin->sin_addr.s_addr = INADDR_ANY;
+ u->wildcard = 1;
+ }
+
+ if (u->no_port) {
+ u->port = u->default_port;
+ sin->sin_port = htons(u->default_port);
+ }
+
+ if (u->listen) {
+ return NGX_OK;
+ }
+
+ if (ngx_inet_resolve_host(pool, u) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)
+{
+#if (NGX_HAVE_INET6)
+ u_char *p, *host, *port, *last, *uri;
+ size_t len;
+ ngx_int_t n;
+ struct sockaddr_in6 *sin6;
+
+ u->socklen = sizeof(struct sockaddr_in6);
+ sin6 = (struct sockaddr_in6 *) &u->sockaddr;
+ sin6->sin6_family = AF_INET6;
+
+ host = u->url.data + 1;
+
+ last = u->url.data + u->url.len;
+
+ p = ngx_strlchr(host, last, ']');
+
+ if (p == NULL) {
+ u->err = "invalid host";
+ return NGX_ERROR;
+ }
+
+ if (last - p) {
+
+ port = p + 1;
+
+ uri = ngx_strlchr(port, last, '/');
+
+ if (uri) {
+ if (u->listen || !u->uri_part) {
+ u->err = "invalid host";
+ return NGX_ERROR;
+ }
+
+ u->uri.len = last - uri;
+ u->uri.data = uri;
+ }
+
+ if (*port == ':') {
+ port++;
+
+ len = last - port;
+
+ if (len == 0) {
+ u->err = "invalid port";
+ return NGX_ERROR;
+ }
+
+ n = ngx_atoi(port, len);
+
+ if (n < 1 || n > 65536) {
+ u->err = "invalid port";
+ return NGX_ERROR;
+ }
+
+ u->port = (in_port_t) n;
+ sin6->sin6_port = htons((in_port_t) n);
+
+ u->port_text.len = len;
+ u->port_text.data = port;
+
+ } else {
+ u->no_port = 1;
+ }
+ }
+
+ len = p - host;
+
+ if (len == 0) {
+ u->err = "no host";
+ return NGX_ERROR;
+ }
+
+ u->host.len = len;
+ u->host.data = host;
+
+ if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) {
+ u->err = "invalid IPv6 address";
+ return NGX_ERROR;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {
+ u->wildcard = 1;
+ }
+
+ u->family = AF_INET6;
+
+ if (u->no_resolve) {
+ return NGX_OK;
+ }
+
+ if (u->no_port) {
+ u->port = u->default_port;
+ sin6->sin6_port = htons(u->default_port);
+ }
+
+ return NGX_OK;
+
+#else
+
+ u->err = "the INET6 sockets are not supported on this platform";
+
+ return NGX_ERROR;
+
+#endif
+}
+
+
+ngx_int_t
+ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)
+{
+ u_char *p, *host;
+ size_t len;
+ in_port_t port;
+ in_addr_t in_addr;
+ ngx_uint_t i;
+ struct hostent *h;
+ struct sockaddr_in *sin;
+
+ /* AF_INET only */
+
+ port = htons(u->port);
+
+ in_addr = ngx_inet_addr(u->host.data, u->host.len);
+
+ if (in_addr == INADDR_NONE) {
+ host = ngx_alloc(u->host.len + 1, pool->log);
+ if (host == NULL) {
+ return NGX_ERROR;
+ }
+
+ (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);
+
+ h = gethostbyname((char *) host);
+
+ ngx_free(host);
+
+ if (h == NULL || h->h_addr_list[0] == NULL) {
+ u->err = "host not found";
+ return NGX_ERROR;
+ }
+
+ if (u->one_addr == 0) {
+ for (i = 0; h->h_addr_list[i] != NULL; i++) { /* void */ }
+
+ } else {
+ i = 1;
+ }
+
+ /* MP: ngx_shared_palloc() */
+
+ u->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));
+ if (u->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->naddrs = i;
+
+ for (i = 0; i < u->naddrs; i++) {
+
+ sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ return NGX_ERROR;
+ }
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ sin->sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);
+
+ u->addrs[i].sockaddr = (struct sockaddr *) sin;
+ u->addrs[i].socklen = sizeof(struct sockaddr_in);
+
+ len = NGX_INET_ADDRSTRLEN + sizeof(":65535") - 1;
+
+ p = ngx_pnalloc(pool, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ len = ngx_sock_ntop((struct sockaddr *) sin, p, len, 1);
+
+ u->addrs[i].name.len = len;
+ u->addrs[i].name.data = p;
+ }
+
+ } else {
+
+ /* MP: ngx_shared_palloc() */
+
+ u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));
+ if (u->addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->naddrs = 1;
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = port;
+ sin->sin_addr.s_addr = in_addr;
+
+ u->addrs[0].sockaddr = (struct sockaddr *) sin;
+ u->addrs[0].socklen = sizeof(struct sockaddr_in);
+
+ p = ngx_pnalloc(pool, u->host.len + sizeof(":65535") - 1);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ u->addrs[0].name.len = ngx_sprintf(p, "%V:%d",
+ &u->host, ntohs(port)) - p;
+ u->addrs[0].name.data = p;
+ }
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_inet.h b/usr.sbin/nginx/src/core/ngx_inet.h
new file mode 100644
index 00000000000..0b42abd8a64
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_inet.h
@@ -0,0 +1,119 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_INET_H_INCLUDED_
+#define _NGX_INET_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * TODO: autoconfigure NGX_SOCKADDRLEN and NGX_SOCKADDR_STRLEN as
+ * sizeof(struct sockaddr_storage)
+ * sizeof(struct sockaddr_un)
+ * sizeof(struct sockaddr_in6)
+ * sizeof(struct sockaddr_in)
+ */
+
+#define NGX_INET_ADDRSTRLEN (sizeof("255.255.255.255") - 1)
+#define NGX_INET6_ADDRSTRLEN \
+ (sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255") - 1)
+#define NGX_UNIX_ADDRSTRLEN \
+ (sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+#define NGX_SOCKADDR_STRLEN (sizeof("unix:") - 1 + NGX_UNIX_ADDRSTRLEN)
+#else
+#define NGX_SOCKADDR_STRLEN (NGX_INET6_ADDRSTRLEN + sizeof(":65535") - 1)
+#endif
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+#define NGX_SOCKADDRLEN sizeof(struct sockaddr_un)
+#else
+#define NGX_SOCKADDRLEN 512
+#endif
+
+
+typedef struct {
+ in_addr_t addr;
+ in_addr_t mask;
+} ngx_in_cidr_t;
+
+
+#if (NGX_HAVE_INET6)
+
+typedef struct {
+ struct in6_addr addr;
+ struct in6_addr mask;
+} ngx_in6_cidr_t;
+
+#endif
+
+
+typedef struct {
+ ngx_uint_t family;
+ union {
+ ngx_in_cidr_t in;
+#if (NGX_HAVE_INET6)
+ ngx_in6_cidr_t in6;
+#endif
+ } u;
+} ngx_cidr_t;
+
+
+typedef struct {
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t name;
+} ngx_addr_t;
+
+
+typedef struct {
+ ngx_str_t url;
+ ngx_str_t host;
+ ngx_str_t port_text;
+ ngx_str_t uri;
+
+ in_port_t port;
+ in_port_t default_port;
+ int family;
+
+ unsigned listen:1;
+ unsigned uri_part:1;
+ unsigned no_resolve:1;
+ unsigned one_addr:1;
+
+ unsigned no_port:1;
+ unsigned wildcard:1;
+
+ socklen_t socklen;
+ u_char sockaddr[NGX_SOCKADDRLEN];
+
+ ngx_addr_t *addrs;
+ ngx_uint_t naddrs;
+
+ char *err;
+} ngx_url_t;
+
+
+in_addr_t ngx_inet_addr(u_char *text, size_t len);
+#if (NGX_HAVE_INET6)
+ngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr);
+size_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len);
+#endif
+size_t ngx_sock_ntop(struct sockaddr *sa, u_char *text, size_t len,
+ ngx_uint_t port);
+size_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len);
+ngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr);
+ngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,
+ size_t len);
+ngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u);
+ngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u);
+
+
+#endif /* _NGX_INET_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_list.c b/usr.sbin/nginx/src/core/ngx_list.c
new file mode 100644
index 00000000000..84bc0026306
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_list.c
@@ -0,0 +1,70 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ngx_list_t *
+ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)
+{
+ ngx_list_t *list;
+
+ list = ngx_palloc(pool, sizeof(ngx_list_t));
+ if (list == NULL) {
+ return NULL;
+ }
+
+ list->part.elts = ngx_palloc(pool, n * size);
+ if (list->part.elts == NULL) {
+ return NULL;
+ }
+
+ list->part.nelts = 0;
+ list->part.next = NULL;
+ list->last = &list->part;
+ list->size = size;
+ list->nalloc = n;
+ list->pool = pool;
+
+ return list;
+}
+
+
+void *
+ngx_list_push(ngx_list_t *l)
+{
+ void *elt;
+ ngx_list_part_t *last;
+
+ last = l->last;
+
+ if (last->nelts == l->nalloc) {
+
+ /* the last part is full, allocate a new list part */
+
+ last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));
+ if (last == NULL) {
+ return NULL;
+ }
+
+ last->elts = ngx_palloc(l->pool, l->nalloc * l->size);
+ if (last->elts == NULL) {
+ return NULL;
+ }
+
+ last->nelts = 0;
+ last->next = NULL;
+
+ l->last->next = last;
+ l->last = last;
+ }
+
+ elt = (char *) last->elts + l->size * last->nelts;
+ last->nelts++;
+
+ return elt;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_list.h b/usr.sbin/nginx/src/core/ngx_list.h
new file mode 100644
index 00000000000..75db8688686
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_list.h
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_LIST_H_INCLUDED_
+#define _NGX_LIST_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct ngx_list_part_s ngx_list_part_t;
+
+struct ngx_list_part_s {
+ void *elts;
+ ngx_uint_t nelts;
+ ngx_list_part_t *next;
+};
+
+
+typedef struct {
+ ngx_list_part_t *last;
+ ngx_list_part_t part;
+ size_t size;
+ ngx_uint_t nalloc;
+ ngx_pool_t *pool;
+} ngx_list_t;
+
+
+ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
+
+static ngx_inline ngx_int_t
+ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
+{
+ list->part.elts = ngx_palloc(pool, n * size);
+ if (list->part.elts == NULL) {
+ return NGX_ERROR;
+ }
+
+ list->part.nelts = 0;
+ list->part.next = NULL;
+ list->last = &list->part;
+ list->size = size;
+ list->nalloc = n;
+ list->pool = pool;
+
+ return NGX_OK;
+}
+
+
+/*
+ *
+ * the iteration through the list:
+ *
+ * part = &list.part;
+ * data = part->elts;
+ *
+ * for (i = 0 ;; i++) {
+ *
+ * if (i >= part->nelts) {
+ * if (part->next == NULL) {
+ * break;
+ * }
+ *
+ * part = part->next;
+ * data = part->elts;
+ * i = 0;
+ * }
+ *
+ * ... data[i] ...
+ *
+ * }
+ */
+
+
+void *ngx_list_push(ngx_list_t *list);
+
+
+#endif /* _NGX_LIST_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_log.c b/usr.sbin/nginx/src/core/ngx_log.c
new file mode 100644
index 00000000000..44333146dae
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_log.c
@@ -0,0 +1,454 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+static ngx_command_t ngx_errlog_commands[] = {
+
+ {ngx_string("error_log"),
+ NGX_MAIN_CONF|NGX_CONF_1MORE,
+ ngx_error_log,
+ 0,
+ 0,
+ NULL},
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_errlog_module_ctx = {
+ ngx_string("errlog"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_errlog_module = {
+ NGX_MODULE_V1,
+ &ngx_errlog_module_ctx, /* module context */
+ ngx_errlog_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init master */
+ NULL, /* init module */
+ NULL, /* init process */
+ NULL, /* init thread */
+ NULL, /* exit thread */
+ NULL, /* exit process */
+ NULL, /* exit master */
+ NGX_MODULE_V1_PADDING
+};
+
+
+static ngx_log_t ngx_log;
+static ngx_open_file_t ngx_log_file;
+ngx_uint_t ngx_use_stderr = 1;
+
+
+static ngx_str_t err_levels[] = {
+ ngx_null_string,
+ ngx_string("emerg"),
+ ngx_string("alert"),
+ ngx_string("crit"),
+ ngx_string("error"),
+ ngx_string("warn"),
+ ngx_string("notice"),
+ ngx_string("info"),
+ ngx_string("debug")
+};
+
+static const char *debug_levels[] = {
+ "debug_core", "debug_alloc", "debug_mutex", "debug_event",
+ "debug_http", "debug_mail", "debug_mysql"
+};
+
+
+#if (NGX_HAVE_VARIADIC_MACROS)
+
+void
+ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...)
+
+#else
+
+void
+ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, va_list args)
+
+#endif
+{
+#if (NGX_HAVE_VARIADIC_MACROS)
+ va_list args;
+#endif
+ u_char *p, *last, *msg;
+ u_char errstr[NGX_MAX_ERROR_STR];
+
+ if (log->file->fd == NGX_INVALID_FILE) {
+ return;
+ }
+
+ last = errstr + NGX_MAX_ERROR_STR;
+
+ ngx_memcpy(errstr, ngx_cached_err_log_time.data,
+ ngx_cached_err_log_time.len);
+
+ p = errstr + ngx_cached_err_log_time.len;
+
+ p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);
+
+ /* pid#tid */
+ p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
+ ngx_log_pid, ngx_log_tid);
+
+ if (log->connection) {
+ p = ngx_slprintf(p, last, "*%uA ", log->connection);
+ }
+
+ msg = p;
+
+#if (NGX_HAVE_VARIADIC_MACROS)
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(p, last, fmt, args);
+ va_end(args);
+
+#else
+
+ p = ngx_vslprintf(p, last, fmt, args);
+
+#endif
+
+ if (err) {
+ p = ngx_log_errno(p, last, err);
+ }
+
+ if (level != NGX_LOG_DEBUG && log->handler) {
+ p = log->handler(log, p, last - p);
+ }
+
+ if (p > last - NGX_LINEFEED_SIZE) {
+ p = last - NGX_LINEFEED_SIZE;
+ }
+
+ ngx_linefeed(p);
+
+ (void) ngx_write_fd(log->file->fd, errstr, p - errstr);
+
+ if (!ngx_use_stderr
+ || level > NGX_LOG_WARN
+ || log->file->fd == ngx_stderr)
+ {
+ return;
+ }
+
+ msg -= (7 + err_levels[level].len + 3);
+
+ (void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]);
+
+ (void) ngx_write_console(ngx_stderr, msg, p - msg);
+}
+
+
+#if !(NGX_HAVE_VARIADIC_MACROS)
+
+void ngx_cdecl
+ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...)
+{
+ va_list args;
+
+ if (log->log_level >= level) {
+ va_start(args, fmt);
+ ngx_log_error_core(level, log, err, fmt, args);
+ va_end(args);
+ }
+}
+
+
+void ngx_cdecl
+ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args);
+ va_end(args);
+}
+
+#endif
+
+
+void ngx_cdecl
+ngx_log_abort(ngx_err_t err, const char *fmt, ...)
+{
+ u_char *p;
+ va_list args;
+ u_char errstr[NGX_MAX_CONF_ERRSTR];
+
+ va_start(args, fmt);
+ p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
+ va_end(args);
+
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
+ "%*s", p - errstr, errstr);
+}
+
+
+void ngx_cdecl
+ngx_log_stderr(ngx_err_t err, const char *fmt, ...)
+{
+ u_char *p, *last;
+ va_list args;
+ u_char errstr[NGX_MAX_ERROR_STR];
+
+ last = errstr + NGX_MAX_ERROR_STR;
+ p = errstr + 7;
+
+ ngx_memcpy(errstr, "nginx: ", 7);
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(p, last, fmt, args);
+ va_end(args);
+
+ if (err) {
+ p = ngx_log_errno(p, last, err);
+ }
+
+ if (p > last - NGX_LINEFEED_SIZE) {
+ p = last - NGX_LINEFEED_SIZE;
+ }
+
+ ngx_linefeed(p);
+
+ (void) ngx_write_console(ngx_stderr, errstr, p - errstr);
+}
+
+
+u_char *
+ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)
+{
+ if (buf > last - 50) {
+
+ /* leave a space for an error code */
+
+ buf = last - 50;
+ *buf++ = '.';
+ *buf++ = '.';
+ *buf++ = '.';
+ }
+
+#if (NGX_WIN32)
+ buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)
+ ? " (%d: " : " (%Xd: ", err);
+#else
+ buf = ngx_slprintf(buf, last, " (%d: ", err);
+#endif
+
+ buf = ngx_strerror(err, buf, last - buf);
+
+ if (buf < last) {
+ *buf++ = ')';
+ }
+
+ return buf;
+}
+
+
+ngx_log_t *
+ngx_log_init(u_char *prefix)
+{
+ u_char *p, *name;
+ size_t nlen, plen;
+
+ ngx_log.file = &ngx_log_file;
+ ngx_log.log_level = NGX_LOG_NOTICE;
+
+ name = (u_char *) NGX_ERROR_LOG_PATH;
+
+ /*
+ * we use ngx_strlen() here since BCC warns about
+ * condition is always false and unreachable code
+ */
+
+ nlen = ngx_strlen(name);
+
+ if (nlen == 0) {
+ ngx_log_file.fd = ngx_stderr;
+ return &ngx_log;
+ }
+
+ p = NULL;
+
+#if (NGX_WIN32)
+ if (name[1] != ':') {
+#else
+ if (name[0] != '/') {
+#endif
+
+ if (prefix) {
+ plen = ngx_strlen(prefix);
+
+ } else {
+#ifdef NGX_PREFIX
+ prefix = (u_char *) NGX_PREFIX;
+ plen = ngx_strlen(prefix);
+#else
+ plen = 0;
+#endif
+ }
+
+ if (plen) {
+ name = malloc(plen + nlen + 2);
+ if (name == NULL) {
+ return NULL;
+ }
+
+ p = ngx_cpymem(name, prefix, plen);
+
+ if (!ngx_path_separator(*(p - 1))) {
+ *p++ = '/';
+ }
+
+ ngx_cpystrn(p, (u_char *) NGX_ERROR_LOG_PATH, nlen + 1);
+
+ p = name;
+ }
+ }
+
+ ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
+ NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (ngx_log_file.fd == NGX_INVALID_FILE) {
+ ngx_log_stderr(ngx_errno,
+ "[alert] could not open error log file: "
+ ngx_open_file_n " \"%s\" failed", name);
+#if (NGX_WIN32)
+ ngx_event_log(ngx_errno,
+ "could not open error log file: "
+ ngx_open_file_n " \"%s\" failed", name);
+#endif
+
+ ngx_log_file.fd = ngx_stderr;
+ }
+
+ if (p) {
+ ngx_free(p);
+ }
+
+ return &ngx_log;
+}
+
+
+ngx_log_t *
+ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name)
+{
+ ngx_log_t *log;
+
+ log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));
+ if (log == NULL) {
+ return NULL;
+ }
+
+ log->file = ngx_conf_open_file(cycle, name);
+ if (log->file == NULL) {
+ return NULL;
+ }
+
+ return log;
+}
+
+
+char *
+ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
+{
+ ngx_uint_t i, n, d;
+ ngx_str_t *value;
+
+ value = cf->args->elts;
+
+ for (i = 2; i < cf->args->nelts; i++) {
+
+ for (n = 1; n <= NGX_LOG_DEBUG; n++) {
+ if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {
+
+ if (log->log_level != 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate log level \"%V\"",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ log->log_level = n;
+ continue;
+ }
+ }
+
+ for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) {
+ if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {
+ if (log->log_level & ~NGX_LOG_DEBUG_ALL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid log level \"%V\"",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ log->log_level |= d;
+ }
+ }
+
+
+ if (log->log_level == 0) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid log level \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (log->log_level == NGX_LOG_DEBUG) {
+ log->log_level = NGX_LOG_DEBUG_ALL;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_str_t *value, name;
+
+ if (cf->cycle->new_log.file) {
+ return "is duplicate";
+ }
+
+ value = cf->args->elts;
+
+ if (ngx_strcmp(value[1].data, "stderr") == 0) {
+ ngx_str_null(&name);
+
+ } else {
+ name = value[1];
+ }
+
+ cf->cycle->new_log.file = ngx_conf_open_file(cf->cycle, &name);
+ if (cf->cycle->new_log.file == NULL) {
+ return NULL;
+ }
+
+ if (cf->args->nelts == 2) {
+ cf->cycle->new_log.log_level = NGX_LOG_ERR;
+ return NGX_CONF_OK;
+ }
+
+ cf->cycle->new_log.log_level = 0;
+
+ return ngx_log_set_levels(cf, &cf->cycle->new_log);
+}
diff --git a/usr.sbin/nginx/src/core/ngx_log.h b/usr.sbin/nginx/src/core/ngx_log.h
new file mode 100644
index 00000000000..1054836f13e
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_log.h
@@ -0,0 +1,210 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_LOG_H_INCLUDED_
+#define _NGX_LOG_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_LOG_STDERR 0
+#define NGX_LOG_EMERG 1
+#define NGX_LOG_ALERT 2
+#define NGX_LOG_CRIT 3
+#define NGX_LOG_ERR 4
+#define NGX_LOG_WARN 5
+#define NGX_LOG_NOTICE 6
+#define NGX_LOG_INFO 7
+#define NGX_LOG_DEBUG 8
+
+#define NGX_LOG_DEBUG_CORE 0x010
+#define NGX_LOG_DEBUG_ALLOC 0x020
+#define NGX_LOG_DEBUG_MUTEX 0x040
+#define NGX_LOG_DEBUG_EVENT 0x080
+#define NGX_LOG_DEBUG_HTTP 0x100
+#define NGX_LOG_DEBUG_MAIL 0x200
+#define NGX_LOG_DEBUG_MYSQL 0x400
+
+/*
+ * do not forget to update debug_levels[] in src/core/ngx_log.c
+ * after the adding a new debug level
+ */
+
+#define NGX_LOG_DEBUG_FIRST NGX_LOG_DEBUG_CORE
+#define NGX_LOG_DEBUG_LAST NGX_LOG_DEBUG_MYSQL
+#define NGX_LOG_DEBUG_CONNECTION 0x80000000
+#define NGX_LOG_DEBUG_ALL 0x7ffffff0
+
+
+typedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);
+
+
+struct ngx_log_s {
+ ngx_uint_t log_level;
+ ngx_open_file_t *file;
+
+ ngx_atomic_uint_t connection;
+
+ ngx_log_handler_pt handler;
+ void *data;
+
+ /*
+ * we declare "action" as "char *" because the actions are usually
+ * the static strings and in the "u_char *" case we have to override
+ * their types all the time
+ */
+
+ char *action;
+};
+
+
+#define NGX_MAX_ERROR_STR 2048
+
+
+/*********************************/
+
+#if (NGX_HAVE_C99_VARIADIC_MACROS)
+
+#define NGX_HAVE_VARIADIC_MACROS 1
+
+#define ngx_log_error(level, log, ...) \
+ if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__)
+
+void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...);
+
+#define ngx_log_debug(level, log, ...) \
+ if ((log)->log_level & level) \
+ ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__)
+
+/*********************************/
+
+#elif (NGX_HAVE_GCC_VARIADIC_MACROS)
+
+#define NGX_HAVE_VARIADIC_MACROS 1
+
+#define ngx_log_error(level, log, args...) \
+ if ((log)->log_level >= level) ngx_log_error_core(level, log, args)
+
+void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...);
+
+#define ngx_log_debug(level, log, args...) \
+ if ((log)->log_level & level) \
+ ngx_log_error_core(NGX_LOG_DEBUG, log, args)
+
+/*********************************/
+
+#else /* NO VARIADIC MACROS */
+
+#define NGX_HAVE_VARIADIC_MACROS 0
+
+void ngx_cdecl ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...);
+void ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
+ const char *fmt, va_list args);
+void ngx_cdecl ngx_log_debug_core(ngx_log_t *log, ngx_err_t err,
+ const char *fmt, ...);
+
+
+#endif /* VARIADIC MACROS */
+
+
+/*********************************/
+
+#if (NGX_DEBUG)
+
+#if (NGX_HAVE_VARIADIC_MACROS)
+
+#define ngx_log_debug0 ngx_log_debug
+#define ngx_log_debug1 ngx_log_debug
+#define ngx_log_debug2 ngx_log_debug
+#define ngx_log_debug3 ngx_log_debug
+#define ngx_log_debug4 ngx_log_debug
+#define ngx_log_debug5 ngx_log_debug
+#define ngx_log_debug6 ngx_log_debug
+#define ngx_log_debug7 ngx_log_debug
+#define ngx_log_debug8 ngx_log_debug
+
+
+#else /* NO VARIADIC MACROS */
+
+#define ngx_log_debug0(level, log, err, fmt) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt)
+
+#define ngx_log_debug1(level, log, err, fmt, arg1) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1)
+
+#define ngx_log_debug2(level, log, err, fmt, arg1, arg2) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1, arg2)
+
+#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3)
+
+#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4)
+
+#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5)
+
+#define ngx_log_debug6(level, log, err, fmt, \
+ arg1, arg2, arg3, arg4, arg5, arg6) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)
+
+#define ngx_log_debug7(level, log, err, fmt, \
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, \
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7)
+
+#define ngx_log_debug8(level, log, err, fmt, \
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) \
+ if ((log)->log_level & level) \
+ ngx_log_debug_core(log, err, fmt, \
+ arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
+
+#endif
+
+#else /* NO NGX_DEBUG */
+
+#define ngx_log_debug0(level, log, err, fmt)
+#define ngx_log_debug1(level, log, err, fmt, arg1)
+#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)
+#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)
+#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)
+#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)
+#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)
+#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7)
+#define ngx_log_debug8(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7, arg8)
+
+#endif
+
+/*********************************/
+
+ngx_log_t *ngx_log_init(u_char *prefix);
+ngx_log_t *ngx_log_create(ngx_cycle_t *cycle, ngx_str_t *name);
+char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);
+void ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);
+void ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);
+u_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);
+
+
+extern ngx_module_t ngx_errlog_module;
+extern ngx_uint_t ngx_use_stderr;
+
+
+#endif /* _NGX_LOG_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_md5.c b/usr.sbin/nginx/src/core/ngx_md5.c
new file mode 100644
index 00000000000..09a93991eb1
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_md5.c
@@ -0,0 +1,288 @@
+
+/*
+ * An internal implementation, based on Alexander Peslyak's
+ * public domain implementation:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ * It is not expected to be optimal and is used only
+ * if no MD5 implementation was found in system.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_md5.h>
+
+
+#if !(NGX_HAVE_MD5)
+
+static const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data,
+ size_t size);
+
+
+void
+ngx_md5_init(ngx_md5_t *ctx)
+{
+ ctx->a = 0x67452301;
+ ctx->b = 0xefcdab89;
+ ctx->c = 0x98badcfe;
+ ctx->d = 0x10325476;
+
+ ctx->bytes = 0;
+}
+
+
+void
+ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size)
+{
+ size_t used, free;
+
+ used = (size_t) (ctx->bytes & 0x3f);
+ ctx->bytes += size;
+
+ if (used) {
+ free = 64 - used;
+
+ if (size < free) {
+ ngx_memcpy(&ctx->buffer[used], data, size);
+ return;
+ }
+
+ data = ngx_cpymem(&ctx->buffer[used], data, free);
+ size -= free;
+ (void) ngx_md5_body(ctx, ctx->buffer, 64);
+ }
+
+ if (size >= 64) {
+ data = ngx_md5_body(ctx, data, size & ~(size_t) 0x3f);
+ size &= 0x3f;
+ }
+
+ ngx_memcpy(ctx->buffer, data, size);
+}
+
+
+void
+ngx_md5_final(u_char result[16], ngx_md5_t *ctx)
+{
+ size_t used, free;
+
+ used = (size_t) (ctx->bytes & 0x3f);
+
+ ctx->buffer[used++] = 0x80;
+
+ free = 64 - used;
+
+ if (free < 8) {
+ ngx_memzero(&ctx->buffer[used], free);
+ (void) ngx_md5_body(ctx, ctx->buffer, 64);
+ used = 0;
+ free = 64;
+ }
+
+ ngx_memzero(&ctx->buffer[used], free - 8);
+
+ ctx->bytes <<= 3;
+ ctx->buffer[56] = (u_char) ctx->bytes;
+ ctx->buffer[57] = (u_char) (ctx->bytes >> 8);
+ ctx->buffer[58] = (u_char) (ctx->bytes >> 16);
+ ctx->buffer[59] = (u_char) (ctx->bytes >> 24);
+ ctx->buffer[60] = (u_char) (ctx->bytes >> 32);
+ ctx->buffer[61] = (u_char) (ctx->bytes >> 40);
+ ctx->buffer[62] = (u_char) (ctx->bytes >> 48);
+ ctx->buffer[63] = (u_char) (ctx->bytes >> 56);
+
+ (void) ngx_md5_body(ctx, ctx->buffer, 64);
+
+ result[0] = (u_char) ctx->a;
+ result[1] = (u_char) (ctx->a >> 8);
+ result[2] = (u_char) (ctx->a >> 16);
+ result[3] = (u_char) (ctx->a >> 24);
+ result[4] = (u_char) ctx->b;
+ result[5] = (u_char) (ctx->b >> 8);
+ result[6] = (u_char) (ctx->b >> 16);
+ result[7] = (u_char) (ctx->b >> 24);
+ result[8] = (u_char) ctx->c;
+ result[9] = (u_char) (ctx->c >> 8);
+ result[10] = (u_char) (ctx->c >> 16);
+ result[11] = (u_char) (ctx->c >> 24);
+ result[12] = (u_char) ctx->d;
+ result[13] = (u_char) (ctx->d >> 8);
+ result[14] = (u_char) (ctx->d >> 16);
+ result[15] = (u_char) (ctx->d >> 24);
+
+ ngx_memzero(ctx, sizeof(*ctx));
+}
+
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in
+ * Colin Plumb's implementation.
+ */
+
+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+
+#define STEP(f, a, b, c, d, x, t, s) \
+ (a) += f((b), (c), (d)) + (x) + (t); \
+ (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+ (a) += (b)
+
+/*
+ * SET() reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ *
+ * The check for little-endian architectures that tolerate unaligned
+ * memory accesses is just an optimization. Nothing will break if it
+ * does not work.
+ */
+
+#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+
+#define SET(n) (*(uint32_t *) &p[n * 4])
+#define GET(n) (*(uint32_t *) &p[n * 4])
+
+#else
+
+#define SET(n) \
+ (block[n] = \
+ (uint32_t) p[n * 4] | \
+ ((uint32_t) p[n * 4 + 1] << 8) | \
+ ((uint32_t) p[n * 4 + 2] << 16) | \
+ ((uint32_t) p[n * 4 + 3] << 24))
+
+#define GET(n) block[n]
+
+#endif
+
+
+/*
+ * This processes one or more 64-byte data blocks, but does not update
+ * the bit counters. There are no alignment requirements.
+ */
+
+static const u_char *
+ngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size)
+{
+ uint32_t a, b, c, d;
+ uint32_t saved_a, saved_b, saved_c, saved_d;
+ const u_char *p;
+#if !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)
+ uint32_t block[16];
+#endif
+
+ p = data;
+
+ a = ctx->a;
+ b = ctx->b;
+ c = ctx->c;
+ d = ctx->d;
+
+ do {
+ saved_a = a;
+ saved_b = b;
+ saved_c = c;
+ saved_d = d;
+
+ /* Round 1 */
+
+ STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7);
+ STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12);
+ STEP(F, c, d, a, b, SET(2), 0x242070db, 17);
+ STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22);
+ STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7);
+ STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12);
+ STEP(F, c, d, a, b, SET(6), 0xa8304613, 17);
+ STEP(F, b, c, d, a, SET(7), 0xfd469501, 22);
+ STEP(F, a, b, c, d, SET(8), 0x698098d8, 7);
+ STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12);
+ STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17);
+ STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22);
+ STEP(F, a, b, c, d, SET(12), 0x6b901122, 7);
+ STEP(F, d, a, b, c, SET(13), 0xfd987193, 12);
+ STEP(F, c, d, a, b, SET(14), 0xa679438e, 17);
+ STEP(F, b, c, d, a, SET(15), 0x49b40821, 22);
+
+ /* Round 2 */
+
+ STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5);
+ STEP(G, d, a, b, c, GET(6), 0xc040b340, 9);
+ STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14);
+ STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20);
+ STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5);
+ STEP(G, d, a, b, c, GET(10), 0x02441453, 9);
+ STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14);
+ STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20);
+ STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5);
+ STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9);
+ STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14);
+ STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20);
+ STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5);
+ STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9);
+ STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14);
+ STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20);
+
+ /* Round 3 */
+
+ STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4);
+ STEP(H, d, a, b, c, GET(8), 0x8771f681, 11);
+ STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16);
+ STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23);
+ STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4);
+ STEP(H, d, a, b, c, GET(4), 0x4bdecfa9, 11);
+ STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16);
+ STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23);
+ STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4);
+ STEP(H, d, a, b, c, GET(0), 0xeaa127fa, 11);
+ STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16);
+ STEP(H, b, c, d, a, GET(6), 0x04881d05, 23);
+ STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4);
+ STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11);
+ STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16);
+ STEP(H, b, c, d, a, GET(2), 0xc4ac5665, 23);
+
+ /* Round 4 */
+
+ STEP(I, a, b, c, d, GET(0), 0xf4292244, 6);
+ STEP(I, d, a, b, c, GET(7), 0x432aff97, 10);
+ STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15);
+ STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21);
+ STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6);
+ STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10);
+ STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15);
+ STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21);
+ STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6);
+ STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10);
+ STEP(I, c, d, a, b, GET(6), 0xa3014314, 15);
+ STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21);
+ STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6);
+ STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10);
+ STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15);
+ STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21);
+
+ a += saved_a;
+ b += saved_b;
+ c += saved_c;
+ d += saved_d;
+
+ p += 64;
+
+ } while (size -= 64);
+
+ ctx->a = a;
+ ctx->b = b;
+ ctx->c = c;
+ ctx->d = d;
+
+ return p;
+}
+
+#endif
diff --git a/usr.sbin/nginx/src/core/ngx_md5.h b/usr.sbin/nginx/src/core/ngx_md5.h
new file mode 100644
index 00000000000..45a1011bb5d
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_md5.h
@@ -0,0 +1,59 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_MD5_H_INCLUDED_
+#define _NGX_MD5_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_MD5)
+
+#if (NGX_HAVE_OPENSSL_MD5_H)
+#include <openssl/md5.h>
+#else
+#include <md5.h>
+#endif
+
+
+typedef MD5_CTX ngx_md5_t;
+
+
+#if (NGX_OPENSSL_MD5)
+
+#define ngx_md5_init MD5_Init
+#define ngx_md5_update MD5_Update
+#define ngx_md5_final MD5_Final
+
+#else
+
+#define ngx_md5_init MD5Init
+#define ngx_md5_update MD5Update
+#define ngx_md5_final MD5Final
+
+#endif
+
+
+#else /* !NGX_HAVE_MD5 */
+
+
+typedef struct {
+ uint64_t bytes;
+ uint32_t a, b, c, d;
+ u_char buffer[64];
+} ngx_md5_t;
+
+
+void ngx_md5_init(ngx_md5_t *ctx);
+void ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size);
+void ngx_md5_final(u_char result[16], ngx_md5_t *ctx);
+
+
+#endif
+
+#endif /* _NGX_MD5_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_murmurhash.c b/usr.sbin/nginx/src/core/ngx_murmurhash.c
new file mode 100644
index 00000000000..c31e0e03500
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_murmurhash.c
@@ -0,0 +1,50 @@
+
+/*
+ * Copyright (C) Austin Appleby
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+uint32_t
+ngx_murmur_hash2(u_char *data, size_t len)
+{
+ uint32_t h, k;
+
+ h = 0 ^ len;
+
+ while (len >= 4) {
+ k = data[0];
+ k |= data[1] << 8;
+ k |= data[2] << 16;
+ k |= data[3] << 24;
+
+ k *= 0x5bd1e995;
+ k ^= k >> 24;
+ k *= 0x5bd1e995;
+
+ h *= 0x5bd1e995;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ switch (len) {
+ case 3:
+ h ^= data[2] << 16;
+ case 2:
+ h ^= data[1] << 8;
+ case 1:
+ h ^= data[0];
+ h *= 0x5bd1e995;
+ }
+
+ h ^= h >> 13;
+ h *= 0x5bd1e995;
+ h ^= h >> 15;
+
+ return h;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_murmurhash.h b/usr.sbin/nginx/src/core/ngx_murmurhash.h
new file mode 100644
index 00000000000..ae40779d4b1
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_murmurhash.h
@@ -0,0 +1,18 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_MURMURHASH_H_INCLUDED_
+#define _NGX_MURMURHASH_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+uint32_t ngx_murmur_hash2(u_char *data, size_t len);
+
+
+#endif /* _NGX_CRC_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_open_file_cache.c b/usr.sbin/nginx/src/core/ngx_open_file_cache.c
new file mode 100644
index 00000000000..a70385c3544
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_open_file_cache.c
@@ -0,0 +1,888 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+/*
+ * open file cache caches
+ * open file handles with stat() info;
+ * directories stat() info;
+ * files and directories errors: not found, access denied, etc.
+ */
+
+
+#define NGX_MIN_READ_AHEAD (128 * 1024)
+
+
+static void ngx_open_file_cache_cleanup(void *data);
+static ngx_int_t ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of,
+ ngx_log_t *log);
+static void ngx_open_file_add_event(ngx_open_file_cache_t *cache,
+ ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);
+static void ngx_open_file_cleanup(void *data);
+static void ngx_close_cached_file(ngx_open_file_cache_t *cache,
+ ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);
+static void ngx_open_file_del_event(ngx_cached_open_file_t *file);
+static void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,
+ ngx_uint_t n, ngx_log_t *log);
+static void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static ngx_cached_open_file_t *
+ ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
+ uint32_t hash);
+static void ngx_open_file_cache_remove(ngx_event_t *ev);
+
+
+ngx_open_file_cache_t *
+ngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)
+{
+ ngx_pool_cleanup_t *cln;
+ ngx_open_file_cache_t *cache;
+
+ cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));
+ if (cache == NULL) {
+ return NULL;
+ }
+
+ ngx_rbtree_init(&cache->rbtree, &cache->sentinel,
+ ngx_open_file_cache_rbtree_insert_value);
+
+ ngx_queue_init(&cache->expire_queue);
+
+ cache->current = 0;
+ cache->max = max;
+ cache->inactive = inactive;
+
+ cln = ngx_pool_cleanup_add(pool, 0);
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ cln->handler = ngx_open_file_cache_cleanup;
+ cln->data = cache;
+
+ return cache;
+}
+
+
+static void
+ngx_open_file_cache_cleanup(void *data)
+{
+ ngx_open_file_cache_t *cache = data;
+
+ ngx_queue_t *q;
+ ngx_cached_open_file_t *file;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "open file cache cleanup");
+
+ for ( ;; ) {
+
+ if (ngx_queue_empty(&cache->expire_queue)) {
+ break;
+ }
+
+ q = ngx_queue_last(&cache->expire_queue);
+
+ file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
+
+ ngx_queue_remove(q);
+
+ ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+ cache->current--;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "delete cached open file: %s", file->name);
+
+ if (!file->err && !file->is_dir) {
+ file->close = 1;
+ file->count = 0;
+ ngx_close_cached_file(cache, file, 0, ngx_cycle->log);
+
+ } else {
+ ngx_free(file->name);
+ ngx_free(file);
+ }
+ }
+
+ if (cache->current) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "%d items still leave in open file cache",
+ cache->current);
+ }
+
+ if (cache->rbtree.root != cache->rbtree.sentinel) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
+ "rbtree still is not empty in open file cache");
+
+ }
+}
+
+
+ngx_int_t
+ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
+ ngx_open_file_info_t *of, ngx_pool_t *pool)
+{
+ time_t now;
+ uint32_t hash;
+ ngx_int_t rc;
+ ngx_file_info_t fi;
+ ngx_pool_cleanup_t *cln;
+ ngx_cached_open_file_t *file;
+ ngx_pool_cleanup_file_t *clnf;
+ ngx_open_file_cache_cleanup_t *ofcln;
+
+ of->fd = NGX_INVALID_FILE;
+ of->err = 0;
+
+ if (cache == NULL) {
+
+ if (of->test_only) {
+
+ if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {
+ of->err = ngx_errno;
+ of->failed = ngx_file_info_n;
+ return NGX_ERROR;
+ }
+
+ of->uniq = ngx_file_uniq(&fi);
+ of->mtime = ngx_file_mtime(&fi);
+ of->size = ngx_file_size(&fi);
+ of->fs_size = ngx_file_fs_size(&fi);
+ of->is_dir = ngx_is_dir(&fi);
+ of->is_file = ngx_is_file(&fi);
+ of->is_link = ngx_is_link(&fi);
+ of->is_exec = ngx_is_exec(&fi);
+
+ return NGX_OK;
+ }
+
+ cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+ if (rc == NGX_OK && !of->is_dir) {
+ cln->handler = ngx_pool_cleanup_file;
+ clnf = cln->data;
+
+ clnf->fd = of->fd;
+ clnf->name = name->data;
+ clnf->log = pool->log;
+ }
+
+ return rc;
+ }
+
+ cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ now = ngx_time();
+
+ hash = ngx_crc32_long(name->data, name->len);
+
+ file = ngx_open_file_lookup(cache, name, hash);
+
+ if (file) {
+
+ file->uses++;
+
+ ngx_queue_remove(&file->queue);
+
+ if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
+
+ /* file was not used often enough to keep open */
+
+ rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+ if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+ goto failed;
+ }
+
+ goto add_event;
+ }
+
+ if (file->use_event
+ || (file->event == NULL
+ && (of->uniq == 0 || of->uniq == file->uniq)
+ && now - file->created < of->valid))
+ {
+ if (file->err == 0) {
+
+ of->fd = file->fd;
+ of->uniq = file->uniq;
+ of->mtime = file->mtime;
+ of->size = file->size;
+
+ of->is_dir = file->is_dir;
+ of->is_file = file->is_file;
+ of->is_link = file->is_link;
+ of->is_exec = file->is_exec;
+ of->is_directio = file->is_directio;
+
+ if (!file->is_dir) {
+ file->count++;
+ ngx_open_file_add_event(cache, file, of, pool->log);
+ }
+
+ } else {
+ of->err = file->err;
+ of->failed = ngx_open_file_n;
+ }
+
+ goto found;
+ }
+
+ ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
+ "retest open file: %s, fd:%d, c:%d, e:%d",
+ file->name, file->fd, file->count, file->err);
+
+ if (file->is_dir) {
+
+ /*
+ * chances that directory became file are very small
+ * so test_dir flag allows to use a single syscall
+ * in ngx_file_info() instead of three syscalls
+ */
+
+ of->test_dir = 1;
+ }
+
+ of->fd = file->fd;
+ of->uniq = file->uniq;
+
+ rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+ if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+ goto failed;
+ }
+
+ if (of->is_dir) {
+
+ if (file->is_dir || file->err) {
+ goto update;
+ }
+
+ /* file became directory */
+
+ } else if (of->err == 0) { /* file */
+
+ if (file->is_dir || file->err) {
+ goto add_event;
+ }
+
+ if (of->uniq == file->uniq) {
+
+ file->count++;
+
+ if (file->event) {
+ file->use_event = 1;
+ }
+
+ goto renew;
+ }
+
+ /* file was changed */
+
+ } else { /* error to cache */
+
+ if (file->err || file->is_dir) {
+ goto update;
+ }
+
+ /* file was removed, etc. */
+ }
+
+ if (file->count == 0) {
+
+ ngx_open_file_del_event(file);
+
+ if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed",
+ name->data);
+ }
+
+ goto add_event;
+ }
+
+ ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+ cache->current--;
+
+ file->close = 1;
+
+ goto create;
+ }
+
+ /* not found */
+
+ rc = ngx_open_and_stat_file(name->data, of, pool->log);
+
+ if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
+ goto failed;
+ }
+
+create:
+
+ if (cache->current >= cache->max) {
+ ngx_expire_old_cached_files(cache, 0, pool->log);
+ }
+
+ file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
+
+ if (file == NULL) {
+ goto failed;
+ }
+
+ file->name = ngx_alloc(name->len + 1, pool->log);
+
+ if (file->name == NULL) {
+ ngx_free(file);
+ file = NULL;
+ goto failed;
+ }
+
+ ngx_cpystrn(file->name, name->data, name->len + 1);
+
+ file->node.key = hash;
+
+ ngx_rbtree_insert(&cache->rbtree, &file->node);
+
+ cache->current++;
+
+ file->uses = 1;
+ file->count = 0;
+ file->use_event = 0;
+ file->event = NULL;
+
+add_event:
+
+ ngx_open_file_add_event(cache, file, of, pool->log);
+
+update:
+
+ file->fd = of->fd;
+ file->err = of->err;
+
+ if (of->err == 0) {
+ file->uniq = of->uniq;
+ file->mtime = of->mtime;
+ file->size = of->size;
+
+ file->close = 0;
+
+ file->is_dir = of->is_dir;
+ file->is_file = of->is_file;
+ file->is_link = of->is_link;
+ file->is_exec = of->is_exec;
+ file->is_directio = of->is_directio;
+
+ if (!of->is_dir) {
+ file->count++;
+ }
+ }
+
+renew:
+
+ file->created = now;
+
+found:
+
+ file->accessed = now;
+
+ ngx_queue_insert_head(&cache->expire_queue, &file->queue);
+
+ ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
+ "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
+ file->name, file->fd, file->count, file->err, file->uses);
+
+ if (of->err == 0) {
+
+ if (!of->is_dir) {
+ cln->handler = ngx_open_file_cleanup;
+ ofcln = cln->data;
+
+ ofcln->cache = cache;
+ ofcln->file = file;
+ ofcln->min_uses = of->min_uses;
+ ofcln->log = pool->log;
+ }
+
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+
+failed:
+
+ if (file) {
+ ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+ cache->current--;
+
+ if (file->count == 0) {
+
+ if (file->fd != NGX_INVALID_FILE) {
+ if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed",
+ file->name);
+ }
+ }
+
+ ngx_free(file->name);
+ ngx_free(file);
+
+ } else {
+ file->close = 1;
+ }
+ }
+
+ if (of->fd != NGX_INVALID_FILE) {
+ if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name->data);
+ }
+ }
+
+ return NGX_ERROR;
+}
+
+
+static ngx_int_t
+ngx_open_and_stat_file(u_char *name, ngx_open_file_info_t *of, ngx_log_t *log)
+{
+ ngx_fd_t fd;
+ ngx_file_info_t fi;
+
+ if (of->fd != NGX_INVALID_FILE) {
+
+ if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+ of->failed = ngx_file_info_n;
+ goto failed;
+ }
+
+ if (of->uniq == ngx_file_uniq(&fi)) {
+ goto done;
+ }
+
+ } else if (of->test_dir) {
+
+ if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {
+ of->failed = ngx_file_info_n;
+ goto failed;
+ }
+
+ if (ngx_is_dir(&fi)) {
+ goto done;
+ }
+ }
+
+ if (!of->log) {
+
+ /*
+ * Use non-blocking open() not to hang on FIFO files, etc.
+ * This flag has no effect on a regular files.
+ */
+
+ fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
+ NGX_FILE_OPEN, 0);
+
+ } else {
+ fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+ }
+
+ if (fd == NGX_INVALID_FILE) {
+ of->failed = ngx_open_file_n;
+ goto failed;
+ }
+
+ if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
+ ngx_fd_info_n " \"%s\" failed", name);
+
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ of->fd = NGX_INVALID_FILE;
+
+ return NGX_ERROR;
+ }
+
+ if (ngx_is_dir(&fi)) {
+ if (ngx_close_file(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", name);
+ }
+
+ of->fd = NGX_INVALID_FILE;
+
+ } else {
+ of->fd = fd;
+
+ if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
+ if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_read_ahead_n " \"%s\" failed", name);
+ }
+ }
+
+ if (of->directio <= ngx_file_size(&fi)) {
+ if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_directio_on_n " \"%s\" failed", name);
+
+ } else {
+ of->is_directio = 1;
+ }
+ }
+ }
+
+done:
+
+ of->uniq = ngx_file_uniq(&fi);
+ of->mtime = ngx_file_mtime(&fi);
+ of->size = ngx_file_size(&fi);
+ of->fs_size = ngx_file_fs_size(&fi);
+ of->is_dir = ngx_is_dir(&fi);
+ of->is_file = ngx_is_file(&fi);
+ of->is_link = ngx_is_link(&fi);
+ of->is_exec = ngx_is_exec(&fi);
+
+ return NGX_OK;
+
+failed:
+
+ of->fd = NGX_INVALID_FILE;
+ of->err = ngx_errno;
+
+ return NGX_ERROR;
+}
+
+
+/*
+ * we ignore any possible event setting error and
+ * fallback to usual periodic file retests
+ */
+
+static void
+ngx_open_file_add_event(ngx_open_file_cache_t *cache,
+ ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)
+{
+ ngx_open_file_cache_event_t *fev;
+
+ if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)
+ || !of->events
+ || file->event
+ || of->fd == NGX_INVALID_FILE
+ || file->uses < of->min_uses)
+ {
+ return;
+ }
+
+ file->use_event = 0;
+
+ file->event = ngx_calloc(sizeof(ngx_event_t), log);
+ if (file->event== NULL) {
+ return;
+ }
+
+ fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);
+ if (fev == NULL) {
+ ngx_free(file->event);
+ file->event = NULL;
+ return;
+ }
+
+ fev->fd = of->fd;
+ fev->file = file;
+ fev->cache = cache;
+
+ file->event->handler = ngx_open_file_cache_remove;
+ file->event->data = fev;
+
+ /*
+ * although vnode event may be called while ngx_cycle->poll
+ * destruction, however, cleanup procedures are run before any
+ * memory freeing and events will be canceled.
+ */
+
+ file->event->log = ngx_cycle->log;
+
+ if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)
+ != NGX_OK)
+ {
+ ngx_free(file->event->data);
+ ngx_free(file->event);
+ file->event = NULL;
+ return;
+ }
+
+ /*
+ * we do not set file->use_event here because there may be a race
+ * condition: a file may be deleted between opening the file and
+ * adding event, so we rely upon event notification only after
+ * one file revalidation on next file access
+ */
+
+ return;
+}
+
+
+static void
+ngx_open_file_cleanup(void *data)
+{
+ ngx_open_file_cache_cleanup_t *c = data;
+
+ c->file->count--;
+
+ ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);
+
+ /* drop one or two expired open files */
+ ngx_expire_old_cached_files(c->cache, 1, c->log);
+}
+
+
+static void
+ngx_close_cached_file(ngx_open_file_cache_t *cache,
+ ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)
+{
+ ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,
+ "close cached open file: %s, fd:%d, c:%d, u:%d, %d",
+ file->name, file->fd, file->count, file->uses, file->close);
+
+ if (!file->close) {
+
+ file->accessed = ngx_time();
+
+ ngx_queue_remove(&file->queue);
+
+ ngx_queue_insert_head(&cache->expire_queue, &file->queue);
+
+ if (file->uses >= min_uses || file->count) {
+ return;
+ }
+ }
+
+ ngx_open_file_del_event(file);
+
+ if (file->count) {
+ return;
+ }
+
+ if (file->fd != NGX_INVALID_FILE) {
+
+ if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", file->name);
+ }
+
+ file->fd = NGX_INVALID_FILE;
+ }
+
+ if (!file->close) {
+ return;
+ }
+
+ ngx_free(file->name);
+ ngx_free(file);
+}
+
+
+static void
+ngx_open_file_del_event(ngx_cached_open_file_t *file)
+{
+ if (file->event == NULL) {
+ return;
+ }
+
+ (void) ngx_del_event(file->event, NGX_VNODE_EVENT,
+ file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);
+
+ ngx_free(file->event->data);
+ ngx_free(file->event);
+ file->event = NULL;
+ file->use_event = 0;
+}
+
+
+static void
+ngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,
+ ngx_log_t *log)
+{
+ time_t now;
+ ngx_queue_t *q;
+ ngx_cached_open_file_t *file;
+
+ now = ngx_time();
+
+ /*
+ * n == 1 deletes one or two inactive files
+ * n == 0 deletes least recently used file by force
+ * and one or two inactive files
+ */
+
+ while (n < 3) {
+
+ if (ngx_queue_empty(&cache->expire_queue)) {
+ return;
+ }
+
+ q = ngx_queue_last(&cache->expire_queue);
+
+ file = ngx_queue_data(q, ngx_cached_open_file_t, queue);
+
+ if (n++ != 0 && now - file->accessed <= cache->inactive) {
+ return;
+ }
+
+ ngx_queue_remove(q);
+
+ ngx_rbtree_delete(&cache->rbtree, &file->node);
+
+ cache->current--;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,
+ "expire cached open file: %s", file->name);
+
+ if (!file->err && !file->is_dir) {
+ file->close = 1;
+ ngx_close_cached_file(cache, file, 0, log);
+
+ } else {
+ ngx_free(file->name);
+ ngx_free(file);
+ }
+ }
+}
+
+
+static void
+ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_cached_open_file_t *file, *file_temp;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ file = (ngx_cached_open_file_t *) node;
+ file_temp = (ngx_cached_open_file_t *) temp;
+
+ p = (ngx_strcmp(file->name, file_temp->name) < 0)
+ ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+static ngx_cached_open_file_t *
+ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,
+ uint32_t hash)
+{
+ ngx_int_t rc;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_cached_open_file_t *file;
+
+ node = cache->rbtree.root;
+ sentinel = cache->rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ do {
+ file = (ngx_cached_open_file_t *) node;
+
+ rc = ngx_strcmp(name->data, file->name);
+
+ if (rc == 0) {
+ return file;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+
+ } while (node != sentinel && hash == node->key);
+
+ break;
+ }
+
+ return NULL;
+}
+
+
+static void
+ngx_open_file_cache_remove(ngx_event_t *ev)
+{
+ ngx_cached_open_file_t *file;
+ ngx_open_file_cache_event_t *fev;
+
+ fev = ev->data;
+ file = fev->file;
+
+ ngx_queue_remove(&file->queue);
+
+ ngx_rbtree_delete(&fev->cache->rbtree, &file->node);
+
+ fev->cache->current--;
+
+ /* NGX_ONESHOT_EVENT was already deleted */
+ file->event = NULL;
+ file->use_event = 0;
+
+ file->close = 1;
+
+ ngx_close_cached_file(fev->cache, file, 0, ev->log);
+
+ /* free memory only when fev->cache and fev->file are already not needed */
+
+ ngx_free(ev->data);
+ ngx_free(ev);
+}
diff --git a/usr.sbin/nginx/src/core/ngx_open_file_cache.h b/usr.sbin/nginx/src/core/ngx_open_file_cache.h
new file mode 100644
index 00000000000..638bc5dd115
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_open_file_cache.h
@@ -0,0 +1,118 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_
+#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_
+
+
+#define NGX_OPEN_FILE_DIRECTIO_OFF NGX_MAX_OFF_T_VALUE
+
+
+typedef struct {
+ ngx_fd_t fd;
+ ngx_file_uniq_t uniq;
+ time_t mtime;
+ off_t size;
+ off_t fs_size;
+ off_t directio;
+ size_t read_ahead;
+
+ ngx_err_t err;
+ char *failed;
+
+ time_t valid;
+
+ ngx_uint_t min_uses;
+
+ unsigned test_dir:1;
+ unsigned test_only:1;
+ unsigned log:1;
+ unsigned errors:1;
+ unsigned events:1;
+
+ unsigned is_dir:1;
+ unsigned is_file:1;
+ unsigned is_link:1;
+ unsigned is_exec:1;
+ unsigned is_directio:1;
+} ngx_open_file_info_t;
+
+
+typedef struct ngx_cached_open_file_s ngx_cached_open_file_t;
+
+struct ngx_cached_open_file_s {
+ ngx_rbtree_node_t node;
+ ngx_queue_t queue;
+
+ u_char *name;
+ time_t created;
+ time_t accessed;
+
+ ngx_fd_t fd;
+ ngx_file_uniq_t uniq;
+ time_t mtime;
+ off_t size;
+ ngx_err_t err;
+
+ uint32_t uses;
+
+ unsigned count:24;
+ unsigned close:1;
+ unsigned use_event:1;
+
+ unsigned is_dir:1;
+ unsigned is_file:1;
+ unsigned is_link:1;
+ unsigned is_exec:1;
+ unsigned is_directio:1;
+
+ ngx_event_t *event;
+};
+
+
+typedef struct {
+ ngx_rbtree_t rbtree;
+ ngx_rbtree_node_t sentinel;
+ ngx_queue_t expire_queue;
+
+ ngx_uint_t current;
+ ngx_uint_t max;
+ time_t inactive;
+} ngx_open_file_cache_t;
+
+
+typedef struct {
+ ngx_open_file_cache_t *cache;
+ ngx_cached_open_file_t *file;
+ ngx_uint_t min_uses;
+ ngx_log_t *log;
+} ngx_open_file_cache_cleanup_t;
+
+
+typedef struct {
+
+ /* ngx_connection_t stub to allow use c->fd as event ident */
+ void *data;
+ ngx_event_t *read;
+ ngx_event_t *write;
+ ngx_fd_t fd;
+
+ ngx_cached_open_file_t *file;
+ ngx_open_file_cache_t *cache;
+} ngx_open_file_cache_event_t;
+
+
+ngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool,
+ ngx_uint_t max, time_t inactive);
+ngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
+ ngx_open_file_info_t *of, ngx_pool_t *pool);
+
+
+#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_output_chain.c b/usr.sbin/nginx/src/core/ngx_output_chain.c
new file mode 100644
index 00000000000..4f100a81837
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_output_chain.c
@@ -0,0 +1,672 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#if 0
+#define NGX_SENDFILE_LIMIT 4096
+#endif
+
+/*
+ * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
+ * to an application memory from a device if parameters are aligned
+ * to device sector boundary (512 bytes). They fallback to usual read
+ * operation if the parameters are not aligned.
+ * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
+ * sector boundary, otherwise it returns EINVAL. The sector size is
+ * usually 512 bytes, however, on XFS it may be 4096 bytes.
+ */
+
+#define NGX_NONE 1
+
+
+static ngx_inline ngx_int_t
+ ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
+static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
+ ngx_chain_t **chain, ngx_chain_t *in);
+static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
+ off_t bsize);
+static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
+ off_t bsize);
+static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
+
+
+ngx_int_t
+ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
+{
+ off_t bsize;
+ ngx_int_t rc, last;
+ ngx_chain_t *cl, *out, **last_out;
+
+ if (ctx->in == NULL && ctx->busy == NULL) {
+
+ /*
+ * the short path for the case when the ctx->in and ctx->busy chains
+ * are empty, the incoming chain is empty too or has the single buf
+ * that does not require the copy
+ */
+
+ if (in == NULL) {
+ return ctx->output_filter(ctx->filter_ctx, in);
+ }
+
+ if (in->next == NULL
+#if (NGX_SENDFILE_LIMIT)
+ && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
+#endif
+ && ngx_output_chain_as_is(ctx, in->buf))
+ {
+ return ctx->output_filter(ctx->filter_ctx, in);
+ }
+ }
+
+ /* add the incoming buf to the chain ctx->in */
+
+ if (in) {
+ if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ out = NULL;
+ last_out = &out;
+ last = NGX_NONE;
+
+ for ( ;; ) {
+
+#if (NGX_HAVE_FILE_AIO)
+ if (ctx->aio) {
+ return NGX_AGAIN;
+ }
+#endif
+
+ while (ctx->in) {
+
+ /*
+ * cycle while there are the ctx->in bufs
+ * and there are the free output bufs to copy in
+ */
+
+ bsize = ngx_buf_size(ctx->in->buf);
+
+ if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
+
+ ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
+ "zero size buf in output "
+ "t:%d r:%d f:%d %p %p-%p %p %O-%O",
+ ctx->in->buf->temporary,
+ ctx->in->buf->recycled,
+ ctx->in->buf->in_file,
+ ctx->in->buf->start,
+ ctx->in->buf->pos,
+ ctx->in->buf->last,
+ ctx->in->buf->file,
+ ctx->in->buf->file_pos,
+ ctx->in->buf->file_last);
+
+ ngx_debug_point();
+
+ ctx->in = ctx->in->next;
+
+ continue;
+ }
+
+ if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
+
+ /* move the chain link to the output chain */
+
+ cl = ctx->in;
+ ctx->in = cl->next;
+
+ *last_out = cl;
+ last_out = &cl->next;
+ cl->next = NULL;
+
+ continue;
+ }
+
+ if (ctx->buf == NULL) {
+
+ rc = ngx_output_chain_align_file_buf(ctx, bsize);
+
+ if (rc == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (rc != NGX_OK) {
+
+ if (ctx->free) {
+
+ /* get the free buf */
+
+ cl = ctx->free;
+ ctx->buf = cl->buf;
+ ctx->free = cl->next;
+
+ ngx_free_chain(ctx->pool, cl);
+
+ } else if (out || ctx->allocated == ctx->bufs.num) {
+
+ break;
+
+ } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ rc = ngx_output_chain_copy_buf(ctx);
+
+ if (rc == NGX_ERROR) {
+ return rc;
+ }
+
+ if (rc == NGX_AGAIN) {
+ if (out) {
+ break;
+ }
+
+ return rc;
+ }
+
+ /* delete the completed buf from the ctx->in chain */
+
+ if (ngx_buf_size(ctx->in->buf) == 0) {
+ ctx->in = ctx->in->next;
+ }
+
+ cl = ngx_alloc_chain_link(ctx->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = ctx->buf;
+ cl->next = NULL;
+ *last_out = cl;
+ last_out = &cl->next;
+ ctx->buf = NULL;
+ }
+
+ if (out == NULL && last != NGX_NONE) {
+
+ if (ctx->in) {
+ return NGX_AGAIN;
+ }
+
+ return last;
+ }
+
+ last = ctx->output_filter(ctx->filter_ctx, out);
+
+ if (last == NGX_ERROR || last == NGX_DONE) {
+ return last;
+ }
+
+ ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
+ last_out = &out;
+ }
+}
+
+
+static ngx_inline ngx_int_t
+ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
+{
+ ngx_uint_t sendfile;
+
+ if (ngx_buf_special(buf)) {
+ return 1;
+ }
+
+ if (buf->in_file && buf->file->directio) {
+ return 0;
+ }
+
+ sendfile = ctx->sendfile;
+
+#if (NGX_SENDFILE_LIMIT)
+
+ if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
+ sendfile = 0;
+ }
+
+#endif
+
+ if (!sendfile) {
+
+ if (!ngx_buf_in_memory(buf)) {
+ return 0;
+ }
+
+ buf->in_file = 0;
+ }
+
+ if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
+ return 0;
+ }
+
+ if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+static ngx_int_t
+ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
+ ngx_chain_t *in)
+{
+ ngx_chain_t *cl, **ll;
+#if (NGX_SENDFILE_LIMIT)
+ ngx_buf_t *b, *buf;
+#endif
+
+ ll = chain;
+
+ for (cl = *chain; cl; cl = cl->next) {
+ ll = &cl->next;
+ }
+
+ while (in) {
+
+ cl = ngx_alloc_chain_link(pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+#if (NGX_SENDFILE_LIMIT)
+
+ buf = in->buf;
+
+ if (buf->in_file
+ && buf->file_pos < NGX_SENDFILE_LIMIT
+ && buf->file_last > NGX_SENDFILE_LIMIT)
+ {
+ /* split a file buf on two bufs by the sendfile limit */
+
+ b = ngx_calloc_buf(pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ ngx_memcpy(b, buf, sizeof(ngx_buf_t));
+
+ if (ngx_buf_in_memory(buf)) {
+ buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
+ b->last = buf->pos;
+ }
+
+ buf->file_pos = NGX_SENDFILE_LIMIT;
+ b->file_last = NGX_SENDFILE_LIMIT;
+
+ cl->buf = b;
+
+ } else {
+ cl->buf = buf;
+ in = in->next;
+ }
+
+#else
+ cl->buf = in->buf;
+ in = in->next;
+
+#endif
+
+ cl->next = NULL;
+ *ll = cl;
+ ll = &cl->next;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
+{
+ size_t size;
+ ngx_buf_t *in;
+
+ in = ctx->in->buf;
+
+ if (in->file == NULL || !in->file->directio) {
+ return NGX_DECLINED;
+ }
+
+ ctx->directio = 1;
+
+ size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));
+
+ if (size == 0) {
+
+ if (bsize >= (off_t) ctx->bufs.size) {
+ return NGX_DECLINED;
+ }
+
+ size = (size_t) bsize;
+
+ } else {
+ size = (size_t) ctx->alignment - size;
+
+ if ((off_t) size > bsize) {
+ size = (size_t) bsize;
+ }
+ }
+
+ ctx->buf = ngx_create_temp_buf(ctx->pool, size);
+ if (ctx->buf == NULL) {
+ return NGX_ERROR;
+ }
+
+ /*
+ * we do not set ctx->buf->tag, because we do not want
+ * to reuse the buf via ctx->free list
+ */
+
+#if (NGX_HAVE_ALIGNED_DIRECTIO)
+ ctx->unaligned = 1;
+#endif
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
+{
+ size_t size;
+ ngx_buf_t *b, *in;
+ ngx_uint_t recycled;
+
+ in = ctx->in->buf;
+ size = ctx->bufs.size;
+ recycled = 1;
+
+ if (in->last_in_chain) {
+
+ if (bsize < (off_t) size) {
+
+ /*
+ * allocate a small temp buf for a small last buf
+ * or its small last part
+ */
+
+ size = (size_t) bsize;
+ recycled = 0;
+
+ } else if (!ctx->directio
+ && ctx->bufs.num == 1
+ && (bsize < (off_t) (size + size / 4)))
+ {
+ /*
+ * allocate a temp buf that equals to a last buf,
+ * if there is no directio, the last buf size is lesser
+ * than 1.25 of bufs.size and the temp buf is single
+ */
+
+ size = (size_t) bsize;
+ recycled = 0;
+ }
+ }
+
+ b = ngx_calloc_buf(ctx->pool);
+ if (b == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->directio) {
+
+ /*
+ * allocate block aligned to a disk sector size to enable
+ * userland buffer direct usage conjunctly with directio
+ */
+
+ b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);
+ if (b->start == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ b->start = ngx_palloc(ctx->pool, size);
+ if (b->start == NULL) {
+ return NGX_ERROR;
+ }
+ }
+
+ b->pos = b->start;
+ b->last = b->start;
+ b->end = b->last + size;
+ b->temporary = 1;
+ b->tag = ctx->tag;
+ b->recycled = recycled;
+
+ ctx->buf = b;
+ ctx->allocated++;
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
+{
+ off_t size;
+ ssize_t n;
+ ngx_buf_t *src, *dst;
+ ngx_uint_t sendfile;
+
+ src = ctx->in->buf;
+ dst = ctx->buf;
+
+ size = ngx_buf_size(src);
+ size = ngx_min(size, dst->end - dst->pos);
+
+ sendfile = ctx->sendfile & !ctx->directio;
+
+#if (NGX_SENDFILE_LIMIT)
+
+ if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
+ sendfile = 0;
+ }
+
+#endif
+
+ if (ngx_buf_in_memory(src)) {
+ ngx_memcpy(dst->pos, src->pos, (size_t) size);
+ src->pos += (size_t) size;
+ dst->last += (size_t) size;
+
+ if (src->in_file) {
+
+ if (sendfile) {
+ dst->in_file = 1;
+ dst->file = src->file;
+ dst->file_pos = src->file_pos;
+ dst->file_last = src->file_pos + size;
+
+ } else {
+ dst->in_file = 0;
+ }
+
+ src->file_pos += size;
+
+ } else {
+ dst->in_file = 0;
+ }
+
+ if (src->pos == src->last) {
+ dst->flush = src->flush;
+ dst->last_buf = src->last_buf;
+ dst->last_in_chain = src->last_in_chain;
+ }
+
+ } else {
+
+#if (NGX_HAVE_ALIGNED_DIRECTIO)
+
+ if (ctx->unaligned) {
+ if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
+ ngx_directio_off_n " \"%s\" failed",
+ src->file->name.data);
+ }
+ }
+
+#endif
+
+#if (NGX_HAVE_FILE_AIO)
+
+ if (ctx->aio_handler) {
+ n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
+ src->file_pos, ctx->pool);
+ if (n == NGX_AGAIN) {
+ ctx->aio_handler(ctx, src->file);
+ return NGX_AGAIN;
+ }
+
+ } else {
+ n = ngx_read_file(src->file, dst->pos, (size_t) size,
+ src->file_pos);
+ }
+#else
+
+ n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
+
+#endif
+
+#if (NGX_HAVE_ALIGNED_DIRECTIO)
+
+ if (ctx->unaligned) {
+ ngx_err_t err;
+
+ err = ngx_errno;
+
+ if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
+ ngx_directio_on_n " \"%s\" failed",
+ src->file->name.data);
+ }
+
+ ngx_set_errno(err);
+
+ ctx->unaligned = 0;
+ }
+
+#endif
+
+ if (n == NGX_ERROR) {
+ return (ngx_int_t) n;
+ }
+
+ if (n != size) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
+ ngx_read_file_n " read only %z of %O from \"%s\"",
+ n, size, src->file->name.data);
+ return NGX_ERROR;
+ }
+
+ dst->last += n;
+
+ if (sendfile) {
+ dst->in_file = 1;
+ dst->file = src->file;
+ dst->file_pos = src->file_pos;
+ dst->file_last = src->file_pos + n;
+
+ } else {
+ dst->in_file = 0;
+ }
+
+ src->file_pos += n;
+
+ if (src->file_pos == src->file_last) {
+ dst->flush = src->flush;
+ dst->last_buf = src->last_buf;
+ dst->last_in_chain = src->last_in_chain;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_chain_writer(void *data, ngx_chain_t *in)
+{
+ ngx_chain_writer_ctx_t *ctx = data;
+
+ off_t size;
+ ngx_chain_t *cl;
+ ngx_connection_t *c;
+
+ c = ctx->connection;
+
+ for (size = 0; in; in = in->next) {
+
+#if 1
+ if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
+ ngx_debug_point();
+ }
+#endif
+
+ size += ngx_buf_size(in->buf);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "chain writer buf fl:%d s:%uO",
+ in->buf->flush, ngx_buf_size(in->buf));
+
+ cl = ngx_alloc_chain_link(ctx->pool);
+ if (cl == NULL) {
+ return NGX_ERROR;
+ }
+
+ cl->buf = in->buf;
+ cl->next = NULL;
+ *ctx->last = cl;
+ ctx->last = &cl->next;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "chain writer in: %p", ctx->out);
+
+ for (cl = ctx->out; cl; cl = cl->next) {
+
+#if 1
+ if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
+ ngx_debug_point();
+ }
+
+#endif
+
+ size += ngx_buf_size(cl->buf);
+ }
+
+ if (size == 0 && !c->buffered) {
+ return NGX_OK;
+ }
+
+ ctx->out = c->send_chain(c, ctx->out, ctx->limit);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
+ "chain writer out: %p", ctx->out);
+
+ if (ctx->out == NGX_CHAIN_ERROR) {
+ return NGX_ERROR;
+ }
+
+ if (ctx->out == NULL) {
+ ctx->last = &ctx->out;
+
+ if (!c->buffered) {
+ return NGX_OK;
+ }
+ }
+
+ return NGX_AGAIN;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_palloc.c b/usr.sbin/nginx/src/core/ngx_palloc.c
new file mode 100644
index 00000000000..35f13119c87
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_palloc.c
@@ -0,0 +1,432 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static void *ngx_palloc_block(ngx_pool_t *pool, size_t size);
+static void *ngx_palloc_large(ngx_pool_t *pool, size_t size);
+
+
+ngx_pool_t *
+ngx_create_pool(size_t size, ngx_log_t *log)
+{
+ ngx_pool_t *p;
+
+ p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+ p->d.end = (u_char *) p + size;
+ p->d.next = NULL;
+ p->d.failed = 0;
+
+ size = size - sizeof(ngx_pool_t);
+ p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
+
+ p->current = p;
+ p->chain = NULL;
+ p->large = NULL;
+ p->cleanup = NULL;
+ p->log = log;
+
+ return p;
+}
+
+
+void
+ngx_destroy_pool(ngx_pool_t *pool)
+{
+ ngx_pool_t *p, *n;
+ ngx_pool_large_t *l;
+ ngx_pool_cleanup_t *c;
+
+ for (c = pool->cleanup; c; c = c->next) {
+ if (c->handler) {
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
+ "run cleanup: %p", c);
+ c->handler(c->data);
+ }
+ }
+
+ for (l = pool->large; l; l = l->next) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, "free: %p", l->alloc);
+
+ if (l->alloc) {
+ ngx_free(l->alloc);
+ }
+ }
+
+#if (NGX_DEBUG)
+
+ /*
+ * we could allocate the pool->log from this pool
+ * so we can not use this log while the free()ing the pool
+ */
+
+ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
+ ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
+ "free: %p, unused: %uz", p, p->d.end - p->d.last);
+
+ if (n == NULL) {
+ break;
+ }
+ }
+
+#endif
+
+ for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {
+ ngx_free(p);
+
+ if (n == NULL) {
+ break;
+ }
+ }
+}
+
+
+void
+ngx_reset_pool(ngx_pool_t *pool)
+{
+ ngx_pool_t *p;
+ ngx_pool_large_t *l;
+
+ for (l = pool->large; l; l = l->next) {
+ if (l->alloc) {
+ ngx_free(l->alloc);
+ }
+ }
+
+ pool->large = NULL;
+
+ for (p = pool; p; p = p->d.next) {
+ p->d.last = (u_char *) p + sizeof(ngx_pool_t);
+ }
+}
+
+
+void *
+ngx_palloc(ngx_pool_t *pool, size_t size)
+{
+ u_char *m;
+ ngx_pool_t *p;
+
+ if (size <= pool->max) {
+
+ p = pool->current;
+
+ do {
+ m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
+
+ if ((size_t) (p->d.end - m) >= size) {
+ p->d.last = m + size;
+
+ return m;
+ }
+
+ p = p->d.next;
+
+ } while (p);
+
+ return ngx_palloc_block(pool, size);
+ }
+
+ return ngx_palloc_large(pool, size);
+}
+
+
+void *
+ngx_pnalloc(ngx_pool_t *pool, size_t size)
+{
+ u_char *m;
+ ngx_pool_t *p;
+
+ if (size <= pool->max) {
+
+ p = pool->current;
+
+ do {
+ m = p->d.last;
+
+ if ((size_t) (p->d.end - m) >= size) {
+ p->d.last = m + size;
+
+ return m;
+ }
+
+ p = p->d.next;
+
+ } while (p);
+
+ return ngx_palloc_block(pool, size);
+ }
+
+ return ngx_palloc_large(pool, size);
+}
+
+
+static void *
+ngx_palloc_block(ngx_pool_t *pool, size_t size)
+{
+ u_char *m;
+ size_t psize;
+ ngx_pool_t *p, *new, *current;
+
+ psize = (size_t) (pool->d.end - (u_char *) pool);
+
+ m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);
+ if (m == NULL) {
+ return NULL;
+ }
+
+ new = (ngx_pool_t *) m;
+
+ new->d.end = m + psize;
+ new->d.next = NULL;
+ new->d.failed = 0;
+
+ m += sizeof(ngx_pool_data_t);
+ m = ngx_align_ptr(m, NGX_ALIGNMENT);
+ new->d.last = m + size;
+
+ current = pool->current;
+
+ for (p = current; p->d.next; p = p->d.next) {
+ if (p->d.failed++ > 4) {
+ current = p->d.next;
+ }
+ }
+
+ p->d.next = new;
+
+ pool->current = current ? current : new;
+
+ return m;
+}
+
+
+static void *
+ngx_palloc_large(ngx_pool_t *pool, size_t size)
+{
+ void *p;
+ ngx_uint_t n;
+ ngx_pool_large_t *large;
+
+ p = ngx_alloc(size, pool->log);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ n = 0;
+
+ for (large = pool->large; large; large = large->next) {
+ if (large->alloc == NULL) {
+ large->alloc = p;
+ return p;
+ }
+
+ if (n++ > 3) {
+ break;
+ }
+ }
+
+ large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
+ if (large == NULL) {
+ ngx_free(p);
+ return NULL;
+ }
+
+ large->alloc = p;
+ large->next = pool->large;
+ pool->large = large;
+
+ return p;
+}
+
+
+void *
+ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)
+{
+ void *p;
+ ngx_pool_large_t *large;
+
+ p = ngx_memalign(alignment, size, pool->log);
+ if (p == NULL) {
+ return NULL;
+ }
+
+ large = ngx_palloc(pool, sizeof(ngx_pool_large_t));
+ if (large == NULL) {
+ ngx_free(p);
+ return NULL;
+ }
+
+ large->alloc = p;
+ large->next = pool->large;
+ pool->large = large;
+
+ return p;
+}
+
+
+ngx_int_t
+ngx_pfree(ngx_pool_t *pool, void *p)
+{
+ ngx_pool_large_t *l;
+
+ for (l = pool->large; l; l = l->next) {
+ if (p == l->alloc) {
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,
+ "free: %p", l->alloc);
+ ngx_free(l->alloc);
+ l->alloc = NULL;
+
+ return NGX_OK;
+ }
+ }
+
+ return NGX_DECLINED;
+}
+
+
+void *
+ngx_pcalloc(ngx_pool_t *pool, size_t size)
+{
+ void *p;
+
+ p = ngx_palloc(pool, size);
+ if (p) {
+ ngx_memzero(p, size);
+ }
+
+ return p;
+}
+
+
+ngx_pool_cleanup_t *
+ngx_pool_cleanup_add(ngx_pool_t *p, size_t size)
+{
+ ngx_pool_cleanup_t *c;
+
+ c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));
+ if (c == NULL) {
+ return NULL;
+ }
+
+ if (size) {
+ c->data = ngx_palloc(p, size);
+ if (c->data == NULL) {
+ return NULL;
+ }
+
+ } else {
+ c->data = NULL;
+ }
+
+ c->handler = NULL;
+ c->next = p->cleanup;
+
+ p->cleanup = c;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, "add cleanup: %p", c);
+
+ return c;
+}
+
+
+void
+ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd)
+{
+ ngx_pool_cleanup_t *c;
+ ngx_pool_cleanup_file_t *cf;
+
+ for (c = p->cleanup; c; c = c->next) {
+ if (c->handler == ngx_pool_cleanup_file) {
+
+ cf = c->data;
+
+ if (cf->fd == fd) {
+ c->handler(cf);
+ c->handler = NULL;
+ return;
+ }
+ }
+ }
+}
+
+
+void
+ngx_pool_cleanup_file(void *data)
+{
+ ngx_pool_cleanup_file_t *c = data;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d",
+ c->fd);
+
+ if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", c->name);
+ }
+}
+
+
+void
+ngx_pool_delete_file(void *data)
+{
+ ngx_pool_cleanup_file_t *c = data;
+
+ ngx_err_t err;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, "file cleanup: fd:%d %s",
+ c->fd, c->name);
+
+ if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {
+ err = ngx_errno;
+
+ if (err != NGX_ENOENT) {
+ ngx_log_error(NGX_LOG_CRIT, c->log, err,
+ ngx_delete_file_n " \"%s\" failed", c->name);
+ }
+ }
+
+ if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", c->name);
+ }
+}
+
+
+#if 0
+
+static void *
+ngx_get_cached_block(size_t size)
+{
+ void *p;
+ ngx_cached_block_slot_t *slot;
+
+ if (ngx_cycle->cache == NULL) {
+ return NULL;
+ }
+
+ slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];
+
+ slot->tries++;
+
+ if (slot->number) {
+ p = slot->block;
+ slot->block = slot->block->next;
+ slot->number--;
+ return p;
+ }
+
+ return NULL;
+}
+
+#endif
diff --git a/usr.sbin/nginx/src/core/ngx_palloc.h b/usr.sbin/nginx/src/core/ngx_palloc.h
new file mode 100644
index 00000000000..6e6612cca7e
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_palloc.h
@@ -0,0 +1,94 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_PALLOC_H_INCLUDED_
+#define _NGX_PALLOC_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.
+ * On Windows NT it decreases a number of locked pages in a kernel.
+ */
+#define NGX_MAX_ALLOC_FROM_POOL (ngx_pagesize - 1)
+
+#define NGX_DEFAULT_POOL_SIZE (16 * 1024)
+
+#define NGX_POOL_ALIGNMENT 16
+#define NGX_MIN_POOL_SIZE \
+ ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)), \
+ NGX_POOL_ALIGNMENT)
+
+
+typedef void (*ngx_pool_cleanup_pt)(void *data);
+
+typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
+
+struct ngx_pool_cleanup_s {
+ ngx_pool_cleanup_pt handler;
+ void *data;
+ ngx_pool_cleanup_t *next;
+};
+
+
+typedef struct ngx_pool_large_s ngx_pool_large_t;
+
+struct ngx_pool_large_s {
+ ngx_pool_large_t *next;
+ void *alloc;
+};
+
+
+typedef struct {
+ u_char *last;
+ u_char *end;
+ ngx_pool_t *next;
+ ngx_uint_t failed;
+} ngx_pool_data_t;
+
+
+struct ngx_pool_s {
+ ngx_pool_data_t d;
+ size_t max;
+ ngx_pool_t *current;
+ ngx_chain_t *chain;
+ ngx_pool_large_t *large;
+ ngx_pool_cleanup_t *cleanup;
+ ngx_log_t *log;
+};
+
+
+typedef struct {
+ ngx_fd_t fd;
+ u_char *name;
+ ngx_log_t *log;
+} ngx_pool_cleanup_file_t;
+
+
+void *ngx_alloc(size_t size, ngx_log_t *log);
+void *ngx_calloc(size_t size, ngx_log_t *log);
+
+ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
+void ngx_destroy_pool(ngx_pool_t *pool);
+void ngx_reset_pool(ngx_pool_t *pool);
+
+void *ngx_palloc(ngx_pool_t *pool, size_t size);
+void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
+void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
+void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
+ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
+
+
+ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
+void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
+void ngx_pool_cleanup_file(void *data);
+void ngx_pool_delete_file(void *data);
+
+
+#endif /* _NGX_PALLOC_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_parse.c b/usr.sbin/nginx/src/core/ngx_parse.c
new file mode 100644
index 00000000000..6c4640cf7c4
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_parse.c
@@ -0,0 +1,248 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+ssize_t
+ngx_parse_size(ngx_str_t *line)
+{
+ u_char unit;
+ size_t len;
+ ssize_t size;
+ ngx_int_t scale;
+
+ len = line->len;
+ unit = line->data[len - 1];
+
+ switch (unit) {
+ case 'K':
+ case 'k':
+ len--;
+ scale = 1024;
+ break;
+
+ case 'M':
+ case 'm':
+ len--;
+ scale = 1024 * 1024;
+ break;
+
+ default:
+ scale = 1;
+ }
+
+ size = ngx_atosz(line->data, len);
+ if (size == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ size *= scale;
+
+ return size;
+}
+
+
+off_t
+ngx_parse_offset(ngx_str_t *line)
+{
+ u_char unit;
+ off_t offset;
+ size_t len;
+ ngx_int_t scale;
+
+ len = line->len;
+ unit = line->data[len - 1];
+
+ switch (unit) {
+ case 'K':
+ case 'k':
+ len--;
+ scale = 1024;
+ break;
+
+ case 'M':
+ case 'm':
+ len--;
+ scale = 1024 * 1024;
+ break;
+
+ case 'G':
+ case 'g':
+ len--;
+ scale = 1024 * 1024 * 1024;
+ break;
+
+ default:
+ scale = 1;
+ }
+
+ offset = ngx_atoof(line->data, len);
+ if (offset == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+
+ offset *= scale;
+
+ return offset;
+}
+
+
+ngx_int_t
+ngx_parse_time(ngx_str_t *line, ngx_uint_t sec)
+{
+ u_char *p, *last;
+ ngx_int_t value, total, scale;
+ ngx_uint_t max, valid;
+ enum {
+ st_start = 0,
+ st_year,
+ st_month,
+ st_week,
+ st_day,
+ st_hour,
+ st_min,
+ st_sec,
+ st_msec,
+ st_last
+ } step;
+
+ valid = 0;
+ value = 0;
+ total = 0;
+ step = sec ? st_start : st_month;
+ scale = sec ? 1 : 1000;
+
+ p = line->data;
+ last = p + line->len;
+
+ while (p < last) {
+
+ if (*p >= '0' && *p <= '9') {
+ value = value * 10 + (*p++ - '0');
+ valid = 1;
+ continue;
+ }
+
+ switch (*p++) {
+
+ case 'y':
+ if (step > st_start) {
+ return NGX_ERROR;
+ }
+ step = st_year;
+ max = 68;
+ scale = 60 * 60 * 24 * 365;
+ break;
+
+ case 'M':
+ if (step > st_year) {
+ return NGX_ERROR;
+ }
+ step = st_month;
+ max = 828;
+ scale = 60 * 60 * 24 * 30;
+ break;
+
+ case 'w':
+ if (step > st_month) {
+ return NGX_ERROR;
+ }
+ step = st_week;
+ max = 3550;
+ scale = 60 * 60 * 24 * 7;
+ break;
+
+ case 'd':
+ if (step > st_week) {
+ return NGX_ERROR;
+ }
+ step = st_day;
+ max = 24855;
+ scale = 60 * 60 * 24;
+ break;
+
+ case 'h':
+ if (step > st_day) {
+ return NGX_ERROR;
+ }
+ step = st_hour;
+ max = 596523;
+ scale = 60 * 60;
+ break;
+
+ case 'm':
+ if (*p == 's') {
+ if (sec || step > st_sec) {
+ return NGX_ERROR;
+ }
+ p++;
+ step = st_msec;
+ max = 2147483647;
+ scale = 1;
+ break;
+ }
+
+ if (step > st_hour) {
+ return NGX_ERROR;
+ }
+ step = st_min;
+ max = 35791394;
+ scale = 60;
+ break;
+
+ case 's':
+ if (step > st_min) {
+ return NGX_ERROR;
+ }
+ step = st_sec;
+ max = 2147483647;
+ scale = 1;
+ break;
+
+ case ' ':
+ if (step > st_min) {
+ return NGX_ERROR;
+ }
+ step = st_last;
+ max = 2147483647;
+ scale = 1;
+ break;
+
+ default:
+ return NGX_ERROR;
+ }
+
+ if (step != st_msec && !sec) {
+ scale *= 1000;
+ max /= 1000;
+ }
+
+ if ((ngx_uint_t) value > max) {
+ return NGX_ERROR;
+ }
+
+ total += value * scale;
+
+ if ((ngx_uint_t) total > 2147483647) {
+ return NGX_ERROR;
+ }
+
+ value = 0;
+ scale = sec ? 1 : 1000;
+
+ while (p < last && *p == ' ') {
+ p++;
+ }
+ }
+
+ if (valid) {
+ return total + value * scale;
+ }
+
+ return NGX_ERROR;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_parse.h b/usr.sbin/nginx/src/core/ngx_parse.h
new file mode 100644
index 00000000000..c25a3a02348
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_parse.h
@@ -0,0 +1,23 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_PARSE_H_INCLUDED_
+#define _NGX_PARSE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_PARSE_LARGE_TIME -2
+
+
+ssize_t ngx_parse_size(ngx_str_t *line);
+off_t ngx_parse_offset(ngx_str_t *line);
+ngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t sec);
+
+
+#endif /* _NGX_PARSE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_queue.c b/usr.sbin/nginx/src/core/ngx_queue.c
new file mode 100644
index 00000000000..a49b5c6638a
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_queue.c
@@ -0,0 +1,79 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * find the middle queue element if the queue has odd number of elements
+ * or the first element of the queue's second part otherwise
+ */
+
+ngx_queue_t *
+ngx_queue_middle(ngx_queue_t *queue)
+{
+ ngx_queue_t *middle, *next;
+
+ middle = ngx_queue_head(queue);
+
+ if (middle == ngx_queue_last(queue)) {
+ return middle;
+ }
+
+ next = ngx_queue_head(queue);
+
+ for ( ;; ) {
+ middle = ngx_queue_next(middle);
+
+ next = ngx_queue_next(next);
+
+ if (next == ngx_queue_last(queue)) {
+ return middle;
+ }
+
+ next = ngx_queue_next(next);
+
+ if (next == ngx_queue_last(queue)) {
+ return middle;
+ }
+ }
+}
+
+
+/* the stable insertion sort */
+
+void
+ngx_queue_sort(ngx_queue_t *queue,
+ ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))
+{
+ ngx_queue_t *q, *prev, *next;
+
+ q = ngx_queue_head(queue);
+
+ if (q == ngx_queue_last(queue)) {
+ return;
+ }
+
+ for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {
+
+ prev = ngx_queue_prev(q);
+ next = ngx_queue_next(q);
+
+ ngx_queue_remove(q);
+
+ do {
+ if (cmp(prev, q) <= 0) {
+ break;
+ }
+
+ prev = ngx_queue_prev(prev);
+
+ } while (prev != ngx_queue_sentinel(queue));
+
+ ngx_queue_insert_after(prev, q);
+ }
+}
diff --git a/usr.sbin/nginx/src/core/ngx_queue.h b/usr.sbin/nginx/src/core/ngx_queue.h
new file mode 100644
index 00000000000..9a1763d7b6c
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_queue.h
@@ -0,0 +1,111 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_QUEUE_H_INCLUDED_
+#define _NGX_QUEUE_H_INCLUDED_
+
+
+typedef struct ngx_queue_s ngx_queue_t;
+
+struct ngx_queue_s {
+ ngx_queue_t *prev;
+ ngx_queue_t *next;
+};
+
+
+#define ngx_queue_init(q) \
+ (q)->prev = q; \
+ (q)->next = q
+
+
+#define ngx_queue_empty(h) \
+ (h == (h)->prev)
+
+
+#define ngx_queue_insert_head(h, x) \
+ (x)->next = (h)->next; \
+ (x)->next->prev = x; \
+ (x)->prev = h; \
+ (h)->next = x
+
+
+#define ngx_queue_insert_after ngx_queue_insert_head
+
+
+#define ngx_queue_insert_tail(h, x) \
+ (x)->prev = (h)->prev; \
+ (x)->prev->next = x; \
+ (x)->next = h; \
+ (h)->prev = x
+
+
+#define ngx_queue_head(h) \
+ (h)->next
+
+
+#define ngx_queue_last(h) \
+ (h)->prev
+
+
+#define ngx_queue_sentinel(h) \
+ (h)
+
+
+#define ngx_queue_next(q) \
+ (q)->next
+
+
+#define ngx_queue_prev(q) \
+ (q)->prev
+
+
+#if (NGX_DEBUG)
+
+#define ngx_queue_remove(x) \
+ (x)->next->prev = (x)->prev; \
+ (x)->prev->next = (x)->next; \
+ (x)->prev = NULL; \
+ (x)->next = NULL
+
+#else
+
+#define ngx_queue_remove(x) \
+ (x)->next->prev = (x)->prev; \
+ (x)->prev->next = (x)->next
+
+#endif
+
+
+#define ngx_queue_split(h, q, n) \
+ (n)->prev = (h)->prev; \
+ (n)->prev->next = n; \
+ (n)->next = q; \
+ (h)->prev = (q)->prev; \
+ (h)->prev->next = h; \
+ (q)->prev = n;
+
+
+#define ngx_queue_add(h, n) \
+ (h)->prev->next = (n)->next; \
+ (n)->next->prev = (h)->prev; \
+ (h)->prev = (n)->prev; \
+ (h)->prev->next = h;
+
+
+#define ngx_queue_data(q, type, link) \
+ (type *) ((u_char *) q - offsetof(type, link))
+
+
+ngx_queue_t *ngx_queue_middle(ngx_queue_t *queue);
+void ngx_queue_sort(ngx_queue_t *queue,
+ ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));
+
+
+#endif /* _NGX_QUEUE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_radix_tree.c b/usr.sbin/nginx/src/core/ngx_radix_tree.c
new file mode 100644
index 00000000000..f339e6fab76
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_radix_tree.c
@@ -0,0 +1,290 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static void *ngx_radix_alloc(ngx_radix_tree_t *tree);
+
+
+ngx_radix_tree_t *
+ngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)
+{
+ uint32_t key, mask, inc;
+ ngx_radix_tree_t *tree;
+
+ tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ tree->pool = pool;
+ tree->free = NULL;
+ tree->start = NULL;
+ tree->size = 0;
+
+ tree->root = ngx_radix_alloc(tree);
+ if (tree->root == NULL) {
+ return NULL;
+ }
+
+ tree->root->right = NULL;
+ tree->root->left = NULL;
+ tree->root->parent = NULL;
+ tree->root->value = NGX_RADIX_NO_VALUE;
+
+ if (preallocate == 0) {
+ return tree;
+ }
+
+ /*
+ * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.
+ * increases TLB hits even if for first lookup iterations.
+ * On 32-bit platforms the 7 preallocated bits takes continuous 4K,
+ * 8 - 8K, 9 - 16K, etc. On 64-bit platforms the 6 preallocated bits
+ * takes continuous 4K, 7 - 8K, 8 - 16K, etc. There is no sense to
+ * to preallocate more than one page, because further preallocation
+ * distributes the only bit per page. Instead, a random insertion
+ * may distribute several bits per page.
+ *
+ * Thus, by default we preallocate maximum
+ * 6 bits on amd64 (64-bit platform and 4K pages)
+ * 7 bits on i386 (32-bit platform and 4K pages)
+ * 7 bits on sparc64 in 64-bit mode (8K pages)
+ * 8 bits on sparc64 in 32-bit mode (8K pages)
+ */
+
+ if (preallocate == -1) {
+ switch (ngx_pagesize / sizeof(ngx_radix_tree_t)) {
+
+ /* amd64 */
+ case 128:
+ preallocate = 6;
+ break;
+
+ /* i386, sparc64 */
+ case 256:
+ preallocate = 7;
+ break;
+
+ /* sparc64 in 32-bit mode */
+ default:
+ preallocate = 8;
+ }
+ }
+
+ mask = 0;
+ inc = 0x80000000;
+
+ while (preallocate--) {
+
+ key = 0;
+ mask >>= 1;
+ mask |= 0x80000000;
+
+ do {
+ if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ key += inc;
+
+ } while (key);
+
+ inc >>= 1;
+ }
+
+ return tree;
+}
+
+
+ngx_int_t
+ngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask,
+ uintptr_t value)
+{
+ uint32_t bit;
+ ngx_radix_node_t *node, *next;
+
+ bit = 0x80000000;
+
+ node = tree->root;
+ next = tree->root;
+
+ while (bit & mask) {
+ if (key & bit) {
+ next = node->right;
+
+ } else {
+ next = node->left;
+ }
+
+ if (next == NULL) {
+ break;
+ }
+
+ bit >>= 1;
+ node = next;
+ }
+
+ if (next) {
+ if (node->value != NGX_RADIX_NO_VALUE) {
+ return NGX_BUSY;
+ }
+
+ node->value = value;
+ return NGX_OK;
+ }
+
+ while (bit & mask) {
+ next = ngx_radix_alloc(tree);
+ if (next == NULL) {
+ return NGX_ERROR;
+ }
+
+ next->right = NULL;
+ next->left = NULL;
+ next->parent = node;
+ next->value = NGX_RADIX_NO_VALUE;
+
+ if (key & bit) {
+ node->right = next;
+
+ } else {
+ node->left = next;
+ }
+
+ bit >>= 1;
+ node = next;
+ }
+
+ node->value = value;
+
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)
+{
+ uint32_t bit;
+ ngx_radix_node_t *node;
+
+ bit = 0x80000000;
+ node = tree->root;
+
+ while (node && (bit & mask)) {
+ if (key & bit) {
+ node = node->right;
+
+ } else {
+ node = node->left;
+ }
+
+ bit >>= 1;
+ }
+
+ if (node == NULL) {
+ return NGX_ERROR;
+ }
+
+ if (node->right || node->left) {
+ if (node->value != NGX_RADIX_NO_VALUE) {
+ node->value = NGX_RADIX_NO_VALUE;
+ return NGX_OK;
+ }
+
+ return NGX_ERROR;
+ }
+
+ for ( ;; ) {
+ if (node->parent->right == node) {
+ node->parent->right = NULL;
+
+ } else {
+ node->parent->left = NULL;
+ }
+
+ node->right = tree->free;
+ tree->free = node;
+
+ node = node->parent;
+
+ if (node->right || node->left) {
+ break;
+ }
+
+ if (node->value != NGX_RADIX_NO_VALUE) {
+ break;
+ }
+
+ if (node->parent == NULL) {
+ break;
+ }
+ }
+
+ return NGX_OK;
+}
+
+
+uintptr_t
+ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)
+{
+ uint32_t bit;
+ uintptr_t value;
+ ngx_radix_node_t *node;
+
+ bit = 0x80000000;
+ value = NGX_RADIX_NO_VALUE;
+ node = tree->root;
+
+ while (node) {
+ if (node->value != NGX_RADIX_NO_VALUE) {
+ value = node->value;
+ }
+
+ if (key & bit) {
+ node = node->right;
+
+ } else {
+ node = node->left;
+ }
+
+ bit >>= 1;
+ }
+
+ return value;
+}
+
+
+static void *
+ngx_radix_alloc(ngx_radix_tree_t *tree)
+{
+ char *p;
+
+ if (tree->free) {
+ p = (char *) tree->free;
+ tree->free = tree->free->right;
+ return p;
+ }
+
+ if (tree->size < sizeof(ngx_radix_node_t)) {
+ tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);
+ if (tree->start == NULL) {
+ return NULL;
+ }
+
+ tree->size = ngx_pagesize;
+ }
+
+ p = tree->start;
+ tree->start += sizeof(ngx_radix_node_t);
+ tree->size -= sizeof(ngx_radix_node_t);
+
+ return p;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_radix_tree.h b/usr.sbin/nginx/src/core/ngx_radix_tree.h
new file mode 100644
index 00000000000..02a9f383b7e
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_radix_tree.h
@@ -0,0 +1,45 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_RADIX_TREE_H_INCLUDED_
+#define _NGX_RADIX_TREE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_RADIX_NO_VALUE (uintptr_t) -1
+
+typedef struct ngx_radix_node_s ngx_radix_node_t;
+
+struct ngx_radix_node_s {
+ ngx_radix_node_t *right;
+ ngx_radix_node_t *left;
+ ngx_radix_node_t *parent;
+ uintptr_t value;
+};
+
+
+typedef struct {
+ ngx_radix_node_t *root;
+ ngx_pool_t *pool;
+ ngx_radix_node_t *free;
+ char *start;
+ size_t size;
+} ngx_radix_tree_t;
+
+
+ngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool,
+ ngx_int_t preallocate);
+ngx_int_t ngx_radix32tree_insert(ngx_radix_tree_t *tree,
+ uint32_t key, uint32_t mask, uintptr_t value);
+ngx_int_t ngx_radix32tree_delete(ngx_radix_tree_t *tree,
+ uint32_t key, uint32_t mask);
+uintptr_t ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key);
+
+
+#endif /* _NGX_RADIX_TREE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_rbtree.c b/usr.sbin/nginx/src/core/ngx_rbtree.c
new file mode 100644
index 00000000000..749b601f2b2
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_rbtree.c
@@ -0,0 +1,382 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * The red-black tree code is based on the algorithm described in
+ * the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
+ */
+
+
+static ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_node_t **root,
+ ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);
+static ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_node_t **root,
+ ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);
+
+
+void
+ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,
+ ngx_rbtree_node_t *node)
+{
+ ngx_rbtree_node_t **root, *temp, *sentinel;
+
+ /* a binary tree insert */
+
+ root = (ngx_rbtree_node_t **) &tree->root;
+ sentinel = tree->sentinel;
+
+ if (*root == sentinel) {
+ node->parent = NULL;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_black(node);
+ *root = node;
+
+ return;
+ }
+
+ tree->insert(*root, node, sentinel);
+
+ /* re-balance tree */
+
+ while (node != *root && ngx_rbt_is_red(node->parent)) {
+
+ if (node->parent == node->parent->parent->left) {
+ temp = node->parent->parent->right;
+
+ if (ngx_rbt_is_red(temp)) {
+ ngx_rbt_black(node->parent);
+ ngx_rbt_black(temp);
+ ngx_rbt_red(node->parent->parent);
+ node = node->parent->parent;
+
+ } else {
+ if (node == node->parent->right) {
+ node = node->parent;
+ ngx_rbtree_left_rotate(root, sentinel, node);
+ }
+
+ ngx_rbt_black(node->parent);
+ ngx_rbt_red(node->parent->parent);
+ ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
+ }
+
+ } else {
+ temp = node->parent->parent->left;
+
+ if (ngx_rbt_is_red(temp)) {
+ ngx_rbt_black(node->parent);
+ ngx_rbt_black(temp);
+ ngx_rbt_red(node->parent->parent);
+ node = node->parent->parent;
+
+ } else {
+ if (node == node->parent->left) {
+ node = node->parent;
+ ngx_rbtree_right_rotate(root, sentinel, node);
+ }
+
+ ngx_rbt_black(node->parent);
+ ngx_rbt_red(node->parent->parent);
+ ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
+ }
+ }
+ }
+
+ ngx_rbt_black(*root);
+}
+
+
+void
+ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
+ ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+
+ for ( ;; ) {
+
+ p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+void
+ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
+ ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+
+ for ( ;; ) {
+
+ /*
+ * Timer values
+ * 1) are spread in small range, usually several minutes,
+ * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.
+ * The comparison takes into account that overflow.
+ */
+
+ /* node->key < temp->key */
+
+ p = ((ngx_rbtree_key_int_t) node->key - (ngx_rbtree_key_int_t) temp->key
+ < 0)
+ ? &temp->left : &temp->right;
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+void
+ngx_rbtree_delete(ngx_thread_volatile ngx_rbtree_t *tree,
+ ngx_rbtree_node_t *node)
+{
+ ngx_uint_t red;
+ ngx_rbtree_node_t **root, *sentinel, *subst, *temp, *w;
+
+ /* a binary tree delete */
+
+ root = (ngx_rbtree_node_t **) &tree->root;
+ sentinel = tree->sentinel;
+
+ if (node->left == sentinel) {
+ temp = node->right;
+ subst = node;
+
+ } else if (node->right == sentinel) {
+ temp = node->left;
+ subst = node;
+
+ } else {
+ subst = ngx_rbtree_min(node->right, sentinel);
+
+ if (subst->left != sentinel) {
+ temp = subst->left;
+ } else {
+ temp = subst->right;
+ }
+ }
+
+ if (subst == *root) {
+ *root = temp;
+ ngx_rbt_black(temp);
+
+ /* DEBUG stuff */
+ node->left = NULL;
+ node->right = NULL;
+ node->parent = NULL;
+ node->key = 0;
+
+ return;
+ }
+
+ red = ngx_rbt_is_red(subst);
+
+ if (subst == subst->parent->left) {
+ subst->parent->left = temp;
+
+ } else {
+ subst->parent->right = temp;
+ }
+
+ if (subst == node) {
+
+ temp->parent = subst->parent;
+
+ } else {
+
+ if (subst->parent == node) {
+ temp->parent = subst;
+
+ } else {
+ temp->parent = subst->parent;
+ }
+
+ subst->left = node->left;
+ subst->right = node->right;
+ subst->parent = node->parent;
+ ngx_rbt_copy_color(subst, node);
+
+ if (node == *root) {
+ *root = subst;
+
+ } else {
+ if (node == node->parent->left) {
+ node->parent->left = subst;
+ } else {
+ node->parent->right = subst;
+ }
+ }
+
+ if (subst->left != sentinel) {
+ subst->left->parent = subst;
+ }
+
+ if (subst->right != sentinel) {
+ subst->right->parent = subst;
+ }
+ }
+
+ /* DEBUG stuff */
+ node->left = NULL;
+ node->right = NULL;
+ node->parent = NULL;
+ node->key = 0;
+
+ if (red) {
+ return;
+ }
+
+ /* a delete fixup */
+
+ while (temp != *root && ngx_rbt_is_black(temp)) {
+
+ if (temp == temp->parent->left) {
+ w = temp->parent->right;
+
+ if (ngx_rbt_is_red(w)) {
+ ngx_rbt_black(w);
+ ngx_rbt_red(temp->parent);
+ ngx_rbtree_left_rotate(root, sentinel, temp->parent);
+ w = temp->parent->right;
+ }
+
+ if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
+ ngx_rbt_red(w);
+ temp = temp->parent;
+
+ } else {
+ if (ngx_rbt_is_black(w->right)) {
+ ngx_rbt_black(w->left);
+ ngx_rbt_red(w);
+ ngx_rbtree_right_rotate(root, sentinel, w);
+ w = temp->parent->right;
+ }
+
+ ngx_rbt_copy_color(w, temp->parent);
+ ngx_rbt_black(temp->parent);
+ ngx_rbt_black(w->right);
+ ngx_rbtree_left_rotate(root, sentinel, temp->parent);
+ temp = *root;
+ }
+
+ } else {
+ w = temp->parent->left;
+
+ if (ngx_rbt_is_red(w)) {
+ ngx_rbt_black(w);
+ ngx_rbt_red(temp->parent);
+ ngx_rbtree_right_rotate(root, sentinel, temp->parent);
+ w = temp->parent->left;
+ }
+
+ if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
+ ngx_rbt_red(w);
+ temp = temp->parent;
+
+ } else {
+ if (ngx_rbt_is_black(w->left)) {
+ ngx_rbt_black(w->right);
+ ngx_rbt_red(w);
+ ngx_rbtree_left_rotate(root, sentinel, w);
+ w = temp->parent->left;
+ }
+
+ ngx_rbt_copy_color(w, temp->parent);
+ ngx_rbt_black(temp->parent);
+ ngx_rbt_black(w->left);
+ ngx_rbtree_right_rotate(root, sentinel, temp->parent);
+ temp = *root;
+ }
+ }
+ }
+
+ ngx_rbt_black(temp);
+}
+
+
+static ngx_inline void
+ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
+ ngx_rbtree_node_t *node)
+{
+ ngx_rbtree_node_t *temp;
+
+ temp = node->right;
+ node->right = temp->left;
+
+ if (temp->left != sentinel) {
+ temp->left->parent = node;
+ }
+
+ temp->parent = node->parent;
+
+ if (node == *root) {
+ *root = temp;
+
+ } else if (node == node->parent->left) {
+ node->parent->left = temp;
+
+ } else {
+ node->parent->right = temp;
+ }
+
+ temp->left = node;
+ node->parent = temp;
+}
+
+
+static ngx_inline void
+ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
+ ngx_rbtree_node_t *node)
+{
+ ngx_rbtree_node_t *temp;
+
+ temp = node->left;
+ node->left = temp->right;
+
+ if (temp->right != sentinel) {
+ temp->right->parent = node;
+ }
+
+ temp->parent = node->parent;
+
+ if (node == *root) {
+ *root = temp;
+
+ } else if (node == node->parent->right) {
+ node->parent->right = temp;
+
+ } else {
+ node->parent->left = temp;
+ }
+
+ temp->right = node;
+ node->parent = temp;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_rbtree.h b/usr.sbin/nginx/src/core/ngx_rbtree.h
new file mode 100644
index 00000000000..8531afcea71
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_rbtree.h
@@ -0,0 +1,83 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_RBTREE_H_INCLUDED_
+#define _NGX_RBTREE_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef ngx_uint_t ngx_rbtree_key_t;
+typedef ngx_int_t ngx_rbtree_key_int_t;
+
+
+typedef struct ngx_rbtree_node_s ngx_rbtree_node_t;
+
+struct ngx_rbtree_node_s {
+ ngx_rbtree_key_t key;
+ ngx_rbtree_node_t *left;
+ ngx_rbtree_node_t *right;
+ ngx_rbtree_node_t *parent;
+ u_char color;
+ u_char data;
+};
+
+
+typedef struct ngx_rbtree_s ngx_rbtree_t;
+
+typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+struct ngx_rbtree_s {
+ ngx_rbtree_node_t *root;
+ ngx_rbtree_node_t *sentinel;
+ ngx_rbtree_insert_pt insert;
+};
+
+
+#define ngx_rbtree_init(tree, s, i) \
+ ngx_rbtree_sentinel_init(s); \
+ (tree)->root = s; \
+ (tree)->sentinel = s; \
+ (tree)->insert = i
+
+
+void ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,
+ ngx_rbtree_node_t *node);
+void ngx_rbtree_delete(ngx_thread_volatile ngx_rbtree_t *tree,
+ ngx_rbtree_node_t *node);
+void ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node,
+ ngx_rbtree_node_t *sentinel);
+void ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+
+
+#define ngx_rbt_red(node) ((node)->color = 1)
+#define ngx_rbt_black(node) ((node)->color = 0)
+#define ngx_rbt_is_red(node) ((node)->color)
+#define ngx_rbt_is_black(node) (!ngx_rbt_is_red(node))
+#define ngx_rbt_copy_color(n1, n2) (n1->color = n2->color)
+
+
+/* a sentinel must be black */
+
+#define ngx_rbtree_sentinel_init(node) ngx_rbt_black(node)
+
+
+static ngx_inline ngx_rbtree_node_t *
+ngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ while (node->left != sentinel) {
+ node = node->left;
+ }
+
+ return node;
+}
+
+
+#endif /* _NGX_RBTREE_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_regex.c b/usr.sbin/nginx/src/core/ngx_regex.c
new file mode 100644
index 00000000000..984a28aa983
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_regex.c
@@ -0,0 +1,222 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static void * ngx_libc_cdecl ngx_regex_malloc(size_t size);
+static void ngx_libc_cdecl ngx_regex_free(void *p);
+
+
+static ngx_pool_t *ngx_pcre_pool;
+
+
+void
+ngx_regex_init(void)
+{
+ pcre_malloc = ngx_regex_malloc;
+ pcre_free = ngx_regex_free;
+}
+
+
+static ngx_inline void
+ngx_regex_malloc_init(ngx_pool_t *pool)
+{
+#if (NGX_THREADS)
+ ngx_core_tls_t *tls;
+
+ if (ngx_threaded) {
+ tls = ngx_thread_get_tls(ngx_core_tls_key);
+ tls->pool = pool;
+ return;
+ }
+
+#endif
+
+ ngx_pcre_pool = pool;
+}
+
+
+static ngx_inline void
+ngx_regex_malloc_done(void)
+{
+#if (NGX_THREADS)
+ ngx_core_tls_t *tls;
+
+ if (ngx_threaded) {
+ tls = ngx_thread_get_tls(ngx_core_tls_key);
+ tls->pool = NULL;
+ return;
+ }
+
+#endif
+
+ ngx_pcre_pool = NULL;
+}
+
+
+ngx_int_t
+ngx_regex_compile(ngx_regex_compile_t *rc)
+{
+ int n, erroff;
+ char *p;
+ const char *errstr;
+ ngx_regex_t *re;
+
+ ngx_regex_malloc_init(rc->pool);
+
+ re = pcre_compile((const char *) rc->pattern.data, (int) rc->options,
+ &errstr, &erroff, NULL);
+
+ /* ensure that there is no current pool */
+ ngx_regex_malloc_done();
+
+ if (re == NULL) {
+ if ((size_t) erroff == rc->pattern.len) {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre_compile() failed: %s in \"%V\"",
+ errstr, &rc->pattern)
+ - rc->err.data;
+
+ } else {
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,
+ "pcre_compile() failed: %s in \"%V\" at \"%s\"",
+ errstr, &rc->pattern, rc->pattern.data + erroff)
+ - rc->err.data;
+ }
+
+ return NGX_ERROR;
+ }
+
+ rc->regex = re;
+
+ n = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &rc->captures);
+ if (n < 0) {
+ p = "pcre_fullinfo(\"%V\", PCRE_INFO_CAPTURECOUNT) failed: %d";
+ goto failed;
+ }
+
+ if (rc->captures == 0) {
+ return NGX_OK;
+ }
+
+ n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);
+ if (n < 0) {
+ p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMECOUNT) failed: %d";
+ goto failed;
+ }
+
+ if (rc->named_captures == 0) {
+ return NGX_OK;
+ }
+
+ n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);
+ if (n < 0) {
+ p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMEENTRYSIZE) failed: %d";
+ goto failed;
+ }
+
+ n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);
+ if (n < 0) {
+ p = "pcre_fullinfo(\"%V\", PCRE_INFO_NAMETABLE) failed: %d";
+ goto failed;
+ }
+
+ return NGX_OK;
+
+failed:
+
+ rc->err.len = ngx_snprintf(rc->err.data, rc->err.len, p, &rc->pattern, n)
+ - rc->err.data;
+ return NGX_OK;
+}
+
+
+ngx_int_t
+ngx_regex_capture_count(ngx_regex_t *re)
+{
+ int rc, n;
+
+ n = 0;
+
+ rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &n);
+
+ if (rc < 0) {
+ return (ngx_int_t) rc;
+ }
+
+ return (ngx_int_t) n;
+}
+
+
+ngx_int_t
+ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)
+{
+ ngx_int_t n;
+ ngx_uint_t i;
+ ngx_regex_elt_t *re;
+
+ re = a->elts;
+
+ for (i = 0; i < a->nelts; i++) {
+
+ n = ngx_regex_exec(re[i].regex, s, NULL, 0);
+
+ if (n == NGX_REGEX_NO_MATCHED) {
+ continue;
+ }
+
+ if (n < 0) {
+ ngx_log_error(NGX_LOG_ALERT, log, 0,
+ ngx_regex_exec_n " failed: %i on \"%V\" using \"%s\"",
+ n, s, re[i].name);
+ return NGX_ERROR;
+ }
+
+ /* match */
+
+ return NGX_OK;
+ }
+
+ return NGX_DECLINED;
+}
+
+
+static void * ngx_libc_cdecl
+ngx_regex_malloc(size_t size)
+{
+ ngx_pool_t *pool;
+#if (NGX_THREADS)
+ ngx_core_tls_t *tls;
+
+ if (ngx_threaded) {
+ tls = ngx_thread_get_tls(ngx_core_tls_key);
+ pool = tls->pool;
+
+ } else {
+ pool = ngx_pcre_pool;
+ }
+
+#else
+
+ pool = ngx_pcre_pool;
+
+#endif
+
+ if (pool) {
+ return ngx_palloc(pool, size);
+ }
+
+ return NULL;
+}
+
+
+static void ngx_libc_cdecl
+ngx_regex_free(void *p)
+{
+ return;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_regex.h b/usr.sbin/nginx/src/core/ngx_regex.h
new file mode 100644
index 00000000000..fc35059b3f6
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_regex.h
@@ -0,0 +1,55 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_REGEX_H_INCLUDED_
+#define _NGX_REGEX_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+#include <pcre.h>
+
+
+#define NGX_REGEX_NO_MATCHED PCRE_ERROR_NOMATCH /* -1 */
+
+#define NGX_REGEX_CASELESS PCRE_CASELESS
+
+typedef pcre ngx_regex_t;
+
+
+typedef struct {
+ ngx_str_t pattern;
+ ngx_pool_t *pool;
+ ngx_int_t options;
+
+ ngx_regex_t *regex;
+ int captures;
+ int named_captures;
+ int name_size;
+ u_char *names;
+ ngx_str_t err;
+} ngx_regex_compile_t;
+
+
+typedef struct {
+ ngx_regex_t *regex;
+ u_char *name;
+} ngx_regex_elt_t;
+
+
+void ngx_regex_init(void);
+ngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc);
+
+#define ngx_regex_exec(re, s, captures, size) \
+ pcre_exec(re, NULL, (const char *) (s)->data, (s)->len, 0, 0, \
+ captures, size)
+#define ngx_regex_exec_n "pcre_exec()"
+
+ngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);
+
+
+#endif /* _NGX_REGEX_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_resolver.c b/usr.sbin/nginx/src/core/ngx_resolver.c
new file mode 100644
index 00000000000..ab7883011e8
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_resolver.c
@@ -0,0 +1,2204 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_RESOLVER_UDP_SIZE 4096
+
+
+typedef struct {
+ u_char ident_hi;
+ u_char ident_lo;
+ u_char flags_hi;
+ u_char flags_lo;
+ u_char nqs_hi;
+ u_char nqs_lo;
+ u_char nan_hi;
+ u_char nan_lo;
+ u_char nns_hi;
+ u_char nns_lo;
+ u_char nar_hi;
+ u_char nar_lo;
+} ngx_resolver_query_t;
+
+
+typedef struct {
+ u_char type_hi;
+ u_char type_lo;
+ u_char class_hi;
+ u_char class_lo;
+} ngx_resolver_qs_t;
+
+
+typedef struct {
+ u_char type_hi;
+ u_char type_lo;
+ u_char class_hi;
+ u_char class_lo;
+ u_char ttl[4];
+ u_char len_hi;
+ u_char len_lo;
+} ngx_resolver_an_t;
+
+
+ngx_int_t ngx_udp_connect(ngx_udp_connection_t *uc);
+
+
+static void ngx_resolver_cleanup(void *data);
+static void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);
+static ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,
+ ngx_resolver_ctx_t *ctx);
+static void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,
+ ngx_queue_t *queue);
+static ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,
+ ngx_resolver_node_t *rn);
+static ngx_int_t ngx_resolver_create_name_query(ngx_resolver_node_t *rn,
+ ngx_resolver_ctx_t *ctx);
+static ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_node_t *rn,
+ ngx_resolver_ctx_t *ctx);
+static void ngx_resolver_resend_handler(ngx_event_t *ev);
+static time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree,
+ ngx_queue_t *queue);
+static void ngx_resolver_read_response(ngx_event_t *rev);
+static void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,
+ size_t n);
+static void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,
+ ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans);
+static void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
+ ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);
+static ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,
+ ngx_str_t *name, uint32_t hash);
+static ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,
+ in_addr_t addr);
+static void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+static ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,
+ u_char *buf, u_char *src, u_char *last);
+static void ngx_resolver_timeout_handler(ngx_event_t *ev);
+static void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn);
+static void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size);
+static void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);
+static void ngx_resolver_free(ngx_resolver_t *r, void *p);
+static void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);
+static void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);
+static u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);
+
+
+ngx_resolver_t *
+ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr)
+{
+ ngx_resolver_t *r;
+ ngx_pool_cleanup_t *cln;
+ ngx_udp_connection_t *uc;
+
+ cln = ngx_pool_cleanup_add(cf->pool, 0);
+ if (cln == NULL) {
+ return NULL;
+ }
+
+ cln->handler = ngx_resolver_cleanup;
+
+ r = ngx_calloc(sizeof(ngx_resolver_t), cf->log);
+ if (r == NULL) {
+ return NULL;
+ }
+
+ cln->data = r;
+
+ r->event = ngx_calloc(sizeof(ngx_event_t), cf->log);
+ if (r->event == NULL) {
+ return NULL;
+ }
+
+ ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,
+ ngx_resolver_rbtree_insert_value);
+
+ ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,
+ ngx_rbtree_insert_value);
+
+ ngx_queue_init(&r->name_resend_queue);
+ ngx_queue_init(&r->addr_resend_queue);
+
+ ngx_queue_init(&r->name_expire_queue);
+ ngx_queue_init(&r->addr_expire_queue);
+
+ r->event->handler = ngx_resolver_resend_handler;
+ r->event->data = r;
+ r->event->log = &cf->cycle->new_log;
+ r->ident = -1;
+
+ r->resend_timeout = 5;
+ r->expire = 30;
+ r->valid = 300;
+
+ r->log = &cf->cycle->new_log;
+ r->log_level = NGX_LOG_ERR;
+
+ if (addr) {
+ uc = ngx_calloc(sizeof(ngx_udp_connection_t), cf->log);
+ if (uc == NULL) {
+ return NULL;
+ }
+
+ r->udp_connection = uc;
+
+ uc->sockaddr = addr->sockaddr;
+ uc->socklen = addr->socklen;
+ uc->server = addr->name;
+
+ uc->log = cf->cycle->new_log;
+ uc->log.handler = ngx_resolver_log_error;
+ uc->log.data = uc;
+ uc->log.action = "resolving";
+ }
+
+ return r;
+}
+
+
+static void
+ngx_resolver_cleanup(void *data)
+{
+ ngx_resolver_t *r = data;
+
+ if (r) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "cleanup resolver");
+
+ ngx_resolver_cleanup_tree(r, &r->name_rbtree);
+
+ ngx_resolver_cleanup_tree(r, &r->addr_rbtree);
+
+ if (r->event) {
+ ngx_free(r->event);
+ }
+
+ if (r->udp_connection) {
+ if (r->udp_connection->connection) {
+ ngx_close_connection(r->udp_connection->connection);
+ }
+
+ ngx_free(r->udp_connection);
+ }
+
+ ngx_free(r);
+ }
+}
+
+
+static void
+ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)
+{
+ ngx_resolver_ctx_t *ctx, *next;
+ ngx_resolver_node_t *rn;
+
+ while (tree->root != tree->sentinel) {
+
+ rn = (ngx_resolver_node_t *) ngx_rbtree_min(tree->root, tree->sentinel);
+
+ ngx_queue_remove(&rn->queue);
+
+ for (ctx = rn->waiting; ctx; ctx = next) {
+ next = ctx->next;
+
+ if (ctx->event) {
+ ngx_resolver_free(r, ctx->event);
+ }
+
+ ngx_resolver_free(r, ctx);
+ }
+
+ ngx_rbtree_delete(tree, &rn->node);
+
+ ngx_resolver_free_node(r, rn);
+ }
+}
+
+
+ngx_resolver_ctx_t *
+ngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)
+{
+ in_addr_t addr;
+ ngx_resolver_ctx_t *ctx;
+
+ if (temp) {
+ addr = ngx_inet_addr(temp->name.data, temp->name.len);
+
+ if (addr != INADDR_NONE) {
+ temp->resolver = r;
+ temp->state = NGX_OK;
+ temp->naddrs = 1;
+ temp->addrs = &temp->addr;
+ temp->addr = addr;
+ temp->quick = 1;
+
+ return temp;
+ }
+ }
+
+ if (r->udp_connection == NULL) {
+ return NGX_NO_RESOLVER;
+ }
+
+ ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t));
+
+ if (ctx) {
+ ctx->resolver = r;
+ }
+
+ return ctx;
+}
+
+
+ngx_int_t
+ngx_resolve_name(ngx_resolver_ctx_t *ctx)
+{
+ ngx_int_t rc;
+ ngx_resolver_t *r;
+
+ r = ctx->resolver;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolve: \"%V\"", &ctx->name);
+
+ if (ctx->quick) {
+ ctx->handler(ctx);
+ return NGX_OK;
+ }
+
+ /* lock name mutex */
+
+ rc = ngx_resolve_name_locked(r, ctx);
+
+ if (rc == NGX_OK) {
+ return NGX_OK;
+ }
+
+ /* unlock name mutex */
+
+ if (rc == NGX_AGAIN) {
+ return NGX_OK;
+ }
+
+ /* NGX_ERROR */
+
+ if (ctx->event) {
+ ngx_resolver_free(r, ctx->event);
+ }
+
+ ngx_resolver_free(r, ctx);
+
+ return NGX_ERROR;
+}
+
+
+void
+ngx_resolve_name_done(ngx_resolver_ctx_t *ctx)
+{
+ uint32_t hash;
+ ngx_resolver_t *r;
+ ngx_resolver_ctx_t *w, **p;
+ ngx_resolver_node_t *rn;
+
+ r = ctx->resolver;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolve name done: %i", ctx->state);
+
+ if (ctx->quick) {
+ return;
+ }
+
+ if (ctx->event && ctx->event->timer_set) {
+ ngx_del_timer(ctx->event);
+ }
+
+ /* lock name mutex */
+
+ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
+
+ hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
+
+ rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
+
+ if (rn) {
+ p = &rn->waiting;
+ w = rn->waiting;
+
+ while (w) {
+ if (w == ctx) {
+ *p = w->next;
+
+ goto done;
+ }
+
+ p = &w->next;
+ w = w->next;
+ }
+ }
+
+ ngx_log_error(NGX_LOG_ALERT, r->log, 0,
+ "could not cancel %V resolving", &ctx->name);
+ }
+
+done:
+
+ ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue);
+
+ /* unlock name mutex */
+
+ /* lock alloc mutex */
+
+ if (ctx->event) {
+ ngx_resolver_free_locked(r, ctx->event);
+ }
+
+ ngx_resolver_free_locked(r, ctx);
+
+ /* unlock alloc mutex */
+}
+
+
+/* NGX_RESOLVE_A only */
+
+static ngx_int_t
+ngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)
+{
+ uint32_t hash;
+ in_addr_t addr, *addrs;
+ ngx_int_t rc;
+ ngx_uint_t naddrs;
+ ngx_resolver_ctx_t *next;
+ ngx_resolver_node_t *rn;
+
+ hash = ngx_crc32_short(ctx->name.data, ctx->name.len);
+
+ rn = ngx_resolver_lookup_name(r, &ctx->name, hash);
+
+ if (rn) {
+
+ if (rn->valid >= ngx_time()) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");
+
+ ngx_queue_remove(&rn->queue);
+
+ rn->expire = ngx_time() + r->expire;
+
+ ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+ naddrs = rn->naddrs;
+
+ if (naddrs) {
+
+ /* NGX_RESOLVE_A answer */
+
+ if (naddrs != 1) {
+ addr = 0;
+ addrs = ngx_resolver_dup(r, rn->u.addrs,
+ naddrs * sizeof(in_addr_t));
+ if (addrs == NULL) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ addr = rn->u.addr;
+ addrs = NULL;
+ }
+
+ ctx->next = rn->waiting;
+ rn->waiting = NULL;
+
+ /* unlock name mutex */
+
+ do {
+ ctx->state = NGX_OK;
+ ctx->naddrs = naddrs;
+ ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs;
+ ctx->addr = addr;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+
+ ctx = next;
+ } while (ctx);
+
+ if (addrs) {
+ ngx_resolver_free(r, addrs);
+ }
+
+ return NGX_OK;
+ }
+
+ /* NGX_RESOLVE_CNAME */
+
+ if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {
+
+ ctx->name.len = rn->cnlen;
+ ctx->name.data = rn->u.cname;
+
+ return ngx_resolve_name_locked(r, ctx);
+ }
+
+ ctx->next = rn->waiting;
+ rn->waiting = NULL;
+
+ /* unlock name mutex */
+
+ do {
+ ctx->state = NGX_RESOLVE_NXDOMAIN;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+
+ ctx = next;
+ } while (ctx);
+
+ return NGX_OK;
+ }
+
+ if (rn->waiting) {
+
+ ctx->next = rn->waiting;
+ rn->waiting = ctx;
+ ctx->state = NGX_AGAIN;
+
+ return NGX_AGAIN;
+ }
+
+ ngx_queue_remove(&rn->queue);
+
+ /* lock alloc mutex */
+
+ ngx_resolver_free_locked(r, rn->query);
+ rn->query = NULL;
+
+ if (rn->cnlen) {
+ ngx_resolver_free_locked(r, rn->u.cname);
+ }
+
+ if (rn->naddrs > 1) {
+ ngx_resolver_free_locked(r, rn->u.addrs);
+ }
+
+ /* unlock alloc mutex */
+
+ } else {
+
+ rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
+ if (rn == NULL) {
+ return NGX_ERROR;
+ }
+
+ rn->name = ngx_resolver_dup(r, ctx->name.data, ctx->name.len);
+ if (rn->name == NULL) {
+ ngx_resolver_free(r, rn);
+ return NGX_ERROR;
+ }
+
+ rn->node.key = hash;
+ rn->nlen = (u_short) ctx->name.len;
+ rn->query = NULL;
+
+ ngx_rbtree_insert(&r->name_rbtree, &rn->node);
+ }
+
+ rc = ngx_resolver_create_name_query(rn, ctx);
+
+ if (rc == NGX_ERROR) {
+ goto failed;
+ }
+
+ if (rc == NGX_DECLINED) {
+ ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+ ngx_resolver_free(r, rn->query);
+ ngx_resolver_free(r, rn->name);
+ ngx_resolver_free(r, rn);
+
+ ctx->state = NGX_RESOLVE_NXDOMAIN;
+ ctx->handler(ctx);
+
+ return NGX_OK;
+ }
+
+ if (ngx_resolver_send_query(r, rn) != NGX_OK) {
+ goto failed;
+ }
+
+ if (ctx->event == NULL) {
+ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
+ if (ctx->event == NULL) {
+ goto failed;
+ }
+
+ ctx->event->handler = ngx_resolver_timeout_handler;
+ ctx->event->data = ctx;
+ ctx->event->log = r->log;
+ ctx->ident = -1;
+
+ ngx_add_timer(ctx->event, ctx->timeout);
+ }
+
+ if (ngx_queue_empty(&r->name_resend_queue)) {
+ ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
+ }
+
+ rn->expire = ngx_time() + r->resend_timeout;
+
+ ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);
+
+ rn->cnlen = 0;
+ rn->naddrs = 0;
+ rn->valid = 0;
+ rn->waiting = ctx;
+
+ ctx->state = NGX_AGAIN;
+
+ return NGX_AGAIN;
+
+failed:
+
+ ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+ if (rn->query) {
+ ngx_resolver_free(r, rn->query);
+ }
+
+ ngx_resolver_free(r, rn->name);
+
+ ngx_resolver_free(r, rn);
+
+ return NGX_ERROR;
+}
+
+
+ngx_int_t
+ngx_resolve_addr(ngx_resolver_ctx_t *ctx)
+{
+ u_char *name;
+ ngx_resolver_t *r;
+ ngx_resolver_node_t *rn;
+
+ r = ctx->resolver;
+
+ ctx->addr = ntohl(ctx->addr);
+
+ /* lock addr mutex */
+
+ rn = ngx_resolver_lookup_addr(r, ctx->addr);
+
+ if (rn) {
+
+ if (rn->valid >= ngx_time()) {
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolve cached");
+
+ ngx_queue_remove(&rn->queue);
+
+ rn->expire = ngx_time() + r->expire;
+
+ ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue);
+
+ name = ngx_resolver_dup(r, rn->name, rn->nlen);
+ if (name == NULL) {
+ goto failed;
+ }
+
+ ctx->name.len = rn->nlen;
+ ctx->name.data = name;
+
+ /* unlock addr mutex */
+
+ ctx->state = NGX_OK;
+
+ ctx->handler(ctx);
+
+ ngx_resolver_free(r, name);
+
+ return NGX_OK;
+ }
+
+ if (rn->waiting) {
+
+ ctx->next = rn->waiting;
+ rn->waiting = ctx;
+ ctx->state = NGX_AGAIN;
+
+ /* unlock addr mutex */
+
+ return NGX_OK;
+ }
+
+ ngx_queue_remove(&rn->queue);
+
+ ngx_resolver_free(r, rn->query);
+ rn->query = NULL;
+
+ } else {
+ rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));
+ if (rn == NULL) {
+ goto failed;
+ }
+
+ rn->node.key = ctx->addr;
+ rn->query = NULL;
+
+ ngx_rbtree_insert(&r->addr_rbtree, &rn->node);
+ }
+
+ if (ngx_resolver_create_addr_query(rn, ctx) != NGX_OK) {
+ goto failed;
+ }
+
+ if (ngx_resolver_send_query(r, rn) != NGX_OK) {
+ goto failed;
+ }
+
+ ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));
+ if (ctx->event == NULL) {
+ goto failed;
+ }
+
+ ctx->event->handler = ngx_resolver_timeout_handler;
+ ctx->event->data = ctx;
+ ctx->event->log = r->log;
+ ctx->ident = -1;
+
+ ngx_add_timer(ctx->event, ctx->timeout);
+
+ if (ngx_queue_empty(&r->addr_resend_queue)) {
+ ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));
+ }
+
+ rn->expire = ngx_time() + r->resend_timeout;
+
+ ngx_queue_insert_head(&r->addr_resend_queue, &rn->queue);
+
+ rn->cnlen = 0;
+ rn->naddrs = 0;
+ rn->name = NULL;
+ rn->nlen = 0;
+ rn->valid = 0;
+ rn->waiting = ctx;
+
+ /* unlock addr mutex */
+
+ ctx->state = NGX_AGAIN;
+
+ return NGX_OK;
+
+failed:
+
+ if (rn) {
+ ngx_rbtree_delete(&r->addr_rbtree, &rn->node);
+
+ if (rn->query) {
+ ngx_resolver_free(r, rn->query);
+ }
+
+ ngx_resolver_free(r, rn);
+ }
+
+ /* unlock addr mutex */
+
+ if (ctx->event) {
+ ngx_resolver_free(r, ctx->event);
+ }
+
+ ngx_resolver_free(r, ctx);
+
+ return NGX_ERROR;
+}
+
+
+void
+ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)
+{
+ in_addr_t addr;
+ ngx_resolver_t *r;
+ ngx_resolver_ctx_t *w, **p;
+ ngx_resolver_node_t *rn;
+
+ r = ctx->resolver;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolve addr done: %i", ctx->state);
+
+ if (ctx->event && ctx->event->timer_set) {
+ ngx_del_timer(ctx->event);
+ }
+
+ /* lock addr mutex */
+
+ if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {
+
+ rn = ngx_resolver_lookup_addr(r, ctx->addr);
+
+ if (rn) {
+ p = &rn->waiting;
+ w = rn->waiting;
+
+ while (w) {
+ if (w == ctx) {
+ *p = w->next;
+
+ goto done;
+ }
+
+ p = &w->next;
+ w = w->next;
+ }
+ }
+
+ addr = ntohl(ctx->addr);
+
+ ngx_log_error(NGX_LOG_ALERT, r->log, 0,
+ "could not cancel %ud.%ud.%ud.%ud resolving",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ }
+
+done:
+
+ ngx_resolver_expire(r, &r->addr_rbtree, &r->addr_expire_queue);
+
+ /* unlock addr mutex */
+
+ /* lock alloc mutex */
+
+ if (ctx->event) {
+ ngx_resolver_free_locked(r, ctx->event);
+ }
+
+ ngx_resolver_free_locked(r, ctx);
+
+ /* unlock alloc mutex */
+}
+
+
+static void
+ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)
+{
+ time_t now;
+ ngx_uint_t i;
+ ngx_queue_t *q;
+ ngx_resolver_node_t *rn;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver expire");
+
+ now = ngx_time();
+
+ for (i = 0; i < 2; i++) {
+ if (ngx_queue_empty(queue)) {
+ return;
+ }
+
+ q = ngx_queue_last(queue);
+
+ rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+
+ if (now <= rn->expire) {
+ return;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver expire \"%*s\"", (size_t) rn->nlen, rn->name);
+
+ ngx_queue_remove(q);
+
+ ngx_rbtree_delete(tree, &rn->node);
+
+ ngx_resolver_free_node(r, rn);
+ }
+}
+
+
+static ngx_int_t
+ngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)
+{
+ ssize_t n;
+ ngx_udp_connection_t *uc;
+
+ uc = r->udp_connection;
+
+ if (uc->connection == NULL) {
+ if (ngx_udp_connect(uc) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ uc->connection->data = r;
+ uc->connection->read->handler = ngx_resolver_read_response;
+ uc->connection->read->resolver = 1;
+ }
+
+ n = ngx_send(uc->connection, rn->query, rn->qlen);
+
+ if (n == -1) {
+ return NGX_ERROR;
+ }
+
+ if ((size_t) n != (size_t) rn->qlen) {
+ ngx_log_error(NGX_LOG_CRIT, &uc->log, 0, "send() incomplete");
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+static void
+ngx_resolver_resend_handler(ngx_event_t *ev)
+{
+ time_t timer, atimer, ntimer;
+ ngx_resolver_t *r;
+
+ r = ev->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver resend handler");
+
+ /* lock name mutex */
+
+ ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue);
+
+ /* unlock name mutex */
+
+ /* lock addr mutex */
+
+ atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue);
+
+ /* unlock addr mutex */
+
+ if (ntimer == 0) {
+ timer = atimer;
+
+ } else if (atimer == 0) {
+ timer = ntimer;
+
+ } else {
+ timer = (atimer < ntimer) ? atimer : ntimer;
+ }
+
+ if (timer) {
+ ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000));
+ }
+}
+
+
+static time_t
+ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)
+{
+ time_t now;
+ ngx_queue_t *q;
+ ngx_resolver_node_t *rn;
+
+ now = ngx_time();
+
+ for ( ;; ) {
+ if (ngx_queue_empty(queue)) {
+ return 0;
+ }
+
+ q = ngx_queue_last(queue);
+
+ rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+
+ if (now < rn->expire) {
+ return rn->expire - now;
+ }
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver resend \"%*s\" %p",
+ (size_t) rn->nlen, rn->name, rn->waiting);
+
+ ngx_queue_remove(q);
+
+ if (rn->waiting) {
+
+ if (ngx_resolver_send_query(r, rn) == NGX_OK) {
+
+ rn->expire = now + r->resend_timeout;
+
+ ngx_queue_insert_head(queue, &rn->queue);
+ }
+
+ continue;
+ }
+
+ ngx_rbtree_delete(tree, &rn->node);
+
+ ngx_resolver_free_node(r, rn);
+ }
+}
+
+
+static void
+ngx_resolver_read_response(ngx_event_t *rev)
+{
+ ssize_t n;
+ ngx_connection_t *c;
+ u_char buf[NGX_RESOLVER_UDP_SIZE];
+
+ c = rev->data;
+
+ do {
+ n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE);
+
+ if (n < 0) {
+ return;
+ }
+
+ ngx_resolver_process_response(c->data, buf, n);
+
+ } while (rev->ready);
+}
+
+
+static void
+ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n)
+{
+ char *err;
+ size_t len;
+ ngx_uint_t i, times, ident, qident, flags, code, nqs, nan,
+ qtype, qclass;
+ ngx_queue_t *q;
+ ngx_resolver_qs_t *qs;
+ ngx_resolver_node_t *rn;
+ ngx_resolver_query_t *query;
+
+ if ((size_t) n < sizeof(ngx_resolver_query_t)) {
+ goto short_response;
+ }
+
+ query = (ngx_resolver_query_t *) buf;
+
+ ident = (query->ident_hi << 8) + query->ident_lo;
+ flags = (query->flags_hi << 8) + query->flags_lo;
+ nqs = (query->nqs_hi << 8) + query->nqs_lo;
+ nan = (query->nan_hi << 8) + query->nan_lo;
+
+ ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver DNS response %ui fl:%04Xui %ui/%ui/%ui/%ui",
+ ident, flags, nqs, nan,
+ (query->nns_hi << 8) + query->nns_lo,
+ (query->nar_hi << 8) + query->nar_lo);
+
+ if (!(flags & 0x8000)) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "invalid DNS response %ui fl:%04Xui", ident, flags);
+ return;
+ }
+
+ code = flags & 0x7f;
+
+ if (code == NGX_RESOLVE_FORMERR) {
+
+ times = 0;
+
+ for (q = ngx_queue_head(&r->name_resend_queue);
+ q != ngx_queue_sentinel(&r->name_resend_queue) || times++ < 100;
+ q = ngx_queue_next(q))
+ {
+ rn = ngx_queue_data(q, ngx_resolver_node_t, queue);
+ qident = (rn->query[0] << 8) + rn->query[1];
+
+ if (qident == ident) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "DNS error (%ui: %s), query id:%ui, name:\"%*s\"",
+ code, ngx_resolver_strerror(code), ident,
+ rn->nlen, rn->name);
+ return;
+ }
+ }
+
+ goto dns_error;
+ }
+
+ if (code > NGX_RESOLVE_REFUSED) {
+ goto dns_error;
+ }
+
+ if (nqs != 1) {
+ err = "invalid number of questions in DNS response";
+ goto done;
+ }
+
+ i = sizeof(ngx_resolver_query_t);
+
+ while (i < (ngx_uint_t) n) {
+ if (buf[i] == '\0') {
+ goto found;
+ }
+
+ len = buf[i];
+ i += 1 + len;
+ }
+
+ goto short_response;
+
+found:
+
+ if (i++ == 0) {
+ err = "zero-length domain name in DNS response";
+ goto done;
+ }
+
+ if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t))
+ > (ngx_uint_t) n)
+ {
+ goto short_response;
+ }
+
+ qs = (ngx_resolver_qs_t *) &buf[i];
+
+ qtype = (qs->type_hi << 8) + qs->type_lo;
+ qclass = (qs->class_hi << 8) + qs->class_lo;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver DNS response qt:%ui cl:%ui", qtype, qclass);
+
+ if (qclass != 1) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "unknown query class %ui in DNS response", qclass);
+ return;
+ }
+
+ switch (qtype) {
+
+ case NGX_RESOLVE_A:
+
+ ngx_resolver_process_a(r, buf, n, ident, code, nan,
+ i + sizeof(ngx_resolver_qs_t));
+
+ break;
+
+ case NGX_RESOLVE_PTR:
+
+ ngx_resolver_process_ptr(r, buf, n, ident, code, nan);
+
+ break;
+
+ default:
+ ngx_log_error(r->log_level, r->log, 0,
+ "unknown query type %ui in DNS response", qtype);
+ return;
+ }
+
+ return;
+
+short_response:
+
+ err = "short dns response";
+
+done:
+
+ ngx_log_error(r->log_level, r->log, 0, err);
+
+ return;
+
+dns_error:
+
+ ngx_log_error(r->log_level, r->log, 0,
+ "DNS error (%ui: %s), query id:%ui",
+ code, ngx_resolver_strerror(code), ident);
+ return;
+}
+
+
+static void
+ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t last,
+ ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan, ngx_uint_t ans)
+{
+ char *err;
+ u_char *cname;
+ size_t len;
+ uint32_t hash;
+ in_addr_t addr, *addrs;
+ ngx_str_t name;
+ ngx_uint_t qtype, qident, naddrs, a, i, n, start;
+ ngx_resolver_an_t *an;
+ ngx_resolver_ctx_t *ctx, *next;
+ ngx_resolver_node_t *rn;
+
+ if (ngx_resolver_copy(r, &name, buf, &buf[12], &buf[last]) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver qs:%V", &name);
+
+ hash = ngx_crc32_short(name.data, name.len);
+
+ /* lock name mutex */
+
+ rn = ngx_resolver_lookup_name(r, &name, hash);
+
+ if (rn == NULL || rn->query == NULL) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "unexpected response for %V", &name);
+ goto failed;
+ }
+
+ qident = (rn->query[0] << 8) + rn->query[1];
+
+ if (ident != qident) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "wrong ident %ui response for %V, expect %ui",
+ ident, &name, qident);
+ goto failed;
+ }
+
+ ngx_resolver_free(r, name.data);
+
+ if (code == 0 && nan == 0) {
+ code = 3; /* NXDOMAIN */
+ }
+
+ if (code) {
+ next = rn->waiting;
+ rn->waiting = NULL;
+
+ ngx_queue_remove(&rn->queue);
+
+ ngx_rbtree_delete(&r->name_rbtree, &rn->node);
+
+ ngx_resolver_free_node(r, rn);
+
+ /* unlock name mutex */
+
+ while (next) {
+ ctx = next;
+ ctx->state = code;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+ }
+
+ return;
+ }
+
+ i = ans;
+ naddrs = 0;
+ addr = 0;
+ addrs = NULL;
+ cname = NULL;
+ qtype = 0;
+
+ for (a = 0; a < nan; a++) {
+
+ start = i;
+
+ while (i < last) {
+
+ if (buf[i] & 0xc0) {
+ i += 2;
+ goto found;
+ }
+
+ if (buf[i] == 0) {
+ i++;
+ goto test_length;
+ }
+
+ i += 1 + buf[i];
+ }
+
+ goto short_response;
+
+ test_length:
+
+ if (i - start < 2) {
+ err = "invalid name in dns response";
+ goto invalid;
+ }
+
+ found:
+
+ if (i + sizeof(ngx_resolver_an_t) >= last) {
+ goto short_response;
+ }
+
+ an = (ngx_resolver_an_t *) &buf[i];
+
+ qtype = (an->type_hi << 8) + an->type_lo;
+ len = (an->len_hi << 8) + an->len_lo;
+
+ if (qtype == NGX_RESOLVE_A) {
+
+ i += sizeof(ngx_resolver_an_t);
+
+ if (i + len > last) {
+ goto short_response;
+ }
+
+ addr = htonl((buf[i] << 24) + (buf[i + 1] << 16)
+ + (buf[i + 2] << 8) + (buf[i + 3]));
+
+ naddrs++;
+
+ i += len;
+
+ } else if (qtype == NGX_RESOLVE_CNAME) {
+ cname = &buf[i] + sizeof(ngx_resolver_an_t);
+ i += sizeof(ngx_resolver_an_t) + len;
+
+ } else if (qtype == NGX_RESOLVE_DNAME) {
+ i += sizeof(ngx_resolver_an_t) + len;
+
+ } else {
+ ngx_log_error(r->log_level, r->log, 0,
+ "unexpected qtype %ui", qtype);
+ }
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver naddrs:%ui cname:%p", naddrs, cname);
+
+ if (naddrs) {
+
+ if (naddrs == 1) {
+ rn->u.addr = addr;
+
+ } else {
+
+ addrs = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));
+ if (addrs == NULL) {
+ return;
+ }
+
+ n = 0;
+ i = ans;
+
+ for (a = 0; a < nan; a++) {
+
+ for ( ;; ) {
+
+ if (buf[i] & 0xc0) {
+ i += 2;
+ goto ok;
+ }
+
+ if (buf[i] == 0) {
+ i++;
+ goto ok;
+ }
+
+ i += 1 + buf[i];
+ }
+
+ ok:
+
+ an = (ngx_resolver_an_t *) &buf[i];
+
+ qtype = (an->type_hi << 8) + an->type_lo;
+ len = (an->len_hi << 8) + an->len_lo;
+
+ i += sizeof(ngx_resolver_an_t);
+
+ if (qtype == NGX_RESOLVE_A) {
+
+ addrs[n++] = htonl((buf[i] << 24) + (buf[i + 1] << 16)
+ + (buf[i + 2] << 8) + (buf[i + 3]));
+
+ if (n == naddrs) {
+ break;
+ }
+ }
+
+ i += len;
+ }
+
+ rn->u.addrs = addrs;
+
+ addrs = ngx_resolver_dup(r, rn->u.addrs,
+ naddrs * sizeof(in_addr_t));
+ if (addrs == NULL) {
+ return;
+ }
+ }
+
+ rn->naddrs = (u_short) naddrs;
+
+ ngx_queue_remove(&rn->queue);
+
+ rn->valid = ngx_time() + r->valid;
+ rn->expire = ngx_time() + r->expire;
+
+ ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+ next = rn->waiting;
+ rn->waiting = NULL;
+
+ /* unlock name mutex */
+
+ while (next) {
+ ctx = next;
+ ctx->state = NGX_OK;
+ ctx->naddrs = naddrs;
+ ctx->addrs = (naddrs == 1) ? &ctx->addr : addrs;
+ ctx->addr = addr;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+ }
+
+ if (naddrs > 1) {
+ ngx_resolver_free(r, addrs);
+ }
+
+ return;
+
+ } else if (cname) {
+
+ /* CNAME only */
+
+ if (ngx_resolver_copy(r, &name, buf, cname, &buf[last]) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver cname:\"%V\"", &name);
+
+ ngx_queue_remove(&rn->queue);
+
+ rn->cnlen = (u_short) name.len;
+ rn->u.cname = name.data;
+ rn->valid = ngx_time() + r->valid;
+ rn->expire = ngx_time() + r->expire;
+
+ ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);
+
+ ctx = rn->waiting;
+ rn->waiting = NULL;
+
+ if (ctx) {
+ ctx->name = name;
+
+ (void) ngx_resolve_name_locked(r, ctx);
+ }
+
+ return;
+ }
+
+ ngx_log_error(r->log_level, r->log, 0,
+ "no A or CNAME types in DNS responses, unknown query type: %ui",
+ qtype);
+ return;
+
+short_response:
+
+ err = "short dns response";
+
+invalid:
+
+ /* unlock name mutex */
+
+ ngx_log_error(r->log_level, r->log, 0, err);
+
+ return;
+
+failed:
+
+ /* unlock name mutex */
+
+ ngx_resolver_free(r, name.data);
+
+ return;
+}
+
+
+static void
+ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,
+ ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)
+{
+ char *err;
+ size_t len;
+ in_addr_t addr;
+ ngx_int_t digit;
+ ngx_str_t name;
+ ngx_uint_t i, mask, qident;
+ ngx_resolver_an_t *an;
+ ngx_resolver_ctx_t *ctx, *next;
+ ngx_resolver_node_t *rn;
+
+ if (ngx_resolver_copy(r, NULL, buf, &buf[12], &buf[n]) != NGX_OK) {
+ goto invalid_in_addr_arpa;
+ }
+
+ addr = 0;
+ i = 12;
+
+ for (mask = 0; mask < 32; mask += 8) {
+ len = buf[i++];
+
+ digit = ngx_atoi(&buf[i], len);
+ if (digit == NGX_ERROR || digit > 255) {
+ goto invalid_in_addr_arpa;
+ }
+
+ addr += digit << mask;
+ i += len;
+ }
+
+ if (ngx_strcmp(&buf[i], "\7in-addr\4arpa") != 0) {
+ goto invalid_in_addr_arpa;
+ }
+
+ /* lock addr mutex */
+
+ rn = ngx_resolver_lookup_addr(r, addr);
+
+ if (rn == NULL || rn->query == NULL) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "unexpected response for %ud.%ud.%ud.%ud",
+ (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff);
+ goto failed;
+ }
+
+ qident = (rn->query[0] << 8) + rn->query[1];
+
+ if (ident != qident) {
+ ngx_log_error(r->log_level, r->log, 0,
+ "wrong ident %ui response for %ud.%ud.%ud.%ud, expect %ui",
+ ident, (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+ (addr >> 8) & 0xff, addr & 0xff, qident);
+ goto failed;
+ }
+
+ if (code == 0 && nan == 0) {
+ code = 3; /* NXDOMAIN */
+ }
+
+ if (code) {
+ next = rn->waiting;
+ rn->waiting = NULL;
+
+ ngx_queue_remove(&rn->queue);
+
+ ngx_rbtree_delete(&r->addr_rbtree, &rn->node);
+
+ ngx_resolver_free_node(r, rn);
+
+ /* unlock addr mutex */
+
+ while (next) {
+ ctx = next;
+ ctx->state = code;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+ }
+
+ return;
+ }
+
+ i += sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t);
+
+ if (i + 2 + sizeof(ngx_resolver_an_t) > (ngx_uint_t) n) {
+ goto short_response;
+ }
+
+ /* compression pointer to "XX.XX.XX.XX.in-addr.arpa */
+
+ if (buf[i] != 0xc0 || buf[i + 1] != 0x0c) {
+ err = "invalid in-addr.arpa name in DNS response";
+ goto invalid;
+ }
+
+ an = (ngx_resolver_an_t *) &buf[i + 2];
+
+ len = (an->len_hi << 8) + an->len_lo;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,
+ "resolver qt:%ui cl:%ui len:%uz",
+ (an->type_hi << 8) + an->type_lo,
+ (an->class_hi << 8) + an->class_lo, len);
+
+ i += 2 + sizeof(ngx_resolver_an_t);
+
+ if (i + len > (ngx_uint_t) n) {
+ goto short_response;
+ }
+
+ if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name);
+
+ if (name.len != (size_t) rn->nlen
+ || ngx_strncmp(name.data, rn->name, name.len) != 0)
+ {
+ if (rn->nlen) {
+ ngx_resolver_free(r, rn->name);
+ }
+
+ rn->nlen = (u_short) name.len;
+ rn->name = name.data;
+
+ name.data = ngx_resolver_dup(r, rn->name, name.len);
+ if (name.data == NULL) {
+ goto failed;
+ }
+ }
+
+ ngx_queue_remove(&rn->queue);
+
+ rn->valid = ngx_time() + r->valid;
+ rn->expire = ngx_time() + r->expire;
+
+ ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue);
+
+ next = rn->waiting;
+ rn->waiting = NULL;
+
+ /* unlock addr mutex */
+
+ while (next) {
+ ctx = next;
+ ctx->state = NGX_OK;
+ ctx->name = name;
+ next = ctx->next;
+
+ ctx->handler(ctx);
+ }
+
+ ngx_resolver_free(r, name.data);
+
+ return;
+
+invalid_in_addr_arpa:
+
+ ngx_log_error(r->log_level, r->log, 0,
+ "invalid in-addr.arpa name in DNS response");
+ return;
+
+short_response:
+
+ err = "short DNS response";
+
+invalid:
+
+ /* unlock addr mutex */
+
+ ngx_log_error(r->log_level, r->log, 0, err);
+
+ return;
+
+failed:
+
+ /* unlock addr mutex */
+
+ return;
+}
+
+
+static ngx_resolver_node_t *
+ngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)
+{
+ ngx_int_t rc;
+ ngx_rbtree_node_t *node, *sentinel;
+ ngx_resolver_node_t *rn;
+
+ node = r->name_rbtree.root;
+ sentinel = r->name_rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (hash < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (hash > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* hash == node->key */
+
+ do {
+ rn = (ngx_resolver_node_t *) node;
+
+ rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);
+
+ if (rc == 0) {
+ return rn;
+ }
+
+ node = (rc < 0) ? node->left : node->right;
+
+ } while (node != sentinel && hash == node->key);
+
+ break;
+ }
+
+ /* not found */
+
+ return NULL;
+}
+
+
+static ngx_resolver_node_t *
+ngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)
+{
+ ngx_rbtree_node_t *node, *sentinel;
+
+ node = r->addr_rbtree.root;
+ sentinel = r->addr_rbtree.sentinel;
+
+ while (node != sentinel) {
+
+ if (addr < node->key) {
+ node = node->left;
+ continue;
+ }
+
+ if (addr > node->key) {
+ node = node->right;
+ continue;
+ }
+
+ /* addr == node->key */
+
+ return (ngx_resolver_node_t *) node;
+ }
+
+ /* not found */
+
+ return NULL;
+}
+
+
+static void
+ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_rbtree_node_t **p;
+ ngx_resolver_node_t *rn, *rn_temp;
+
+ for ( ;; ) {
+
+ if (node->key < temp->key) {
+
+ p = &temp->left;
+
+ } else if (node->key > temp->key) {
+
+ p = &temp->right;
+
+ } else { /* node->key == temp->key */
+
+ rn = (ngx_resolver_node_t *) node;
+ rn_temp = (ngx_resolver_node_t *) temp;
+
+ p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)
+ < 0) ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+static ngx_int_t
+ngx_resolver_create_name_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
+{
+ u_char *p, *s;
+ size_t len, nlen;
+ ngx_uint_t ident;
+ ngx_resolver_qs_t *qs;
+ ngx_resolver_query_t *query;
+
+ nlen = ctx->name.len ? (1 + ctx->name.len + 1) : 1;
+
+ len = sizeof(ngx_resolver_query_t) + nlen + sizeof(ngx_resolver_qs_t);
+
+ p = ngx_resolver_alloc(ctx->resolver, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ rn->qlen = (u_short) len;
+ rn->query = p;
+
+ query = (ngx_resolver_query_t *) p;
+
+ ident = ngx_random();
+
+ ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->resolver->log, 0,
+ "resolve: \"%V\" %i", &ctx->name, ident & 0xffff);
+
+ query->ident_hi = (u_char) ((ident >> 8) & 0xff);
+ query->ident_lo = (u_char) (ident & 0xff);
+
+ /* recursion query */
+ query->flags_hi = 1; query->flags_lo = 0;
+
+ /* one question */
+ query->nqs_hi = 0; query->nqs_lo = 1;
+ query->nan_hi = 0; query->nan_lo = 0;
+ query->nns_hi = 0; query->nns_lo = 0;
+ query->nar_hi = 0; query->nar_lo = 0;
+
+ p += sizeof(ngx_resolver_query_t) + nlen;
+
+ qs = (ngx_resolver_qs_t *) p;
+
+ /* query type */
+ qs->type_hi = 0; qs->type_lo = (u_char) ctx->type;
+
+ /* IP query class */
+ qs->class_hi = 0; qs->class_lo = 1;
+
+ /* convert "www.example.com" to "\3www\7example\3com\0" */
+
+ len = 0;
+ p--;
+ *p-- = '\0';
+
+ for (s = ctx->name.data + ctx->name.len - 1; s >= ctx->name.data; s--) {
+ if (*s != '.') {
+ *p = *s;
+ len++;
+
+ } else {
+ if (len == 0) {
+ return NGX_DECLINED;
+ }
+
+ *p = (u_char) len;
+ len = 0;
+ }
+
+ p--;
+ }
+
+ *p = (u_char) len;
+
+ return NGX_OK;
+}
+
+
+/* AF_INET only */
+
+static ngx_int_t
+ngx_resolver_create_addr_query(ngx_resolver_node_t *rn, ngx_resolver_ctx_t *ctx)
+{
+ u_char *p, *d;
+ size_t len;
+ ngx_int_t n;
+ ngx_uint_t ident;
+ ngx_resolver_query_t *query;
+
+ len = sizeof(ngx_resolver_query_t)
+ + sizeof(".255.255.255.255.in-addr.arpa.") - 1
+ + sizeof(ngx_resolver_qs_t);
+
+ p = ngx_resolver_alloc(ctx->resolver, len);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ rn->query = p;
+ query = (ngx_resolver_query_t *) p;
+
+ ident = ngx_random();
+
+ query->ident_hi = (u_char) ((ident >> 8) & 0xff);
+ query->ident_lo = (u_char) (ident & 0xff);
+
+ /* recursion query */
+ query->flags_hi = 1; query->flags_lo = 0;
+
+ /* one question */
+ query->nqs_hi = 0; query->nqs_lo = 1;
+ query->nan_hi = 0; query->nan_lo = 0;
+ query->nns_hi = 0; query->nns_lo = 0;
+ query->nar_hi = 0; query->nar_lo = 0;
+
+ p += sizeof(ngx_resolver_query_t);
+
+ for (n = 0; n < 32; n += 8) {
+ d = ngx_sprintf(&p[1], "%ud", (ctx->addr >> n) & 0xff);
+ *p = (u_char) (d - &p[1]);
+ p = d;
+ }
+
+ /* query type "PTR", IP query class */
+ ngx_memcpy(p, "\7in-addr\4arpa\0\0\14\0\1", 18);
+
+ rn->qlen = (u_short)
+ (p + sizeof("\7in-addr\4arpa") + sizeof(ngx_resolver_qs_t)
+ - rn->query);
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
+ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src,
+ u_char *last)
+{
+ char *err;
+ u_char *p, *dst;
+ ssize_t len;
+ ngx_uint_t i, n;
+
+ p = src;
+ len = -1;
+
+ /*
+ * compression pointers allow to create endless loop, so we set limit;
+ * 128 pointers should be enough to store 255-byte name
+ */
+
+ for (i = 0; i < 128; i++) {
+ n = *p++;
+
+ if (n == 0) {
+ goto done;
+ }
+
+ if (n & 0xc0) {
+ n = ((n & 0x3f) << 8) + *p;
+ p = &buf[n];
+
+ } else {
+ len += 1 + n;
+ p = &p[n];
+ }
+
+ if (p >= last) {
+ err = "name is out of response";
+ goto invalid;
+ }
+ }
+
+ err = "compression pointers loop";
+
+invalid:
+
+ ngx_log_error(r->log_level, r->log, 0, err);
+
+ return NGX_ERROR;
+
+done:
+
+ if (name == NULL) {
+ return NGX_OK;
+ }
+
+ if (len == -1) {
+ name->len = 0;
+ name->data = NULL;
+ return NGX_OK;
+ }
+
+ dst = ngx_resolver_alloc(r, len);
+ if (dst == NULL) {
+ return NGX_ERROR;
+ }
+
+ name->data = dst;
+
+ n = *src++;
+
+ for ( ;; ) {
+ if (n != 0xc0) {
+ ngx_memcpy(dst, src, n);
+ dst += n;
+ src += n;
+
+ n = *src++;
+
+ if (n != 0) {
+ *dst++ = '.';
+ }
+
+ } else {
+ n = ((n & 0x3f) << 8) + *src;
+ src = &buf[n];
+
+ n = *src++;
+ }
+
+ if (n == 0) {
+ name->len = dst - name->data;
+ return NGX_OK;
+ }
+ }
+}
+
+
+static void
+ngx_resolver_timeout_handler(ngx_event_t *ev)
+{
+ ngx_resolver_ctx_t *ctx;
+
+ ctx = ev->data;
+
+ ctx->state = NGX_RESOLVE_TIMEDOUT;
+
+ ctx->handler(ctx);
+}
+
+
+static void
+ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn)
+{
+ /* lock alloc mutex */
+
+ if (rn->query) {
+ ngx_resolver_free_locked(r, rn->query);
+ }
+
+ if (rn->name) {
+ ngx_resolver_free_locked(r, rn->name);
+ }
+
+ if (rn->cnlen) {
+ ngx_resolver_free_locked(r, rn->u.cname);
+ }
+
+ if (rn->naddrs > 1) {
+ ngx_resolver_free_locked(r, rn->u.addrs);
+ }
+
+ ngx_resolver_free_locked(r, rn);
+
+ /* unlock alloc mutex */
+}
+
+
+static void *
+ngx_resolver_alloc(ngx_resolver_t *r, size_t size)
+{
+ u_char *p;
+
+ /* lock alloc mutex */
+
+ p = ngx_alloc(size, r->log);
+
+ /* unlock alloc mutex */
+
+ return p;
+}
+
+
+static void *
+ngx_resolver_calloc(ngx_resolver_t *r, size_t size)
+{
+ u_char *p;
+
+ p = ngx_resolver_alloc(r, size);
+
+ if (p) {
+ ngx_memzero(p, size);
+ }
+
+ return p;
+}
+
+
+static void
+ngx_resolver_free(ngx_resolver_t *r, void *p)
+{
+ /* lock alloc mutex */
+
+ ngx_free(p);
+
+ /* unlock alloc mutex */
+}
+
+
+static void
+ngx_resolver_free_locked(ngx_resolver_t *r, void *p)
+{
+ ngx_free(p);
+}
+
+
+static void *
+ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)
+{
+ void *dst;
+
+ dst = ngx_resolver_alloc(r, size);
+
+ if (dst == NULL) {
+ return dst;
+ }
+
+ ngx_memcpy(dst, src, size);
+
+ return dst;
+}
+
+
+char *
+ngx_resolver_strerror(ngx_int_t err)
+{
+ static char *errors[] = {
+ "Format error", /* FORMERR */
+ "Server failure", /* SERVFAIL */
+ "Host not found", /* NXDOMAIN */
+ "Unimplemented", /* NOTIMP */
+ "Operation refused" /* REFUSED */
+ };
+
+ if (err > 0 && err < 6) {
+ return errors[err - 1];
+ }
+
+ if (err == NGX_RESOLVE_TIMEDOUT) {
+ return "Operation timed out";
+ }
+
+ return "Unknown error";
+}
+
+
+static u_char *
+ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len)
+{
+ u_char *p;
+ ngx_udp_connection_t *uc;
+
+ p = buf;
+
+ if (log->action) {
+ p = ngx_snprintf(buf, len, " while %s", log->action);
+ len -= p - buf;
+ }
+
+ uc = log->data;
+
+ if (uc) {
+ p = ngx_snprintf(p, len, ", resolver: %V", &uc->server);
+ }
+
+ return p;
+}
+
+
+ngx_int_t
+ngx_udp_connect(ngx_udp_connection_t *uc)
+{
+ int rc;
+ ngx_int_t event;
+ ngx_event_t *rev, *wev;
+ ngx_socket_t s;
+ ngx_connection_t *c;
+
+ s = ngx_socket(AF_INET, SOCK_DGRAM, 0);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &uc->log, 0, "UDP socket %d", s);
+
+ if (s == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_socket_n " failed");
+ return NGX_ERROR;
+ }
+
+ c = ngx_get_connection(s, &uc->log);
+
+ if (c == NULL) {
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_close_socket_n "failed");
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (ngx_nonblocking(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_nonblocking_n " failed");
+
+ ngx_free_connection(c);
+
+ if (ngx_close_socket(s) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, &uc->log, ngx_socket_errno,
+ ngx_close_socket_n " failed");
+ }
+
+ return NGX_ERROR;
+ }
+
+ rev = c->read;
+ wev = c->write;
+
+ rev->log = &uc->log;
+ wev->log = &uc->log;
+
+ uc->connection = c;
+
+ c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
+
+#if (NGX_THREADS)
+
+ /* TODO: lock event when call completion handler */
+
+ rev->lock = &c->lock;
+ wev->lock = &c->lock;
+ rev->own_lock = &c->lock;
+ wev->own_lock = &c->lock;
+
+#endif
+
+ ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &uc->log, 0,
+ "connect to %V, fd:%d #%d", &uc->server, s, c->number);
+
+ rc = connect(s, uc->sockaddr, uc->socklen);
+
+ /* TODO: aio, iocp */
+
+ if (rc == -1) {
+ ngx_log_error(NGX_LOG_CRIT, &uc->log, ngx_socket_errno,
+ "connect() failed");
+
+ return NGX_ERROR;
+ }
+
+ /* UDP sockets are always ready to write */
+ wev->ready = 1;
+
+ if (ngx_add_event) {
+
+ event = (ngx_event_flags & NGX_USE_CLEAR_EVENT) ?
+ /* kqueue, epoll */ NGX_CLEAR_EVENT:
+ /* select, poll, /dev/poll */ NGX_LEVEL_EVENT;
+ /* eventport event type has no meaning: oneshot only */
+
+ if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {
+ return NGX_ERROR;
+ }
+
+ } else {
+ /* rtsig */
+
+ if (ngx_add_conn(c) == NGX_ERROR) {
+ return NGX_ERROR;
+ }
+ }
+
+ return NGX_OK;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_resolver.h b/usr.sbin/nginx/src/core/ngx_resolver.h
new file mode 100644
index 00000000000..ef3c2082c24
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_resolver.h
@@ -0,0 +1,148 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#ifndef _NGX_RESOLVER_H_INCLUDED_
+#define _NGX_RESOLVER_H_INCLUDED_
+
+
+#define NGX_RESOLVE_A 1
+#define NGX_RESOLVE_CNAME 5
+#define NGX_RESOLVE_PTR 12
+#define NGX_RESOLVE_MX 15
+#define NGX_RESOLVE_TXT 16
+#define NGX_RESOLVE_DNAME 39
+
+#define NGX_RESOLVE_FORMERR 1
+#define NGX_RESOLVE_SERVFAIL 2
+#define NGX_RESOLVE_NXDOMAIN 3
+#define NGX_RESOLVE_NOTIMP 4
+#define NGX_RESOLVE_REFUSED 5
+#define NGX_RESOLVE_TIMEDOUT NGX_ETIMEDOUT
+
+
+#define NGX_NO_RESOLVER (void *) -1
+
+#define NGX_RESOLVER_MAX_RECURSION 50
+
+
+typedef struct {
+ ngx_connection_t *connection;
+ struct sockaddr *sockaddr;
+ socklen_t socklen;
+ ngx_str_t server;
+ ngx_log_t log;
+} ngx_udp_connection_t;
+
+
+typedef struct ngx_resolver_ctx_s ngx_resolver_ctx_t;
+
+typedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx);
+
+
+typedef struct {
+ ngx_rbtree_node_t node;
+ ngx_queue_t queue;
+
+ /* PTR: resolved name, A: name to resolve */
+ u_char *name;
+
+ u_short nlen;
+ u_short qlen;
+
+ u_char *query;
+
+ union {
+ in_addr_t addr;
+ in_addr_t *addrs;
+ u_char *cname;
+ } u;
+
+ u_short naddrs;
+ u_short cnlen;
+
+ time_t expire;
+ time_t valid;
+
+ ngx_resolver_ctx_t *waiting;
+} ngx_resolver_node_t;
+
+
+typedef struct {
+ /* has to be pointer because of "incomplete type" */
+ ngx_event_t *event;
+
+ /* TODO: DNS peers balancer */
+ /* STUB */
+ ngx_udp_connection_t *udp_connection;
+
+ ngx_log_t *log;
+
+ /* ident must be after 3 pointers */
+ ngx_int_t ident;
+
+ ngx_rbtree_t name_rbtree;
+ ngx_rbtree_node_t name_sentinel;
+
+ ngx_rbtree_t addr_rbtree;
+ ngx_rbtree_node_t addr_sentinel;
+
+ ngx_queue_t name_resend_queue;
+ ngx_queue_t addr_resend_queue;
+
+ ngx_queue_t name_expire_queue;
+ ngx_queue_t addr_expire_queue;
+
+ time_t resend_timeout;
+ time_t expire;
+ time_t valid;
+
+ ngx_uint_t log_level;
+} ngx_resolver_t;
+
+
+struct ngx_resolver_ctx_s {
+ ngx_resolver_ctx_t *next;
+ ngx_resolver_t *resolver;
+ ngx_udp_connection_t *udp_connection;
+
+ /* ident must be after 3 pointers */
+ ngx_int_t ident;
+
+ ngx_int_t state;
+ ngx_int_t type;
+ ngx_str_t name;
+
+ ngx_uint_t naddrs;
+ in_addr_t *addrs;
+ in_addr_t addr;
+
+ /* TODO: DNS peers balancer ctx */
+
+ ngx_resolver_handler_pt handler;
+ void *data;
+ ngx_msec_t timeout;
+
+ ngx_uint_t quick; /* unsigned quick:1; */
+ ngx_uint_t recursion;
+ ngx_event_t *event;
+};
+
+
+ngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_addr_t *addr);
+ngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r,
+ ngx_resolver_ctx_t *temp);
+ngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx);
+void ngx_resolve_name_done(ngx_resolver_ctx_t *ctx);
+ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx);
+void ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx);
+char *ngx_resolver_strerror(ngx_int_t err);
+
+
+#endif /* _NGX_RESOLVER_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_sha1.h b/usr.sbin/nginx/src/core/ngx_sha1.h
new file mode 100644
index 00000000000..f158fd4dc92
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_sha1.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_SHA1_H_INCLUDED_
+#define _NGX_SHA1_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_OPENSSL_SHA1_H)
+#include <openssl/sha.h>
+#else
+#include <sha.h>
+#endif
+
+
+typedef SHA_CTX ngx_sha1_t;
+
+
+#define ngx_sha1_init SHA1_Init
+#define ngx_sha1_update SHA1_Update
+#define ngx_sha1_final SHA1_Final
+
+
+#endif /* _NGX_SHA1_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_shmtx.c b/usr.sbin/nginx/src/core/ngx_shmtx.c
new file mode 100644
index 00000000000..3b429c4f979
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_shmtx.c
@@ -0,0 +1,283 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#if (NGX_HAVE_ATOMIC_OPS)
+
+
+ngx_int_t
+ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name)
+{
+ mtx->lock = addr;
+
+ if (mtx->spin == (ngx_uint_t) -1) {
+ return NGX_OK;
+ }
+
+ mtx->spin = 2048;
+
+#if (NGX_HAVE_POSIX_SEM)
+
+ if (sem_init(&mtx->sem, 1, 0) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+ "sem_init() failed");
+ } else {
+ mtx->semaphore = 1;
+ }
+
+#endif
+
+ return NGX_OK;
+}
+
+
+void
+ngx_shmtx_destory(ngx_shmtx_t *mtx)
+{
+#if (NGX_HAVE_POSIX_SEM)
+
+ if (mtx->semaphore) {
+ if (sem_destroy(&mtx->sem) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+ "sem_destroy() failed");
+ }
+ }
+
+#endif
+}
+
+
+ngx_uint_t
+ngx_shmtx_trylock(ngx_shmtx_t *mtx)
+{
+ ngx_atomic_uint_t val;
+
+ val = *mtx->lock;
+
+ return ((val & 0x80000000) == 0
+ && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000));
+}
+
+
+void
+ngx_shmtx_lock(ngx_shmtx_t *mtx)
+{
+ ngx_uint_t i, n;
+ ngx_atomic_uint_t val;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx lock");
+
+ for ( ;; ) {
+
+ val = *mtx->lock;
+
+ if ((val & 0x80000000) == 0
+ && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000))
+ {
+ return;
+ }
+
+ if (ngx_ncpu > 1) {
+
+ for (n = 1; n < mtx->spin; n <<= 1) {
+
+ for (i = 0; i < n; i++) {
+ ngx_cpu_pause();
+ }
+
+ val = *mtx->lock;
+
+ if ((val & 0x80000000) == 0
+ && ngx_atomic_cmp_set(mtx->lock, val, val | 0x80000000))
+ {
+ return;
+ }
+ }
+ }
+
+#if (NGX_HAVE_POSIX_SEM)
+
+ if (mtx->semaphore) {
+ val = *mtx->lock;
+
+ if ((val & 0x80000000)
+ && ngx_atomic_cmp_set(mtx->lock, val, val + 1))
+ {
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "shmtx wait %XA", val);
+
+ while (sem_wait(&mtx->sem) == -1) {
+ ngx_err_t err;
+
+ err = ngx_errno;
+
+ if (err != NGX_EINTR) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
+ "sem_wait() failed while waiting on shmtx");
+ break;
+ }
+ }
+
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "shmtx awoke");
+ }
+
+ continue;
+ }
+
+#endif
+
+ ngx_sched_yield();
+ }
+}
+
+
+void
+ngx_shmtx_unlock(ngx_shmtx_t *mtx)
+{
+ ngx_atomic_uint_t val, old, wait;
+
+ if (mtx->spin != (ngx_uint_t) -1) {
+ ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, "shmtx unlock");
+ }
+
+ for ( ;; ) {
+
+ old = *mtx->lock;
+ wait = old & 0x7fffffff;
+ val = wait ? wait - 1 : 0;
+
+ if (ngx_atomic_cmp_set(mtx->lock, old, val)) {
+ break;
+ }
+ }
+
+#if (NGX_HAVE_POSIX_SEM)
+
+ if (wait == 0 || !mtx->semaphore) {
+ return;
+ }
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,
+ "shmtx wake %XA", old);
+
+ if (sem_post(&mtx->sem) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+ "sem_post() failed while wake shmtx");
+ }
+
+#endif
+}
+
+
+#else
+
+
+ngx_int_t
+ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name)
+{
+ if (mtx->name) {
+
+ if (ngx_strcmp(name, mtx->name) == 0) {
+ mtx->name = name;
+ return NGX_OK;
+ }
+
+ ngx_shmtx_destory(mtx);
+ }
+
+ mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,
+ NGX_FILE_DEFAULT_ACCESS);
+
+ if (mtx->fd == NGX_INVALID_FILE) {
+ ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
+ ngx_open_file_n " \"%s\" failed", name);
+ return NGX_ERROR;
+ }
+
+ if (ngx_delete_file(name) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+ ngx_delete_file_n " \"%s\" failed", name);
+ }
+
+ mtx->name = name;
+
+ return NGX_OK;
+}
+
+
+void
+ngx_shmtx_destory(ngx_shmtx_t *mtx)
+{
+ if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", mtx->name);
+ }
+}
+
+
+ngx_uint_t
+ngx_shmtx_trylock(ngx_shmtx_t *mtx)
+{
+ ngx_err_t err;
+
+ err = ngx_trylock_fd(mtx->fd);
+
+ if (err == 0) {
+ return 1;
+ }
+
+ if (err == NGX_EAGAIN) {
+ return 0;
+ }
+
+#if __osf__ /* Tru64 UNIX */
+
+ if (err == NGX_EACCESS) {
+ return 0;
+ }
+
+#endif
+
+ ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name);
+
+ return 0;
+}
+
+
+void
+ngx_shmtx_lock(ngx_shmtx_t *mtx)
+{
+ ngx_err_t err;
+
+ err = ngx_lock_fd(mtx->fd);
+
+ if (err == 0) {
+ return;
+ }
+
+ ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name);
+}
+
+
+void
+ngx_shmtx_unlock(ngx_shmtx_t *mtx)
+{
+ ngx_err_t err;
+
+ err = ngx_unlock_fd(mtx->fd);
+
+ if (err == 0) {
+ return;
+ }
+
+ ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name);
+}
+
+#endif
diff --git a/usr.sbin/nginx/src/core/ngx_shmtx.h b/usr.sbin/nginx/src/core/ngx_shmtx.h
new file mode 100644
index 00000000000..714f73aa624
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_shmtx.h
@@ -0,0 +1,37 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_SHMTX_H_INCLUDED_
+#define _NGX_SHMTX_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+#if (NGX_HAVE_ATOMIC_OPS)
+ ngx_atomic_t *lock;
+#if (NGX_HAVE_POSIX_SEM)
+ ngx_uint_t semaphore;
+ sem_t sem;
+#endif
+#else
+ ngx_fd_t fd;
+ u_char *name;
+#endif
+ ngx_uint_t spin;
+} ngx_shmtx_t;
+
+
+ngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, void *addr, u_char *name);
+void ngx_shmtx_destory(ngx_shmtx_t *mtx);
+ngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);
+void ngx_shmtx_lock(ngx_shmtx_t *mtx);
+void ngx_shmtx_unlock(ngx_shmtx_t *mtx);
+
+
+#endif /* _NGX_SHMTX_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_slab.c b/usr.sbin/nginx/src/core/ngx_slab.c
new file mode 100644
index 00000000000..870c59bad3b
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_slab.c
@@ -0,0 +1,700 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+#define NGX_SLAB_PAGE_MASK 3
+#define NGX_SLAB_PAGE 0
+#define NGX_SLAB_BIG 1
+#define NGX_SLAB_EXACT 2
+#define NGX_SLAB_SMALL 3
+
+#if (NGX_PTR_SIZE == 4)
+
+#define NGX_SLAB_PAGE_FREE 0
+#define NGX_SLAB_PAGE_BUSY 0xffffffff
+#define NGX_SLAB_PAGE_START 0x80000000
+
+#define NGX_SLAB_SHIFT_MASK 0x0000000f
+#define NGX_SLAB_MAP_MASK 0xffff0000
+#define NGX_SLAB_MAP_SHIFT 16
+
+#define NGX_SLAB_BUSY 0xffffffff
+
+#else /* (NGX_PTR_SIZE == 8) */
+
+#define NGX_SLAB_PAGE_FREE 0
+#define NGX_SLAB_PAGE_BUSY 0xffffffffffffffff
+#define NGX_SLAB_PAGE_START 0x8000000000000000
+
+#define NGX_SLAB_SHIFT_MASK 0x000000000000000f
+#define NGX_SLAB_MAP_MASK 0xffffffff00000000
+#define NGX_SLAB_MAP_SHIFT 32
+
+#define NGX_SLAB_BUSY 0xffffffffffffffff
+
+#endif
+
+
+#if (NGX_DEBUG_MALLOC)
+
+#define ngx_slab_junk(p, size) ngx_memset(p, 0xD0, size)
+
+#else
+
+#if (NGX_FREEBSD)
+
+#define ngx_slab_junk(p, size) \
+ if (ngx_freebsd_debug_malloc) ngx_memset(p, 0xD0, size)
+
+#else
+
+#define ngx_slab_junk(p, size)
+
+#endif
+
+#endif
+
+static ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool,
+ ngx_uint_t pages);
+static void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
+ ngx_uint_t pages);
+static void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,
+ char *text);
+
+
+static ngx_uint_t ngx_slab_max_size;
+static ngx_uint_t ngx_slab_exact_size;
+static ngx_uint_t ngx_slab_exact_shift;
+
+
+void
+ngx_slab_init(ngx_slab_pool_t *pool)
+{
+ u_char *p;
+ size_t size;
+ ngx_int_t m;
+ ngx_uint_t i, n, pages;
+ ngx_slab_page_t *slots;
+
+ /* STUB */
+ if (ngx_slab_max_size == 0) {
+ ngx_slab_max_size = ngx_pagesize / 2;
+ ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));
+ for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {
+ /* void */
+ }
+ }
+ /**/
+
+ pool->min_size = 1 << pool->min_shift;
+
+ p = (u_char *) pool + sizeof(ngx_slab_pool_t);
+ size = pool->end - p;
+
+ ngx_slab_junk(p, size);
+
+ slots = (ngx_slab_page_t *) p;
+ n = ngx_pagesize_shift - pool->min_shift;
+
+ for (i = 0; i < n; i++) {
+ slots[i].slab = 0;
+ slots[i].next = &slots[i];
+ slots[i].prev = 0;
+ }
+
+ p += n * sizeof(ngx_slab_page_t);
+
+ pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));
+
+ ngx_memzero(p, pages * sizeof(ngx_slab_page_t));
+
+ pool->pages = (ngx_slab_page_t *) p;
+
+ pool->free.prev = 0;
+ pool->free.next = (ngx_slab_page_t *) p;
+
+ pool->pages->slab = pages;
+ pool->pages->next = &pool->free;
+ pool->pages->prev = (uintptr_t) &pool->free;
+
+ pool->start = (u_char *)
+ ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),
+ ngx_pagesize);
+
+ m = pages - (pool->end - pool->start) / ngx_pagesize;
+ if (m > 0) {
+ pages -= m;
+ pool->pages->slab = pages;
+ }
+
+ pool->log_ctx = &pool->zero;
+ pool->zero = '\0';
+}
+
+
+void *
+ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)
+{
+ void *p;
+
+ ngx_shmtx_lock(&pool->mutex);
+
+ p = ngx_slab_alloc_locked(pool, size);
+
+ ngx_shmtx_unlock(&pool->mutex);
+
+ return p;
+}
+
+
+void *
+ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)
+{
+ size_t s;
+ uintptr_t p, n, m, mask, *bitmap;
+ ngx_uint_t i, slot, shift, map;
+ ngx_slab_page_t *page, *prev, *slots;
+
+ if (size >= ngx_slab_max_size) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
+ "slab alloc: %uz", size);
+
+ page = ngx_slab_alloc_pages(pool, (size + ngx_pagesize - 1)
+ >> ngx_pagesize_shift);
+ if (page) {
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ p += (uintptr_t) pool->start;
+
+ } else {
+ p = 0;
+ }
+
+ goto done;
+ }
+
+ if (size > pool->min_size) {
+ shift = 1;
+ for (s = size - 1; s >>= 1; shift++) { /* void */ }
+ slot = shift - pool->min_shift;
+
+ } else {
+ size = pool->min_size;
+ shift = pool->min_shift;
+ slot = 0;
+ }
+
+ ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,
+ "slab alloc: %uz slot: %ui", size, slot);
+
+ slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));
+ page = slots[slot].next;
+
+ if (page->next != page) {
+
+ if (shift < ngx_slab_exact_shift) {
+
+ do {
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ bitmap = (uintptr_t *) (pool->start + p);
+
+ map = (1 << (ngx_pagesize_shift - shift))
+ / (sizeof(uintptr_t) * 8);
+
+ for (n = 0; n < map; n++) {
+
+ if (bitmap[n] != NGX_SLAB_BUSY) {
+
+ for (m = 1, i = 0; m; m <<= 1, i++) {
+ if ((bitmap[n] & m)) {
+ continue;
+ }
+
+ bitmap[n] |= m;
+
+ i = ((n * sizeof(uintptr_t) * 8) << shift)
+ + (i << shift);
+
+ if (bitmap[n] == NGX_SLAB_BUSY) {
+ for (n = n + 1; n < map; n++) {
+ if (bitmap[n] != NGX_SLAB_BUSY) {
+ p = (uintptr_t) bitmap + i;
+
+ goto done;
+ }
+ }
+
+ prev = (ngx_slab_page_t *)
+ (page->prev & ~NGX_SLAB_PAGE_MASK);
+ prev->next = page->next;
+ page->next->prev = page->prev;
+
+ page->next = NULL;
+ page->prev = NGX_SLAB_SMALL;
+ }
+
+ p = (uintptr_t) bitmap + i;
+
+ goto done;
+ }
+ }
+ }
+
+ page = page->next;
+
+ } while (page);
+
+ } else if (shift == ngx_slab_exact_shift) {
+
+ do {
+ if (page->slab != NGX_SLAB_BUSY) {
+
+ for (m = 1, i = 0; m; m <<= 1, i++) {
+ if ((page->slab & m)) {
+ continue;
+ }
+
+ page->slab |= m;
+
+ if (page->slab == NGX_SLAB_BUSY) {
+ prev = (ngx_slab_page_t *)
+ (page->prev & ~NGX_SLAB_PAGE_MASK);
+ prev->next = page->next;
+ page->next->prev = page->prev;
+
+ page->next = NULL;
+ page->prev = NGX_SLAB_EXACT;
+ }
+
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ p += i << shift;
+ p += (uintptr_t) pool->start;
+
+ goto done;
+ }
+ }
+
+ page = page->next;
+
+ } while (page);
+
+ } else { /* shift > ngx_slab_exact_shift */
+
+ n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);
+ n = 1 << n;
+ n = ((uintptr_t) 1 << n) - 1;
+ mask = n << NGX_SLAB_MAP_SHIFT;
+
+ do {
+ if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {
+
+ for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;
+ m & mask;
+ m <<= 1, i++)
+ {
+ if ((page->slab & m)) {
+ continue;
+ }
+
+ page->slab |= m;
+
+ if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {
+ prev = (ngx_slab_page_t *)
+ (page->prev & ~NGX_SLAB_PAGE_MASK);
+ prev->next = page->next;
+ page->next->prev = page->prev;
+
+ page->next = NULL;
+ page->prev = NGX_SLAB_BIG;
+ }
+
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ p += i << shift;
+ p += (uintptr_t) pool->start;
+
+ goto done;
+ }
+ }
+
+ page = page->next;
+
+ } while (page);
+ }
+ }
+
+ page = ngx_slab_alloc_pages(pool, 1);
+
+ if (page) {
+ if (shift < ngx_slab_exact_shift) {
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ bitmap = (uintptr_t *) (pool->start + p);
+
+ s = 1 << shift;
+ n = (1 << (ngx_pagesize_shift - shift)) / 8 / s;
+
+ if (n == 0) {
+ n = 1;
+ }
+
+ bitmap[0] = (2 << n) - 1;
+
+ map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
+
+ for (i = 1; i < map; i++) {
+ bitmap[i] = 0;
+ }
+
+ page->slab = shift;
+ page->next = &slots[slot];
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
+
+ slots[slot].next = page;
+
+ p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;
+ p += (uintptr_t) pool->start;
+
+ goto done;
+
+ } else if (shift == ngx_slab_exact_shift) {
+
+ page->slab = 1;
+ page->next = &slots[slot];
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
+
+ slots[slot].next = page;
+
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ p += (uintptr_t) pool->start;
+
+ goto done;
+
+ } else { /* shift > ngx_slab_exact_shift */
+
+ page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;
+ page->next = &slots[slot];
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
+
+ slots[slot].next = page;
+
+ p = (page - pool->pages) << ngx_pagesize_shift;
+ p += (uintptr_t) pool->start;
+
+ goto done;
+ }
+ }
+
+ p = 0;
+
+done:
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab alloc: %p", p);
+
+ return (void *) p;
+}
+
+
+void
+ngx_slab_free(ngx_slab_pool_t *pool, void *p)
+{
+ ngx_shmtx_lock(&pool->mutex);
+
+ ngx_slab_free_locked(pool, p);
+
+ ngx_shmtx_unlock(&pool->mutex);
+}
+
+
+void
+ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)
+{
+ size_t size;
+ uintptr_t slab, m, *bitmap;
+ ngx_uint_t n, type, slot, shift, map;
+ ngx_slab_page_t *slots, *page;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, "slab free: %p", p);
+
+ if ((u_char *) p < pool->start || (u_char *) p > pool->end) {
+ ngx_slab_error(pool, NGX_LOG_ALERT, "ngx_slab_free(): outside of pool");
+ goto fail;
+ }
+
+ n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
+ page = &pool->pages[n];
+ slab = page->slab;
+ type = page->prev & NGX_SLAB_PAGE_MASK;
+
+ switch (type) {
+
+ case NGX_SLAB_SMALL:
+
+ shift = slab & NGX_SLAB_SHIFT_MASK;
+ size = 1 << shift;
+
+ if ((uintptr_t) p & (size - 1)) {
+ goto wrong_chunk;
+ }
+
+ n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;
+ m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));
+ n /= (sizeof(uintptr_t) * 8);
+ bitmap = (uintptr_t *) ((uintptr_t) p & ~(ngx_pagesize - 1));
+
+ if (bitmap[n] & m) {
+
+ if (page->next == NULL) {
+ slots = (ngx_slab_page_t *)
+ ((u_char *) pool + sizeof(ngx_slab_pool_t));
+ slot = shift - pool->min_shift;
+
+ page->next = slots[slot].next;
+ slots[slot].next = page;
+
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;
+ page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;
+ }
+
+ bitmap[n] &= ~m;
+
+ n = (1 << (ngx_pagesize_shift - shift)) / 8 / (1 << shift);
+
+ if (n == 0) {
+ n = 1;
+ }
+
+ if (bitmap[0] & ~(((uintptr_t) 1 << n) - 1)) {
+ goto done;
+ }
+
+ map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);
+
+ for (n = 1; n < map; n++) {
+ if (bitmap[n]) {
+ goto done;
+ }
+ }
+
+ ngx_slab_free_pages(pool, page, 1);
+
+ goto done;
+ }
+
+ goto chunk_already_free;
+
+ case NGX_SLAB_EXACT:
+
+ m = (uintptr_t) 1 <<
+ (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift);
+ size = ngx_slab_exact_size;
+
+ if ((uintptr_t) p & (size - 1)) {
+ goto wrong_chunk;
+ }
+
+ if (slab & m) {
+ if (slab == NGX_SLAB_BUSY) {
+ slots = (ngx_slab_page_t *)
+ ((u_char *) pool + sizeof(ngx_slab_pool_t));
+ slot = ngx_slab_exact_shift - pool->min_shift;
+
+ page->next = slots[slot].next;
+ slots[slot].next = page;
+
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;
+ page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;
+ }
+
+ page->slab &= ~m;
+
+ if (page->slab) {
+ goto done;
+ }
+
+ ngx_slab_free_pages(pool, page, 1);
+
+ goto done;
+ }
+
+ goto chunk_already_free;
+
+ case NGX_SLAB_BIG:
+
+ shift = slab & NGX_SLAB_SHIFT_MASK;
+ size = 1 << shift;
+
+ if ((uintptr_t) p & (size - 1)) {
+ goto wrong_chunk;
+ }
+
+ m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift)
+ + NGX_SLAB_MAP_SHIFT);
+
+ if (slab & m) {
+
+ if (page->next == NULL) {
+ slots = (ngx_slab_page_t *)
+ ((u_char *) pool + sizeof(ngx_slab_pool_t));
+ slot = shift - pool->min_shift;
+
+ page->next = slots[slot].next;
+ slots[slot].next = page;
+
+ page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;
+ page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;
+ }
+
+ page->slab &= ~m;
+
+ if (page->slab & NGX_SLAB_MAP_MASK) {
+ goto done;
+ }
+
+ ngx_slab_free_pages(pool, page, 1);
+
+ goto done;
+ }
+
+ goto chunk_already_free;
+
+ case NGX_SLAB_PAGE:
+
+ if ((uintptr_t) p & (ngx_pagesize - 1)) {
+ goto wrong_chunk;
+ }
+
+ if (slab == NGX_SLAB_PAGE_FREE) {
+ ngx_slab_error(pool, NGX_LOG_ALERT,
+ "ngx_slab_free(): page is already free");
+ goto fail;
+ }
+
+ if (slab == NGX_SLAB_PAGE_BUSY) {
+ ngx_slab_error(pool, NGX_LOG_ALERT,
+ "ngx_slab_free(): pointer to wrong page");
+ goto fail;
+ }
+
+ n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;
+ size = slab & ~NGX_SLAB_PAGE_START;
+
+ ngx_slab_free_pages(pool, &pool->pages[n], size);
+
+ ngx_slab_junk(p, size << ngx_pagesize_shift);
+
+ return;
+ }
+
+ /* not reached */
+
+ return;
+
+done:
+
+ ngx_slab_junk(p, size);
+
+ return;
+
+wrong_chunk:
+
+ ngx_slab_error(pool, NGX_LOG_ALERT,
+ "ngx_slab_free(): pointer to wrong chunk");
+
+ goto fail;
+
+chunk_already_free:
+
+ ngx_slab_error(pool, NGX_LOG_ALERT,
+ "ngx_slab_free(): chunk is already free");
+
+fail:
+
+ return;
+}
+
+
+static ngx_slab_page_t *
+ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)
+{
+ ngx_slab_page_t *page, *p;
+
+ for (page = pool->free.next; page != &pool->free; page = page->next) {
+
+ if (page->slab >= pages) {
+
+ if (page->slab > pages) {
+ page[pages].slab = page->slab - pages;
+ page[pages].next = page->next;
+ page[pages].prev = page->prev;
+
+ p = (ngx_slab_page_t *) page->prev;
+ p->next = &page[pages];
+ page->next->prev = (uintptr_t) &page[pages];
+
+ } else {
+ p = (ngx_slab_page_t *) page->prev;
+ p->next = page->next;
+ page->next->prev = page->prev;
+ }
+
+ page->slab = pages | NGX_SLAB_PAGE_START;
+ page->next = NULL;
+ page->prev = NGX_SLAB_PAGE;
+
+ if (--pages == 0) {
+ return page;
+ }
+
+ for (p = page + 1; pages; pages--) {
+ p->slab = NGX_SLAB_PAGE_BUSY;
+ p->next = NULL;
+ p->prev = NGX_SLAB_PAGE;
+ p++;
+ }
+
+ return page;
+ }
+ }
+
+ ngx_slab_error(pool, NGX_LOG_CRIT, "ngx_slab_alloc() failed: no memory");
+
+ return NULL;
+}
+
+
+static void
+ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,
+ ngx_uint_t pages)
+{
+ ngx_slab_page_t *prev;
+
+ page->slab = pages--;
+
+ if (pages) {
+ ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));
+ }
+
+ if (page->next) {
+ prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);
+ prev->next = page->next;
+ page->next->prev = page->prev;
+ }
+
+ page->prev = (uintptr_t) &pool->free;
+ page->next = pool->free.next;
+
+ page->next->prev = (uintptr_t) page;
+
+ pool->free.next = page;
+}
+
+
+static void
+ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)
+{
+ ngx_log_error(level, ngx_cycle->log, 0, "%s%s", text, pool->log_ctx);
+}
diff --git a/usr.sbin/nginx/src/core/ngx_slab.h b/usr.sbin/nginx/src/core/ngx_slab.h
new file mode 100644
index 00000000000..291e1b51e13
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_slab.h
@@ -0,0 +1,53 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_SLAB_H_INCLUDED_
+#define _NGX_SLAB_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct ngx_slab_page_s ngx_slab_page_t;
+
+struct ngx_slab_page_s {
+ uintptr_t slab;
+ ngx_slab_page_t *next;
+ uintptr_t prev;
+};
+
+
+typedef struct {
+ ngx_atomic_t lock;
+
+ size_t min_size;
+ size_t min_shift;
+
+ ngx_slab_page_t *pages;
+ ngx_slab_page_t free;
+
+ u_char *start;
+ u_char *end;
+
+ ngx_shmtx_t mutex;
+
+ u_char *log_ctx;
+ u_char zero;
+
+ void *data;
+ void *addr;
+} ngx_slab_pool_t;
+
+
+void ngx_slab_init(ngx_slab_pool_t *pool);
+void *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);
+void *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);
+void ngx_slab_free(ngx_slab_pool_t *pool, void *p);
+void ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);
+
+
+#endif /* _NGX_SLAB_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_spinlock.c b/usr.sbin/nginx/src/core/ngx_spinlock.c
new file mode 100644
index 00000000000..f6c80f76941
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_spinlock.c
@@ -0,0 +1,52 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+void
+ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)
+{
+
+#if (NGX_HAVE_ATOMIC_OPS)
+
+ ngx_uint_t i, n;
+
+ for ( ;; ) {
+
+ if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
+ return;
+ }
+
+ if (ngx_ncpu > 1) {
+
+ for (n = 1; n < spin; n <<= 1) {
+
+ for (i = 0; i < n; i++) {
+ ngx_cpu_pause();
+ }
+
+ if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {
+ return;
+ }
+ }
+ }
+
+ ngx_sched_yield();
+ }
+
+#else
+
+#if (NGX_THREADS)
+
+#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !
+
+#endif
+
+#endif
+
+}
diff --git a/usr.sbin/nginx/src/core/ngx_string.c b/usr.sbin/nginx/src/core/ngx_string.c
new file mode 100644
index 00000000000..4dcfe01ebfc
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_string.c
@@ -0,0 +1,1807 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+static u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,
+ u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);
+static ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src,
+ const u_char *basis);
+
+
+void
+ngx_strlow(u_char *dst, u_char *src, size_t n)
+{
+ while (n) {
+ *dst = ngx_tolower(*src);
+ dst++;
+ src++;
+ n--;
+ }
+}
+
+
+u_char *
+ngx_cpystrn(u_char *dst, u_char *src, size_t n)
+{
+ if (n == 0) {
+ return dst;
+ }
+
+ while (--n) {
+ *dst = *src;
+
+ if (*dst == '\0') {
+ return dst;
+ }
+
+ dst++;
+ src++;
+ }
+
+ *dst = '\0';
+
+ return dst;
+}
+
+
+u_char *
+ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)
+{
+ u_char *dst;
+
+ dst = ngx_pnalloc(pool, src->len);
+ if (dst == NULL) {
+ return NULL;
+ }
+
+ ngx_memcpy(dst, src->data, src->len);
+
+ return dst;
+}
+
+
+/*
+ * supported formats:
+ * %[0][width][x][X]O off_t
+ * %[0][width]T time_t
+ * %[0][width][u][x|X]z ssize_t/size_t
+ * %[0][width][u][x|X]d int/u_int
+ * %[0][width][u][x|X]l long
+ * %[0][width|m][u][x|X]i ngx_int_t/ngx_uint_t
+ * %[0][width][u][x|X]D int32_t/uint32_t
+ * %[0][width][u][x|X]L int64_t/uint64_t
+ * %[0][width|m][u][x|X]A ngx_atomic_int_t/ngx_atomic_uint_t
+ * %[0][width][.width]f double, max valid number fits to %18.15f
+ * %P ngx_pid_t
+ * %M ngx_msec_t
+ * %r rlim_t
+ * %p void *
+ * %V ngx_str_t *
+ * %v ngx_variable_value_t *
+ * %s null-terminated string
+ * %*s length and string
+ * %Z '\0'
+ * %N '\n'
+ * %c char
+ * %% %
+ *
+ * reserved:
+ * %t ptrdiff_t
+ * %S null-terminated wchar string
+ * %C wchar
+ */
+
+
+u_char * ngx_cdecl
+ngx_sprintf(u_char *buf, const char *fmt, ...)
+{
+ u_char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(buf, (void *) -1, fmt, args);
+ va_end(args);
+
+ return p;
+}
+
+
+u_char * ngx_cdecl
+ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...)
+{
+ u_char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(buf, buf + max, fmt, args);
+ va_end(args);
+
+ return p;
+}
+
+
+u_char * ngx_cdecl
+ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)
+{
+ u_char *p;
+ va_list args;
+
+ va_start(args, fmt);
+ p = ngx_vslprintf(buf, last, fmt, args);
+ va_end(args);
+
+ return p;
+}
+
+
+u_char *
+ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)
+{
+ u_char *p, zero;
+ int d;
+ double f, scale;
+ size_t len, slen;
+ int64_t i64;
+ uint64_t ui64;
+ ngx_msec_t ms;
+ ngx_uint_t width, sign, hex, max_width, frac_width, n;
+ ngx_str_t *v;
+ ngx_variable_value_t *vv;
+
+ while (*fmt && buf < last) {
+
+ /*
+ * "buf < last" means that we could copy at least one character:
+ * the plain character, "%%", "%c", and minus without the checking
+ */
+
+ if (*fmt == '%') {
+
+ i64 = 0;
+ ui64 = 0;
+
+ zero = (u_char) ((*++fmt == '0') ? '0' : ' ');
+ width = 0;
+ sign = 1;
+ hex = 0;
+ max_width = 0;
+ frac_width = 0;
+ slen = (size_t) -1;
+
+ while (*fmt >= '0' && *fmt <= '9') {
+ width = width * 10 + *fmt++ - '0';
+ }
+
+
+ for ( ;; ) {
+ switch (*fmt) {
+
+ case 'u':
+ sign = 0;
+ fmt++;
+ continue;
+
+ case 'm':
+ max_width = 1;
+ fmt++;
+ continue;
+
+ case 'X':
+ hex = 2;
+ sign = 0;
+ fmt++;
+ continue;
+
+ case 'x':
+ hex = 1;
+ sign = 0;
+ fmt++;
+ continue;
+
+ case '.':
+ fmt++;
+
+ while (*fmt >= '0' && *fmt <= '9') {
+ frac_width = frac_width * 10 + *fmt++ - '0';
+ }
+
+ break;
+
+ case '*':
+ slen = va_arg(args, size_t);
+ fmt++;
+ continue;
+
+ default:
+ break;
+ }
+
+ break;
+ }
+
+
+ switch (*fmt) {
+
+ case 'V':
+ v = va_arg(args, ngx_str_t *);
+
+ len = ngx_min(((size_t) (last - buf)), v->len);
+ buf = ngx_cpymem(buf, v->data, len);
+ fmt++;
+
+ continue;
+
+ case 'v':
+ vv = va_arg(args, ngx_variable_value_t *);
+
+ len = ngx_min(((size_t) (last - buf)), vv->len);
+ buf = ngx_cpymem(buf, vv->data, len);
+ fmt++;
+
+ continue;
+
+ case 's':
+ p = va_arg(args, u_char *);
+
+ if (slen == (size_t) -1) {
+ while (*p && buf < last) {
+ *buf++ = *p++;
+ }
+
+ } else {
+ len = ngx_min(((size_t) (last - buf)), slen);
+ buf = ngx_cpymem(buf, p, len);
+ }
+
+ fmt++;
+
+ continue;
+
+ case 'O':
+ i64 = (int64_t) va_arg(args, off_t);
+ sign = 1;
+ break;
+
+ case 'P':
+ i64 = (int64_t) va_arg(args, ngx_pid_t);
+ sign = 1;
+ break;
+
+ case 'T':
+ i64 = (int64_t) va_arg(args, time_t);
+ sign = 1;
+ break;
+
+ case 'M':
+ ms = (ngx_msec_t) va_arg(args, ngx_msec_t);
+ if ((ngx_msec_int_t) ms == -1) {
+ sign = 1;
+ i64 = -1;
+ } else {
+ sign = 0;
+ ui64 = (uint64_t) ms;
+ }
+ break;
+
+ case 'z':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, ssize_t);
+ } else {
+ ui64 = (uint64_t) va_arg(args, size_t);
+ }
+ break;
+
+ case 'i':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, ngx_int_t);
+ } else {
+ ui64 = (uint64_t) va_arg(args, ngx_uint_t);
+ }
+
+ if (max_width) {
+ width = NGX_INT_T_LEN;
+ }
+
+ break;
+
+ case 'd':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, int);
+ } else {
+ ui64 = (uint64_t) va_arg(args, u_int);
+ }
+ break;
+
+ case 'l':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, long);
+ } else {
+ ui64 = (uint64_t) va_arg(args, u_long);
+ }
+ break;
+
+ case 'D':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, int32_t);
+ } else {
+ ui64 = (uint64_t) va_arg(args, uint32_t);
+ }
+ break;
+
+ case 'L':
+ if (sign) {
+ i64 = va_arg(args, int64_t);
+ } else {
+ ui64 = va_arg(args, uint64_t);
+ }
+ break;
+
+ case 'A':
+ if (sign) {
+ i64 = (int64_t) va_arg(args, ngx_atomic_int_t);
+ } else {
+ ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);
+ }
+
+ if (max_width) {
+ width = NGX_ATOMIC_T_LEN;
+ }
+
+ break;
+
+ case 'f':
+ f = va_arg(args, double);
+
+ if (f < 0) {
+ *buf++ = '-';
+ f = -f;
+ }
+
+ ui64 = (int64_t) f;
+
+ buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);
+
+ if (frac_width) {
+
+ if (buf < last) {
+ *buf++ = '.';
+ }
+
+ scale = 1.0;
+
+ for (n = frac_width; n; n--) {
+ scale *= 10.0;
+ }
+
+ /*
+ * (int64_t) cast is required for msvc6:
+ * it can not convert uint64_t to double
+ */
+ ui64 = (uint64_t) ((f - (int64_t) ui64) * scale + 0.5);
+
+ buf = ngx_sprintf_num(buf, last, ui64, '0', 0, frac_width);
+ }
+
+ fmt++;
+
+ continue;
+
+#if !(NGX_WIN32)
+ case 'r':
+ i64 = (int64_t) va_arg(args, rlim_t);
+ sign = 1;
+ break;
+#endif
+
+ case 'p':
+ ui64 = (uintptr_t) va_arg(args, void *);
+ hex = 2;
+ sign = 0;
+ zero = '0';
+ width = NGX_PTR_SIZE * 2;
+ break;
+
+ case 'c':
+ d = va_arg(args, int);
+ *buf++ = (u_char) (d & 0xff);
+ fmt++;
+
+ continue;
+
+ case 'Z':
+ *buf++ = '\0';
+ fmt++;
+
+ continue;
+
+ case 'N':
+#if (NGX_WIN32)
+ *buf++ = CR;
+#endif
+ *buf++ = LF;
+ fmt++;
+
+ continue;
+
+ case '%':
+ *buf++ = '%';
+ fmt++;
+
+ continue;
+
+ default:
+ *buf++ = *fmt++;
+
+ continue;
+ }
+
+ if (sign) {
+ if (i64 < 0) {
+ *buf++ = '-';
+ ui64 = (uint64_t) -i64;
+
+ } else {
+ ui64 = (uint64_t) i64;
+ }
+ }
+
+ buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);
+
+ fmt++;
+
+ } else {
+ *buf++ = *fmt++;
+ }
+ }
+
+ return buf;
+}
+
+
+static u_char *
+ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,
+ ngx_uint_t hexadecimal, ngx_uint_t width)
+{
+ u_char *p, temp[NGX_INT64_LEN + 1];
+ /*
+ * we need temp[NGX_INT64_LEN] only,
+ * but icc issues the warning
+ */
+ size_t len;
+ uint32_t ui32;
+ static u_char hex[] = "0123456789abcdef";
+ static u_char HEX[] = "0123456789ABCDEF";
+
+ p = temp + NGX_INT64_LEN;
+
+ if (hexadecimal == 0) {
+
+ if (ui64 <= NGX_MAX_UINT32_VALUE) {
+
+ /*
+ * To divide 64-bit numbers and to find remainders
+ * on the x86 platform gcc and icc call the libc functions
+ * [u]divdi3() and [u]moddi3(), they call another function
+ * in its turn. On FreeBSD it is the qdivrem() function,
+ * its source code is about 170 lines of the code.
+ * The glibc counterpart is about 150 lines of the code.
+ *
+ * For 32-bit numbers and some divisors gcc and icc use
+ * a inlined multiplication and shifts. For example,
+ * unsigned "i32 / 10" is compiled to
+ *
+ * (i32 * 0xCCCCCCCD) >> 35
+ */
+
+ ui32 = (uint32_t) ui64;
+
+ do {
+ *--p = (u_char) (ui32 % 10 + '0');
+ } while (ui32 /= 10);
+
+ } else {
+ do {
+ *--p = (u_char) (ui64 % 10 + '0');
+ } while (ui64 /= 10);
+ }
+
+ } else if (hexadecimal == 1) {
+
+ do {
+
+ /* the "(uint32_t)" cast disables the BCC's warning */
+ *--p = hex[(uint32_t) (ui64 & 0xf)];
+
+ } while (ui64 >>= 4);
+
+ } else { /* hexadecimal == 2 */
+
+ do {
+
+ /* the "(uint32_t)" cast disables the BCC's warning */
+ *--p = HEX[(uint32_t) (ui64 & 0xf)];
+
+ } while (ui64 >>= 4);
+ }
+
+ /* zero or space padding */
+
+ len = (temp + NGX_INT64_LEN) - p;
+
+ while (len++ < width && buf < last) {
+ *buf++ = zero;
+ }
+
+ /* number safe copy */
+
+ len = (temp + NGX_INT64_LEN) - p;
+
+ if (buf + len > last) {
+ len = last - buf;
+ }
+
+ return ngx_cpymem(buf, p, len);
+}
+
+
+/*
+ * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,
+ * and implement our own ngx_strcasecmp()/ngx_strncasecmp()
+ * to avoid libc locale overhead. Besides, we use the ngx_uint_t's
+ * instead of the u_char's, because they are slightly faster.
+ */
+
+ngx_int_t
+ngx_strcasecmp(u_char *s1, u_char *s2)
+{
+ ngx_uint_t c1, c2;
+
+ for ( ;; ) {
+ c1 = (ngx_uint_t) *s1++;
+ c2 = (ngx_uint_t) *s2++;
+
+ c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+ c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+ if (c1 == c2) {
+
+ if (c1) {
+ continue;
+ }
+
+ return 0;
+ }
+
+ return c1 - c2;
+ }
+}
+
+
+ngx_int_t
+ngx_strncasecmp(u_char *s1, u_char *s2, size_t n)
+{
+ ngx_uint_t c1, c2;
+
+ while (n) {
+ c1 = (ngx_uint_t) *s1++;
+ c2 = (ngx_uint_t) *s2++;
+
+ c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+ c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+ if (c1 == c2) {
+
+ if (c1) {
+ n--;
+ continue;
+ }
+
+ return 0;
+ }
+
+ return c1 - c2;
+ }
+
+ return 0;
+}
+
+
+u_char *
+ngx_strnstr(u_char *s1, char *s2, size_t len)
+{
+ u_char c1, c2;
+ size_t n;
+
+ c2 = *(u_char *) s2++;
+
+ n = ngx_strlen(s2);
+
+ do {
+ do {
+ if (len-- == 0) {
+ return NULL;
+ }
+
+ c1 = *s1++;
+
+ if (c1 == 0) {
+ return NULL;
+ }
+
+ } while (c1 != c2);
+
+ if (n > len) {
+ return NULL;
+ }
+
+ } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);
+
+ return --s1;
+}
+
+
+/*
+ * ngx_strstrn() and ngx_strcasestrn() are intended to search for static
+ * substring with known length in null-terminated string. The argument n
+ * must be length of the second substring - 1.
+ */
+
+u_char *
+ngx_strstrn(u_char *s1, char *s2, size_t n)
+{
+ u_char c1, c2;
+
+ c2 = *(u_char *) s2++;
+
+ do {
+ do {
+ c1 = *s1++;
+
+ if (c1 == 0) {
+ return NULL;
+ }
+
+ } while (c1 != c2);
+
+ } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);
+
+ return --s1;
+}
+
+
+u_char *
+ngx_strcasestrn(u_char *s1, char *s2, size_t n)
+{
+ ngx_uint_t c1, c2;
+
+ c2 = (ngx_uint_t) *s2++;
+ c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+ do {
+ do {
+ c1 = (ngx_uint_t) *s1++;
+
+ if (c1 == 0) {
+ return NULL;
+ }
+
+ c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+
+ } while (c1 != c2);
+
+ } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0);
+
+ return --s1;
+}
+
+
+/*
+ * ngx_strlcasestrn() is intended to search for static substring
+ * with known length in string until the argument last. The argument n
+ * must be length of the second substring - 1.
+ */
+
+u_char *
+ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n)
+{
+ ngx_uint_t c1, c2;
+
+ c2 = (ngx_uint_t) *s2++;
+ c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+ last -= n;
+
+ do {
+ do {
+ if (s1 >= last) {
+ return NULL;
+ }
+
+ c1 = (ngx_uint_t) *s1++;
+
+ c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+
+ } while (c1 != c2);
+
+ } while (ngx_strncasecmp(s1, s2, n) != 0);
+
+ return --s1;
+}
+
+
+ngx_int_t
+ngx_rstrncmp(u_char *s1, u_char *s2, size_t n)
+{
+ if (n == 0) {
+ return 0;
+ }
+
+ n--;
+
+ for ( ;; ) {
+ if (s1[n] != s2[n]) {
+ return s1[n] - s2[n];
+ }
+
+ if (n == 0) {
+ return 0;
+ }
+
+ n--;
+ }
+}
+
+
+ngx_int_t
+ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n)
+{
+ u_char c1, c2;
+
+ if (n == 0) {
+ return 0;
+ }
+
+ n--;
+
+ for ( ;; ) {
+ c1 = s1[n];
+ if (c1 >= 'a' && c1 <= 'z') {
+ c1 -= 'a' - 'A';
+ }
+
+ c2 = s2[n];
+ if (c2 >= 'a' && c2 <= 'z') {
+ c2 -= 'a' - 'A';
+ }
+
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+
+ if (n == 0) {
+ return 0;
+ }
+
+ n--;
+ }
+}
+
+
+ngx_int_t
+ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2)
+{
+ size_t n;
+ ngx_int_t m, z;
+
+ if (n1 <= n2) {
+ n = n1;
+ z = -1;
+
+ } else {
+ n = n2;
+ z = 1;
+ }
+
+ m = ngx_memcmp(s1, s2, n);
+
+ if (m || n1 == n2) {
+ return m;
+ }
+
+ return z;
+}
+
+
+ngx_int_t
+ngx_dns_strcmp(u_char *s1, u_char *s2)
+{
+ ngx_uint_t c1, c2;
+
+ for ( ;; ) {
+ c1 = (ngx_uint_t) *s1++;
+ c2 = (ngx_uint_t) *s2++;
+
+ c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;
+ c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;
+
+ if (c1 == c2) {
+
+ if (c1) {
+ continue;
+ }
+
+ return 0;
+ }
+
+ /* in ASCII '.' > '-', but we need '.' to be the lowest character */
+
+ c1 = (c1 == '.') ? ' ' : c1;
+ c2 = (c2 == '.') ? ' ' : c2;
+
+ return c1 - c2;
+ }
+}
+
+
+ngx_int_t
+ngx_atoi(u_char *line, size_t n)
+{
+ ngx_int_t value;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ for (value = 0; n--; line++) {
+ if (*line < '0' || *line > '9') {
+ return NGX_ERROR;
+ }
+
+ value = value * 10 + (*line - '0');
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+/* parse a fixed point number, e.g., ngx_atofp("10.5", 4, 2) returns 1050 */
+
+ngx_int_t
+ngx_atofp(u_char *line, size_t n, size_t point)
+{
+ ngx_int_t value;
+ ngx_uint_t dot;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ dot = 0;
+
+ for (value = 0; n--; line++) {
+
+ if (point == 0) {
+ return NGX_ERROR;
+ }
+
+ if (*line == '.') {
+ if (dot) {
+ return NGX_ERROR;
+ }
+
+ dot = 1;
+ continue;
+ }
+
+ if (*line < '0' || *line > '9') {
+ return NGX_ERROR;
+ }
+
+ value = value * 10 + (*line - '0');
+ point -= dot;
+ }
+
+ while (point--) {
+ value = value * 10;
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+ssize_t
+ngx_atosz(u_char *line, size_t n)
+{
+ ssize_t value;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ for (value = 0; n--; line++) {
+ if (*line < '0' || *line > '9') {
+ return NGX_ERROR;
+ }
+
+ value = value * 10 + (*line - '0');
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+off_t
+ngx_atoof(u_char *line, size_t n)
+{
+ off_t value;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ for (value = 0; n--; line++) {
+ if (*line < '0' || *line > '9') {
+ return NGX_ERROR;
+ }
+
+ value = value * 10 + (*line - '0');
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+time_t
+ngx_atotm(u_char *line, size_t n)
+{
+ time_t value;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ for (value = 0; n--; line++) {
+ if (*line < '0' || *line > '9') {
+ return NGX_ERROR;
+ }
+
+ value = value * 10 + (*line - '0');
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+ngx_int_t
+ngx_hextoi(u_char *line, size_t n)
+{
+ u_char c, ch;
+ ngx_int_t value;
+
+ if (n == 0) {
+ return NGX_ERROR;
+ }
+
+ for (value = 0; n--; line++) {
+ ch = *line;
+
+ if (ch >= '0' && ch <= '9') {
+ value = value * 16 + (ch - '0');
+ continue;
+ }
+
+ c = (u_char) (ch | 0x20);
+
+ if (c >= 'a' && c <= 'f') {
+ value = value * 16 + (c - 'a' + 10);
+ continue;
+ }
+
+ return NGX_ERROR;
+ }
+
+ if (value < 0) {
+ return NGX_ERROR;
+
+ } else {
+ return value;
+ }
+}
+
+
+u_char *
+ngx_hex_dump(u_char *dst, u_char *src, size_t len)
+{
+ static u_char hex[] = "0123456789abcdef";
+
+ while (len--) {
+ *dst++ = hex[*src >> 4];
+ *dst++ = hex[*src++ & 0xf];
+ }
+
+ return dst;
+}
+
+
+void
+ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src)
+{
+ u_char *d, *s;
+ size_t len;
+ static u_char basis64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ len = src->len;
+ s = src->data;
+ d = dst->data;
+
+ while (len > 2) {
+ *d++ = basis64[(s[0] >> 2) & 0x3f];
+ *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
+ *d++ = basis64[((s[1] & 0x0f) << 2) | (s[2] >> 6)];
+ *d++ = basis64[s[2] & 0x3f];
+
+ s += 3;
+ len -= 3;
+ }
+
+ if (len) {
+ *d++ = basis64[(s[0] >> 2) & 0x3f];
+
+ if (len == 1) {
+ *d++ = basis64[(s[0] & 3) << 4];
+ *d++ = '=';
+
+ } else {
+ *d++ = basis64[((s[0] & 3) << 4) | (s[1] >> 4)];
+ *d++ = basis64[(s[1] & 0x0f) << 2];
+ }
+
+ *d++ = '=';
+ }
+
+ dst->len = d - dst->data;
+}
+
+
+ngx_int_t
+ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src)
+{
+ static u_char basis64[] = {
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77, 77, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
+ 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 77,
+ 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
+
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
+ };
+
+ return ngx_decode_base64_internal(dst, src, basis64);
+}
+
+
+ngx_int_t
+ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src)
+{
+ static u_char basis64[] = {
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 62, 77, 77,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 77, 77, 77, 77, 77, 77,
+ 77, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 77, 77, 77, 77, 63,
+ 77, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 77, 77, 77, 77, 77,
+
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77,
+ 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77
+ };
+
+ return ngx_decode_base64_internal(dst, src, basis64);
+}
+
+
+static ngx_int_t
+ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)
+{
+ size_t len;
+ u_char *d, *s;
+
+ for (len = 0; len < src->len; len++) {
+ if (src->data[len] == '=') {
+ break;
+ }
+
+ if (basis[src->data[len]] == 77) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (len % 4 == 1) {
+ return NGX_ERROR;
+ }
+
+ s = src->data;
+ d = dst->data;
+
+ while (len > 3) {
+ *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
+ *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
+ *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);
+
+ s += 4;
+ len -= 4;
+ }
+
+ if (len > 1) {
+ *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);
+ }
+
+ if (len > 2) {
+ *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);
+ }
+
+ dst->len = d - dst->data;
+
+ return NGX_OK;
+}
+
+
+/*
+ * ngx_utf8_decode() decodes two and more bytes UTF sequences only
+ * the return values:
+ * 0x80 - 0x10ffff valid character
+ * 0x110000 - 0xfffffffd invalid sequence
+ * 0xfffffffe incomplete sequence
+ * 0xffffffff error
+ */
+
+uint32_t
+ngx_utf8_decode(u_char **p, size_t n)
+{
+ size_t len;
+ uint32_t u, i, valid;
+
+ u = **p;
+
+ if (u > 0xf0) {
+
+ u &= 0x07;
+ valid = 0xffff;
+ len = 3;
+
+ } else if (u > 0xe0) {
+
+ u &= 0x0f;
+ valid = 0x7ff;
+ len = 2;
+
+ } else if (u > 0xc0) {
+
+ u &= 0x1f;
+ valid = 0x7f;
+ len = 1;
+
+ } else {
+ (*p)++;
+ return 0xffffffff;
+ }
+
+ if (n - 1 < len) {
+ return 0xfffffffe;
+ }
+
+ (*p)++;
+
+ while (len) {
+ i = *(*p)++;
+
+ if (i < 0x80) {
+ return 0xffffffff;
+ }
+
+ u = (u << 6) | (i & 0x3f);
+
+ len--;
+ }
+
+ if (u > valid) {
+ return u;
+ }
+
+ return 0xffffffff;
+}
+
+
+size_t
+ngx_utf8_length(u_char *p, size_t n)
+{
+ u_char c, *last;
+ size_t len;
+
+ last = p + n;
+
+ for (len = 0; p < last; len++) {
+
+ c = *p;
+
+ if (c < 0x80) {
+ p++;
+ continue;
+ }
+
+ if (ngx_utf8_decode(&p, n) > 0x10ffff) {
+ /* invalid UTF-8 */
+ return n;
+ }
+ }
+
+ return len;
+}
+
+
+u_char *
+ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)
+{
+ u_char c, *next;
+
+ if (n == 0) {
+ return dst;
+ }
+
+ while (--n) {
+
+ c = *src;
+ *dst = c;
+
+ if (c < 0x80) {
+
+ if (c != '\0') {
+ dst++;
+ src++;
+ len--;
+
+ continue;
+ }
+
+ return dst;
+ }
+
+ next = src;
+
+ if (ngx_utf8_decode(&next, len) > 0x10ffff) {
+ /* invalid UTF-8 */
+ break;
+ }
+
+ while (src < next) {
+ *dst++ = *src++;
+ len--;
+ }
+ }
+
+ *dst = '\0';
+
+ return dst;
+}
+
+
+uintptr_t
+ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
+{
+ ngx_uint_t n;
+ uint32_t *escape;
+ static u_char hex[] = "0123456789abcdef";
+
+ /* " ", "#", "%", "?", %00-%1F, %7F-%FF */
+
+ static uint32_t uri[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x80000029, /* 1000 0000 0000 0000 0000 0000 0010 1001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "#", "%", "&", "+", "?", %00-%1F, %7F-%FF */
+
+ static uint32_t args[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x88000869, /* 1000 1000 0000 0000 0000 1000 0110 1001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */
+
+ static uint32_t html[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x000000ad, /* 0000 0000 0000 0000 0000 0000 1010 1101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", """, "%", "'", %00-%1F, %7F-%FF */
+
+ static uint32_t refresh[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000085, /* 0000 0000 0000 0000 0000 0000 1000 0101 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+ };
+
+ /* " ", "%", %00-%1F */
+
+ static uint32_t memcached[] = {
+ 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
+
+ /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
+ 0x00000021, /* 0000 0000 0000 0000 0000 0000 0010 0001 */
+
+ /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ /* ~}| {zyx wvut srqp onml kjih gfed cba` */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ 0x00000000, /* 0000 0000 0000 0000 0000 0000 0000 0000 */
+ };
+
+ /* mail_auth is the same as memcached */
+
+ static uint32_t *map[] =
+ { uri, args, html, refresh, memcached, memcached };
+
+
+ escape = map[type];
+
+ if (dst == NULL) {
+
+ /* find the number of the characters to be escaped */
+
+ n = 0;
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ n++;
+ }
+ src++;
+ size--;
+ }
+
+ return (uintptr_t) n;
+ }
+
+ while (size) {
+ if (escape[*src >> 5] & (1 << (*src & 0x1f))) {
+ *dst++ = '%';
+ *dst++ = hex[*src >> 4];
+ *dst++ = hex[*src & 0xf];
+ src++;
+
+ } else {
+ *dst++ = *src++;
+ }
+ size--;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
+void
+ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
+{
+ u_char *d, *s, ch, c, decoded;
+ enum {
+ sw_usual = 0,
+ sw_quoted,
+ sw_quoted_second
+ } state;
+
+ d = *dst;
+ s = *src;
+
+ state = 0;
+ decoded = 0;
+
+ while (size--) {
+
+ ch = *s++;
+
+ switch (state) {
+ case sw_usual:
+ if (ch == '?'
+ && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))
+ {
+ *d++ = ch;
+ goto done;
+ }
+
+ if (ch == '%') {
+ state = sw_quoted;
+ break;
+ }
+
+ *d++ = ch;
+ break;
+
+ case sw_quoted:
+
+ if (ch >= '0' && ch <= '9') {
+ decoded = (u_char) (ch - '0');
+ state = sw_quoted_second;
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ decoded = (u_char) (c - 'a' + 10);
+ state = sw_quoted_second;
+ break;
+ }
+
+ /* the invalid quoted character */
+
+ state = sw_usual;
+
+ *d++ = ch;
+
+ break;
+
+ case sw_quoted_second:
+
+ state = sw_usual;
+
+ if (ch >= '0' && ch <= '9') {
+ ch = (u_char) ((decoded << 4) + ch - '0');
+
+ if (type & NGX_UNESCAPE_REDIRECT) {
+ if (ch > '%' && ch < 0x7f) {
+ *d++ = ch;
+ break;
+ }
+
+ *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+
+ break;
+ }
+
+ *d++ = ch;
+
+ break;
+ }
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'f') {
+ ch = (u_char) ((decoded << 4) + c - 'a' + 10);
+
+ if (type & NGX_UNESCAPE_URI) {
+ if (ch == '?') {
+ *d++ = ch;
+ goto done;
+ }
+
+ *d++ = ch;
+ break;
+ }
+
+ if (type & NGX_UNESCAPE_REDIRECT) {
+ if (ch == '?') {
+ *d++ = ch;
+ goto done;
+ }
+
+ if (ch > '%' && ch < 0x7f) {
+ *d++ = ch;
+ break;
+ }
+
+ *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+ break;
+ }
+
+ *d++ = ch;
+
+ break;
+ }
+
+ /* the invalid quoted character */
+
+ break;
+ }
+ }
+
+done:
+
+ *dst = d;
+ *src = s;
+}
+
+
+uintptr_t
+ngx_escape_html(u_char *dst, u_char *src, size_t size)
+{
+ u_char ch;
+ ngx_uint_t len;
+
+ if (dst == NULL) {
+
+ len = 0;
+
+ while (size) {
+ switch (*src++) {
+
+ case '<':
+ len += sizeof("&lt;") - 2;
+ break;
+
+ case '>':
+ len += sizeof("&gt;") - 2;
+ break;
+
+ case '&':
+ len += sizeof("&amp;") - 2;
+ break;
+
+ default:
+ break;
+ }
+ size--;
+ }
+
+ return (uintptr_t) len;
+ }
+
+ while (size) {
+ ch = *src++;
+
+ switch (ch) {
+
+ case '<':
+ *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';
+ break;
+
+ case '>':
+ *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';
+ break;
+
+ case '&':
+ *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';
+ *dst++ = ';';
+ break;
+
+ default:
+ *dst++ = ch;
+ break;
+ }
+ size--;
+ }
+
+ return (uintptr_t) dst;
+}
+
+
+void
+ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
+{
+ ngx_str_node_t *n, *t;
+ ngx_rbtree_node_t **p;
+
+ for ( ;; ) {
+
+ n = (ngx_str_node_t *) node;
+ t = (ngx_str_node_t *) temp;
+
+ if (node->key != temp->key) {
+
+ p = (node->key < temp->key) ? &temp->left : &temp->right;
+
+ } else if (n->str.len != t->str.len) {
+
+ p = (n->str.len < t->str.len) ? &temp->left : &temp->right;
+
+ } else {
+ p = (ngx_memcmp(n->str.data, t->str.data, n->str.len) < 0)
+ ? &temp->left : &temp->right;
+ }
+
+ if (*p == sentinel) {
+ break;
+ }
+
+ temp = *p;
+ }
+
+ *p = node;
+ node->parent = temp;
+ node->left = sentinel;
+ node->right = sentinel;
+ ngx_rbt_red(node);
+}
+
+
+ngx_str_node_t *
+ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash)
+{
+ ngx_int_t rc;
+ ngx_str_node_t *n;
+ ngx_rbtree_node_t *node, *sentinel;
+
+ node = rbtree->root;
+ sentinel = rbtree->sentinel;
+
+ while (node != sentinel) {
+
+ n = (ngx_str_node_t *) node;
+
+ if (hash != node->key) {
+ node = (hash < node->key) ? node->left : node->right;
+ continue;
+ }
+
+ if (val->len != n->str.len) {
+ node = (val->len < n->str.len) ? node->left : node->right;
+ continue;
+ }
+
+ rc = ngx_memcmp(val->data, n->str.data, val->len);
+
+ if (rc < 0) {
+ node = node->left;
+ continue;
+ }
+
+ if (rc > 0) {
+ node = node->right;
+ continue;
+ }
+
+ return n;
+ }
+
+ return NULL;
+}
+
+
+/* ngx_sort() is implemented as insertion sort because we need stable sort */
+
+void
+ngx_sort(void *base, size_t n, size_t size,
+ ngx_int_t (*cmp)(const void *, const void *))
+{
+ u_char *p1, *p2, *p;
+
+ p = ngx_alloc(size, ngx_cycle->log);
+ if (p == NULL) {
+ return;
+ }
+
+ for (p1 = (u_char *) base + size;
+ p1 < (u_char *) base + n * size;
+ p1 += size)
+ {
+ ngx_memcpy(p, p1, size);
+
+ for (p2 = p1;
+ p2 > (u_char *) base && cmp(p2 - size, p) > 0;
+ p2 -= size)
+ {
+ ngx_memcpy(p2, p2 - size, size);
+ }
+
+ ngx_memcpy(p2, p, size);
+ }
+
+ ngx_free(p);
+}
+
+
+#if (NGX_MEMCPY_LIMIT)
+
+void *
+ngx_memcpy(void *dst, void *src, size_t n)
+{
+ if (n > NGX_MEMCPY_LIMIT) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "memcpy %uz bytes", n);
+ ngx_debug_point();
+ }
+
+ return memcpy(dst, src, n);
+}
+
+#endif
diff --git a/usr.sbin/nginx/src/core/ngx_string.h b/usr.sbin/nginx/src/core/ngx_string.h
new file mode 100644
index 00000000000..2030988d89f
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_string.h
@@ -0,0 +1,229 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_STRING_H_INCLUDED_
+#define _NGX_STRING_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+ size_t len;
+ u_char *data;
+} ngx_str_t;
+
+
+typedef struct {
+ ngx_str_t key;
+ ngx_str_t value;
+} ngx_keyval_t;
+
+
+typedef struct {
+ unsigned len:28;
+
+ unsigned valid:1;
+ unsigned no_cacheable:1;
+ unsigned not_found:1;
+ unsigned escape:1;
+
+ u_char *data;
+} ngx_variable_value_t;
+
+
+#define ngx_string(str) { sizeof(str) - 1, (u_char *) str }
+#define ngx_null_string { 0, NULL }
+#define ngx_str_set(str, text) \
+ (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
+#define ngx_str_null(str) (str)->len = 0; (str)->data = NULL
+
+
+#define ngx_tolower(c) (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)
+#define ngx_toupper(c) (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)
+
+void ngx_strlow(u_char *dst, u_char *src, size_t n);
+
+
+#define ngx_strncmp(s1, s2, n) strncmp((const char *) s1, (const char *) s2, n)
+
+
+/* msvc and icc7 compile strcmp() to inline loop */
+#define ngx_strcmp(s1, s2) strcmp((const char *) s1, (const char *) s2)
+
+
+#define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2)
+#define ngx_strlen(s) strlen((const char *) s)
+
+#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c)
+
+static ngx_inline u_char *
+ngx_strlchr(u_char *p, u_char *last, u_char c)
+{
+ while (p < last) {
+
+ if (*p == c) {
+ return p;
+ }
+
+ p++;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * msvc and icc7 compile memset() to the inline "rep stos"
+ * while ZeroMemory() and bzero() are the calls.
+ * icc7 may also inline several mov's of a zeroed register for small blocks.
+ */
+#define ngx_memzero(buf, n) (void) memset(buf, 0, n)
+#define ngx_memset(buf, c, n) (void) memset(buf, c, n)
+
+
+#if (NGX_MEMCPY_LIMIT)
+
+void *ngx_memcpy(void *dst, void *src, size_t n);
+#define ngx_cpymem(dst, src, n) (((u_char *) ngx_memcpy(dst, src, n)) + (n))
+
+#else
+
+/*
+ * gcc3, msvc, and icc7 compile memcpy() to the inline "rep movs".
+ * gcc3 compiles memcpy(d, s, 4) to the inline "mov"es.
+ * icc8 compile memcpy(d, s, 4) to the inline "mov"es or XMM moves.
+ */
+#define ngx_memcpy(dst, src, n) (void) memcpy(dst, src, n)
+#define ngx_cpymem(dst, src, n) (((u_char *) memcpy(dst, src, n)) + (n))
+
+#endif
+
+
+#if ( __INTEL_COMPILER >= 800 )
+
+/*
+ * the simple inline cycle copies the variable length strings up to 16
+ * bytes faster than icc8 autodetecting _intel_fast_memcpy()
+ */
+
+static ngx_inline u_char *
+ngx_copy(u_char *dst, u_char *src, size_t len)
+{
+ if (len < 17) {
+
+ while (len) {
+ *dst++ = *src++;
+ len--;
+ }
+
+ return dst;
+
+ } else {
+ return ngx_cpymem(dst, src, len);
+ }
+}
+
+#else
+
+#define ngx_copy ngx_cpymem
+
+#endif
+
+
+#define ngx_memmove(dst, src, n) (void) memmove(dst, src, n)
+#define ngx_movemem(dst, src, n) (((u_char *) memmove(dst, src, n)) + (n))
+
+
+/* msvc and icc7 compile memcmp() to the inline loop */
+#define ngx_memcmp(s1, s2, n) memcmp((const char *) s1, (const char *) s2, n)
+
+
+u_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n);
+u_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);
+u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);
+u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);
+u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,
+ ...);
+u_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);
+#define ngx_vsnprintf(buf, max, fmt, args) \
+ ngx_vslprintf(buf, buf + (max), fmt, args)
+
+ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);
+ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);
+
+u_char *ngx_strnstr(u_char *s1, char *s2, size_t n);
+
+u_char *ngx_strstrn(u_char *s1, char *s2, size_t n);
+u_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n);
+u_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n);
+
+ngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);
+ngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);
+ngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);
+ngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);
+
+ngx_int_t ngx_atoi(u_char *line, size_t n);
+ngx_int_t ngx_atofp(u_char *line, size_t n, size_t point);
+ssize_t ngx_atosz(u_char *line, size_t n);
+off_t ngx_atoof(u_char *line, size_t n);
+time_t ngx_atotm(u_char *line, size_t n);
+ngx_int_t ngx_hextoi(u_char *line, size_t n);
+
+u_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len);
+
+
+#define ngx_base64_encoded_length(len) (((len + 2) / 3) * 4)
+#define ngx_base64_decoded_length(len) (((len + 3) / 4) * 3)
+
+void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
+ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);
+ngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);
+
+uint32_t ngx_utf8_decode(u_char **p, size_t n);
+size_t ngx_utf8_length(u_char *p, size_t n);
+u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);
+
+
+#define NGX_ESCAPE_URI 0
+#define NGX_ESCAPE_ARGS 1
+#define NGX_ESCAPE_HTML 2
+#define NGX_ESCAPE_REFRESH 3
+#define NGX_ESCAPE_MEMCACHED 4
+#define NGX_ESCAPE_MAIL_AUTH 5
+
+#define NGX_UNESCAPE_URI 1
+#define NGX_UNESCAPE_REDIRECT 2
+
+uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,
+ ngx_uint_t type);
+void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
+uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);
+
+
+typedef struct {
+ ngx_rbtree_node_t node;
+ ngx_str_t str;
+} ngx_str_node_t;
+
+
+void ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,
+ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
+ngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name,
+ uint32_t hash);
+
+
+void ngx_sort(void *base, size_t n, size_t size,
+ ngx_int_t (*cmp)(const void *, const void *));
+#define ngx_qsort qsort
+
+
+#define ngx_value_helper(n) #n
+#define ngx_value(n) ngx_value_helper(n)
+
+
+#endif /* _NGX_STRING_H_INCLUDED_ */
diff --git a/usr.sbin/nginx/src/core/ngx_times.c b/usr.sbin/nginx/src/core/ngx_times.c
new file mode 100644
index 00000000000..f1685aaf6b2
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_times.c
@@ -0,0 +1,406 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+/*
+ * The time may be updated by signal handler or by several threads.
+ * The time update operations are rare and require to hold the ngx_time_lock.
+ * The time read operations are frequent, so they are lock-free and get time
+ * values and strings from the current slot. Thus thread may get the corrupted
+ * values only if it is preempted while copying and then it is not scheduled
+ * to run more than NGX_TIME_SLOTS seconds.
+ */
+
+#define NGX_TIME_SLOTS 64
+
+static ngx_uint_t slot;
+static ngx_atomic_t ngx_time_lock;
+
+volatile ngx_msec_t ngx_current_msec;
+volatile ngx_time_t *ngx_cached_time;
+volatile ngx_str_t ngx_cached_err_log_time;
+volatile ngx_str_t ngx_cached_http_time;
+volatile ngx_str_t ngx_cached_http_log_time;
+volatile ngx_str_t ngx_cached_http_log_iso8601;
+
+#if !(NGX_WIN32)
+
+/*
+ * locatime() and localtime_r() are not Async-Signal-Safe functions, therefore,
+ * they must not be called by a signal handler, so we use the cached
+ * GMT offset value. Fortunately the value is changed only two times a year.
+ */
+
+static ngx_int_t cached_gmtoff;
+#endif
+
+static ngx_time_t cached_time[NGX_TIME_SLOTS];
+static u_char cached_err_log_time[NGX_TIME_SLOTS]
+ [sizeof("1970/09/28 12:00:00")];
+static u_char cached_http_time[NGX_TIME_SLOTS]
+ [sizeof("Mon, 28 Sep 1970 06:00:00 GMT")];
+static u_char cached_http_log_time[NGX_TIME_SLOTS]
+ [sizeof("28/Sep/1970:12:00:00 +0600")];
+static u_char cached_http_log_iso8601[NGX_TIME_SLOTS]
+ [sizeof("1970-09-28T12:00:00+06:00")];
+
+
+static char *week[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
+static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
+
+void
+ngx_time_init(void)
+{
+ ngx_cached_err_log_time.len = sizeof("1970/09/28 12:00:00") - 1;
+ ngx_cached_http_time.len = sizeof("Mon, 28 Sep 1970 06:00:00 GMT") - 1;
+ ngx_cached_http_log_time.len = sizeof("28/Sep/1970:12:00:00 +0600") - 1;
+ ngx_cached_http_log_iso8601.len = sizeof("1970-09-28T12:00:00+06:00") - 1;
+
+ ngx_cached_time = &cached_time[0];
+
+ ngx_time_update();
+}
+
+
+void
+ngx_time_update(void)
+{
+ u_char *p0, *p1, *p2, *p3;
+ ngx_tm_t tm, gmt;
+ time_t sec;
+ ngx_uint_t msec;
+ ngx_time_t *tp;
+ struct timeval tv;
+
+ if (!ngx_trylock(&ngx_time_lock)) {
+ return;
+ }
+
+ ngx_gettimeofday(&tv);
+
+ sec = tv.tv_sec;
+ msec = tv.tv_usec / 1000;
+
+ ngx_current_msec = (ngx_msec_t) sec * 1000 + msec;
+
+ tp = &cached_time[slot];
+
+ if (tp->sec == sec) {
+ tp->msec = msec;
+ ngx_unlock(&ngx_time_lock);
+ return;
+ }
+
+ if (slot == NGX_TIME_SLOTS - 1) {
+ slot = 0;
+ } else {
+ slot++;
+ }
+
+ tp = &cached_time[slot];
+
+ tp->sec = sec;
+ tp->msec = msec;
+
+ ngx_gmtime(sec, &gmt);
+
+
+ p0 = &cached_http_time[slot][0];
+
+ (void) ngx_sprintf(p0, "%s, %02d %s %4d %02d:%02d:%02d GMT",
+ week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,
+ months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,
+ gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);
+
+#if (NGX_HAVE_GETTIMEZONE)
+
+ tp->gmtoff = ngx_gettimezone();
+ ngx_gmtime(sec + tp->gmtoff * 60, &tm);
+
+#elif (NGX_HAVE_GMTOFF)
+
+ ngx_localtime(sec, &tm);
+ cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);
+ tp->gmtoff = cached_gmtoff;
+
+#else
+
+ ngx_localtime(sec, &tm);
+ cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);
+ tp->gmtoff = cached_gmtoff;
+
+#endif
+
+
+ p1 = &cached_err_log_time[slot][0];
+
+ (void) ngx_sprintf(p1, "%4d/%02d/%02d %02d:%02d:%02d",
+ tm.ngx_tm_year, tm.ngx_tm_mon,
+ tm.ngx_tm_mday, tm.ngx_tm_hour,
+ tm.ngx_tm_min, tm.ngx_tm_sec);
+
+
+ p2 = &cached_http_log_time[slot][0];
+
+ (void) ngx_sprintf(p2, "%02d/%s/%d:%02d:%02d:%02d %c%02d%02d",
+ tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],
+ tm.ngx_tm_year, tm.ngx_tm_hour,
+ tm.ngx_tm_min, tm.ngx_tm_sec,
+ tp->gmtoff < 0 ? '-' : '+',
+ ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
+
+ p3 = &cached_http_log_iso8601[slot][0];
+
+ (void) ngx_sprintf(p3, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d",
+ tm.ngx_tm_year, tm.ngx_tm_mon,
+ tm.ngx_tm_mday, tm.ngx_tm_hour,
+ tm.ngx_tm_min, tm.ngx_tm_sec,
+ tp->gmtoff < 0 ? '-' : '+',
+ ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));
+
+
+ ngx_memory_barrier();
+
+ ngx_cached_time = tp;
+ ngx_cached_http_time.data = p0;
+ ngx_cached_err_log_time.data = p1;
+ ngx_cached_http_log_time.data = p2;
+ ngx_cached_http_log_iso8601.data = p3;
+
+ ngx_unlock(&ngx_time_lock);
+}
+
+
+#if !(NGX_WIN32)
+
+void
+ngx_time_sigsafe_update(void)
+{
+ u_char *p;
+ ngx_tm_t tm;
+ time_t sec;
+ ngx_time_t *tp;
+ struct timeval tv;
+
+ if (!ngx_trylock(&ngx_time_lock)) {
+ return;
+ }
+
+ ngx_gettimeofday(&tv);
+
+ sec = tv.tv_sec;
+
+ tp = &cached_time[slot];
+
+ if (tp->sec == sec) {
+ ngx_unlock(&ngx_time_lock);
+ return;
+ }
+
+ if (slot == NGX_TIME_SLOTS - 1) {
+ slot = 0;
+ } else {
+ slot++;
+ }
+
+ ngx_gmtime(sec + cached_gmtoff * 60, &tm);
+
+ p = &cached_err_log_time[slot][0];
+
+ (void) ngx_sprintf(p, "%4d/%02d/%02d %02d:%02d:%02d",
+ tm.ngx_tm_year, tm.ngx_tm_mon,
+ tm.ngx_tm_mday, tm.ngx_tm_hour,
+ tm.ngx_tm_min, tm.ngx_tm_sec);
+
+ ngx_memory_barrier();
+
+ ngx_cached_err_log_time.data = p;
+
+ ngx_unlock(&ngx_time_lock);
+}
+
+#endif
+
+
+u_char *
+ngx_http_time(u_char *buf, time_t t)
+{
+ ngx_tm_t tm;
+
+ ngx_gmtime(t, &tm);
+
+ return ngx_sprintf(buf, "%s, %02d %s %4d %02d:%02d:%02d GMT",
+ week[tm.ngx_tm_wday],
+ tm.ngx_tm_mday,
+ months[tm.ngx_tm_mon - 1],
+ tm.ngx_tm_year,
+ tm.ngx_tm_hour,
+ tm.ngx_tm_min,
+ tm.ngx_tm_sec);
+}
+
+
+u_char *
+ngx_http_cookie_time(u_char *buf, time_t t)
+{
+ ngx_tm_t tm;
+
+ ngx_gmtime(t, &tm);
+
+ /*
+ * Netscape 3.x does not understand 4-digit years at all and
+ * 2-digit years more than "37"
+ */
+
+ return ngx_sprintf(buf,
+ (tm.ngx_tm_year > 2037) ?
+ "%s, %02d-%s-%d %02d:%02d:%02d GMT":
+ "%s, %02d-%s-%02d %02d:%02d:%02d GMT",
+ week[tm.ngx_tm_wday],
+ tm.ngx_tm_mday,
+ months[tm.ngx_tm_mon - 1],
+ (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year:
+ tm.ngx_tm_year % 100,
+ tm.ngx_tm_hour,
+ tm.ngx_tm_min,
+ tm.ngx_tm_sec);
+}
+
+
+void
+ngx_gmtime(time_t t, ngx_tm_t *tp)
+{
+ ngx_int_t yday;
+ ngx_uint_t n, sec, min, hour, mday, mon, year, wday, days, leap;
+
+ /* the calculation is valid for positive time_t only */
+
+ n = (ngx_uint_t) t;
+
+ days = n / 86400;
+
+ /* Jaunary 1, 1970 was Thursday */
+
+ wday = (4 + days) % 7;
+
+ n %= 86400;
+ hour = n / 3600;
+ n %= 3600;
+ min = n / 60;
+ sec = n % 60;
+
+ /*
+ * the algorithm based on Gauss' formula,
+ * see src/http/ngx_http_parse_time.c
+ */
+
+ /* days since March 1, 1 BC */
+ days = days - (31 + 28) + 719527;
+
+ /*
+ * The "days" should be adjusted to 1 only, however, some March 1st's go
+ * to previous year, so we adjust them to 2. This causes also shift of the
+ * last Feburary days to next year, but we catch the case when "yday"
+ * becomes negative.
+ */
+
+ year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);
+
+ yday = days - (365 * year + year / 4 - year / 100 + year / 400);
+
+ if (yday < 0) {
+ leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));
+ yday = 365 + leap + yday;
+ year--;
+ }
+
+ /*
+ * The empirical formula that maps "yday" to month.
+ * There are at least 10 variants, some of them are:
+ * mon = (yday + 31) * 15 / 459
+ * mon = (yday + 31) * 17 / 520
+ * mon = (yday + 31) * 20 / 612
+ */
+
+ mon = (yday + 31) * 10 / 306;
+
+ /* the Gauss' formula that evaluates days before the month */
+
+ mday = yday - (367 * mon / 12 - 30) + 1;
+
+ if (yday >= 306) {
+
+ year++;
+ mon -= 10;
+
+ /*
+ * there is no "yday" in Win32 SYSTEMTIME
+ *
+ * yday -= 306;
+ */
+
+ } else {
+
+ mon += 2;
+
+ /*
+ * there is no "yday" in Win32 SYSTEMTIME
+ *
+ * yday += 31 + 28 + leap;
+ */
+ }
+
+ tp->ngx_tm_sec = (ngx_tm_sec_t) sec;
+ tp->ngx_tm_min = (ngx_tm_min_t) min;
+ tp->ngx_tm_hour = (ngx_tm_hour_t) hour;
+ tp->ngx_tm_mday = (ngx_tm_mday_t) mday;
+ tp->ngx_tm_mon = (ngx_tm_mon_t) mon;
+ tp->ngx_tm_year = (ngx_tm_year_t) year;
+ tp->ngx_tm_wday = (ngx_tm_wday_t) wday;
+}
+
+
+time_t
+ngx_next_time(time_t when)
+{
+ time_t now, next;
+ struct tm tm;
+
+ now = ngx_time();
+
+ ngx_libc_localtime(now, &tm);
+
+ tm.tm_hour = (int) (when / 3600);
+ when %= 3600;
+ tm.tm_min = (int) (when / 60);
+ tm.tm_sec = (int) (when % 60);
+
+ next = mktime(&tm);
+
+ if (next == -1) {
+ return -1;
+ }
+
+ if (next - now > 0) {
+ return next;
+ }
+
+ tm.tm_mday++;
+
+ /* mktime() should normalize a date (Jan 32, etc) */
+
+ next = mktime(&tm);
+
+ if (next != -1) {
+ return next;
+ }
+
+ return -1;
+}
diff --git a/usr.sbin/nginx/src/core/ngx_times.h b/usr.sbin/nginx/src/core/ngx_times.h
new file mode 100644
index 00000000000..7f2fafd5495
--- /dev/null
+++ b/usr.sbin/nginx/src/core/ngx_times.h
@@ -0,0 +1,50 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_TIMES_H_INCLUDED_
+#define _NGX_TIMES_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+
+
+typedef struct {
+ time_t sec;
+ ngx_uint_t msec;
+ ngx_int_t gmtoff;
+} ngx_time_t;
+
+
+void ngx_time_init(void);
+void ngx_time_update(void);
+void ngx_time_sigsafe_update(void);
+u_char *ngx_http_time(u_char *buf, time_t t);
+u_char *ngx_http_cookie_time(u_char *buf, time_t t);
+void ngx_gmtime(time_t t, ngx_tm_t *tp);
+
+time_t ngx_next_time(time_t when);
+#define ngx_next_time_n "mktime()"
+
+
+extern volatile ngx_time_t *ngx_cached_time;
+
+#define ngx_time() ngx_cached_time->sec
+#define ngx_timeofday() (ngx_time_t *) ngx_cached_time
+
+extern volatile ngx_str_t ngx_cached_err_log_time;
+extern volatile ngx_str_t ngx_cached_http_time;
+extern volatile ngx_str_t ngx_cached_http_log_time;
+extern volatile ngx_str_t ngx_cached_http_log_iso8601;
+
+/*
+ * milliseconds elapsed since epoch and truncated to ngx_msec_t,
+ * used in event timers
+ */
+extern volatile ngx_msec_t ngx_current_msec;
+
+
+#endif /* _NGX_TIMES_H_INCLUDED_ */