Control API.

A new nginx option -l specifies control socket listen address.
The socket is providing an HTTP/1 REST API for retrieving information
about running nginx instance.

Example:

$ nginx -l 127.0.0.1:1234

$ curl 127.0.0.1:1234/1/control/processes | jq
[
  {
    "pid": 41536,
    "name": "worker process",
    "exiting": false
  }
]

Co-authored-by: Roman Arutyunyan <arut@nginx.com>
Co-authored-by: Pavel Pautov <p.pautov@f5.com>
Co-authored-by: Dmitry Plotnikov <d.plotnikov@f5.com>
Co-authored-by: Sergey Kandaurov <pluknet@nginx.com>
Co-authored-by: Ivan Ovchinnikov <i.ovchinnikov@f5.com>
This commit is contained in:
Ava Hahn
2026-09-01 10:34:56 -07:00
committed by Ava Hahn
co-authored by Roman Arutyunyan Pavel Pautov Dmitry Plotnikov Sergey Kandaurov Ivan Ovchinnikov
parent 032e181dad
commit 4bc459a15c
8 changed files with 1375 additions and 1 deletions
+17
View File
@@ -56,6 +56,23 @@ if [ $NGX_TEST_BUILD_SOLARIS_SENDFILEV = YES ]; then
fi
if [ $CONTROL_API = YES ]; then
if [ "$NGX_PLATFORM" = win32 ]; then
cat << END
$0: --with-control-api is not supported on Windows
END
exit 1
fi
have=NGX_CONTROL_API . auto/have
CORE_DEPS="$CORE_DEPS $UNIX_CONTROL_API_DEPS"
CORE_SRCS="$CORE_SRCS $UNIX_CONTROL_API_SRCS"
fi
if [ $HTTP = YES ]; then
HTTP_MODULES=
HTTP_DEPS=
+6
View File
@@ -41,6 +41,8 @@ EVENT_FOUND=NO
EVENT_SELECT=NO
EVENT_POLL=NO
CONTROL_API=NO
USE_THREADS=NO
NGX_FILE_AIO=NO
@@ -225,6 +227,8 @@ do
--without-quic_bpf_module) QUIC_BPF=NONE ;;
--with-control-api) CONTROL_API=YES ;;
--with-ipv6)
NGX_POST_CONF_MSG="$NGX_POST_CONF_MSG
$0: warning: the \"--with-ipv6\" option is deprecated"
@@ -468,6 +472,8 @@ cat << END
--with-poll_module enable poll module
--without-poll_module disable poll module
--with-control-api enable control API
--with-threads enable thread pool support
--with-file-aio enable file AIO support
+3
View File
@@ -192,6 +192,9 @@ UNIX_SRCS="$CORE_SRCS $EVENT_SRCS \
src/os/unix/ngx_dlopen.c \
src/os/unix/ngx_process_cycle.c"
UNIX_CONTROL_API_DEPS=src/os/unix/ngx_control.h
UNIX_CONTROL_API_SRCS=src/os/unix/ngx_control.c
POSIX_DEPS=src/os/unix/ngx_posix_config.h
THREAD_POOL_MODULE=ngx_thread_pool_module
+43 -1
View File
@@ -187,6 +187,9 @@ static u_char *ngx_prefix;
static u_char *ngx_error_log;
static u_char *ngx_conf_file;
static u_char *ngx_conf_params;
#if (NGX_CONTROL_API)
static u_char *ngx_control_addr;
#endif
static char *ngx_signal;
@@ -360,6 +363,18 @@ main(int argc, char *const *argv)
#endif
#if (NGX_CONTROL_API)
if (ngx_process == NGX_PROCESS_SINGLE) {
ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
"control API unavailable in single process mode");
} else {
if (ngx_control_init(ngx_control_addr) != NGX_OK) {
return 1;
}
}
#endif
if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
return 1;
}
@@ -410,6 +425,9 @@ ngx_show_version_info(void)
"during configuration testing" NGX_LINEFEED
" -s signal : send signal to a master process: "
"stop, quit, reopen, reload" NGX_LINEFEED
#if (NGX_CONTROL_API)
" -l addr : open control socket on address" NGX_LINEFEED
#endif
#ifdef NGX_PREFIX
" -p prefix : set prefix path (default: " NGX_PREFIX ")"
NGX_LINEFEED
@@ -711,7 +729,7 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
ctx.name = "new binary process";
ctx.argv = argv;
n = 2;
n = 3;
env = ngx_set_environment(cycle, &n);
if (env == NULL) {
return NGX_INVALID_PID;
@@ -751,6 +769,14 @@ ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)
#endif
#if (NGX_CONTROL_API)
env[n] = ngx_control_handoff();
if (env[n] != NULL) {
n++;
}
#endif
env[n] = NULL;
#if (NGX_DEBUG)
@@ -929,6 +955,22 @@ ngx_get_options(int argc, char *const *argv)
ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
return NGX_ERROR;
#if (NGX_CONTROL_API)
case 'l':
if (*p) {
ngx_control_addr = p;
goto next;
}
if (argv[++i]) {
ngx_control_addr = (u_char *) argv[i];
goto next;
}
ngx_log_stderr(0, "option \"-l\" requires listen address");
return NGX_ERROR;
#endif
default:
ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
return NGX_ERROR;
+3
View File
@@ -92,6 +92,9 @@ typedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);
#endif
#endif
#include <ngx_process_cycle.h>
#if (NGX_CONTROL_API)
#include <ngx_control.h>
#endif
#include <ngx_conf_file.h>
#include <ngx_module.h>
#include <ngx_open_file_cache.h>
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright (C) Nginx, Inc.
*/
#ifndef _NGX_CONTROL_H_INCLUDED_
#define _NGX_CONTROL_H_INCLUDED_
#include <ngx_config.h>
#include <ngx_core.h>
ngx_int_t ngx_control_init(u_char *addr);
void ngx_control_uninit(void);
void ngx_control_close_sockets(void);
char *ngx_control_handoff(void);
void ngx_control_reown(void);
ngx_int_t ngx_control_handle_events(void);
extern ngx_uint_t ngx_control_api_enabled;
#endif /* _NGX_CONTROL_H_INCLUDED_ */
+20
View File
@@ -174,6 +174,14 @@ ngx_master_process_cycle(ngx_cycle_t *cycle)
live = ngx_reap_children(cycle);
}
#if (NGX_CONTROL_API)
if (ngx_sigio) {
ngx_sigio = 0;
ngx_control_handle_events();
}
#endif
if (!live && (ngx_terminate || ngx_quit)) {
ngx_master_process_exit(cycle);
}
@@ -629,6 +637,10 @@ ngx_reap_children(ngx_cycle_t *cycle)
ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);
}
#if (NGX_CONTROL_API)
ngx_control_reown();
#endif
ngx_new_binary = 0;
if (ngx_noaccepting) {
ngx_restart = 1;
@@ -669,6 +681,10 @@ ngx_master_process_exit(ngx_cycle_t *cycle)
ngx_close_listening_sockets(cycle);
#if (NGX_CONTROL_API)
ngx_control_uninit();
#endif
/*
* Copy ngx_cycle->log related data to the special static exit cycle,
* log, and log file structures enough to allow a signal handler to log.
@@ -760,6 +776,10 @@ ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
struct rlimit rlmt;
ngx_core_conf_t *ccf;
#if (NGX_CONTROL_API)
ngx_control_close_sockets();
#endif
if (ngx_set_environment(cycle, NULL) == NULL) {
/* fatal */
exit(2);