Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 306 lines (252 sloc) 10.098 kB
f1a155e First commit.
Evan Miller authored
1 /*
2 * mod_rrd_graph - Graph data in a Round Robin Database (RRD).
3 *
4 * Copyright (C) Evan Miller
5 */
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10 #include <rrd.h>
11
12 typedef struct {
13 ngx_str_t root;
14 } ngx_http_rrd_graph_conf_t;
15
16 static void * ngx_http_rrd_graph_create_conf(ngx_conf_t *cf);
17 static char * ngx_http_rrd_graph_merge_conf(ngx_conf_t *cf, void *parent, void *child);
18 static char* ngx_http_rrd_graph(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
19 static ngx_int_t ngx_http_rrd_graph_handler(ngx_http_request_t *r);
20 static ngx_int_t ngx_http_rrd_graph_send_image(ngx_http_request_t *r, u_char *image, size_t image_size);
21 static ngx_int_t ngx_http_rrd_graph_parse_uri(ngx_http_request_t *r, int *argc_ptr,
22 char ***argv_ptr, size_t **argv_len_ptr);
23
24 static u_char ngx_http_png_header[] = { '\x89', 'P', 'N', 'G' };
25 static u_char ngx_http_pdf_header[] = { '%', 'P', 'D', 'F' };
26 static u_char ngx_http_svg_header[] = { '<', '?', 'x', 'm', 'l' };
27 static u_char ngx_http_eps_header[] = { '%', '!', 'P', 'S' };
28
29 static ngx_command_t ngx_http_rrd_graph_commands[] = {
30 { ngx_string("rrd_graph"),
31 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
32 ngx_http_rrd_graph,
33 0,
34 0,
35 NULL },
36
37 /* the root directory that will be prefixed to file names */
38 { ngx_string("rrd_graph_root"),
39 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
40 ngx_conf_set_str_slot,
41 NGX_HTTP_LOC_CONF_OFFSET,
42 offsetof(ngx_http_rrd_graph_conf_t, root),
43 NULL },
44
45 ngx_null_command
46 };
47
48
49 static ngx_http_module_t ngx_http_rrd_graph_module_ctx = {
50 NULL, /* preconfiguration */
51 NULL, /* postconfiguration */
52
53 NULL, /* create main configuration */
54 NULL, /* init main configuration */
55
56 NULL, /* create server configuration */
57 NULL, /* merge server configuration */
58
59 ngx_http_rrd_graph_create_conf,/* create location configuration */
60 ngx_http_rrd_graph_merge_conf /* merge location configuration */
61 };
62
63
64 ngx_module_t ngx_http_rrd_graph_module = {
65 NGX_MODULE_V1,
66 &ngx_http_rrd_graph_module_ctx, /* module context */
67 ngx_http_rrd_graph_commands, /* module directives */
68 NGX_HTTP_MODULE, /* module type */
69 NULL, /* init master */
70 NULL, /* init module */
71 NULL, /* init process */
72 NULL, /* init thread */
73 NULL, /* exit thread */
74 NULL, /* exit process */
75 NULL, /* exit master */
76 NGX_MODULE_V1_PADDING
77 };
78
79
80 static ngx_int_t
81 ngx_http_rrd_graph_handler(ngx_http_request_t *r)
82 {
83 ngx_int_t rc;
84 /* image metadata */
85 rrd_info_t *image_info = NULL, *walker;
86 u_char *image = NULL, *tmp;
87 size_t image_size = 0;
88
89 int argc = 3; /* two dummies + at least one real */
90 char **argv;
91 size_t *argv_len;
92
93 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD)))
94 return NGX_HTTP_NOT_ALLOWED;
95
96 rc = ngx_http_discard_request_body(r);
97 if (rc != NGX_OK && rc != NGX_AGAIN)
98 return rc;
99
100 if ((rc = ngx_http_rrd_graph_parse_uri(r, &argc, &argv, &argv_len)) != NGX_OK)
101 return rc;
102
103 image_info = rrd_graph_v(argc, argv);
104
105 if (rrd_test_error()) {
106 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mod_rrd_graph: RRD graph failed: %s", rrd_get_error());
107 rrd_clear_error();
108 rrd_info_free(image_info);
109 return NGX_HTTP_INTERNAL_SERVER_ERROR;
110 }
111
112 for (walker = image_info; walker; walker = walker->next) {
113 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
114 "mod_rrd_graph: found key '%s'", walker->key);
115 if (ngx_strcmp(walker->key, "image") == 0) {
116 image = walker->value.u_blo.ptr;
117 image_size = walker->value.u_blo.size;
118 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
119 "mod_rrd_graph: image size is %d bytes", image_size);
120 }
121 }
122
123 if (image_size) {
124 if ((tmp = ngx_palloc(r->pool, image_size)) == NULL) {
125 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
126 "mod_rrd_graph: Failed to allocate response buffer.");
127 return NGX_HTTP_INTERNAL_SERVER_ERROR;
128 }
129 ngx_memcpy(tmp, image, image_size);
130 rrd_info_free(image_info);
131 return ngx_http_rrd_graph_send_image(r, tmp, image_size);
132 }
133
134 rrd_info_free(image_info);
135
136 return NGX_HTTP_NOT_FOUND;
137 }
138
139 static char *
140 ngx_http_rrd_graph(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
141 {
142 ngx_http_core_loc_conf_t *clcf;
143
144 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
145 clcf->handler = ngx_http_rrd_graph_handler;
146
147 return NGX_CONF_OK;
148 }
149
150 static ngx_int_t
151 ngx_http_rrd_graph_parse_uri(ngx_http_request_t *r, int *argc_ptr,
152 char ***argv_ptr, size_t **argv_len_ptr)
153 {
a67111a Support spaces with quotation marks around strings
Evan Miller authored
154 int i, argc = 3, in_quote = 0;
f1a155e First commit.
Evan Miller authored
155 char **argv;
156 size_t *argv_len;
157 char *tmp, *p;
158 u_char *uri_copy;
159 ngx_http_core_loc_conf_t *clcf;
160 ngx_http_rrd_graph_conf_t *conf;
161
162 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
163
164 if (r->uri.len == clcf->name.len)
165 return NGX_HTTP_NOT_FOUND;
166
167 /* tokenize */
168 if ((uri_copy = ngx_palloc(r->pool, (r->uri.len + 1)*sizeof(char))) == NULL) {
169 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mod_rrd_graph: Failed to copy URI.");
170 return NGX_HTTP_INTERNAL_SERVER_ERROR;
171 }
172
173 ngx_memcpy(uri_copy, r->uri.data, r->uri.len);
174 uri_copy[r->uri.len] = '\0'; /* RRDtool needs null-terminated strings */
175 p = (char *)uri_copy + clcf->name.len;
176
a67111a Support spaces with quotation marks around strings
Evan Miller authored
177 while(*p++) {
178 if (*p == '"') {
179 in_quote = !in_quote;
180 } else if (*p == ' ' && !in_quote) {
f1a155e First commit.
Evan Miller authored
181 argc++;
a67111a Support spaces with quotation marks around strings
Evan Miller authored
182 }
183 }
184
185 if (in_quote)
186 return NGX_ERROR;
f1a155e First commit.
Evan Miller authored
187
188 argv = ngx_palloc(r->pool, argc*sizeof(char *));
189 argv_len = ngx_pcalloc(r->pool, argc*sizeof(size_t));
190 argv[0] = "mod_rrd_graph";
191 argv[1] = "-";
192 argv[2] = p = (char *)uri_copy + clcf->name.len;
193 argc = 3;
194 while (*p) {
a67111a Support spaces with quotation marks around strings
Evan Miller authored
195 if (*p == ' ' && !in_quote) {
f1a155e First commit.
Evan Miller authored
196 *p = '\0';
197 argv[argc++] = p+1;
198 } else {
a67111a Support spaces with quotation marks around strings
Evan Miller authored
199 if (*p == '"')
200 in_quote = !in_quote;
201
f1a155e First commit.
Evan Miller authored
202 argv_len[argc-1]++;
203 }
204 p++;
205 }
206
207 conf = ngx_http_get_module_loc_conf(r, ngx_http_rrd_graph_module);
208 /* splice in the RRD directory root */
209 /* TODO guard against relative paths */
210 for (i=2; i<argc; i++) {
211 if (ngx_strncmp(argv[i], "DEF:", sizeof("DEF:") - 1) == 0) {
212 p = argv[i] + sizeof("DEF:") - 1;
213 if ((p = ngx_strchr(p, '=')) != NULL) {
214 p++;
215 tmp = ngx_pcalloc(r->pool, argv_len[i] + conf->root.len + 1);
216 ngx_memcpy(tmp, argv[i], p - argv[i]); /* prefix */
217 ngx_memcpy(tmp + (p - argv[i]), conf->root.data, conf->root.len); /* root dir */
218 ngx_memcpy(tmp + (p - argv[i]) + conf->root.len, /* suffix */
219 p, argv_len[i] - (p - argv[i]));
220 argv[i] = tmp;
221 }
222 }
223 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
224 "mod_rrd_graph: parsed arg %s", argv[i]);
225 }
226 *argc_ptr = argc;
227 *argv_ptr = argv;
228 *argv_len_ptr = argv_len;
229 return NGX_OK;
230 }
231
232 static ngx_int_t
233 ngx_http_rrd_graph_send_image(ngx_http_request_t *r, u_char *image, size_t image_size)
234 {
235 int rc;
236 ngx_chain_t out;
237
238 out.next = NULL;
239 if ((out.buf = ngx_pcalloc(r->pool, sizeof(ngx_buf_t))) == NULL) {
240 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mod_rrd_graph: Failed to allocate response buffer.");
241 free(image);
242 return NGX_HTTP_INTERNAL_SERVER_ERROR;
243 }
244
245 if (image_size > 4) {
246 if (ngx_strncmp(image, ngx_http_png_header,
247 sizeof(ngx_http_png_header)) == 0) {
248 r->headers_out.content_type.len = sizeof("image/png") - 1;
249 r->headers_out.content_type.data = (u_char *)"image/png";
250 } else if (ngx_strncmp(image, ngx_http_pdf_header,
251 sizeof(ngx_http_pdf_header)) == 0) {
252 r->headers_out.content_type.len = sizeof("application/pdf") - 1;
253 r->headers_out.content_type.data = (u_char *)"application/pdf";
254 } else if (ngx_strncmp(image, ngx_http_eps_header,
255 sizeof(ngx_http_eps_header)) == 0) {
256 r->headers_out.content_type.len = sizeof("application/postscript") - 1;
257 r->headers_out.content_type.data = (u_char *)"application/postscript";
258 } else if (ngx_strncmp(image, ngx_http_svg_header,
259 sizeof(ngx_http_svg_header)) == 0) {
260 r->headers_out.content_type.len = sizeof("image/svg+xml") - 1;
261 r->headers_out.content_type.data = (u_char *)"image/svg+xml";
262 }
263 }
264
265 r->headers_out.status = NGX_HTTP_OK;
266 r->headers_out.content_length_n = image_size;
267
268 rc = ngx_http_send_header(r);
269
270 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
271 free(image);
272 return rc;
273 }
274
275 out.buf->pos = image;
276 out.buf->last = image + image_size;
277
278 out.buf->memory = 1;
279 out.buf->last_buf = 1;
280
281 return ngx_http_output_filter(r, &out);
282 }
283
284 static void *
285 ngx_http_rrd_graph_create_conf(ngx_conf_t *cf)
286 {
287 ngx_http_rrd_graph_conf_t *conf;
288
289 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rrd_graph_conf_t));
290 if (conf == NULL) {
291 return NGX_CONF_ERROR;
292 }
293 return conf;
294 }
295
296 static char *
297 ngx_http_rrd_graph_merge_conf(ngx_conf_t *cf, void *parent, void *child)
298 {
299 ngx_http_rrd_graph_conf_t *prev = parent;
300 ngx_http_rrd_graph_conf_t *conf = child;
301
302 ngx_conf_merge_str_value(conf->root, prev->root, "");
303
304 return NGX_CONF_OK;
305 }
Something went wrong with that request. Please try again.