1 /* -*- mode:c; coding:utf-8 -*- */
2
3 #include <nginx.h>
4 #include <ngx_config.h>
5 #include <ngx_core.h>
6 #include <ngx_http.h>
7 #include "ngx_http_json_status_module.h"
8
9 static ngx_command_t
10 ngx_http_json_status_commands[] = {
11 {
12 ngx_string("status"),
13 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
14 ngx_http_json_status,
15 0,
16 0,
17 NULL
18 },
19 ngx_null_command
20 };
21
22 static ngx_http_module_t ngx_http_json_status_module_ctx = {
23 NULL, /* preconfiguration */
24 NULL, /* postconfiguration */
25
26 ngx_http_json_status_create_main_conf, /* create main configuration */
27 ngx_http_json_status_init_main_conf, /* init main configuration */
28
29 NULL, /* create server configuration */
30 NULL, /* merge server configuration */
31
32 NULL, /* create location configuration */
33 NULL /* merge location configuration */
34 };
35
36 ngx_module_t ngx_http_json_status_module = {
37 NGX_MODULE_V1,
38 &ngx_http_json_status_module_ctx, /* module context */
39 ngx_http_json_status_commands, /* module directives */
40 NGX_HTTP_MODULE, /* module type */
41 NULL, /* init master */
42 NULL, /* init module */
43 NULL, /* init process */
44 NULL, /* init thread */
45 NULL, /* exit thread */
46 NULL, /* exit process */
47 NULL, /* exit master */
48 NGX_MODULE_V1_PADDING
49 };
50
51 static void *
ngx_http_json_status_create_main_conf(ngx_conf_t * cf)52 ngx_http_json_status_create_main_conf(ngx_conf_t *cf)
53 {
54 ngx_http_json_status_main_conf_t *jsmcf;
55
56 jsmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_json_status_main_conf_t));
57 if (jsmcf == NULL) {
58 return NULL;
59 }
60
61 return jsmcf;
62 }
63
64 static char *
ngx_http_json_status_init_main_conf(ngx_conf_t * cf,void * conf)65 ngx_http_json_status_init_main_conf(ngx_conf_t *cf, void *conf)
66 {
67 ngx_http_json_status_main_conf_t *jsmcf;
68 ngx_http_upstream_main_conf_t *umcf;
69 struct hostent *host;
70 ngx_uint_t i;
71
72 jsmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_json_status_module);
73 umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);
74 if (jsmcf == NULL || umcf == NULL) {
75 return NGX_CONF_ERROR;
76 }
77
78 if (gethostname(jsmcf->hostname, NGX_MAXHOSTNAMELEN) == -1) {
79 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "gethostname() failed");
80 return NULL;
81 }
82 host = gethostbyname(jsmcf->hostname);
83 if (host == NULL) {
84 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "gethostbyname() failed");
85 return NULL;
86 }
87 ngx_sprintf(jsmcf->addr, "%d.%d.%d.%d",
88 (BYTE)*(host->h_addr),
89 (BYTE)*(host->h_addr + 1),
90 (BYTE)*(host->h_addr + 2),
91 (BYTE)*(host->h_addr + 3));
92
93 /* size calculation of upstreams */
94 size_t upstream_size = sizeof("\"upstreams\":{},");
95 for (i = 0; i < umcf->upstreams.nelts; i++) {
96 ngx_http_upstream_srv_conf_t *uscf = ((ngx_http_upstream_srv_conf_t **)umcf->upstreams.elts)[i];
97 ngx_http_upstream_rr_peers_t *peers = uscf->peer.data;
98 upstream_size += sizeof("\"\":[]")+sizeof(uscf->host)+ // upstream name
99 (sizeof("{\"server\":\"\",\"backup\":\"\",\"weight\":\"\",\"state\":\"\",\"active\":\"\",\"keepalive\":\"\",\"requests\":\"\",\"responses\":\"\",\"sent\":\"\",\"received\":\"\",\"fails\":\"\",\"unavail\":\"\",\"health_checks\":\"\",\"downtime\":\"\",\"downstart\":\"\"}")+
100 sizeof("{\"total\":\"\",\"1xx\":\"\",\"2xx\":\"\",\"3xx\":\"\",\"4xx\":\"\",\"5xx\":\"\"}")+ // responses
101 sizeof("{\"checks\":\"\",\"fails\":\"\",\"unhealthy\":\"\",\"last_passed\":\"\"}")+ // health_checks
102 //sizeof(ngx_uint_t)*10+ // responses + health_checks values
103 sizeof("N/A")*10+ // responses + health_checks values (Tentative)
104 sizeof(ngx_str_t)*1+ // server
105 sizeof(ngx_uint_t)*3+ // backup + weight + fails
106 sizeof("unhealthy")+ // satte
107 sizeof("N/A")*8 // etc(Tentative)
108 )*peers->number
109 ;
110 }
111
112 /* sum total */
113 jsmcf->contents_size = sizeof("{}")+
114 sizeof("();")+ // callback for
115 /* server info */
116 sizeof("\"version\":\"\",")+sizeof(NGX_HTTP_JSON_STATUS_MODULE_VERSION)+
117 sizeof("\"nginx_version\":\"\",")+sizeof(NGINX_VERSION)+
118 sizeof("\"address\":\"\",")+sizeof(jsmcf->addr)+
119 sizeof("\"timestamp\":\"\",")+sizeof(time_t)+
120 /* connections */
121 sizeof("\"connections\":{},")+
122 sizeof("\"accepted\":\"\",")+NGX_ATOMIC_T_LEN+
123 sizeof("\"dropped\":\"\",")+NGX_ATOMIC_T_LEN+ // c_dropped = c_accepted.to_i - handled.to_i (see newrelic_nginx_agent)
124 sizeof("\"active\":\"\",")+NGX_ATOMIC_T_LEN+
125 sizeof("\"idle\":\"\",")+NGX_ATOMIC_T_LEN+
126 sizeof("\"counter\":\"\",")+NGX_ATOMIC_T_LEN+
127 /* requests */
128 sizeof("\"requests\":{},")+
129 sizeof("\"total\":\"\",")+NGX_ATOMIC_T_LEN+
130 sizeof("\"current\":\"\"")+NGX_ATOMIC_T_LEN+ // r_current = c_reading.to_i + c_writing.to_i (see newrelic_nginx_agent)
131 /* upstreams */
132 upstream_size+
133 /* terminate */
134 sizeof("\0")
135 ;
136
137 return NGX_CONF_OK;
138 }
139
140 static ngx_int_t
ngx_http_json_status_handler(ngx_http_request_t * r)141 ngx_http_json_status_handler(ngx_http_request_t *r)
142 {
143 ngx_http_upstream_main_conf_t *umcf;
144 ngx_http_json_status_main_conf_t *jsmcf;
145 ngx_buf_t *b;
146 ngx_int_t rc;
147 ngx_chain_t out;
148 time_t now = time((time_t *)0);
149 ngx_atomic_int_t ap, hn, ac, rq, rd, wr, wa, acc;
150 ngx_uint_t i, j, k;
151 ngx_str_t *callback = ngx_http_get_arg_string(r, (u_char *)ARG_PARAMETER_CALLBACK);
152
153 ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "#start. %s:%d", __FUNCTION__, __LINE__);
154
155 umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);
156 jsmcf = ngx_http_get_module_main_conf(r, ngx_http_json_status_module); /*ngx_http_request_t, module(ngx_module_t)*/
157
158 /* GET or HEAD only */
159 if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {
160 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "method = %d: GET = %d,HEAD = %d", r->method, NGX_HTTP_GET, NGX_HTTP_HEAD);
161 return NGX_HTTP_NOT_ALLOWED;
162 }
163
164 /* discard request body is not required */
165 rc = ngx_http_discard_request_body(r);
166 if (rc != NGX_OK) {
167 return rc;
168 }
169
170 b = ngx_create_temp_buf(r->pool, jsmcf->contents_size + (sizeof(u_char)*callback->len));
171 if (b == NULL) {
172 return NGX_HTTP_INTERNAL_SERVER_ERROR;
173 }
174 out.buf = b;
175 out.next = NULL;
176
177 /* You enable NGX_STAT_STUB and (src/event/ngx_event.h) */
178 acc = *ngx_connection_counter;
179 ap = *ngx_stat_accepted;
180 ac = *ngx_stat_active; // it is necessary to reset the value if the worker was aborted
181 hn = *ngx_stat_handled;
182 rq = *ngx_stat_requests;
183 rd = *ngx_stat_reading;
184 wr = *ngx_stat_writing;
185 #if nginx_version >= 1004001 /* From 1.4.1 (http://lxr.evanmiller.org/http/ident?i=ngx_stat_waiting) */
186 wa = *ngx_stat_waiting;
187 #else
188 wa = ac - (rd + wr);
189 #endif
190
191 if (callback->len > 0) {
192 b->last = ngx_sprintf(b->last, "%V(", callback);
193 }
194
195 b->last = ngx_sprintf(b->last, "{"); /* contents start */
196 b->last = ngx_sprintf(b->last, "\"version\":\"%s\",", NGX_HTTP_JSON_STATUS_MODULE_VERSION); /* module version */
197 b->last = ngx_sprintf(b->last, "\"nginx_version\":\"%s\",", NGINX_VERSION);
198 b->last = ngx_sprintf(b->last, "\"address\":\"%s\",", jsmcf->addr);
199 b->last = ngx_sprintf(b->last, "\"timestamp\":\"%l\",", now);
200 b->last = ngx_sprintf(b->last, "\"connections\":{\"accepted\":\"%uA\",\"dropped\":\"%uA\",\"active\":\"%uA\",\"idle\":\"%uA\",\"counter\":\"%uA\"},", ap, ap-hn, ac, wa, acc);
201 b->last = ngx_sprintf(b->last, "\"requests\":{\"total\":\"%uA\",\"current\":\"%uA\"},", rq, rd+wr);
202 b->last = ngx_sprintf(b->last, "\"upstreams\":{");
203
204 for (i = 0; i < umcf->upstreams.nelts; i++) {
205 ngx_http_upstream_srv_conf_t *uscf = ((ngx_http_upstream_srv_conf_t **)umcf->upstreams.elts)[i];
206 ngx_http_upstream_rr_peers_t *peers = uscf->peer.data;
207 ngx_http_upstream_server_t *server = uscf->servers->elts;
208
209 if (i>0) {b->last = ngx_sprintf(b->last, ",", &peers->peer[j].name);}
210 // upstream directive
211 b->last = ngx_sprintf(b->last, "\"%V\":[", &uscf->host);
212
213 ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "#upstream: %V", &uscf->host);
214
215 for (j = 0; j < peers->number; j++) {
216
217 // config information
218 ngx_uint_t config_backup = 0;
219 ngx_uint_t config_down = 0;
220 for (k = 0; k < uscf->servers->nelts; k++) {
221 if (ngx_strtcmp(&peers->peer[j].name, &server[k].addrs[SERVER_ADDRS_ZERO].name) == 0) {
222 config_down = server[k].down;
223 config_backup = server[k].backup;
224 break;
225 }
226 }
227
228 ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "#config: down=%d, backup=%d(%s:%d)", config_down, config_backup, __FUNCTION__, __LINE__);
229
230 if (j>0) {b->last = ngx_sprintf(b->last, ",", &peers->peer[j].name);}
231 b->last = ngx_sprintf(b->last, "{\"server\":\"%V\",\"backup\":\"%d\",\"weight\":\"%d\",\"state\":\"%s\",\"active\":\"%s\",\"keepalive\":\"%s\",\"requests\":\"%s\",\"responses\":%s,\"sent\":\"%s\",\"received\":\"%s\",\"fails\":\"%d\",\"unavail\":\"%s\",\"health_checks\":%s,\"downtime\":\"%s\",\"downstart\":\"%s\"}",
232 &peers->peer[j].name,
233 config_backup,
234 peers->peer[j].weight, //effective_weight, //current_weight,
235 (peers->peer[j].down == 1)?(u_char*)"down":(u_char*)"up", // Current state, which may be one of "up”, "down”, "unavail”, or "unhealthy"
236 (u_char *)"N/A", // active
237 (u_char *)"N/A", // keepalive
238 (u_char *)"N/A", // requests
239 (u_char *)"{\"total\":\"N/A\",\"1xx\":\"N/A\",\"2xx\":\"N/A\",\"3xx\":\"N/A\",\"4xx\":\"N/A\",\"5xx\":\"N/A\"}", // responses
240 (u_char *)"N/A", // sent
241 (u_char *)"N/A", // received
242 peers->peer[j].fails,
243 (u_char *)"N/A", // unavail
244 (u_char *)"{\"checks\":\"N/A\",\"fails\":\"N/A\",\"unhealthy\":\"N/A\",\"last_passed\":\"N/A\"}", // health_checks
245 (u_char *)"N/A", // downtime
246 (u_char *)"N/A" // downstart
247 );
248 }
249
250 b->last = ngx_sprintf(b->last, "]", &uscf->host);
251 }
252
253 b->last = ngx_sprintf(b->last, "}");
254 b->last = ngx_sprintf(b->last, "}"); /* contents end */
255 if (callback->len > 0) {
256 b->last = ngx_sprintf(b->last, ");");
257 }
258
259 b->memory = 1;
260 b->flush = 1;
261 b->last_buf = 1;
262 b->last_in_chain = 1;
263
264 ngx_str_set(&r->headers_out.content_type, "application/json; charset=utf-8");
265 r->headers_out.status = NGX_HTTP_OK;
266 r->headers_out.content_length_n = b->last - b->pos;
267 rc = ngx_http_send_header(r);
268 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
269 return rc;
270 }
271
272 return ngx_http_output_filter(r, &out);
273 }
274
275 static char *
ngx_http_json_status(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)276 ngx_http_json_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
277 {
278 ngx_http_core_loc_conf_t *clcf;
279
280 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
281 clcf->handler = ngx_http_json_status_handler;
282
283 return NGX_CONF_OK;
284 }
285
286 /* ********** misc ********** */
287 static int
ngx_strtcmp(ngx_str_t * s1,ngx_str_t * s2)288 ngx_strtcmp(ngx_str_t *s1, ngx_str_t *s2) {
289 if (s1->len == 0 || s2->len == 0 || s1->data == NULL || s2->data == NULL) {
290 return 128;
291 }
292 if (s1->len == s2->len) {
293 return ngx_strncmp(s1->data, s2->data, s1->len);
294 }
295 return ngx_strcmp(s1->data, s2->data);
296 }
297
298 static ngx_str_t *
ngx_http_get_arg_string(ngx_http_request_t * r,u_char * name)299 ngx_http_get_arg_string(ngx_http_request_t *r, u_char *name) {
300 ngx_http_variable_value_t *vv;
301 ngx_str_t key = {ngx_strlen(name), (u_char *)name};
302 u_char *data;
303 ngx_str_t *str;
304
305 str = ngx_pcalloc(r->pool, sizeof(ngx_str_t));
306
307 vv = ngx_http_get_variable(r, &key, ngx_hash_key(key.data, key.len));
308 if (vv == NULL || vv->not_found) {
309 str->len = 0;
310 str->data = (u_char *)"";
311 return str;
312 }
313
314 data = ngx_pcalloc(r->pool, (vv->len + 1) * sizeof(u_char));
315 ngx_cpystrn(data, vv->data, vv->len + 1);
316
317 str->data = data;
318 str->len = vv->len;
319
320 ngx_str_null(&key);
321 vv->data = NULL;
322
323 return str;
324 }
325