]> git.kaiwu.me - nginx.git/commitdiff
Control API: in memory config dump.
authorAva Hahn <a.hahn@f5.com>
Thu, 5 Feb 2026 22:25:19 +0000 (22:25 +0000)
committerAva Hahn <110854134+avahahn@users.noreply.github.com>
Tue, 1 Sep 2026 17:34:56 +0000 (10:34 -0700)
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>
src/core/nginx.c
src/core/ngx_conf_file.c
src/os/unix/ngx_control.c

index 5df9919ce3b0d3fb8ea6dec5abf191a4c9a4bdba..2d619c6e35e9c06ca1790e150346b919aabd7973 100644 (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;
index 6ac0d88296d40b47927e753aefac8fa96b573576..5aee80146c38406cd4d07947554c5a0c3a262637 100644 (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
index b607389959ff8d4ac4c0502e1fa6bd33765b1454..8a6167077b41a64de1de19b82f1df479b34e566b 100644 (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);
+            }
+
+            if (r->method == NGX_CTRL_GET) {
+                return ngx_control_print_config(r);
             }
 
-            return ngx_control_reload_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)
 {