src/os/unix/ngx_control.c - nginx-1.31.7 nginx/ @ 939334eff

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        reuseaddr;
  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 (fcntl(fd, F_SETFL, O_ASYNC|O_NONBLOCK) == -1) {
  152.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  153.                       "control: fcntl(O_ASYNC|O_NONBLOCK) failed");
  154.         ngx_close_socket(fd);
  155.         return NGX_ERROR;
  156.     }

  157.     if (fcntl(fd, F_SETOWN, ngx_pid) == -1) {
  158.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  159.                       "control: fcntl(F_SETOWN) failed");
  160.         ngx_close_socket(fd);
  161.         return NGX_ERROR;
  162.     }

  163.     reuseaddr = 1;

  164.     if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  165.                    (const void *) &reuseaddr, sizeof(int))
  166.         == -1)
  167.     {
  168.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  169.                       "control: setsockopt(SO_REUSEADDR) failed");
  170.         ngx_close_socket(fd);
  171.         return NGX_ERROR;
  172.     }

  173.     if (bind(fd, &u.sockaddr.sockaddr, u.socklen) == -1) {
  174.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  175.                       "control: bind() failed");
  176.         ngx_close_socket(fd);
  177.         return NGX_ERROR;
  178.     }

  179. #if (NGX_HAVE_UNIX_DOMAIN)
  180.     if (u.family == AF_UNIX) {
  181.         if (chmod(ngx_control_unix_path, S_IRUSR|S_IWUSR) == -1) {
  182.             ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
  183.                           "control: chmod() failed");
  184.             ngx_close_socket(fd);
  185.             return NGX_ERROR;
  186.         }
  187.     }
  188. #endif

  189.     if (listen(fd, NGX_LISTEN_BACKLOG) == -1) {
  190.         ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_socket_errno,
  191.                       "control: listen() failed");
  192.         ngx_close_socket(fd);
  193.         return NGX_ERROR;
  194.     }

  195.     ngx_control_pollfd[0].fd = fd;
  196.     ngx_control_pollfd[0].events = POLLIN;
  197.     ngx_control_pollfd_n = 1;

  198.     return NGX_OK;
  199. }


  200. void
  201. ‌ngx_control_uninit(void)
  202. {
  203.     ngx_control_close_sockets();

  204.     if (ngx_control_unix_path[0] && ngx_new_binary == 0
  205.         && (!ngx_control_inherited || ngx_getppid() != ngx_parent))
  206.     {
  207.         if (ngx_delete_file(ngx_control_unix_path) == NGX_FILE_ERROR) {
  208.             ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,
  209.                          ngx_delete_file_n " %s failed", ngx_control_unix_path);
  210.         }
  211.     }
  212. }


  213. void
  214. ‌ngx_control_close_sockets(void)
  215. {
  216.     ngx_uint_t  i;

  217.     for (i = 0; i < ngx_control_pollfd_n; i++) {
  218.         ngx_close_socket(ngx_control_pollfd[i].fd);
  219.     }
  220. }


  221. char *
  222. ‌ngx_control_handoff(void)
  223. {
  224.     static u_char  env[sizeof(NGX_CTRL_ENV) + NGX_INT32_LEN + 1];

  225.     if (ngx_control_pollfd_n == 0) {
  226.         return NULL;
  227.     }

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

  229.     return (char *) env;
  230. }


  231. void
  232. ‌ngx_control_reown(void)
  233. {
  234.     if (ngx_control_pollfd_n == 0) {
  235.         return;
  236.     }

  237.     if (fcntl(ngx_control_pollfd[0].fd, F_SETOWN, ngx_pid) == -1) {
  238.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
  239.                       "control: fcntl(F_SETOWN) failed");
  240.     }
  241. }


  242. ngx_int_t
  243. ‌ngx_control_handle_events(void)
  244. {
  245.     int                     ready, revents;
  246.     ngx_uint_t              i, reloaded;
  247.     struct pollfd          *pfd;
  248.     ngx_control_request_t  *r;

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

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

  252.     if (ready <= 0) {
  253.         if (ready == -1 && ngx_errno != NGX_EINTR) {
  254.             ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,
  255.                           "control: poll() failed");
  256.             return NGX_ERROR;
  257.         }

  258.         return NGX_OK;
  259.     }

  260.     reloaded = 0;

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

  262.         r = &ngx_control_requests[i];
  263.         pfd = &ngx_control_pollfd[i];
  264.         revents = pfd->revents;

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

  268.         if (i) {
  269.             r->pool->log = ngx_cycle->log;
  270.         }

  271.         if ((revents & (POLLERR|POLLHUP|POLLNVAL)) && i != 0) {
  272.             ngx_control_close(r);
  273.             continue;
  274.         }

  275.         if (revents & POLLIN) {
  276.             if (i == 0) {
  277.                 ngx_control_handle_accept();
  278.                 i++;
  279.                 continue;
  280.             }

  281.             if (ngx_control_handle_read(r) != NGX_AGAIN) {
  282.                 reloaded |= r->reloaded;
  283.                 ngx_control_close(r);
  284.                 continue;
  285.             }

  286.             reloaded |= r->reloaded;
  287.         }

  288.         if (revents & POLLOUT) {
  289.             if (ngx_control_handle_write(r) != NGX_AGAIN) {
  290.                 ngx_control_close(r);
  291.                 continue;
  292.             }
  293.         }

  294.         i++;
  295.     }

  296.     return reloaded ? NGX_DONE : NGX_OK;
  297. }


  298. static ngx_int_t
  299. ‌ngx_control_inherit(void)
  300. {
  301.     u_char    *env;
  302.     ngx_fd_t   fd;

  303.     env = ngx_control_env;
  304.     if (env == NULL) {
  305.         return NGX_DECLINED;
  306.     }

  307.     fd = ngx_atoi(env, ngx_strlen(env));
  308.     if (fd == NGX_ERROR) {
  309.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
  310.                       "control: invalid " NGX_CTRL_ENV " value \"%s\"", env);
  311.         return NGX_ERROR;
  312.     }

  313.     if (fcntl(fd, F_SETOWN, ngx_pid) == -1) {
  314.         ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
  315.                       "control: fcntl(F_SETOWN) failed");
  316.         ngx_close_socket(fd);
  317.         return NGX_ERROR;
  318.     }

  319.     ngx_control_pollfd[0].fd = fd;
  320.     ngx_control_pollfd[0].events = POLLIN;
  321.     ngx_control_pollfd_n = 1;

  322.     return NGX_OK;
  323. }


  324. static void
  325. ‌ngx_control_close(ngx_control_request_t *r)
  326. {
  327.     ngx_uint_t  i;

  328.     i = r->index;

  329.     ngx_close_socket(r->fd);

  330.     ngx_destroy_pool(r->pool);

  331.     if (i + 1 < ngx_control_pollfd_n) {
  332.         ngx_memmove(&ngx_control_pollfd[i], &ngx_control_pollfd[i + 1],
  333.                     (ngx_control_pollfd_n - (i + 1)) * sizeof(struct pollfd));
  334.         ngx_memmove(&ngx_control_requests[i], &ngx_control_requests[i + 1],
  335.             (ngx_control_pollfd_n - (i + 1)) * sizeof(ngx_control_request_t));

  336.         for (/* void */; i < ngx_control_pollfd_n - 1; i++) {
  337.             ngx_control_requests[i].index--;
  338.         }
  339.     }

  340.     ngx_control_pollfd_n--;
  341. }


  342. static void
  343. ‌ngx_control_handle_accept(void)
  344. {
  345.     ngx_fd_t                fd;
  346.     ngx_err_t               err;
  347.     struct pollfd          *pfd;
  348.     ngx_control_request_t  *r;

  349.     for ( ;; ) {
  350.         fd = accept(ngx_control_pollfd[0].fd, NULL, NULL);

  351.         if (fd == -1) {
  352.             err = ngx_socket_errno;

  353.             if (err == NGX_EAGAIN) {
  354.                 return;
  355.             }

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

  358.             if (err == NGX_ECONNABORTED) {
  359.                 continue;
  360.             }

  361.             return;
  362.         }

  363.         if (ngx_control_pollfd_n == NGX_CTRL_MAX_FD) {
  364.             ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,
  365.                           "control: too many client connections");
  366.             /* close the oldest connection */
  367.             r = &ngx_control_requests[1];
  368.             r->pool->log = ngx_cycle->log;
  369.             ngx_control_close(r);
  370.         }

  371.         r = &ngx_control_requests[ngx_control_pollfd_n];
  372.         ngx_memzero(r, sizeof(ngx_control_request_t));
  373.         r->fd = fd;

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

  376.         if (fcntl(r->fd, F_SETFD, FD_CLOEXEC) == -1) {
  377.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  378.                           "control: fcntl(FD_CLOEXEC) failed");
  379.             ngx_close_socket(r->fd);
  380.             continue;
  381.         }

  382.         if (fcntl(r->fd, F_SETFL, O_ASYNC|O_NONBLOCK) == -1) {
  383.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  384.                           "control: fcntl(O_ASYNC|O_NONBLOCK) failed");
  385.             ngx_close_socket(r->fd);
  386.             continue;
  387.         }

  388.         if (fcntl(r->fd, F_SETOWN, ngx_pid) == -1) {
  389.             ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  390.                           "control: fcntl(F_SETOWN) failed");
  391.             ngx_close_socket(r->fd);
  392.             continue;
  393.         }

  394.         pfd = &ngx_control_pollfd[ngx_control_pollfd_n];
  395.         ngx_memzero(pfd, sizeof(struct pollfd));
  396.         pfd->fd = r->fd;
  397.         pfd->events = POLLIN;
  398.         pfd->revents = POLLIN;

  399.         r->pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, ngx_cycle->log);
  400.         if (r->pool == NULL) {
  401.             ngx_close_socket(r->fd);
  402.             continue;
  403.         }

  404.         r->in = ngx_create_temp_buf(r->pool, NGX_CTRL_MAX_REQUEST);
  405.         if (r->in == NULL) {
  406.             ngx_destroy_pool(r->pool);
  407.             ngx_close_socket(r->fd);
  408.             continue;
  409.         }

  410.         r->index = ngx_control_pollfd_n++;
  411.     }
  412. }


  413. static ngx_int_t
  414. ‌ngx_control_handle_read(ngx_control_request_t *r)
  415. {
  416.     ssize_t     n;
  417.     ngx_buf_t  *b;

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

  420.     b = r->in;

  421.     if (b->last == b->end) {
  422.         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  423.                       "control: request too large");
  424.         return NGX_ERROR;
  425.     }

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

  427.     if (n < 0) {
  428.         if (ngx_errno == NGX_EAGAIN) {
  429.             return NGX_AGAIN;
  430.         }

  431.         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  432.                       "control: recv() failed");
  433.         return NGX_ERROR;
  434.     }

  435.     if (n == 0) {
  436.         return NGX_DONE;
  437.     }

  438.     b->last += n;

  439.     switch (ngx_control_parse_request_line(r)) {

  440.     case NGX_AGAIN:
  441.         return NGX_AGAIN;

  442.     case NGX_DECLINED:
  443.         ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,
  444.                       "control: failed to parse request");
  445.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  446.         return ngx_control_send_response(r, NGX_CTRL_BAD_REQUEST, NULL);

  447.     case NGX_DONE:
  448.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  449.         return ngx_control_content(r);

  450.     default:
  451.         ngx_control_pollfd[r->index].events &= ~POLLIN;
  452.         return NGX_ERROR;
  453.     }
  454. }


  455. static ngx_int_t
  456. ‌ngx_control_content(ngx_control_request_t *r)
  457. {
  458.     u_char  *p;
  459.     size_t   len;

  460.     static const char  *root_endpoints[] = { "1" };
  461.     static const char  *api_endpoints[] = { "control", "nginx" };
  462.     static const char  *control_endpoints[] = { "processes", "config" };

  463.     p = r->path.data;
  464.     len = r->path.len;

  465.     if (len > 1 && p[len - 1] == '/') {
  466.         r->path.len = --len;
  467.     }

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

  469.     switch (len) {
  470.     case 1:
  471.         if (r->method != NGX_CTRL_GET) {
  472.             return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  473.         }

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

  475.     case 2:
  476.         if (ngx_strncmp(p, "/1", len) == 0) {
  477.             if (r->method != NGX_CTRL_GET) {
  478.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  479.             }

  480.             return ngx_control_api_endpoints(r, api_endpoints, 2);
  481.         }

  482.         break;

  483.     case 8:
  484.         if (ngx_strncmp(p, "/1/nginx", len) == 0) {
  485.             if (r->method != NGX_CTRL_GET) {
  486.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  487.             }

  488.             return ngx_control_show_version(r);
  489.         }

  490.         break;

  491.     case 10:
  492.         if (ngx_strncmp(p, "/1/control", 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, control_endpoints, 2);
  497.         }

  498.         break;

  499.     case 17:
  500.         if (ngx_strncmp(p, "/1/control/config", len) == 0) {
  501.             if (r->method == NGX_CTRL_PATCH) {
  502.                 return ngx_control_reload_config(r);
  503.             }

  504.             if (r->method == NGX_CTRL_GET) {
  505.                 return ngx_control_print_config(r);
  506.             }

  507.             return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  508.         }

  509.         break;

  510.     case 20:
  511.         if (ngx_strncmp(p, "/1/control/processes", len) == 0) {
  512.             if (r->method != NGX_CTRL_GET) {
  513.                 return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  514.             }

  515.             return ngx_control_show_processes(r);
  516.         }

  517.         break;
  518.     }

  519.     return ngx_control_send_response(r, NGX_CTRL_NOT_FOUND, NULL);
  520. }


  521. static char *
  522. ‌ngx_control_status_text(ngx_uint_t code)
  523. {
  524.     switch (code) {
  525.     case NGX_CTRL_OK:
  526.         return "OK";
  527.     case NGX_CTRL_BAD_REQUEST:
  528.         return "Bad Request";
  529.     case NGX_CTRL_NOT_FOUND:
  530.         return "Not Found";
  531.     case NGX_CTRL_NOT_ALLOWED:
  532.         return "Not Allowed";
  533.     case NGX_CTRL_UNPROCESSABLE:
  534.         return "Unprocessable Entity";
  535.     case NGX_CTRL_INTERNAL_ERROR:
  536.         return "Internal Server Error";
  537.     default:
  538.         return "";
  539.     }
  540. }


  541. static ngx_int_t
  542. ‌ngx_control_send_response(ngx_control_request_t *r, ngx_uint_t code,
  543.     ngx_buf_t *body)
  544. {
  545.     char         *status;
  546.     ngx_uint_t    n;
  547.     ngx_chain_t  *c;

  548.     const char  response_fmt[] =
  549.         "HTTP/1.1 %03ui %s\r\n"
  550.         "Server: " NGINX_VER "\r\n"
  551.         "Connection: close\r\n"
  552.         "Content-Type: application/json\r\n"
  553.         "Content-Length: %ui\r\n"
  554.         "\r\n";

  555.     c = ngx_alloc_chain_link(r->pool);
  556.     if (c == NULL) {
  557.         return NGX_ERROR;
  558.     }

  559.     status = ngx_control_status_text(code);

  560.     c->buf = ngx_create_temp_buf(r->pool,
  561.                 sizeof(response_fmt) + 2 * NGX_INT_T_LEN + ngx_strlen(status));
  562.     if (c->buf == NULL) {
  563.         return NGX_ERROR;
  564.     }

  565.     c->next = NULL;

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

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

  568.     if (body) {
  569.         c->next = ngx_alloc_chain_link(r->pool);
  570.         if (c->next == NULL) {
  571.             return NGX_ERROR;
  572.         }

  573.         c->next->buf = body;
  574.         c->next->next = NULL;
  575.     }

  576.     r->out = c;

  577.     return ngx_control_handle_write(r);
  578. }


  579. static ngx_int_t
  580. ‌ngx_control_handle_write(ngx_control_request_t *r)
  581. {
  582.     ssize_t       n;
  583.     ngx_buf_t    *b;
  584.     ngx_chain_t  *c;

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

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

  589.             if (n <= 0) {
  590.                 if (n == -1 && ngx_errno == NGX_EAGAIN) {
  591.                     ngx_control_pollfd[r->index].events |= POLLOUT;
  592.                     return NGX_AGAIN;
  593.                 }

  594.                 ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_socket_errno,
  595.                               "control: send() failed");
  596.                 return NGX_ERROR;
  597.             }

  598.             b->pos += n;

  599.             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,
  600.                            "control: wrote %i bytes", n);
  601.         }
  602.     }

  603.     return NGX_DONE;
  604. }


  605. static ngx_int_t
  606. ‌ngx_control_send_json(ngx_control_request_t *r, ngx_int_t code,
  607.     ngx_data_item_t *obj)
  608. {
  609.     ngx_buf_t  *b;

  610.     b = ngx_json_render(r->pool, obj);
  611.     if (b == NULL) {
  612.         return NGX_ERROR;
  613.     }

  614.     return ngx_control_send_response(r, code, b);
  615. }


  616. static ngx_int_t
  617. ‌ngx_control_api_endpoints(ngx_control_request_t *r, const char **paths,
  618.     ngx_int_t npaths)
  619. {
  620.     ngx_int_t         i;
  621.     ngx_str_t         p;
  622.     ngx_data_item_t  *json, *path;

  623.     if (r->method != NGX_CTRL_GET) {
  624.         return ngx_control_send_response(r, NGX_CTRL_NOT_ALLOWED, NULL);
  625.     }

  626.     json = ngx_data_new_list(r->pool);
  627.     if (json == NULL) {
  628.         return NGX_ERROR;
  629.     }

  630.     for (i = 0; i < npaths; i++) {
  631.         p.data = (u_char *) paths[i];
  632.         p.len = ngx_strlen(paths[i]);

  633.         path = ngx_data_string_handler((uintptr_t) &p, r->pool, NULL);
  634.         if (path == NULL) {
  635.             return NGX_ERROR;
  636.         }

  637.         ngx_data_add_item(json, NULL, path);
  638.     }

  639.     return ngx_control_send_json(r, NGX_CTRL_OK, (ngx_data_item_t *) json);
  640. }


  641. static ngx_int_t
  642. ‌ngx_control_show_processes(ngx_control_request_t *r)
  643. {
  644.     ngx_int_t               i;
  645.     ngx_data_item_t        *process_list, *obj;
  646.     ngx_control_process_t   process;

  647.     process_list = ngx_data_new_list(r->pool);
  648.     if (process_list == NULL) {
  649.         return NGX_ERROR;
  650.     }

  651.     for (i = 0; i < ngx_last_process; i++) {
  652.         if (ngx_processes[i].pid == -1) {
  653.             continue;
  654.         }

  655.         process.name.data = (u_char *) ngx_processes[i].name;
  656.         process.name.len = ngx_strlen(process.name.data);
  657.         process.pid = ngx_processes[i].pid;
  658.         process.exiting = ngx_processes[i].exiting;

  659.         obj = ngx_data_obj_handler((uintptr_t) ngx_control_process_fields,
  660.                                    r->pool, &process);
  661.         if (obj == NULL) {
  662.             return NGX_ERROR;
  663.         }

  664.         ngx_data_add_item(process_list, NULL, obj);
  665.     }

  666.     return ngx_control_send_json(r, NGX_CTRL_OK, process_list);
  667. }


  668. static ngx_int_t
  669. ‌ngx_control_show_version(ngx_control_request_t *r)
  670. {
  671.     ngx_data_item_t  *resp;

  672.     resp = ngx_data_obj_handler((uintptr_t) ngx_control_status_fields, r->pool,
  673.                                 NULL);
  674.     if (resp == NULL) {
  675.         return NGX_ERROR;
  676.     }

  677.     return ngx_control_send_json(r, NGX_CTRL_OK, resp);
  678. }


  679. static ngx_int_t
  680. ‌ngx_control_print_config(ngx_control_request_t *r)
  681. {
  682.     ngx_uint_t        i;
  683.     ngx_keyval_t      item;
  684.     ngx_data_item_t  *list, *obj;
  685.     ngx_conf_dump_t  *dump;

  686.     list = ngx_data_new_list(r->pool);
  687.     if (list == NULL) {
  688.         return NGX_ERROR;
  689.     }

  690.     dump = ngx_cycle->config_dump.elts;

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

  692.         item.key = dump[i].name;
  693.         item.value.data = dump[i].buffer->pos;
  694.         item.value.len = dump[i].buffer->last - dump[i].buffer->pos;

  695.         obj = ngx_data_obj_handler((uintptr_t) ngx_control_config_fields,
  696.                                    r->pool, &item);
  697.         if (obj == NULL) {
  698.             return NGX_ERROR;
  699.         }

  700.         ngx_data_add_item(list, NULL, obj);
  701.     }

  702.     return ngx_control_send_json(r, NGX_CTRL_OK, list);
  703. }


  704. static ngx_data_item_t *
  705. ‌ngx_control_json_logs(ngx_array_t *logs)
  706. {
  707.     ngx_str_t        *l;
  708.     ngx_uint_t        i;
  709.     ngx_data_item_t  *obj, *log_list, *log;

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

  711.     obj = ngx_data_new_object(logs->pool);
  712.     if (obj == NULL) {
  713.         return NULL;
  714.     }

  715.     log_list = ngx_data_new_list(logs->pool);
  716.     if (log_list == NULL) {
  717.         return NULL;
  718.     }

  719.     l = logs->elts;
  720.     for (i = 0; i < logs->nelts; i++) {
  721.         log = ngx_data_string_handler((uintptr_t) &l[i], logs->pool, NULL);
  722.         if (log == NULL) {
  723.             return NULL;
  724.         }

  725.         ngx_data_add_item(log_list, NULL, log);
  726.     }

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

  728.     return obj;
  729. }


  730. static ngx_int_t
  731. ‌ngx_control_reload_config(ngx_control_request_t *r)
  732. {
  733.     ngx_int_t         status;
  734.     ngx_log_t         log, *tmp;
  735.     ngx_cycle_t      *cycle;
  736.     ngx_array_t       log_store;
  737.     ngx_data_item_t  *obj;

  738.     status = NGX_CTRL_OK;

  739.     if (ngx_array_init(&log_store, r->pool, 3, sizeof(ngx_str_t)) != NGX_OK) {
  740.         return NGX_ERROR;
  741.     }

  742.     ngx_memzero(&log, sizeof(ngx_log_t));
  743.     log.wdata = &log_store;
  744.     log.writer = ngx_control_log_capture;
  745.     log.log_level = NGX_LOG_DEBUG;

  746.     tmp = ngx_cycle->log;
  747.     log.next = tmp;
  748.     ngx_cycle->log = &log;
  749.     cycle = ngx_init_cycle((ngx_cycle_t *) ngx_cycle);

  750.     if (cycle == NULL) {
  751.         status = NGX_CTRL_UNPROCESSABLE;
  752.         ngx_cycle->log = tmp;

  753.     } else {
  754.         ngx_cycle = cycle;
  755.         r->reloaded = 1;
  756.         r->pool->log = ngx_cycle->log;
  757.     }

  758.     obj = ngx_control_json_logs(&log_store);
  759.     if (obj == NULL) {
  760.         return NGX_ERROR;
  761.     }

  762.     return ngx_control_send_json(r, status, obj);
  763. }


  764. static void
  765. ‌ngx_control_log_capture(ngx_log_t *log, ngx_uint_t lvl, u_char *buf, size_t len)
  766. {
  767.     u_char       *log_data;
  768.     ngx_str_t    *log_str;
  769.     ngx_array_t  *store;

  770.     if (lvl > NGX_LOG_WARN) {
  771.         return;
  772.     }

  773.     store = log->wdata;

  774.     log_data = ngx_pnalloc(store->pool, len);
  775.     if (log_data == NULL) {
  776.         return;
  777.     }

  778.     log_str = ngx_array_push(store);
  779.     if (log_str == NULL) {
  780.         return;
  781.     }

  782.     ngx_memcpy(log_data, buf, len);

  783.     log_str->data = log_data;
  784.     log_str->len = len;
  785. }


  786. static ngx_int_t
  787. ‌ngx_control_parse_request_line(ngx_control_request_t *r)
  788. {
  789.     static uint32_t  usual[] = {
  790.         0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */
  791.                     /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
  792.         0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */
  793.                     /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
  794.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  795.                     /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
  796.         0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */
  797.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  798.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  799.         0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  800.         0xffffffff  /* 1111 1111 1111 1111  1111 1111 1111 1111 */
  801.     };

  802.     u_char      c, ch, *p;
  803.     ngx_buf_t  *b;
  804. ‌    enum {
  805.         sw_start = 0,
  806.         sw_method,
  807.         sw_spaces_before_uri,
  808.         sw_schema,
  809.         sw_schema_slash,
  810.         sw_schema_slash_slash,
  811.         sw_spaces_before_host,
  812.         sw_host_start,
  813.         sw_host,
  814.         sw_host_end,
  815.         sw_host_ip_literal,
  816.         sw_port_start,
  817.         sw_port,
  818.         sw_uri,
  819.         sw_http_,
  820.         sw_http_H,
  821.         sw_http_HT,
  822.         sw_http_HTT,
  823.         sw_http_HTTP,
  824.         sw_first_major_digit,
  825.         sw_major_digit,
  826.         sw_first_minor_digit,
  827.         sw_minor_digit,
  828.         sw_almost_done
  829.     } state;

  830.     b = r->in;
  831.     state = r->state;

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

  834.         switch (state) {
  835.         /* HTTP methods: GET, PATCH */
  836.         case sw_start:
  837.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  838.                 return NGX_DECLINED;
  839.             }

  840.             state = sw_method;

  841.             break;

  842.         case sw_method:
  843.             if (ch == ' ') {
  844.                 state = sw_spaces_before_uri;

  845.                 switch (p - b->start) {
  846.                 case 3:
  847.                     if (ngx_strncmp(b->start, "GET", 3) == 0) {
  848.                         r->method = NGX_CTRL_GET;
  849.                         break;
  850.                     }
  851.                     break;

  852.                 case 5:
  853.                     if (ngx_strncmp(b->start, "PATCH", 5) == 0) {
  854.                         r->method = NGX_CTRL_PATCH;
  855.                         break;
  856.                     }
  857.                     break;
  858.                 }

  859.                 break;
  860.             }

  861.             if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
  862.                 return NGX_DECLINED;
  863.             }

  864.             break;

  865.         /* space* before URI */
  866.         case sw_spaces_before_uri:
  867.             if (ch == '/') {
  868.                 r->path.data = p;
  869.                 state = sw_uri;
  870.                 break;
  871.             }

  872.             c = (u_char) (ch | 0x20);
  873.             if (c >= 'a' && c <= 'z') {
  874.                 state = sw_schema;
  875.                 break;
  876.             }

  877.             if (ch != ' ') {
  878.                 return NGX_DECLINED;
  879.             }

  880.             break;

  881.         case sw_schema:
  882.             c = (u_char) (ch | 0x20);
  883.             if (c >= 'a' && c <= 'z') {
  884.                 break;
  885.             }

  886.             if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')
  887.             {
  888.                 break;
  889.             }

  890.             if (ch != ':') {
  891.                 return NGX_DECLINED;
  892.             }

  893.             state = sw_schema_slash;
  894.             break;

  895.         case sw_schema_slash:
  896.             if (ch != '/') {
  897.                 return NGX_DECLINED;
  898.             }

  899.             state = sw_schema_slash_slash;
  900.             break;

  901.         case sw_schema_slash_slash:
  902.             if (ch != '/') {
  903.                 return NGX_DECLINED;
  904.             }

  905.             state = sw_host_start;
  906.             break;

  907.         case sw_spaces_before_host:
  908.             if (ch == ' ') {
  909.                 break;
  910.             }
  911.             /* fall through */

  912.         case sw_host_start:
  913.             if (ch == '[') {
  914.                 state = sw_host_ip_literal;
  915.                 break;
  916.             }

  917.             state = sw_host;
  918.             /* fall through */

  919.         case sw_host:
  920.             c = (u_char) (ch | 0x20);
  921.             if (c >= 'a' && c <= 'z') {
  922.                 break;
  923.             }

  924.             if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {
  925.                 break;
  926.             }
  927.             /* fall through */

  928.         case sw_host_end:
  929.             if (ch == ':') {
  930.                 state = sw_port_start;
  931.                 break;
  932.             }

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

  934.             switch (ch) {
  935.             case '/':
  936.                 r->path.data = p;
  937.                 state = sw_uri;
  938.                 break;
  939.             /* empty path cases */
  940.             case '?':
  941.                 ngx_str_set(&r->path, "/");
  942.                 state = sw_uri;
  943.                 break;
  944.             case ' ':
  945.                 ngx_str_set(&r->path, "/");
  946.                 state = sw_http_;
  947.                 break;
  948.             }
  949.             break;

  950.         case sw_host_ip_literal:
  951.             if (ch >= '0' && ch <= '9') {
  952.                 break;
  953.             }

  954.             c = (u_char) (ch | 0x20);
  955.             if (c >= 'a' && c <= 'z') {
  956.                 break;
  957.             }

  958.             switch (ch) {
  959.             case ':':
  960.                 break;
  961.             case ']':
  962.                 state = sw_host_end;
  963.                 break;
  964.             case '-':
  965.             case '.':
  966.             case '_':
  967.             case '~':
  968.                 /* unreserved */
  969.                 break;
  970.             case '!':
  971.             case '$':
  972.             case '&':
  973.             case '\'':
  974.             case '(':
  975.             case ')':
  976.             case '*':
  977.             case '+':
  978.             case ',':
  979.             case ';':
  980.             case '=':
  981.                 /* sub-delims */
  982.                 break;
  983.             default:
  984.                 return NGX_DECLINED;
  985.             }
  986.             break;

  987.         case sw_port_start:
  988.             state = sw_port;

  989.             if (ch >= '0' && ch <= '9') {
  990.                 break;
  991.             }

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

  993.             /* fall through */

  994.         case sw_port:
  995.             if (ch >= '0' && ch <= '9') {
  996.                 break;
  997.             }

  998.             switch (ch) {
  999.             case '/':
  1000.                 r->path.data = p;
  1001.                 state = sw_uri;
  1002.                 break;
  1003.             /* empty path cases */
  1004.             case '?':
  1005.                 ngx_str_set(&r->path, "/");
  1006.                 state = sw_uri;
  1007.                 break;
  1008.             case ' ':
  1009.                 ngx_str_set(&r->path, "/");
  1010.                 state = sw_http_;
  1011.                 break;
  1012.             default:
  1013.                 return NGX_DECLINED;
  1014.             }
  1015.             break;

  1016.         /* URI */
  1017.         case sw_uri:
  1018.             if (usual[ch >> 5] & (1U << (ch & 0x1f))) {
  1019.                 break;
  1020.             }

  1021.             switch (ch) {
  1022.             case ' ':
  1023.                 state = sw_http_;
  1024.                 /* fall through */
  1025.             case '?':
  1026.                 if (r->path.len == 0) {
  1027.                     r->path.len = p - r->path.data;
  1028.                 }
  1029.                 break;

  1030.             case '.':
  1031.             case '%':
  1032.             case '#':
  1033.             case '+':
  1034.             case 0x7f:
  1035.                 return NGX_DECLINED;
  1036.             default:
  1037.                 if (ch < 0x20) {
  1038.                     return NGX_DECLINED;
  1039.                 }
  1040.                 break;
  1041.             }
  1042.             break;

  1043.         /* space+ after URI */
  1044.         case sw_http_:
  1045.             switch (ch) {
  1046.             case ' ':
  1047.                 break;
  1048.             case 'H':
  1049.                 state = sw_http_H;
  1050.                 break;
  1051.             default:
  1052.                 return NGX_DECLINED;
  1053.             }
  1054.             break;

  1055.         case sw_http_H:
  1056.             switch (ch) {
  1057.             case 'T':
  1058.                 state = sw_http_HT;
  1059.                 break;
  1060.             default:
  1061.                 return NGX_DECLINED;
  1062.             }
  1063.             break;

  1064.         case sw_http_HT:
  1065.             switch (ch) {
  1066.             case 'T':
  1067.                 state = sw_http_HTT;
  1068.                 break;
  1069.             default:
  1070.                 return NGX_DECLINED;
  1071.             }
  1072.             break;

  1073.         case sw_http_HTT:
  1074.             switch (ch) {
  1075.             case 'P':
  1076.                 state = sw_http_HTTP;
  1077.                 break;
  1078.             default:
  1079.                 return NGX_DECLINED;
  1080.             }
  1081.             break;

  1082.         case sw_http_HTTP:
  1083.             switch (ch) {
  1084.             case '/':
  1085.                 state = sw_first_major_digit;
  1086.                 break;
  1087.             default:
  1088.                 return NGX_DECLINED;
  1089.             }
  1090.             break;

  1091.         /* first digit of major HTTP version */
  1092.         case sw_first_major_digit:
  1093.             if (ch != '1') {
  1094.                 return NGX_DECLINED;
  1095.             }

  1096.             state = sw_major_digit;
  1097.             break;

  1098.         /* major HTTP version or dot */
  1099.         case sw_major_digit:
  1100.             if (ch == '.') {
  1101.                 state = sw_first_minor_digit;
  1102.                 break;
  1103.             } else {
  1104.                 /* major versions other than 1 are not supported */
  1105.                 return NGX_DECLINED;
  1106.             }

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

  1112.             state = sw_minor_digit;
  1113.             break;

  1114.         /* minor HTTP version or end of request line */
  1115.         case sw_minor_digit:
  1116.             if (ch == CR) {
  1117.                 state = sw_almost_done;
  1118.                 break;

  1119.             } else if (ch == LF) {
  1120.                 goto done;

  1121.             } else {
  1122.                 /* support only 1.1 or 1.0 */
  1123.                 return NGX_DECLINED;
  1124.             }

  1125.         /* end of request line */
  1126.         case sw_almost_done:
  1127.             switch (ch) {
  1128.             case LF:
  1129.                 goto done;
  1130.             default:
  1131.                 return NGX_DECLINED;
  1132.             }
  1133.         }
  1134.     }

  1135.     b->pos = p;
  1136.     r->state = state;

  1137.     return NGX_AGAIN;

  1138. done:
  1139.     b->pos = p + 1;
  1140.     r->state = sw_start;

  1141.     return NGX_DONE;
  1142. }