src/os/unix/ngx_control.c - nginx-1.31.9 nginx/ @ 2a3a19c6f

Global variables defined

Data types defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <poll.h>


  7. ‌#define NGX_CTRL_MAX_FD          64
  8. ‌#define NGX_CTRL_MAX_REQUEST     1024

  9. ‌#define NGX_CTRL_OK              200
  10. ‌#define NGX_CTRL_BAD_REQUEST     400
  11. ‌#define NGX_CTRL_NOT_FOUND       404
  12. ‌#define NGX_CTRL_NOT_ALLOWED     405
  13. ‌#define NGX_CTRL_UNPROCESSABLE   422
  14. ‌#define NGX_CTRL_INTERNAL_ERROR  500

  15. ‌#define NGX_CTRL_GET             1
  16. ‌#define NGX_CTRL_PATCH           2

  17. ‌#define NGX_CTRL_ENV             "NGINX_CTRL"


  18. ‌typedef struct {
  19.     ngx_fd_t      fd;
  20.     ngx_pool_t   *pool;
  21.     ngx_uint_t    index;
  22.     ngx_uint_t    state;

  23.     ngx_str_t     path;
  24.     ngx_uint_t    method;

  25.     ngx_buf_t    *in;
  26.     ngx_chain_t  *out;

  27.     ngx_uint_t    reloaded;
  28. ‌} ngx_control_request_t;


  29. ‌typedef struct {
  30.     ngx_str_t     name;
  31.     ngx_int_t     pid;
  32.     ngx_uint_t    exiting;
  33. ‌} ngx_control_process_t;


  34. static ngx_int_t ngx_control_inherit(void);
  35. static void ngx_control_close(ngx_control_request_t *r);
  36. static void ngx_control_handle_accept(void);
  37. static ngx_int_t ngx_control_handle_read(ngx_control_request_t *r);
  38. static ngx_int_t ngx_control_content(ngx_control_request_t *r);
  39. static char *ngx_control_status_text(ngx_uint_t code);
  40. static ngx_int_t ngx_control_send_response(ngx_control_request_t *r,
  41.     ngx_uint_t code, ngx_buf_t *body);
  42. static ngx_int_t ngx_control_handle_write(ngx_control_request_t *r);

  43. static ngx_int_t ngx_control_api_endpoints(ngx_control_request_t *r,
  44.     const char **paths, ngx_int_t npaths);
  45. static ngx_int_t ngx_control_show_processes(ngx_control_request_t *r);
  46. static ngx_int_t ngx_control_show_version(ngx_control_request_t *r);
  47. static ngx_int_t ngx_control_reload_config(ngx_control_request_t *r);
  48. static ngx_int_t ngx_control_print_config(ngx_control_request_t *r);
  49. static void ngx_control_log_capture(ngx_log_t *log, ngx_uint_t l,
  50.     u_char *buf, size_t len);

  51. static ngx_int_t ngx_control_parse_request_line(ngx_control_request_t *r);
  52. static ngx_int_t ngx_control_send_json(ngx_control_request_t *r, ngx_int_t code,
  53.     ngx_data_item_t *obj);


  54. ‌static ngx_control_request_t  ngx_control_requests[NGX_CTRL_MAX_FD];
  55. ‌static struct pollfd          ngx_control_pollfd[NGX_CTRL_MAX_FD];
  56. ‌static ngx_uint_t             ngx_control_pollfd_n;
  57. ‌static char                   ngx_control_unix_path[NGX_UNIX_ADDRSTRLEN];
  58. ‌static ngx_uint_t             ngx_control_inherited;
  59. ‌static u_char                *ngx_control_env;
  60. ‌ngx_uint_t                    ngx_control_api_enabled;


  61. static ngx_str_t  ngx_control_version = ngx_string(NGINX_VERSION);

  62. #ifdef NGX_BUILD
  63. static ngx_str_t  ngx_control_build = ngx_string(NGX_BUILD);
  64. #else
  65. ‌static ngx_str_t  ngx_control_build;
  66. #endif

  67. ‌static ngx_data_decl_t  ngx_control_process_fields[] = {

  68.     { ngx_string("name"),
  69.       ngx_data_struct_str_handler,
  70.       offsetof(ngx_control_process_t, name) },

  71.     { ngx_string("pid"),
  72.       ngx_data_struct_int_handler,
  73.       offsetof(ngx_control_process_t, pid) },

  74.     { ngx_string("exiting"),
  75.       ngx_data_struct_boolean_handler,
  76.       offsetof(ngx_control_process_t, exiting) },

  77.       ngx_data_null_decl
  78. };

  79. ‌static ngx_data_decl_t  ngx_control_status_fields[] = {

  80.     { ngx_string("version"),
  81.       ngx_data_string_handler,
  82.       (uintptr_t) &ngx_control_version },

  83.     { ngx_string("build"),
  84.       ngx_data_string_handler,
  85.       (uintptr_t) &ngx_control_build },

  86.       ngx_data_null_decl
  87. };

  88. ‌static ngx_data_decl_t  ngx_control_config_fields[] = {
  89.     { ngx_string("name"),
  90.       ngx_data_struct_str_handler,
  91.       offsetof(ngx_keyval_t, key) },

  92.     { ngx_string("content"),
  93.       ngx_data_struct_str_handler,
  94.       offsetof(ngx_keyval_t, value) },

  95.       ngx_data_null_decl
  96. };


  97. void
  98. ‌ngx_control_preinit(void)
  99. {
  100.     /*
  101.      * the variable is read before ngx_init_cycle(), which may replace
  102.      * the environment while parsing configuration
  103.      */

  104.     ngx_control_env = (u_char *) getenv(NGX_CTRL_ENV);
  105. }


  106. ngx_int_t
  107. ‌ngx_control_init(u_char *addr)
  108. {
  109.     int        value;
  110.     ngx_fd_t   fd;
  111.     ngx_url_t  u;

  112.     if (addr == NULL) {
  113.         return NGX_OK;
  114.     }

  115.     ngx_memzero(&u, sizeof(ngx_url_t));

  116.     u.url.data = addr;
  117.     u.url.len = ngx_strlen(addr);
  118.     u.listen = 1;

  119.     if (ngx_parse_url(ngx_cycle->pool, &u) != NGX_OK) {
  120.         if (u.err) {
  121.             ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,
  122.                           "control: %s in \"%V\"", u.err, &u.url);
  123.         }

  124.         return NGX_ERROR;
  125.     }

  126. #if (NGX_HAVE_UNIX_DOMAIN)
  127.     if (u.family == AF_UNIX) {
  128.         ngx_memcpy(ngx_control_unix_path, u.host.data, u.host.len);

  129.     } else if (u.no_port || u.last_port) {
  130.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,
  131.                       "control: invalid port in \"%V\"", &u.url);
  132.         return NGX_ERROR;
  133.     }
  134. #else
  135.     if (u.no_port || u.last_port) {
  136.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,
  137.                       "control: invalid port in \"%V\"", &u.url);
  138.         return NGX_ERROR;
  139.     }
  140. #endif

  141.     if (ngx_control_inherit() == NGX_OK) {
  142.         ngx_control_inherited = 1;
  143.         return NGX_OK;
  144.     }

  145.     fd = ngx_socket(u.family, SOCK_STREAM, 0);

  146.     if (fd == -1) {
  147.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  148.                       "control: " ngx_socket_n " failed");
  149.         return NGX_ERROR;
  150.     }

  151.     if (ngx_nonblocking(fd) == -1) {
  152.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  153.                       "control: " ngx_nonblocking_n " failed");
  154.         ngx_close_socket(fd);
  155.         return NGX_ERROR;
  156.     }

  157.     value = 1;

  158.     if (ioctl(fd, FIOASYNC, &value) == -1) {
  159.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  160.                       "control: ioctl(FIOASYNC) failed");
  161.         ngx_close_socket(fd);
  162.         return NGX_ERROR;
  163.     }

  164.     if (fcntl(fd, F_SETOWN, ngx_pid) == -1) {
  165.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  166.                       "control: fcntl(F_SETOWN) failed");
  167.         ngx_close_socket(fd);
  168.         return NGX_ERROR;
  169.     }

  170.     value = 1;

  171.     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  172.                    (const void *) &value, sizeof(int))
  173.         == -1)
  174.     {
  175.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  176.                       "control: setsockopt(SO_REUSEADDR) failed");
  177.         ngx_close_socket(fd);
  178.         return NGX_ERROR;
  179.     }

  180.     if (bind(fd, &u.sockaddr.sockaddr, u.socklen) == -1) {
  181.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  182.                       "control: bind() failed");
  183.         ngx_close_socket(fd);
  184.         return NGX_ERROR;
  185.     }

  186. #if (NGX_HAVE_UNIX_DOMAIN)
  187.     if (u.family == AF_UNIX) {
  188.         if (chmod(ngx_control_unix_path, S_IRUSR|S_IWUSR) == -1) {
  189.             ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
  190.                           "control: chmod() failed");
  191.             ngx_close_socket(fd);
  192.             return NGX_ERROR;
  193.         }
  194.     }
  195. #endif

  196.     if (listen(fd, NGX_LISTEN_BACKLOG) == -1) {
  197.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  198.                       "control: listen() failed");
  199.         ngx_close_socket(fd);
  200.         return NGX_ERROR;
  201.     }

  202.     ngx_control_pollfd[0].fd = fd;
  203.     ngx_control_pollfd[0].events = POLLIN;
  204.     ngx_control_pollfd_n = 1;

  205.     return NGX_OK;
  206. }


  207. void
  208. ‌ngx_control_uninit(void)
  209. {
  210.     ngx_control_close_sockets();

  211.     if (ngx_control_unix_path[0] && ngx_new_binary == 0
  212.         && (!ngx_control_inherited || ngx_getppid() != ngx_parent))
  213.     {
  214.         if (ngx_delete_file(ngx_control_unix_path) == NGX_FILE_ERROR) {
  215.             ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
  216.                          ngx_delete_file_n " %s failed", ngx_control_unix_path);
  217.         }
  218.     }
  219. }


  220. void
  221. ‌ngx_control_close_sockets(void)
  222. {
  223.     ngx_uint_t  i;

  224.     for (i = 0; i < ngx_control_pollfd_n; i++) {
  225.         ngx_close_socket(ngx_control_pollfd[i].fd);
  226.     }
  227. }


  228. char *
  229. ‌ngx_control_handoff(void)
  230. {
  231.     static u_char  env[sizeof(NGX_CTRL_ENV) + NGX_INT32_LEN + 1];

  232.     if (ngx_control_pollfd_n == 0) {
  233.         return NULL;
  234.     }

  235.     ngx_sprintf(env, NGX_CTRL_ENV "=%d%Z", ngx_control_pollfd[0].fd);

  236.     return (char *) env;
  237. }


  238. void
  239. ‌ngx_control_reown(void)
  240. {
  241.     if (ngx_control_pollfd_n == 0) {
  242.         return;
  243.     }

  244.     if (fcntl(ngx_control_pollfd[0].fd, F_SETOWN, ngx_pid) == -1) {
  245.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
  246.                       "control: fcntl(F_SETOWN) failed");
  247.     }
  248. }


  249. ngx_int_t
  250. ‌ngx_control_handle_events(void)
  251. {
  252.     int                     ready, revents;
  253.     ngx_uint_t              i, reloaded;
  254.     struct pollfd          *pfd;
  255.     ngx_control_request_t  *r;

  256.     ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  257.                    "control: handle events");

  258.     ready = poll(ngx_control_pollfd, ngx_control_pollfd_n, 0);

  259.     if (ready <= 0) {
  260.         if (ready == -1 && ngx_errno != NGX_EINTR) {
  261.             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
  262.                           "control: poll() failed");
  263.             return NGX_ERROR;
  264.         }

  265.         return NGX_OK;
  266.     }

  267.     reloaded = 0;

  268.     for (i = 0; i < ngx_control_pollfd_n; /* void */) {

  269.         r = &ngx_control_requests[i];
  270.         pfd = &ngx_control_pollfd[i];
  271.         revents = pfd->revents;

  272.         ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  273.                        "control: i:%ui fd:%d e:%d re:%d",
  274.                        i, pfd->fd, pfd->events, pfd->revents);

  275.         if (i) {
  276.             r->pool->log = ngx_cycle->log;
  277.         }

  278.         if ((revents & (POLLERR|POLLHUP|POLLNVAL)) && i != 0) {
  279.             ngx_control_close(r);
  280.             continue;
  281.         }

  282.         if (revents & POLLIN) {
  283.             if (i == 0) {
  284.                 ngx_control_handle_accept();
  285.                 i++;
  286.                 continue;
  287.             }

  288.             if (ngx_control_handle_read(r) != NGX_AGAIN) {
  289.                 reloaded |= r->reloaded;
  290.                 ngx_control_close(r);
  291.                 continue;
  292.             }

  293.             reloaded |= r->reloaded;
  294.         }

  295.         if (revents & POLLOUT) {
  296.             if (ngx_control_handle_write(r) != NGX_AGAIN) {
  297.                 ngx_control_close(r);
  298.                 continue;
  299.             }
  300.         }

  301.         i++;
  302.     }

  303.     return reloaded ? NGX_DONE : NGX_OK;
  304. }


  305. static ngx_int_t
  306. ‌ngx_control_inherit(void)
  307. {
  308.     u_char    *env;
  309.     ngx_fd_t   fd;

  310.     env = ngx_control_env;
  311.     if (env == NULL) {
  312.         return NGX_DECLINED;
  313.     }

  314.     fd = ngx_atoi(env, ngx_strlen(env));
  315.     if (fd == NGX_ERROR) {
  316.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  317.                       "control: invalid " NGX_CTRL_ENV " value \"%s\"", env);
  318.         return NGX_ERROR;
  319.     }

  320.     if (fcntl(fd, F_SETOWN, ngx_pid) == -1) {
  321.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
  322.                       "control: fcntl(F_SETOWN) failed");
  323.         ngx_close_socket(fd);
  324.         return NGX_ERROR;
  325.     }

  326.     ngx_control_pollfd[0].fd = fd;
  327.     ngx_control_pollfd[0].events = POLLIN;
  328.     ngx_control_pollfd_n = 1;

  329.     return NGX_OK;
  330. }


  331. static void
  332. ‌ngx_control_close(ngx_control_request_t *r)
  333. {
  334.     ngx_uint_t  i;

  335.     i = r->index;

  336.     ngx_close_socket(r->fd);

  337.     ngx_destroy_pool(r->pool);

  338.     if (i + 1 < ngx_control_pollfd_n) {
  339.         ngx_memmove(&ngx_control_pollfd[i], &ngx_control_pollfd[i + 1],
  340.                     (ngx_control_pollfd_n - (i + 1)) * sizeof(struct pollfd));
  341.         ngx_memmove(&ngx_control_requests[i], &ngx_control_requests[i + 1],
  342.             (ngx_control_pollfd_n - (i + 1)) * sizeof(ngx_control_request_t));

  343.         for (/* void */; i < ngx_control_pollfd_n - 1; i++) {
  344.             ngx_control_requests[i].index--;
  345.         }
  346.     }

  347.     ngx_control_pollfd_n--;
  348. }


  349. static void
  350. ‌ngx_control_handle_accept(void)
  351. {
  352.     int                     value;
  353.     ngx_fd_t                fd;
  354.     ngx_err_t               err;
  355.     struct pollfd          *pfd;
  356.     ngx_control_request_t  *r;

  357.     for ( ;; ) {
  358.         fd = accept(ngx_control_pollfd[0].fd, NULL, NULL);

  359.         if (fd == -1) {
  360.             err = ngx_socket_errno;

  361.             if (err == NGX_EAGAIN) {
  362.                 return;
  363.             }

  364.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, err,
  365.                           "control: accept() failed");

  366.             if (err == NGX_ECONNABORTED) {
  367.                 continue;
  368.             }

  369.             return;
  370.         }

  371.         if (ngx_control_pollfd_n == NGX_CTRL_MAX_FD) {
  372.             ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,
  373.                           "control: too many client connections");
  374.             /* close the oldest connection */
  375.             r = &ngx_control_requests[1];
  376.             r->pool->log = ngx_cycle->log;
  377.             ngx_control_close(r);
  378.         }

  379.         r = &ngx_control_requests[ngx_control_pollfd_n];
  380.         ngx_memzero(r, sizeof(ngx_control_request_t));
  381.         r->fd = fd;

  382.         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  383.                        "control accept fd:%d", r->fd);

  384.         if (fcntl(r->fd, F_SETFD, FD_CLOEXEC) == -1) {
  385.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  386.                           "control: fcntl(FD_CLOEXEC) failed");
  387.             ngx_close_socket(r->fd);
  388.             continue;
  389.         }

  390.         if (ngx_nonblocking(r->fd) == -1) {
  391.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  392.                           "control: " ngx_nonblocking_n " failed");
  393.             ngx_close_socket(r->fd);
  394.             continue;
  395.         }

  396.         value = 1;

  397.         if (ioctl(r->fd, FIOASYNC, &value) == -1) {
  398.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  399.                           "control: ioctl(FIOASYNC) failed");
  400.             ngx_close_socket(r->fd);
  401.             continue;
  402.         }

  403.         if (fcntl(r->fd, F_SETOWN, ngx_pid) == -1) {
  404.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  405.                           "control: fcntl(F_SETOWN) failed");
  406.             ngx_close_socket(r->fd);
  407.             continue;
  408.         }

  409.         pfd = &ngx_control_pollfd[ngx_control_pollfd_n];
  410.         ngx_memzero(pfd, sizeof(struct pollfd));
  411.         pfd->fd = r->fd;
  412.         pfd->events = POLLIN;
  413.         pfd->revents = POLLIN;

  414.         r->pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, ngx_cycle->log);
  415.         if (r->pool == NULL) {
  416.             ngx_close_socket(r->fd);
  417.             continue;
  418.         }

  419.         r->in = ngx_create_temp_buf(r->pool, NGX_CTRL_MAX_REQUEST);
  420.         if (r->in == NULL) {
  421.             ngx_destroy_pool(r->pool);
  422.             ngx_close_socket(r->fd);
  423.             continue;
  424.         }

  425.         r->index = ngx_control_pollfd_n++;
  426.     }
  427. }


  428. static ngx_int_t
  429. ‌ngx_control_handle_read(ngx_control_request_t *r)
  430. {
  431.     ssize_t     n;
  432.     ngx_buf_t  *b;

  433.     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  434.                    "control: read fd:%d", r->fd);

  435.     b = r->in;

  436.     n = recv(r->fd, b->last, b->end - b->last, 0);

  437.     if (n < 0) {
  438.         if (ngx_errno == NGX_EAGAIN) {
  439.             return NGX_AGAIN;
  440.         }

  441.         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  442.                       "control: recv() failed");
  443.         return NGX_ERROR;
  444.     }

  445.     if (n == 0) {
  446.         return NGX_DONE;
  447.     }

  448.     b->last += n;

  449.     switch (ngx_control_parse_request_line(r)) {

  450.     case NGX_AGAIN:
  451.         if (b->last == b->end) {
  452.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  453.                           "control: request too large");
  454.             ngx_control_pollfd[r->index].events &= ~POLLIN;
  455.             return ngx_control_send_response(r, NGX_CTRL_BAD_REQUEST, NULL);
  456.         }

  457.         return NGX_AGAIN;

  458.     case NGX_DECLINED:
  459.         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  460.                       "control: failed to parse request");
  461.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  462.         return ngx_control_send_response(r, NGX_CTRL_BAD_REQUEST, NULL);

  463.     case NGX_DONE:
  464.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  465.         return ngx_control_content(r);

  466.     default:
  467.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  468.         return NGX_ERROR;
  469.     }
  470. }


  471. static ngx_int_t
  472. ‌ngx_control_content(ngx_control_request_t *r)
  473. {
  474.     u_char  *p;
  475.     size_t   len;

  476.     static const char  *root_endpoints[] = { "1" };
  477.     static const char  *api_endpoints[] = { "control", "nginx" };
  478.     static const char  *control_endpoints[] = { "processes", "config" };

  479.     p = r->path.data;
  480.     len = r->path.len;

  481.     if (len > 1 && p[len - 1] == '/') {
  482.         r->path.len = --len;
  483.     }

  484.     /* the path always starts with "/" */

  485.     switch (len) {
  486.     case 1:
  487.         if (r->method != NGX_CTRL_GET) {
  488.             return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  489.         }

  490.         return ngx_control_api_endpoints(r, root_endpoints, 1);

  491.     case 2:
  492.         if (ngx_strncmp(p, "/1", len) == 0) {
  493.             if (r->method != NGX_CTRL_GET) {
  494.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  495.             }

  496.             return ngx_control_api_endpoints(r, api_endpoints, 2);
  497.         }

  498.         break;

  499.     case 8:
  500.         if (ngx_strncmp(p, "/1/nginx", len) == 0) {
  501.             if (r->method != NGX_CTRL_GET) {
  502.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  503.             }

  504.             return ngx_control_show_version(r);
  505.         }

  506.         break;

  507.     case 10:
  508.         if (ngx_strncmp(p, "/1/control", len) == 0) {
  509.             if (r->method != NGX_CTRL_GET) {
  510.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  511.             }

  512.             return ngx_control_api_endpoints(r, control_endpoints, 2);
  513.         }

  514.         break;

  515.     case 17:
  516.         if (ngx_strncmp(p, "/1/control/config", len) == 0) {
  517.             if (r->method == NGX_CTRL_PATCH) {
  518.                 return ngx_control_reload_config(r);
  519.             }

  520.             if (r->method == NGX_CTRL_GET) {
  521.                 return ngx_control_print_config(r);
  522.             }

  523.             return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  524.         }

  525.         break;

  526.     case 20:
  527.         if (ngx_strncmp(p, "/1/control/processes", len) == 0) {
  528.             if (r->method != NGX_CTRL_GET) {
  529.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  530.             }

  531.             return ngx_control_show_processes(r);
  532.         }

  533.         break;
  534.     }

  535.     return ngx_control_send_response(r, NGX_CTRL_NOT_FOUND, NULL);
  536. }


  537. static char *
  538. ‌ngx_control_status_text(ngx_uint_t code)
  539. {
  540.     switch (code) {
  541.     case NGX_CTRL_OK:
  542.         return "OK";
  543.     case NGX_CTRL_BAD_REQUEST:
  544.         return "Bad Request";
  545.     case NGX_CTRL_NOT_FOUND:
  546.         return "Not Found";
  547.     case NGX_CTRL_NOT_ALLOWED:
  548.         return "Not Allowed";
  549.     case NGX_CTRL_UNPROCESSABLE:
  550.         return "Unprocessable Entity";
  551.     case NGX_CTRL_INTERNAL_ERROR:
  552.         return "Internal Server Error";
  553.     default:
  554.         return "";
  555.     }
  556. }


  557. static ngx_int_t
  558. ‌ngx_control_send_response(ngx_control_request_t *r, ngx_uint_t code,
  559.     ngx_buf_t *body)
  560. {
  561.     char         *status;
  562.     ngx_uint_t    n;
  563.     ngx_chain_t  *c;

  564.     const char  response_fmt[] =
  565.         "HTTP/1.1 %03ui %s\r\n"
  566.         "Server: " NGINX_VER "\r\n"
  567.         "Connection: close\r\n"
  568.         "Content-Type: application/json\r\n"
  569.         "Content-Length: %ui\r\n"
  570.         "\r\n";

  571.     c = ngx_alloc_chain_link(r->pool);
  572.     if (c == NULL) {
  573.         return NGX_ERROR;
  574.     }

  575.     status = ngx_control_status_text(code);

  576.     c->buf = ngx_create_temp_buf(r->pool,
  577.                 sizeof(response_fmt) + 2 * NGX_INT_T_LEN + ngx_strlen(status));
  578.     if (c->buf == NULL) {
  579.         return NGX_ERROR;
  580.     }

  581.     c->next = NULL;

  582.     n = body ? body->last - body->pos : 0;

  583.     c->buf->last = ngx_sprintf(c->buf->pos, response_fmt, code, status, n);

  584.     if (body) {
  585.         c->next = ngx_alloc_chain_link(r->pool);
  586.         if (c->next == NULL) {
  587.             return NGX_ERROR;
  588.         }

  589.         c->next->buf = body;
  590.         c->next->next = NULL;
  591.     }

  592.     r->out = c;

  593.     return ngx_control_handle_write(r);
  594. }


  595. static ngx_int_t
  596. ‌ngx_control_handle_write(ngx_control_request_t *r)
  597. {
  598.     ssize_t       n;
  599.     ngx_buf_t    *b;
  600.     ngx_chain_t  *c;

  601.     for (c = r->out; c != NULL; c = c->next) {
  602.         b = c->buf;

  603.         while (b && b->pos < b->last) {
  604.             n = send(r->fd, b->pos, b->last - b->pos, 0);

  605.             if (n <= 0) {
  606.                 if (n == -1 && ngx_errno == NGX_EAGAIN) {
  607.                     ngx_control_pollfd[r->index].events |= POLLOUT;
  608.                     return NGX_AGAIN;
  609.                 }

  610.                 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  611.                               "control: send() failed");
  612.                 return NGX_ERROR;
  613.             }

  614.             b->pos += n;

  615.             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  616.                            "control: wrote %i bytes", n);
  617.         }
  618.     }

  619.     return NGX_DONE;
  620. }


  621. static ngx_int_t
  622. ‌ngx_control_send_json(ngx_control_request_t *r, ngx_int_t code,
  623.     ngx_data_item_t *obj)
  624. {
  625.     ngx_buf_t  *b;

  626.     b = ngx_json_render(r->pool, obj);
  627.     if (b == NULL) {
  628.         return NGX_ERROR;
  629.     }

  630.     return ngx_control_send_response(r, code, b);
  631. }


  632. static ngx_int_t
  633. ‌ngx_control_api_endpoints(ngx_control_request_t *r, const char **paths,
  634.     ngx_int_t npaths)
  635. {
  636.     ngx_int_t         i;
  637.     ngx_str_t         p;
  638.     ngx_data_item_t  *json, *path;

  639.     if (r->method != NGX_CTRL_GET) {
  640.         return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  641.     }

  642.     json = ngx_data_new_list(r->pool);
  643.     if (json == NULL) {
  644.         return NGX_ERROR;
  645.     }

  646.     for (i = 0; i < npaths; i++) {
  647.         p.data = (u_char *) paths[i];
  648.         p.len = ngx_strlen(paths[i]);

  649.         path = ngx_data_string_handler((uintptr_t) &p, r->pool, NULL);
  650.         if (path == NULL) {
  651.             return NGX_ERROR;
  652.         }

  653.         ngx_data_add_item(json, NULL, path);
  654.     }

  655.     return ngx_control_send_json(r, NGX_CTRL_OK, (ngx_data_item_t *) json);
  656. }


  657. static ngx_int_t
  658. ‌ngx_control_show_processes(ngx_control_request_t *r)
  659. {
  660.     ngx_int_t               i;
  661.     ngx_data_item_t        *process_list, *obj;
  662.     ngx_control_process_t   process;

  663.     process_list = ngx_data_new_list(r->pool);
  664.     if (process_list == NULL) {
  665.         return NGX_ERROR;
  666.     }

  667.     for (i = 0; i < ngx_last_process; i++) {
  668.         if (ngx_processes[i].pid == -1) {
  669.             continue;
  670.         }

  671.         process.name.data = (u_char *) ngx_processes[i].name;
  672.         process.name.len = ngx_strlen(process.name.data);
  673.         process.pid = ngx_processes[i].pid;
  674.         process.exiting = ngx_processes[i].exiting;

  675.         obj = ngx_data_obj_handler((uintptr_t) ngx_control_process_fields,
  676.                                    r->pool, &process);
  677.         if (obj == NULL) {
  678.             return NGX_ERROR;
  679.         }

  680.         ngx_data_add_item(process_list, NULL, obj);
  681.     }

  682.     return ngx_control_send_json(r, NGX_CTRL_OK, process_list);
  683. }


  684. static ngx_int_t
  685. ‌ngx_control_show_version(ngx_control_request_t *r)
  686. {
  687.     ngx_data_item_t  *resp;

  688.     resp = ngx_data_obj_handler((uintptr_t) ngx_control_status_fields, r->pool,
  689.                                 NULL);
  690.     if (resp == NULL) {
  691.         return NGX_ERROR;
  692.     }

  693.     return ngx_control_send_json(r, NGX_CTRL_OK, resp);
  694. }


  695. static ngx_int_t
  696. ‌ngx_control_print_config(ngx_control_request_t *r)
  697. {
  698.     ngx_uint_t        i;
  699.     ngx_keyval_t      item;
  700.     ngx_data_item_t  *list, *obj;
  701.     ngx_conf_dump_t  *dump;

  702.     list = ngx_data_new_list(r->pool);
  703.     if (list == NULL) {
  704.         return NGX_ERROR;
  705.     }

  706.     dump = ngx_cycle->config_dump.elts;

  707.     for (i = 0; i < ngx_cycle->config_dump.nelts; i++) {

  708.         item.key = dump[i].name;
  709.         item.value.data = dump[i].buffer->pos;
  710.         item.value.len = dump[i].buffer->last - dump[i].buffer->pos;

  711.         obj = ngx_data_obj_handler((uintptr_t) ngx_control_config_fields,
  712.                                    r->pool, &item);
  713.         if (obj == NULL) {
  714.             return NGX_ERROR;
  715.         }

  716.         ngx_data_add_item(list, NULL, obj);
  717.     }

  718.     return ngx_control_send_json(r, NGX_CTRL_OK, list);
  719. }


  720. static ngx_data_item_t *
  721. ‌ngx_control_json_logs(ngx_array_t *logs)
  722. {
  723.     ngx_str_t        *l;
  724.     ngx_uint_t        i;
  725.     ngx_data_item_t  *obj, *log_list, *log;

  726.     static ngx_str_t  logs_field = ngx_string("logs");

  727.     obj = ngx_data_new_object(logs->pool);
  728.     if (obj == NULL) {
  729.         return NULL;
  730.     }

  731.     log_list = ngx_data_new_list(logs->pool);
  732.     if (log_list == NULL) {
  733.         return NULL;
  734.     }

  735.     l = logs->elts;
  736.     for (i = 0; i < logs->nelts; i++) {
  737.         log = ngx_data_string_handler((uintptr_t) &l[i], logs->pool, NULL);
  738.         if (log == NULL) {
  739.             return NULL;
  740.         }

  741.         ngx_data_add_item(log_list, NULL, log);
  742.     }

  743.     ngx_data_add_item(obj, &logs_field, log_list);

  744.     return obj;
  745. }


  746. static ngx_int_t
  747. ‌ngx_control_reload_config(ngx_control_request_t *r)
  748. {
  749.     ngx_int_t         status;
  750.     ngx_log_t         log, *tmp;
  751.     ngx_cycle_t      *cycle;
  752.     ngx_array_t       log_store;
  753.     ngx_data_item_t  *obj;

  754.     status = NGX_CTRL_OK;

  755.     if (ngx_array_init(&log_store, r->pool, 3, sizeof(ngx_str_t)) != NGX_OK) {
  756.         return NGX_ERROR;
  757.     }

  758.     ngx_memzero(&log, sizeof(ngx_log_t));
  759.     log.wdata = &log_store;
  760.     log.writer = ngx_control_log_capture;
  761.     log.log_level = NGX_LOG_DEBUG;

  762.     tmp = ngx_cycle->log;
  763.     log.next = tmp;
  764.     ngx_cycle->log = &log;
  765.     cycle = ngx_init_cycle((ngx_cycle_t *) ngx_cycle);

  766.     if (cycle == NULL) {
  767.         status = NGX_CTRL_UNPROCESSABLE;
  768.         ngx_cycle->log = tmp;

  769.     } else {
  770.         ngx_cycle = cycle;
  771.         r->reloaded = 1;
  772.         r->pool->log = ngx_cycle->log;
  773.     }

  774.     obj = ngx_control_json_logs(&log_store);
  775.     if (obj == NULL) {
  776.         return NGX_ERROR;
  777.     }

  778.     return ngx_control_send_json(r, status, obj);
  779. }


  780. static void
  781. ‌ngx_control_log_capture(ngx_log_t *log, ngx_uint_t lvl, u_char *buf, size_t len)
  782. {
  783.     u_char       *log_data;
  784.     ngx_str_t    *log_str;
  785.     ngx_array_t  *store;

  786.     if (lvl > NGX_LOG_WARN) {
  787.         return;
  788.     }

  789.     store = log->wdata;

  790.     log_data = ngx_pnalloc(store->pool, len);
  791.     if (log_data == NULL) {
  792.         return;
  793.     }

  794.     log_str = ngx_array_push(store);
  795.     if (log_str == NULL) {
  796.         return;
  797.     }

  798.     ngx_memcpy(log_data, buf, len);

  799.     log_str->data = log_data;
  800.     log_str->len = len;
  801. }


  802. static ngx_int_t
  803. ‌ngx_control_parse_request_line(ngx_control_request_t *r)
  804. {
  805.     static uint32_t  usual[] = {
  806.         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
  807.                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
  808.         0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
  809.                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
  810.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  811.                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
  812.         0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */
  813.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  814.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  815.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  816.         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  817.     };

  818.     u_char      c, ch, *p;
  819.     ngx_buf_t  *b;
  820. ‌    enum {
  821.         sw_start = 0,
  822.         sw_method,
  823.         sw_spaces_before_uri,
  824.         sw_schema,
  825.         sw_schema_slash,
  826.         sw_schema_slash_slash,
  827.         sw_spaces_before_host,
  828.         sw_host_start,
  829.         sw_host,
  830.         sw_host_end,
  831.         sw_host_ip_literal,
  832.         sw_port_start,
  833.         sw_port,
  834.         sw_uri,
  835.         sw_http_,
  836.         sw_http_H,
  837.         sw_http_HT,
  838.         sw_http_HTT,
  839.         sw_http_HTTP,
  840.         sw_first_major_digit,
  841.         sw_major_digit,
  842.         sw_first_minor_digit,
  843.         sw_minor_digit,
  844.         sw_almost_done
  845.     } state;

  846.     b = r->in;
  847.     state = r->state;

  848.     for (p = b->pos; p < b->last; p++) {
  849.         ch = *p;

  850.         switch (state) {
  851.         /* HTTP methods: GET, PATCH */
  852.         case sw_start:
  853.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  854.                 return NGX_DECLINED;
  855.             }

  856.             state = sw_method;

  857.             break;

  858.         case sw_method:
  859.             if (ch == ' ') {
  860.                 state = sw_spaces_before_uri;

  861.                 switch (p - b->start) {
  862.                 case 3:
  863.                     if (ngx_strncmp(b->start, "GET", 3) == 0) {
  864.                         r->method = NGX_CTRL_GET;
  865.                         break;
  866.                     }
  867.                     break;

  868.                 case 5:
  869.                     if (ngx_strncmp(b->start, "PATCH", 5) == 0) {
  870.                         r->method = NGX_CTRL_PATCH;
  871.                         break;
  872.                     }
  873.                     break;
  874.                 }

  875.                 break;
  876.             }

  877.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  878.                 return NGX_DECLINED;
  879.             }

  880.             break;

  881.         /* space* before URI */
  882.         case sw_spaces_before_uri:
  883.             if (ch == '/') {
  884.                 r->path.data = p;
  885.                 state = sw_uri;
  886.                 break;
  887.             }

  888.             c = (u_char) (ch | 0x20);
  889.             if (c >= 'a' && c <= 'z') {
  890.                 state = sw_schema;
  891.                 break;
  892.             }

  893.             if (ch != ' ') {
  894.                 return NGX_DECLINED;
  895.             }

  896.             break;

  897.         case sw_schema:
  898.             c = (u_char) (ch | 0x20);
  899.             if (c >= 'a' && c <= 'z') {
  900.                 break;
  901.             }

  902.             if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
  903.             {
  904.                 break;
  905.             }

  906.             if (ch != ':') {
  907.                 return NGX_DECLINED;
  908.             }

  909.             state = sw_schema_slash;
  910.             break;

  911.         case sw_schema_slash:
  912.             if (ch != '/') {
  913.                 return NGX_DECLINED;
  914.             }

  915.             state = sw_schema_slash_slash;
  916.             break;

  917.         case sw_schema_slash_slash:
  918.             if (ch != '/') {
  919.                 return NGX_DECLINED;
  920.             }

  921.             state = sw_host_start;
  922.             break;

  923.         case sw_spaces_before_host:
  924.             if (ch == ' ') {
  925.                 break;
  926.             }
  927.             /* fall through */

  928.         case sw_host_start:
  929.             if (ch == '[') {
  930.                 state = sw_host_ip_literal;
  931.                 break;
  932.             }

  933.             state = sw_host;
  934.             /* fall through */

  935.         case sw_host:
  936.             c = (u_char) (ch | 0x20);
  937.             if (c >= 'a' && c <= 'z') {
  938.                 break;
  939.             }

  940.             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
  941.                 break;
  942.             }
  943.             /* fall through */

  944.         case sw_host_end:
  945.             if (ch == ':') {
  946.                 state = sw_port_start;
  947.                 break;
  948.             }

  949.             /* if we supported CONNECT verb we would need to decline here */

  950.             switch (ch) {
  951.             case '/':
  952.                 r->path.data = p;
  953.                 state = sw_uri;
  954.                 break;
  955.             /* empty path cases */
  956.             case '?':
  957.                 ngx_str_set(&r->path, "/");
  958.                 state = sw_uri;
  959.                 break;
  960.             case ' ':
  961.                 ngx_str_set(&r->path, "/");
  962.                 state = sw_http_;
  963.                 break;
  964.             }
  965.             break;

  966.         case sw_host_ip_literal:
  967.             if (ch >= '0' && ch <= '9') {
  968.                 break;
  969.             }

  970.             c = (u_char) (ch | 0x20);
  971.             if (c >= 'a' && c <= 'z') {
  972.                 break;
  973.             }

  974.             switch (ch) {
  975.             case ':':
  976.                 break;
  977.             case ']':
  978.                 state = sw_host_end;
  979.                 break;
  980.             case '-':
  981.             case '.':
  982.             case '_':
  983.             case '~':
  984.                 /* unreserved */
  985.                 break;
  986.             case '!':
  987.             case '$':
  988.             case '&':
  989.             case '\'':
  990.             case '(':
  991.             case ')':
  992.             case '*':
  993.             case '+':
  994.             case ',':
  995.             case ';':
  996.             case '=':
  997.                 /* sub-delims */
  998.                 break;
  999.             default:
  1000.                 return NGX_DECLINED;
  1001.             }
  1002.             break;

  1003.         case sw_port_start:
  1004.             state = sw_port;

  1005.             if (ch >= '0' && ch <= '9') {
  1006.                 break;
  1007.             }

  1008.             /* if we supported CONNECT verb we would need to decline here */

  1009.             /* fall through */

  1010.         case sw_port:
  1011.             if (ch >= '0' && ch <= '9') {
  1012.                 break;
  1013.             }

  1014.             switch (ch) {
  1015.             case '/':
  1016.                 r->path.data = p;
  1017.                 state = sw_uri;
  1018.                 break;
  1019.             /* empty path cases */
  1020.             case '?':
  1021.                 ngx_str_set(&r->path, "/");
  1022.                 state = sw_uri;
  1023.                 break;
  1024.             case ' ':
  1025.                 ngx_str_set(&r->path, "/");
  1026.                 state = sw_http_;
  1027.                 break;
  1028.             default:
  1029.                 return NGX_DECLINED;
  1030.             }
  1031.             break;

  1032.         /* URI */
  1033.         case sw_uri:
  1034.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1035.                 break;
  1036.             }

  1037.             switch (ch) {
  1038.             case ' ':
  1039.                 state = sw_http_;
  1040.                 /* fall through */
  1041.             case '?':
  1042.                 if (r->path.len == 0) {
  1043.                     r->path.len = p - r->path.data;
  1044.                 }
  1045.                 break;

  1046.             case '.':
  1047.             case '%':
  1048.             case '#':
  1049.             case '+':
  1050.             case 0x7f:
  1051.                 return NGX_DECLINED;
  1052.             default:
  1053.                 if (ch < 0x20) {
  1054.                     return NGX_DECLINED;
  1055.                 }
  1056.                 break;
  1057.             }
  1058.             break;

  1059.         /* space+ after URI */
  1060.         case sw_http_:
  1061.             switch (ch) {
  1062.             case ' ':
  1063.                 break;
  1064.             case 'H':
  1065.                 state = sw_http_H;
  1066.                 break;
  1067.             default:
  1068.                 return NGX_DECLINED;
  1069.             }
  1070.             break;

  1071.         case sw_http_H:
  1072.             switch (ch) {
  1073.             case 'T':
  1074.                 state = sw_http_HT;
  1075.                 break;
  1076.             default:
  1077.                 return NGX_DECLINED;
  1078.             }
  1079.             break;

  1080.         case sw_http_HT:
  1081.             switch (ch) {
  1082.             case 'T':
  1083.                 state = sw_http_HTT;
  1084.                 break;
  1085.             default:
  1086.                 return NGX_DECLINED;
  1087.             }
  1088.             break;

  1089.         case sw_http_HTT:
  1090.             switch (ch) {
  1091.             case 'P':
  1092.                 state = sw_http_HTTP;
  1093.                 break;
  1094.             default:
  1095.                 return NGX_DECLINED;
  1096.             }
  1097.             break;

  1098.         case sw_http_HTTP:
  1099.             switch (ch) {
  1100.             case '/':
  1101.                 state = sw_first_major_digit;
  1102.                 break;
  1103.             default:
  1104.                 return NGX_DECLINED;
  1105.             }
  1106.             break;

  1107.         /* first digit of major HTTP version */
  1108.         case sw_first_major_digit:
  1109.             if (ch != '1') {
  1110.                 return NGX_DECLINED;
  1111.             }

  1112.             state = sw_major_digit;
  1113.             break;

  1114.         /* major HTTP version or dot */
  1115.         case sw_major_digit:
  1116.             if (ch == '.') {
  1117.                 state = sw_first_minor_digit;
  1118.                 break;
  1119.             } else {
  1120.                 /* major versions other than 1 are not supported */
  1121.                 return NGX_DECLINED;
  1122.             }

  1123.         /* first digit of minor HTTP version */
  1124.         case sw_first_minor_digit:
  1125.             if (ch != '0' && ch != '1') {
  1126.                 return NGX_DECLINED;
  1127.             }

  1128.             state = sw_minor_digit;
  1129.             break;

  1130.         /* minor HTTP version or end of request line */
  1131.         case sw_minor_digit:
  1132.             if (ch == CR) {
  1133.                 state = sw_almost_done;
  1134.                 break;

  1135.             } else if (ch == LF) {
  1136.                 goto done;

  1137.             } else {
  1138.                 /* support only 1.1 or 1.0 */
  1139.                 return NGX_DECLINED;
  1140.             }

  1141.         /* end of request line */
  1142.         case sw_almost_done:
  1143.             switch (ch) {
  1144.             case LF:
  1145.                 goto done;
  1146.             default:
  1147.                 return NGX_DECLINED;
  1148.             }
  1149.         }
  1150.     }

  1151.     b->pos = p;
  1152.     r->state = state;

  1153.     return NGX_AGAIN;

  1154. done:
  1155.     b->pos = p + 1;
  1156.     r->state = sw_start;

  1157.     return NGX_DONE;
  1158. }