From 50f9816daab6c1f9f51764bd488cea0dd11ce965 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 12 May 2020 16:25:16 +0300 Subject: Blocking config change when applying the initial router config. --- src/nxt_controller.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/src/nxt_controller.c b/src/nxt_controller.c index f9b2cf26..d17b0cc6 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -47,6 +47,7 @@ static void nxt_controller_router_ready_handler(nxt_task_t *task, static nxt_int_t nxt_controller_conf_default(void); static void nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); +static void nxt_controller_flush_requests(nxt_task_t *task); static nxt_int_t nxt_controller_conf_send(nxt_task_t *task, nxt_conf_value_t *conf, nxt_port_rpc_handler_t handler, void *data); @@ -103,6 +104,7 @@ static nxt_uint_t nxt_controller_listening; static nxt_uint_t nxt_controller_router_ready; static nxt_controller_conf_t nxt_controller_conf; static nxt_queue_t nxt_controller_waiting_requests; +static nxt_bool_t nxt_controller_waiting_init_conf; static const nxt_event_conn_state_t nxt_controller_conn_read_state; @@ -245,6 +247,8 @@ nxt_controller_send_current_conf(nxt_task_t *task) nxt_controller_conf_init_handler, NULL); if (nxt_fast_path(rc == NXT_OK)) { + nxt_controller_waiting_init_conf = 1; + return; } @@ -322,6 +326,8 @@ nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, { nxt_runtime_t *rt; + nxt_controller_waiting_init_conf = 0; + if (msg->port_msg.type != NXT_PORT_MSG_RPC_READY) { nxt_alert(task, "failed to apply previous configuration"); @@ -343,6 +349,25 @@ nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_controller_listening = 1; } + + nxt_controller_flush_requests(task); +} + + +static void +nxt_controller_flush_requests(nxt_task_t *task) +{ + nxt_queue_t queue; + nxt_controller_request_t *req; + + nxt_queue_init(&queue); + nxt_queue_add(&queue, &nxt_controller_waiting_requests); + + nxt_queue_init(&nxt_controller_waiting_requests); + + nxt_queue_each(req, &queue, nxt_controller_request_t, link) { + nxt_controller_process_request(task, req); + } nxt_queue_loop; } @@ -961,7 +986,9 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (post || nxt_str_eq(&req->parser.method, "PUT", 3)) { - if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) { + if (!nxt_queue_is_empty(&nxt_controller_waiting_requests) + || nxt_controller_waiting_init_conf) + { nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); return; } @@ -1076,7 +1103,9 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (nxt_str_eq(&req->parser.method, "DELETE", 6)) { - if (!nxt_queue_is_empty(&nxt_controller_waiting_requests)) { + if (!nxt_queue_is_empty(&nxt_controller_waiting_requests) + || nxt_controller_waiting_init_conf) + { nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); return; } @@ -1469,7 +1498,6 @@ static void nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_queue_t queue; nxt_controller_request_t *req; nxt_controller_response_t resp; @@ -1502,14 +1530,7 @@ nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, nxt_controller_response(task, req, &resp); - nxt_queue_init(&queue); - nxt_queue_add(&queue, &nxt_controller_waiting_requests); - - nxt_queue_init(&nxt_controller_waiting_requests); - - nxt_queue_each(req, &queue, nxt_controller_request_t, link) { - nxt_controller_process_request(task, req); - } nxt_queue_loop; + nxt_controller_flush_requests(task); } -- cgit From 3ec72362b93432ddd8f8fb06f8dbc0bcd8a7e95d Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 12 May 2020 16:25:24 +0300 Subject: Waiting for router instead of reporting to user on config update. --- src/nxt_controller.c | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/nxt_controller.c b/src/nxt_controller.c index d17b0cc6..ea70cf78 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -76,6 +76,7 @@ static void nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req); static void nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, nxt_str_t *path); +static nxt_bool_t nxt_controller_check_postpone_request(nxt_task_t *task); #if (NXT_TLS) static void nxt_controller_process_cert(nxt_task_t *task, nxt_controller_request_t *req, nxt_str_t *path); @@ -270,6 +271,8 @@ nxt_controller_send_current_conf(nxt_task_t *task) } nxt_controller_listening = 1; + + nxt_controller_flush_requests(task); } @@ -386,9 +389,8 @@ nxt_controller_conf_send(nxt_task_t *task, nxt_conf_value_t *conf, router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; - if (nxt_slow_path(router_port == NULL || !nxt_controller_router_ready)) { - return NXT_DECLINED; - } + nxt_assert(router_port != NULL); + nxt_assert(nxt_controller_router_ready); controller_port = rt->port_by_type[NXT_PROCESS_CONTROLLER]; @@ -986,9 +988,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (post || nxt_str_eq(&req->parser.method, "PUT", 3)) { - if (!nxt_queue_is_empty(&nxt_controller_waiting_requests) - || nxt_controller_waiting_init_conf) - { + if (nxt_controller_check_postpone_request(task)) { nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); return; } @@ -1085,10 +1085,6 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (nxt_slow_path(rc != NXT_OK)) { nxt_mp_destroy(mp); - if (rc == NXT_DECLINED) { - goto no_router; - } - /* rc == NXT_ERROR */ goto alloc_fail; } @@ -1103,9 +1099,7 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (nxt_str_eq(&req->parser.method, "DELETE", 6)) { - if (!nxt_queue_is_empty(&nxt_controller_waiting_requests) - || nxt_controller_waiting_init_conf) - { + if (nxt_controller_check_postpone_request(task)) { nxt_queue_insert_tail(&nxt_controller_waiting_requests, &req->link); return; } @@ -1172,10 +1166,6 @@ nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req, if (nxt_slow_path(rc != NXT_OK)) { nxt_mp_destroy(mp); - if (rc == NXT_DECLINED) { - goto no_router; - } - /* rc == NXT_ERROR */ goto alloc_fail; } @@ -1222,16 +1212,27 @@ alloc_fail: resp.offset = -1; nxt_controller_response(task, req, &resp); - return; +} -no_router: - resp.status = 500; - resp.title = (u_char *) "Router process isn't available."; - resp.offset = -1; +static nxt_bool_t +nxt_controller_check_postpone_request(nxt_task_t *task) +{ + nxt_port_t *router_port; + nxt_runtime_t *rt; - nxt_controller_response(task, req, &resp); - return; + if (!nxt_queue_is_empty(&nxt_controller_waiting_requests) + || nxt_controller_waiting_init_conf + || !nxt_controller_router_ready) + { + return 1; + } + + rt = task->thread->runtime; + + router_port = rt->port_by_type[NXT_PROCESS_ROUTER]; + + return (router_port == NULL); } -- cgit From 0174c971b5ec0d604e4e9becfa41e0bc31179e57 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 14 May 2020 13:15:00 +0300 Subject: Configuration: URI encoding in the "pass" option. This is useful to escape "/" in path fragments. For example, in order to reference the application named "foo/bar": { "pass": "applications/foo%2Fbar" } --- src/nxt_conf_validation.c | 60 +++++++++++-------------- src/nxt_http.h | 5 ++- src/nxt_http_route.c | 112 ++++++++++++++++++++++++++++++++++------------ src/nxt_router.c | 7 ++- 4 files changed, 119 insertions(+), 65 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index bc03bdfb..476fc97b 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1032,79 +1032,73 @@ static nxt_int_t nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) { - u_char *p; - nxt_str_t pass, first, second; + nxt_str_t pass; + nxt_int_t ret; + nxt_str_t segments[2]; nxt_conf_get_string(value, &pass); - p = nxt_memchr(pass.start, '/', pass.length); - - if (p != NULL) { - first.length = p - pass.start; - first.start = pass.start; + ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 2); - if (pass.length - first.length == 1) { - goto error; + if (ret != NXT_OK) { + if (ret == NXT_DECLINED) { + return nxt_conf_vldt_error(vldt, "Request \"pass\" value \"%V\" " + "is invalid.", &pass); } - second.length = pass.length - first.length - 1; - second.start = p + 1; - - } else { - first = pass; - second.length = 0; + return NXT_ERROR; } - if (nxt_str_eq(&first, "applications", 12)) { + if (nxt_str_eq(&segments[0], "applications", 12)) { - if (second.length == 0) { + if (segments[1].length == 0) { goto error; } - value = nxt_conf_get_object_member(vldt->conf, &first, NULL); + value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } - value = nxt_conf_get_object_member(value, &second, NULL); + value = nxt_conf_get_object_member(value, &segments[1], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } return NXT_OK; } - if (nxt_str_eq(&first, "upstreams", 9)) { + if (nxt_str_eq(&segments[0], "upstreams", 9)) { - if (second.length == 0) { + if (segments[1].length == 0) { goto error; } - value = nxt_conf_get_object_member(vldt->conf, &first, NULL); + value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } - value = nxt_conf_get_object_member(value, &second, NULL); + value = nxt_conf_get_object_member(value, &segments[1], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } return NXT_OK; } - if (nxt_str_eq(&first, "routes", 6)) { - value = nxt_conf_get_object_member(vldt->conf, &first, NULL); + if (nxt_str_eq(&segments[0], "routes", 6)) { + value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } - if (second.length == 0) { + if (segments[1].length == 0) { if (nxt_conf_type(value) != NXT_CONF_ARRAY) { goto error; } @@ -1116,9 +1110,9 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, goto error; } - value = nxt_conf_get_object_member(value, &second, NULL); + value = nxt_conf_get_object_member(value, &segments[1], NULL); - if (nxt_slow_path(value == NULL)) { + if (value == NULL) { goto error; } diff --git a/src/nxt_http.h b/src/nxt_http.h index 841f5b40..6f593cd2 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -277,7 +277,10 @@ nxt_http_routes_t *nxt_http_routes_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *routes_conf); nxt_http_action_t *nxt_http_action_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_str_t *name); -void nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf); +nxt_int_t nxt_http_routes_resolve(nxt_task_t *task, + nxt_router_temp_conf_t *tmcf); +nxt_int_t nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, + nxt_str_t *segments, nxt_uint_t n); nxt_http_action_t *nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_str_t *name); void nxt_http_routes_cleanup(nxt_task_t *task, nxt_http_routes_t *routes); diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index ca43c060..bb6e9dc9 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -201,9 +201,9 @@ static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, nxt_http_route_pattern_case_t pattern_case); -static void nxt_http_route_resolve(nxt_task_t *task, +static nxt_int_t nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route); -static void nxt_http_action_resolve(nxt_task_t *task, +static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action); static void nxt_http_route_find(nxt_http_routes_t *routes, nxt_str_t *name, nxt_http_action_t *action); @@ -1097,9 +1097,10 @@ nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, } -void +nxt_int_t nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) { + nxt_int_t ret; nxt_http_route_t **route, **end; nxt_http_routes_t *routes; @@ -1110,75 +1111,128 @@ nxt_http_routes_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf) end = route + routes->items; while (route < end) { - nxt_http_route_resolve(task, tmcf, *route); + ret = nxt_http_route_resolve(task, tmcf, *route); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } route++; } } + + return NXT_OK; } -static void +static nxt_int_t nxt_http_route_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_route_t *route) { + nxt_int_t ret; nxt_http_route_match_t **match, **end; match = &route->match[0]; end = match + route->items; while (match < end) { - nxt_http_action_resolve(task, tmcf, &(*match)->action); + ret = nxt_http_action_resolve(task, tmcf, &(*match)->action); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } match++; } + + return NXT_OK; } -static void +static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { - nxt_str_t name; + nxt_int_t ret; + nxt_str_t segments[2]; if (action->handler != NULL) { if (action->handler == nxt_http_static_handler && action->u.fallback != NULL) { - nxt_http_action_resolve(task, tmcf, action->u.fallback); + return nxt_http_action_resolve(task, tmcf, action->u.fallback); } - return; + return NXT_OK; } - name = action->name; - - if (nxt_str_start(&name, "applications/", 13)) { - name.length -= 13; - name.start += 13; + ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 2); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } - nxt_router_listener_application(tmcf, &name, action); + if (nxt_str_eq(&segments[0], "applications", 12)) { + nxt_router_listener_application(tmcf, &segments[1], action); nxt_router_app_use(task, action->u.application, 1); - } else if (nxt_str_start(&name, "upstreams/", 10)) { - name.length -= 10; - name.start += 10; + } else if (nxt_str_eq(&segments[0], "upstreams", 9)) { + nxt_upstream_find(tmcf->router_conf->upstreams, &segments[1], action); - nxt_upstream_find(tmcf->router_conf->upstreams, &name, action); + } else if (nxt_str_eq(&segments[0], "routes", 6)) { + nxt_http_route_find(tmcf->router_conf->routes, &segments[1], action); + } - } else if (nxt_str_start(&name, "routes", 6)) { + return NXT_OK; +} - if (name.length == 6) { - name.length = 0; - name.start = NULL; - } else if (name.start[6] == '/') { - name.length -= 7; - name.start += 7; - } +nxt_int_t +nxt_http_pass_segments(nxt_mp_t *mp, nxt_str_t *pass, nxt_str_t *segments, + nxt_uint_t n) +{ + u_char *p; + nxt_str_t rest; - nxt_http_route_find(tmcf->router_conf->routes, &name, action); + if (nxt_slow_path(nxt_str_dup(mp, &rest, pass) == NULL)) { + return NXT_ERROR; } + + nxt_memzero(segments, n * sizeof(nxt_str_t)); + + do { + p = nxt_memchr(rest.start, '/', rest.length); + + if (p != NULL) { + n--; + + if (n == 0) { + return NXT_DECLINED; + } + + segments->length = p - rest.start; + segments->start = rest.start; + + rest.length -= segments->length + 1; + rest.start = p + 1; + + } else { + n = 0; + *segments = rest; + } + + if (segments->length == 0) { + return NXT_DECLINED; + } + + p = nxt_decode_uri(segments->start, segments->start, segments->length); + if (p == NULL) { + return NXT_DECLINED; + } + + segments->length = p - segments->start; + segments++; + + } while (n); + + return NXT_OK; } diff --git a/src/nxt_router.c b/src/nxt_router.c index 93b750a0..a699effc 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1793,6 +1793,11 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, } } + ret = nxt_http_routes_resolve(task, tmcf); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } + value = nxt_conf_get_path(conf, &access_log_path); if (value != NULL) { @@ -1827,8 +1832,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, tmcf->router_conf->access_log = access_log; } - nxt_http_routes_resolve(task, tmcf); - nxt_queue_add(&tmcf->deleting, &router->sockets); nxt_queue_init(&router->sockets); -- cgit From 376d758dd72ac27f2bd5bb833ba68f5c9b531880 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 14 May 2020 13:15:01 +0300 Subject: PHP: implemented "targets" option. This allows to specify multiple subsequent targets inside PHP applications. For example: { "listeners": { "*:80": { "pass": "routes" } }, "routes": [ { "match": { "uri": "/info" }, "action": { "pass": "applications/my_app/phpinfo" } }, { "match": { "uri": "/hello" }, "action": { "pass": "applications/my_app/hello" } }, { "action": { "pass": "applications/my_app/rest" } } ], "applications": { "my_app": { "type": "php", "targets": { "phpinfo": { "script": "phpinfo.php", "root": "/www/data/admin", }, "hello": { "script": "hello.php", "root": "/www/data/test", }, "rest": { "root": "/www/data/example.com", "index": "index.php" }, } } } } --- src/nxt_application.h | 6 +- src/nxt_conf.h | 2 +- src/nxt_conf_validation.c | 202 ++++++++++++++++++--- src/nxt_http.h | 2 + src/nxt_http_request.c | 2 + src/nxt_http_route.c | 21 ++- src/nxt_main_process.c | 20 +-- src/nxt_php_sapi.c | 446 +++++++++++++++++++++++++--------------------- src/nxt_router.c | 64 +++++-- src/nxt_router.h | 3 + src/nxt_unit_request.h | 1 + 11 files changed, 509 insertions(+), 260 deletions(-) (limited to 'src') diff --git a/src/nxt_application.h b/src/nxt_application.h index e7177887..972a712b 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -54,9 +54,7 @@ typedef struct { typedef struct { - char *root; - nxt_str_t script; - nxt_str_t index; + nxt_conf_value_t *targets; nxt_conf_value_t *options; } nxt_php_app_conf_t; @@ -101,6 +99,8 @@ struct nxt_common_app_conf_s { nxt_ruby_app_conf_t ruby; nxt_java_app_conf_t java; } u; + + nxt_conf_value_t *self; }; diff --git a/src/nxt_conf.h b/src/nxt_conf.h index 201a3a14..149af39a 100644 --- a/src/nxt_conf.h +++ b/src/nxt_conf.h @@ -118,7 +118,7 @@ NXT_EXPORT double nxt_conf_get_number(nxt_conf_value_t *value); NXT_EXPORT uint8_t nxt_conf_get_boolean(nxt_conf_value_t *value); // FIXME reimplement and reorder functions below -nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value); +NXT_EXPORT nxt_uint_t nxt_conf_object_members_count(nxt_conf_value_t *value); nxt_conf_value_t *nxt_conf_create_object(nxt_mp_t *mp, nxt_uint_t count); void nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name, nxt_conf_value_t *value, uint32_t index); diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 476fc97b..0f46560d 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -23,13 +23,24 @@ typedef enum { NXT_CONF_VLDT_OBJECT = 1 << NXT_CONF_OBJECT, } nxt_conf_vldt_type_t; +#define NXT_CONF_VLDT_ANY_TYPE (NXT_CONF_VLDT_NULL \ + |NXT_CONF_VLDT_BOOLEAN \ + |NXT_CONF_VLDT_NUMBER \ + |NXT_CONF_VLDT_STRING \ + |NXT_CONF_VLDT_ARRAY \ + |NXT_CONF_VLDT_OBJECT) + + +typedef nxt_int_t (*nxt_conf_vldt_handler_t)(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, + void *data); + typedef struct { - nxt_str_t name; - nxt_conf_vldt_type_t type; - nxt_int_t (*validator)(nxt_conf_validation_t *vldt, - nxt_conf_value_t *value, void *data); - void *data; + nxt_str_t name; + nxt_conf_vldt_type_t type; + nxt_conf_vldt_handler_t validator; + void *data; } nxt_conf_vldt_object_t; @@ -106,6 +117,14 @@ static nxt_int_t nxt_conf_vldt_environment(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_php(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_targets_exclusive( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_java_classpath(nxt_conf_validation_t *vldt, @@ -630,6 +649,24 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_python_members[] = { }; +static nxt_conf_vldt_object_t nxt_conf_vldt_php_target_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + + { nxt_string("script"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + + { nxt_string("index"), + NXT_CONF_VLDT_STRING, + NULL, + NULL } +}; + + static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { { nxt_string("file"), NXT_CONF_VLDT_STRING, @@ -650,7 +687,17 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_options_members[] = { }; -static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { +static nxt_conf_vldt_object_t nxt_conf_vldt_php_common_members[] = { + { nxt_string("options"), + NXT_CONF_VLDT_OBJECT, + &nxt_conf_vldt_object, + (void *) &nxt_conf_vldt_php_options_members }, + + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_notargets_members[] = { { nxt_string("root"), NXT_CONF_VLDT_STRING, NULL, @@ -666,12 +713,32 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { NULL, NULL }, - { nxt_string("options"), + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) +}; + + +static nxt_conf_vldt_object_t nxt_conf_vldt_php_members[] = { + { nxt_string("root"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "root" }, + + { nxt_string("script"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "script" }, + + { nxt_string("index"), + NXT_CONF_VLDT_ANY_TYPE, + &nxt_conf_vldt_php_targets_exclusive, + (void *) "index" }, + + { nxt_string("targets"), NXT_CONF_VLDT_OBJECT, - &nxt_conf_vldt_object, - (void *) &nxt_conf_vldt_php_options_members }, + &nxt_conf_vldt_php_targets, + NULL }, - NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_common_members) + NXT_CONF_VLDT_NEXT(&nxt_conf_vldt_php_common_members) }; @@ -755,7 +822,7 @@ nxt_conf_validate(nxt_conf_validation_t *vldt) } -#define NXT_CONF_VLDT_ANY_TYPE \ +#define NXT_CONF_VLDT_ANY_TYPE_STR \ "either a null, a boolean, an integer, " \ "a number, a string, an array, or an object" @@ -768,7 +835,7 @@ nxt_conf_vldt_type(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_str_t expected; nxt_bool_t serial; nxt_uint_t value_type, n, t; - u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE)]; + u_char buf[nxt_length(NXT_CONF_VLDT_ANY_TYPE_STR)]; static nxt_str_t type_name[] = { nxt_string("a null"), @@ -1034,11 +1101,13 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, { nxt_str_t pass; nxt_int_t ret; - nxt_str_t segments[2]; + nxt_str_t segments[3]; + + static nxt_str_t targets_str = nxt_string("targets"); nxt_conf_get_string(value, &pass); - ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 2); + ret = nxt_http_pass_segments(vldt->pool, &pass, segments, 3); if (ret != NXT_OK) { if (ret == NXT_DECLINED) { @@ -1067,12 +1136,26 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, goto error; } + if (segments[2].length > 0) { + value = nxt_conf_get_object_member(value, &targets_str, NULL); + + if (value == NULL) { + goto error; + } + + value = nxt_conf_get_object_member(value, &segments[2], NULL); + + if (value == NULL) { + goto error; + } + } + return NXT_OK; } if (nxt_str_eq(&segments[0], "upstreams", 9)) { - if (segments[1].length == 0) { + if (segments[1].length == 0 || segments[2].length != 0) { goto error; } @@ -1092,6 +1175,11 @@ nxt_conf_vldt_pass(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, } if (nxt_str_eq(&segments[0], "routes", 6)) { + + if (segments[2].length != 0) { + goto error; + } + value = nxt_conf_get_object_member(vldt->conf, &segments[0], NULL); if (value == NULL) { @@ -1482,13 +1570,17 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, static nxt_str_t type_str = nxt_string("type"); - static void *members[] = { - nxt_conf_vldt_external_members, - nxt_conf_vldt_python_members, - nxt_conf_vldt_php_members, - nxt_conf_vldt_perl_members, - nxt_conf_vldt_ruby_members, - nxt_conf_vldt_java_members, + static struct { + nxt_conf_vldt_handler_t validator; + nxt_conf_vldt_object_t *members; + + } types[] = { + { nxt_conf_vldt_object, nxt_conf_vldt_external_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_python_members }, + { nxt_conf_vldt_php, NULL }, + { nxt_conf_vldt_object, nxt_conf_vldt_perl_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_ruby_members }, + { nxt_conf_vldt_object, nxt_conf_vldt_java_members }, }; ret = nxt_conf_vldt_type(vldt, name, value, NXT_CONF_VLDT_OBJECT); @@ -1522,7 +1614,7 @@ nxt_conf_vldt_app(nxt_conf_validation_t *vldt, nxt_str_t *name, &type); } - return nxt_conf_vldt_object(vldt, value, members[lang->type]); + return types[lang->type].validator(vldt, value, types[lang->type].members); } @@ -1933,6 +2025,70 @@ nxt_conf_vldt_argument(nxt_conf_validation_t *vldt, nxt_conf_value_t *value) } +static nxt_int_t +nxt_conf_vldt_php(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_conf_value_t *targets; + + static nxt_str_t targets_str = nxt_string("targets"); + + targets = nxt_conf_get_object_member(value, &targets_str, NULL); + + if (targets != NULL) { + return nxt_conf_vldt_object(vldt, value, nxt_conf_vldt_php_members); + } + + return nxt_conf_vldt_object(vldt, value, + nxt_conf_vldt_php_notargets_members); +} + + +static nxt_int_t +nxt_conf_vldt_php_targets_exclusive(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + return nxt_conf_vldt_error(vldt, "The \"%s\" option is mutually exclusive " + "with the \"targets\" object.", data); +} + + +static nxt_int_t +nxt_conf_vldt_php_targets(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, + void *data) +{ + nxt_uint_t n; + + n = nxt_conf_object_members_count(value); + + if (n > 254) { + return nxt_conf_vldt_error(vldt, "The \"targets\" object must not " + "contain more than 254 members."); + } + + return nxt_conf_vldt_object_iterator(vldt, value, + &nxt_conf_vldt_php_target); +} + + +static nxt_int_t +nxt_conf_vldt_php_target(nxt_conf_validation_t *vldt, nxt_str_t *name, + nxt_conf_value_t *value) +{ + if (name->length == 0) { + return nxt_conf_vldt_error(vldt, + "The PHP target name must not be empty."); + } + + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"%V\" PHP target must be " + "an object.", name); + } + + return nxt_conf_vldt_object(vldt, value, &nxt_conf_vldt_php_target_members); +} + + static nxt_int_t nxt_conf_vldt_php_option(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value) diff --git a/src/nxt_http.h b/src/nxt_http.h index 6f593cd2..68051e69 100644 --- a/src/nxt_http.h +++ b/src/nxt_http.h @@ -175,6 +175,7 @@ struct nxt_http_request_s { nxt_http_status_t status:16; uint8_t pass_count; /* 8 bits */ + uint8_t app_target; nxt_http_protocol_t protocol:8; /* 2 bits */ uint8_t logged; /* 1 bit */ uint8_t header_sent; /* 1 bit */ @@ -201,6 +202,7 @@ struct nxt_http_action_s { } u; nxt_str_t name; + nxt_int_t target; }; diff --git a/src/nxt_http_request.c b/src/nxt_http_request.c index 050587f7..cc1ae17d 100644 --- a/src/nxt_http_request.c +++ b/src/nxt_http_request.c @@ -341,6 +341,8 @@ nxt_http_application_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&r->server_name, "localhost"); } + r->app_target = action->target; + nxt_router_process_http_request(task, r, action->u.application); return NULL; diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index bb6e9dc9..06bc91da 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1151,8 +1151,10 @@ static nxt_int_t nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_http_action_t *action) { - nxt_int_t ret; - nxt_str_t segments[2]; + nxt_str_t *targets; + nxt_int_t ret; + nxt_uint_t i; + nxt_str_t segments[3]; if (action->handler != NULL) { if (action->handler == nxt_http_static_handler @@ -1164,7 +1166,7 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, return NXT_OK; } - ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 2); + ret = nxt_http_pass_segments(tmcf->mem_pool, &action->name, segments, 3); if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } @@ -1173,6 +1175,17 @@ nxt_http_action_resolve(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_listener_application(tmcf, &segments[1], action); nxt_router_app_use(task, action->u.application, 1); + if (segments[2].length != 0) { + targets = action->u.application->targets; + + for (i = 0; !nxt_strstr_eq(&segments[2], &targets[i]); i++); + + action->target = i; + + } else { + action->target = 0; + } + } else if (nxt_str_eq(&segments[0], "upstreams", 9)) { nxt_upstream_find(tmcf->router_conf->upstreams, &segments[1], action); @@ -1298,6 +1311,8 @@ nxt_http_pass_application(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, nxt_router_listener_application(tmcf, name, action); nxt_router_app_use(task, action->u.application, 1); + action->target = 0; + return action; } diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index eed37752..c35954c0 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -275,21 +275,9 @@ static nxt_conf_map_t nxt_python_app_conf[] = { static nxt_conf_map_t nxt_php_app_conf[] = { { - nxt_string("root"), - NXT_CONF_MAP_CSTRZ, - offsetof(nxt_common_app_conf_t, u.php.root), - }, - - { - nxt_string("script"), - NXT_CONF_MAP_STR, - offsetof(nxt_common_app_conf_t, u.php.script), - }, - - { - nxt_string("index"), - NXT_CONF_MAP_STR, - offsetof(nxt_common_app_conf_t, u.php.index), + nxt_string("targets"), + NXT_CONF_MAP_PTR, + offsetof(nxt_common_app_conf_t, u.php.targets), }, { @@ -457,6 +445,8 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) } } + app_conf.self = conf; + ret = nxt_main_start_worker_process(task, task->thread->runtime, &app_conf, msg->port_msg.stream); diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index f5053652..ccbdd475 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -29,8 +29,20 @@ #define NXT_PHP7 1 #endif + +typedef struct { + nxt_str_t root; + nxt_str_t index; + nxt_str_t script_name; + nxt_str_t script_dirname; + nxt_str_t script_filename; +} nxt_php_target_t; + + typedef struct { char *cookie; + nxt_str_t *root; + nxt_str_t *index; nxt_str_t path_info; nxt_str_t script_name; nxt_str_t script_filename; @@ -53,26 +65,27 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, + nxt_conf_value_t *conf); +static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, + int type); +static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, + int type); +static void nxt_php_disable(nxt_task_t *task, const char *type, + nxt_str_t *value, char **ptr, nxt_php_disable_t disable); +static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); static void nxt_php_str_trim_trail(nxt_str_t *str, u_char t); static void nxt_php_str_trim_lead(nxt_str_t *str, u_char t); -static nxt_int_t nxt_php_dirname(const nxt_str_t *file, nxt_str_t *dir); nxt_inline u_char *nxt_realpath(const void *c); -nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, - const nxt_str_t *dirname); -static void nxt_php_script_request_handler(nxt_unit_request_info_t *req); -static void nxt_php_path_request_handler(nxt_unit_request_info_t *req); -static nxt_int_t nxt_php_request_init(nxt_php_run_ctx_t *ctx, +static void nxt_php_request_handler(nxt_unit_request_info_t *req); +static void nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r); +static void nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r); +nxt_inline void nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir); static int nxt_php_startup(sapi_module_struct *sapi_module); -static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, - int type); -static nxt_int_t nxt_php_alter_option(nxt_str_t *name, nxt_str_t *value, - int type); -static void nxt_php_disable(nxt_task_t *task, const char *type, - nxt_str_t *value, char **ptr, nxt_php_disable_t disable); static int nxt_php_send_headers(sapi_headers_struct *sapi_headers TSRMLS_DC); static void *nxt_php_hash_str_find_ptr(const HashTable *ht, const nxt_str_t *str); @@ -210,13 +223,6 @@ static sapi_module_struct nxt_php_sapi_module = }; -static nxt_str_t nxt_php_root; -static nxt_str_t nxt_php_script_name; -static nxt_str_t nxt_php_script_dirname; -static nxt_str_t nxt_php_script_filename; -static nxt_str_t nxt_php_index = nxt_string("index.php"); - - static uint32_t compat[] = { NXT_VERNUM, NXT_DEBUG, }; @@ -232,6 +238,9 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { }; +static nxt_php_target_t *nxt_php_targets; +static nxt_int_t nxt_php_last_target = -1; + static nxt_task_t *nxt_php_task; #if defined(ZTS) && PHP_VERSION_ID < 70400 static void ***tsrm_ls; @@ -241,11 +250,11 @@ static void ***tsrm_ls; static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) { - u_char *p, *tmp; - nxt_str_t ini_path; - nxt_str_t *root, *script_filename, *script_dirname, *script_name; - nxt_str_t *index; + u_char *p; + uint32_t next; + nxt_str_t ini_path, name; nxt_int_t ret; + nxt_uint_t n; nxt_port_t *my_port, *main_port; nxt_runtime_t *rt; nxt_unit_ctx_t *unit_ctx; @@ -261,105 +270,33 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) c = &conf->u.php; - if (c->root == NULL) { - nxt_alert(task, "php root is empty"); - return NXT_ERROR; - } - - root = &nxt_php_root; - script_filename = &nxt_php_script_filename; - script_dirname = &nxt_php_script_dirname; - script_name = &nxt_php_script_name; - index = &nxt_php_index; + n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1; - root->start = nxt_realpath(c->root); - if (nxt_slow_path(root->start == NULL)) { - nxt_alert(task, "root realpath(%s) failed %E", c->root, nxt_errno); + nxt_php_targets = nxt_zalloc(sizeof(nxt_php_target_t) * n); + if (nxt_slow_path(nxt_php_targets == NULL)) { return NXT_ERROR; } - root->length = nxt_strlen(root->start); - - nxt_php_str_trim_trail(root, '/'); - - if (c->script.length > 0) { - nxt_php_str_trim_lead(&c->script, '/'); - - tmp = nxt_malloc(root->length + 1 + c->script.length + 1); - if (nxt_slow_path(tmp == NULL)) { - return NXT_ERROR; - } - - p = tmp; - - p = nxt_cpymem(p, root->start, root->length); - *p++ = '/'; - - p = nxt_cpymem(p, c->script.start, c->script.length); - *p = '\0'; - - script_filename->start = nxt_realpath(tmp); - if (nxt_slow_path(script_filename->start == NULL)) { - nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); - return NXT_ERROR; - } - - nxt_free(tmp); - - script_filename->length = nxt_strlen(script_filename->start); - - if (!nxt_str_start(script_filename, root->start, root->length)) { - nxt_alert(task, "script is not under php root"); - return NXT_ERROR; - } + if (c->targets != NULL) { + next = 0; - ret = nxt_php_dirname(script_filename, script_dirname); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } + for (n = 0; /* void */; n++) { + value = nxt_conf_next_object_member(c->targets, &name, &next); + if (value == NULL) { + break; + } - script_name->length = c->script.length + 1; - script_name->start = nxt_malloc(script_name->length); - if (nxt_slow_path(script_name->start == NULL)) { - return NXT_ERROR; + ret = nxt_php_set_target(task, &nxt_php_targets[n], value); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } } - script_name->start[0] = '/'; - nxt_memcpy(script_name->start + 1, c->script.start, c->script.length); - - nxt_log_error(NXT_LOG_INFO, task->log, - "(ABS_MODE) php script \"%V\" root: \"%V\"", - script_name, root); - } else { - nxt_log_error(NXT_LOG_INFO, task->log, - "(non ABS_MODE) php root: \"%V\"", root); - } - - if (c->index.length > 0) { - index->length = c->index.length; - index->start = nxt_malloc(index->length); - if (nxt_slow_path(index->start == NULL)) { - return NXT_ERROR; - } - - nxt_memcpy(index->start, c->index.start, c->index.length); - } - - nxt_memzero(&php_init, sizeof(nxt_unit_init_t)); - - if (nxt_php_script_filename.start != NULL) { - if (nxt_slow_path(chdir((char *) script_dirname->start) != 0)) { - nxt_alert(task, "failed to chdir(%V) %E", script_dirname, - nxt_errno); - + ret = nxt_php_set_target(task, &nxt_php_targets[0], conf->self); + if (nxt_slow_path(ret != NXT_OK)) { return NXT_ERROR; } - - php_init.callbacks.request_handler = nxt_php_script_request_handler; - - } else { - php_init.callbacks.request_handler = nxt_php_path_request_handler; } #ifdef ZTS @@ -430,6 +367,10 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) return NXT_ERROR; } + nxt_memzero(&php_init, sizeof(nxt_unit_init_t)); + + php_init.callbacks.request_handler = nxt_php_request_handler; + php_init.ready_port.id.pid = main_port->pid; php_init.ready_port.id.id = main_port->id; php_init.ready_port.out_fd = main_port->pair[1]; @@ -462,6 +403,125 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) } +static nxt_int_t +nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, + nxt_conf_value_t *conf) +{ + u_char *tmp, *p; + nxt_str_t str; + nxt_int_t ret; + nxt_conf_value_t *value; + + static nxt_str_t root_str = nxt_string("root"); + static nxt_str_t script_str = nxt_string("script"); + static nxt_str_t index_str = nxt_string("index"); + + value = nxt_conf_get_object_member(conf, &root_str, NULL); + + if (value == NULL) { + nxt_alert(task, "no php root specified"); + return NXT_ERROR; + } + + nxt_conf_get_string(value, &str); + + tmp = nxt_malloc(str.length + 1); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + p = tmp; + + p = nxt_cpymem(p, str.start, str.length); + *p = '\0'; + + p = nxt_realpath(tmp); + if (nxt_slow_path(p == NULL)) { + nxt_alert(task, "root realpath(%s) failed %E", tmp, nxt_errno); + return NXT_ERROR; + } + + nxt_free(tmp); + + target->root.length = nxt_strlen(p); + target->root.start = p; + + nxt_php_str_trim_trail(&target->root, '/'); + + value = nxt_conf_get_object_member(conf, &script_str, NULL); + + if (value != NULL) { + nxt_conf_get_string(value, &str); + + nxt_php_str_trim_lead(&str, '/'); + + tmp = nxt_malloc(target->root.length + 1 + str.length + 1); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + p = tmp; + + p = nxt_cpymem(p, target->root.start, target->root.length); + *p++ = '/'; + + p = nxt_cpymem(p, str.start, str.length); + *p = '\0'; + + p = nxt_realpath(tmp); + if (nxt_slow_path(p == NULL)) { + nxt_alert(task, "script realpath(%s) failed %E", tmp, nxt_errno); + return NXT_ERROR; + } + + nxt_free(tmp); + + target->script_filename.length = nxt_strlen(p); + target->script_filename.start = p; + + if (!nxt_str_start(&target->script_filename, + target->root.start, target->root.length)) + { + nxt_alert(task, "script is not under php root"); + return NXT_ERROR; + } + + ret = nxt_php_dirname(&target->script_filename, + &target->script_dirname); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + target->script_name.length = target->script_filename.length + - target->root.length; + target->script_name.start = target->script_filename.start + + target->root.length; + + } else { + value = nxt_conf_get_object_member(conf, &index_str, NULL); + + if (value != NULL) { + nxt_conf_get_string(value, &str); + + tmp = nxt_malloc(str.length); + if (nxt_slow_path(tmp == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(tmp, str.start, str.length); + + target->index.length = str.length; + target->index.start = tmp; + + } else { + nxt_str_set(&target->index, "index.php"); + } + } + + return NXT_OK; +} + + static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, int type) { @@ -686,56 +746,44 @@ nxt_realpath(const void *c) static void -nxt_php_script_request_handler(nxt_unit_request_info_t *req) +nxt_php_request_handler(nxt_unit_request_info_t *req) { - zend_file_handle file_handle; - nxt_php_run_ctx_t ctx; + nxt_php_target_t *target; + nxt_php_run_ctx_t ctx; + nxt_unit_request_t *r; + + r = req->request; + target = &nxt_php_targets[r->app_target]; nxt_memzero(&ctx, sizeof(ctx)); ctx.req = req; - ctx.script_filename = nxt_php_script_filename; - ctx.script_dirname = nxt_php_script_dirname; - ctx.script_name = nxt_php_script_name; - - nxt_memzero(&file_handle, sizeof(file_handle)); + ctx.root = &target->root; + ctx.index = &target->index; - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = (char *) ctx.script_filename.start; - - if (nxt_slow_path(nxt_php_request_init(&ctx, req->request) != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + if (target->script_filename.length == 0) { + nxt_php_dynamic_request(&ctx, r); return; } - php_execute_script(&file_handle TSRMLS_CC); + ctx.script_filename = target->script_filename; + ctx.script_dirname = target->script_dirname; + ctx.script_name = target->script_name; - if (ctx.chdir) { - nxt_php_vcwd_chdir(ctx.req, &nxt_php_script_dirname); - } + ctx.chdir = (r->app_target != nxt_php_last_target); - php_request_shutdown(NULL); + nxt_php_execute(&ctx, r); - nxt_unit_request_done(req, NXT_UNIT_OK); + nxt_php_last_target = ctx.chdir ? -1 : r->app_target; } static void -nxt_php_path_request_handler(nxt_unit_request_info_t *req) +nxt_php_dynamic_request(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) { - u_char *p; - nxt_str_t path, script_name; - nxt_int_t ret; - zend_file_handle file_handle; - nxt_php_run_ctx_t run_ctx, *ctx; - nxt_unit_request_t *r; - - nxt_memzero(&run_ctx, sizeof(run_ctx)); - - ctx = &run_ctx; - ctx->req = req; - - r = req->request; + u_char *p; + nxt_str_t path, script_name; + nxt_int_t ret; path.length = r->path_length; path.start = nxt_unit_sptr_get(&r->path); @@ -750,26 +798,26 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) ctx->path_info.length = r->path_length - path.length; } else if (path.start[path.length - 1] == '/') { - script_name = nxt_php_index; + script_name = *ctx->index; } else { if (nxt_slow_path(path.length < 4 || nxt_memcmp(path.start + (path.length - 4), ".php", 4))) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); return; } } - ctx->script_filename.length = nxt_php_root.length + ctx->script_filename.length = ctx->root->length + path.length + script_name.length; p = nxt_malloc(ctx->script_filename.length + 1); if (nxt_slow_path(p == NULL)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); return; } @@ -777,9 +825,9 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) ctx->script_filename.start = p; ctx->script_name.length = path.length + script_name.length; - ctx->script_name.start = p + nxt_php_root.length; + ctx->script_name.start = p + ctx->root->length; - p = nxt_cpymem(p, nxt_php_root.start, nxt_php_root.length); + p = nxt_cpymem(p, ctx->root->start, ctx->root->length); p = nxt_cpymem(p, path.start, path.length); if (script_name.length > 0) { @@ -788,50 +836,33 @@ nxt_php_path_request_handler(nxt_unit_request_info_t *req) *p = '\0'; - nxt_memzero(&file_handle, sizeof(file_handle)); - - file_handle.type = ZEND_HANDLE_FILENAME; - file_handle.filename = (char *) ctx->script_filename.start; + ctx->chdir = 1; ret = nxt_php_dirname(&ctx->script_filename, &ctx->script_dirname); if (nxt_slow_path(ret != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); nxt_free(ctx->script_filename.start); return; } - if (nxt_slow_path(nxt_php_request_init(ctx, req->request) != NXT_OK)) { - nxt_unit_request_done(req, NXT_UNIT_ERROR); - goto cleanup; - } - - nxt_php_vcwd_chdir(ctx->req, &ctx->script_dirname); - - php_execute_script(&file_handle TSRMLS_CC); - - php_request_shutdown(NULL); - - nxt_unit_request_done(req, NXT_UNIT_OK); - -cleanup: + nxt_php_execute(ctx, r); nxt_free(ctx->script_filename.start); nxt_free(ctx->script_dirname.start); -} - -static int -nxt_php_startup(sapi_module_struct *sapi_module) -{ - return php_module_startup(sapi_module, &nxt_php_unit_module, 1); + nxt_php_last_target = -1; } -static nxt_int_t -nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) +static void +nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) { nxt_unit_field_t *f; + zend_file_handle file_handle; + + nxt_unit_req_debug(ctx->req, "PHP execute script %s", + ctx->script_filename.start); SG(server_context) = ctx; SG(options) |= SAPI_OPTION_NO_CHDIR; @@ -860,20 +891,6 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) SG(request_info).path_translated = NULL; - nxt_unit_req_debug(ctx->req, "handle.filename = '%s'", - ctx->script_filename.start); - - if (nxt_php_script_filename.start != NULL) { - nxt_unit_req_debug(ctx->req, "run script %.*s in absolute mode", - (int) nxt_php_script_filename.length, - (char *) nxt_php_script_filename.start); - - } else { - nxt_unit_req_debug(ctx->req, "run script %.*s", - (int) ctx->script_filename.length, - (char *) ctx->script_filename.start); - } - #ifdef NXT_PHP7 if (nxt_slow_path(php_request_startup() == FAILURE)) { #else @@ -881,23 +898,45 @@ nxt_php_request_init(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) #endif nxt_unit_req_debug(ctx->req, "php_request_startup() failed"); - return NXT_ERROR; + nxt_unit_request_done(ctx->req, NXT_UNIT_ERROR); + return; } - return NXT_OK; + if (ctx->chdir) { + ctx->chdir = 0; + nxt_php_vcwd_chdir(ctx->req, ctx->script_dirname.start); + } + + nxt_memzero(&file_handle, sizeof(file_handle)); + + file_handle.type = ZEND_HANDLE_FILENAME; + file_handle.filename = (char *) ctx->script_filename.start; + + php_execute_script(&file_handle TSRMLS_CC); + + php_request_shutdown(NULL); + + nxt_unit_request_done(ctx->req, NXT_UNIT_OK); } nxt_inline void -nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, const nxt_str_t *dir) +nxt_php_vcwd_chdir(nxt_unit_request_info_t *req, u_char *dir) { - if (nxt_slow_path(VCWD_CHDIR((char *) dir->start) != 0)) { + if (nxt_slow_path(VCWD_CHDIR((char *) dir) != 0)) { nxt_unit_req_alert(req, "VCWD_CHDIR(%s) failed (%d: %s)", - dir->start, errno, strerror(errno)); + dir, errno, strerror(errno)); } } +static int +nxt_php_startup(sapi_module_struct *sapi_module) +{ + return php_module_startup(sapi_module, &nxt_php_unit_module, 1); +} + + #ifdef NXT_PHP7 static size_t nxt_php_unbuffered_write(const char *str, size_t str_length TSRMLS_DC) @@ -1061,19 +1100,16 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) * available. */ - if (nxt_php_script_name.start != NULL) { - /* ABS_MODE */ - nxt_php_set_str(req, "PHP_SELF", &nxt_php_script_name, - track_vars_array TSRMLS_CC); - - } else { + if (ctx->path_info.length != 0) { nxt_php_set_sptr(req, "PHP_SELF", &r->path, r->path_length, track_vars_array TSRMLS_CC); - } - if (ctx->path_info.length != 0) { nxt_php_set_str(req, "PATH_INFO", &ctx->path_info, track_vars_array TSRMLS_CC); + + } else { + nxt_php_set_str(req, "PHP_SELF", &ctx->script_name, + track_vars_array TSRMLS_CC); } /* @@ -1100,7 +1136,7 @@ nxt_php_register_variables(zval *track_vars_array TSRMLS_DC) * as defined in the server's configuration file. */ - nxt_php_set_str(req, "DOCUMENT_ROOT", &nxt_php_root, + nxt_php_set_str(req, "DOCUMENT_ROOT", ctx->root, track_vars_array TSRMLS_CC); nxt_php_set_sptr(req, "REQUEST_METHOD", &r->method, r->method_length, diff --git a/src/nxt_router.c b/src/nxt_router.c index a699effc..d94a34af 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -27,6 +27,7 @@ typedef struct { uint32_t requests; nxt_conf_value_t *limits_value; nxt_conf_value_t *processes_value; + nxt_conf_value_t *targets_value; } nxt_router_app_conf_t; @@ -1272,6 +1273,12 @@ static nxt_conf_map_t nxt_router_app_conf[] = { NXT_CONF_MAP_PTR, offsetof(nxt_router_app_conf_t, processes_value), }, + + { + nxt_string("targets"), + NXT_CONF_MAP_PTR, + offsetof(nxt_router_app_conf_t, targets_value), + }, }; @@ -1423,12 +1430,13 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, { u_char *p; size_t size; - nxt_mp_t *mp; - uint32_t next; + nxt_mp_t *mp, *app_mp; + uint32_t next, next_target; nxt_int_t ret; - nxt_str_t name, path; + nxt_str_t name, path, target; nxt_app_t *app, *prev; - nxt_str_t *t; + nxt_str_t *t, *s, *targets; + nxt_uint_t n, i; nxt_router_t *router; nxt_app_joint_t *app_joint; nxt_conf_value_t *conf, *http, *value, *websocket; @@ -1501,13 +1509,20 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, size = nxt_conf_json_length(application, NULL); - app = nxt_malloc(sizeof(nxt_app_t) + name.length + size); - if (app == NULL) { + app_mp = nxt_mp_create(4096, 128, 1024, 64); + if (nxt_slow_path(app_mp == NULL)) { goto fail; } + app = nxt_mp_get(app_mp, sizeof(nxt_app_t) + name.length + size); + if (app == NULL) { + goto app_fail; + } + nxt_memzero(app, sizeof(nxt_app_t)); + app->mem_pool = app_mp; + app->name.start = nxt_pointer_to(app, sizeof(nxt_app_t)); app->conf.start = nxt_pointer_to(app, sizeof(nxt_app_t) + name.length); @@ -1522,7 +1537,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, prev = nxt_router_app_find(&router->apps, &name); if (prev != NULL && nxt_strstr_eq(&app->conf, &prev->conf)) { - nxt_free(app); + nxt_mp_destroy(app_mp); nxt_queue_remove(&prev->link); nxt_queue_insert_tail(&tmcf->previous, &prev->link); @@ -1538,6 +1553,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, apcf.requests = 0; apcf.limits_value = NULL; apcf.processes_value = NULL; + apcf.targets_value = NULL; app_joint = nxt_malloc(sizeof(nxt_app_joint_t)); if (nxt_slow_path(app_joint == NULL)) { @@ -1587,6 +1603,30 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, apcf.spare_processes = apcf.processes; } + if (apcf.targets_value != NULL) { + n = nxt_conf_object_members_count(apcf.targets_value); + + targets = nxt_mp_get(app_mp, sizeof(nxt_str_t) * n); + if (nxt_slow_path(targets == NULL)) { + goto app_fail; + } + + next_target = 0; + + for (i = 0; i < n; i++) { + value = nxt_conf_next_object_member(apcf.targets_value, + &target, &next_target); + + s = nxt_str_dup(app_mp, &targets[i], &target); + if (nxt_slow_path(s == NULL)) { + goto app_fail; + } + } + + } else { + targets = NULL; + } + nxt_debug(task, "application type: %V", &apcf.type); nxt_debug(task, "application processes: %D", apcf.processes); nxt_debug(task, "application request timeout: %M", apcf.timeout); @@ -1628,6 +1668,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app->max_pending_responses = 2; app->max_requests = apcf.requests; + app->targets = targets; + engine = task->thread->engine; app->engine = engine; @@ -1839,7 +1881,7 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, app_fail: - nxt_free(app); + nxt_mp_destroy(app_mp); fail: @@ -1847,7 +1889,7 @@ fail: nxt_queue_remove(&app->link); nxt_thread_mutex_destroy(&app->mutex); - nxt_free(app); + nxt_mp_destroy(app->mem_pool); } nxt_queue_loop; @@ -4538,7 +4580,7 @@ nxt_router_free_app(nxt_task_t *task, void *obj, void *data) nxt_assert(nxt_queue_is_empty(&app->idle_ports)); nxt_thread_mutex_destroy(&app->mutex); - nxt_free(app); + nxt_mp_destroy(app->mem_pool); app_joint->app = NULL; @@ -4992,6 +5034,8 @@ nxt_router_prepare_msg(nxt_task_t *task, nxt_http_request_t *r, req = (nxt_unit_request_t *) out->mem.free; out->mem.free += req_size; + req->app_target = r->app_target; + req->content_length = content_length; p = (u_char *) (req->fields + fields_count); diff --git a/src/nxt_router.h b/src/nxt_router.h index 08142ce3..6004a459 100644 --- a/src/nxt_router.h +++ b/src/nxt_router.h @@ -133,8 +133,11 @@ struct nxt_app_s { nxt_nsec_t res_timeout; nxt_msec_t idle_timeout; + nxt_str_t *targets; + nxt_app_type_t type:8; + nxt_mp_t *mem_pool; nxt_queue_link_t link; nxt_str_t conf; diff --git a/src/nxt_unit_request.h b/src/nxt_unit_request.h index 52017a42..fede00d2 100644 --- a/src/nxt_unit_request.h +++ b/src/nxt_unit_request.h @@ -21,6 +21,7 @@ struct nxt_unit_request_s { uint8_t local_length; uint8_t tls; uint8_t websocket_handshake; + uint8_t app_target; uint32_t server_name_length; uint32_t target_length; uint32_t path_length; -- cgit From ee1e248f4b038bb9e03fd78463da580af03c28f7 Mon Sep 17 00:00:00 2001 From: Axel Duch Date: Thu, 14 May 2020 12:29:06 +0200 Subject: Router: decode uri and args. --- src/nxt_conf_validation.c | 117 ++++++++++++++++++- src/nxt_http_route.c | 278 ++++++++++++++++++++++++++++++++++++---------- src/nxt_string.c | 43 ++++++- src/nxt_string.h | 3 + 4 files changed, 381 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 0f46560d..a7a8d139 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -85,6 +85,16 @@ static nxt_int_t nxt_conf_vldt_routes_member(nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_route(nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set_member( + nxt_conf_validation_t *vldt, nxt_str_t *name, nxt_conf_value_t *value); +static nxt_int_t nxt_conf_vldt_match_encoded_patterns( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); +static nxt_int_t nxt_conf_vldt_match_encoded_pattern( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value); static nxt_int_t nxt_conf_vldt_match_patterns(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data); static nxt_int_t nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, @@ -343,12 +353,12 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_match_members[] = { { nxt_string("uri"), NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY, - &nxt_conf_vldt_match_patterns, + &nxt_conf_vldt_match_encoded_patterns, NULL }, { nxt_string("arguments"), NXT_CONF_VLDT_OBJECT | NXT_CONF_VLDT_ARRAY, - &nxt_conf_vldt_match_patterns_sets, + &nxt_conf_vldt_match_encoded_patterns_sets, NULL }, { nxt_string("headers"), @@ -1379,6 +1389,109 @@ nxt_conf_vldt_match_pattern(nxt_conf_validation_t *vldt, } +static nxt_int_t nxt_conf_vldt_match_encoded_patterns_sets( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_encoded_patterns_set); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_match_encoded_patterns_set(vldt, value); +} + + +static nxt_int_t nxt_conf_vldt_match_encoded_patterns_set( + nxt_conf_validation_t *vldt, nxt_conf_value_t *value) +{ + if (nxt_conf_type(value) != NXT_CONF_OBJECT) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for " + "\"arguments\" must be an object."); + } + + return nxt_conf_vldt_object_iterator(vldt, value, + &nxt_conf_vldt_match_encoded_patterns_set_member); +} + + +static nxt_int_t +nxt_conf_vldt_match_encoded_patterns_set_member(nxt_conf_validation_t *vldt, + nxt_str_t *name, nxt_conf_value_t *value) +{ + u_char *p, *end; + + if (nxt_slow_path(name->length == 0)) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern objects must " + "not contain empty member names."); + } + + p = nxt_mp_nget(vldt->pool, name->length); + if (nxt_slow_path(p == NULL)) { + return NXT_ERROR; + } + + end = nxt_decode_uri(p, name->start, name->length); + if (nxt_slow_path(end == NULL)) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for " + "\"arguments\" is encoded but is invalid."); + } + + return nxt_conf_vldt_match_encoded_patterns(vldt, value, NULL); +} + + +static nxt_int_t +nxt_conf_vldt_match_encoded_patterns(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value, void *data) +{ + if (nxt_conf_type(value) == NXT_CONF_ARRAY) { + return nxt_conf_vldt_array_iterator(vldt, value, + &nxt_conf_vldt_match_encoded_pattern); + } + + /* NXT_CONF_STRING */ + + return nxt_conf_vldt_match_encoded_pattern(vldt, value); +} + + +static nxt_int_t +nxt_conf_vldt_match_encoded_pattern(nxt_conf_validation_t *vldt, + nxt_conf_value_t *value) +{ + u_char *p, *end; + nxt_int_t ret; + nxt_str_t pattern; + + if (nxt_conf_type(value) != NXT_CONF_STRING) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" " + "must be a string."); + } + + ret = nxt_conf_vldt_match_pattern(vldt, value); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + + nxt_conf_get_string(value, &pattern); + + p = nxt_mp_nget(vldt->pool, pattern.length); + if (nxt_slow_path(p == NULL)) { + return NXT_ERROR; + } + + end = nxt_decode_uri(p, pattern.start, pattern.length); + if (nxt_slow_path(end == NULL)) { + return nxt_conf_vldt_error(vldt, "The \"match\" pattern for \"uri\" " + "is encoded but is invalid."); + } + + return NXT_OK; +} + + static nxt_int_t nxt_conf_vldt_match_addrs(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, void *data) diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 06bc91da..5010c561 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -39,6 +39,13 @@ typedef enum { } nxt_http_route_pattern_case_t; +typedef enum { + NXT_HTTP_ROUTE_ENCODING_NONE = 0, + NXT_HTTP_ROUTE_ENCODING_URI, + NXT_HTTP_ROUTE_ENCODING_URI_PLUS +} nxt_http_route_encoding_t; + + typedef struct { nxt_conf_value_t *pass; nxt_conf_value_t *ret; @@ -181,23 +188,27 @@ static nxt_int_t nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, nxt_http_action_t *action); static nxt_http_route_table_t *nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object, - nxt_bool_t case_sensitive); + nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); static nxt_http_route_ruleset_t *nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object, - nxt_bool_t case_sensitive); + nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); static nxt_http_route_rule_t *nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *rule_cv, nxt_str_t *name, - nxt_bool_t case_sensitive); + nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding); static nxt_http_route_addr_rule_t *nxt_http_route_addr_rule_create( nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv); static nxt_http_route_rule_t *nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, - nxt_http_route_pattern_case_t pattern_case); + nxt_http_route_pattern_case_t pattern_case, + nxt_http_route_encoding_t encoding); static int nxt_http_pattern_compare(const void *one, const void *two); static int nxt_http_addr_pattern_compare(const void *one, const void *two); static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern, - nxt_http_route_pattern_case_t pattern_case); + nxt_http_route_pattern_case_t pattern_case, + nxt_http_route_encoding_t encoding); +static nxt_int_t nxt_http_route_decode_str(nxt_str_t *str, + nxt_http_route_encoding_t encoding); static u_char *nxt_http_route_pattern_copy(nxt_mp_t *mp, nxt_str_t *test, nxt_http_route_pattern_case_t pattern_case); @@ -463,7 +474,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.scheme != NULL) { rule = nxt_http_route_rule_create(task, mp, mtcf.scheme, 1, - NXT_HTTP_ROUTE_PATTERN_NOCASE); + NXT_HTTP_ROUTE_PATTERN_NOCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); if (rule == NULL) { return NULL; } @@ -475,7 +487,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.host != NULL) { rule = nxt_http_route_rule_create(task, mp, mtcf.host, 1, - NXT_HTTP_ROUTE_PATTERN_LOWCASE); + NXT_HTTP_ROUTE_PATTERN_LOWCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); if (rule == NULL) { return NULL; } @@ -488,7 +501,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.uri != NULL) { rule = nxt_http_route_rule_create(task, mp, mtcf.uri, 1, - NXT_HTTP_ROUTE_PATTERN_NOCASE); + NXT_HTTP_ROUTE_PATTERN_NOCASE, + NXT_HTTP_ROUTE_ENCODING_URI); if (rule == NULL) { return NULL; } @@ -501,7 +515,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.method != NULL) { rule = nxt_http_route_rule_create(task, mp, mtcf.method, 1, - NXT_HTTP_ROUTE_PATTERN_UPCASE); + NXT_HTTP_ROUTE_PATTERN_UPCASE, + NXT_HTTP_ROUTE_ENCODING_NONE); if (rule == NULL) { return NULL; } @@ -514,7 +529,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.headers != NULL) { table = nxt_http_route_table_create(task, mp, mtcf.headers, - NXT_HTTP_ROUTE_HEADER, 0); + NXT_HTTP_ROUTE_HEADER, 0, + NXT_HTTP_ROUTE_ENCODING_NONE); if (table == NULL) { return NULL; } @@ -525,7 +541,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.arguments != NULL) { table = nxt_http_route_table_create(task, mp, mtcf.arguments, - NXT_HTTP_ROUTE_ARGUMENT, 1); + NXT_HTTP_ROUTE_ARGUMENT, 1, + NXT_HTTP_ROUTE_ENCODING_URI_PLUS); if (table == NULL) { return NULL; } @@ -536,7 +553,8 @@ nxt_http_route_match_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, if (mtcf.cookies != NULL) { table = nxt_http_route_table_create(task, mp, mtcf.cookies, - NXT_HTTP_ROUTE_COOKIE, 1); + NXT_HTTP_ROUTE_COOKIE, 1, + NXT_HTTP_ROUTE_ENCODING_NONE); if (table == NULL) { return NULL; } @@ -699,7 +717,7 @@ nxt_http_route_action_create(nxt_router_temp_conf_t *tmcf, nxt_conf_value_t *cv, static nxt_http_route_table_t * nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *table_cv, nxt_http_route_object_t object, - nxt_bool_t case_sensitive) + nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding) { size_t size; uint32_t i, n; @@ -722,8 +740,8 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, table->object = NXT_HTTP_ROUTE_TABLE; if (!array) { - ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, - object, case_sensitive); + ruleset = nxt_http_route_ruleset_create(task, mp, table_cv, object, + case_sensitive, encoding); if (nxt_slow_path(ruleset == NULL)) { return NULL; } @@ -736,8 +754,8 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, for (i = 0; i < n; i++) { ruleset_cv = nxt_conf_get_array_element(table_cv, i); - ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, - object, case_sensitive); + ruleset = nxt_http_route_ruleset_create(task, mp, ruleset_cv, object, + case_sensitive, encoding); if (nxt_slow_path(ruleset == NULL)) { return NULL; } @@ -752,7 +770,7 @@ nxt_http_route_table_create(nxt_task_t *task, nxt_mp_t *mp, static nxt_http_route_ruleset_t * nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *ruleset_cv, nxt_http_route_object_t object, - nxt_bool_t case_sensitive) + nxt_bool_t case_sensitive, nxt_http_route_encoding_t encoding) { size_t size; uint32_t i, n, next; @@ -778,7 +796,7 @@ nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, rule_cv = nxt_conf_next_object_member(ruleset_cv, &name, &next); rule = nxt_http_route_rule_name_create(task, mp, rule_cv, &name, - case_sensitive); + case_sensitive, encoding); if (nxt_slow_path(rule == NULL)) { return NULL; } @@ -793,15 +811,18 @@ nxt_http_route_ruleset_create(nxt_task_t *task, nxt_mp_t *mp, static nxt_http_route_rule_t * nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, - nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive) + nxt_conf_value_t *rule_cv, nxt_str_t *name, nxt_bool_t case_sensitive, + nxt_http_route_encoding_t encoding) { - u_char c, *p; + u_char c, *p, *src, *start, *end, plus; + uint8_t d0, d1; uint32_t hash; nxt_uint_t i; nxt_http_route_rule_t *rule; rule = nxt_http_route_rule_create(task, mp, rule_cv, case_sensitive, - NXT_HTTP_ROUTE_PATTERN_NOCASE); + NXT_HTTP_ROUTE_PATTERN_NOCASE, + encoding); if (nxt_slow_path(rule == NULL)) { return NULL; } @@ -813,18 +834,65 @@ nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, return NULL; } + hash = NXT_HTTP_FIELD_HASH_INIT; rule->u.name.start = p; - hash = NXT_HTTP_FIELD_HASH_INIT; + if (encoding == NXT_HTTP_ROUTE_ENCODING_NONE) { + for (i = 0; i < name->length; i++) { + c = name->start[i]; + *p++ = c; - for (i = 0; i < name->length; i++) { - c = name->start[i]; - *p++ = c; + c = case_sensitive ? c : nxt_lowcase(c); + hash = nxt_http_field_hash_char(hash, c); + } + + goto end; + } + + plus = (encoding == NXT_HTTP_ROUTE_ENCODING_URI_PLUS) ? ' ' : '+'; + + start = name->start; + end = start + name->length; + + for (src = start; src < end; src++) { + c = *src; + + switch (c) { + case '%': + if (nxt_slow_path(end - src <= 2)) { + return NULL; + } + + d0 = nxt_hex2int[src[1]]; + d1 = nxt_hex2int[src[2]]; + src += 2; + + if (nxt_slow_path((d0 | d1) >= 16)) { + return NULL; + } + + c = (d0 << 4) + d1; + *p++ = c; + break; + + case '+': + c = plus; + *p++ = c; + break; + + default: + *p++ = c; + break; + } c = case_sensitive ? c : nxt_lowcase(c); hash = nxt_http_field_hash_char(hash, c); } + rule->u.name.length = p - rule->u.name.start; + +end: + rule->u.name.hash = nxt_http_field_hash_end(hash) & 0xFFFF; return rule; @@ -834,7 +902,8 @@ nxt_http_route_rule_name_create(nxt_task_t *task, nxt_mp_t *mp, static nxt_http_route_rule_t * nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_bool_t case_sensitive, - nxt_http_route_pattern_case_t pattern_case) + nxt_http_route_pattern_case_t pattern_case, + nxt_http_route_encoding_t encoding) { size_t size; uint32_t i, n; @@ -860,7 +929,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, if (string) { pattern[0].case_sensitive = case_sensitive; ret = nxt_http_route_pattern_create(task, mp, cv, &pattern[0], - pattern_case); + pattern_case, encoding); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } @@ -875,7 +944,7 @@ nxt_http_route_rule_create(nxt_task_t *task, nxt_mp_t *mp, value = nxt_conf_get_array_element(cv, i); ret = nxt_http_route_pattern_create(task, mp, value, &pattern[i], - pattern_case); + pattern_case, encoding); if (nxt_slow_path(ret != NXT_OK)) { return NULL; } @@ -972,10 +1041,12 @@ nxt_http_addr_pattern_compare(const void *one, const void *two) static nxt_int_t nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_conf_value_t *cv, nxt_http_route_pattern_t *pattern, - nxt_http_route_pattern_case_t pattern_case) + nxt_http_route_pattern_case_t pattern_case, + nxt_http_route_encoding_t encoding) { u_char *start; - nxt_str_t test; + nxt_str_t test, test2; + nxt_int_t ret; nxt_uint_t n, length; nxt_http_route_pattern_type_t type; @@ -988,6 +1059,7 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, pattern->negative = 0; pattern->any = 1; + pattern->min_length = 0; if (test.length != 0) { @@ -1000,7 +1072,6 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, } if (test.length != 0) { - if (test.start[0] == '*') { test.start++; test.length--; @@ -1026,18 +1097,45 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, length = test.length - 1; for (n = 1; n < length; n++) { - if (test.start[n] == '*') { - test.length = n; - type = NXT_HTTP_ROUTE_PATTERN_MIDDLE; - break; + if (test.start[n] != '*') { + continue; } + + test.length = n; + + test2.start = &test.start[n + 1]; + test2.length = length - n; + + ret = nxt_http_route_decode_str(&test2, encoding); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + + type = NXT_HTTP_ROUTE_PATTERN_MIDDLE; + + pattern->length2 = test2.length; + pattern->min_length += test2.length; + + start = nxt_http_route_pattern_copy(mp, &test2, + pattern_case); + if (nxt_slow_path(start == NULL)) { + return NXT_ERROR; + } + + pattern->start2 = start; + break; } } + + ret = nxt_http_route_decode_str(&test, encoding); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } } } pattern->type = type; - pattern->min_length = test.length; + pattern->min_length += test.length; pattern->length1 = test.length; start = nxt_http_route_pattern_copy(mp, &test, pattern_case); @@ -1047,20 +1145,43 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, pattern->start1 = start; - if (type == NXT_HTTP_ROUTE_PATTERN_MIDDLE) { - length -= test.length; - pattern->length2 = length; - pattern->min_length += length; + return NXT_OK; +} + + +static nxt_int_t +nxt_http_route_decode_str(nxt_str_t *str, nxt_http_route_encoding_t encoding) +{ + u_char *start, *end; + + switch (encoding) { + case NXT_HTTP_ROUTE_ENCODING_NONE: + break; + + case NXT_HTTP_ROUTE_ENCODING_URI: + start = str->start; + + end = nxt_decode_uri(start, start, str->length); + if (nxt_slow_path(end == NULL)) { + return NXT_ERROR; + } + + str->length = end - start; + break; - test.start = &test.start[test.length + 1]; - test.length = length; + case NXT_HTTP_ROUTE_ENCODING_URI_PLUS: + start = str->start; - start = nxt_http_route_pattern_copy(mp, &test, pattern_case); - if (nxt_slow_path(start == NULL)) { + end = nxt_decode_uri_plus(start, start, str->length); + if (nxt_slow_path(end == NULL)) { return NXT_ERROR; } - pattern->start2 = start; + str->length = end - start; + break; + + default: + nxt_unreachable(); } return NXT_OK; @@ -1746,7 +1867,8 @@ static nxt_array_t * nxt_http_route_arguments_parse(nxt_http_request_t *r) { size_t name_length; - u_char c, *p, *start, *end, *name; + u_char c, *p, *dst, *dst_start, *start, *end, *name; + uint8_t d0, d1; uint32_t hash; nxt_bool_t valid; nxt_array_t *args; @@ -1766,39 +1888,81 @@ nxt_http_route_arguments_parse(nxt_http_request_t *r) name = NULL; name_length = 0; + dst_start = nxt_mp_nget(r->mem_pool, r->args->length); + if (nxt_slow_path(dst_start == NULL)) { + return NULL; + } + start = r->args->start; end = start + r->args->length; - for (p = start; p < end; p++) { + for (p = start, dst = dst_start; p < end; p++, dst++) { c = *p; + *dst = c; - if (c == '=') { - name_length = p - start; - name = start; - start = p + 1; + switch (c) { + case '=': + if (name != NULL) { + break; + } + + name_length = dst - dst_start; valid = (name_length != 0); + name = dst_start; + dst_start = dst + 1; + + continue; - } else if (c == '&') { + case '&': if (valid) { nv = nxt_http_route_argument(args, name, name_length, hash, - start, p); + dst_start, dst); if (nxt_slow_path(nv == NULL)) { return NULL; } } hash = NXT_HTTP_FIELD_HASH_INIT; + name_length = 0; valid = 1; name = NULL; - start = p + 1; + dst_start = dst + 1; + + continue; + + case '+': + c = ' '; + *dst = ' '; + + break; + + case '%': + if (nxt_slow_path(end - p <= 2)) { + break; + } + + d0 = nxt_hex2int[p[1]]; + d1 = nxt_hex2int[p[2]]; + + if (nxt_slow_path((d0 | d1) >= 16)) { + break; + } + + p += 2; + c = (d0 << 4) + d1; + *dst = c; + + break; + } - } else if (name == NULL) { + if (name == NULL) { hash = nxt_http_field_hash_char(hash, c); } } if (valid) { - nv = nxt_http_route_argument(args, name, name_length, hash, start, p); + nv = nxt_http_route_argument(args, name, name_length, hash, dst_start, + dst); if (nxt_slow_path(nv == NULL)) { return NULL; } diff --git a/src/nxt_string.c b/src/nxt_string.c index 54f96abc..ab568990 100644 --- a/src/nxt_string.c +++ b/src/nxt_string.c @@ -475,7 +475,7 @@ nxt_strvers_match(u_char *version, u_char *prefix, size_t length) } -static const uint8_t nxt_hex2int[256] +const uint8_t nxt_hex2int[256] nxt_aligned(32) = { 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, @@ -551,6 +551,47 @@ nxt_decode_uri(u_char *dst, u_char *src, size_t length) } +u_char * +nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length) +{ + u_char *end, ch; + uint8_t d0, d1; + + nxt_prefetch(&nxt_hex2int['0']); + + end = src + length; + + while (src < end) { + ch = *src++; + + switch (ch) { + case '%': + if (nxt_slow_path(end - src < 2)) { + return NULL; + } + + d0 = nxt_hex2int[*src++]; + d1 = nxt_hex2int[*src++]; + + if (nxt_slow_path((d0 | d1) >= 16)) { + return NULL; + } + + ch = (d0 << 4) + d1; + break; + + case '+': + ch = ' '; + break; + } + + *dst++ = ch; + } + + return dst; +} + + uintptr_t nxt_encode_uri(u_char *dst, u_char *src, size_t length) { diff --git a/src/nxt_string.h b/src/nxt_string.h index 7863c60e..7d1e044d 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -174,10 +174,13 @@ NXT_EXPORT nxt_bool_t nxt_strvers_match(u_char *version, u_char *prefix, size_t length); NXT_EXPORT u_char *nxt_decode_uri(u_char *dst, u_char *src, size_t length); +NXT_EXPORT u_char *nxt_decode_uri_plus(u_char *dst, u_char *src, size_t length); NXT_EXPORT uintptr_t nxt_encode_uri(u_char *dst, u_char *src, size_t length); NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length); NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length); +NXT_EXPORT const uint8_t nxt_hex2int[256]; + #endif /* _NXT_STRING_H_INCLUDED_ */ -- cgit From 79f5e531fe6451eccff1c4dc14826c30f922ead1 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 15 May 2020 17:08:37 +0300 Subject: Router: removed two unused assignments. This should resolve some static analyzers warnings. --- src/nxt_http_route.c | 3 --- src/nxt_router.c | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/nxt_http_route.c b/src/nxt_http_route.c index 5010c561..a8a6b181 100644 --- a/src/nxt_http_route.c +++ b/src/nxt_http_route.c @@ -1050,9 +1050,6 @@ nxt_http_route_pattern_create(nxt_task_t *task, nxt_mp_t *mp, nxt_uint_t n, length; nxt_http_route_pattern_type_t type; - /* Suppress warning about uninitialized variable. */ - length = 0; - type = NXT_HTTP_ROUTE_PATTERN_EXACT; nxt_conf_get_string(cv, &test); diff --git a/src/nxt_router.c b/src/nxt_router.c index d94a34af..4093db8d 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -1614,8 +1614,8 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, next_target = 0; for (i = 0; i < n; i++) { - value = nxt_conf_next_object_member(apcf.targets_value, - &target, &next_target); + (void) nxt_conf_next_object_member(apcf.targets_value, + &target, &next_target); s = nxt_str_dup(app_mp, &targets[i], &target); if (nxt_slow_path(s == NULL)) { -- cgit From d0de6df83987a7e8e25fab9ba5f274b993c7f094 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Fri, 15 May 2020 21:32:07 +0300 Subject: Fixed global constant declaration (appeared in 9af10e099d09). This fixes building with GCC 10, which is default to -fno-common. See: https://gcc.gnu.org/gcc-10/porting_to.html --- src/nxt_string.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_string.h b/src/nxt_string.h index 7d1e044d..3f9192e2 100644 --- a/src/nxt_string.h +++ b/src/nxt_string.h @@ -180,7 +180,7 @@ NXT_EXPORT uintptr_t nxt_encode_complex_uri(u_char *dst, u_char *src, size_t length); NXT_EXPORT nxt_bool_t nxt_is_complex_uri_encoded(u_char *s, size_t length); -NXT_EXPORT const uint8_t nxt_hex2int[256]; +extern const uint8_t nxt_hex2int[256]; #endif /* _NXT_STRING_H_INCLUDED_ */ -- cgit From 140b81208e83569913aa81f964eb64e15940d897 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Wed, 20 May 2020 11:18:03 +0300 Subject: PHP: building with PHP 8 (development version). --- src/nxt_php_sapi.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index ccbdd475..d3c23c31 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -29,6 +29,14 @@ #define NXT_PHP7 1 #endif +/* PHP 8 */ +#ifndef TSRMLS_CC +#define TSRMLS_CC +#define TSRMLS_DC +#define TSRMLS_D void +#define TSRMLS_C +#endif + typedef struct { nxt_str_t root; -- cgit From b2e6ef7bebff632fc64e10bdb03b5da02817d172 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 20 May 2020 11:18:03 +0300 Subject: Static: fixed potential undefined behavior in memcpy(). According to the C standard, pointer arguments passed to memcpy() calls shall still have valid values. NULL is considered as invalid. Found with GCC Static Analyzer. --- src/nxt_http_static.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nxt_http_static.c b/src/nxt_http_static.c index 46ae57a7..ee18be1b 100644 --- a/src/nxt_http_static.c +++ b/src/nxt_http_static.c @@ -76,7 +76,7 @@ nxt_http_static_handler(nxt_task_t *task, nxt_http_request_t *r, nxt_str_set(&extension, ".html"); } else { - nxt_str_null(&index); + nxt_str_set(&index, ""); nxt_str_null(&extension); } -- cgit From 89b1e88f8f0b1d66a19ac9657c9568ef5fb0ff27 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 28 May 2020 12:40:49 +0300 Subject: Closing unsent file descriptors from port queue. After a process exits, all ports linked to it from other processes should be closed. All unsent file descriptors in port queue, marked as "close after send", should be closed to avoid resource leakage. --- src/nxt_port_socket.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'src') diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 9c7da970..4e3eaef6 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -993,6 +993,12 @@ nxt_port_error_handler(nxt_task_t *task, void *obj, void *data) nxt_queue_each(msg, &port->messages, nxt_port_send_msg_t, link) { + if (msg->fd != -1 && msg->close_fd != 0) { + nxt_fd_close(msg->fd); + + msg->fd = -1; + } + for (b = msg->buf; b != NULL; b = next) { next = b->next; b->next = NULL; -- cgit From de368f033dd2d22312269010f20f7f6388ecd95f Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 28 May 2020 12:40:54 +0300 Subject: Added NULL check for engine->port. This is required to handle REMOVE_PID messages if router engine initialization is incomplete. --- src/nxt_router.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 4093db8d..b4cba08b 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -944,8 +944,10 @@ nxt_router_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) nxt_queue_each(engine, &nxt_router->engines, nxt_event_engine_t, link0) { - nxt_port_post(task, engine->port, nxt_router_app_process_remove_pid, - msg->u.data); + if (nxt_fast_path(engine->port != NULL)) { + nxt_port_post(task, engine->port, nxt_router_app_process_remove_pid, + msg->u.data); + } } nxt_queue_loop; -- cgit From aacf11152c314efb1895b6d44ba72dc9f1801c7d Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 28 May 2020 12:41:00 +0300 Subject: Moving nxt_stream_ident to shared memory. This aims to avoid stream id clashes after router restart. --- src/nxt_port_rpc.c | 28 +++++++++++++++++++++++++--- src/nxt_port_rpc.h | 2 ++ src/nxt_runtime.c | 4 ++++ 3 files changed, 31 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/nxt_port_rpc.c b/src/nxt_port_rpc.c index 77e8af45..37f2d902 100644 --- a/src/nxt_port_rpc.c +++ b/src/nxt_port_rpc.c @@ -8,7 +8,7 @@ #include -static nxt_atomic_t nxt_stream_ident = 1; +static volatile uint32_t *nxt_stream_ident; typedef struct nxt_port_rpc_reg_s nxt_port_rpc_reg_t; @@ -30,6 +30,29 @@ nxt_port_rpc_remove_from_peers(nxt_task_t *task, nxt_port_t *port, nxt_port_rpc_reg_t *reg); +nxt_int_t +nxt_port_rpc_init(void) +{ + void *p; + + if (nxt_stream_ident != NULL) { + return NXT_OK; + } + + p = nxt_mem_mmap(NULL, sizeof(*nxt_stream_ident), PROT_READ | PROT_WRITE, + MAP_ANON | MAP_SHARED, -1, 0); + + if (nxt_slow_path(p == MAP_FAILED)) { + return NXT_ERROR; + } + + nxt_stream_ident = p; + *nxt_stream_ident = 1; + + return NXT_OK; +} + + static nxt_int_t nxt_rpc_reg_test(nxt_lvlhsh_query_t *lhq, void *data) { @@ -105,8 +128,7 @@ nxt_port_rpc_register_handler_ex(nxt_task_t *task, nxt_port_t *port, nxt_assert(port->pair[0] != -1); - stream = - (uint32_t) nxt_atomic_fetch_add(&nxt_stream_ident, 1) & 0x3FFFFFFF; + stream = nxt_atomic_fetch_add(nxt_stream_ident, 1); reg = nxt_mp_zalloc(port->mem_pool, sizeof(nxt_port_rpc_reg_t) + ex_size); diff --git a/src/nxt_port_rpc.h b/src/nxt_port_rpc.h index 8011e474..c07683fb 100644 --- a/src/nxt_port_rpc.h +++ b/src/nxt_port_rpc.h @@ -11,6 +11,8 @@ typedef void (*nxt_port_rpc_handler_t)(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); +nxt_int_t nxt_port_rpc_init(void); + uint32_t nxt_port_rpc_register_handler(nxt_task_t *task, nxt_port_t *port, nxt_port_rpc_handler_t ready_handler, nxt_port_rpc_handler_t error_handler, nxt_pid_t peer, void *data); diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index bcd156ee..ea01f06f 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -118,6 +118,10 @@ nxt_runtime_create(nxt_task_t *task) goto fail; } + if (nxt_port_rpc_init() != NXT_OK) { + goto fail; + } + nxt_work_queue_add(&task->thread->engine->fast_work_queue, nxt_runtime_start, task, rt, NULL); -- cgit From e9e5ddd5a5d9ce99768833137eac2551a710becf Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Mon, 9 Mar 2020 16:28:25 +0000 Subject: Refactor of process management. The process abstraction has changed to: setup(task, process) start(task, process_data) prefork(task, process, mp) The prefork() occurs in the main process right before fork. The file src/nxt_main_process.c is completely free of process specific logic. The creation of a process now supports a PROCESS_CREATED state. The The setup() function of each process can set its state to either created or ready. If created, a MSG_PROCESS_CREATED is sent to main process, where external setup can be done (required for rootfs under container). The core processes (discovery, controller and router) doesn't need external setup, then they all proceeds to their start() function straight away. In the case of applications, the load of the module happens at the process setup() time and The module's init() function has changed to be the start() of the process. The module API has changed to: setup(task, process, conf) start(task, data) As a direct benefit of the PROCESS_CREATED message, the clone(2) of processes using pid namespaces now doesn't need to create a pipe to make the child block until parent setup uid/gid mappings nor it needs to receive the child pid. --- src/nxt_application.c | 363 +++++++++++++++- src/nxt_application.h | 11 +- src/nxt_cert.c | 11 +- src/nxt_cert.h | 2 +- src/nxt_clone.h | 14 +- src/nxt_controller.c | 142 +++++- src/nxt_external.c | 11 +- src/nxt_java.c | 61 +-- src/nxt_main_process.c | 1049 +++++++++++---------------------------------- src/nxt_main_process.h | 19 +- src/nxt_php_sapi.c | 32 +- src/nxt_port.c | 4 +- src/nxt_port.h | 141 +++--- src/nxt_port_memory.c | 2 +- src/nxt_process.c | 534 ++++++++++++++++------- src/nxt_process.h | 137 ++++-- src/nxt_process_type.h | 2 +- src/nxt_python_wsgi.c | 13 +- src/nxt_router.c | 44 +- src/nxt_runtime.c | 60 ++- src/nxt_runtime.h | 1 + src/nxt_signal_handlers.c | 67 +++ src/nxt_unit.c | 2 +- src/nxt_worker_process.c | 118 ----- src/perl/nxt_perl_psgi.c | 11 +- src/ruby/nxt_ruby.c | 20 +- 26 files changed, 1541 insertions(+), 1330 deletions(-) create mode 100644 src/nxt_signal_handlers.c delete mode 100644 src/nxt_worker_process.c (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index bebe3907..6de82257 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -25,6 +25,8 @@ typedef struct { } nxt_module_t; +static nxt_int_t nxt_discovery_start(nxt_task_t *task, + nxt_process_data_t *data); static nxt_buf_t *nxt_discovery_modules(nxt_task_t *task, const char *path); static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, const char *name); @@ -34,7 +36,27 @@ static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_app_module_t *nxt_app_module_load(nxt_task_t *task, const char *name); +static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, + nxt_mp_t *mp); +static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); +static nxt_int_t nxt_app_isolation(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); + +#if (NXT_HAVE_CLONE) +static nxt_int_t nxt_app_clone_flags(nxt_task_t *task, + nxt_conf_value_t *namespaces, nxt_clone_t *clone); +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) +static nxt_int_t nxt_app_isolation_creds(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +static nxt_int_t nxt_app_isolation_credential_map(nxt_task_t *task, + nxt_mp_t *mem_pool, nxt_conf_value_t *map_array, + nxt_clone_credential_map_t *map); +#endif + +nxt_str_t nxt_server = nxt_string(NXT_SERVER); static uint32_t compat[] = { @@ -42,14 +64,53 @@ static uint32_t compat[] = { }; -nxt_str_t nxt_server = nxt_string(NXT_SERVER); +static nxt_app_module_t *nxt_app; -static nxt_app_module_t *nxt_app; +static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { + .quit = nxt_signal_quit_handler, + .new_port = nxt_port_new_port_handler, + .change_file = nxt_port_change_log_file_handler, + .mmap = nxt_port_mmap_handler, + .data = nxt_port_data_handler, + .remove_pid = nxt_port_remove_pid_handler, + .rpc_ready = nxt_port_rpc_handler, + .rpc_error = nxt_port_rpc_handler, +}; -nxt_int_t -nxt_discovery_start(nxt_task_t *task, void *data) +static const nxt_port_handlers_t nxt_app_process_port_handlers = { + .quit = nxt_signal_quit_handler, + .rpc_ready = nxt_port_rpc_handler, + .rpc_error = nxt_port_rpc_handler, +}; + + +const nxt_process_init_t nxt_discovery_process = { + .name = "discovery", + .type = NXT_PROCESS_DISCOVERY, + .prefork = NULL, + .restart = 0, + .setup = nxt_process_core_setup, + .start = nxt_discovery_start, + .port_handlers = &nxt_discovery_process_port_handlers, + .signals = nxt_process_signals, +}; + + +const nxt_process_init_t nxt_app_process = { + .type = NXT_PROCESS_APP, + .setup = nxt_app_setup, + .prefork = nxt_app_prefork, + .restart = 0, + .start = NULL, /* set to module->start */ + .port_handlers = &nxt_app_process_port_handlers, + .signals = nxt_process_signals, +}; + + +static nxt_int_t +nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data) { uint32_t stream; nxt_buf_t *b; @@ -57,7 +118,7 @@ nxt_discovery_start(nxt_task_t *task, void *data) nxt_port_t *main_port, *discovery_port; nxt_runtime_t *rt; - nxt_debug(task, "DISCOVERY"); + nxt_log(task, NXT_LOG_INFO, "discovery started"); rt = task->thread->runtime; @@ -301,18 +362,85 @@ nxt_discovery_completion_handler(nxt_task_t *task, void *obj, void *data) static void nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) { - nxt_worker_process_quit_handler(task, msg); + nxt_signal_quit_handler(task, msg); } -nxt_int_t -nxt_app_start(nxt_task_t *task, void *data) +static nxt_int_t +nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) +{ + nxt_int_t cap_setid; + nxt_int_t ret; + nxt_runtime_t *rt; + nxt_common_app_conf_t *app_conf; + + rt = task->thread->runtime; + app_conf = process->data.app; + cap_setid = rt->capabilities.setid; + + if (app_conf->isolation != NULL) { + ret = nxt_app_isolation(task, app_conf->isolation, process); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + } + +#if (NXT_HAVE_CLONE_NEWUSER) + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { + cap_setid = 1; + } +#endif + + if (cap_setid) { + ret = nxt_process_creds_set(task, process, &app_conf->user, + &app_conf->group); + + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + + } else { + if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user, + nxt_strlen(rt->user_cred.user))) + { + nxt_alert(task, "cannot set user \"%V\" for app \"%V\": " + "missing capabilities", &app_conf->user, &app_conf->name); + + return NXT_ERROR; + } + + if (app_conf->group.length > 0 + && !nxt_str_eq(&app_conf->group, (u_char *) rt->group, + nxt_strlen(rt->group))) + { + nxt_alert(task, "cannot set group \"%V\" for app \"%V\": " + "missing capabilities", &app_conf->group, + &app_conf->name); + + return NXT_ERROR; + } + } + +#if (NXT_HAVE_CLONE_NEWUSER) + ret = nxt_process_vldt_isolation_creds(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } +#endif + + return NXT_OK; +} + + +static nxt_int_t +nxt_app_setup(nxt_task_t *task, nxt_process_t *process) { nxt_int_t ret; + nxt_process_init_t *init; nxt_app_lang_module_t *lang; nxt_common_app_conf_t *app_conf; - app_conf = data; + app_conf = process->data.app; lang = nxt_app_lang_module(task->thread->runtime, &app_conf->type); if (nxt_slow_path(lang == NULL)) { @@ -332,8 +460,8 @@ nxt_app_start(nxt_task_t *task, void *data) } } - if (nxt_app->pre_init != NULL) { - ret = nxt_app->pre_init(task, data); + if (nxt_app->setup != NULL) { + ret = nxt_app->setup(task, process, app_conf); if (nxt_slow_path(ret != NXT_OK)) { return ret; @@ -360,16 +488,13 @@ nxt_app_start(nxt_task_t *task, void *data) return NXT_ERROR; } - ret = nxt_app->init(task, data); + init = nxt_process_init(process); - if (nxt_slow_path(ret != NXT_OK)) { - nxt_debug(task, "application init failed"); + init->start = nxt_app->start; - } else { - nxt_debug(task, "application init done"); - } + process->state = NXT_PROCESS_STATE_CREATED; - return ret; + return NXT_OK; } @@ -429,6 +554,206 @@ nxt_app_set_environment(nxt_conf_value_t *environment) } +static nxt_int_t +nxt_app_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ +#if (NXT_HAVE_CLONE) + nxt_int_t ret; + nxt_conf_value_t *obj; + + static nxt_str_t nsname = nxt_string("namespaces"); + + obj = nxt_conf_get_object_member(isolation, &nsname, NULL); + if (obj != NULL) { + ret = nxt_app_clone_flags(task, obj, &process->isolation.clone); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) + ret = nxt_app_isolation_creds(task, isolation, process); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#if (NXT_HAVE_CLONE_NEWUSER) + +static nxt_int_t +nxt_app_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_int_t ret; + nxt_clone_t *clone; + nxt_conf_value_t *array; + + static nxt_str_t uidname = nxt_string("uidmap"); + static nxt_str_t gidname = nxt_string("gidmap"); + + clone = &process->isolation.clone; + + array = nxt_conf_get_object_member(isolation, &uidname, NULL); + if (array != NULL) { + ret = nxt_app_isolation_credential_map(task, process->mem_pool, array, + &clone->uidmap); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + array = nxt_conf_get_object_member(isolation, &gidname, NULL); + if (array != NULL) { + ret = nxt_app_isolation_credential_map(task, process->mem_pool, array, + &clone->gidmap); + + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_app_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mp, + nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map) +{ + nxt_int_t ret; + nxt_uint_t i; + nxt_conf_value_t *obj; + + static nxt_conf_map_t nxt_clone_map_entry_conf[] = { + { + nxt_string("container"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, container), + }, + + { + nxt_string("host"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, host), + }, + + { + nxt_string("size"), + NXT_CONF_MAP_INT, + offsetof(nxt_clone_map_entry_t, size), + }, + }; + + map->size = nxt_conf_array_elements_count(map_array); + + if (map->size == 0) { + return NXT_OK; + } + + map->map = nxt_mp_alloc(mp, map->size * sizeof(nxt_clone_map_entry_t)); + if (nxt_slow_path(map->map == NULL)) { + return NXT_ERROR; + } + + for (i = 0; i < map->size; i++) { + obj = nxt_conf_get_array_element(map_array, i); + + ret = nxt_conf_map_object(mp, obj, nxt_clone_map_entry_conf, + nxt_nitems(nxt_clone_map_entry_conf), + map->map + i); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "clone map entry map error"); + return NXT_ERROR; + } + } + + return NXT_OK; +} + +#endif + +#if (NXT_HAVE_CLONE) + +static nxt_int_t +nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, + nxt_clone_t *clone) +{ + uint32_t index; + nxt_str_t name; + nxt_int_t flag; + nxt_conf_value_t *value; + + index = 0; + + for ( ;; ) { + value = nxt_conf_next_object_member(namespaces, &name, &index); + + if (value == NULL) { + break; + } + + flag = 0; + +#if (NXT_HAVE_CLONE_NEWUSER) + if (nxt_str_eq(&name, "credential", 10)) { + flag = CLONE_NEWUSER; + } +#endif + +#if (NXT_HAVE_CLONE_NEWPID) + if (nxt_str_eq(&name, "pid", 3)) { + flag = CLONE_NEWPID; + } +#endif + +#if (NXT_HAVE_CLONE_NEWNET) + if (nxt_str_eq(&name, "network", 7)) { + flag = CLONE_NEWNET; + } +#endif + +#if (NXT_HAVE_CLONE_NEWUTS) + if (nxt_str_eq(&name, "uname", 5)) { + flag = CLONE_NEWUTS; + } +#endif + +#if (NXT_HAVE_CLONE_NEWNS) + if (nxt_str_eq(&name, "mount", 5)) { + flag = CLONE_NEWNS; + } +#endif + +#if (NXT_HAVE_CLONE_NEWCGROUP) + if (nxt_str_eq(&name, "cgroup", 6)) { + flag = CLONE_NEWCGROUP; + } +#endif + + if (!flag) { + nxt_alert(task, "unknown namespace flag: \"%V\"", &name); + return NXT_ERROR; + } + + if (nxt_conf_get_boolean(value)) { + clone->flags |= flag; + } + } + + return NXT_OK; +} + +#endif + + + nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) { @@ -539,7 +864,7 @@ nxt_unit_default_init(nxt_task_t *task, nxt_unit_init_t *init) nxt_fd_blocking(task, main_port->pair[1]); - init->ready_stream = my_port->process->init->stream; + init->ready_stream = my_port->process->stream; init->read_port.id.pid = my_port->pid; init->read_port.id.id = my_port->id; diff --git a/src/nxt_application.h b/src/nxt_application.h index 972a712b..b4231e3b 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -27,6 +27,8 @@ typedef enum { typedef struct nxt_app_module_s nxt_app_module_t; +typedef nxt_int_t (*nxt_application_setup_t)(nxt_task_t *task, + nxt_process_t *process, nxt_common_app_conf_t *conf); typedef struct { @@ -37,9 +39,6 @@ typedef struct { } nxt_app_lang_module_t; -typedef struct nxt_common_app_conf_s nxt_common_app_conf_t; - - typedef struct { char *executable; nxt_conf_value_t *arguments; @@ -111,10 +110,8 @@ struct nxt_app_module_s { nxt_str_t type; const char *version; - nxt_int_t (*pre_init)(nxt_task_t *task, - nxt_common_app_conf_t *conf); - nxt_int_t (*init)(nxt_task_t *task, - nxt_common_app_conf_t *conf); + nxt_application_setup_t setup; + nxt_process_start_t start; }; diff --git a/src/nxt_cert.c b/src/nxt_cert.c index ee258646..9e825d80 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -797,12 +797,11 @@ nxt_cert_info_delete(nxt_str_t *name) nxt_array_t * -nxt_cert_store_load(nxt_task_t *task) +nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mp) { DIR *dir; size_t size, alloc; u_char *buf, *p; - nxt_mp_t *mp; nxt_str_t name; nxt_int_t ret; nxt_file_t file; @@ -818,14 +817,8 @@ nxt_cert_store_load(nxt_task_t *task) return NULL; } - mp = nxt_mp_create(1024, 128, 256, 32); - if (nxt_slow_path(mp == NULL)) { - return NULL; - } - certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t)); if (nxt_slow_path(certs == NULL)) { - nxt_mp_destroy(mp); return NULL; } @@ -933,7 +926,7 @@ nxt_cert_store_release(nxt_array_t *certs) nxt_fd_close(items[i].fd); } - nxt_mp_destroy(certs->mem_pool); + nxt_array_destroy(certs); } diff --git a/src/nxt_cert.h b/src/nxt_cert.h index 319d5d3c..dbaddcf9 100644 --- a/src/nxt_cert.h +++ b/src/nxt_cert.h @@ -19,7 +19,7 @@ nxt_conf_value_t *nxt_cert_info_get(nxt_str_t *name); nxt_conf_value_t *nxt_cert_info_get_all(nxt_mp_t *mp); nxt_int_t nxt_cert_info_delete(nxt_str_t *name); -nxt_array_t *nxt_cert_store_load(nxt_task_t *task); +nxt_array_t *nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mem_pool); void nxt_cert_store_release(nxt_array_t *certs); void nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp, diff --git a/src/nxt_clone.h b/src/nxt_clone.h index dcccf1db..c2066ce6 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -3,8 +3,8 @@ * Copyright (C) NGINX, Inc. */ -#ifndef _NXT_CLONE_INCLUDED_ -#define _NXT_CLONE_INCLUDED_ +#ifndef _NXT_CLONE_H_INCLUDED_ +#define _NXT_CLONE_H_INCLUDED_ #if (NXT_HAVE_CLONE_NEWUSER) @@ -36,10 +36,11 @@ typedef struct { pid_t nxt_clone(nxt_int_t flags); -#if (NXT_HAVE_CLONE_NEWUSER) +#define nxt_is_clone_flag_set(flags, test) \ + ((flags & CLONE_##test) == CLONE_##test) + -#define NXT_CLONE_USER(flags) \ - ((flags & CLONE_NEWUSER) == CLONE_NEWUSER) +#if (NXT_HAVE_CLONE_NEWUSER) NXT_EXPORT nxt_int_t nxt_clone_credential_map(nxt_task_t *task, pid_t pid, nxt_credential_t *creds, nxt_clone_t *clone); @@ -50,4 +51,5 @@ NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_gidmap(nxt_task_t *task, #endif -#endif /* _NXT_CLONE_INCLUDED_ */ + +#endif /* _NXT_CLONE_H_INCLUDED_ */ diff --git a/src/nxt_controller.c b/src/nxt_controller.c index ea70cf78..a61c127d 100644 --- a/src/nxt_controller.c +++ b/src/nxt_controller.c @@ -39,11 +39,17 @@ typedef struct { } nxt_controller_response_t; +static nxt_int_t nxt_controller_prefork(nxt_task_t *task, + nxt_process_t *process, nxt_mp_t *mp); +static nxt_int_t nxt_controller_start(nxt_task_t *task, + nxt_process_data_t *data); static void nxt_controller_process_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_controller_send_current_conf(nxt_task_t *task); static void nxt_controller_router_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); +static void nxt_controller_remove_pid_handler(nxt_task_t *task, + nxt_port_recv_msg_t *msg); static nxt_int_t nxt_controller_conf_default(void); static void nxt_controller_conf_init_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); @@ -83,6 +89,8 @@ static void nxt_controller_process_cert(nxt_task_t *task, static void nxt_controller_process_cert_save(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name); +static void nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, + void *data); #endif static void nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); @@ -114,21 +122,117 @@ static const nxt_event_conn_state_t nxt_controller_conn_write_state; static const nxt_event_conn_state_t nxt_controller_conn_close_state; -nxt_port_handlers_t nxt_controller_process_port_handlers = { - .quit = nxt_worker_process_quit_handler, +static const nxt_port_handlers_t nxt_controller_process_port_handlers = { + .quit = nxt_signal_quit_handler, .new_port = nxt_controller_process_new_port_handler, .change_file = nxt_port_change_log_file_handler, .mmap = nxt_port_mmap_handler, .process_ready = nxt_controller_router_ready_handler, .data = nxt_port_data_handler, - .remove_pid = nxt_port_remove_pid_handler, + .remove_pid = nxt_controller_remove_pid_handler, .rpc_ready = nxt_port_rpc_handler, .rpc_error = nxt_port_rpc_handler, }; -nxt_int_t -nxt_controller_start(nxt_task_t *task, void *data) +const nxt_process_init_t nxt_controller_process = { + .name = "controller", + .type = NXT_PROCESS_CONTROLLER, + .prefork = nxt_controller_prefork, + .restart = 1, + .setup = nxt_process_core_setup, + .start = nxt_controller_start, + .port_handlers = &nxt_controller_process_port_handlers, + .signals = nxt_process_signals, +}; + + +static nxt_int_t +nxt_controller_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) +{ + ssize_t n; + nxt_int_t ret; + nxt_str_t *conf; + nxt_file_t file; + nxt_runtime_t *rt; + nxt_file_info_t fi; + nxt_controller_init_t ctrl_init; + + nxt_log(task, NXT_LOG_INFO, "controller started"); + + rt = task->thread->runtime; + + nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t)); + + conf = &ctrl_init.conf; + + nxt_memzero(&file, sizeof(nxt_file_t)); + + file.name = (nxt_file_name_t *) rt->conf; + + ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); + + if (ret == NXT_OK) { + ret = nxt_file_info(&file, &fi); + + if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) { + conf->length = nxt_file_size(&fi); + conf->start = nxt_mp_alloc(mp, conf->length); + if (nxt_slow_path(conf->start == NULL)) { + nxt_file_close(task, &file); + return NXT_ERROR; + } + + n = nxt_file_read(&file, conf->start, conf->length, 0); + + if (nxt_slow_path(n != (ssize_t) conf->length)) { + conf->start = NULL; + conf->length = 0; + + nxt_alert(task, "failed to restore previous configuration: " + "cannot read the file"); + } + } + + nxt_file_close(task, &file); + } + +#if (NXT_TLS) + ctrl_init.certs = nxt_cert_store_load(task, mp); + + nxt_mp_cleanup(mp, nxt_controller_cert_cleanup, task, ctrl_init.certs, rt); +#endif + + process->data.controller = ctrl_init; + + return NXT_OK; +} + + +#if (NXT_TLS) + +static void +nxt_controller_cert_cleanup(nxt_task_t *task, void *obj, void *data) +{ + pid_t main_pid; + nxt_array_t *certs; + nxt_runtime_t *rt; + + certs = obj; + rt = data; + + main_pid = rt->port_by_type[NXT_PROCESS_MAIN]->pid; + + if (nxt_pid == main_pid && certs != NULL) { + nxt_cert_store_release(certs); + } +} + +#endif + + +static nxt_int_t +nxt_controller_start(nxt_task_t *task, nxt_process_data_t *data) { nxt_mp_t *mp; nxt_int_t ret; @@ -147,15 +251,13 @@ nxt_controller_start(nxt_task_t *task, void *data) nxt_queue_init(&nxt_controller_waiting_requests); - init = data; + init = &data->controller; #if (NXT_TLS) - if (init->certs != NULL) { nxt_cert_info_init(task, init->certs); nxt_cert_store_release(init->certs); } - #endif json = &init->conf; @@ -170,8 +272,6 @@ nxt_controller_start(nxt_task_t *task, void *data) } conf = nxt_conf_json_parse_str(mp, json); - nxt_free(json->start); - if (nxt_slow_path(conf == NULL)) { nxt_alert(task, "failed to restore previous configuration: " "file is corrupted or not enough memory"); @@ -295,6 +395,28 @@ nxt_controller_router_ready_handler(nxt_task_t *task, } +static void +nxt_controller_remove_pid_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_pid_t pid; + nxt_process_t *process; + nxt_runtime_t *rt; + + rt = task->thread->runtime; + + nxt_assert(nxt_buf_used_size(msg->buf) == sizeof(pid)); + + nxt_memcpy(&pid, msg->buf->mem.pos, sizeof(pid)); + + process = nxt_runtime_process_find(rt, pid); + if (process != NULL && nxt_process_type(process) == NXT_PROCESS_ROUTER) { + nxt_controller_router_ready = 0; + } + + nxt_port_remove_pid_handler(task, msg); +} + + static nxt_int_t nxt_controller_conf_default(void) { diff --git a/src/nxt_external.c b/src/nxt_external.c index e498a938..58523525 100644 --- a/src/nxt_external.c +++ b/src/nxt_external.c @@ -9,8 +9,7 @@ #include -static nxt_int_t nxt_external_init(nxt_task_t *task, - nxt_common_app_conf_t *conf); +static nxt_int_t nxt_external_start(nxt_task_t *task, nxt_process_data_t *data); nxt_app_module_t nxt_external_module = { @@ -19,7 +18,7 @@ nxt_app_module_t nxt_external_module = { nxt_string("external"), "*", NULL, - nxt_external_init, + nxt_external_start, }; @@ -58,7 +57,7 @@ nxt_external_fd_no_cloexec(nxt_task_t *task, nxt_socket_t fd) static nxt_int_t -nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_external_start(nxt_task_t *task, nxt_process_data_t *data) { char **argv; u_char buf[256]; @@ -71,9 +70,11 @@ nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_port_t *my_port, *main_port; nxt_runtime_t *rt; nxt_conf_value_t *value; + nxt_common_app_conf_t *conf; nxt_external_app_conf_t *c; rt = task->thread->runtime; + conf = data->app; main_port = rt->port_by_type[NXT_PROCESS_MAIN]; my_port = nxt_runtime_port_find(rt, nxt_pid, 0); @@ -99,7 +100,7 @@ nxt_external_init(nxt_task_t *task, nxt_common_app_conf_t *conf) "%PI,%ud,%d;" "%PI,%ud,%d;" "%d,%z,%Z", - NXT_VERSION, my_port->process->init->stream, + NXT_VERSION, my_port->process->stream, main_port->pid, main_port->id, main_port->pair[1], my_port->pid, my_port->id, my_port->pair[0], 2, conf->shm_limit); diff --git a/src/nxt_java.c b/src/nxt_java.c index 004907d6..c4145c1d 100644 --- a/src/nxt_java.c +++ b/src/nxt_java.c @@ -27,9 +27,10 @@ #include "nxt_jars.h" -static nxt_int_t nxt_java_pre_init(nxt_task_t *task, +static nxt_int_t nxt_java_setup(nxt_task_t *task, nxt_process_t *process, nxt_common_app_conf_t *conf); -static nxt_int_t nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_java_start(nxt_task_t *task, + nxt_process_data_t *data); static void nxt_java_request_handler(nxt_unit_request_info_t *req); static void nxt_java_websocket_handler(nxt_unit_websocket_frame_t *ws); static void nxt_java_close_handler(nxt_unit_request_info_t *req); @@ -49,8 +50,8 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { compat, nxt_string("java"), NXT_STRING(NXT_JAVA_VERSION), - nxt_java_pre_init, - nxt_java_init, + nxt_java_setup, + nxt_java_start, }; typedef struct { @@ -60,7 +61,8 @@ typedef struct { static nxt_int_t -nxt_java_pre_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_java_setup(nxt_task_t *task, nxt_process_t *process, + nxt_common_app_conf_t *conf) { const char *unit_jars; @@ -115,24 +117,26 @@ nxt_java_module_jars(const char *jars[], int jar_count) static nxt_int_t -nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_java_start(nxt_task_t *task, nxt_process_data_t *data) { - jint rc; - char *opt, *real_path; - char **classpath_arr, **unit_jars, **system_jars; - JavaVM *jvm; - JNIEnv *env; - jobject cl, classpath; - nxt_str_t str; - nxt_int_t opt_len, real_path_len; - nxt_uint_t i, unit_jars_count, classpath_count, system_jars_count; - JavaVMOption *jvm_opt; - JavaVMInitArgs jvm_args; - nxt_unit_ctx_t *ctx; - nxt_unit_init_t java_init; - nxt_java_data_t data; - nxt_conf_value_t *value; - nxt_java_app_conf_t *c; + jint rc; + char *opt, *real_path; + char **classpath_arr, **unit_jars, **system_jars; + JavaVM *jvm; + JNIEnv *env; + jobject cl, classpath; + nxt_str_t str; + nxt_int_t opt_len, real_path_len; + nxt_uint_t i, unit_jars_count, classpath_count; + nxt_uint_t system_jars_count; + JavaVMOption *jvm_opt; + JavaVMInitArgs jvm_args; + nxt_unit_ctx_t *ctx; + nxt_unit_init_t java_init; + nxt_java_data_t java_data; + nxt_conf_value_t *value; + nxt_java_app_conf_t *c; + nxt_common_app_conf_t *app_conf; //setenv("ASAN_OPTIONS", "handle_segv=0", 1); @@ -140,7 +144,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) jvm_args.nOptions = 0; jvm_args.ignoreUnrecognized = 0; - c = &conf->u.java; + app_conf = data->app; + c = &app_conf->u.java; if (c->options != NULL) { jvm_args.nOptions += nxt_conf_array_elements_count(c->options); @@ -338,8 +343,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) goto env_failed; } - data.env = env; - data.ctx = nxt_java_startContext(env, c->webapp, classpath); + java_data.env = env; + java_data.ctx = nxt_java_startContext(env, c->webapp, classpath); if ((*env)->ExceptionCheck(env)) { nxt_alert(task, "Unhandled exception in application start"); @@ -353,8 +358,8 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) java_init.callbacks.websocket_handler = nxt_java_websocket_handler; java_init.callbacks.close_handler = nxt_java_close_handler; java_init.request_data_size = sizeof(nxt_java_request_data_t); - java_init.data = &data; - java_init.shm_limit = conf->shm_limit; + java_init.data = &java_data; + java_init.shm_limit = app_conf->shm_limit; ctx = nxt_unit_init(&java_init); if (nxt_slow_path(ctx == NULL)) { @@ -367,7 +372,7 @@ nxt_java_init(nxt_task_t *task, nxt_common_app_conf_t *conf) /* TODO report error */ } - nxt_java_stopContext(env, data.ctx); + nxt_java_stopContext(env, java_data.ctx); if ((*env)->ExceptionCheck(env)) { (*env)->ExceptionDescribe(env); diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index c35954c0..0dff050b 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -36,20 +36,11 @@ extern nxt_port_handlers_t nxt_router_process_port_handlers; static nxt_int_t nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt); static void nxt_main_process_title(nxt_task_t *task); -static nxt_int_t nxt_main_start_controller_process(nxt_task_t *task, - nxt_runtime_t *rt); -static nxt_int_t nxt_main_create_controller_process(nxt_task_t *task, - nxt_runtime_t *rt, nxt_process_init_t *init); -static nxt_int_t nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt, - nxt_process_init_t *init); -static nxt_int_t nxt_main_start_router_process(nxt_task_t *task, - nxt_runtime_t *rt); -static nxt_int_t nxt_main_start_discovery_process(nxt_task_t *task, - nxt_runtime_t *rt); -static nxt_int_t nxt_main_start_worker_process(nxt_task_t *task, - nxt_runtime_t *rt, nxt_common_app_conf_t *app_conf, uint32_t stream); -static nxt_int_t nxt_main_create_worker_process(nxt_task_t *task, - nxt_runtime_t *rt, nxt_process_init_t *init); +static nxt_int_t nxt_main_process_create(nxt_task_t *task, + const nxt_process_init_t init); +static nxt_int_t nxt_main_start_process(nxt_task_t *task, + nxt_process_t *process); +static nxt_process_t *nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt); static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data); static void nxt_main_process_sigquit_handler(nxt_task_t *task, void *obj, @@ -60,8 +51,7 @@ static void nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data); static void nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data); -static void nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid); -static void nxt_main_stop_worker_processes(nxt_task_t *task, nxt_runtime_t *rt); +static void nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid); static void nxt_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static nxt_int_t nxt_main_listening_socket(nxt_sockaddr_t *sa, @@ -73,29 +63,6 @@ static void nxt_main_port_conf_store_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); static void nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); -static nxt_process_init_t *nxt_process_init_create(nxt_task_t *task, - nxt_process_type_t type, const nxt_str_t *name); -static nxt_int_t nxt_process_init_name_set(nxt_process_init_t *init, - nxt_process_type_t type, const nxt_str_t *name); -static nxt_int_t nxt_process_init_creds_set(nxt_task_t *task, - nxt_process_init_t *init, nxt_str_t *user, nxt_str_t *group); - -static nxt_int_t nxt_init_isolation(nxt_task_t *task, - nxt_conf_value_t *isolation, nxt_process_init_t *init); -#if (NXT_HAVE_CLONE) -static nxt_int_t nxt_init_clone_flags(nxt_task_t *task, - nxt_conf_value_t *namespaces, nxt_process_init_t *init); -#endif - -#if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_init_isolation_creds(nxt_task_t *task, - nxt_conf_value_t *isolation, nxt_process_init_t *init); -static nxt_int_t nxt_init_vldt_isolation_creds(nxt_task_t *task, - nxt_process_init_t *init); -static nxt_int_t nxt_init_isolation_credential_map(nxt_task_t *task, - nxt_mp_t *mem_pool, nxt_conf_value_t *map_array, - nxt_clone_credential_map_t *map); -#endif const nxt_sig_event_t nxt_main_process_signals[] = { nxt_event_signal(SIGHUP, nxt_main_process_signal_handler), @@ -108,54 +75,6 @@ const nxt_sig_event_t nxt_main_process_signals[] = { }; -static const nxt_port_handlers_t nxt_app_process_port_handlers = { - .new_port = nxt_port_new_port_handler, - .change_file = nxt_port_change_log_file_handler, - .mmap = nxt_port_mmap_handler, - .remove_pid = nxt_port_remove_pid_handler, -}; - - -static const nxt_port_handlers_t nxt_discovery_process_port_handlers = { - .quit = nxt_worker_process_quit_handler, - .new_port = nxt_port_new_port_handler, - .change_file = nxt_port_change_log_file_handler, - .mmap = nxt_port_mmap_handler, - .data = nxt_port_data_handler, - .remove_pid = nxt_port_remove_pid_handler, - .rpc_ready = nxt_port_rpc_handler, - .rpc_error = nxt_port_rpc_handler, -}; - - -static const nxt_port_handlers_t *nxt_process_port_handlers[NXT_PROCESS_MAX] = -{ - NULL, - &nxt_discovery_process_port_handlers, - &nxt_controller_process_port_handlers, - &nxt_router_process_port_handlers, - &nxt_app_process_port_handlers -}; - - -static const nxt_process_start_t nxt_process_starts[NXT_PROCESS_MAX] = { - NULL, - nxt_discovery_start, - nxt_controller_start, - nxt_router_start, - nxt_app_start -}; - - -static const nxt_process_restart_t nxt_process_restarts[NXT_PROCESS_MAX] = { - NULL, - NULL, - &nxt_main_create_controller_process, - &nxt_main_create_router_process, - NULL -}; - - static nxt_bool_t nxt_exiting; @@ -172,11 +91,11 @@ nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task, nxt_main_process_title(task); /* - * The dicsovery process will send a message processed by + * The discovery process will send a message processed by * nxt_main_port_modules_handler() which starts the controller * and router processes. */ - return nxt_main_start_discovery_process(task, rt); + return nxt_main_process_create(task, nxt_discovery_process); } @@ -350,48 +269,71 @@ nxt_port_main_data_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) static void -nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +nxt_port_main_start_process_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { - u_char *start, ch; + u_char *start, *p, ch; size_t type_len; - nxt_mp_t *mp; nxt_int_t ret; nxt_buf_t *b; nxt_port_t *port; nxt_runtime_t *rt; + nxt_process_t *process; nxt_app_type_t idx; nxt_conf_value_t *conf; - nxt_common_app_conf_t app_conf; + nxt_process_init_t *init; + nxt_common_app_conf_t *app_conf; ret = NXT_ERROR; - mp = nxt_mp_create(1024, 128, 256, 32); + rt = task->thread->runtime; - if (nxt_slow_path(mp == NULL)) { + process = nxt_main_process_new(task, rt); + if (nxt_slow_path(process == NULL)) { return; } - b = nxt_buf_chk_make_plain(mp, msg->buf, msg->size); + init = nxt_process_init(process); + + *init = nxt_app_process; + b = nxt_buf_chk_make_plain(process->mem_pool, msg->buf, msg->size); if (b == NULL) { - return; + goto failed; } - nxt_debug(task, "main start worker: %*s", b->mem.free - b->mem.pos, + nxt_debug(task, "main start process: %*s", b->mem.free - b->mem.pos, b->mem.pos); - nxt_memzero(&app_conf, sizeof(nxt_common_app_conf_t)); + app_conf = nxt_mp_zalloc(process->mem_pool, sizeof(nxt_common_app_conf_t)); + if (nxt_slow_path(app_conf == NULL)) { + goto failed; + } start = b->mem.pos; - app_conf.name.start = start; - app_conf.name.length = nxt_strlen(start); - app_conf.shm_limit = 100 * 1024 * 1024; + app_conf->name.start = start; + app_conf->name.length = nxt_strlen(start); + + init->name = (const char *) start; - start += app_conf.name.length + 1; + process->name = nxt_mp_alloc(process->mem_pool, app_conf->name.length + + sizeof("\"\" application") + 1); - conf = nxt_conf_json_parse(mp, start, b->mem.free, NULL); + if (nxt_slow_path(process->name == NULL)) { + goto failed; + } + + p = (u_char *) process->name; + *p++ = '"'; + p = nxt_cpymem(p, init->name, app_conf->name.length); + p = nxt_cpymem(p, "\" application", 13); + *p = '\0'; + app_conf->shm_limit = 100 * 1024 * 1024; + + start += app_conf->name.length + 1; + + conf = nxt_conf_json_parse(process->mem_pool, start, b->mem.free, NULL); if (conf == NULL) { nxt_alert(task, "router app configuration parsing error"); @@ -400,44 +342,45 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) rt = task->thread->runtime; - app_conf.user.start = (u_char*)rt->user_cred.user; - app_conf.user.length = nxt_strlen(rt->user_cred.user); + app_conf->user.start = (u_char*)rt->user_cred.user; + app_conf->user.length = nxt_strlen(rt->user_cred.user); + + ret = nxt_conf_map_object(process->mem_pool, conf, nxt_common_app_conf, + nxt_nitems(nxt_common_app_conf), app_conf); - ret = nxt_conf_map_object(mp, conf, nxt_common_app_conf, - nxt_nitems(nxt_common_app_conf), &app_conf); if (ret != NXT_OK) { nxt_alert(task, "failed to map common app conf received from router"); goto failed; } - for (type_len = 0; type_len != app_conf.type.length; type_len++) { - ch = app_conf.type.start[type_len]; + for (type_len = 0; type_len != app_conf->type.length; type_len++) { + ch = app_conf->type.start[type_len]; if (ch == ' ' || nxt_isdigit(ch)) { break; } } - idx = nxt_app_parse_type(app_conf.type.start, type_len); + idx = nxt_app_parse_type(app_conf->type.start, type_len); if (nxt_slow_path(idx >= nxt_nitems(nxt_app_maps))) { nxt_alert(task, "invalid app type %d received from router", (int) idx); goto failed; } - ret = nxt_conf_map_object(mp, conf, nxt_app_maps[idx].map, - nxt_app_maps[idx].size, &app_conf); + ret = nxt_conf_map_object(process->mem_pool, conf, nxt_app_maps[idx].map, + nxt_app_maps[idx].size, app_conf); if (nxt_slow_path(ret != NXT_OK)) { nxt_alert(task, "failed to map app conf received from router"); goto failed; } - if (app_conf.limits != NULL) { - ret = nxt_conf_map_object(mp, app_conf.limits, + if (app_conf->limits != NULL) { + ret = nxt_conf_map_object(process->mem_pool, app_conf->limits, nxt_common_app_limits_conf, nxt_nitems(nxt_common_app_limits_conf), - &app_conf); + app_conf); if (nxt_slow_path(ret != NXT_OK)) { nxt_alert(task, "failed to map app limits received from router"); @@ -445,40 +388,91 @@ nxt_port_main_start_worker_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) } } - app_conf.self = conf; + app_conf->self = conf; - ret = nxt_main_start_worker_process(task, task->thread->runtime, - &app_conf, msg->port_msg.stream); + process->stream = msg->port_msg.stream; + process->data.app = app_conf; + + ret = nxt_main_start_process(task, process); + if (nxt_fast_path(ret == NXT_OK || ret == NXT_AGAIN)) { + return; + } failed: - if (ret == NXT_ERROR) { - port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid, - msg->port_msg.reply_port); - if (nxt_fast_path(port != NULL)) { - nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, - -1, msg->port_msg.stream, 0, NULL); - } + nxt_process_use(task, process, -1); + + port = nxt_runtime_port_find(rt, msg->port_msg.pid, + msg->port_msg.reply_port); + + if (nxt_fast_path(port != NULL)) { + nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, + -1, msg->port_msg.stream, 0, NULL); } +} - nxt_mp_destroy(mp); + +static void +nxt_main_process_created_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_port_t *port; + nxt_process_t *process; + nxt_runtime_t *rt; + + rt = task->thread->runtime; + + process = nxt_runtime_process_find(rt, msg->port_msg.pid); + if (nxt_slow_path(process == NULL)) { + return; + } + + nxt_assert(process->state == NXT_PROCESS_STATE_CREATING); + + port = nxt_runtime_port_find(rt, msg->port_msg.pid, + msg->port_msg.reply_port); + + + if (nxt_slow_path(port == NULL)) { + return; + } + +#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { + if (nxt_slow_path(nxt_clone_credential_map(task, process->pid, + process->user_cred, + &process->isolation.clone) + != NXT_OK)) + { + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, + -1, msg->port_msg.stream, 0, NULL); + return; + } + } + +#endif + + process->state = NXT_PROCESS_STATE_CREATED; + + (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, + -1, msg->port_msg.stream, 0, NULL); } static nxt_port_handlers_t nxt_main_process_port_handlers = { - .data = nxt_port_main_data_handler, - .process_ready = nxt_port_process_ready_handler, - .start_worker = nxt_port_main_start_worker_handler, - .socket = nxt_main_port_socket_handler, - .modules = nxt_main_port_modules_handler, - .conf_store = nxt_main_port_conf_store_handler, + .data = nxt_port_main_data_handler, + .process_created = nxt_main_process_created_handler, + .process_ready = nxt_port_process_ready_handler, + .start_process = nxt_port_main_start_process_handler, + .socket = nxt_main_port_socket_handler, + .modules = nxt_main_port_modules_handler, + .conf_store = nxt_main_port_conf_store_handler, #if (NXT_TLS) - .cert_get = nxt_cert_store_get_handler, - .cert_delete = nxt_cert_store_delete_handler, + .cert_get = nxt_cert_store_get_handler, + .cert_delete = nxt_cert_store_delete_handler, #endif - .access_log = nxt_main_port_access_log_handler, - .rpc_ready = nxt_port_rpc_handler, - .rpc_error = nxt_port_rpc_handler, + .access_log = nxt_main_port_access_log_handler, + .rpc_ready = nxt_port_rpc_handler, + .rpc_error = nxt_port_rpc_handler, }; @@ -499,16 +493,17 @@ nxt_main_process_port_create(nxt_task_t *task, nxt_runtime_t *rt) ret = nxt_port_socket_init(task, port, 0); if (nxt_slow_path(ret != NXT_OK)) { + nxt_port_use(task, port, -1); return ret; } /* * A main process port. A write port is not closed - * since it should be inherited by worker processes. + * since it should be inherited by processes. */ nxt_port_enable(task, port, &nxt_main_process_port_handlers); - process->ready = 1; + process->state = NXT_PROCESS_STATE_READY; return NXT_OK; } @@ -541,234 +536,68 @@ nxt_main_process_title(nxt_task_t *task) static nxt_int_t -nxt_main_start_controller_process(nxt_task_t *task, nxt_runtime_t *rt) +nxt_main_process_create(nxt_task_t *task, const nxt_process_init_t init) { - nxt_process_init_t *init; + nxt_int_t ret; + nxt_runtime_t *rt; + nxt_process_t *process; + nxt_process_init_t *pinit; - static const nxt_str_t name = nxt_string("controller"); + rt = task->thread->runtime; - init = nxt_process_init_create(task, NXT_PROCESS_CONTROLLER, &name); - if (nxt_slow_path(init == NULL)) { + process = nxt_main_process_new(task, rt); + if (nxt_slow_path(process == NULL)) { return NXT_ERROR; } - return nxt_main_create_controller_process(task, rt, init);; -} + process->name = init.name; + process->user_cred = &rt->user_cred; + pinit = nxt_process_init(process); + *pinit = init; -static nxt_int_t -nxt_main_create_controller_process(nxt_task_t *task, nxt_runtime_t *rt, - nxt_process_init_t *init) -{ - ssize_t n; - nxt_int_t ret; - nxt_str_t *conf; - nxt_file_t file; - nxt_file_info_t fi; - nxt_controller_init_t ctrl_init; - - nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t)); - - conf = &ctrl_init.conf; - - nxt_memzero(&file, sizeof(nxt_file_t)); - - file.name = (nxt_file_name_t *) rt->conf; - - ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN, 0); - - if (ret == NXT_OK) { - ret = nxt_file_info(&file, &fi); - - if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) { - conf->length = nxt_file_size(&fi); - conf->start = nxt_malloc(conf->length); - - if (nxt_slow_path(conf->start == NULL)) { - nxt_file_close(task, &file); - return NXT_ERROR; - } - - n = nxt_file_read(&file, conf->start, conf->length, 0); - - if (nxt_slow_path(n != (ssize_t) conf->length)) { - nxt_free(conf->start); - conf->start = NULL; - - nxt_alert(task, "failed to restore previous configuration: " - "cannot read the file"); - } - } - - nxt_file_close(task, &file); - } - -#if (NXT_TLS) - ctrl_init.certs = nxt_cert_store_load(task); -#endif - - init->data = &ctrl_init; - - ret = nxt_main_create_worker_process(task, rt, init); - - if (ret == NXT_OK) { - if (conf->start != NULL) { - nxt_free(conf->start); - } - -#if (NXT_TLS) - if (ctrl_init.certs != NULL) { - nxt_cert_store_release(ctrl_init.certs); - } -#endif + ret = nxt_main_start_process(task, process); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_process_use(task, process, -1); } return ret; } -static nxt_int_t -nxt_main_start_discovery_process(nxt_task_t *task, nxt_runtime_t *rt) +static nxt_process_t * +nxt_main_process_new(nxt_task_t *task, nxt_runtime_t *rt) { - nxt_process_init_t *init; - - static const nxt_str_t name = nxt_string("discovery"); + nxt_process_t *process; - init = nxt_process_init_create(task, NXT_PROCESS_DISCOVERY, &name); - if (nxt_slow_path(init == NULL)) { - return NXT_ERROR; + process = nxt_runtime_process_new(rt); + if (nxt_slow_path(process == NULL)) { + return NULL; } - return nxt_main_create_worker_process(task, rt, init); -} - - -static nxt_int_t -nxt_main_start_router_process(nxt_task_t *task, nxt_runtime_t *rt) -{ - nxt_process_init_t *init; - - static const nxt_str_t name = nxt_string("router"); - - init = nxt_process_init_create(task, NXT_PROCESS_ROUTER, &name); - if (nxt_slow_path(init == NULL)) { - return NXT_ERROR; + process->mem_pool = nxt_mp_create(1024, 128, 256, 32); + if (process->mem_pool == NULL) { + nxt_process_use(task, process, -1); + return NULL; } - return nxt_main_create_router_process(task, rt, init); + return process; } static nxt_int_t -nxt_main_create_router_process(nxt_task_t *task, nxt_runtime_t *rt, - nxt_process_init_t *init) +nxt_main_start_process(nxt_task_t *task, nxt_process_t *process) { - nxt_main_stop_worker_processes(task, rt); - - return nxt_main_create_worker_process(task, rt, init); -} - - -static nxt_int_t -nxt_main_start_worker_process(nxt_task_t *task, nxt_runtime_t *rt, - nxt_common_app_conf_t *app_conf, uint32_t stream) -{ - nxt_int_t cap_setid; + nxt_mp_t *tmp_mp; nxt_int_t ret; + nxt_pid_t pid; + nxt_port_t *port; nxt_process_init_t *init; - init = nxt_process_init_create(task, NXT_PROCESS_WORKER, &app_conf->name); - if (nxt_slow_path(init == NULL)) { - return NXT_ERROR; - } - - cap_setid = rt->capabilities.setid; - - if (app_conf->isolation != NULL) { - ret = nxt_init_isolation(task, app_conf->isolation, init); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - } - -#if (NXT_HAVE_CLONE_NEWUSER) - if (NXT_CLONE_USER(init->isolation.clone.flags)) { - cap_setid = 1; - } -#endif - - if (cap_setid) { - ret = nxt_process_init_creds_set(task, init, &app_conf->user, - &app_conf->group); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - - } else { - if (!nxt_str_eq(&app_conf->user, (u_char *) rt->user_cred.user, - nxt_strlen(rt->user_cred.user))) - { - nxt_alert(task, "cannot set user \"%V\" for app \"%V\": " - "missing capabilities", &app_conf->user, &app_conf->name); - goto fail; - } - - if (app_conf->group.length > 0 - && !nxt_str_eq(&app_conf->group, (u_char *) rt->group, - nxt_strlen(rt->group))) - { - nxt_alert(task, "cannot set group \"%V\" for app \"%V\": " - "missing capabilities", &app_conf->group, - &app_conf->name); - goto fail; - } - } - - init->data = app_conf; - init->stream = stream; - -#if (NXT_HAVE_CLONE_NEWUSER) - ret = nxt_init_vldt_isolation_creds(task, init); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } -#endif - - return nxt_main_create_worker_process(task, rt, init); - -fail: - - nxt_mp_destroy(init->mem_pool); - - return NXT_ERROR; -} - - -nxt_int_t -nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt, - nxt_process_init_t *init) -{ - nxt_int_t ret; - nxt_pid_t pid; - nxt_port_t *port; - nxt_process_t *process; - - /* - * TODO: remove process, init, ports from array on memory and fork failures. - */ - - process = nxt_runtime_process_new(rt); - if (nxt_slow_path(process == NULL)) { - nxt_mp_destroy(init->mem_pool); - - return NXT_ERROR; - } - - process->init = init; + init = nxt_process_init(process); port = nxt_port_new(task, 0, 0, init->type); if (nxt_slow_path(port == NULL)) { - nxt_process_use(task, process, -1); return NXT_ERROR; } @@ -776,10 +605,24 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt, nxt_process_use(task, process, -1); + ret = NXT_ERROR; + tmp_mp = NULL; + ret = nxt_port_socket_init(task, port, 0); if (nxt_slow_path(ret != NXT_OK)) { - nxt_port_use(task, port, -1); - return ret; + goto fail; + } + + tmp_mp = nxt_mp_create(1024, 128, 256, 32); + if (tmp_mp == NULL) { + goto fail; + } + + if (init->prefork) { + ret = init->prefork(task, process, tmp_mp); + if (nxt_slow_path(ret != NXT_OK)) { + goto fail; + } } pid = nxt_process_create(task, process); @@ -788,15 +631,13 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt, case -1: nxt_port_close(task, port); - nxt_port_use(task, port, -1); - - return NXT_ERROR; + break; case 0: - /* A worker process, return to the event engine work queue loop. */ - nxt_port_use(task, port, -1); + /* The child process: return to the event engine work queue loop. */ - return NXT_AGAIN; + ret = NXT_AGAIN; + break; default: /* The main process created a new process. */ @@ -804,35 +645,22 @@ nxt_main_create_worker_process(nxt_task_t *task, nxt_runtime_t *rt, nxt_port_read_close(port); nxt_port_write_enable(task, port); - nxt_port_use(task, port, -1); - - return NXT_OK; + ret = NXT_OK; + break; } -} - -void -nxt_main_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) { - - if (nxt_pid != process->pid) { - nxt_process_port_each(process, port) { +fail: - (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_QUIT, - -1, 0, 0, NULL); + nxt_port_use(task, port, -1); - } nxt_process_port_loop; - } + if (nxt_fast_path(tmp_mp != NULL)) { + nxt_mp_destroy(tmp_mp); + } - } nxt_runtime_process_loop; + return ret; } - static void nxt_main_process_sigterm_handler(nxt_task_t *task, void *obj, void *data) { @@ -1009,7 +837,7 @@ nxt_main_process_sigchld_handler(nxt_task_t *task, void *obj, void *data) pid, WEXITSTATUS(status)); } - nxt_main_cleanup_worker_process(task, pid); + nxt_main_cleanup_process(task, pid); } } @@ -1023,101 +851,80 @@ nxt_main_process_signal_handler(nxt_task_t *task, void *obj, void *data) static void -nxt_main_cleanup_worker_process(nxt_task_t *task, nxt_pid_t pid) +nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid) { - nxt_buf_t *buf; - nxt_port_t *port; - nxt_runtime_t *rt; - nxt_process_t *process; - nxt_process_type_t ptype; - nxt_process_init_t *init; - nxt_process_restart_t restart; + int stream; + nxt_int_t ret; + nxt_buf_t *buf; + nxt_port_t *port; + const char *name; + nxt_runtime_t *rt; + nxt_process_t *process; + nxt_process_init_t init; rt = task->thread->runtime; process = nxt_runtime_process_find(rt, pid); + if (!process) { + return; + } - if (process) { - init = process->init; - process->init = NULL; + name = process->name; + stream = process->stream; + init = *((nxt_process_init_t *) nxt_process_init(process)); - ptype = nxt_process_type(process); - restart = nxt_process_restarts[ptype]; + if (process->state == NXT_PROCESS_STATE_READY) { + process->stream = 0; + } - if (process->ready) { - init->stream = 0; - } + nxt_process_close_ports(task, process); - nxt_process_close_ports(task, process); + if (nxt_exiting) { + if (rt->nprocesses <= 1) { + nxt_runtime_quit(task, 0); + } - if (nxt_exiting) { - nxt_mp_destroy(init->mem_pool); + return; + } - if (rt->nprocesses <= 2) { - nxt_runtime_quit(task, 0); - } + nxt_runtime_process_each(rt, process) { - return; + if (process->pid == nxt_pid + || process->pid == pid + || nxt_queue_is_empty(&process->ports)) + { + continue; } - nxt_runtime_process_each(rt, process) { + port = nxt_process_port_first(process); - if (process->pid == nxt_pid - || process->pid == pid - || nxt_queue_is_empty(&process->ports)) - { - continue; - } - - port = nxt_process_port_first(process); + if (nxt_proc_remove_notify_matrix[init.type][port->type] == 0) { + continue; + } - if (nxt_proc_remove_notify_matrix[ptype][port->type] == 0) { - continue; - } + buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, + sizeof(pid)); - buf = nxt_buf_mem_ts_alloc(task, task->thread->engine->mem_pool, - sizeof(pid)); - if (nxt_slow_path(buf == NULL)) { - continue; - } + if (nxt_slow_path(buf == NULL)) { + continue; + } - buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid)); + buf->mem.free = nxt_cpymem(buf->mem.free, &pid, sizeof(pid)); - nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID, - -1, init->stream, 0, buf); - } nxt_runtime_process_loop; + nxt_port_socket_write(task, port, NXT_PORT_MSG_REMOVE_PID, -1, + stream, 0, buf); - if (restart != NULL) { - restart(task, rt, init); + } nxt_runtime_process_loop; - } else { - nxt_mp_destroy(init->mem_pool); + if (init.restart) { + ret = nxt_main_process_create(task, init); + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_alert(task, "failed to restart %s", name); } } } -static void -nxt_main_stop_worker_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) { - - if (port->type == NXT_PROCESS_WORKER) { - (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_main_port_socket_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { @@ -1419,10 +1226,15 @@ fail: nxt_mp_destroy(mp); - ret = nxt_main_start_controller_process(task, rt); - + ret = nxt_main_process_create(task, nxt_controller_process); if (ret == NXT_OK) { - (void) nxt_main_start_router_process(task, rt); + ret = nxt_main_process_create(task, nxt_router_process); + } + + if (nxt_slow_path(ret == NXT_ERROR)) { + nxt_exiting = 1; + + nxt_runtime_quit(task, 1); } } @@ -1534,362 +1346,3 @@ nxt_main_port_access_log_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) msg->port_msg.stream, 0, NULL); } } - - -static nxt_int_t -nxt_init_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, - nxt_process_init_t *init) -{ -#if (NXT_HAVE_CLONE) - nxt_int_t ret; - nxt_conf_value_t *obj; - - static nxt_str_t nsname = nxt_string("namespaces"); - - obj = nxt_conf_get_object_member(isolation, &nsname, NULL); - if (obj != NULL) { - ret = nxt_init_clone_flags(task, obj, init); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - } -#endif - -#if (NXT_HAVE_CLONE_NEWUSER) - ret = nxt_init_isolation_creds(task, isolation, init); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } -#endif - - return NXT_OK; -} - - -#if (NXT_HAVE_CLONE_NEWUSER) - -static nxt_int_t -nxt_init_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, - nxt_process_init_t *init) -{ - nxt_int_t ret; - nxt_clone_t *clone; - nxt_conf_value_t *array; - - static nxt_str_t uidname = nxt_string("uidmap"); - static nxt_str_t gidname = nxt_string("gidmap"); - - clone = &init->isolation.clone; - - array = nxt_conf_get_object_member(isolation, &uidname, NULL); - if (array != NULL) { - ret = nxt_init_isolation_credential_map(task, init->mem_pool, array, - &clone->uidmap); - - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - } - - array = nxt_conf_get_object_member(isolation, &gidname, NULL); - if (array != NULL) { - ret = nxt_init_isolation_credential_map(task, init->mem_pool, array, - &clone->gidmap); - - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - } - - return NXT_OK; -} - - -static nxt_int_t -nxt_init_vldt_isolation_creds(nxt_task_t *task, nxt_process_init_t *init) -{ - nxt_int_t ret; - nxt_clone_t *clone; - - clone = &init->isolation.clone; - - if (clone->uidmap.size == 0 && clone->gidmap.size == 0) { - return NXT_OK; - } - - if (!NXT_CLONE_USER(clone->flags)) { - if (nxt_slow_path(clone->uidmap.size > 0)) { - nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but " - "\"isolation.namespaces.credential\" is false or unset"); - - return NXT_ERROR; - } - - if (nxt_slow_path(clone->gidmap.size > 0)) { - nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but " - "\"isolation.namespaces.credential\" is false or unset"); - - return NXT_ERROR; - } - - return NXT_OK; - } - - ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap, - init->user_cred); - - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; - } - - return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap, - init->user_cred); -} - - -static nxt_int_t -nxt_init_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool, - nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map) -{ - nxt_int_t ret; - nxt_uint_t i; - nxt_conf_value_t *obj; - - static nxt_conf_map_t nxt_clone_map_entry_conf[] = { - { - nxt_string("container"), - NXT_CONF_MAP_INT, - offsetof(nxt_clone_map_entry_t, container), - }, - - { - nxt_string("host"), - NXT_CONF_MAP_INT, - offsetof(nxt_clone_map_entry_t, host), - }, - - { - nxt_string("size"), - NXT_CONF_MAP_INT, - offsetof(nxt_clone_map_entry_t, size), - }, - }; - - map->size = nxt_conf_array_elements_count(map_array); - - if (map->size == 0) { - return NXT_OK; - } - - map->map = nxt_mp_alloc(mem_pool, - map->size * sizeof(nxt_clone_map_entry_t)); - if (nxt_slow_path(map->map == NULL)) { - return NXT_ERROR; - } - - for (i = 0; i < map->size; i++) { - obj = nxt_conf_get_array_element(map_array, i); - - ret = nxt_conf_map_object(mem_pool, obj, nxt_clone_map_entry_conf, - nxt_nitems(nxt_clone_map_entry_conf), - map->map + i); - if (nxt_slow_path(ret != NXT_OK)) { - nxt_alert(task, "clone map entry map error"); - return NXT_ERROR; - } - } - - return NXT_OK; -} - -#endif - -#if (NXT_HAVE_CLONE) - -static nxt_int_t -nxt_init_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, - nxt_process_init_t *init) -{ - uint32_t index; - nxt_str_t name; - nxt_int_t flag; - nxt_conf_value_t *value; - - index = 0; - - for ( ;; ) { - value = nxt_conf_next_object_member(namespaces, &name, &index); - - if (value == NULL) { - break; - } - - flag = 0; - -#if (NXT_HAVE_CLONE_NEWUSER) - if (nxt_str_eq(&name, "credential", 10)) { - flag = CLONE_NEWUSER; - } -#endif - -#if (NXT_HAVE_CLONE_NEWPID) - if (nxt_str_eq(&name, "pid", 3)) { - flag = CLONE_NEWPID; - } -#endif - -#if (NXT_HAVE_CLONE_NEWNET) - if (nxt_str_eq(&name, "network", 7)) { - flag = CLONE_NEWNET; - } -#endif - -#if (NXT_HAVE_CLONE_NEWUTS) - if (nxt_str_eq(&name, "uname", 5)) { - flag = CLONE_NEWUTS; - } -#endif - -#if (NXT_HAVE_CLONE_NEWNS) - if (nxt_str_eq(&name, "mount", 5)) { - flag = CLONE_NEWNS; - } -#endif - -#if (NXT_HAVE_CLONE_NEWCGROUP) - if (nxt_str_eq(&name, "cgroup", 6)) { - flag = CLONE_NEWCGROUP; - } -#endif - - if (!flag) { - nxt_alert(task, "unknown namespace flag: \"%V\"", &name); - return NXT_ERROR; - } - - if (nxt_conf_get_boolean(value)) { - init->isolation.clone.flags |= flag; - } - } - - return NXT_OK; -} - -#endif - - -static nxt_process_init_t * -nxt_process_init_create(nxt_task_t *task, nxt_process_type_t type, - const nxt_str_t *name) -{ - nxt_mp_t *mp; - nxt_int_t ret; - nxt_runtime_t *rt; - nxt_process_init_t *init; - - mp = nxt_mp_create(1024, 128, 256, 32); - if (nxt_slow_path(mp == NULL)) { - return NULL; - } - - init = nxt_mp_zalloc(mp, sizeof(nxt_process_init_t)); - if (nxt_slow_path(init == NULL)) { - goto fail; - } - - init->mem_pool = mp; - - ret = nxt_process_init_name_set(init, type, name); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - - rt = task->thread->runtime; - - init->type = type; - init->start = nxt_process_starts[type]; - init->port_handlers = nxt_process_port_handlers[type]; - init->signals = nxt_worker_process_signals; - init->user_cred = &rt->user_cred; - init->data = &rt; - - return init; - -fail: - - nxt_mp_destroy(mp); - - return NULL; -} - - -static nxt_int_t -nxt_process_init_name_set(nxt_process_init_t *init, nxt_process_type_t type, - const nxt_str_t *name) -{ - u_char *str, *end; - size_t size; - const char *fmt; - - size = name->length + 1; - - if (type == NXT_PROCESS_WORKER) { - size += nxt_length("\"\" application"); - fmt = "\"%V\" application%Z"; - - } else { - fmt = "%V%Z"; - } - - str = nxt_mp_alloc(init->mem_pool, size); - if (nxt_slow_path(str == NULL)) { - return NXT_ERROR; - } - - end = str + size; - - nxt_sprintf(str, end, fmt, name); - - init->name = (char *) str; - - return NXT_OK; -} - - -static nxt_int_t -nxt_process_init_creds_set(nxt_task_t *task, nxt_process_init_t *init, - nxt_str_t *user, nxt_str_t *group) -{ - char *str; - - init->user_cred = nxt_mp_zalloc(init->mem_pool, sizeof(nxt_credential_t)); - - if (nxt_slow_path(init->user_cred == NULL)) { - return NXT_ERROR; - } - - str = nxt_mp_zalloc(init->mem_pool, user->length + 1); - if (nxt_slow_path(str == NULL)) { - return NXT_ERROR; - } - - nxt_memcpy(str, user->start, user->length); - str[user->length] = '\0'; - - init->user_cred->user = str; - - if (group->start != NULL) { - str = nxt_mp_zalloc(init->mem_pool, group->length + 1); - if (nxt_slow_path(str == NULL)) { - return NXT_ERROR; - } - - nxt_memcpy(str, group->start, group->length); - str[group->length] = '\0'; - - } else { - str = NULL; - } - - return nxt_credential_get(task, init->mem_pool, init->user_cred, str); -} diff --git a/src/nxt_main_process.h b/src/nxt_main_process.h index b0570a84..f9c974d8 100644 --- a/src/nxt_main_process.h +++ b/src/nxt_main_process.h @@ -19,26 +19,17 @@ typedef enum { } nxt_socket_error_t; -typedef struct { - nxt_str_t conf; -#if (NXT_TLS) - nxt_array_t *certs; -#endif -} nxt_controller_init_t; - - nxt_int_t nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task, nxt_runtime_t *runtime); -void nxt_main_stop_all_processes(nxt_task_t *task, nxt_runtime_t *runtime); -nxt_int_t nxt_controller_start(nxt_task_t *task, void *data); -nxt_int_t nxt_router_start(nxt_task_t *task, void *data); -nxt_int_t nxt_discovery_start(nxt_task_t *task, void *data); -nxt_int_t nxt_app_start(nxt_task_t *task, void *data); +NXT_EXPORT extern const nxt_process_init_t nxt_discovery_process; +NXT_EXPORT extern const nxt_process_init_t nxt_controller_process; +NXT_EXPORT extern const nxt_process_init_t nxt_router_process; +NXT_EXPORT extern const nxt_process_init_t nxt_app_process; extern const nxt_sig_event_t nxt_main_process_signals[]; -extern const nxt_sig_event_t nxt_worker_process_signals[]; +extern const nxt_sig_event_t nxt_process_signals[]; #endif /* _NXT_MAIN_PROCESS_H_INCLUDED_ */ diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index d3c23c31..ddad5761 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -72,7 +72,7 @@ typedef void (*zif_handler)(INTERNAL_FUNCTION_PARAMETERS); #endif -static nxt_int_t nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_php_start(nxt_task_t *task, nxt_process_data_t *data); static nxt_int_t nxt_php_set_target(nxt_task_t *task, nxt_php_target_t *target, nxt_conf_value_t *conf); static void nxt_php_set_options(nxt_task_t *task, nxt_conf_value_t *options, @@ -242,7 +242,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_string("php"), PHP_VERSION, NULL, - nxt_php_init, + nxt_php_start, }; @@ -256,19 +256,20 @@ static void ***tsrm_ls; static nxt_int_t -nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_php_start(nxt_task_t *task, nxt_process_data_t *data) { - u_char *p; - uint32_t next; - nxt_str_t ini_path, name; - nxt_int_t ret; - nxt_uint_t n; - nxt_port_t *my_port, *main_port; - nxt_runtime_t *rt; - nxt_unit_ctx_t *unit_ctx; - nxt_unit_init_t php_init; - nxt_conf_value_t *value; - nxt_php_app_conf_t *c; + u_char *p; + uint32_t next; + nxt_str_t ini_path, name; + nxt_int_t ret; + nxt_uint_t n; + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t php_init; + nxt_conf_value_t *value; + nxt_php_app_conf_t *c; + nxt_common_app_conf_t *conf; static nxt_str_t file_str = nxt_string("file"); static nxt_str_t user_str = nxt_string("user"); @@ -276,6 +277,7 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_php_task = task; + conf = data->app; c = &conf->u.php; n = (c->targets != NULL) ? nxt_conf_object_members_count(c->targets) : 1; @@ -385,7 +387,7 @@ nxt_php_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_fd_blocking(task, main_port->pair[1]); - php_init.ready_stream = my_port->process->init->stream; + php_init.ready_stream = my_port->process->stream; php_init.read_port.id.pid = my_port->pid; php_init.read_port.id.id = my_port->id; diff --git a/src/nxt_port.c b/src/nxt_port.c index 70cf33e6..7232c465 100644 --- a/src/nxt_port.c +++ b/src/nxt_port.c @@ -296,7 +296,9 @@ nxt_port_process_ready_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) return; } - process->ready = 1; + nxt_assert(process->state != NXT_PROCESS_STATE_READY); + + process->state = NXT_PROCESS_STATE_READY; nxt_assert(!nxt_queue_is_empty(&process->ports)); diff --git a/src/nxt_port.h b/src/nxt_port.h index c6f15238..0e8707f3 100644 --- a/src/nxt_port.h +++ b/src/nxt_port.h @@ -14,7 +14,7 @@ struct nxt_port_handlers_s { nxt_port_handler_t rpc_error; /* Main process RPC requests. */ - nxt_port_handler_t start_worker; + nxt_port_handler_t start_process; nxt_port_handler_t socket; nxt_port_handler_t modules; nxt_port_handler_t conf_store; @@ -27,7 +27,8 @@ struct nxt_port_handlers_s { nxt_port_handler_t new_port; nxt_port_handler_t mmap; - /* New process ready. */ + /* New process */ + nxt_port_handler_t process_created; nxt_port_handler_t process_ready; /* Process exit/crash notification. */ @@ -53,76 +54,76 @@ struct nxt_port_handlers_s { #define nxt_port_handler_idx(name) \ ( offsetof(nxt_port_handlers_t, name) / sizeof(nxt_port_handler_t) ) +#define nxt_msg_last(handler) \ + (handler | NXT_PORT_MSG_LAST) typedef enum { - NXT_PORT_MSG_LAST = 0x100, - NXT_PORT_MSG_CLOSE_FD = 0x200, - NXT_PORT_MSG_SYNC = 0x400, - - NXT_PORT_MSG_MASK = 0xFF, - - _NXT_PORT_MSG_RPC_READY = nxt_port_handler_idx(rpc_ready), - _NXT_PORT_MSG_RPC_ERROR = nxt_port_handler_idx(rpc_error), - - _NXT_PORT_MSG_START_WORKER = nxt_port_handler_idx(start_worker), - _NXT_PORT_MSG_SOCKET = nxt_port_handler_idx(socket), - _NXT_PORT_MSG_MODULES = nxt_port_handler_idx(modules), - _NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store), - _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get), - _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete), - _NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log), - - _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file), - _NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port), - _NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap), - - _NXT_PORT_MSG_PROCESS_READY = nxt_port_handler_idx(process_ready), - _NXT_PORT_MSG_REMOVE_PID = nxt_port_handler_idx(remove_pid), - _NXT_PORT_MSG_QUIT = nxt_port_handler_idx(quit), - - _NXT_PORT_MSG_REQ_HEADERS = nxt_port_handler_idx(req_headers), - _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame), - - _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data), - - _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm), - _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack), - - NXT_PORT_MSG_MAX = sizeof(nxt_port_handlers_t) - / sizeof(nxt_port_handler_t), - - NXT_PORT_MSG_RPC_READY = _NXT_PORT_MSG_RPC_READY, - NXT_PORT_MSG_RPC_READY_LAST = _NXT_PORT_MSG_RPC_READY | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_RPC_ERROR = _NXT_PORT_MSG_RPC_ERROR | NXT_PORT_MSG_LAST, - - NXT_PORT_MSG_START_WORKER = _NXT_PORT_MSG_START_WORKER - | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_SOCKET = _NXT_PORT_MSG_SOCKET | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_MODULES = _NXT_PORT_MSG_MODULES | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_CONF_STORE = _NXT_PORT_MSG_CONF_STORE | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_CERT_GET = _NXT_PORT_MSG_CERT_GET | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_CERT_DELETE = _NXT_PORT_MSG_CERT_DELETE | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_ACCESS_LOG = _NXT_PORT_MSG_ACCESS_LOG | NXT_PORT_MSG_LAST, - - NXT_PORT_MSG_CHANGE_FILE = _NXT_PORT_MSG_CHANGE_FILE | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_NEW_PORT = _NXT_PORT_MSG_NEW_PORT | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_MMAP = _NXT_PORT_MSG_MMAP | NXT_PORT_MSG_LAST - | NXT_PORT_MSG_CLOSE_FD | NXT_PORT_MSG_SYNC, - - NXT_PORT_MSG_PROCESS_READY = _NXT_PORT_MSG_PROCESS_READY - | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_QUIT = _NXT_PORT_MSG_QUIT | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_REMOVE_PID = _NXT_PORT_MSG_REMOVE_PID | NXT_PORT_MSG_LAST, - - NXT_PORT_MSG_REQ_HEADERS = _NXT_PORT_MSG_REQ_HEADERS, - NXT_PORT_MSG_WEBSOCKET = _NXT_PORT_MSG_WEBSOCKET, - NXT_PORT_MSG_WEBSOCKET_LAST = _NXT_PORT_MSG_WEBSOCKET | NXT_PORT_MSG_LAST, - - NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA, - NXT_PORT_MSG_DATA_LAST = _NXT_PORT_MSG_DATA | NXT_PORT_MSG_LAST, - - NXT_PORT_MSG_OOSM = _NXT_PORT_MSG_OOSM | NXT_PORT_MSG_LAST, - NXT_PORT_MSG_SHM_ACK = _NXT_PORT_MSG_SHM_ACK | NXT_PORT_MSG_LAST, + NXT_PORT_MSG_LAST = 0x100, + NXT_PORT_MSG_CLOSE_FD = 0x200, + NXT_PORT_MSG_SYNC = 0x400, + + NXT_PORT_MSG_MASK = 0xFF, + + _NXT_PORT_MSG_RPC_READY = nxt_port_handler_idx(rpc_ready), + _NXT_PORT_MSG_RPC_ERROR = nxt_port_handler_idx(rpc_error), + + _NXT_PORT_MSG_START_PROCESS = nxt_port_handler_idx(start_process), + _NXT_PORT_MSG_SOCKET = nxt_port_handler_idx(socket), + _NXT_PORT_MSG_MODULES = nxt_port_handler_idx(modules), + _NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store), + _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get), + _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete), + _NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log), + + _NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file), + _NXT_PORT_MSG_NEW_PORT = nxt_port_handler_idx(new_port), + _NXT_PORT_MSG_MMAP = nxt_port_handler_idx(mmap), + + _NXT_PORT_MSG_PROCESS_CREATED = nxt_port_handler_idx(process_created), + _NXT_PORT_MSG_PROCESS_READY = nxt_port_handler_idx(process_ready), + _NXT_PORT_MSG_REMOVE_PID = nxt_port_handler_idx(remove_pid), + _NXT_PORT_MSG_QUIT = nxt_port_handler_idx(quit), + + _NXT_PORT_MSG_REQ_HEADERS = nxt_port_handler_idx(req_headers), + _NXT_PORT_MSG_WEBSOCKET = nxt_port_handler_idx(websocket_frame), + + _NXT_PORT_MSG_DATA = nxt_port_handler_idx(data), + + _NXT_PORT_MSG_OOSM = nxt_port_handler_idx(oosm), + _NXT_PORT_MSG_SHM_ACK = nxt_port_handler_idx(shm_ack), + + NXT_PORT_MSG_MAX = sizeof(nxt_port_handlers_t) + / sizeof(nxt_port_handler_t), + + NXT_PORT_MSG_RPC_READY = _NXT_PORT_MSG_RPC_READY, + NXT_PORT_MSG_RPC_READY_LAST = nxt_msg_last(_NXT_PORT_MSG_RPC_READY), + NXT_PORT_MSG_RPC_ERROR = nxt_msg_last(_NXT_PORT_MSG_RPC_ERROR), + NXT_PORT_MSG_START_PROCESS = nxt_msg_last(_NXT_PORT_MSG_START_PROCESS), + NXT_PORT_MSG_SOCKET = nxt_msg_last(_NXT_PORT_MSG_SOCKET), + NXT_PORT_MSG_MODULES = nxt_msg_last(_NXT_PORT_MSG_MODULES), + NXT_PORT_MSG_CONF_STORE = nxt_msg_last(_NXT_PORT_MSG_CONF_STORE), + NXT_PORT_MSG_CERT_GET = nxt_msg_last(_NXT_PORT_MSG_CERT_GET), + NXT_PORT_MSG_CERT_DELETE = nxt_msg_last(_NXT_PORT_MSG_CERT_DELETE), + NXT_PORT_MSG_ACCESS_LOG = nxt_msg_last(_NXT_PORT_MSG_ACCESS_LOG), + NXT_PORT_MSG_CHANGE_FILE = nxt_msg_last(_NXT_PORT_MSG_CHANGE_FILE), + NXT_PORT_MSG_NEW_PORT = nxt_msg_last(_NXT_PORT_MSG_NEW_PORT), + NXT_PORT_MSG_MMAP = nxt_msg_last(_NXT_PORT_MSG_MMAP) + | NXT_PORT_MSG_CLOSE_FD | NXT_PORT_MSG_SYNC, + + NXT_PORT_MSG_PROCESS_CREATED = nxt_msg_last(_NXT_PORT_MSG_PROCESS_CREATED), + NXT_PORT_MSG_PROCESS_READY = nxt_msg_last(_NXT_PORT_MSG_PROCESS_READY), + NXT_PORT_MSG_QUIT = nxt_msg_last(_NXT_PORT_MSG_QUIT), + NXT_PORT_MSG_REMOVE_PID = nxt_msg_last(_NXT_PORT_MSG_REMOVE_PID), + + NXT_PORT_MSG_REQ_HEADERS = _NXT_PORT_MSG_REQ_HEADERS, + NXT_PORT_MSG_WEBSOCKET = _NXT_PORT_MSG_WEBSOCKET, + NXT_PORT_MSG_WEBSOCKET_LAST = nxt_msg_last(_NXT_PORT_MSG_WEBSOCKET), + + NXT_PORT_MSG_DATA = _NXT_PORT_MSG_DATA, + NXT_PORT_MSG_DATA_LAST = nxt_msg_last(_NXT_PORT_MSG_DATA), + + NXT_PORT_MSG_OOSM = nxt_msg_last(_NXT_PORT_MSG_OOSM), + NXT_PORT_MSG_SHM_ACK = nxt_msg_last(_NXT_PORT_MSG_SHM_ACK), } nxt_port_msg_type_t; diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index 33d3777e..f4d2125c 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -174,7 +174,7 @@ complete_buf: if (process != NULL && !nxt_queue_is_empty(&process->ports)) { port = nxt_process_port_first(process); - if (port->type == NXT_PROCESS_WORKER) { + if (port->type == NXT_PROCESS_APP) { (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_SHM_ACK, -1, 0, 0, NULL); } diff --git a/src/nxt_process.c b/src/nxt_process.c index f5959edf..e84549b3 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -13,9 +13,18 @@ #include -static void nxt_process_start(nxt_task_t *task, nxt_process_t *process); -static nxt_int_t nxt_process_worker_setup(nxt_task_t *task, - nxt_process_t *process, int parentfd); +static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process); +static nxt_int_t nxt_process_child_fixup(nxt_task_t *task, + nxt_process_t *process); +static nxt_int_t nxt_process_send_created(nxt_task_t *task, + nxt_process_t *process); +static nxt_int_t nxt_process_send_ready(nxt_task_t *task, + nxt_process_t *process); +static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data); +static void nxt_process_created_error(nxt_task_t *task, + nxt_port_recv_msg_t *msg, void *data); + /* A cached process pid. */ nxt_pid_t nxt_pid; @@ -47,62 +56,58 @@ nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX] = { static nxt_int_t -nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) +nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process) { - pid_t rpid, pid; - ssize_t n; - nxt_int_t parent_status; nxt_process_t *p; nxt_runtime_t *rt; nxt_process_init_t *init; nxt_process_type_t ptype; - pid = getpid(); - rpid = 0; - rt = task->thread->runtime; - init = process->init; + init = nxt_process_init(process); - /* Setup the worker process. */ + nxt_pid = nxt_getpid(); - n = read(parentfd, &rpid, sizeof(rpid)); - if (nxt_slow_path(n == -1 || n != sizeof(rpid))) { - nxt_alert(task, "failed to read real pid"); - return NXT_ERROR; - } - - if (nxt_slow_path(rpid == 0)) { - nxt_alert(task, "failed to get real pid from parent"); - return NXT_ERROR; - } - - nxt_pid = rpid; + process->pid = nxt_pid; /* Clean inherited cached thread tid. */ task->thread->tid = 0; - process->pid = nxt_pid; +#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWPID) + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID)) { + ssize_t pidsz; + char procpid[10]; - if (nxt_pid != pid) { - nxt_debug(task, "app \"%s\" real pid %d", init->name, nxt_pid); - nxt_debug(task, "app \"%s\" isolated pid: %d", init->name, pid); - } + nxt_debug(task, "%s isolated pid is %d", process->name, nxt_pid); - n = read(parentfd, &parent_status, sizeof(parent_status)); - if (nxt_slow_path(n == -1 || n != sizeof(parent_status))) { - nxt_alert(task, "failed to read parent status"); - return NXT_ERROR; - } + pidsz = readlink("/proc/self", procpid, sizeof(procpid)); + + if (nxt_slow_path(pidsz < 0 || pidsz >= (ssize_t) sizeof(procpid))) { + nxt_alert(task, "failed to read real pid from /proc/self"); + return NXT_ERROR; + } + + procpid[pidsz] = '\0'; + + nxt_pid = (nxt_pid_t) strtol(procpid, NULL, 10); + + nxt_assert(nxt_pid > 0 && nxt_errno != ERANGE); - if (nxt_slow_path(parent_status != NXT_OK)) { - return parent_status; + process->pid = nxt_pid; + task->thread->tid = nxt_pid; + + nxt_debug(task, "%s real pid is %d", process->name, nxt_pid); } +#endif + ptype = init->type; nxt_port_reset_next_id(); nxt_event_engine_thread_adopt(task->thread->engine); + rt = task->thread->runtime; + /* Remove not ready processes. */ nxt_runtime_process_each(rt, p) { @@ -114,7 +119,7 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) continue; } - if (!p->ready) { + if (p->state != NXT_PROCESS_STATE_READY) { nxt_debug(task, "remove not ready process %PI", p->pid); nxt_process_close_ports(task, p); @@ -127,12 +132,6 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) } nxt_runtime_process_loop; - nxt_runtime_process_add(task, process); - - nxt_process_start(task, process); - - process->ready = 1; - return NXT_OK; } @@ -140,48 +139,36 @@ nxt_process_worker_setup(nxt_task_t *task, nxt_process_t *process, int parentfd) nxt_pid_t nxt_process_create(nxt_task_t *task, nxt_process_t *process) { - int pipefd[2]; nxt_int_t ret; nxt_pid_t pid; - nxt_process_init_t *init; - - if (nxt_slow_path(pipe(pipefd) == -1)) { - nxt_alert(task, "failed to create process pipe for passing rpid"); - return -1; - } - - init = process->init; #if (NXT_HAVE_CLONE) - pid = nxt_clone(SIGCHLD | init->isolation.clone.flags); + pid = nxt_clone(SIGCHLD | process->isolation.clone.flags); if (nxt_slow_path(pid < 0)) { - nxt_alert(task, "clone() failed while creating \"%s\" %E", - init->name, nxt_errno); - goto cleanup; + nxt_alert(task, "clone() failed for %s %E", process->name, nxt_errno); + return pid; } #else pid = fork(); if (nxt_slow_path(pid < 0)) { - nxt_alert(task, "fork() failed while creating \"%s\" %E", - init->name, nxt_errno); - goto cleanup; + nxt_alert(task, "fork() failed for %s %E", process->name, nxt_errno); + return pid; } #endif if (pid == 0) { /* Child. */ - if (nxt_slow_path(close(pipefd[1]) == -1)) { - nxt_alert(task, "failed to close writer pipe fd"); - } - - ret = nxt_process_worker_setup(task, process, pipefd[0]); + ret = nxt_process_child_fixup(task, process); if (nxt_slow_path(ret != NXT_OK)) { - exit(1); + nxt_process_quit(task, 1); + return -1; } - if (nxt_slow_path(close(pipefd[0]) == -1)) { - nxt_alert(task, "failed to close writer pipe fd"); + nxt_runtime_process_add(task, process); + + if (nxt_slow_path(nxt_process_setup(task, process) != NXT_OK)) { + nxt_process_quit(task, 1); } /* @@ -193,78 +180,24 @@ nxt_process_create(nxt_task_t *task, nxt_process_t *process) /* Parent. */ - /* - * At this point, the child process is blocked reading the - * pipe fd to get its real pid (rpid). - * - * If anything goes wrong now, we need to terminate the child - * process by sending a NXT_ERROR in the pipe. - */ - #if (NXT_HAVE_CLONE) - nxt_debug(task, "clone(\"%s\"): %PI", init->name, pid); + nxt_debug(task, "clone(%s): %PI", process->name, pid); #else - nxt_debug(task, "fork(\"%s\"): %PI", init->name, pid); + nxt_debug(task, "fork(%s): %PI", process->name, pid); #endif - if (nxt_slow_path(write(pipefd[1], &pid, sizeof(pid)) == -1)) { - nxt_alert(task, "failed to write real pid"); - goto fail; - } - -#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) - if (NXT_CLONE_USER(init->isolation.clone.flags)) { - ret = nxt_clone_credential_map(task, pid, init->user_cred, - &init->isolation.clone); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - } -#endif - - ret = NXT_OK; - - if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) { - nxt_alert(task, "failed to write status"); - goto fail; - } - process->pid = pid; nxt_runtime_process_add(task, process); - goto cleanup; - -fail: - - ret = NXT_ERROR; - - if (nxt_slow_path(write(pipefd[1], &ret, sizeof(ret)) == -1)) { - nxt_alert(task, "failed to write status"); - } - - waitpid(pid, NULL, 0); - - pid = -1; - -cleanup: - - if (nxt_slow_path(close(pipefd[0]) != 0)) { - nxt_alert(task, "failed to close pipe: %E", nxt_errno); - } - - if (nxt_slow_path(close(pipefd[1]) != 0)) { - nxt_alert(task, "failed to close pipe: %E", nxt_errno); - } - return pid; } -static void -nxt_process_start(nxt_task_t *task, nxt_process_t *process) +static nxt_int_t +nxt_process_setup(nxt_task_t *task, nxt_process_t *process) { - nxt_int_t ret, cap_setid; + nxt_int_t ret; nxt_port_t *port, *main_port; nxt_thread_t *thread; nxt_runtime_t *rt; @@ -272,37 +205,17 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process) nxt_event_engine_t *engine; const nxt_event_interface_t *interface; - init = process->init; + init = nxt_process_init(process); - nxt_log(task, NXT_LOG_INFO, "%s started", init->name); + nxt_debug(task, "%s setup", process->name); - nxt_process_title(task, "unit: %s", init->name); + nxt_process_title(task, "unit: %s", process->name); thread = task->thread; rt = thread->runtime; nxt_random_init(&thread->random); - cap_setid = rt->capabilities.setid; - -#if (NXT_HAVE_CLONE_NEWUSER) - if (!cap_setid && NXT_CLONE_USER(init->isolation.clone.flags)) { - cap_setid = 1; - } -#endif - - if (cap_setid) { - ret = nxt_credential_setgids(task, init->user_cred); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - - ret = nxt_credential_setuid(task, init->user_cred); - if (nxt_slow_path(ret != NXT_OK)) { - goto fail; - } - } - rt->type = init->type; engine = thread->engine; @@ -312,17 +225,17 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process) interface = nxt_service_get(rt->services, "engine", rt->engine); if (nxt_slow_path(interface == NULL)) { - goto fail; + return NXT_ERROR; } if (nxt_event_engine_change(engine, interface, rt->batch) != NXT_OK) { - goto fail; + return NXT_ERROR; } ret = nxt_runtime_thread_pool_create(thread, rt, rt->auxiliary_threads, 60000 * 1000000LL); if (nxt_slow_path(ret != NXT_OK)) { - goto fail; + return NXT_ERROR; } main_port = rt->port_by_type[NXT_PROCESS_MAIN]; @@ -334,28 +247,282 @@ nxt_process_start(nxt_task_t *task, nxt_process_t *process) nxt_port_write_close(port); - ret = init->start(task, init->data); + nxt_port_enable(task, port, init->port_handlers); + + ret = init->setup(task, process); if (nxt_slow_path(ret != NXT_OK)) { - goto fail; + return NXT_ERROR; } - nxt_port_enable(task, port, init->port_handlers); + switch (process->state) { - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY, - -1, init->stream, 0, NULL); + case NXT_PROCESS_STATE_CREATED: + ret = nxt_process_send_created(task, process); + break; + + case NXT_PROCESS_STATE_READY: + ret = nxt_process_send_ready(task, process); + + if (nxt_slow_path(ret != NXT_OK)) { + break; + } + + ret = init->start(task, &process->data); + break; + + default: + nxt_assert(0); + } if (nxt_slow_path(ret != NXT_OK)) { - nxt_log(task, NXT_LOG_ERR, "failed to send READY message to main"); + nxt_alert(task, "%s failed to start", process->name); + } + + return ret; +} + +static nxt_int_t +nxt_process_send_created(nxt_task_t *task, nxt_process_t *process) +{ + uint32_t stream; + nxt_int_t ret; + nxt_port_t *my_port, *main_port; + nxt_runtime_t *rt; + + nxt_assert(process->state == NXT_PROCESS_STATE_CREATED); + + rt = task->thread->runtime; + + my_port = nxt_process_port_first(process); + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + + nxt_assert(my_port != NULL && main_port != NULL); + + stream = nxt_port_rpc_register_handler(task, my_port, + nxt_process_created_ok, + nxt_process_created_error, + main_port->pid, process); + + if (nxt_slow_path(stream == 0)) { + return NXT_ERROR; + } + + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_CREATED, + -1, stream, my_port->id, NULL); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "%s failed to send CREATED message", process->name); + nxt_port_rpc_cancel(task, my_port, stream); + return NXT_ERROR; + } + + nxt_debug(task, "%s created", process->name); + + return NXT_OK; +} + + +static void +nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) +{ + nxt_int_t ret; + nxt_process_t *process; + nxt_process_init_t *init; + + process = data; + init = nxt_process_init(process); + + ret = nxt_process_apply_creds(task, process); + if (nxt_slow_path(ret != NXT_OK)) { goto fail; } - return; + nxt_log(task, NXT_LOG_INFO, "%s started", process->name); + + ret = init->start(task, &process->data); fail: - exit(1); + nxt_process_quit(task, ret == NXT_OK ? 0 : 1); +} + + +static void +nxt_process_created_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, + void *data) +{ + nxt_process_t *process; + nxt_process_init_t *init; + + process = data; + init = nxt_process_init(process); + + nxt_alert(task, "%s failed to start", init->name); + + nxt_process_quit(task, 1); +} + + +nxt_int_t +nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process) +{ + nxt_int_t ret; + + ret = nxt_process_apply_creds(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + process->state = NXT_PROCESS_STATE_READY; + + return NXT_OK; +} + + +#if (NXT_HAVE_CLONE_NEWUSER) + +nxt_int_t +nxt_process_vldt_isolation_creds(nxt_task_t *task, nxt_process_t *process) +{ + nxt_int_t ret; + nxt_clone_t *clone; + nxt_credential_t *creds; + + clone = &process->isolation.clone; + creds = process->user_cred; + + if (clone->uidmap.size == 0 && clone->gidmap.size == 0) { + return NXT_OK; + } + + if (!nxt_is_clone_flag_set(clone->flags, NEWUSER)) { + if (nxt_slow_path(clone->uidmap.size > 0)) { + nxt_log(task, NXT_LOG_ERR, "\"uidmap\" is set but " + "\"isolation.namespaces.credential\" is false or unset"); + + return NXT_ERROR; + } + + if (nxt_slow_path(clone->gidmap.size > 0)) { + nxt_log(task, NXT_LOG_ERR, "\"gidmap\" is set but " + "\"isolation.namespaces.credential\" is false or unset"); + + return NXT_ERROR; + } + + return NXT_OK; + } + + ret = nxt_clone_vldt_credential_uidmap(task, &clone->uidmap, creds); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + return nxt_clone_vldt_credential_gidmap(task, &clone->gidmap, creds); +} + +#endif + + +nxt_int_t +nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process, nxt_str_t *user, + nxt_str_t *group) +{ + char *str; + + process->user_cred = nxt_mp_zalloc(process->mem_pool, + sizeof(nxt_credential_t)); + + if (nxt_slow_path(process->user_cred == NULL)) { + return NXT_ERROR; + } + + str = nxt_mp_zalloc(process->mem_pool, user->length + 1); + if (nxt_slow_path(str == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(str, user->start, user->length); + str[user->length] = '\0'; + + process->user_cred->user = str; + + if (group->start != NULL) { + str = nxt_mp_zalloc(process->mem_pool, group->length + 1); + if (nxt_slow_path(str == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(str, group->start, group->length); + str[group->length] = '\0'; + + } else { + str = NULL; + } + + return nxt_credential_get(task, process->mem_pool, process->user_cred, str); +} + + +nxt_int_t +nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process) +{ + nxt_int_t ret, cap_setid; + nxt_runtime_t *rt; + + rt = task->thread->runtime; + + cap_setid = rt->capabilities.setid; + +#if (NXT_HAVE_CLONE && NXT_HAVE_CLONE_NEWUSER) + if (!cap_setid + && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { + cap_setid = 1; + } +#endif + + if (cap_setid) { + ret = nxt_credential_setgids(task, process->user_cred); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + + ret = nxt_credential_setuid(task, process->user_cred); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process) +{ + nxt_int_t ret; + nxt_port_t *main_port; + nxt_runtime_t *rt; + + rt = task->thread->runtime; + + main_port = rt->port_by_type[NXT_PROCESS_MAIN]; + + nxt_assert(main_port != NULL); + + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_PROCESS_READY, + -1, process->stream, 0, NULL); + + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "%s failed to send READY message", process->name); + return NXT_ERROR; + } + + nxt_debug(task, "%s sent ready", process->name); + + return NXT_OK; } @@ -625,3 +792,50 @@ nxt_process_connected_port_find(nxt_process_t *process, nxt_port_t *port) return res; } + + +void +nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status) +{ + nxt_uint_t n; + nxt_queue_t *listen; + nxt_runtime_t *rt; + nxt_queue_link_t *link, *next; + nxt_listen_event_t *lev; + nxt_listen_socket_t *ls; + + rt = task->thread->runtime; + + nxt_debug(task, "close listen connections"); + + listen = &task->thread->engine->listen_connections; + + for (link = nxt_queue_first(listen); + link != nxt_queue_tail(listen); + link = next) + { + next = nxt_queue_next(link); + lev = nxt_queue_link_data(link, nxt_listen_event_t, link); + nxt_queue_remove(link); + + nxt_fd_event_close(task->thread->engine, &lev->socket); + } + + if (rt->listen_sockets != NULL) { + + ls = rt->listen_sockets->elts; + n = rt->listen_sockets->nelts; + + while (n != 0) { + nxt_socket_close(task, ls->socket); + ls->socket = -1; + + ls++; + n--; + } + + rt->listen_sockets->nelts = 0; + } + + nxt_runtime_quit(task, exit_status); +} diff --git a/src/nxt_process.h b/src/nxt_process.h index 3f7155c8..45bab25e 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -8,68 +8,123 @@ #define _NXT_PROCESS_H_INCLUDED_ #if (NXT_HAVE_CLONE) +#include #include #endif -typedef pid_t nxt_pid_t; +#if (NXT_HAVE_CLONE) +/* + * Old glibc wrapper for getpid(2) returns a cached pid invalidated only by + * fork(2) calls. As we use clone(2) for container, it returns the wrong pid. + */ +#define nxt_getpid() \ + syscall(__NR_getpid) +#else +#define nxt_getpid() \ + getpid() +#endif +typedef pid_t nxt_pid_t; -typedef struct nxt_process_init_s nxt_process_init_t; -typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, void *data); -typedef nxt_int_t (*nxt_process_restart_t)(nxt_task_t *task, nxt_runtime_t *rt, - nxt_process_init_t *init); -struct nxt_process_init_s { - nxt_mp_t *mem_pool; - nxt_process_start_t start; - const char *name; - nxt_credential_t *user_cred; +typedef struct nxt_common_app_conf_s nxt_common_app_conf_t; - const nxt_port_handlers_t *port_handlers; - const nxt_sig_event_t *signals; - nxt_process_type_t type; +typedef struct { + nxt_runtime_t *rt; +} nxt_discovery_init_t; - void *data; - uint32_t stream; - union { -#if (NXT_HAVE_CLONE) - nxt_clone_t clone; +typedef struct { + nxt_str_t conf; +#if (NXT_TLS) + nxt_array_t *certs; #endif - } isolation; -}; +} nxt_controller_init_t; + + +typedef union { + void *discovery; + nxt_controller_init_t controller; + void *router; + nxt_common_app_conf_t *app; +} nxt_process_data_t; + + +typedef enum { + NXT_PROCESS_STATE_CREATING = 0, + NXT_PROCESS_STATE_CREATED, + NXT_PROCESS_STATE_READY, +} nxt_process_state_t; typedef struct nxt_port_mmap_s nxt_port_mmap_t; -typedef struct nxt_port_mmaps_s nxt_port_mmaps_t; -struct nxt_port_mmaps_s { + +typedef struct { nxt_thread_mutex_t mutex; uint32_t size; uint32_t cap; nxt_port_mmap_t *elts; -}; +} nxt_port_mmaps_t; typedef struct { - nxt_pid_t pid; - nxt_queue_t ports; /* of nxt_port_t */ - nxt_bool_t ready; - nxt_bool_t registered; - nxt_int_t use_count; + nxt_pid_t pid; + const char *name; + nxt_queue_t ports; /* of nxt_port_t */ + nxt_process_state_t state; + nxt_bool_t registered; + nxt_int_t use_count; + + nxt_port_mmaps_t incoming; + nxt_port_mmaps_t outgoing; + + nxt_thread_mutex_t cp_mutex; + nxt_lvlhsh_t connected_ports; /* of nxt_port_t */ - nxt_process_init_t *init; + uint32_t stream; - nxt_port_mmaps_t incoming; - nxt_port_mmaps_t outgoing; + nxt_mp_t *mem_pool; + nxt_credential_t *user_cred; - nxt_thread_mutex_t cp_mutex; - nxt_lvlhsh_t connected_ports; /* of nxt_port_t */ + nxt_process_data_t data; + + union { +#if (NXT_HAVE_CLONE) + nxt_clone_t clone; +#endif + } isolation; } nxt_process_t; +typedef nxt_int_t (*nxt_process_prefork_t)(nxt_task_t *task, + nxt_process_t *process, nxt_mp_t *mp); +typedef nxt_int_t (*nxt_process_postfork_t)(nxt_task_t *task, + nxt_process_t *process, nxt_mp_t *mp); +typedef nxt_int_t (*nxt_process_setup_t)(nxt_task_t *task, + nxt_process_t *process); +typedef nxt_int_t (*nxt_process_start_t)(nxt_task_t *task, + nxt_process_data_t *data); + + +typedef struct { + const char *name; + nxt_process_type_t type; + + nxt_process_prefork_t prefork; + + nxt_process_setup_t setup; + nxt_process_start_t start; + + uint8_t restart; /* 1-bit */ + + const nxt_port_handlers_t *port_handlers; + const nxt_sig_event_t *signals; +} nxt_process_init_t; + + extern nxt_bool_t nxt_proc_conn_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX]; extern nxt_bool_t nxt_proc_remove_notify_matrix[NXT_PROCESS_MAX][NXT_PROCESS_MAX]; @@ -84,6 +139,9 @@ NXT_EXPORT void nxt_nanosleep(nxt_nsec_t ns); NXT_EXPORT void nxt_process_arguments(nxt_task_t *task, char **orig_argv, char ***orig_envp); +#define nxt_process_init(process) \ + (nxt_pointer_to(process, sizeof(nxt_process_t))) + #define nxt_process_port_remove(port) \ nxt_queue_remove(&port->link) @@ -113,11 +171,18 @@ void nxt_process_connected_port_remove(nxt_process_t *process, nxt_port_t *nxt_process_connected_port_find(nxt_process_t *process, nxt_port_t *port); -void nxt_worker_process_quit_handler(nxt_task_t *task, - nxt_port_recv_msg_t *msg); +void nxt_process_quit(nxt_task_t *task, nxt_uint_t exit_status); +void nxt_signal_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg); -void nxt_init_destroy(nxt_runtime_t *rt, nxt_process_init_t *init); +nxt_int_t nxt_process_core_setup(nxt_task_t *task, nxt_process_t *process); +nxt_int_t nxt_process_creds_set(nxt_task_t *task, nxt_process_t *process, + nxt_str_t *user, nxt_str_t *group); +nxt_int_t nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process); +#if (NXT_HAVE_CLONE_NEWUSER) +nxt_int_t nxt_process_vldt_isolation_creds(nxt_task_t *task, + nxt_process_t *process); +#endif #if (NXT_HAVE_SETPROCTITLE) diff --git a/src/nxt_process_type.h b/src/nxt_process_type.h index 5ff06d63..14deda19 100644 --- a/src/nxt_process_type.h +++ b/src/nxt_process_type.h @@ -13,7 +13,7 @@ typedef enum { NXT_PROCESS_DISCOVERY, NXT_PROCESS_CONTROLLER, NXT_PROCESS_ROUTER, - NXT_PROCESS_WORKER, + NXT_PROCESS_APP, NXT_PROCESS_MAX, } nxt_process_type_t; diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 14211f3f..089d15c0 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -65,7 +65,8 @@ typedef struct { PyObject_HEAD } nxt_py_error_t; -static nxt_int_t nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_python_start(nxt_task_t *task, + nxt_process_data_t *data); static nxt_int_t nxt_python_init_strings(void); static void nxt_python_request_handler(nxt_unit_request_info_t *req); static void nxt_python_atexit(void); @@ -116,7 +117,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_string("python"), PY_VERSION, NULL, - nxt_python_init, + nxt_python_start, }; @@ -211,7 +212,7 @@ static nxt_python_string_t nxt_python_strings[] = { static nxt_int_t -nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_python_start(nxt_task_t *task, nxt_process_data_t *data) { int rc; char *nxt_py_module; @@ -219,6 +220,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) PyObject *obj, *pypath, *module; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t python_init; + nxt_common_app_conf_t *app_conf; nxt_python_app_conf_t *c; #if PY_MAJOR_VERSION == 3 char *path; @@ -229,7 +231,8 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) static const char bin_python[] = "/bin/python"; #endif - c = &conf->u.python; + app_conf = data->app; + c = &app_conf->u.python; if (c->module.length == 0) { nxt_alert(task, "python module is empty"); @@ -410,7 +413,7 @@ nxt_python_init(nxt_task_t *task, nxt_common_app_conf_t *conf) nxt_unit_default_init(task, &python_init); python_init.callbacks.request_handler = nxt_python_request_handler; - python_init.shm_limit = conf->shm_limit; + python_init.shm_limit = data->app->shm_limit; unit_ctx = nxt_unit_init(&python_init); if (nxt_slow_path(unit_ctx == NULL)) { diff --git a/src/nxt_router.c b/src/nxt_router.c index b4cba08b..788199c7 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -75,6 +75,9 @@ struct nxt_port_select_state_s { typedef struct nxt_port_select_state_s nxt_port_select_state_t; +static nxt_int_t nxt_router_prefork(nxt_task_t *task, nxt_process_t *process, + nxt_mp_t *mp); +static nxt_int_t nxt_router_start(nxt_task_t *task, nxt_process_data_t *data); static void nxt_router_greet_controller(nxt_task_t *task, nxt_port_t *controller_port); @@ -268,8 +271,8 @@ static const nxt_str_t *nxt_app_msg_prefix[] = { }; -nxt_port_handlers_t nxt_router_process_port_handlers = { - .quit = nxt_worker_process_quit_handler, +static const nxt_port_handlers_t nxt_router_process_port_handlers = { + .quit = nxt_signal_quit_handler, .new_port = nxt_router_new_port_handler, .change_file = nxt_port_change_log_file_handler, .mmap = nxt_port_mmap_handler, @@ -282,8 +285,29 @@ nxt_port_handlers_t nxt_router_process_port_handlers = { }; -nxt_int_t -nxt_router_start(nxt_task_t *task, void *data) +const nxt_process_init_t nxt_router_process = { + .name = "router", + .type = NXT_PROCESS_ROUTER, + .prefork = nxt_router_prefork, + .restart = 1, + .setup = nxt_process_core_setup, + .start = nxt_router_start, + .port_handlers = &nxt_router_process_port_handlers, + .signals = nxt_process_signals, +}; + + +static nxt_int_t +nxt_router_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) +{ + nxt_runtime_stop_app_processes(task, task->thread->runtime); + + return NXT_OK; +} + + +static nxt_int_t +nxt_router_start(nxt_task_t *task, nxt_process_data_t *data) { nxt_int_t ret; nxt_port_t *controller_port; @@ -292,6 +316,8 @@ nxt_router_start(nxt_task_t *task, void *data) rt = task->thread->runtime; + nxt_log(task, NXT_LOG_INFO, "router started"); + #if (NXT_TLS) rt->tls = nxt_service_get(rt->services, "SSL/TLS", "OpenSSL"); if (nxt_slow_path(rt->tls == NULL)) { @@ -382,8 +408,8 @@ nxt_router_start_app_process_handler(nxt_task_t *task, nxt_port_t *port, goto failed; } - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_WORKER, -1, - stream, port->id, b); + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, + -1, stream, port->id, b); if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, port, stream); @@ -862,7 +888,7 @@ nxt_router_new_port_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) } if (msg->u.new_port == NULL - || msg->u.new_port->type != NXT_PROCESS_WORKER) + || msg->u.new_port->type != NXT_PROCESS_APP) { msg->port_msg.type = _NXT_PORT_MSG_RPC_ERROR; } @@ -2400,8 +2426,8 @@ nxt_router_app_rpc_create(nxt_task_t *task, goto fail; } - ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_WORKER, -1, - stream, router_port->id, b); + ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_START_PROCESS, + -1, stream, router_port->id, b); if (nxt_slow_path(ret != NXT_OK)) { nxt_port_rpc_cancel(task, router_port, stream); diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index ea01f06f..d7e35dec 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -21,6 +21,7 @@ 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); @@ -438,7 +439,7 @@ nxt_runtime_quit(nxt_task_t *task, nxt_uint_t status) } if (rt->type == NXT_PROCESS_MAIN) { - nxt_main_stop_all_processes(task, rt); + nxt_runtime_stop_all_processes(task, rt); done = 0; } } @@ -478,6 +479,50 @@ nxt_runtime_close_idle_connections(nxt_event_engine_t *engine) } +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) { + + 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) { + + (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) { @@ -525,6 +570,10 @@ nxt_runtime_exit(nxt_task_t *task, void *obj, void *data) } nxt_runtime_process_loop; + 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); status = rt->status; @@ -1306,7 +1355,9 @@ nxt_runtime_process_new(nxt_runtime_t *rt) /* TODO: memory failures. */ - process = nxt_mp_zalloc(rt->mem_pool, sizeof(nxt_process_t)); + process = nxt_mp_zalloc(rt->mem_pool, + sizeof(nxt_process_t) + sizeof(nxt_process_init_t)); + if (nxt_slow_path(process == NULL)) { return NULL; } @@ -1347,8 +1398,9 @@ nxt_runtime_process_release(nxt_runtime_t *rt, nxt_process_t *process) nxt_thread_mutex_destroy(&process->outgoing.mutex); nxt_thread_mutex_destroy(&process->cp_mutex); - if (process->init != NULL) { - nxt_mp_destroy(process->init->mem_pool); + /* 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); diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h index a364c38c..d29b6b4d 100644 --- a/src/nxt_runtime.h +++ b/src/nxt_runtime.h @@ -110,6 +110,7 @@ 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); void nxt_runtime_port_remove(nxt_task_t *task, nxt_port_t *port); +void nxt_runtime_stop_app_processes(nxt_task_t *task, nxt_runtime_t *rt); NXT_EXPORT nxt_port_t *nxt_runtime_port_find(nxt_runtime_t *rt, nxt_pid_t pid, nxt_port_id_t port_id); diff --git a/src/nxt_signal_handlers.c b/src/nxt_signal_handlers.c new file mode 100644 index 00000000..69ae2bc4 --- /dev/null +++ b/src/nxt_signal_handlers.c @@ -0,0 +1,67 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) NGINX, Inc. + */ + +#include +#include +#include +#include +#include + + +static void nxt_signal_handler(nxt_task_t *task, void *obj, void *data); +static void nxt_signal_sigterm_handler(nxt_task_t *task, void *obj, void *data); +static void nxt_signal_sigquit_handler(nxt_task_t *task, void *obj, void *data); + + +const nxt_sig_event_t nxt_process_signals[] = { + nxt_event_signal(SIGHUP, nxt_signal_handler), + nxt_event_signal(SIGINT, nxt_signal_sigterm_handler), + nxt_event_signal(SIGQUIT, nxt_signal_sigquit_handler), + nxt_event_signal(SIGTERM, nxt_signal_sigterm_handler), + nxt_event_signal(SIGCHLD, nxt_signal_handler), + nxt_event_signal(SIGUSR1, nxt_signal_handler), + nxt_event_signal(SIGUSR2, nxt_signal_handler), + nxt_event_signal_end, +}; + + +static void +nxt_signal_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_trace(task, "signal signo:%d (%s) recevied, ignored", + (int) (uintptr_t) obj, data); +} + + +void +nxt_signal_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) +{ + nxt_process_quit(task, 0); +} + + +static void +nxt_signal_sigterm_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_debug(task, "sigterm handler signo:%d (%s)", + (int) (uintptr_t) obj, data); + + /* A fast exit. */ + + nxt_runtime_quit(task, 0); +} + + +static void +nxt_signal_sigquit_handler(nxt_task_t *task, void *obj, void *data) +{ + nxt_debug(task, "sigquit handler signo:%d (%s)", + (int) (uintptr_t) obj, data); + + /* A graceful exit. */ + + nxt_process_quit(task, 0); +} diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 67244420..9f6eab95 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -4248,7 +4248,7 @@ nxt_unit_send_port(nxt_unit_ctx_t *ctx, nxt_unit_port_id_t *dst, m.new_port.id = new_port->id; m.new_port.pid = new_port->pid; - m.new_port.type = NXT_PROCESS_WORKER; + m.new_port.type = NXT_PROCESS_APP; m.new_port.max_size = 16 * 1024; m.new_port.max_share = 64 * 1024; diff --git a/src/nxt_worker_process.c b/src/nxt_worker_process.c deleted file mode 100644 index 754e2ea8..00000000 --- a/src/nxt_worker_process.c +++ /dev/null @@ -1,118 +0,0 @@ - -/* - * Copyright (C) Igor Sysoev - * Copyright (C) NGINX, Inc. - */ - -#include -#include -#include -#include -#include - - -static void nxt_worker_process_quit(nxt_task_t *task); -static void nxt_worker_process_signal_handler(nxt_task_t *task, void *obj, - void *data); -static void nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj, - void *data); -static void nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj, - void *data); - - -const nxt_sig_event_t nxt_worker_process_signals[] = { - nxt_event_signal(SIGHUP, nxt_worker_process_signal_handler), - nxt_event_signal(SIGINT, nxt_worker_process_sigterm_handler), - nxt_event_signal(SIGQUIT, nxt_worker_process_sigquit_handler), - nxt_event_signal(SIGTERM, nxt_worker_process_sigterm_handler), - nxt_event_signal(SIGCHLD, nxt_worker_process_signal_handler), - nxt_event_signal(SIGUSR1, nxt_worker_process_signal_handler), - nxt_event_signal(SIGUSR2, nxt_worker_process_signal_handler), - nxt_event_signal_end, -}; - - -static void -nxt_worker_process_quit(nxt_task_t *task) -{ - nxt_uint_t n; - nxt_queue_t *listen; - nxt_runtime_t *rt; - nxt_queue_link_t *link, *next; - nxt_listen_event_t *lev; - nxt_listen_socket_t *ls; - - rt = task->thread->runtime; - - nxt_debug(task, "close listen connections"); - - listen = &task->thread->engine->listen_connections; - - for (link = nxt_queue_first(listen); - link != nxt_queue_tail(listen); - link = next) - { - next = nxt_queue_next(link); - lev = nxt_queue_link_data(link, nxt_listen_event_t, link); - nxt_queue_remove(link); - - nxt_fd_event_close(task->thread->engine, &lev->socket); - } - - if (rt->listen_sockets != NULL) { - - ls = rt->listen_sockets->elts; - n = rt->listen_sockets->nelts; - - while (n != 0) { - nxt_socket_close(task, ls->socket); - ls->socket = -1; - - ls++; - n--; - } - - rt->listen_sockets->nelts = 0; - } - - nxt_runtime_quit(task, 0); -} - - -static void -nxt_worker_process_signal_handler(nxt_task_t *task, void *obj, void *data) -{ - nxt_trace(task, "signal signo:%d (%s) recevied, ignored", - (int) (uintptr_t) obj, data); -} - - -void -nxt_worker_process_quit_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) -{ - nxt_worker_process_quit(task); -} - - -static void -nxt_worker_process_sigterm_handler(nxt_task_t *task, void *obj, void *data) -{ - nxt_debug(task, "sigterm handler signo:%d (%s)", - (int) (uintptr_t) obj, data); - - /* A fast exit. */ - - nxt_runtime_quit(task, 0); -} - - -static void -nxt_worker_process_sigquit_handler(nxt_task_t *task, void *obj, void *data) -{ - nxt_debug(task, "sigquit handler signo:%d (%s)", - (int) (uintptr_t) obj, data); - - /* A graceful exit. */ - - nxt_worker_process_quit(task); -} diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 548e6daa..5e9200dc 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -95,8 +95,8 @@ static int nxt_perl_psgi_result_array(PerlInterpreter *my_perl, static void nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, nxt_unit_request_info_t *req); -static nxt_int_t nxt_perl_psgi_init(nxt_task_t *task, - nxt_common_app_conf_t *conf); +static nxt_int_t nxt_perl_psgi_start(nxt_task_t *task, + nxt_process_data_t *conf); static void nxt_perl_psgi_request_handler(nxt_unit_request_info_t *req); static void nxt_perl_psgi_atexit(void); @@ -119,7 +119,7 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_string("perl"), PERL_VERSION_STRING, NULL, - nxt_perl_psgi_init, + nxt_perl_psgi_start, }; @@ -1134,14 +1134,17 @@ nxt_perl_psgi_result_cb(PerlInterpreter *my_perl, SV *result, static nxt_int_t -nxt_perl_psgi_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_perl_psgi_start(nxt_task_t *task, nxt_process_data_t *data) { int rc; nxt_unit_ctx_t *unit_ctx; nxt_unit_init_t perl_init; PerlInterpreter *my_perl; + nxt_common_app_conf_t *conf; nxt_perl_psgi_module_t module; + conf = data->app; + my_perl = nxt_perl_psgi_interpreter_init(task, conf->u.perl.script, &module.app); diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 417e2d8d..40f72f51 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -28,7 +28,8 @@ typedef struct { } nxt_ruby_rack_init_t; -static nxt_int_t nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf); +static nxt_int_t nxt_ruby_start(nxt_task_t *task, + nxt_process_data_t *data); static VALUE nxt_ruby_init_basic(VALUE arg); static nxt_int_t nxt_ruby_init_io(nxt_task_t *task); static VALUE nxt_ruby_rack_init(nxt_ruby_rack_init_t *rack_init); @@ -78,21 +79,24 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_string("ruby"), ruby_version, NULL, - nxt_ruby_init, + nxt_ruby_start, }; static nxt_int_t -nxt_ruby_init(nxt_task_t *task, nxt_common_app_conf_t *conf) +nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) { - int state, rc; - VALUE res; - nxt_unit_ctx_t *unit_ctx; - nxt_unit_init_t ruby_unit_init; - nxt_ruby_rack_init_t rack_init; + int state, rc; + VALUE res; + nxt_unit_ctx_t *unit_ctx; + nxt_unit_init_t ruby_unit_init; + nxt_ruby_rack_init_t rack_init; + nxt_common_app_conf_t *conf; static char *argv[2] = { (char *) "NGINX_Unit", (char *) "-e0" }; + conf = data->app; + RUBY_INIT_STACK ruby_init(); ruby_options(2, argv); -- cgit From e2b53e16c60ba1e3bbbe59172c184e97f889326b Mon Sep 17 00:00:00 2001 From: Tiago Natel de Moura Date: Thu, 28 May 2020 14:57:41 +0100 Subject: Added "rootfs" feature. --- src/nxt_application.c | 481 ++++++++++++++++++++++++++++++++++++++++++---- src/nxt_application.h | 4 + src/nxt_array.c | 39 ++++ src/nxt_array.h | 3 +- src/nxt_capability.c | 5 + src/nxt_capability.h | 3 +- src/nxt_clone.h | 3 + src/nxt_conf_validation.c | 18 ++ src/nxt_external.c | 2 + src/nxt_fs.c | 163 ++++++++++++++++ src/nxt_fs.h | 36 ++++ src/nxt_java.c | 72 ++++++- src/nxt_main.h | 1 + src/nxt_main_process.c | 89 ++++++++- src/nxt_php_sapi.c | 2 + src/nxt_process.c | 358 ++++++++++++++++++++++++++++++++++ src/nxt_process.h | 53 +++-- src/nxt_python_wsgi.c | 9 + src/nxt_runtime.c | 1 + src/nxt_unix.h | 3 + src/perl/nxt_perl_psgi.c | 6 + src/ruby/nxt_ruby.c | 5 + 22 files changed, 1285 insertions(+), 71 deletions(-) create mode 100644 src/nxt_fs.c create mode 100644 src/nxt_fs.h (limited to 'src') diff --git a/src/nxt_application.c b/src/nxt_application.c index 6de82257..566bf256 100644 --- a/src/nxt_application.c +++ b/src/nxt_application.c @@ -17,11 +17,16 @@ #include +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) +#include +#endif + typedef struct { nxt_app_type_t type; nxt_str_t version; nxt_str_t file; + nxt_array_t *mounts; } nxt_module_t; @@ -40,22 +45,40 @@ static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp); static nxt_int_t nxt_app_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_app_set_environment(nxt_conf_value_t *environment); -static nxt_int_t nxt_app_isolation(nxt_task_t *task, +static u_char *nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src); + +#if (NXT_HAVE_ISOLATION_ROOTFS) +static nxt_int_t nxt_app_prepare_rootfs(nxt_task_t *task, + nxt_process_t *process); +static nxt_int_t nxt_app_prepare_lang_mounts(nxt_task_t *task, + nxt_process_t *process, nxt_array_t *syspaths); +static nxt_int_t nxt_app_set_isolation_rootfs(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +#endif + +static nxt_int_t nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); #if (NXT_HAVE_CLONE) +static nxt_int_t nxt_app_set_isolation_namespaces(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); static nxt_int_t nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, nxt_clone_t *clone); #endif #if (NXT_HAVE_CLONE_NEWUSER) -static nxt_int_t nxt_app_isolation_creds(nxt_task_t *task, +static nxt_int_t nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process); static nxt_int_t nxt_app_isolation_credential_map(nxt_task_t *task, nxt_mp_t *mem_pool, nxt_conf_value_t *map_array, nxt_clone_credential_map_t *map); #endif +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) +static nxt_int_t nxt_app_set_isolation_new_privs(nxt_task_t *task, + nxt_conf_value_t *isolation, nxt_process_t *process); +#endif + nxt_str_t nxt_server = nxt_string(NXT_SERVER); @@ -154,16 +177,17 @@ nxt_discovery_start(nxt_task_t *task, nxt_process_data_t *data) static nxt_buf_t * nxt_discovery_modules(nxt_task_t *task, const char *path) { - char *name; - u_char *p, *end; - size_t size; - glob_t glb; - nxt_mp_t *mp; - nxt_buf_t *b; - nxt_int_t ret; - nxt_uint_t i, n; - nxt_array_t *modules; - nxt_module_t *module; + char *name; + u_char *p, *end; + size_t size; + glob_t glb; + nxt_mp_t *mp; + nxt_buf_t *b; + nxt_int_t ret; + nxt_uint_t i, n, j; + nxt_array_t *modules, *mounts; + nxt_module_t *module; + nxt_fs_mount_t *mnt; b = NULL; @@ -206,11 +230,26 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) size += nxt_length("{\"type\": ,"); size += nxt_length(" \"version\": \"\","); - size += nxt_length(" \"file\": \"\"},"); + size += nxt_length(" \"file\": \"\","); + size += nxt_length(" \"mounts\": []},"); size += NXT_INT_T_LEN + module[i].version.length + module[i].file.length; + + mounts = module[i].mounts; + + size += mounts->nelts * nxt_length("{\"src\": \"\", \"dst\": \"\", " + "\"fstype\": \"\", \"flags\": , " + "\"data\": \"\"},"); + + mnt = mounts->elts; + + for (j = 0; j < mounts->nelts; j++) { + size += nxt_strlen(mnt[j].src) + nxt_strlen(mnt[j].dst) + + nxt_strlen(mnt[j].fstype) + NXT_INT_T_LEN + + (mnt[j].data == NULL ? 0 : nxt_strlen(mnt[j].data)); + } } b = nxt_buf_mem_alloc(mp, size, 0); @@ -225,12 +264,34 @@ nxt_discovery_modules(nxt_task_t *task, const char *path) *p++ = '['; for (i = 0; i < n; i++) { - p = nxt_sprintf(p, end, - "{\"type\": %d, \"version\": \"%V\", \"file\": \"%V\"},", - module[i].type, &module[i].version, &module[i].file); + mounts = module[i].mounts; + + p = nxt_sprintf(p, end, "{\"type\": %d, \"version\": \"%V\", " + "\"file\": \"%V\", \"mounts\": [", + module[i].type, &module[i].version, &module[i].file); + + mnt = mounts->elts; + for (j = 0; j < mounts->nelts; j++) { + p = nxt_sprintf(p, end, + "{\"src\": \"%s\", \"dst\": \"%s\", " + "\"fstype\": \"%s\", \"flags\": %d, " + "\"data\": \"%s\"},", + mnt[j].src, mnt[j].dst, mnt[j].fstype, mnt[j].flags, + mnt[j].data == NULL ? (u_char *) "" : mnt[j].data); + } + + *p++ = ']'; + *p++ = '}'; + *p++ = ','; } *p++ = ']'; + + if (nxt_slow_path(p >= end)) { + nxt_alert(task, "discovery write past the buffer"); + goto fail; + } + b->mem.free = p; fail: @@ -245,13 +306,16 @@ static nxt_int_t nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, const char *name) { - void *dl; - nxt_str_t version; - nxt_int_t ret; - nxt_uint_t i, n; - nxt_module_t *module; - nxt_app_type_t type; - nxt_app_module_t *app; + void *dl; + nxt_str_t version; + nxt_int_t ret; + nxt_uint_t i, j, n; + nxt_array_t *mounts; + nxt_module_t *module; + nxt_app_type_t type; + nxt_fs_mount_t *to; + nxt_app_module_t *app; + const nxt_fs_mount_t *from; /* * Only memory allocation failure should return NXT_ERROR. @@ -328,6 +392,47 @@ nxt_discovery_module(nxt_task_t *task, nxt_mp_t *mp, nxt_array_t *modules, nxt_memcpy(module->file.start, name, module->file.length); + module->mounts = nxt_array_create(mp, app->nmounts, + sizeof(nxt_fs_mount_t)); + + if (nxt_slow_path(module->mounts == NULL)) { + goto fail; + } + + mounts = module->mounts; + + for (j = 0; j < app->nmounts; j++) { + from = &app->mounts[j]; + to = nxt_array_zero_add(mounts); + if (nxt_slow_path(to == NULL)) { + goto fail; + } + + to->src = nxt_cstr_dup(mp, to->src, from->src); + if (nxt_slow_path(to->src == NULL)) { + goto fail; + } + + to->dst = nxt_cstr_dup(mp, to->dst, from->dst); + if (nxt_slow_path(to->dst == NULL)) { + goto fail; + } + + to->fstype = nxt_cstr_dup(mp, to->fstype, from->fstype); + if (nxt_slow_path(to->fstype == NULL)) { + goto fail; + } + + if (from->data != NULL) { + to->data = nxt_cstr_dup(mp, to->data, from->data); + if (nxt_slow_path(to->data == NULL)) { + goto fail; + } + } + + to->flags = from->flags; + } + } else { nxt_alert(task, "dlsym(\"%s\"), failed: \"%s\"", name, dlerror()); } @@ -369,17 +474,23 @@ nxt_discovery_quit(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data) static nxt_int_t nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) { - nxt_int_t cap_setid; + nxt_int_t cap_setid, cap_chroot; nxt_int_t ret; nxt_runtime_t *rt; nxt_common_app_conf_t *app_conf; + nxt_app_lang_module_t *lang; rt = task->thread->runtime; app_conf = process->data.app; cap_setid = rt->capabilities.setid; + cap_chroot = rt->capabilities.chroot; + + lang = nxt_app_lang_module(rt, &app_conf->type); + + nxt_assert(lang != NULL); if (app_conf->isolation != NULL) { - ret = nxt_app_isolation(task, app_conf->isolation, process); + ret = nxt_app_set_isolation(task, app_conf->isolation, process); if (nxt_slow_path(ret != NXT_OK)) { return ret; } @@ -388,6 +499,25 @@ nxt_app_prefork(nxt_task_t *task, nxt_process_t *process, nxt_mp_t *mp) #if (NXT_HAVE_CLONE_NEWUSER) if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWUSER)) { cap_setid = 1; + cap_chroot = 1; + } +#endif + +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL) { + if (!cap_chroot) { + nxt_log(task, NXT_LOG_ERR, + "The \"rootfs\" field requires privileges"); + + return NXT_ERROR; + } + + if (lang->mounts != NULL && lang->mounts->nelts > 0) { + ret = nxt_app_prepare_lang_mounts(task, process, lang->mounts); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } } #endif @@ -460,6 +590,13 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } + if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) + != NXT_OK)) + { + nxt_alert(task, "failed to set environment"); + return NXT_ERROR; + } + if (nxt_app->setup != NULL) { ret = nxt_app->setup(task, process, app_conf); @@ -468,6 +605,22 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL) { + if (process->isolation.mounts != NULL) { + ret = nxt_app_prepare_rootfs(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return ret; + } + } + + ret = nxt_process_change_root(task, process); + if (nxt_slow_path(ret != NXT_OK)) { + return NXT_ERROR; + } + } +#endif + if (app_conf->working_directory != NULL && app_conf->working_directory[0] != 0) { @@ -481,13 +634,6 @@ nxt_app_setup(nxt_task_t *task, nxt_process_t *process) } } - if (nxt_slow_path(nxt_app_set_environment(app_conf->environment) - != NXT_OK)) - { - nxt_alert(task, "failed to set environment"); - return NXT_ERROR; - } - init = nxt_process_init(process); init->start = nxt_app->start; @@ -555,10 +701,51 @@ nxt_app_set_environment(nxt_conf_value_t *environment) static nxt_int_t -nxt_app_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, +nxt_app_set_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process) { #if (NXT_HAVE_CLONE) + if (nxt_slow_path(nxt_app_set_isolation_namespaces(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_CLONE_NEWUSER) + if (nxt_slow_path(nxt_app_set_isolation_creds(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (nxt_slow_path(nxt_app_set_isolation_rootfs(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + if (nxt_slow_path(nxt_app_set_isolation_new_privs(task, isolation, process) + != NXT_OK)) + { + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#if (NXT_HAVE_CLONE) + +static nxt_int_t +nxt_app_set_isolation_namespaces(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ nxt_int_t ret; nxt_conf_value_t *obj; @@ -571,23 +758,82 @@ nxt_app_isolation(nxt_task_t *task, nxt_conf_value_t *isolation, return NXT_ERROR; } } + + return NXT_OK; +} + #endif -#if (NXT_HAVE_CLONE_NEWUSER) - ret = nxt_app_isolation_creds(task, isolation, process); - if (nxt_slow_path(ret != NXT_OK)) { - return NXT_ERROR; + +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_int_t +nxt_app_set_isolation_rootfs(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_str_t str; + nxt_conf_value_t *obj; + + static nxt_str_t rootfs_name = nxt_string("rootfs"); + + obj = nxt_conf_get_object_member(isolation, &rootfs_name, NULL); + if (obj != NULL) { + nxt_conf_get_string(obj, &str); + + if (nxt_slow_path(str.length <= 1 || str.start[0] != '/')) { + nxt_log(task, NXT_LOG_ERR, "rootfs requires an absolute path other " + "than \"/\" but given \"%V\"", &str); + + return NXT_ERROR; + } + + if (str.start[str.length - 1] == '/') { + str.length--; + } + + process->isolation.rootfs = nxt_mp_alloc(process->mem_pool, + str.length + 1); + + if (nxt_slow_path(process->isolation.rootfs == NULL)) { + return NXT_ERROR; + } + + nxt_memcpy(process->isolation.rootfs, str.start, str.length); + + process->isolation.rootfs[str.length] = '\0'; } + + return NXT_OK; +} + #endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + +static nxt_int_t +nxt_app_set_isolation_new_privs(nxt_task_t *task, nxt_conf_value_t *isolation, + nxt_process_t *process) +{ + nxt_conf_value_t *obj; + + static nxt_str_t new_privs_name = nxt_string("new_privs"); + + obj = nxt_conf_get_object_member(isolation, &new_privs_name, NULL); + if (obj != NULL) { + process->isolation.new_privs = nxt_conf_get_boolean(obj); + } + return NXT_OK; } +#endif + #if (NXT_HAVE_CLONE_NEWUSER) static nxt_int_t -nxt_app_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, +nxt_app_set_isolation_creds(nxt_task_t *task, nxt_conf_value_t *isolation, nxt_process_t *process) { nxt_int_t ret; @@ -753,6 +999,165 @@ nxt_app_clone_flags(nxt_task_t *task, nxt_conf_value_t *namespaces, #endif +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_int_t +nxt_app_prepare_lang_mounts(nxt_task_t *task, nxt_process_t *process, + nxt_array_t *lang_mounts) +{ + u_char *p; + size_t i, n, rootfs_len, len; + nxt_mp_t *mp; + nxt_array_t *mounts; + const u_char *rootfs; + nxt_fs_mount_t *mnt, *lang_mnt; + + rootfs = process->isolation.rootfs; + rootfs_len = nxt_strlen(rootfs); + mp = process->mem_pool; + + /* copy to init mem pool */ + mounts = nxt_array_copy(mp, NULL, lang_mounts); + if (mounts == NULL) { + return NXT_ERROR; + } + + n = mounts->nelts; + mnt = mounts->elts; + lang_mnt = lang_mounts->elts; + + for (i = 0; i < n; i++) { + len = nxt_strlen(lang_mnt[i].dst); + + mnt[i].dst = nxt_mp_alloc(mp, rootfs_len + len + 1); + if (mnt[i].dst == NULL) { + return NXT_ERROR; + } + + p = nxt_cpymem(mnt[i].dst, rootfs, rootfs_len); + p = nxt_cpymem(p, lang_mnt[i].dst, len); + *p = '\0'; + } + + process->isolation.mounts = mounts; + + return NXT_OK; +} + + + +static nxt_int_t +nxt_app_prepare_rootfs(nxt_task_t *task, nxt_process_t *process) +{ + size_t i, n; + nxt_int_t ret, hasproc; + struct stat st; + nxt_array_t *mounts; + const u_char *dst; + nxt_fs_mount_t *mnt; + + hasproc = 0; + +#if (NXT_HAVE_CLONE_NEWPID) && (NXT_HAVE_CLONE_NEWNS) + nxt_fs_mount_t mount; + + if (nxt_is_clone_flag_set(process->isolation.clone.flags, NEWPID) + && nxt_is_clone_flag_set(process->isolation.clone.flags, NEWNS)) + { + /* + * This mount point will automatically be gone when the namespace is + * destroyed. + */ + + mount.fstype = (u_char *) "proc"; + mount.src = (u_char *) "proc"; + mount.dst = (u_char *) "/proc"; + mount.data = (u_char *) ""; + mount.flags = 0; + + ret = nxt_fs_mkdir_all(mount.dst, S_IRWXU | S_IRWXG | S_IRWXO); + if (nxt_fast_path(ret == NXT_OK)) { + ret = nxt_fs_mount(task, &mount); + if (nxt_fast_path(ret == NXT_OK)) { + hasproc = 1; + } + + } else { + nxt_log(task, NXT_LOG_WARN, "mkdir(%s) %E", mount.dst, nxt_errno); + } + } +#endif + + mounts = process->isolation.mounts; + + n = mounts->nelts; + mnt = mounts->elts; + + for (i = 0; i < n; i++) { + dst = mnt[i].dst; + + if (nxt_slow_path(nxt_memcmp(mnt[i].fstype, "bind", 4) == 0 + && stat((const char *) mnt[i].src, &st) != 0)) + { + nxt_log(task, NXT_LOG_WARN, "host path not found: %s", mnt[i].src); + continue; + } + + if (hasproc && nxt_memcmp(mnt[i].fstype, "proc", 4) == 0 + && nxt_memcmp(mnt[i].dst, "/proc", 5) == 0) + { + continue; + } + + ret = nxt_fs_mkdir_all(dst, S_IRWXU | S_IRWXG | S_IRWXO); + if (nxt_slow_path(ret != NXT_OK)) { + nxt_alert(task, "mkdir(%s) %E", dst, nxt_errno); + goto undo; + } + + ret = nxt_fs_mount(task, &mnt[i]); + if (nxt_slow_path(ret != NXT_OK)) { + goto undo; + } + } + + return NXT_OK; + +undo: + + n = i + 1; + + for (i = 0; i < n; i++) { + nxt_fs_unmount(mnt[i].dst); + } + + return NXT_ERROR; +} + +#endif + + +static u_char * +nxt_cstr_dup(nxt_mp_t *mp, u_char *dst, u_char *src) +{ + u_char *p; + size_t len; + + len = nxt_strlen(src); + + if (dst == NULL) { + dst = nxt_mp_alloc(mp, len + 1); + if (nxt_slow_path(dst == NULL)) { + return NULL; + } + } + + p = nxt_cpymem(dst, src, len); + *p = '\0'; + + return dst; +} + nxt_app_lang_module_t * nxt_app_lang_module(nxt_runtime_t *rt, nxt_str_t *name) diff --git a/src/nxt_application.h b/src/nxt_application.h index b4231e3b..3144dc3f 100644 --- a/src/nxt_application.h +++ b/src/nxt_application.h @@ -36,6 +36,7 @@ typedef struct { u_char *version; char *file; nxt_app_module_t *module; + nxt_array_t *mounts; /* of nxt_fs_mount_t */ } nxt_app_lang_module_t; @@ -110,6 +111,9 @@ struct nxt_app_module_s { nxt_str_t type; const char *version; + const nxt_fs_mount_t *mounts; + nxt_uint_t nmounts; + nxt_application_setup_t setup; nxt_process_start_t start; }; diff --git a/src/nxt_array.c b/src/nxt_array.c index 82019f92..6fe9ad6a 100644 --- a/src/nxt_array.c +++ b/src/nxt_array.c @@ -109,3 +109,42 @@ nxt_array_remove(nxt_array_t *array, void *elt) array->nelts--; } + + +nxt_array_t * +nxt_array_copy(nxt_mp_t *mp, nxt_array_t *dst, nxt_array_t *src) +{ + void *data; + uint32_t i, size; + + size = src->size; + + if (dst == NULL) { + dst = nxt_array_create(mp, src->nelts, size); + if (nxt_slow_path(dst == NULL)) { + return NULL; + } + } + + nxt_assert(size == dst->size); + + if (dst->nalloc >= src->nelts) { + nxt_memcpy(dst->elts, src->elts, src->nelts * size); + + } else { + nxt_memcpy(dst->elts, src->elts, dst->nelts * size); + + for (i = dst->nelts; i < src->nelts; i++) { + data = nxt_array_add(dst); + if (nxt_slow_path(data == NULL)) { + return NULL; + } + + nxt_memcpy(data, src->elts + (i * size), size); + } + } + + dst->nelts = src->nelts; + + return dst; +} diff --git a/src/nxt_array.h b/src/nxt_array.h index 8d2b14f1..5762ec27 100644 --- a/src/nxt_array.h +++ b/src/nxt_array.h @@ -24,7 +24,8 @@ NXT_EXPORT void nxt_array_destroy(nxt_array_t *array); NXT_EXPORT void *nxt_array_add(nxt_array_t *array); NXT_EXPORT void *nxt_array_zero_add(nxt_array_t *array); NXT_EXPORT void nxt_array_remove(nxt_array_t *array, void *elt); - +NXT_EXPORT nxt_array_t *nxt_array_copy(nxt_mp_t *mp, nxt_array_t *dst, + nxt_array_t *src); #define \ nxt_array_last(array) \ diff --git a/src/nxt_capability.c b/src/nxt_capability.c index dfa7a834..24fd55d0 100644 --- a/src/nxt_capability.c +++ b/src/nxt_capability.c @@ -39,6 +39,7 @@ nxt_capability_set(nxt_task_t *task, nxt_capabilities_t *cap) if (geteuid() == 0) { cap->setid = 1; + cap->chroot = 1; return NXT_OK; } @@ -91,6 +92,10 @@ nxt_capability_specific_set(nxt_task_t *task, nxt_capabilities_t *cap) return NXT_ERROR; } + if ((val->effective & (1 << CAP_SYS_CHROOT)) != 0) { + cap->chroot = 1; + } + if ((val->effective & (1 << CAP_SETUID)) == 0) { return NXT_OK; } diff --git a/src/nxt_capability.h b/src/nxt_capability.h index 60bbd5f8..1575d409 100644 --- a/src/nxt_capability.h +++ b/src/nxt_capability.h @@ -7,7 +7,8 @@ #define _NXT_CAPABILITY_INCLUDED_ typedef struct { - uint8_t setid; /* 1 bit */ + uint8_t setid; /* 1 bit */ + uint8_t chroot; /* 1 bit */ } nxt_capabilities_t; diff --git a/src/nxt_clone.h b/src/nxt_clone.h index c2066ce6..e89fd82d 100644 --- a/src/nxt_clone.h +++ b/src/nxt_clone.h @@ -42,6 +42,9 @@ pid_t nxt_clone(nxt_int_t flags); #if (NXT_HAVE_CLONE_NEWUSER) +#define NXT_CLONE_MNT(flags) \ + ((flags & CLONE_NEWNS) == CLONE_NEWNS) + NXT_EXPORT nxt_int_t nxt_clone_credential_map(nxt_task_t *task, pid_t pid, nxt_credential_t *creds, nxt_clone_t *clone); NXT_EXPORT nxt_int_t nxt_clone_vldt_credential_uidmap(nxt_task_t *task, diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index a7a8d139..c4f78608 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -573,6 +573,24 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_app_isolation_members[] = { &nxt_conf_vldt_array_iterator, (void *) &nxt_conf_vldt_clone_gidmap }, +#endif + +#if (NXT_HAVE_ISOLATION_ROOTFS) + + { nxt_string("rootfs"), + NXT_CONF_VLDT_STRING, + NULL, + NULL }, + +#endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + + { nxt_string("new_privs"), + NXT_CONF_VLDT_BOOLEAN, + NULL, + NULL }, + #endif NXT_CONF_VLDT_END diff --git a/src/nxt_external.c b/src/nxt_external.c index 58523525..6370a9c4 100644 --- a/src/nxt_external.c +++ b/src/nxt_external.c @@ -18,6 +18,8 @@ nxt_app_module_t nxt_external_module = { nxt_string("external"), "*", NULL, + 0, + NULL, nxt_external_start, }; diff --git a/src/nxt_fs.c b/src/nxt_fs.c new file mode 100644 index 00000000..fe271802 --- /dev/null +++ b/src/nxt_fs.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) NGINX, Inc. + */ + +#include + +#if (NXT_HAVE_FREEBSD_NMOUNT) +#include +#include +#endif + + +static nxt_int_t nxt_fs_mkdir(const u_char *dir, mode_t mode); + + +#if (NXT_HAVE_LINUX_MOUNT) + +nxt_int_t +nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) +{ + int rc; + + rc = mount((const char *) mnt->src, (const char *) mnt->dst, + (const char *) mnt->fstype, mnt->flags, mnt->data); + + if (nxt_slow_path(rc < 0)) { + nxt_alert(task, "mount(\"%s\", \"%s\", \"%s\", %d, \"%s\") %E", + mnt->src, mnt->dst, mnt->fstype, mnt->flags, mnt->data, + nxt_errno); + + return NXT_ERROR; + } + + return NXT_OK; +} + + +#elif (NXT_HAVE_FREEBSD_NMOUNT) + +nxt_int_t +nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt) +{ + const char *fstype; + uint8_t is_bind, is_proc; + struct iovec iov[8]; + char errmsg[256]; + + is_bind = nxt_strncmp(mnt->fstype, "bind", 4) == 0; + is_proc = nxt_strncmp(mnt->fstype, "proc", 4) == 0; + + if (nxt_slow_path(!is_bind && !is_proc)) { + nxt_alert(task, "mount type \"%s\" not implemented.", mnt->fstype); + return NXT_ERROR; + } + + if (is_bind) { + fstype = "nullfs"; + + } else { + fstype = "procfs"; + } + + iov[0].iov_base = (void *) "fstype"; + iov[0].iov_len = 7; + iov[1].iov_base = (void *) fstype; + iov[1].iov_len = strlen(fstype) + 1; + iov[2].iov_base = (void *) "fspath"; + iov[2].iov_len = 7; + iov[3].iov_base = (void *) mnt->dst; + iov[3].iov_len = nxt_strlen(mnt->dst) + 1; + iov[4].iov_base = (void *) "target"; + iov[4].iov_len = 7; + iov[5].iov_base = (void *) mnt->src; + iov[5].iov_len = nxt_strlen(mnt->src) + 1; + iov[6].iov_base = (void *) "errmsg"; + iov[6].iov_len = 7; + iov[7].iov_base = (void *) errmsg; + iov[7].iov_len = sizeof(errmsg); + + if (nxt_slow_path(nmount(iov, 8, 0) < 0)) { + nxt_alert(task, "nmount(%p, 8, 0) %s", errmsg); + return NXT_ERROR; + } + + return NXT_OK; +} + +#endif + + +#if (NXT_HAVE_LINUX_UMOUNT2) + +void +nxt_fs_unmount(const u_char *path) +{ + if (nxt_slow_path(umount2((const char *) path, MNT_DETACH) < 0)) { + nxt_thread_log_error(NXT_LOG_WARN, "umount2(%s, MNT_DETACH) %E", + path, nxt_errno); + } +} + +#elif (NXT_HAVE_UNMOUNT) + +void +nxt_fs_unmount(const u_char *path) +{ + if (nxt_slow_path(unmount((const char *) path, MNT_FORCE) < 0)) { + nxt_thread_log_error(NXT_LOG_WARN, "unmount(%s) %E", path, nxt_errno); + } +} + +#endif + + +nxt_int_t +nxt_fs_mkdir_all(const u_char *dir, mode_t mode) +{ + char *start, *end, *dst; + size_t dirlen; + char path[PATH_MAX]; + + dirlen = nxt_strlen(dir); + + nxt_assert(dirlen < PATH_MAX && dirlen > 1 && dir[0] == '/'); + + dst = path; + start = end = (char *) dir; + + while (*start != '\0') { + if (*start == '/') { + *dst++ = *start++; + } + + end = strchr(start, '/'); + if (end == NULL) { + end = ((char *)dir + dirlen); + } + + dst = nxt_cpymem(dst, start, end - start); + *dst = '\0'; + + if (nxt_slow_path(nxt_fs_mkdir((u_char *) path, mode) != NXT_OK + && nxt_errno != EEXIST)) + { + return NXT_ERROR; + } + + start = end; + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_fs_mkdir(const u_char *dir, mode_t mode) +{ + if (nxt_fast_path(mkdir((const char *) dir, mode) == 0)) { + return NXT_OK; + } + + return NXT_ERROR; +} diff --git a/src/nxt_fs.h b/src/nxt_fs.h new file mode 100644 index 00000000..85c78b27 --- /dev/null +++ b/src/nxt_fs.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) NGINX, Inc. + */ + +#ifndef _NXT_FS_H_INCLUDED_ +#define _NXT_FS_H_INCLUDED_ + + +#ifdef MS_BIND +#define NXT_MS_BIND MS_BIND +#else +#define NXT_MS_BIND 0 +#endif + +#ifdef MS_REC +#define NXT_MS_REC MS_BIND +#else +#define NXT_MS_REC 0 +#endif + + +typedef struct { + u_char *src; + u_char *dst; + u_char *fstype; + nxt_int_t flags; + u_char *data; +} nxt_fs_mount_t; + + +nxt_int_t nxt_fs_mkdir_all(const u_char *dir, mode_t mode); +nxt_int_t nxt_fs_mount(nxt_task_t *task, nxt_fs_mount_t *mnt); +void nxt_fs_unmount(const u_char *path); + + +#endif /* _NXT_FS_H_INCLUDED_ */ diff --git a/src/nxt_java.c b/src/nxt_java.c index c4145c1d..c7471509 100644 --- a/src/nxt_java.c +++ b/src/nxt_java.c @@ -26,6 +26,7 @@ #include "java/nxt_jni_URLClassLoader.h" #include "nxt_jars.h" +#include "nxt_java_mounts.h" static nxt_int_t nxt_java_setup(nxt_task_t *task, nxt_process_t *process, nxt_common_app_conf_t *conf); @@ -50,6 +51,8 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { compat, nxt_string("java"), NXT_STRING(NXT_JAVA_VERSION), + nxt_java_mounts, + nxt_nitems(nxt_java_mounts), nxt_java_setup, nxt_java_start, }; @@ -64,20 +67,66 @@ static nxt_int_t nxt_java_setup(nxt_task_t *task, nxt_process_t *process, nxt_common_app_conf_t *conf) { + char *path, *relpath, *p, *rootfs; + size_t jars_dir_len, rootfs_len; const char *unit_jars; + rootfs = (char *) process->isolation.rootfs; + rootfs_len = 0; + unit_jars = conf->u.java.unit_jars; if (unit_jars == NULL) { - unit_jars = NXT_JARS; + if (rootfs != NULL) { + unit_jars = "/"; + } else { + unit_jars = NXT_JARS; + } } - nxt_java_modules = realpath(unit_jars, NULL); - if (nxt_java_modules == NULL) { - nxt_alert(task, "realpath(%s) failed: %E", unit_jars, nxt_errno); + relpath = strdup(unit_jars); + if (nxt_slow_path(relpath == NULL)) { return NXT_ERROR; } + if (rootfs != NULL) { + jars_dir_len = strlen(unit_jars); + rootfs_len = strlen(rootfs); + + path = nxt_malloc(jars_dir_len + rootfs_len + 1); + if (nxt_slow_path(path == NULL)) { + free(relpath); + return NXT_ERROR; + } + + p = nxt_cpymem(path, process->isolation.rootfs, rootfs_len); + p = nxt_cpymem(p, relpath, jars_dir_len); + *p = '\0'; + + free(relpath); + + } else { + path = relpath; + } + + nxt_java_modules = realpath(path, NULL); + if (nxt_java_modules == NULL) { + nxt_alert(task, "realpath(\"%s\") failed %E", path, nxt_errno); + goto free; + } + + if (rootfs != NULL && strlen(path) > rootfs_len) { + nxt_java_modules = path + rootfs_len; + } + + nxt_debug(task, "JAVA MODULES: %s", nxt_java_modules); + return NXT_OK; + +free: + + nxt_free(path); + + return NXT_ERROR; } @@ -85,6 +134,7 @@ static char ** nxt_java_module_jars(const char *jars[], int jar_count) { char **res, *jurl; + uint8_t pathsep; nxt_int_t modules_len, jlen, i; const char **jar; @@ -95,9 +145,13 @@ nxt_java_module_jars(const char *jars[], int jar_count) modules_len = nxt_strlen(nxt_java_modules); + pathsep = nxt_java_modules[modules_len - 1] == '/'; + for (i = 0, jar = jars; *jar != NULL; jar++) { - jlen = nxt_length("file:") + modules_len + nxt_length("/") - + nxt_strlen(*jar) + 1; + jlen = nxt_length("file:") + modules_len + + (!pathsep ? nxt_length("/") : 0) + + nxt_strlen(*jar) + 1; + jurl = nxt_malloc(jlen); if (jurl == NULL) { return NULL; @@ -107,7 +161,11 @@ nxt_java_module_jars(const char *jars[], int jar_count) jurl = nxt_cpymem(jurl, "file:", nxt_length("file:")); jurl = nxt_cpymem(jurl, nxt_java_modules, modules_len); - *jurl++ = '/'; + + if (!pathsep) { + *jurl++ = '/'; + } + jurl = nxt_cpymem(jurl, *jar, nxt_strlen(*jar)); *jurl++ = '\0'; } diff --git a/src/nxt_main.h b/src/nxt_main.h index b310c4fa..5914fbd1 100644 --- a/src/nxt_main.h +++ b/src/nxt_main.h @@ -59,6 +59,7 @@ typedef uint16_t nxt_port_id_t; #include #include #include +#include #include #include #include diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c index 0dff050b..a16e44d3 100644 --- a/src/nxt_main_process.c +++ b/src/nxt_main_process.c @@ -14,6 +14,8 @@ #include #endif +#include + typedef struct { nxt_socket_t socket; @@ -869,6 +871,12 @@ nxt_main_cleanup_process(nxt_task_t *task, nxt_pid_t pid) return; } +#if (NXT_HAVE_ISOLATION_ROOTFS) + if (process->isolation.rootfs != NULL && process->isolation.mounts) { + (void) nxt_process_unmount_all(task, process); + } +#endif + name = process->name; stream = process->stream; init = *((nxt_process_init_t *) nxt_process_init(process)); @@ -1132,19 +1140,50 @@ static nxt_conf_map_t nxt_app_lang_module_map[] = { }; +static nxt_conf_map_t nxt_app_lang_mounts_map[] = { + { + nxt_string("src"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_fs_mount_t, src), + }, + { + nxt_string("dst"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_fs_mount_t, dst), + }, + { + nxt_string("fstype"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_fs_mount_t, fstype), + }, + { + nxt_string("flags"), + NXT_CONF_MAP_INT, + offsetof(nxt_fs_mount_t, flags), + }, + { + nxt_string("data"), + NXT_CONF_MAP_CSTRZ, + offsetof(nxt_fs_mount_t, data), + }, +}; + + static void nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) { - uint32_t index; + uint32_t index, jindex, nmounts; nxt_mp_t *mp; nxt_int_t ret; nxt_buf_t *b; nxt_port_t *port; nxt_runtime_t *rt; - nxt_conf_value_t *conf, *root, *value; + nxt_fs_mount_t *mnt; + nxt_conf_value_t *conf, *root, *value, *mounts; nxt_app_lang_module_t *lang; static nxt_str_t root_path = nxt_string("/"); + static nxt_str_t mounts_name = nxt_string("mounts"); rt = task->thread->runtime; @@ -1201,7 +1240,7 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) break; } - lang = nxt_array_add(rt->languages); + lang = nxt_array_zero_add(rt->languages); if (lang == NULL) { goto fail; } @@ -1215,8 +1254,48 @@ nxt_main_port_modules_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg) goto fail; } - nxt_debug(task, "lang %d %s \"%s\"", - lang->type, lang->version, lang->file); + mounts = nxt_conf_get_object_member(value, &mounts_name, NULL); + if (mounts == NULL) { + nxt_alert(task, "missing mounts from discovery message."); + goto fail; + } + + if (nxt_conf_type(mounts) != NXT_CONF_ARRAY) { + nxt_alert(task, "invalid mounts type from discovery message."); + goto fail; + } + + nmounts = nxt_conf_array_elements_count(mounts); + + lang->mounts = nxt_array_create(rt->mem_pool, nmounts, + sizeof(nxt_fs_mount_t)); + + if (lang->mounts == NULL) { + goto fail; + } + + for (jindex = 0; /* */; jindex++) { + value = nxt_conf_get_array_element(mounts, jindex); + if (value == NULL) { + break; + } + + mnt = nxt_array_zero_add(lang->mounts); + if (mnt == NULL) { + goto fail; + } + + ret = nxt_conf_map_object(rt->mem_pool, value, + nxt_app_lang_mounts_map, + nxt_nitems(nxt_app_lang_mounts_map), mnt); + + if (ret != NXT_OK) { + goto fail; + } + } + + nxt_debug(task, "lang %d %s \"%s\" (%d mounts)", + lang->type, lang->version, lang->file, lang->mounts->nelts); } qsort(rt->languages->elts, rt->languages->nelts, diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index ddad5761..7ae8484d 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -242,6 +242,8 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_string("php"), PHP_VERSION, NULL, + 0, + NULL, nxt_php_start, }; diff --git a/src/nxt_process.c b/src/nxt_process.c index e84549b3..c4c44d14 100644 --- a/src/nxt_process.c +++ b/src/nxt_process.c @@ -13,6 +13,14 @@ #include +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) +#include +#endif + +#if (NXT_HAVE_PIVOT_ROOT) +#include +#endif + static nxt_int_t nxt_process_setup(nxt_task_t *task, nxt_process_t *process); static nxt_int_t nxt_process_child_fixup(nxt_task_t *task, nxt_process_t *process); @@ -25,6 +33,19 @@ static void nxt_process_created_ok(nxt_task_t *task, nxt_port_recv_msg_t *msg, static void nxt_process_created_error(nxt_task_t *task, nxt_port_recv_msg_t *msg, void *data); +#if (NXT_HAVE_ISOLATION_ROOTFS) +static nxt_int_t nxt_process_chroot(nxt_task_t *task, const char *path); +#endif + +#if (NXT_HAVE_PIVOT_ROOT) +static nxt_int_t nxt_process_pivot_root(nxt_task_t *task, const char *rootfs); +static nxt_int_t nxt_process_private_mount(nxt_task_t *task, + const char *rootfs); +#endif + +#if (NXT_HAVE_PIVOT_ROOT) +static int nxt_pivot_root(const char *new_root, const char *old_root); +#endif /* A cached process pid. */ nxt_pid_t nxt_pid; @@ -495,10 +516,347 @@ nxt_process_apply_creds(nxt_task_t *task, nxt_process_t *process) } } +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + if (nxt_slow_path(process->isolation.new_privs == 0 + && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0)) + { + nxt_alert(task, "failed to set no_new_privs %E", nxt_errno); + return NXT_ERROR; + } +#endif + + return NXT_OK; +} + + +#if (NXT_HAVE_ISOLATION_ROOTFS) + + +#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS) + + +nxt_int_t +nxt_process_change_root(nxt_task_t *task, nxt_process_t *process) +{ + char *rootfs; + nxt_int_t ret; + + rootfs = (char *) process->isolation.rootfs; + + nxt_debug(task, "change root: %s", rootfs); + + if (NXT_CLONE_MNT(process->isolation.clone.flags)) { + ret = nxt_process_pivot_root(task, rootfs); + } else { + ret = nxt_process_chroot(task, rootfs); + } + + if (nxt_fast_path(ret == NXT_OK)) { + if (nxt_slow_path(chdir("/") < 0)) { + nxt_alert(task, "chdir(\"/\") %E", nxt_errno); + return NXT_ERROR; + } + } + + return ret; +} + + +#else + + +nxt_int_t +nxt_process_change_root(nxt_task_t *task, nxt_process_t *process) +{ + char *rootfs; + + rootfs = (char *) process->isolation.rootfs; + + nxt_debug(task, "change root: %s", rootfs); + + if (nxt_fast_path(nxt_process_chroot(task, rootfs) == NXT_OK)) { + if (nxt_slow_path(chdir("/") < 0)) { + nxt_alert(task, "chdir(\"/\") %E", nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; + } + + return NXT_ERROR; +} + + +#endif + + +#endif + + +#if (NXT_HAVE_ISOLATION_ROOTFS) + +static nxt_int_t +nxt_process_chroot(nxt_task_t *task, const char *path) +{ + if (nxt_slow_path(chroot(path) < 0)) { + nxt_alert(task, "chroot(%s) %E", path, nxt_errno); + return NXT_ERROR; + } + return NXT_OK; } +void +nxt_process_unmount_all(nxt_task_t *task, nxt_process_t *process) +{ + size_t i, n; + nxt_array_t *mounts; + nxt_fs_mount_t *mnt; + + nxt_debug(task, "unmount all (%s)", process->name); + + mounts = process->isolation.mounts; + n = mounts->nelts; + mnt = mounts->elts; + + for (i = 0; i < n; i++) { + nxt_fs_unmount(mnt[i].dst); + } +} + +#endif + + +#if (NXT_HAVE_PIVOT_ROOT) && (NXT_HAVE_CLONE_NEWNS) + +/* + * pivot_root(2) can only be safely used with containers, otherwise it can + * umount(2) the global root filesystem and screw up the machine. + */ + +static nxt_int_t +nxt_process_pivot_root(nxt_task_t *task, const char *path) +{ + /* + * This implementation makes use of a kernel trick that works for ages + * and now documented in Linux kernel 5. + * https://lore.kernel.org/linux-man/87r24piwhm.fsf@x220.int.ebiederm.org/T/ + */ + + if (nxt_slow_path(mount("", "/", "", MS_SLAVE|MS_REC, "") != 0)) { + nxt_alert(task, "failed to make / a slave mount %E", nxt_errno); + return NXT_ERROR; + } + + if (nxt_slow_path(nxt_process_private_mount(task, path) != NXT_OK)) { + return NXT_ERROR; + } + + if (nxt_slow_path(mount(path, path, "bind", MS_BIND|MS_REC, "") != 0)) { + nxt_alert(task, "error bind mounting rootfs %E", nxt_errno); + return NXT_ERROR; + } + + if (nxt_slow_path(chdir(path) != 0)) { + nxt_alert(task, "failed to chdir(%s) %E", path, nxt_errno); + return NXT_ERROR; + } + + if (nxt_slow_path(nxt_pivot_root(".", ".") != 0)) { + nxt_alert(task, "failed to pivot_root %E", nxt_errno); + return NXT_ERROR; + } + + /* + * Make oldroot a slave mount to avoid unmounts getting propagated to the + * host. + */ + if (nxt_slow_path(mount("", ".", "", MS_SLAVE | MS_REC, NULL) != 0)) { + nxt_alert(task, "failed to bind mount rootfs %E", nxt_errno); + return NXT_ERROR; + } + + if (nxt_slow_path(umount2(".", MNT_DETACH) != 0)) { + nxt_alert(task, "failed to umount old root directory %E", nxt_errno); + return NXT_ERROR; + } + + return NXT_OK; +} + + +static nxt_int_t +nxt_process_private_mount(nxt_task_t *task, const char *rootfs) +{ + char *parent_mnt; + FILE *procfile; + u_char **mounts; + size_t len; + uint8_t *shared; + nxt_int_t ret, index, nmounts; + struct mntent *ent; + + static const char *mount_path = "/proc/self/mounts"; + + ret = NXT_ERROR; + ent = NULL; + shared = NULL; + procfile = NULL; + parent_mnt = NULL; + + nmounts = 256; + + mounts = nxt_malloc(nmounts * sizeof(uintptr_t)); + if (nxt_slow_path(mounts == NULL)) { + goto fail; + } + + shared = nxt_malloc(nmounts); + if (nxt_slow_path(shared == NULL)) { + goto fail; + } + + procfile = setmntent(mount_path, "r"); + if (nxt_slow_path(procfile == NULL)) { + nxt_alert(task, "failed to open %s %E", mount_path, nxt_errno); + + goto fail; + } + + index = 0; + +again: + + for ( ; index < nmounts; index++) { + ent = getmntent(procfile); + if (ent == NULL) { + nmounts = index; + break; + } + + mounts[index] = (u_char *) strdup(ent->mnt_dir); + shared[index] = hasmntopt(ent, "shared") != NULL; + } + + if (ent != NULL) { + /* there are still entries to be read */ + + nmounts *= 2; + mounts = nxt_realloc(mounts, nmounts); + if (nxt_slow_path(mounts == NULL)) { + goto fail; + } + + shared = nxt_realloc(shared, nmounts); + if (nxt_slow_path(shared == NULL)) { + goto fail; + } + + goto again; + } + + for (index = 0; index < nmounts; index++) { + if (nxt_strcmp(mounts[index], rootfs) == 0) { + parent_mnt = (char *) rootfs; + break; + } + } + + if (parent_mnt == NULL) { + len = nxt_strlen(rootfs); + + parent_mnt = nxt_malloc(len + 1); + if (parent_mnt == NULL) { + goto fail; + } + + nxt_memcpy(parent_mnt, rootfs, len); + parent_mnt[len] = '\0'; + + if (parent_mnt[len - 1] == '/') { + parent_mnt[len - 1] = '\0'; + len--; + } + + for ( ;; ) { + for (index = 0; index < nmounts; index++) { + if (nxt_strcmp(mounts[index], parent_mnt) == 0) { + goto found; + } + } + + if (len == 1 && parent_mnt[0] == '/') { + nxt_alert(task, "parent mount not found"); + goto fail; + } + + /* parent dir */ + while (parent_mnt[len - 1] != '/' && len > 0) { + len--; + } + + if (nxt_slow_path(len == 0)) { + nxt_alert(task, "parent mount not found"); + goto fail; + } + + if (len == 1) { + parent_mnt[len] = '\0'; /* / */ + } else { + parent_mnt[len - 1] = '\0'; /* / */ + } + } + } + +found: + + if (shared[index]) { + if (nxt_slow_path(mount("", parent_mnt, "", MS_PRIVATE, "") != 0)) { + nxt_alert(task, "mount(\"\", \"%s\", MS_PRIVATE) %E", parent_mnt, + nxt_errno); + + goto fail; + } + } + + ret = NXT_OK; + +fail: + + if (procfile != NULL) { + endmntent(procfile); + } + + if (mounts != NULL) { + for (index = 0; index < nmounts; index++) { + nxt_free(mounts[index]); + } + + nxt_free(mounts); + } + + if (shared != NULL) { + nxt_free(shared); + } + + if (parent_mnt != NULL && parent_mnt != rootfs) { + nxt_free(parent_mnt); + } + + return ret; +} + + +static int +nxt_pivot_root(const char *new_root, const char *old_root) +{ + return syscall(__NR_pivot_root, new_root, old_root); +} + +#endif + + static nxt_int_t nxt_process_send_ready(nxt_task_t *task, nxt_process_t *process) { diff --git a/src/nxt_process.h b/src/nxt_process.h index 45bab25e..d3311722 100644 --- a/src/nxt_process.h +++ b/src/nxt_process.h @@ -69,33 +69,42 @@ typedef struct { nxt_port_mmap_t *elts; } nxt_port_mmaps_t; +typedef struct { + u_char *rootfs; + nxt_array_t *mounts; /* of nxt_mount_t */ + +#if (NXT_HAVE_CLONE) + nxt_clone_t clone; +#endif + +#if (NXT_HAVE_PR_SET_NO_NEW_PRIVS) + uint8_t new_privs; /* 1 bit */ +#endif +} nxt_process_isolation_t; + typedef struct { - nxt_pid_t pid; - const char *name; - nxt_queue_t ports; /* of nxt_port_t */ - nxt_process_state_t state; - nxt_bool_t registered; - nxt_int_t use_count; + nxt_pid_t pid; + const char *name; + nxt_queue_t ports; /* of nxt_port_t */ + nxt_process_state_t state; + nxt_bool_t registered; + nxt_int_t use_count; - nxt_port_mmaps_t incoming; - nxt_port_mmaps_t outgoing; + nxt_port_mmaps_t incoming; + nxt_port_mmaps_t outgoing; - nxt_thread_mutex_t cp_mutex; - nxt_lvlhsh_t connected_ports; /* of nxt_port_t */ + nxt_thread_mutex_t cp_mutex; + nxt_lvlhsh_t connected_ports; /* of nxt_port_t */ - uint32_t stream; + uint32_t stream; - nxt_mp_t *mem_pool; - nxt_credential_t *user_cred; + nxt_mp_t *mem_pool; + nxt_credential_t *user_cred; - nxt_process_data_t data; + nxt_process_data_t data; - union { -#if (NXT_HAVE_CLONE) - nxt_clone_t clone; -#endif - } isolation; + nxt_process_isolation_t isolation; } nxt_process_t; @@ -184,6 +193,12 @@ nxt_int_t nxt_process_vldt_isolation_creds(nxt_task_t *task, nxt_process_t *process); #endif +nxt_int_t nxt_process_change_root(nxt_task_t *task, nxt_process_t *process); + +#if (NXT_HAVE_ISOLATION_ROOTFS) +void nxt_process_unmount_all(nxt_task_t *task, nxt_process_t *process); +#endif + #if (NXT_HAVE_SETPROCTITLE) #define nxt_process_title(task, fmt, ...) \ diff --git a/src/nxt_python_wsgi.c b/src/nxt_python_wsgi.c index 089d15c0..b9033a75 100644 --- a/src/nxt_python_wsgi.c +++ b/src/nxt_python_wsgi.c @@ -18,6 +18,7 @@ #include #include #include +#include /* * According to "PEP 3333 / A Note On String Types" @@ -38,11 +39,17 @@ */ +#define _NXT_PYTHON_MOUNTS(major, minor) \ + nxt_python ## major ## minor ## _mounts + +#define NXT_PYTHON_MOUNTS(major, minor) _NXT_PYTHON_MOUNTS(major, minor) + #if PY_MAJOR_VERSION == 3 #define NXT_PYTHON_BYTES_TYPE "bytestring" #define PyString_FromStringAndSize(str, size) \ PyUnicode_DecodeLatin1((str), (size), "strict") + #else #define NXT_PYTHON_BYTES_TYPE "string" @@ -116,6 +123,8 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { compat, nxt_string("python"), PY_VERSION, + NXT_PYTHON_MOUNTS(PY_MAJOR_VERSION, PY_MINOR_VERSION), + nxt_nitems(NXT_PYTHON_MOUNTS(PY_MAJOR_VERSION, PY_MINOR_VERSION)), NULL, nxt_python_start, }; diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c index d7e35dec..5aa061dd 100644 --- a/src/nxt_runtime.c +++ b/src/nxt_runtime.c @@ -84,6 +84,7 @@ nxt_runtime_create(nxt_task_t *task) lang->version = (u_char *) ""; lang->file = NULL; lang->module = &nxt_external_module; + lang->mounts = NULL; listen_sockets = nxt_array_create(mp, 1, sizeof(nxt_listen_socket_t)); if (nxt_slow_path(listen_sockets == NULL)) { diff --git a/src/nxt_unix.h b/src/nxt_unix.h index 151dd555..609f7e95 100644 --- a/src/nxt_unix.h +++ b/src/nxt_unix.h @@ -238,6 +238,9 @@ #include /* getentropy(). */ #endif +#if (NXT_HAVE_ISOLATION_ROOTFS) +#include +#endif #if (NXT_TEST_BUILD) #include diff --git a/src/perl/nxt_perl_psgi.c b/src/perl/nxt_perl_psgi.c index 5e9200dc..14e107e4 100644 --- a/src/perl/nxt_perl_psgi.c +++ b/src/perl/nxt_perl_psgi.c @@ -118,6 +118,12 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { nxt_perl_psgi_compat, nxt_string("perl"), PERL_VERSION_STRING, + +#if (NXT_HAVE_ISOLATION_ROOTFS) + NULL, + 0, +#endif + NULL, nxt_perl_psgi_start, }; diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 40f72f51..489ddcf4 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -7,6 +7,7 @@ #include #include +#include #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 @@ -78,6 +79,10 @@ NXT_EXPORT nxt_app_module_t nxt_app_module = { compat, nxt_string("ruby"), ruby_version, +#if (NXT_HAVE_ISOLATION_ROOTFS) + nxt_ruby_mounts, + nxt_nitems(nxt_ruby_mounts), +#endif NULL, nxt_ruby_start, }; -- cgit