/* * Copyright (C) Igor Sysoev * Copyright (C) Valentin V. Bartenev * Copyright (C) NGINX, Inc. */ #include #include #include #include #include #include static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_systemd_listen_sockets(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_event_engines(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_thread_pools(nxt_thread_t *thr, nxt_runtime_t *rt); static void nxt_runtime_start(nxt_task_t *task, void *obj, void *data); static void nxt_runtime_initial_start(nxt_task_t *task, nxt_uint_t status); static void nxt_runtime_close_idle_connections(nxt_event_engine_t *engine); static void nxt_runtime_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt); static void nxt_runtime_exit(nxt_task_t *task, void *obj, void *data); static nxt_int_t nxt_runtime_event_engine_change(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_hostname(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_log_files_init(nxt_runtime_t *rt); static nxt_int_t nxt_runtime_log_files_create(nxt_task_t *task, nxt_runtime_t *rt); static nxt_int_t nxt_runtime_pid_file_create(nxt_task_t *task, nxt_file_name_t *pid_file); static void nxt_runtime_thread_pool_destroy(nxt_task_t *task, nxt_runtime_t *rt, nxt_runtime_cont_t cont); static void nxt_runtime_thread_pool_init(void); static void nxt_runtime_thread_pool_exit(nxt_task_t *task, void *obj, void *data); static nxt_process_t *nxt_runtime_process_get(nxt_runtime_t *rt, nxt_pid_t pid); static void nxt_runtime_port_add(nxt_task_t *task, nxt_port_t *port); nxt_int_t nxt_runtime_create(nxt_task_t *task) { nxt_mp_t *mp; nxt_int_t ret; nxt_array_t *listen_sockets; nxt_runtime_t *rt; nxt_app_lang_module_t *lang; mp = nxt_mp_create(1024, 128, 256, 32); if (nxt_slow_path(mp == NULL)) { return NXT_ERROR; } rt = nxt_mp_zget(mp, sizeof(nxt_runtime_t)); if (nxt_slow_path(rt == NULL)) { goto fail; } task->thread->runtime = rt; rt->mem_pool = mp; nxt_thread_mutex_create(&rt->processes_mutex); rt->services = nxt_services_init(mp); if (nxt_slow_path(rt->services == NULL)) { goto fail; } rt->languages = nxt_array_create(mp, 1, sizeof(nxt_app_lang_module_t)); if (nxt_slow_path(rt->languages == NULL)) { goto fail; } /* Should not fail. */ lang = nxt_array_add(rt->languages); lang->type = NXT_APP_EXTERNAL; lang->version = (u_char *) ""; lang->file = NULL; lang->module = &nxt_external_module; lang->mounts = nxt_array_create(mp, 1, sizeof(nxt_fs_mount_t)); if (nxt_slow_path(lang->mounts == NULL)) { goto fail; } listen_sockets = nxt_array_create(mp, 1, sizeof(nxt_listen_socket_t)); if (nxt_slow_path(listen_sockets == NULL)) { goto fail; } rt->listen_sockets = listen_sockets; ret = nxt_runtime_inherited_listen_sockets(task, rt); if (nxt_slow_path(ret != NXT_OK)) { goto fail; } if (nxt_runtime_hostname(task, rt) != NXT_OK) { goto fail; } if (nxt_slow_path(nxt_runtime_log_files_init(rt) != NXT_OK)) { goto fail; } if (nxt_runtime_event_engines(task, rt) != NXT_OK) { goto fail; } if (nxt_slow_path(nxt_runtime_thread_pools(task->thread, rt) != NXT_OK)) { goto fail; } rt->start = nxt_runtime_initial_start; if (nxt_runtime_conf_init(task, rt) != NXT_OK) { goto fail; } if (nxt_port_rpc_init() != NXT_OK) { goto fail; } if (nxt_slow_path(nxt_http_register_variables() != NXT_OK)) { goto fail; } if (nxt_slow_path(nxt_var_index_init() != NXT_OK)) { goto fail; } nxt_work_queue_add(&task->thread->engine->fast_work_queue, nxt_runtime_start, task, rt, NULL); return NXT_OK; fail: nxt_mp_destroy(mp); return NXT_ERROR; } static nxt_int_t nxt_runtime_inherited_listen_sockets(nxt_task_t *task, nxt_runtime_t *rt) { u_char *v, *p; nxt_int_t type; nxt_array_t *inherited_sockets; nxt_socket_t s; nxt_listen_socket_t *ls; v = (u_char *) getenv("NGINX"); if (v == NULL) { return nxt_runtime_systemd_listen_sockets(task, rt); } nxt_alert(task, "using inherited listen sockets: %s", v); inherited_sockets = nxt_array_create(rt->mem_pool, 1, sizeof(nxt_listen_socket_t)); if (inherited_sockets == NULL) { return NXT_ERROR; } rt->inherited_sockets = inherited_sockets; for (p = v; *p != '\0'; p++) { if (*p == ';') { s = nxt_int_parse(v, p - v); if (nxt_slow_path(s < 0)) { nxt_alert(task, "invalid socket number \"%s\" " "in NGINX environment variable, " "ignoring the rest of the variable", v); return NXT_ERROR; } v = p + 1; ls = nxt_array_zero_add(inherited_sockets); if (nxt_slow_path(ls == NULL)) { return NXT_ERROR; } ls->socket = s; ls->sockaddr = nxt_getsockname(task, rt->mem_pool, s); if (nxt_slow_path(ls->sockaddr == NULL)) { return NXT_ERROR; } type = nxt_socket_getsockopt(task, s, SOL_SOCKET, SO_TYPE); if (nxt_slow_path(type == -1)) { return NXT_ERROR; } ls->sockaddr->type = (uint16_t) type; } } return NXT_OK; } static nxt_int_t nxt_runtime_systemd_listen_sockets(nxt_task_t *task, nxt_runtime_t *rt) { u_char *nfd, *pid; nxt_int_t n; nxt_array_t *inherited_sockets; nxt_socket_t s; nxt_listen_socket_t *ls; /* * Number of listening sockets passed. The socket * descriptors start from number 3 and are sequential. */ nfd = (u_char *) getenv("LISTEN_FDS"); if (nfd == NULL) { return NXT_OK; } /* The pid of the service process. */ pid = (u_char *) getenv("LISTEN_PID"); if (pid == NULL) { return NXT_OK; } n = nxt_int_parse(nfd, nxt_strlen(nfd)); if (n < 0) { return NXT_OK; } if (nxt_pid != nxt_int_parse(pid, nxt_strlen(pid))) { return NXT_OK; } nxt_log(task, NXT_LOG_INFO, "using %i systemd listen sockets", n); inherited_sockets = nxt_array_create(rt->mem_pool, n, sizeof(nxt_listen_socket_t)); if (inherited_sockets == NULL) { return NXT_ERROR; } rt->inherited_sockets = inherited_sockets; for (s = 3; s < n; s++) { ls = nxt_array_zero_add(inherited_sockets); if (nxt_slow_path(ls == NULL)) { return NXT_ERROR; } ls->socket = s; ls->sockaddr = nxt_getsockname(task, rt->mem_pool, s); if (nxt_slow_path(ls->sockaddr == NULL)) { return NXT_ERROR; } ls->sockaddr->type = SOCK_STREAM; } return NXT_OK; } static nxt_int_t nxt_runtime_event_engines(nxt_task_t *task, nxt_runtime_t *rt) { nxt_thread_t *thread; nxt_event_engine_t *engine; const nxt_event_interface_t *interface; interface = nxt_service_get(rt->services, "engine", NULL); if (nxt_slow_path(interface == NULL)) { /* TODO: log */ return NXT_ERROR; } engine = nxt_event_engine_create(task, interface, nxt_main_process_signals, 0, 0); if (nxt_slow_path(engine == NULL)) { return NXT_ERROR; } thread = task->thread; thread->engine = engine; #if 0 thread->fiber = &engine->fibers->fiber; #endif engine->id = rt->last_engine_id++; engine->mem_pool = nxt_mp_create(1024, 128, 256, 32); nxt_queue_init(&rt->engines); nxt_queue_insert_tail(&rt->engines, &engine->link); return NXT_OK; } static nxt_int_t nxt_runtime_thread_pools(nxt_thread_t *thr, nxt_runtime_t *rt) { nxt_int_t ret; nxt_array_t *thread_pools; thread_pools = nxt_array_create(rt->mem_pool, 1, sizeof(nxt_thread_pool_t *)); if (nxt_slow_path(thread_pools == NULL)) { return NXT_ERROR; } rt->thread_pools = thread_pools; ret = nxt_runtime_thread_pool_create(thr, rt, 2, 60000 * 1000000LL); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } return NXT_OK; } static void nxt_runtime_start(nxt_task_t *task, void *obj, void *data) { nxt_runtime_t *rt; rt = obj; nxt_debug(task, "rt conf done"); task->thread->log->ctx_handler = NULL; task->thread->log->ctx = NULL; if (nxt_runtime_log_files_create(task, rt) != NXT_OK) { goto fail; } if (nxt_runtime_event_engine_change(task, rt) != NXT_OK) { goto fail; } /* * Thread pools should be destroyed before starting worker * processes, because thread pool semaphores will stick in * locked state in new processes after fork(). */ nxt_runtime_thread_pool_destroy(task, rt, rt->start); return; fail: nxt_runtime_quit(task, 1); } static void nxt_runtime_initial_start(nxt_task_t *task, nxt_uint_t status) { nxt_int_t ret; nxt_thread_t *thr; nxt_runtime_t *rt; const nxt_event_interface_t *interface; thr = task->thread; rt = thr->runtime; if (rt->inherited_sockets == NULL && rt->daemon) { if (nxt_process_daemon(task) != NXT_OK) { goto fail; } /* * An event engine should be updated after fork() * even if an event facility was not changed because: * 1) inherited kqueue descriptor is invalid, * 2) the signal thread is not inherited. */ interface = nxt_service_get(rt->services, "engine", rt->engine); if (interface == NULL) { goto fail; } ret = nxt_event_engine_change(task->thread->engine, interface, rt->batch); if (ret != NXT_OK) { goto fail; } } ret = nxt_runtime_pid_file_create(task, rt->pid_file); if (ret != NXT_OK) { goto fail; } if (nxt_runtime_event_engine_change(task, rt) != NXT_OK) { goto fail; } thr->engine->max_connections = rt->engine_connections; if (nxt_main_process_start(thr, task, rt) != NXT_ERROR) { return; } fail: nxt_runtime_quit(task, 1); } void nxt_runtime_quit(nxt_task_t *task, nxt_uint_t status) { nxt_bool_t done; nxt_runtime_t *rt; nxt_event_engine_t *engine; rt = task->thread->runtime; rt->status |= status; engine = task->thread->engine; nxt_debug(task, "exiting"); done = 1; if (!engine->shutdown) { engine->shutdown = 1; if (!nxt_array_is_empty(rt->thread_pools)) { nxt_runtime_thread_pool_destroy(task, rt, nxt_runtime_quit); done = 0; } if (rt->type == NXT_PROCESS_MAIN) { nxt_runtime_stop_all_processes(task, rt); done = 0; } } nxt_runtime_close_idle_connections(engine); if (done) { nxt_work_queue_add(&engine->fast_work_queue, nxt_runtime_exit, task, rt, engine); } } static void nxt_runtime_close_idle_connections(nxt_event_engine_t *engine) { nxt_conn_t *c; nxt_queue_t *idle; nxt_queue_link_t *link, *next; nxt_debug(&engine->task, "close idle connections"); idle = &engine->idle_connections; for (link = nxt_queue_first(idle); link != nxt_queue_tail(idle); link = next) { next = nxt_queue_next(link); c = nxt_queue_link_data(link, nxt_conn_t, link); if (!c->socket.read_ready) { nxt_queue_remove(link); nxt_conn_close(engine, c); } } } void nxt_runtime_stop_app_processes(nxt_task_t *task, nxt_runtime_t *rt) { nxt_port_t *port; nxt_process_t *process; nxt_process_init_t *init; nxt_runtime_process_each(rt, process) { init = nxt_process_init(process); if (init->type == NXT_PROCESS_APP || init->type == NXT_PROCESS_PROTOTYPE) { nxt_process_port_each(process, port) { (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); } nxt_process_port_loop; } } nxt_runtime_process_loop; } static void nxt_runtime_stop_all_processes(nxt_task_t *task, nxt_runtime_t *rt) { nxt_port_t *port; nxt_process_t *process; nxt_runtime_process_each(rt, process) { nxt_process_port_each(process, port) { nxt_debug(task, "%d sending quit to %PI", rt->type, port->pid); (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, -1, 0, 0, NULL); } nxt_process_port_loop; } nxt_runtime_process_loop; } static void nxt_runtime_exit(nxt_task_t *task, void *obj, void *data) { int status, engine_count; nxt_runtime_t *rt; nxt_process_t *process; nxt_event_engine_t *engine; rt = obj; engine = data; nxt_debug(task, "thread pools: %d", rt->thread_pools->nelts); if (!nxt_array_is_empty(rt->thread_pools)) { return; } if (rt->type == NXT_PROCESS_MAIN) { if (rt->pid_file != NULL) { nxt_file_delete(rt->pid_file); } #if (NXT_HAVE_UNIX_DOMAIN) { size_t i; nxt_sockaddr_t *sa; nxt_file_name_t *name; sa = rt->controller_listen; if (sa->u.sockaddr.sa_family == AF_UNIX) { name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; (void) nxt_file_delete(name); } for (i = 0; i < rt->listen_sockets->nelts; i++) { nxt_listen_socket_t *ls; ls = (nxt_listen_socket_t *) rt->listen_sockets->elts + i; sa = ls->sockaddr; if (sa->u.sockaddr.sa_family != AF_UNIX || sa->u.sockaddr_un.sun_path[0] == '\0') { continue; } name = (nxt_file_name_t *) sa->u.sockaddr_un.sun_path; (void) nxt_file_delete(name); } } #endif } if (!engine->event.signal_support) { nxt_event_engine_signals_stop(engine); } nxt_runtime_process_each(rt, process) { nxt_runtime_process_remove(rt, process); nxt_process_close_ports(task, process); } nxt_runtime_process_loop; status = rt->status; engine_count = 0; nxt_queue_each(engine, &rt->engines, nxt_event_engine_t, link) { engine_count++; } nxt_queue_loop; if (engine_count <= 1) { if (rt->port_by_type[rt->type] != NULL) { nxt_port_use(task, rt->port_by_type[rt->type], -1); } nxt_thread_mutex_destroy(&rt->processes_mutex); nxt_mp_destroy(rt->mem_pool); } nxt_debug(task, "exit: %d", status); exit(status); nxt_unreachable(); } static nxt_int_t nxt_runtime_event_engine_change(nxt_task_t *task, nxt_runtime_t *rt) { nxt_event_engine_t *engine; const nxt_event_interface_t *interface; engine = task->thread->engine; if (engine->batch == rt->batch && nxt_strcmp(engine->event.name, rt->engine) == 0) { return NXT_OK; } interface = nxt_service_get(rt->services, "engine", rt->engine); if (interface != NULL) { return nxt_event_engine_change(engine, interface, rt->batch); } return NXT_ERROR; } void nxt_runtime_event_engine_free(nxt_runtime_t *rt) { nxt_queue_link_t *link; nxt_event_engine_t *engine; link = nxt_queue_first(&rt->engines); nxt_queue_remove(link); engine = nxt_queue_link_data(link, nxt_event_engine_t, link); nxt_event_engine_free(engine); } nxt_int_t nxt_runtime_thread_pool_create(nxt_thread_t *thr, nxt_runtime_t *rt, nxt_uint_t max_threads, nxt_nsec_t timeout) { nxt_thread_pool_t *thread_pool, **tp; tp = nxt_array_add(rt->thread_pools); if (tp == NULL) { return NXT_ERROR; } thread_pool = nxt_thread_pool_create(max_threads, timeout, nxt_runtime_thread_pool_init, thr->engine, nxt_runtime_thread_pool_exit); if (nxt_fast_path(thread_pool != NULL)) { *tp = thread_pool; } return NXT_OK; } static void nxt_runtime_thread_pool_destroy(nxt_task_t *task, nxt_runtime_t *rt, nxt_runtime_cont_t cont) { nxt_uint_t n; nxt_thread_pool_t **tp; rt->continuation = cont; n = rt->thread_pools->nelts; if (n == 0) { cont(task, 0); return; } tp = rt->thread_pools->elts; do { nxt_thread_pool_destroy(*tp); tp++; n--; } while (n != 0); } static void nxt_runtime_thread_pool_init(void) { } static void nxt_runtime_thread_pool_exit(nxt_task_t *task, void *obj, void *data) { nxt_uint_t i, n; nxt_runtime_t *rt; nxt_thread_pool_t *tp, **thread_pools; nxt_thread_handle_t handle; tp = obj; if (data != NULL) { handle = (nxt_thread_handle_t) (uintptr_t) data; nxt_thread_wait(handle); } rt = task->thread->runtime; thread_pools = rt->thread_pools->elts; n = rt->thread_pools->nelts; nxt_debug(task, "thread pools: %ui", n); for (i = 0; i < n; i++) { if (tp == thread_pools[i]) { nxt_array_remove(rt->thread_pools, &thread_pools[i]); nxt_free(tp); if (n == 1) { /* The last thread pool. */ rt->continuation(task, 0); } return; } } } static nxt_int_t nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt) { nxt_int_t ret; nxt_str_t control; nxt_uint_t n; nxt_file_t *file; const char *slash; nxt_sockaddr_t *sa; nxt_file_name_str_t file_name; const nxt_event_interface_t *interface; rt->daemon = 1; rt->engine_connections = 256; rt->auxiliary_threads = 2; rt->user_cred.user = NXT_USER; rt->group = NXT_GROUP; rt->pid = NXT_PID; rt->log = NXT_LOG; rt->modules = NXT_MODULESDIR; rt->state = NXT_STATEDIR; rt->control = NXT_CONTROL_SOCK; rt->tmp = NXT_TMPDIR; nxt_memzero(&rt->capabilities, sizeof(nxt_capabilities_t)); if (nxt_runtime_conf_read_cmd(task, rt) != NXT_OK) { return NXT_ERROR; } if (nxt_capability_set(task, &rt->capabilities) != NXT_OK) { return NXT_ERROR; } if (rt->capabilities.setid) { ret = nxt_credential_get(task, rt->mem_pool, &rt->user_cred, rt->group); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } } else { nxt_log(task, NXT_LOG_WARN, "Unit is running unprivileged, then it " "cannot use arbitrary user and group."); } /* An engine's parameters. */ interface = nxt_service_get(rt->services, "engine", rt->engine); if (interface == NULL) { return NXT_ERROR; } rt->engine = interface->name; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%Z", rt->pid); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->pid_file = file_name.start; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%Z", rt->log); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } file = nxt_list_first(rt->log_files); file->name = file_name.start; slash = ""; n = nxt_strlen(rt->modules); if (n > 1 && rt->modules[n - 1] != '/') { slash = "/"; } ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%s*.unit.so%Z", rt->modules, slash); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->modules = (char *) file_name.start; slash = ""; n = nxt_strlen(rt->state); if (n > 1 && rt->state[n - 1] != '/') { slash = "/"; } ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%sversion%Z", rt->state, slash); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->ver = (char *) file_name.start; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s.tmp%Z", rt->ver); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->ver_tmp = (char *) file_name.start; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%sconf.json%Z", rt->state, slash); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->conf = (char *) file_name.start; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s.tmp%Z", rt->conf); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } rt->conf_tmp = (char *) file_name.start; ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%scerts/%Z", rt->state, slash); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } ret = mkdir((char *) file_name.start, S_IRWXU); if (nxt_fast_path(ret == 0 || nxt_errno == EEXIST)) { rt->certs.length = file_name.len; rt->certs.start = file_name.start; } else { nxt_alert(task, "Unable to create certificates storage directory: " "mkdir(%s) failed %E", file_name.start, nxt_errno); } control.length = nxt_strlen(rt->control); control.start = (u_char *) rt->control; sa = nxt_sockaddr_parse(rt->mem_pool, &control); if (nxt_slow_path(sa == NULL)) { return NXT_ERROR; } sa->type = SOCK_STREAM; rt->controller_listen = sa; if (nxt_runtime_controller_socket(task, rt) != NXT_OK) { return NXT_ERROR; } return NXT_OK; } static nxt_int_t nxt_runtime_conf_read_cmd(nxt_task_t *task, nxt_runtime_t *rt) { char *p, **argv; u_char *end; u_char buf[1024]; static const char version[] = "unit version: " NXT_VERSION "\n" "configured as ./configure" NXT_CONFIGURE_OPTIONS "\n"; static const char no_control[] = "option \"--control\" requires socket address\n"; static const char no_user[] = "option \"--user\" requires username\n"; static const char no_group[] = "option \"--group\" requires group name\n"; static const char no_pid[] = "option \"--pid\" requires filename\n"; static const char no_log[] = "option \"--log\" requires filename\n"; static const char no_modules[] = "option \"--modulesdir\" requires directory\n"; static const char no_state[] = "option \"--statedir\" requires directory\n"; static const char no_tmp[] = "option \"--tmpdir\" requires directory\n"; static const char help[] = "\n" "unit options:\n" "\n" " --version print unit version and configure options\n" "\n" " --no-daemon run unit in non-daemon mode\n" "\n" " --control ADDRESS set address of control API socket\n" " default: \"" NXT_CONTROL_SOCK "\"\n" "\n" " --pid FILE set pid filename\n" " default: \"" NXT_PID "\"\n" "\n" " --log FILE set log filename\n" " default: \"" NXT_LOG "\"\n" "\n" " --modulesdir DIR set modules directory name\n" " default: \"" NXT_MODULESDIR "\"\n" "\n" " --statedir DIR set state directory name\n" " default: \"" NXT_STATEDIR "\"\n" "\n" " --tmpdir DIR set tmp directory name\n" " default: \"" NXT_TMPDIR "\"\n" "\n" " --user USER set non-privileged processes to run" " as specified user\n" " default: \"" NXT_USER "\"\n" "\n" " --group GROUP set non-privileged processes to run" " as specified group\n" " default: "; static const char group[] = "\"" NXT_GROUP "\"\n\n"; static const char primary[] = "user's primary group\n\n"; argv = &nxt_process_argv[1]; while (*argv != NULL) { p = *argv++; if (nxt_strcmp(p, "--control") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_control, nxt_length(no_control)); return NXT_ERROR; } p = *argv++; rt->control = p; continue; } if (nxt_strcmp(p, "--user") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_user, nxt_length(no_user)); return NXT_ERROR; } p = *argv++; rt->user_cred.user = p; continue; } if (nxt_strcmp(p, "--group") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_group, nxt_length(no_group)); return NXT_ERROR; } p = *argv++; rt->group = p; continue; } if (nxt_strcmp(p, "--pid") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_pid, nxt_length(no_pid)); return NXT_ERROR; } p = *argv++; rt->pid = p; continue; } if (nxt_strcmp(p, "--log") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_log, nxt_length(no_log)); return NXT_ERROR; } p = *argv++; rt->log = p; continue; } if (nxt_strcmp(p, "--modulesdir") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_modules, nxt_length(no_modules)); return NXT_ERROR; } p = *argv++; rt->modules = p; continue; } if (nxt_strcmp(p, "--statedir") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_state, nxt_length(no_state)); return NXT_ERROR; } p = *argv++; rt->state = p; continue; } if (nxt_strcmp(p, "--tmpdir") == 0) { if (*argv == NULL) { write(STDERR_FILENO, no_tmp, nxt_length(no_tmp)); return NXT_ERROR; } p = *argv++; rt->tmp = p; continue; } if (nxt_strcmp(p, "--no-daemon") == 0) { rt->daemon = 0; continue; } if (nxt_strcmp(p, "--version") == 0) { write(STDERR_FILENO, version, nxt_length(version)); exit(0); } if (nxt_strcmp(p, "--help") == 0 || nxt_strcmp(p, "-h") == 0) { write(STDOUT_FILENO, help, nxt_length(help)); if (sizeof(NXT_GROUP) == 1) { write(STDOUT_FILENO, primary, nxt_length(primary)); } else { write(STDOUT_FILENO, group, nxt_length(group)); } exit(0); } end = nxt_sprintf(buf, buf + sizeof(buf), "unknown option \"%s\", " "try \"%s -h\" for available options\n", p, nxt_process_argv[0]); write(STDERR_FILENO, buf, end - buf); return NXT_ERROR; } return NXT_OK; } nxt_listen_socket_t * nxt_runtime_listen_socket_add(nxt_runtime_t *rt, nxt_sockaddr_t *sa) { nxt_mp_t *mp; nxt_listen_socket_t *ls; ls = nxt_array_zero_add(rt->listen_sockets); if (ls == NULL) { return NULL; } mp = rt->mem_pool; ls->sockaddr = nxt_sockaddr_create(mp, &sa->u.sockaddr, sa->socklen, sa->length); if (ls->sockaddr == NULL) { return NULL; } ls->sockaddr->type = sa->type; nxt_sockaddr_text(ls->sockaddr); ls->socket = -1; ls->backlog = NXT_LISTEN_BACKLOG; return ls; } static nxt_int_t nxt_runtime_hostname(nxt_task_t *task, nxt_runtime_t *rt) { size_t length; char hostname[NXT_MAXHOSTNAMELEN + 1]; if (gethostname(hostname, NXT_MAXHOSTNAMELEN) != 0) { nxt_alert(task, "gethostname() failed %E", nxt_errno); return NXT_ERROR; } /* * Linux gethostname(2): * * If the null-terminated hostname is too large to fit, * then the name is truncated, and no error is returned. * * For this reason an additional byte is reserved in the buffer. */ hostname[NXT_MAXHOSTNAMELEN] = '\0'; length = nxt_strlen(hostname); rt->hostname.length = length; rt->hostname.start = nxt_mp_nget(rt->mem_pool, length); if (rt->hostname.start != NULL) { nxt_memcpy_lowcase(rt->hostname.start, (u_char *) hostname, length); return NXT_OK; } return NXT_ERROR; } static nxt_int_t nxt_runtime_log_files_init(nxt_runtime_t *rt) { nxt_file_t *file; nxt_list_t *log_files; log_files = nxt_list_create(rt->mem_pool, 1, sizeof(nxt_file_t)); if (nxt_fast_path(log_files != NULL)) { rt->log_files = log_files; /* Preallocate the main log. This allocation cannot fail. */ file = nxt_list_zero_add(log_files); file->fd = NXT_FILE_INVALID; file->log_level = NXT_LOG_ALERT; return NXT_OK; } return NXT_ERROR; } nxt_file_t * nxt_runtime_log_file_add(nxt_runtime_t *rt, nxt_str_t *name) { nxt_int_t ret; nxt_file_t *file; nxt_file_name_str_t file_name; ret = nxt_file_name_create(rt->mem_pool, &file_name, "V%Z", name); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } nxt_list_each(file, rt->log_files) { /* STUB: hardecoded case sensitive/insensitive. */ if (file->name != NULL && nxt_file_name_eq(file->name, file_name.start)) { return file; } } nxt_list_loop; file = nxt_list_zero_add(rt->log_files); if (nxt_slow_path(file == NULL)) { return NULL; } file->fd = NXT_FILE_INVALID; file->log_level = NXT_LOG_ALERT; file->name = file_name.start; return file; } static nxt_int_t nxt_runtime_log_files_create(nxt_task_t *task, nxt_runtime_t *rt) { nxt_int_t ret; nxt_file_t *file; nxt_list_each(file, rt->log_files) { ret = nxt_file_open(task, file, O_WRONLY | O_APPEND, O_CREAT, NXT_FILE_OWNER_ACCESS); if (ret != NXT_OK) { return NXT_ERROR; } } nxt_list_loop; file = nxt_list_first(rt->log_files); return nxt_file_stderr(file); } nxt_int_t nxt_runtime_listen_sockets_create(nxt_task_t *task, nxt_runtime_t *rt) { nxt_int_t ret; nxt_uint_t c, p, ncurr, nprev; nxt_listen_socket_t *curr, *prev; curr = rt->listen_sockets->elts; ncurr = rt->listen_sockets->nelts; if (rt->inherited_sockets != NULL) { prev = rt->inherited_sockets->elts; nprev = rt->inherited_sockets->nelts; } else { prev = NULL; nprev = 0; } for (c = 0; c < ncurr; c++) { for (p = 0; p < nprev; p++) { if (nxt_sockaddr_cmp(curr[c].sockaddr, prev[p].sockaddr)) { ret = nxt_listen_socket_update(task, &curr[c], &prev[p]); if (ret != NXT_OK) { return NXT_ERROR; } goto next; } } if (nxt_listen_socket_create(task, rt->mem_pool, &curr[c]) != NXT_OK) { return NXT_ERROR; } next: continue; } return NXT_OK; } nxt_int_t nxt_runtime_listen_sockets_enable(nxt_task_t *task, nxt_runtime_t *rt) { nxt_uint_t i, n; nxt_listen_socket_t *ls; ls = rt->listen_sockets->elts; n = rt->listen_sockets->nelts; for (i = 0; i < n; i++) { if (ls[i].flags == NXT_NONBLOCK) { if (nxt_listen_event(task, &ls[i]) == NULL) { return NXT_ERROR; } } } return NXT_OK; } nxt_str_t * nxt_current_directory(nxt_mp_t *mp) { size_t length; u_char *p; nxt_str_t *name; char buf[NXT_MAX_PATH_LEN]; length = nxt_dir_current(buf, NXT_MAX_PATH_LEN); if (nxt_fast_path(length != 0)) { name = nxt_str_alloc(mp, length + 1); if (nxt_fast_path(name != NULL)) { p = nxt_cpymem(name->start, buf, length); *p = '/'; return name; } } return NULL; } static nxt_int_t nxt_runtime_pid_file_create(nxt_task_t *task, nxt_file_name_t *pid_file) { ssize_t length; nxt_int_t n; nxt_file_t file; u_char pid[NXT_INT64_T_LEN + nxt_length("\n")]; nxt_memzero(&file, sizeof(nxt_file_t)); file.name = pid_file; nxt_fs_mkdir_parent(pid_file, 0755); n = nxt_file_open(task, &file, O_WRONLY, O_CREAT | O_TRUNC, NXT_FILE_DEFAULT_ACCESS); if (n != NXT_OK) { return NXT_ERROR; } length = nxt_sprintf(pid, pid + sizeof(pid), "%PI%n", nxt_pid) - pid; if (nxt_file_write(&file, pid, length, 0) != length) { return NXT_ERROR; } nxt_file_close(task, &file); return NXT_OK; } void nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process) { nxt_process_t *child; if (process->registered == 1) { nxt_runtime_process_remove(rt, process); } if (process->link.next != NULL) { nxt_queue_remove(&process->link); } nxt_queue_each(child, &process->children, nxt_process_t, link) { nxt_queue_remove(&child->link); child->link.next = NULL; } nxt_queue_loop; nxt_assert(process->use_count == 0); nxt_assert(process->registered == 0); nxt_assert(nxt_queue_is_empty(&process->ports)); nxt_port_mmaps_destroy(&process->incoming, 1); nxt_thread_mutex_destroy(&process->incoming.mutex); /* processes from nxt_runtime_process_get() have no memory pool */ if (process->mem_pool != NULL) { nxt_mp_destroy(process->mem_pool); } nxt_mp_free(rt->mem_pool, process); } static nxt_int_t nxt_runtime_lvlhsh_pid_test(nxt_lvlhsh_query_t *lhq, void *data) { nxt_process_t *process; process = data; if (lhq->key.length == sizeof(nxt_pid_t) && *(nxt_pid_t *) lhq->key.start == process->pid) { return NXT_OK; } return NXT_DECLINED; } static const nxt_lvlhsh_proto_t lvlhsh_processes_proto nxt_aligned(64) = { NXT_LVLHSH_DEFAULT, nxt_runtime_lvlhsh_pid_test, nxt_lvlhsh_alloc, nxt_lvlhsh_free, }; nxt_inline void nxt_runtime_process_lhq_pid(nxt_lvlhsh_query_t *lhq, nxt_pid_t *pid) { lhq->key_hash = nxt_murmur_hash2(pid, sizeof(*pid)); lhq->key.length = sizeof(*pid); lhq->key.start = (u_char *) pid; lhq->proto = &lvlhsh_processes_proto; } nxt_process_t * nxt_runtime_process_find(nxt_runtime_t *rt, nxt_pid_t pid) { nxt_process_t *process; nxt_lvlhsh_query_t lhq; process = NULL; nxt_runtime_process_lhq_pid(&lhq, &pid); nxt_thread_mutex_lock(&rt->processes_mutex); if (nxt_lvlhsh_find(&rt->processes, &lhq) == NXT_OK) { process = lhq.value; } else { nxt_thread_log_debug("process %PI not found", pid); } nxt_thread_mutex_unlock(&rt->processes_mutex); return process; } static nxt_process_t * nxt_runtime_process_get(nxt_runtime_t *rt, nxt_pid_t pid) { nxt_process_t *process; nxt_lvlhsh_query_t lhq; nxt_runtime_process_lhq_pid(&lhq, &pid); nxt_thread_mutex_lock(&rt->processes_mutex); if (nxt_lvlhsh_find(&rt->processes, &lhq) == NXT_OK) { nxt_thread_log_debug("process %PI found", pid); nxt_thread_mutex_unlock(&rt->processes_mutex); process = lhq.value; process->use_count++; return process; } process = nxt_process_new(rt); if (nxt_slow_path(process == NULL)) { nxt_thread_mutex_unlock(&rt->processes_mutex); return NULL; } process->pid = pid; lhq.replace = 0; lhq.value = process; lhq.pool = rt->mem_pool; switch (nxt_lvlhsh_insert(&rt->processes, &lhq)) { case NXT_OK: if (rt->nprocesses == 0) { rt->mprocess = process; } rt->nprocesses++; process->registered = 1; nxt_thread_log_debug("process %PI insert", pid); break; default: nxt_thread_log_debug("process %PI insert failed", pid); break; } nxt_thread_mutex_unlock(&rt->processes_mutex); return process; } void nxt_runtime_process_add(nxt_task_t *task, nxt_process_t *process) { nxt_port_t *port; nxt_runtime_t *rt; nxt_lvlhsh_query_t lhq; nxt_assert(process->registered == 0); rt = task->thread->runtime; nxt_runtime_process_lhq_pid(&lhq, &process->pid); lhq.replace = 0; lhq.value = process; lhq.pool = rt->mem_pool; nxt_thread_mutex_lock(&rt->processes_mutex); switch (nxt_lvlhsh_insert(&rt->processes, &lhq)) { case NXT_OK: if (rt->nprocesses == 0) { rt->mprocess = process; } rt->nprocesses++; nxt_process_port_each(process, port) { port->pid = process->pid; nxt_runtime_port_add(task, port); } nxt_process_port_loop; process->registered = 1; nxt_debug(task, "process %PI added", process->pid); break; default: nxt_alert(task, "process %PI failed to add", process->pid); break; } nxt_thread_mutex_unlock(&rt->processes_mutex); } void nxt_runtime_process_remove(nxt_runtime_t *rt, nxt_process_t *process) { nxt_pid_t pid; nxt_lvlhsh_query_t lhq; nxt_assert(process->registered != 0); pid = process->pid; nxt_runtime_process_lhq_pid(&lhq, &pid); lhq.pool = rt->mem_pool; nxt_thread_mutex_lock(&rt->processes_mutex); switch (nxt_lvlhsh_delete(&rt->processes, &lhq)) { case NXT_OK: nxt_assert(lhq.value == process); rt->nprocesses--; process->registered = 0; nxt_thread_log_debug("process %PI removed", pid); break; default: nxt_thread_log_alert("process %PI remove failed", pid); break; } nxt_thread_mutex_unlock(&rt->processes_mutex); } nxt_process_t * nxt_runtime_process_first(nxt_runtime_t *rt, nxt_lvlhsh_each_t *lhe) { nxt_lvlhsh_each_init(lhe, &lvlhsh_processes_proto); return nxt_runtime_process_next(rt, lhe); } nxt_port_t * nxt_runtime_process_port_create(nxt_task_t *task, nxt_runtime_t *rt, nxt_pid_t pid, nxt_port_id_t id, nxt_process_type_t type) { nxt_port_t *port; nxt_process_t *process; process = nxt_runtime_process_get(rt, pid); if (nxt_slow_path(process == NULL)) { return NULL; } port = nxt_port_new(task, id, pid, type); if (nxt_slow_path(port == NULL)) { nxt_process_use(task, process, -1); return NULL; } nxt_process_port_add(task, process, port); nxt_process_use(task, process, -1); nxt_runtime_port_add(task, port); nxt_port_use(task, port, -1); return port; } static void nxt_runtime_port_add(nxt_task_t *task, nxt_port_t *port) { nxt_int_t res; nxt_runtime_t *rt; rt = task->thread->runtime; res = nxt_port_hash_add(&rt->ports, port); if (res != NXT_OK) { return; } rt->port_by_type[port->type] = port; nxt_port_use(task, port, 1); } void nxt_runtime_port_remove(nxt_task_t *task, nxt_port_t *port) { nxt_int_t res; nxt_runtime_t *rt; rt = task->thread->runtime; res = nxt_port_hash_remove(&rt->ports, port); if (res != NXT_OK) { return; } if (rt->port_by_type[port->type] == port) { rt->port_by_type[port->type] = NULL; } nxt_port_use(task, port, -1); } nxt_port_t * nxt_runtime_port_find(nxt_runtime_t *rt, nxt_pid_t pid, nxt_port_id_t port_id) { return nxt_port_hash_find(&rt->ports, pid, port_id); }