Control API: in memory config dump.

The /control/config endpoint returns the currently parsed and running
configuration on GET request.

Example:

$ curl 127.0.0.1:1234/1/control/config | jq
[
  {
    "name": "/path/to/nginx.conf",
    "content": "..."
  },
  {
    "name": "/path/to/included.conf",
    "content": "..."
  }
]

Co-authored-by: Pavel Pautov <p.pautov@f5.com>
This commit is contained in:
Ava Hahn
2026-09-01 10:34:56 -07:00
committed by Ava Hahn
co-authored by Pavel Pautov
parent 74133fba7c
commit 1c1c17c104
3 changed files with 59 additions and 3 deletions
+2
View File
@@ -957,6 +957,8 @@ ngx_get_options(int argc, char *const *argv)
#if (NGX_CONTROL_API)
case 'l':
ngx_control_api_enabled = 1;
if (*p) {
ngx_control_addr = p;
goto next;
+3
View File
@@ -217,6 +217,9 @@ ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
type = parse_file;
if (ngx_dump_config
#if (NGX_CONTROL_API)
|| ngx_control_api_enabled
#endif
#if (NGX_DEBUG)
|| 1
#endif
+54 -3
View File
@@ -63,6 +63,7 @@ static ngx_int_t ngx_control_api_endpoints(ngx_control_request_t *r,
static ngx_int_t ngx_control_show_processes(ngx_control_request_t *r);
static ngx_int_t ngx_control_show_version(ngx_control_request_t *r);
static ngx_int_t ngx_control_reload_config(ngx_control_request_t *r);
static ngx_int_t ngx_control_print_config(ngx_control_request_t *r);
static void ngx_control_log_capture(ngx_log_t *log, ngx_uint_t l,
u_char *buf, size_t len);
@@ -117,6 +118,18 @@ static ngx_data_decl_t ngx_control_status_fields[] = {
ngx_data_null_decl
};
static ngx_data_decl_t ngx_control_config_fields[] = {
{ ngx_string("name"),
ngx_data_struct_str_handler,
offsetof(ngx_keyval_t, key) },
{ ngx_string("content"),
ngx_data_struct_str_handler,
offsetof(ngx_keyval_t, value) },
ngx_data_null_decl
};
ngx_int_t
ngx_control_init(u_char *addr)
@@ -630,11 +643,15 @@ ngx_control_content(ngx_control_request_t *r)
case 17:
if (ngx_strncmp(p, "/1/control/config", len) == 0) {
if (r->method != NGX_CTRL_PATCH) {
return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
if (r->method == NGX_CTRL_PATCH) {
return ngx_control_reload_config(r);
}
return ngx_control_reload_config(r);
if (r->method == NGX_CTRL_GET) {
return ngx_control_print_config(r);
}
return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
}
break;
@@ -861,6 +878,40 @@ ngx_control_show_version(ngx_control_request_t *r)
}
static ngx_int_t
ngx_control_print_config(ngx_control_request_t *r)
{
ngx_uint_t i;
ngx_keyval_t item;
ngx_data_item_t *list, *obj;
ngx_conf_dump_t *dump;
list = ngx_data_new_list(r->pool);
if (list == NULL) {
return NGX_ERROR;
}
dump = ngx_cycle->config_dump.elts;
for (i = 0; i < ngx_cycle->config_dump.nelts; i++) {
item.key = dump[i].name;
item.value.data = dump[i].buffer->pos;
item.value.len = dump[i].buffer->last - dump[i].buffer->pos;
obj = ngx_data_obj_handler((uintptr_t) ngx_control_config_fields,
r->pool, &item);
if (obj == NULL) {
return NGX_ERROR;
}
ngx_data_add_item(list, NULL, obj);
}
return ngx_control_send_json(r, NGX_CTRL_OK, list);
}
static ngx_data_item_t *
ngx_control_json_logs(ngx_array_t *logs)
{