"Fossies" - the Fresh Open Source Software Archive 
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 /*
18 * mod_rrd.c --- The Apache mod_rrd module provides a set of handlers to
19 * manipulate and display RRD graphs.
20 *
21 * Example config:
22 *
23 * <IfModule mod_rrd.c>
24 * <Directory "/var/lib/collectd/rrd">
25 * Require all granted
26 * </Directory>
27 * Alias /rrd /var/lib/collectd/rrd
28 * <Location /rrd>
29 * RRDGraph on
30 * RRDGraphOption title %{SERVER_NAME}
31 * RRDGraphEnv METHODS %{REQUEST_METHOD}
32 * RRDGraphElement DEF:xifOutOctets=monitor*.rrd:ifOutOctets:AVERAGE "optional/expression/monitor*.rrd" "/optional/path/prefix/"
33 * RRDGraphElement VDEF:xifOutOctetsmax=xifOutOctets+,MAXIMUM
34 * RRDGraphElement CDEF:xcombined=xifOutOctets,1,+
35 * RRDGraphElement LINE1:xifOutOctets#00ff00:Out+Octets :%{SERVER_NAME}
36 * RRDGraphElement AREA:xifOutOctets#00ff00:Out+Octets :%{SERVER_NAME}
37 * RRDGraphElement TICK:xifOutOctets#00ff00:1.0:Failures :%{SERVER_NAME}
38 * RRDGraphElement "VRULE:0#FF0000:dashed line:dashes" :%{SERVER_NAME}
39 * RRDGraphElement "HRULE:0#FF0000:dashed line:dashes" :%{SERVER_NAME}
40 * RRDGraphElement "COMMENT:Foo" %{env:METHODS}
41 * </Location>
42 * </IfModule>
43 *
44 * Returns a dynamically generated graph file with the format controlled
45 * by the given suffix. The graph file in the URL must not already exist,
46 * otherwise the existing file will be returned.
47 *
48 * Options are passed as query parameters, either as a name value pair, or
49 * a name only for options that do not take a parameter.
50 *
51 * Graph elements are passed between & characters.
52 *
53 * The parameters in the query string must be URLEncoded. Most notably the
54 * '+' character is not decoded.
55 *
56 * All RRD files are checked against Apache httpd permissions, and if not
57 * accessible the DEF line is ignored.
58 *
59 * Unlike rrdgraph, DEF lines can accept wildcard filenames. A CDEF is
60 * generated automatically to add the wildcard RRDs together.
61 *
62 * When a LINE, AREA or TICK is rendered, each RRD file that matches the
63 * wildcard will form the basis of the expressions parsed.
64 *
65 * Example call:
66 * curl "http://localhost/rrd/monitor.png?DEF:ifOutOctets=monitor*.rrd:ifOutOctets:AVERAGE&LINE1:ifOutOctets%2300ff00:Out+Octets"
67 *
68 * Notes:
69 * - Write as a handler, not a filter (alas)
70 * - Use rrd_graph_v() to return images in memory buffer
71 *
72 * - GET with Accept of xml - map to rrdtool dump
73 * - PUT with Content-Type of XML - map to rrdtool restore
74 * - PATCH - map to update/updatev
75 * - PROPFIND - map to rrdtool info?
76 *
77 * - Graph handler - map specific path to specific graph.
78 * - Option to expand each wildcard to one line per rrd, to
79 * supporting a combined syntax, eg ifOutOctets+ for all
80 * the DEFs added together using a CDEF, to ifOutOctets* for all the
81 * DEFs multiplied together using a CDEF.
82 *
83 * - rrd_fetch_cb_register / rrd_fetch_fn_cb are too limited - we'll
84 * need to build the DEF values ourselves.
85 */
86
87 #include "apr.h"
88 #include "apr_escape.h"
89 #include "apr_strings.h"
90 #include "apr_buckets.h"
91 #include "apr_fnmatch.h"
92 #include "apr_lib.h"
93 #include "apr_hash.h"
94 #include "apr_tables.h"
95 #include "apr_cstr.h"
96 #include "apr_uuid.h"
97
98 #include "ap_config.h"
99 #include "ap_expr.h"
100 #include "ap_mpm.h"
101 #include "util_filter.h"
102 #include "httpd.h"
103 #include "http_config.h"
104 #include "http_log.h"
105 #include "http_protocol.h"
106 #include "http_request.h"
107
108 #undef PACKAGE_BUGREPORT
109 #undef PACKAGE_NAME
110 #undef PACKAGE_STRING
111 #undef PACKAGE_TARNAME
112 #undef PACKAGE_VERSION
113
114 #include "config.h"
115
116 #include "rrd.h"
117
118 #if HAVE_SYS_XATTR_H
119 #include <sys/xattr.h>
120 #endif
121
122 #if APR_HAS_THREADS
123 static apr_thread_mutex_t *rrd_mutex = NULL;
124 #endif
125
126 module AP_MODULE_DECLARE_DATA rrd_module;
127
128 typedef struct rrd_conf {
129 const char *location;
130 apr_array_header_t *options;
131 apr_array_header_t *elements;
132 apr_hash_t *env;
133 const char *format;
134 int graph;
135 unsigned int location_set:1;
136 unsigned int format_set:1;
137 unsigned int graph_set:1;
138 } rrd_conf;
139
140 typedef struct rrd_ctx {
141 apr_file_t *file;
142 apr_bucket_brigade *bb;
143 } rrd_ctx;
144
145 typedef enum rrd_conf_e {
146 RRD_CONF_DEF,
147 RRD_CONF_CDEF,
148 RRD_CONF_VDEF,
149 RRD_CONF_PRINT,
150 RRD_CONF_GPRINT,
151 RRD_CONF_COMMENT,
152 RRD_CONF_VRULE,
153 RRD_CONF_HRULE,
154 RRD_CONF_LINE,
155 RRD_CONF_AREA,
156 RRD_CONF_TICK,
157 RRD_CONF_SHIFT,
158 RRD_CONF_TEXTALIGN
159 } rrd_conf_e;
160
161 typedef struct rrd_cmd_t rrd_cmd_t;
162
163 typedef struct rrd_def_t {
164 const char *vname;
165 const char *path;
166 const char *dsname;
167 const char *cf;
168 apr_pool_t *pool;
169 apr_array_header_t *requests;
170 ap_expr_info_t *epath;
171 ap_expr_info_t *edirpath;
172 } rrd_def_t;
173
174 typedef struct rrd_vdef_t {
175 const char *vname;
176 const char *dsname;
177 const char *rpn;
178 rrd_cmd_t *ref;
179 } rrd_vdef_t;
180
181 typedef struct rrd_cdef_t {
182 const char *vname;
183 apr_array_header_t *rpns;
184 const char *rpn;
185 rrd_cmd_t *ref;
186 } rrd_cdef_t;
187
188 typedef struct rrd_rpn_t {
189 const char *rpn;
190 rrd_cmd_t *def;
191 } rrd_rpn_t;
192
193 typedef struct rrd_line_t {
194 const char *line;
195 const char *vname;
196 const char *colour;
197 const char *legend;
198 ap_expr_info_t *elegend;
199 const char *args;
200 } rrd_line_t;
201
202 typedef struct rrd_area_t {
203 const char *vname;
204 const char *colour;
205 const char *legend;
206 ap_expr_info_t *elegend;
207 const char *args;
208 } rrd_area_t;
209
210 typedef struct rrd_tick_t {
211 const char *vname;
212 const char *colour;
213 const char *fraction;
214 const char *legend;
215 ap_expr_info_t *elegend;
216 const char *args;
217 } rrd_tick_t;
218
219 typedef struct rrd_shift_t {
220 const char *vname;
221 const char *shift;
222 } rrd_shift_t;
223
224 typedef struct rrd_print_t {
225 const char *vname;
226 const char *format;
227 } rrd_print_t;
228
229 typedef struct rrd_rule_t {
230 const char *val;
231 const char *colour;
232 const char *legend;
233 ap_expr_info_t *elegend;
234 const char *args;
235 } rrd_rule_t;
236
237 typedef struct rrd_element_t {
238 const char *element;
239 const char *legend;
240 ap_expr_info_t *elegend;
241 } rrd_element_t;
242
243 typedef struct rrd_cmd_t {
244 rrd_conf_e type;
245 int num;
246 rrd_cmd_t *def;
247 union {
248 rrd_def_t d;
249 rrd_vdef_t v;
250 rrd_cdef_t c;
251 rrd_line_t l;
252 rrd_area_t a;
253 rrd_rule_t r;
254 rrd_tick_t t;
255 rrd_shift_t s;
256 rrd_element_t e;
257 rrd_print_t p;
258 };
259 } rrd_cmd_t;
260
261 typedef struct rrd_opt_t {
262 const char *key;
263 const char *val;
264 ap_expr_info_t *eval;
265 } rrd_opt_t;
266
267 typedef struct rrd_cmds_t {
268 apr_array_header_t *cmds;
269 apr_array_header_t *opts;
270 apr_hash_t *names;
271 } rrd_cmds_t;
272
273 typedef struct rrd_cb_t {
274 request_rec *r;
275 rrd_cmd_t *cmd;
276 } rrd_cb_t;
277
278 static char *substring_quote(apr_pool_t *p, const char *start, int len,
279 char quote)
280 {
281 char *result = apr_palloc(p, len + 1);
282 char *resp = result;
283 int i;
284
285 for (i = 0; i < len; ++i) {
286 if (start[i] == '\\' && (start[i + 1] == '\\'
287 || (quote && start[i + 1] == quote)))
288 *resp++ = start[++i];
289 else
290 *resp++ = start[i];
291 }
292
293 *resp++ = '\0';
294 return result;
295 }
296
297 static char *getword_quote(apr_pool_t *p, const char **line, char stop)
298 {
299 const char *str = *line, *strend;
300 char *res;
301 char quote;
302
303 if (!*str) {
304 *line = str;
305 return "";
306 }
307
308 if ((quote = *str) == '"' || quote == '\'') {
309 strend = str + 1;
310 while (*strend && *strend != quote) {
311 if (*strend == '\\' && strend[1] &&
312 (strend[1] == quote || strend[1] == '\\')) {
313 strend += 2;
314 }
315 else {
316 ++strend;
317 }
318 }
319 res = substring_quote(p, str + 1, strend - str - 1, quote);
320
321 if (*strend == quote) {
322 ++strend;
323 }
324 while (*strend && *strend != stop) {
325 ++strend;
326 }
327 }
328 else {
329 strend = str;
330 while (*strend && *strend != stop) {
331 ++strend;
332 }
333
334 res = substring_quote(p, str, strend - str, 0);
335 }
336
337 if (*strend == stop) {
338 ++strend;
339 }
340 *line = strend;
341 return res;
342 }
343
344 static apr_status_t escape_colon(char *escaped, const char *str,
345 apr_ssize_t slen, apr_size_t *len)
346 {
347 unsigned char *d;
348 const unsigned char *s;
349 apr_size_t size = 1;
350 int found = 0;
351
352 d = (unsigned char *) escaped;
353 s = (const unsigned char *) str;
354
355 if (s) {
356 if (d) {
357 for (; *s && slen; ++s, slen--) {
358 if (':' == *s) {
359 *d++ = '\\';
360 size++;
361 found = 1;
362 }
363 *d++ = *s;
364 size++;
365 }
366 *d = '\0';
367 }
368 else {
369 for (; *s && slen; ++s, slen--) {
370 if (':' == *s) {
371 size++;
372 found = 1;
373 }
374 size++;
375 }
376 }
377 }
378
379 if (len) {
380 *len = size;
381 }
382 if (!found) {
383 return APR_NOTFOUND;
384 }
385
386 return APR_SUCCESS;
387 }
388
389 static const char *pescape_colon(apr_pool_t *p, const char *str)
390 {
391 apr_size_t len;
392
393 switch (escape_colon(NULL, str, APR_ESCAPE_STRING, &len)) {
394 case APR_SUCCESS: {
395 char *cmd = apr_palloc(p, len);
396 escape_colon(cmd, str, APR_ESCAPE_STRING, NULL);
397 return cmd;
398 }
399 case APR_NOTFOUND: {
400 break;
401 }
402 }
403
404 return str;
405 }
406
407 static void log_message(request_rec *r, apr_status_t status,
408 const char *message, const char *err)
409 {
410
411 /* Allow "error-notes" string to be printed by ap_send_error_response() */
412 apr_table_setn(r->notes, "verbose-error-to", "*");
413
414 if (err) {
415
416 apr_table_setn(r->notes, "error-notes",
417 ap_escape_html(r->pool,
418 apr_pstrcat(r->pool, "RRD error: ", message, ": ", err,
419 NULL)));
420
421 ap_log_rerror(
422 APLOG_MARK, APLOG_ERR, status, r, "mod_rrd: "
423 "%s (%s)", message, err);
424 }
425 else {
426
427 apr_table_setn(r->notes, "error-notes",
428 ap_escape_html(r->pool,
429 apr_pstrcat(r->pool, "RRD error: ", message, NULL)));
430
431 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "mod_rrd: "
432 "%s", message);
433 }
434
435 }
436
437 static int options_wadl(request_rec *r, rrd_conf *conf)
438 {
439 int rv;
440
441 /* discard the request body */
442 if ((rv = ap_discard_request_body(r)) != OK) {
443 return rv;
444 }
445
446 ap_set_content_type(r, "application/vnd.sun.wadl+xml");
447
448 ap_rprintf(r,
449 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
450 "<wadl:application xmlns:wadl=\"http://wadl.dev.java.net/2009/02\"\n"
451 " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
452 " xsi:schemaLocation=\"http://wadl.dev.java.net/2009/02 file:wadl.xsd\">\n"
453 " <wadl:resources base=\"%s\">\n"
454 " <wadl:resource path=\"/\">\n"
455 " <wadl:method name=\"GET\" id=\"\">\n"
456 " </wadl:method>\n"
457 " </wadl:resource>\n"
458 " </wadl:resources>\n"
459 "</wadl:application>\n",
460 conf->location ? conf->location :
461 apr_pstrcat(r->pool, ap_http_scheme(r), "://",
462 r->server->server_hostname, r->uri, NULL));
463
464 return OK;
465 }
466
467 static const char *lookup_content_type(const char *format)
468 {
469 switch(format[0]) {
470 case 'p':
471 case 'P':
472 if (strcasecmp(format, "PNG") == 0) {
473 return "image/png";
474 }
475 if (strcasecmp(format, "PDF") == 0) {
476 return "application/pdf";
477 }
478 break;
479 case 's':
480 case 'S':
481 if (strcasecmp(format, "SVG") == 0) {
482 return "image/svg+xml";
483 }
484 if (strcasecmp(format, "SSV") == 0) {
485 return "text/plain";
486 }
487 break;
488 case 'e':
489 case 'E':
490 if (strcasecmp(format, "EPS") == 0) {
491 return "application/eps";
492 }
493 break;
494 case 'x':
495 case 'X':
496 if (strcasecmp(format, "XML") == 0) {
497 return "application/xml";
498 }
499 if (strcasecmp(format, "XMLENUM") == 0) {
500 return "application/xml";
501 }
502 break;
503 case 'j':
504 case 'J':
505 if (strcasecmp(format, "JSON") == 0) {
506 return "application/json";
507 }
508 if (strcasecmp(format, "JSONTIME") == 0) {
509 return "application/json";
510 }
511 break;
512 case 'c':
513 case 'C':
514 if (strcasecmp(format, "CSV") == 0) {
515 return "text/csv";
516 }
517 break;
518 case 't':
519 case 'T':
520 if (strcasecmp(format, "TSV") == 0) {
521 return "text/tab-separated-values";
522 }
523 break;
524 }
525 return NULL;
526 }
527
528 static const char *parse_rrdgraph_suffix(request_rec *r)
529 {
530 const char *fname = ap_strrchr_c(r->filename, '/');
531
532 if (fname) {
533 /* PNG|SVG|EPS|PDF|XML|XMLENUM|JSON|JSONTIME|CSV|TSV|SSV */
534 const char *suffix = ap_strrchr_c(fname, '.');
535 if (suffix) {
536 switch (suffix[1]) {
537 case 'p':
538 case 'P':
539 if (strcasecmp(suffix, ".png") == 0) {
540 return "PNG";
541 }
542 if (strcasecmp(suffix, ".pdf") == 0) {
543 return "PDF";
544 }
545 break;
546 case 's':
547 case 'S':
548 if (strcasecmp(suffix, ".svg") == 0) {
549 return "SVG";
550 }
551 if (strcasecmp(suffix, ".ssv") == 0) {
552 return "SSV";
553 }
554 break;
555 case 'e':
556 case 'E':
557 if (strcasecmp(suffix, ".eps") == 0) {
558 return "EPS";
559 }
560 break;
561 case 'x':
562 case 'X':
563 if (strcasecmp(suffix, ".xml") == 0) {
564 return "XML";
565 }
566 if (strcasecmp(suffix, ".xmlenum") == 0) {
567 return "XMLENUM";
568 }
569 break;
570 case 'j':
571 case 'J':
572 if (strcasecmp(suffix, ".json") == 0) {
573 return "JSON";
574 }
575 if (strcasecmp(suffix, ".jsontime") == 0) {
576 return "JSONTIME";
577 }
578 break;
579 case 'c':
580 case 'C':
581 if (strcasecmp(suffix, ".csv") == 0) {
582 return "CSV";
583 }
584 break;
585 case 't':
586 case 'T':
587 if (strcasecmp(suffix, ".tsv") == 0) {
588 return "TSV";
589 }
590 break;
591 }
592 }
593 }
594 return NULL;
595 }
596
597 static int parse_element(apr_pool_t *p, const char *element, ap_expr_info_t *expr1,
598 ap_expr_info_t *expr2, apr_array_header_t *cmds)
599 {
600 switch (element[0]) {
601 case 'A':
602 /* handle AREA sections */
603 if (strncmp(element, "AREA:", 5) == 0) {
604 char *vncol;
605 rrd_cmd_t *cmd = apr_array_push(cmds);
606 cmd->type = RRD_CONF_AREA;
607 element += 5;
608 vncol = ap_getword(p, &element, ':');
609 cmd->a.legend = getword_quote(p, &element, ':');
610 cmd->a.elegend = expr1;
611 cmd->a.args = element;
612 cmd->a.vname = apr_cstr_tokenize("#", &vncol);
613 cmd->a.colour = vncol;
614 return 1;
615 }
616 break;
617 case 'C':
618 /* handle CDEF sections */
619 if (strncmp(element, "CDEF:", 5) == 0) {
620 char *rpn, *rpns;
621 rrd_cmd_t *cmd = apr_array_push(cmds);
622 cmd->type = RRD_CONF_CDEF;
623 element += 5;
624 cmd->c.vname = ap_getword(p, &element, '=');
625 cmd->c.rpns = apr_array_make(p, 4, sizeof(rrd_rpn_t));
626 cmd->c.rpn = element;
627 rpns = apr_pstrdup(p, element);
628 while ((rpn = apr_cstr_tokenize(",", &rpns))) {
629 rrd_rpn_t *rp = apr_array_push(cmd->c.rpns);
630 rp->rpn = rpn;
631 }
632 return 1;
633 }
634 /* handle COMMENT sections */
635 if (strncmp(element, "COMMENT:", 7) == 0) {
636 rrd_cmd_t *cmd = apr_array_push(cmds);
637 cmd->type = RRD_CONF_COMMENT;
638 cmd->e.element = ap_getword(p, &element, ':');
639 cmd->a.legend = getword_quote(p, &element, ':');
640 cmd->e.elegend = expr1;
641 return 1;
642 }
643 break;
644 case 'D':
645 /* handle DEF sections */
646 if (strncmp(element, "DEF:", 4) == 0) {
647 rrd_cmd_t *cmd = apr_array_push(cmds);
648 cmd->type = RRD_CONF_DEF;
649 element += 4;
650 cmd->d.vname = ap_getword(p, &element, '=');
651 cmd->d.path = ap_getword(p, &element, ':');
652 cmd->d.dsname = ap_getword(p, &element, ':');
653 cmd->d.cf = element;
654 cmd->d.pool = p;
655 cmd->d.requests = apr_array_make(p, 10, sizeof(request_rec *));
656 cmd->d.epath = expr1;
657 cmd->d.edirpath = expr2;
658 return 1;
659 }
660 break;
661 case 'G':
662 /* handle GPRINT sections */
663 if (strncmp(element, "GPRINT:", 7) == 0) {
664 rrd_cmd_t *cmd = apr_array_push(cmds);
665 cmd->type = RRD_CONF_GPRINT;
666 element += 7;
667 cmd->p.vname = ap_getword(p, &element, ':');
668 cmd->p.format = element;
669 return 1;
670 }
671 break;
672 case 'H':
673 /* handle HRULE sections */
674 if (strncmp(element, "HRULE:", 6) == 0) {
675 char *vncol;
676 rrd_cmd_t *cmd = apr_array_push(cmds);
677 cmd->type = RRD_CONF_HRULE;
678 element += 6;
679 vncol = ap_getword(p, &element, ':');
680 cmd->r.legend = getword_quote(p, &element, ':');
681 cmd->r.elegend = expr1;
682 cmd->r.args = element;
683 cmd->r.val = apr_cstr_tokenize("#", &vncol);
684 cmd->r.colour = vncol;
685 return 1;
686 }
687 break;
688 case 'L':
689 /* handle LINE sections */
690 if (strncmp(element, "LINE", 4) == 0) {
691 char *vncol;
692 rrd_cmd_t *cmd = apr_array_push(cmds);
693 cmd->type = RRD_CONF_LINE;
694 cmd->l.line = ap_getword(p, &element, ':');
695 vncol = ap_getword(p, &element, ':');
696 cmd->l.legend = getword_quote(p, &element, ':');
697 cmd->l.elegend = expr1;
698 cmd->l.args = element;
699 cmd->l.vname = apr_cstr_tokenize("#", &vncol);
700 cmd->l.colour = vncol;
701 cmd->l.elegend = expr1;
702 return 1;
703 }
704 break;
705 case 'P':
706 /* handle PRINT sections */
707 if (strncmp(element, "PRINT:", 6) == 0) {
708 rrd_cmd_t *cmd = apr_array_push(cmds);
709 cmd->type = RRD_CONF_PRINT;
710 element += 6;
711 cmd->p.vname = ap_getword(p, &element, ':');
712 cmd->p.format = element;
713 return 1;
714 }
715 break;
716 case 'S':
717 /* handle SHIFT sections */
718 if (strncmp(element, "SHIFT:", 6) == 0) {
719 rrd_cmd_t *cmd = apr_array_push(cmds);
720 cmd->type = RRD_CONF_SHIFT;
721 element += 6;
722 cmd->s.vname = ap_getword(p, &element, ':');
723 cmd->s.shift = element;
724 return 1;
725 }
726 break;
727 case 'T':
728 /* handle TICK sections */
729 if (strncmp(element, "TICK:", 5) == 0) {
730 char *vncol;
731 rrd_cmd_t *cmd = apr_array_push(cmds);
732 cmd->type = RRD_CONF_TICK;
733 element += 5;
734 vncol = ap_getword(p, &element, ':');
735 cmd->t.fraction = ap_getword(p, &element, ':');
736 cmd->t.legend = getword_quote(p, &element, ':');
737 cmd->t.elegend = expr1;
738 cmd->t.args = element;
739 cmd->t.vname = apr_cstr_tokenize("#", &vncol);
740 cmd->t.colour = vncol;
741 return 1;
742 }
743 /* handle TEXTALIGN sections */
744 else if (strncmp(element, "TEXTALIGN:", 10) == 0) {
745 rrd_cmd_t *cmd = apr_array_push(cmds);
746 cmd->type = RRD_CONF_TEXTALIGN;
747 cmd->e.element = ap_getword(p, &element, ':');
748 cmd->a.legend = getword_quote(p, &element, ':');
749 cmd->e.elegend = expr1;
750 return 1;
751 }
752 break;
753 case 'V':
754 /* handle VDEF sections */
755 if (strncmp(element, "VDEF:", 5) == 0) {
756 rrd_cmd_t *cmd = apr_array_push(cmds);
757 cmd->type = RRD_CONF_VDEF;
758 element += 5;
759 cmd->v.vname = ap_getword(p, &element, '=');
760 cmd->v.dsname = ap_getword(p, &element, ',');
761 cmd->v.rpn = element;
762 return 1;
763 }
764 /* handle VRULE sections */
765 if (strncmp(element, "VRULE:", 6) == 0) {
766 char *vncol;
767 rrd_cmd_t *cmd = apr_array_push(cmds);
768 cmd->type = RRD_CONF_VRULE;
769 element += 6;
770 vncol = ap_getword(p, &element, ':');
771 cmd->r.legend = getword_quote(p, &element, ':');
772 cmd->r.elegend = expr1;
773 cmd->r.args = element;
774 cmd->r.val = apr_cstr_tokenize("#", &vncol);
775 cmd->r.colour = vncol;
776 return 1;
777 }
778 break;
779 }
780 return 0;
781 }
782
783 static int parse_option(apr_pool_t *p, const char *key, const char *val,
784 ap_expr_info_t *eval, apr_array_header_t *opts)
785 {
786 /* with value */
787 if (val) {
788 switch (key[0]) {
789 case 'b':
790 /* [--border width] */
791 if (strcmp(key, "border") == 0) {
792 rrd_opt_t *opt = apr_array_push(opts);
793 opt->key = key;
794 opt->val = val;
795 opt->eval = eval;
796 return 1;
797 }
798 break;
799 case 'c':
800 /* [-c|--color COLORTAG#rrggbb[aa]] */
801 if (strcmp(key, "color") == 0) {
802 rrd_opt_t *opt = apr_array_push(opts);
803 opt->key = key;
804 opt->val = val;
805 opt->eval = eval;
806 return 1;
807 }
808 break;
809 case 'f':
810 /* [-n|--font FONTTAG:size:font] */
811 if (strcmp(key, "font") == 0) {
812 rrd_opt_t *opt = apr_array_push(opts);
813 opt->key = key;
814 opt->val = val;
815 opt->eval = eval;
816 return 1;
817 }
818 /* [-R|--font-render-mode {normal,light,mono}] */
819 if (strcmp(key, "font-render-mode") == 0) {
820 rrd_opt_t *opt = apr_array_push(opts);
821 opt->key = key;
822 opt->val = val;
823 opt->eval = eval;
824 return 1;
825 }
826 /* [-B|--font-smoothing-threshold size] */
827 if (strcmp(key, "font-smoothing-threshold") == 0) {
828 rrd_opt_t *opt = apr_array_push(opts);
829 opt->key = key;
830 opt->val = val;
831 opt->eval = eval;
832 return 1;
833 }
834 break;
835 case 'g':
836 /* [-G|--graph-render-mode {normal,mono}] */
837 if (strcmp(key, "graph-render-mode") == 0) {
838 rrd_opt_t *opt = apr_array_push(opts);
839 opt->key = key;
840 opt->val = val;
841 opt->eval = eval;
842 return 1;
843 }
844 break;
845 case 'h':
846 /* [-h|--height pixels] */
847 if (strcmp(key, "height") == 0) {
848 rrd_opt_t *opt = apr_array_push(opts);
849 opt->key = key;
850 opt->val = val;
851 opt->eval = eval;
852 return 1;
853 }
854 break;
855 case 'l':
856 /* [--left-axis-format format] */
857 if (strcmp(key, "left-axis-format") == 0) {
858 rrd_opt_t *opt = apr_array_push(opts);
859 opt->key = key;
860 opt->val = val;
861 opt->eval = eval;
862 return 1;
863 }
864 /* [-l|--lower-limit value] */
865 if (strcmp(key, "lower-limit") == 0) {
866 rrd_opt_t *opt = apr_array_push(opts);
867 opt->key = key;
868 opt->val = val;
869 opt->eval = eval;
870 return 1;
871 }
872 break;
873 case 'r':
874 /* [--right-axis scale:shift] */
875 if (strcmp(key, "right-axis") == 0) {
876 rrd_opt_t *opt = apr_array_push(opts);
877 opt->key = key;
878 opt->val = val;
879 opt->eval = eval;
880 return 1;
881 }
882 /* [--right-axis-label label] */
883 if (strcmp(key, "right-axis-label") == 0) {
884 rrd_opt_t *opt = apr_array_push(opts);
885 opt->key = key;
886 opt->val = val;
887 opt->eval = eval;
888 return 1;
889 }
890 /* [--right-axis-format format] */
891 if (strcmp(key, "right-axis-format") == 0) {
892 rrd_opt_t *opt = apr_array_push(opts);
893 opt->key = key;
894 opt->val = val;
895 opt->eval = eval;
896 return 1;
897 }
898 break;
899 case 's':
900 /* [-S|--step seconds] */
901 if (strcmp(key, "step") == 0) {
902 rrd_opt_t *opt = apr_array_push(opts);
903 opt->key = key;
904 opt->val = val;
905 opt->eval = eval;
906 return 1;
907 }
908 break;
909 case 't':
910 /* [-T|--tabwidth width] */
911 if (strcmp(key, "tabwidth") == 0) {
912 rrd_opt_t *opt = apr_array_push(opts);
913 opt->key = key;
914 opt->val = val;
915 opt->eval = eval;
916 return 1;
917 }
918 /* [-t|--title string] */
919 if (strcmp(key, "title") == 0) {
920 rrd_opt_t *opt = apr_array_push(opts);
921 opt->key = key;
922 opt->val = val;
923 opt->eval = eval;
924 return 1;
925 }
926 break;
927 case 'u':
928 /* [-X|--units-exponent value] */
929 if (strcmp(key, "units-exponent") == 0) {
930 rrd_opt_t *opt = apr_array_push(opts);
931 opt->key = key;
932 opt->val = val;
933 opt->eval = eval;
934 return 1;
935 }
936 /* [-L|--units-length value] */
937 if (strcmp(key, "units-length") == 0) {
938 rrd_opt_t *opt = apr_array_push(opts);
939 opt->key = key;
940 opt->val = val;
941 opt->eval = eval;
942 return 1;
943 }
944 break;
945 case 'v':
946 /* [-v|--vertical-label string] */
947 if (strcmp(key, "vertical-label") == 0) {
948 rrd_opt_t *opt = apr_array_push(opts);
949 opt->key = key;
950 opt->val = val;
951 opt->eval = eval;
952 return 1;
953 }
954 break;
955 case 'w':
956 /* [-w|--width pixels] */
957 if (strcmp(key, "width") == 0) {
958 rrd_opt_t *opt = apr_array_push(opts);
959 opt->key = key;
960 opt->val = val;
961 opt->eval = eval;
962 return 1;
963 }
964 /* [-W|--watermark string] */
965 if (strcmp(key, "watermark") == 0) {
966 rrd_opt_t *opt = apr_array_push(opts);
967 opt->key = key;
968 opt->val = val;
969 opt->eval = eval;
970 return 1;
971 }
972 break;
973 case 'x':
974 /* [-x|--x-grid x-axis grid and label] */
975 if (strcmp(key, "x-grid") == 0) {
976 rrd_opt_t *opt = apr_array_push(opts);
977 opt->key = key;
978 opt->val = val;
979 opt->eval = eval;
980 return 1;
981 }
982 break;
983 case 'y':
984 /* [-y|--y-grid y-axis grid and label] */
985 if (strcmp(key, "y-grid") == 0) {
986 rrd_opt_t *opt = apr_array_push(opts);
987 opt->key = key;
988 opt->val = val;
989 opt->eval = eval;
990 return 1;
991 }
992 break;
993 case 'z':
994 /* [-m|--zoom factor] */
995 if (strcmp(key, "zoom") == 0) {
996 rrd_opt_t *opt = apr_array_push(opts);
997 opt->key = key;
998 opt->val = val;
999 opt->eval = eval;
1000 return 1;
1001 }
1002 break;
1003 }
1004 }
1005
1006 /* no value */
1007 else {
1008 switch (key[0]) {
1009 case 'a':
1010 /* [-Y|--alt-y-grid] */
1011 if (strcmp(key, "alt-y-grid") == 0) {
1012 rrd_opt_t *opt = apr_array_push(opts);
1013 opt->key = key;
1014 return 1;
1015 }
1016 /* [-A|--alt-autoscale] */
1017 if (strcmp(key, "alt-autoscale") == 0) {
1018 rrd_opt_t *opt = apr_array_push(opts);
1019 opt->key = key;
1020 return 1;
1021 }
1022 /* [-M|--alt-autoscale-max] */
1023 if (strcmp(key, "alt-autoscale-max") == 0) {
1024 rrd_opt_t *opt = apr_array_push(opts);
1025 opt->key = key;
1026 return 1;
1027 }
1028 break;
1029 case 'f':
1030 /* [--full-size-mode] */
1031 if (strcmp(key, "full-size-mode") == 0) {
1032 rrd_opt_t *opt = apr_array_push(opts);
1033 opt->key = key;
1034 return 1;
1035 }
1036 /* [-F|--force-rules-legend] */
1037 if (strcmp(key, "force-rules-legend") == 0) {
1038 rrd_opt_t *opt = apr_array_push(opts);
1039 opt->key = key;
1040 return 1;
1041 }
1042 break;
1043 case 'l':
1044 /* [-o|--logarithmic] */
1045 if (strcmp(key, "logarithmic") == 0) {
1046 rrd_opt_t *opt = apr_array_push(opts);
1047 opt->key = key;
1048 return 1;
1049 }
1050 /* [-z|--lazy] */
1051 if (strcmp(key, "lazy") == 0) {
1052 rrd_opt_t *opt = apr_array_push(opts);
1053 opt->key = key;
1054 return 1;
1055 }
1056 break;
1057 case 'n':
1058 /* [-g|--no-legend] */
1059 if (strcmp(key, "no-legend") == 0) {
1060 rrd_opt_t *opt = apr_array_push(opts);
1061 opt->key = key;
1062 return 1;
1063 }
1064 /* [-N|--no-gridfit] */
1065 if (strcmp(key, "no-gridfit") == 0) {
1066 rrd_opt_t *opt = apr_array_push(opts);
1067 opt->key = key;
1068 return 1;
1069 }
1070 break;
1071 case 'o':
1072 /* [-j|--only-graph] */
1073 if (strcmp(key, "only-graph") == 0) {
1074 rrd_opt_t *opt = apr_array_push(opts);
1075 opt->key = key;
1076 return 1;
1077 }
1078 break;
1079 case 'p':
1080 /* [-P|--pango-markup] */
1081 if (strcmp(key, "pango-markup") == 0) {
1082 rrd_opt_t *opt = apr_array_push(opts);
1083 opt->key = key;
1084 return 1;
1085 }
1086 break;
1087 case 'r':
1088 /* [-r|--rigid] */
1089 if (strcmp(key, "rigid") == 0) {
1090 rrd_opt_t *opt = apr_array_push(opts);
1091 opt->key = key;
1092 return 1;
1093 }
1094 break;
1095 case 's':
1096 /* [-E|--slope-mode] */
1097 if (strcmp(key, "slope-mode") == 0) {
1098 rrd_opt_t *opt = apr_array_push(opts);
1099 opt->key = key;
1100 return 1;
1101 }
1102 break;
1103 case 'u':
1104 /* [-u|--upper-limit value] */
1105 if (strcmp(key, "upper-limit") == 0) {
1106 rrd_opt_t *opt = apr_array_push(opts);
1107 opt->key = key;
1108 return 1;
1109 }
1110 /* [-Z|--use-nan-for-all-missing-data] */
1111 if (strcmp(key, "use-nan-for-all-missing-data") == 0) {
1112 rrd_opt_t *opt = apr_array_push(opts);
1113 opt->key = key;
1114 return 1;
1115 }
1116 break;
1117 }
1118
1119 }
1120 return 0;
1121 }
1122
1123 static int parse_query(request_rec *r, rrd_cmds_t **pcmds)
1124 {
1125 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
1126 &rrd_module);
1127
1128 char *arg, *args;
1129 rrd_cmds_t *cmds = *pcmds = apr_pcalloc(r->pool, sizeof(rrd_cmds_t));
1130 int optnum = 0, cmdnum = 0;
1131
1132 cmds->names = apr_hash_make(r->pool);
1133
1134 /* count the query string */
1135 args = apr_pstrdup(r->pool, r->args);
1136 while ((arg = apr_cstr_tokenize("&", &args))) {
1137 if (apr_islower(arg[0])) {
1138 optnum++;
1139 }
1140 else {
1141 cmdnum++;
1142 }
1143 }
1144
1145 cmds->opts = apr_array_make(r->pool, optnum + conf->options->nelts, sizeof(rrd_opt_t));
1146 cmds->cmds = apr_array_make(r->pool, cmdnum + conf->elements->nelts, sizeof(rrd_cmd_t));
1147
1148 /* pass the system wide options */
1149 apr_array_cat(cmds->opts, conf->options);
1150 apr_array_cat(cmds->cmds, conf->elements);
1151
1152 /* parse the query string */
1153 args = apr_pstrdup(r->pool, r->args);
1154 while ((arg = apr_cstr_tokenize("&", &args))) {
1155 const char *key, *val;
1156 char *element;
1157
1158 if (!arg[0]) {
1159 continue;
1160 }
1161
1162 element = apr_pstrdup(r->pool, apr_punescape_url(r->pool, arg, NULL, NULL, 0));
1163 if (!element) {
1164 log_message(r, APR_SUCCESS,
1165 apr_psprintf(r->pool,
1166 "The following element could not be unescaped: %s", arg), NULL);
1167 return HTTP_BAD_REQUEST;
1168 }
1169
1170 if (parse_element(r->pool, element, NULL, NULL, cmds->cmds)) {
1171 continue;
1172 }
1173
1174 /* try parse options that take the form of name value pairs */
1175 key = apr_cstr_tokenize("=", &element);
1176 val = element;
1177
1178 if (parse_option(r->pool, key, val, NULL, cmds->opts)) {
1179 continue;
1180 }
1181
1182 /* else unrecognised option */
1183 log_message(r, APR_SUCCESS,
1184 apr_psprintf(r->pool,
1185 "Query was not recognised: %s", arg), NULL);
1186 return HTTP_BAD_REQUEST;
1187 }
1188
1189 return OK;
1190 }
1191
1192 static const char *resolve_def_cb(ap_dir_match_t *w, const char *fname)
1193 {
1194 rrd_cb_t *ctx = w->ctx;
1195 request_rec *rr;
1196
1197 rr = ap_sub_req_lookup_file(fname, ctx->r, NULL);
1198
1199 if (rr->status == HTTP_OK) {
1200 APR_ARRAY_PUSH(ctx->cmd->d.requests, request_rec *) = rr;
1201 ctx->cmd->num++;
1202 }
1203 else {
1204 ap_log_rerror(
1205 APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, rr, "mod_rrd: Access to path returned %d, ignoring: %s",
1206 rr->status, fname);
1207 }
1208
1209 return NULL;
1210 }
1211
1212 static int resolve_def(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1213 {
1214 ap_dir_match_t w;
1215 rrd_cb_t ctx;
1216 apr_pool_t *ptemp;
1217 const char *last, *path, *dirpath = r->filename;
1218 apr_hash_index_t *hi, *hi2;
1219 apr_hash_t *set;
1220
1221 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
1222 &rrd_module);
1223
1224 apr_pool_create(&ptemp, r->pool);
1225
1226 /* process the wildcards */
1227 ctx.r = r;
1228 ctx.cmd = cmd;
1229
1230 w.prefix = "rrd path: ";
1231 w.p = r->pool;
1232 w.ptemp = ptemp;
1233 w.flags = AP_DIR_FLAG_OPTIONAL | AP_DIR_FLAG_RECURSIVE;
1234 w.cb = resolve_def_cb;
1235 w.ctx = &ctx;
1236 w.depth = 0;
1237
1238 path = cmd->d.path;
1239 if (cmd->d.epath) {
1240 const char *err = NULL;
1241 path = ap_expr_str_exec(r, cmd->d.epath, &err);
1242 if (err) {
1243 log_message(r, APR_SUCCESS,
1244 apr_psprintf(r->pool,
1245 "While evaluating an element expression: %s", err), NULL);
1246 return HTTP_INTERNAL_SERVER_ERROR;
1247 }
1248 }
1249
1250 if (cmd->d.edirpath) {
1251 const char *err = NULL;
1252 dirpath = ap_expr_str_exec(r, cmd->d.edirpath, &err);
1253 if (err) {
1254 log_message(r, APR_SUCCESS,
1255 apr_psprintf(r->pool,
1256 "While evaluating an element expression: %s", err), NULL);
1257 return HTTP_INTERNAL_SERVER_ERROR;
1258 }
1259 }
1260 else {
1261 last = strrchr(r->filename, '/');
1262 if (last) {
1263 dirpath = apr_pstrndup(ptemp, r->filename, last - r->filename);
1264 }
1265 }
1266
1267 ap_log_rerror(
1268 APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
1269 "mod_rrd: Attempting to match wildcard RRD path '%s' against base '%s'",
1270 path, dirpath);
1271
1272 const char *err = ap_dir_fnmatch(&w, dirpath, path);
1273 if (err) {
1274 log_message(r, APR_SUCCESS,
1275 apr_psprintf(r->pool,
1276 "While parsing DEF path '%s': %s", path, err), NULL);
1277 return HTTP_BAD_REQUEST;
1278 }
1279
1280 /* process the environment lookup */
1281 set = apr_hash_make(ptemp);
1282 for (hi = apr_hash_first(NULL, conf->env); hi; hi = apr_hash_next(hi)) {
1283 const char *err = NULL, *key, *val;
1284 ap_expr_info_t *eval;
1285 void *v;
1286 const void *k;
1287 int j;
1288
1289 apr_hash_this(hi, &k, NULL, &v);
1290 key = k;
1291 eval = v;
1292
1293 for (j = 0; j < cmd->d.requests->nelts; ++j) {
1294 request_rec *rr = APR_ARRAY_IDX(cmd->d.requests, j, request_rec *);
1295
1296 val = ap_expr_str_exec(rr, eval, &err);
1297 if (err) {
1298 log_message(r, APR_SUCCESS,
1299 apr_psprintf(r->pool,
1300 "While evaluating an element expression: %s", err), NULL);
1301 return HTTP_INTERNAL_SERVER_ERROR;
1302 }
1303 if (val && val[0]) {
1304 apr_hash_set(set, val, APR_HASH_KEY_STRING, val);
1305 }
1306
1307 }
1308
1309 if (apr_hash_count(set)) {
1310 apr_array_header_t *arr = apr_array_make(ptemp, apr_hash_count(set), sizeof(const char *));
1311 for (hi2 = apr_hash_first(NULL, set); hi2; hi2 = apr_hash_next(hi2)) {
1312 apr_hash_this(hi2, apr_array_push(arr), NULL, NULL);
1313 }
1314 apr_table_setn(r->subprocess_env, key, apr_array_pstrcat(r->pool, arr, ','));
1315
1316 }
1317 apr_hash_clear(set);
1318 }
1319
1320 apr_pool_destroy(ptemp);
1321
1322 cmd->def = cmd;
1323 apr_hash_set(cmds->names, cmd->d.vname, APR_HASH_KEY_STRING, cmd);
1324
1325 return OK;
1326 }
1327
1328 static int resolve_vdef(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1329 {
1330 cmd->v.ref = apr_hash_get(cmds->names, cmd->v.dsname, APR_HASH_KEY_STRING);
1331 if (cmd->v.ref) {
1332 cmd->def = cmd->v.ref->def;
1333 }
1334
1335 if (!cmd->v.ref) {
1336 log_message(r, APR_SUCCESS,
1337 apr_psprintf(r->pool,
1338 "While parsing VDEF '%s': '%s' was not found", cmd->v.vname, cmd->v.dsname), NULL);
1339 return HTTP_BAD_REQUEST;
1340 }
1341 else {
1342 cmd->num = cmd->v.ref->num;
1343 }
1344
1345 apr_hash_set(cmds->names, cmd->v.vname, APR_HASH_KEY_STRING, cmd);
1346 return OK;
1347 }
1348
1349 static int resolve_cdef(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1350 {
1351 int i;
1352
1353 for (i = 0; i < cmd->c.rpns->nelts; ++i) {
1354 rrd_rpn_t *rp = &((rrd_rpn_t *) cmd->c.rpns->elts)[i];
1355
1356 if (!cmd->c.ref) {
1357 rrd_cmd_t *ref = apr_hash_get(cmds->names, rp->rpn,
1358 APR_HASH_KEY_STRING);
1359 if (ref) {
1360 cmd->c.ref = ref;
1361 rp->def = cmd->def = ref->def;
1362 }
1363 }
1364
1365 }
1366 if (cmd->c.ref) {
1367 cmd->num = cmd->c.ref->num;
1368 }
1369 apr_hash_set(cmds->names, cmd->c.vname, APR_HASH_KEY_STRING, cmd);
1370 return OK;
1371 }
1372
1373 static int resolve_area(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1374 {
1375 rrd_cmd_t *ref;
1376
1377 ref = apr_hash_get(cmds->names, cmd->a.vname, APR_HASH_KEY_STRING);
1378 if (ref) {
1379 cmd->def = ref->def;
1380 }
1381 else {
1382 log_message(r, APR_SUCCESS,
1383 apr_psprintf(r->pool,
1384 "While parsing AREA: '%s' was not found", cmd->a.vname), NULL);
1385 return HTTP_BAD_REQUEST;
1386 }
1387
1388 return OK;
1389 }
1390
1391 static int resolve_line(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1392 {
1393 rrd_cmd_t *ref;
1394
1395 ref = apr_hash_get(cmds->names, cmd->l.vname, APR_HASH_KEY_STRING);
1396 if (ref) {
1397 cmd->def = ref->def;
1398 }
1399 else {
1400 log_message(r, APR_SUCCESS,
1401 apr_psprintf(r->pool,
1402 "While parsing LINE: '%s' was not found", cmd->l.vname), NULL);
1403 return HTTP_BAD_REQUEST;
1404 }
1405
1406 return OK;
1407 }
1408
1409 static int resolve_tick(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1410 {
1411 rrd_cmd_t *ref;
1412
1413 ref = apr_hash_get(cmds->names, cmd->t.vname, APR_HASH_KEY_STRING);
1414 if (ref) {
1415 cmd->def = ref->def;
1416 }
1417 else {
1418 log_message(r, APR_SUCCESS,
1419 apr_psprintf(r->pool,
1420 "While parsing TICK: '%s' was not found", cmd->t.vname), NULL);
1421 return HTTP_BAD_REQUEST;
1422 }
1423
1424 return OK;
1425 }
1426
1427 static int resolve_shift(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1428 {
1429 rrd_cmd_t *ref;
1430
1431 ref = apr_hash_get(cmds->names, cmd->s.vname, APR_HASH_KEY_STRING);
1432 if (ref) {
1433 cmd->def = ref->def;
1434 }
1435 else {
1436 log_message(r, APR_SUCCESS,
1437 apr_psprintf(r->pool,
1438 "While parsing SHIFT: '%s' was not found", cmd->s.vname), NULL);
1439 return HTTP_BAD_REQUEST;
1440 }
1441
1442 return OK;
1443 }
1444
1445 static int resolve_gprint(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1446 {
1447 rrd_cmd_t *ref;
1448
1449 ref = apr_hash_get(cmds->names, cmd->p.vname, APR_HASH_KEY_STRING);
1450 if (ref) {
1451 cmd->def = ref->def;
1452 }
1453 else {
1454 log_message(r, APR_SUCCESS,
1455 apr_psprintf(r->pool,
1456 "While parsing GPRINT: '%s' was not found", cmd->p.vname), NULL);
1457 return HTTP_BAD_REQUEST;
1458 }
1459
1460 return OK;
1461 }
1462
1463 static int resolve_print(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds)
1464 {
1465 rrd_cmd_t *ref;
1466
1467 ref = apr_hash_get(cmds->names, cmd->p.vname, APR_HASH_KEY_STRING);
1468 if (ref) {
1469 cmd->def = ref->def;
1470 }
1471 else {
1472 log_message(r, APR_SUCCESS,
1473 apr_psprintf(r->pool,
1474 "While parsing PRINT: '%s' was not found", cmd->p.vname), NULL);
1475 return HTTP_BAD_REQUEST;
1476 }
1477
1478 return OK;
1479 }
1480
1481 static int resolve_rrds(request_rec *r, rrd_cmds_t *cmds)
1482 {
1483 rrd_cmd_t *cmd;
1484 int i, ret;
1485
1486 for (i = 0; i < cmds->cmds->nelts; ++i) {
1487
1488 cmd = &((rrd_cmd_t *)cmds->cmds->elts)[i];
1489
1490 switch (cmd->type) {
1491 case RRD_CONF_DEF:
1492
1493 ret = resolve_def(r, cmd, cmds);
1494 if (OK != ret) {
1495 return ret;
1496 }
1497
1498 break;
1499 case RRD_CONF_CDEF:
1500
1501 ret = resolve_cdef(r, cmd, cmds);
1502 if (OK != ret) {
1503 return ret;
1504 }
1505
1506 break;
1507 case RRD_CONF_VDEF:
1508
1509 ret = resolve_vdef(r, cmd, cmds);
1510 if (OK != ret) {
1511 return ret;
1512 }
1513
1514 break;
1515 case RRD_CONF_AREA:
1516
1517 ret = resolve_area(r, cmd, cmds);
1518 if (OK != ret) {
1519 return ret;
1520 }
1521
1522 break;
1523 case RRD_CONF_LINE:
1524
1525 ret = resolve_line(r, cmd, cmds);
1526 if (OK != ret) {
1527 return ret;
1528 }
1529
1530 break;
1531 case RRD_CONF_TICK:
1532
1533 ret = resolve_tick(r, cmd, cmds);
1534 if (OK != ret) {
1535 return ret;
1536 }
1537
1538 break;
1539 case RRD_CONF_SHIFT:
1540
1541 ret = resolve_shift(r, cmd, cmds);
1542 if (OK != ret) {
1543 return ret;
1544 }
1545
1546 break;
1547 case RRD_CONF_GPRINT:
1548
1549 ret = resolve_gprint(r, cmd, cmds);
1550 if (OK != ret) {
1551 return ret;
1552 }
1553
1554 break;
1555 case RRD_CONF_PRINT:
1556
1557 ret = resolve_print(r, cmd, cmds);
1558 if (OK != ret) {
1559 return ret;
1560 }
1561
1562 break;
1563 default:
1564 break;
1565 }
1566
1567 }
1568
1569 return OK;
1570 }
1571
1572 static int generate_element(request_rec *r, rrd_cmd_t *cmd,
1573 apr_array_header_t *args)
1574 {
1575 /* one result */
1576 const char *arg;
1577
1578 const char *legend = cmd->e.legend;
1579 if (cmd->e.elegend) {
1580 const char *err = NULL;
1581 legend = ap_expr_str_exec(r, cmd->e.elegend, &err);
1582 if (err) {
1583 log_message(r, APR_SUCCESS,
1584 apr_psprintf(r->pool,
1585 "While evaluating an element expression: %s", err), NULL);
1586 return HTTP_INTERNAL_SERVER_ERROR;
1587 }
1588 legend = pescape_colon(r->pool, legend);
1589 }
1590
1591 arg = apr_psprintf(r->pool, "%s:%s",
1592 cmd->e.element, legend);
1593 APR_ARRAY_PUSH(args, const char *) = arg;
1594
1595 return OK;
1596 }
1597
1598 static int generate_gprint(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
1599 {
1600 int j;
1601
1602 /* no reference */
1603 if (!cmd->def) {
1604 log_message(r, APR_SUCCESS,
1605 apr_psprintf(r->pool,
1606 "GPRINT element referred to '%s', which does not exist",
1607 cmd->p.vname), NULL);
1608 return HTTP_BAD_REQUEST;
1609 }
1610
1611 /* no results */
1612 else if (cmd->def->num == 0) {
1613 /* output nothing */
1614 }
1615
1616 /* one result */
1617 else if (cmd->def->num == 1) {
1618 const char *arg = apr_psprintf(r->pool, "GPRINT:%s:%s",
1619 cmd->p.vname, cmd->p.format);
1620 APR_ARRAY_PUSH(args, const char *) = arg;
1621 }
1622
1623 /* more than one result */
1624 else {
1625 /* handle each PRINT: line */
1626 for (j = 0; j < cmd->def->num; ++j) {
1627 const char *arg = apr_psprintf(r->pool, "GPRINT:%sw%d:%s",
1628 cmd->p.vname, j, cmd->p.format);
1629 APR_ARRAY_PUSH(args, const char *) = arg;
1630 }
1631 }
1632
1633 return OK;
1634 }
1635
1636 static int generate_print(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
1637 {
1638 int j;
1639
1640 /* no reference */
1641 if (!cmd->def) {
1642 log_message(r, APR_SUCCESS,
1643 apr_psprintf(r->pool,
1644 "PRINT element referred to '%s', which does not exist",
1645 cmd->p.vname), NULL);
1646 return HTTP_BAD_REQUEST;
1647 }
1648
1649 /* no results */
1650 else if (cmd->def->num == 0) {
1651 /* output nothing */
1652 }
1653
1654 /* one result */
1655 else if (cmd->def->num == 1) {
1656 const char *arg = apr_psprintf(r->pool, "PRINT:%s:%s",
1657 cmd->p.vname, cmd->p.format);
1658 APR_ARRAY_PUSH(args, const char *) = arg;
1659 }
1660
1661 /* more than one result */
1662 else {
1663 /* handle each PRINT: line */
1664 for (j = 0; j < cmd->def->num; ++j) {
1665 const char *arg = apr_psprintf(r->pool, "PRINT:%sw%d:%s",
1666 cmd->p.vname, j, cmd->p.format);
1667 APR_ARRAY_PUSH(args, const char *) = arg;
1668 }
1669 }
1670
1671 return OK;
1672 }
1673
1674 static int generate_shift(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
1675 {
1676 int j;
1677
1678 /* no reference */
1679 if (!cmd->def) {
1680 log_message(r, APR_SUCCESS,
1681 apr_psprintf(r->pool,
1682 "SHIFT element referred to '%s', which does not exist",
1683 cmd->s.vname), NULL);
1684 return HTTP_BAD_REQUEST;
1685 }
1686
1687 /* no results */
1688 else if (cmd->def->num == 0) {
1689 /* output nothing */
1690 }
1691
1692 /* one result */
1693 else if (cmd->def->num == 1) {
1694 const char *arg = apr_psprintf(r->pool, "SHIFT:%s:%s",
1695 cmd->s.vname, cmd->s.shift);
1696 APR_ARRAY_PUSH(args, const char *) = arg;
1697 }
1698
1699 /* more than one result */
1700 else {
1701 /* handle each LINE: line */
1702 for (j = 0; j < cmd->def->num; ++j) {
1703 const char *arg = apr_psprintf(r->pool, "SHIFT:%sw%d:%s",
1704 cmd->s.vname, j, cmd->s.shift);
1705 APR_ARRAY_PUSH(args, const char *) = arg;
1706 }
1707 }
1708
1709 return OK;
1710 }
1711
1712 static int generate_hrule(request_rec *r, rrd_cmd_t *cmd,
1713 apr_array_header_t *args)
1714 {
1715 /* one result */
1716 const char *arg;
1717
1718 const char *legend = cmd->r.legend;
1719 if (cmd->r.elegend) {
1720 const char *err = NULL;
1721 legend = ap_expr_str_exec(r, cmd->r.elegend, &err);
1722 if (err) {
1723 log_message(r, APR_SUCCESS,
1724 apr_psprintf(r->pool,
1725 "While evaluating an element expression: %s", err), NULL);
1726 return HTTP_INTERNAL_SERVER_ERROR;
1727 }
1728 legend = pescape_colon(r->pool, legend);
1729 }
1730
1731 arg = apr_psprintf(r->pool, "HRULE:%s%s%s:%s%s%s",
1732 cmd->r.val,
1733 cmd->r.colour ? "#" : "", cmd->r.colour ? cmd->r.colour : "",
1734 legend,
1735 cmd->r.args[0] ? ":" : "", cmd->r.args);
1736 APR_ARRAY_PUSH(args, const char *) = arg;
1737
1738 return OK;
1739 }
1740
1741 static int generate_vrule(request_rec *r, rrd_cmd_t *cmd,
1742 apr_array_header_t *args)
1743 {
1744 /* one result */
1745 const char *arg;
1746
1747 const char *legend = cmd->r.legend;
1748 if (cmd->r.elegend) {
1749 const char *err = NULL;
1750 legend = ap_expr_str_exec(r, cmd->r.elegend, &err);
1751 if (err) {
1752 log_message(r, APR_SUCCESS,
1753 apr_psprintf(r->pool,
1754 "While evaluating an element expression: %s", err), NULL);
1755 return HTTP_INTERNAL_SERVER_ERROR;
1756 }
1757 legend = pescape_colon(r->pool, legend);
1758 }
1759
1760 arg = apr_psprintf(r->pool, "VRULE:%s%s%s:%s%s%s",
1761 cmd->r.val,
1762 cmd->r.colour ? "#" : "", cmd->r.colour ? cmd->r.colour : "",
1763 legend,
1764 cmd->r.args[0] ? ":" : "", cmd->r.args);
1765 APR_ARRAY_PUSH(args, const char *) = arg;
1766
1767 return OK;
1768 }
1769
1770 static int generate_tick(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds,
1771 apr_array_header_t *args, int *i)
1772 {
1773 int j, k;
1774
1775 /* no reference */
1776 if (!cmd->def) {
1777 log_message(r, APR_SUCCESS,
1778 apr_psprintf(r->pool,
1779 "TICK element referred to '%s', which does not exist",
1780 cmd->t.vname), NULL);
1781 return HTTP_BAD_REQUEST;
1782 }
1783
1784 /* no results */
1785 else if (cmd->def->num == 0) {
1786 /* output nothing */
1787 }
1788
1789 /* one result */
1790 else if (cmd->def->num == 1) {
1791 const char *arg;
1792 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[0];
1793
1794 const char *legend = cmd->t.legend;
1795 if (cmd->t.elegend) {
1796 const char *err = NULL;
1797 legend = ap_expr_str_exec(rr, cmd->t.elegend, &err);
1798 if (err) {
1799 log_message(r, APR_SUCCESS,
1800 apr_psprintf(r->pool,
1801 "While evaluating an element expression: %s", err), NULL);
1802 return HTTP_INTERNAL_SERVER_ERROR;
1803 }
1804 legend = pescape_colon(r->pool, legend);
1805 }
1806 arg = apr_psprintf(r->pool, "TICK:%s%s%s:%s:%s%s%s",
1807 cmd->t.vname,
1808 cmd->t.colour ? "#" : "", cmd->t.colour ? cmd->t.colour : "",
1809 cmd->t.fraction,
1810 legend,
1811 cmd->t.args[0] ? ":" : "", cmd->t.args);
1812 APR_ARRAY_PUSH(args, const char *) = arg;
1813 }
1814
1815 /* more than one result */
1816 else {
1817 int skip = 0;
1818
1819 /* handle each TICK: line */
1820 for (j = 0; j < cmd->def->num; ++j) {
1821 const char *arg;
1822 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[j];
1823
1824 const char *legend = cmd->t.legend;
1825 if (cmd->t.elegend) {
1826 const char *err = NULL;
1827 legend = ap_expr_str_exec(rr, cmd->t.elegend, &err);
1828 if (err) {
1829 log_message(r, APR_SUCCESS,
1830 apr_psprintf(r->pool,
1831 "While evaluating an element expression: %s", err), NULL);
1832 return HTTP_INTERNAL_SERVER_ERROR;
1833 }
1834 legend = pescape_colon(r->pool, legend);
1835 }
1836
1837 arg = apr_psprintf(r->pool, "TICK:%sw%d%s%s:%s:%s%s%s",
1838 cmd->t.vname, j,
1839 cmd->t.colour ? "#" : "", cmd->t.colour ? cmd->t.colour : "",
1840 cmd->t.fraction,
1841 legend,
1842 cmd->t.args[0] ? ":" : "", cmd->t.args);
1843 APR_ARRAY_PUSH(args, const char *) = arg;
1844
1845 for (k = *i + 1; k < cmds->cmds->nelts; ++k) {
1846 rrd_cmd_t *pcmd = &((rrd_cmd_t *)cmds->cmds->elts)[k];
1847 if (pcmd->def == cmd->def) {
1848 switch (pcmd->type) {
1849 case RRD_CONF_PRINT:
1850
1851 APR_ARRAY_PUSH(args, const char *) =
1852 apr_psprintf(r->pool, "PRINT:%sw%d:%s",
1853 pcmd->p.vname, j, pcmd->p.format);
1854
1855 break;
1856 case RRD_CONF_GPRINT:
1857
1858 APR_ARRAY_PUSH(args, const char *) =
1859 apr_psprintf(r->pool, "GPRINT:%sw%d:%s",
1860 pcmd->p.vname, j, pcmd->p.format);
1861
1862 break;
1863 default:
1864 /* skip the print/grint */
1865 skip = k - *i - 1;
1866 /* jump out of the loop */
1867 k = cmds->cmds->nelts;
1868 }
1869 }
1870 else {
1871 /* skip the print/grint */
1872 skip = k - *i - 1;
1873 /* jump out of the loop */
1874 k = cmds->cmds->nelts;
1875 }
1876 }
1877
1878 }
1879 *i += skip;
1880 }
1881
1882 return OK;
1883 }
1884
1885 static int generate_area(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds,
1886 apr_array_header_t *args, int *i)
1887 {
1888 int j, k;
1889
1890 /* no reference */
1891 if (!cmd->def) {
1892 log_message(r, APR_SUCCESS,
1893 apr_psprintf(r->pool,
1894 "AREA element referred to '%s', which does not exist",
1895 cmd->a.vname), NULL);
1896 return HTTP_BAD_REQUEST;
1897 }
1898
1899 /* no results */
1900 else if (cmd->def->num == 0) {
1901 /* output nothing */
1902 }
1903
1904 /* one result */
1905 else if (cmd->def->num == 1) {
1906 const char *arg;
1907 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[0];
1908
1909 const char *legend = cmd->a.legend;
1910 if (cmd->a.elegend) {
1911 const char *err = NULL;
1912 legend = ap_expr_str_exec(rr, cmd->a.elegend, &err);
1913 if (err) {
1914 log_message(r, APR_SUCCESS,
1915 apr_psprintf(r->pool,
1916 "While evaluating an element expression: %s", err), NULL);
1917 return HTTP_INTERNAL_SERVER_ERROR;
1918 }
1919 legend = pescape_colon(r->pool, legend);
1920 }
1921
1922 arg = apr_psprintf(r->pool, "AREA:%s%s%s:%s%s%s",
1923 cmd->a.vname,
1924 cmd->a.colour ? "#" : "", cmd->a.colour ? cmd->a.colour : "",
1925 legend,
1926 cmd->a.args[0] ? ":" : "", cmd->a.args);
1927 APR_ARRAY_PUSH(args, const char *) = arg;
1928 }
1929
1930 /* more than one result */
1931 else {
1932 int skip = 0;
1933
1934 /* handle each AREA: line */
1935 for (j = 0; j < cmd->def->num; ++j) {
1936 const char *arg;
1937 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[j];
1938
1939 const char *legend = cmd->a.legend;
1940 if (cmd->a.elegend) {
1941 const char *err = NULL;
1942 legend = ap_expr_str_exec(rr, cmd->a.elegend, &err);
1943 if (err) {
1944 log_message(r, APR_SUCCESS,
1945 apr_psprintf(r->pool,
1946 "While evaluating an element expression: %s", err), NULL);
1947 return HTTP_INTERNAL_SERVER_ERROR;
1948 }
1949 legend = pescape_colon(r->pool, legend);
1950 }
1951
1952 arg = apr_psprintf(r->pool, "AREA:%sw%d%s%s:%s%s%s",
1953 cmd->a.vname, j,
1954 cmd->a.colour ? "#" : "", cmd->a.colour ? cmd->a.colour : "",
1955 legend,
1956 cmd->a.args[0] ? ":" : "", cmd->a.args);
1957 APR_ARRAY_PUSH(args, const char *) = arg;
1958
1959 for (k = *i + 1; k < cmds->cmds->nelts; ++k) {
1960 rrd_cmd_t *pcmd = &((rrd_cmd_t *)cmds->cmds->elts)[k];
1961 if (pcmd->def == cmd->def) {
1962 switch (pcmd->type) {
1963 case RRD_CONF_PRINT:
1964
1965 APR_ARRAY_PUSH(args, const char *) =
1966 apr_psprintf(r->pool, "PRINT:%sw%d:%s",
1967 pcmd->p.vname, j, pcmd->p.format);
1968
1969 break;
1970 case RRD_CONF_GPRINT:
1971
1972 APR_ARRAY_PUSH(args, const char *) =
1973 apr_psprintf(r->pool, "GPRINT:%sw%d:%s",
1974 pcmd->p.vname, j, pcmd->p.format);
1975
1976 break;
1977 default:
1978 /* skip the print/grint */
1979 skip = k - *i - 1;
1980 /* jump out of the loop */
1981 k = cmds->cmds->nelts;
1982 }
1983 }
1984 else {
1985 /* skip the print/grint */
1986 skip = k - *i - 1;
1987 /* jump out of the loop */
1988 k = cmds->cmds->nelts;
1989 }
1990 }
1991
1992 }
1993 *i += skip;
1994 }
1995
1996 return OK;
1997 }
1998
1999 static int generate_line(request_rec *r, rrd_cmd_t *cmd, rrd_cmds_t *cmds,
2000 apr_array_header_t *args, int *i)
2001 {
2002 int j, k;
2003
2004 /* no reference */
2005 if (!cmd->def) {
2006 log_message(r, APR_SUCCESS,
2007 apr_psprintf(r->pool,
2008 "LINE element referred to '%s', which does not exist",
2009 cmd->l.vname), NULL);
2010 return HTTP_BAD_REQUEST;
2011 }
2012
2013 /* no results */
2014 else if (cmd->def->num == 0) {
2015 /* output nothing */
2016 }
2017
2018 /* one result */
2019 else if (cmd->def->num == 1) {
2020 const char *arg;
2021 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[0];
2022
2023 const char *legend = cmd->l.legend;
2024 if (cmd->l.elegend) {
2025 const char *err = NULL;
2026 legend = ap_expr_str_exec(rr, cmd->l.elegend, &err);
2027 if (err) {
2028 log_message(r, APR_SUCCESS,
2029 apr_psprintf(r->pool,
2030 "While evaluating an element expression: %s", err), NULL);
2031 return HTTP_INTERNAL_SERVER_ERROR;
2032 }
2033 legend = pescape_colon(r->pool, legend);
2034 }
2035 arg = apr_psprintf(r->pool, "%s:%s%s%s:%s%s%s",
2036 cmd->l.line, cmd->l.vname,
2037 cmd->l.colour ? "#" : "", cmd->l.colour ? cmd->l.colour : "",
2038 legend,
2039 cmd->l.args[0] ? ":" : "", cmd->l.args);
2040 APR_ARRAY_PUSH(args, const char *) = arg;
2041 }
2042
2043 /* more than one result */
2044 else {
2045 int skip = 0;
2046
2047 /* handle each LINE: line */
2048 for (j = 0; j < cmd->def->num; ++j) {
2049 const char *arg;
2050 request_rec *rr = ((request_rec **)cmd->def->d.requests->elts)[j];
2051
2052 const char *legend = cmd->l.legend;
2053 if (cmd->l.elegend) {
2054 const char *err = NULL;
2055 legend = ap_expr_str_exec(rr, cmd->l.elegend, &err);
2056 if (err) {
2057 log_message(r, APR_SUCCESS,
2058 apr_psprintf(r->pool,
2059 "While evaluating an element expression: %s", err), NULL);
2060 return HTTP_INTERNAL_SERVER_ERROR;
2061 }
2062 legend = pescape_colon(r->pool, legend);
2063 }
2064
2065 arg = apr_psprintf(r->pool, "%s:%sw%d%s%s:%s%s%s",
2066 cmd->l.line, cmd->l.vname, j,
2067 cmd->l.colour ? "#" : "", cmd->l.colour ? cmd->l.colour : "",
2068 legend,
2069 cmd->l.args[0] ? ":" : "", cmd->l.args);
2070 APR_ARRAY_PUSH(args, const char *) = arg;
2071
2072 for (k = *i + 1; k < cmds->cmds->nelts; ++k) {
2073 rrd_cmd_t *pcmd = &((rrd_cmd_t *)cmds->cmds->elts)[k];
2074 if (pcmd->def == cmd->def) {
2075 switch (pcmd->type) {
2076 case RRD_CONF_PRINT:
2077
2078 APR_ARRAY_PUSH(args, const char *) =
2079 apr_psprintf(r->pool, "PRINT:%sw%d:%s",
2080 pcmd->p.vname, j, pcmd->p.format);
2081
2082 break;
2083 case RRD_CONF_GPRINT:
2084
2085 APR_ARRAY_PUSH(args, const char *) =
2086 apr_psprintf(r->pool, "GPRINT:%sw%d:%s",
2087 pcmd->p.vname, j, pcmd->p.format);
2088
2089 break;
2090 default:
2091 /* skip the print/grint */
2092 skip = k - *i - 1;
2093 /* jump out of the loop */
2094 k = cmds->cmds->nelts;
2095 }
2096 }
2097 else {
2098 /* skip the print/grint */
2099 skip = k - *i - 1;
2100 /* jump out of the loop */
2101 k = cmds->cmds->nelts;
2102 }
2103 }
2104
2105 }
2106 *i += skip;
2107 }
2108
2109 return OK;
2110 }
2111
2112 static int generate_vdef(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
2113 {
2114 int j;
2115
2116 /* no reference */
2117 if (!cmd->def) {
2118 log_message(r, APR_SUCCESS,
2119 apr_psprintf(r->pool,
2120 "VDEF element referred to '%s', which does not exist",
2121 cmd->v.vname), NULL);
2122 return HTTP_BAD_REQUEST;
2123 }
2124
2125 /* no results */
2126 else if (cmd->def->num == 0) {
2127 /* output nothing */
2128 }
2129
2130 /* one result */
2131 else if (cmd->def->num == 1) {
2132 APR_ARRAY_PUSH(args, const char *) =
2133 apr_pstrcat(r->pool, "VDEF:", cmd->v.vname, "=",
2134 cmd->v.dsname, ",", cmd->v.rpn, NULL);
2135 }
2136
2137 /* more than one result */
2138 else {
2139 /* handle each VDEF: line */
2140 for (j = 0; j < cmd->def->num; ++j) {
2141 const char *arg = apr_psprintf(r->pool, "VDEF:%sw%d=%sw%d,%s", cmd->v.vname,
2142 j, cmd->v.dsname, j, cmd->v.rpn);
2143 APR_ARRAY_PUSH(args, const char *) = arg;
2144 }
2145 }
2146
2147 return OK;
2148 }
2149
2150 static int generate_cdef(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
2151 {
2152 int j, k;
2153
2154 /* no reference */
2155 if (!cmd->def) {
2156 log_message(r, APR_SUCCESS,
2157 apr_psprintf(r->pool,
2158 "CDEF element '%s' referred to no existing definitions",
2159 cmd->c.vname), NULL);
2160 return HTTP_BAD_REQUEST;
2161 }
2162
2163 /* no results */
2164 else if (cmd->def->num == 0) {
2165 /* output nothing */
2166 }
2167
2168 /* one result */
2169 else if (cmd->def->num == 1) {
2170 APR_ARRAY_PUSH(args, const char *) =
2171 apr_pstrcat(r->pool, "CDEF:", cmd->c.vname, "=",
2172 cmd->c.rpn, NULL);
2173 }
2174
2175 /* more than one result */
2176 else {
2177 /* handle each CDEF: line */
2178 for (j = 0; j < cmd->num; ++j) {
2179 char *cdef;
2180 int len;
2181
2182 /* first pass - work out the length */
2183 len = apr_snprintf(NULL, 0, "CDEF:%sw%d=", cmd->c.vname, j);
2184 for (k = 0; k < cmd->c.rpns->nelts; ++k) {
2185 rrd_rpn_t *rp = (((rrd_rpn_t *)cmd->c.rpns->elts) + k);
2186 if (k) {
2187 len++;
2188 }
2189 if (!rp->def || rp->def->num < 2) {
2190 len += apr_snprintf(NULL, 0, "%s", rp->rpn);
2191 }
2192 else {
2193 len += apr_snprintf(NULL, 0, "%sw%d", rp->rpn, j);
2194 }
2195 }
2196
2197 /* second pass, write the cdef */
2198 cdef = apr_palloc(r->pool, len + 1);
2199 APR_ARRAY_PUSH(args, const char *) = cdef;
2200 cdef += apr_snprintf(cdef, len, "CDEF:%sw%d=", cmd->c.vname, j);
2201 for (k = 0; k < cmd->c.rpns->nelts; ++k) {
2202 rrd_rpn_t *rp = (((rrd_rpn_t *)cmd->c.rpns->elts) + k);
2203 if (k) {
2204 *cdef++ = ',';
2205 }
2206 if (!rp->def || rp->def->num < 2) {
2207 cdef += apr_snprintf(cdef, len, "%s", rp->rpn);
2208 }
2209 else {
2210 cdef += apr_snprintf(cdef, len, "%sw%d", rp->rpn, j);
2211 }
2212 }
2213 }
2214 }
2215
2216 return OK;
2217 }
2218
2219 static int generate_def(request_rec *r, rrd_cmd_t *cmd, apr_array_header_t *args)
2220 {
2221 int j;
2222
2223 /* safety check - reject anything trying to set the daemon */
2224 if (ap_strstr_c(cmd->d.cf, ":daemon=")) {
2225 log_message(r, APR_SUCCESS,
2226 "DEF elements must not contain a 'daemon' parameter", NULL);
2227 return HTTP_BAD_REQUEST;
2228 }
2229
2230 /* no results */
2231 if (cmd->d.requests->nelts == 0) {
2232 /* output nothing */
2233 }
2234
2235 /* one result */
2236 else if (cmd->d.requests->nelts == 1) {
2237 request_rec *rr = APR_ARRAY_IDX(cmd->d.requests, 0, request_rec *);
2238 const char *arg = apr_psprintf(r->pool, "DEF:%s=%s:%s:%s", cmd->d.vname,
2239 pescape_colon(r->pool, rr->filename), cmd->d.dsname, cmd->d.cf);
2240 APR_ARRAY_PUSH(args, const char *) = arg;
2241 }
2242
2243 /* more than one result */
2244 else {
2245 char *cdef;
2246 int len = apr_snprintf(NULL, 0, "CDEF:%s=", cmd->d.vname);
2247
2248 /* handle each DEF: line */
2249 for (j = 0; j < cmd->d.requests->nelts; ++j) {
2250 request_rec *rr = APR_ARRAY_IDX(cmd->d.requests, j, request_rec *);
2251 const char *arg = apr_psprintf(r->pool, "DEF:%sw%d=%s:%s:%s", cmd->d.vname,
2252 j, pescape_colon(r->pool, rr->filename), cmd->d.dsname, cmd->d.cf);
2253 APR_ARRAY_PUSH(args, const char *) = arg;
2254 len += apr_snprintf(NULL, 0, "%s%sw%d%s", j ? "," : "", cmd->d.vname, j, j ? ",+" : "");
2255 }
2256
2257 /* calculate the CDEF summary line */
2258 cdef = apr_palloc(r->pool, len + 1);
2259 APR_ARRAY_PUSH(args, const char *) = cdef;
2260 cdef += apr_snprintf(cdef, len, "CDEF:%s=", cmd->d.vname);
2261 for (j = 0; j < cmd->d.requests->nelts; ++j) {
2262 cdef += apr_snprintf(cdef, len, "%s%sw%d%s", j ? "," : "", cmd->d.vname, j, j ? ",+" : "");
2263 }
2264
2265 }
2266
2267 return OK;
2268 }
2269
2270 static int generate_args(request_rec *r, rrd_cmds_t *cmds, apr_array_header_t **pargs)
2271 {
2272 apr_array_header_t *args;
2273 rrd_cmd_t *cmd;
2274 rrd_opt_t *opt;
2275 const char *format;
2276 int i, num = 4, ret = OK;
2277
2278 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
2279 &rrd_module);
2280
2281 /* count the options */
2282 for (i = 0; i < cmds->opts->nelts; ++i) {
2283
2284 opt = &((rrd_opt_t *)cmds->opts->elts)[i];
2285
2286 if (opt->val) {
2287 num++;
2288 }
2289
2290 num++;
2291 }
2292 /* count the number of elements we need */
2293 for (i = 0; i < cmds->cmds->nelts; ++i) {
2294
2295 cmd = &APR_ARRAY_IDX(cmds->cmds, i, rrd_cmd_t);
2296
2297 if (cmd->def) {
2298 num += cmd->def->d.requests->nelts;
2299 }
2300
2301 num++;
2302 }
2303
2304 /* work out the format */
2305 format = conf->format ? conf->format : parse_rrdgraph_suffix(r);
2306
2307 /* set the content type */
2308 ap_set_content_type(r, lookup_content_type(format));
2309
2310 /* create arguments of the correct size */
2311 args = *pargs = apr_array_make(r->pool, num, sizeof(const char *));
2312
2313 /* the argv array */
2314 APR_ARRAY_PUSH(args, const char *) = "rrdgraph";
2315 APR_ARRAY_PUSH(args, const char *) = "-";
2316 APR_ARRAY_PUSH(args, const char *) = "--imgformat";
2317 APR_ARRAY_PUSH(args, const char *) = format;
2318
2319 /* first create the options */
2320 for (i = 0; i < cmds->opts->nelts; ++i) {
2321
2322 opt = &((rrd_opt_t *)cmds->opts->elts)[i];
2323
2324 APR_ARRAY_PUSH(args, const char *) =
2325 apr_pstrcat(r->pool, "--", opt->key, NULL);
2326 if (opt->eval) {
2327 const char *err = NULL;
2328
2329 APR_ARRAY_PUSH(args, const char *) = ap_expr_str_exec(r, opt->eval, &err);
2330 if (err) {
2331 log_message(r, APR_SUCCESS,
2332 apr_psprintf(r->pool,
2333 "While evaluating expressions for '%s': %s", opt->key, err), NULL);
2334 return HTTP_INTERNAL_SERVER_ERROR;
2335 }
2336
2337 }
2338 else if (opt->val) {
2339 APR_ARRAY_PUSH(args, const char *) = opt->val;
2340 }
2341
2342 }
2343
2344 /* and finally create the elements */
2345 for (i = 0; i < cmds->cmds->nelts; ++i) {
2346
2347 cmd = &((rrd_cmd_t *)cmds->cmds->elts)[i];
2348
2349 switch (cmd->type) {
2350 case RRD_CONF_DEF:
2351
2352 ret = generate_def(r, cmd, args);
2353
2354 break;
2355 case RRD_CONF_CDEF:
2356
2357 ret = generate_cdef(r, cmd, args);
2358
2359 break;
2360 case RRD_CONF_VDEF:
2361
2362 ret = generate_vdef(r, cmd, args);
2363
2364 break;
2365 case RRD_CONF_LINE:
2366
2367 ret = generate_line(r, cmd, cmds, args, &i);
2368
2369 break;
2370 case RRD_CONF_AREA:
2371
2372 ret = generate_area(r, cmd, cmds, args, &i);
2373
2374 break;
2375 case RRD_CONF_TICK:
2376
2377 ret = generate_tick(r, cmd, cmds, args, &i);
2378
2379 break;
2380 case RRD_CONF_SHIFT:
2381
2382 ret = generate_shift(r, cmd, args);
2383
2384 break;
2385 case RRD_CONF_PRINT:
2386
2387 ret = generate_print(r, cmd, args);
2388
2389 break;
2390 case RRD_CONF_GPRINT:
2391
2392 ret = generate_gprint(r, cmd, args);
2393
2394 break;
2395 case RRD_CONF_HRULE:
2396
2397 ret = generate_hrule(r, cmd, args);
2398
2399 break;
2400 case RRD_CONF_VRULE:
2401
2402 ret = generate_vrule(r, cmd, args);
2403
2404 break;
2405 case RRD_CONF_COMMENT:
2406 case RRD_CONF_TEXTALIGN:
2407
2408 ret = generate_element(r, cmd, args);
2409
2410 break;
2411 }
2412
2413 if (OK != ret) {
2414 return ret;
2415 }
2416
2417 }
2418
2419 for (i = 0; i < args->nelts; ++i) {
2420 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, "mod_rrd: rrdgraph:%d: %s",
2421 i, ((const char **) args->elts)[i]);
2422 }
2423
2424 return OK;
2425 }
2426
2427 static int cleanup_args(request_rec *r, rrd_cmds_t *cmds)
2428 {
2429 rrd_cmd_t *cmd;
2430 int i;
2431
2432 for (i = 0; i < cmds->cmds->nelts; ++i) {
2433 request_rec **rr;
2434
2435 cmd = &APR_ARRAY_IDX(cmds->cmds, i, rrd_cmd_t);
2436
2437 /* free all the saved requests */
2438 if (RRD_CONF_DEF == cmd->type && cmd->d.requests) {
2439 while ((rr = apr_array_pop(cmd->d.requests))) {
2440 apr_pool_destroy((*rr)->pool);
2441 }
2442 }
2443
2444 }
2445
2446 return OK;
2447 }
2448
2449 static int get_rrdgraph(request_rec *r)
2450 {
2451 rrd_info_t *grinfo = NULL;
2452 apr_array_header_t *args;
2453 apr_bucket_brigade *bb = apr_brigade_create(r->pool,
2454 r->connection->bucket_alloc);
2455 rrd_cmds_t *cmds;
2456
2457 apr_status_t rv;
2458 int ret;
2459
2460 /* pull apart the query string, reject unrecognised options */
2461 ret = parse_query(r, &cmds);
2462 if (OK != ret) {
2463 return ret;
2464 }
2465
2466 /* resolve permissions and wildcards of rrd files */
2467 ret = resolve_rrds(r, cmds);
2468 if (OK != ret) {
2469 return ret;
2470 }
2471
2472 /* create the args string for rrd_graph */
2473 ret = generate_args(r, cmds, &args);
2474 if (OK != ret) {
2475 return ret;
2476 }
2477
2478 /* rrd_graph_v is not thread safe */
2479 #if APR_HAS_THREADS
2480 if (rrd_mutex) {
2481 apr_thread_mutex_lock(rrd_mutex);
2482 }
2483 #endif
2484
2485 /* we're ready, let's generate the graph */
2486 grinfo = rrd_graph_v(args->nelts, (char **)args->elts);
2487 if (grinfo == NULL) {
2488 log_message(r, APR_SUCCESS, "Call to rrd_graph_v failed", rrd_get_error());
2489 ret = HTTP_INTERNAL_SERVER_ERROR;
2490 }
2491 else {
2492 /* grab the image data from the results */
2493 while (grinfo) {
2494 if (strcmp(grinfo->key, "image") == 0) {
2495 apr_brigade_write(bb, NULL, NULL, (const char *)grinfo->value.u_blo.ptr,
2496 grinfo->value.u_blo.size);
2497 ap_set_content_length(r, grinfo->value.u_blo.size);
2498 break;
2499 }
2500 /* skip anything else */
2501 grinfo = grinfo->next;
2502 }
2503 rrd_info_free(grinfo);
2504 }
2505 rrd_clear_error();
2506
2507 #if APR_HAS_THREADS
2508 if (rrd_mutex) {
2509 apr_thread_mutex_unlock(rrd_mutex);
2510 }
2511 #endif
2512
2513 /* trigger an early cleanup to save memory */
2514 ret = cleanup_args(r, cmds);
2515 if (OK != ret) {
2516 return ret;
2517 }
2518
2519 /* send our response down the stack */
2520 if (OK == ret) {
2521 rv = ap_pass_brigade(r->output_filters, bb);
2522 if (rv == APR_SUCCESS || r->status != HTTP_OK
2523 || r->connection->aborted) {
2524 return OK;
2525 }
2526 else {
2527 /* no way to know what type of error occurred */
2528 ap_log_rerror(
2529 APLOG_MARK, APLOG_DEBUG, rv, r, "rrd_handler: ap_pass_brigade returned %i", rv);
2530 return HTTP_INTERNAL_SERVER_ERROR;
2531 }
2532 }
2533
2534 return ret;
2535 }
2536
2537 static int get_rrd(request_rec *r)
2538 {
2539 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
2540 &rrd_module);
2541 /*
2542 * if a file does not exist, assume it is a request for a graph, otherwise
2543 * go with the original file.
2544 */
2545 if ((conf->format) ||
2546 (r->filename && r->finfo.filetype == APR_NOFILE && parse_rrdgraph_suffix(r))) {
2547 return get_rrdgraph(r);
2548 }
2549
2550 return DECLINED;
2551 }
2552
2553 static int rrd_fixups(request_rec *r)
2554 {
2555 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
2556 &rrd_module);
2557
2558 if (!conf) {
2559 return DECLINED;
2560 }
2561
2562 if (conf->graph) {
2563 r->handler = "rrdgraph";
2564 return OK;
2565 }
2566
2567 return DECLINED;
2568 }
2569
2570 static int rrd_handler(request_rec *r)
2571 {
2572
2573 rrd_conf *conf = ap_get_module_config(r->per_dir_config,
2574 &rrd_module);
2575
2576 if (!conf || !conf->graph) {
2577 return DECLINED;
2578 }
2579
2580 /* A GET should return the CRL, OPTIONS should return the WADL */
2581 ap_allow_methods(r, 1, "GET", "OPTIONS", NULL);
2582 if (!strcmp(r->method, "GET")) {
2583 return get_rrd(r);
2584 }
2585 else if (!strcmp(r->method, "OPTIONS")) {
2586 return options_wadl(r, conf);
2587 }
2588 else {
2589 return HTTP_METHOD_NOT_ALLOWED;
2590 }
2591
2592 }
2593
2594 static void rrd_child_init(apr_pool_t *pchild, server_rec *s)
2595 {
2596 #if APR_HAS_THREADS
2597 int threaded_mpm;
2598 if (ap_mpm_query(AP_MPMQ_IS_THREADED, &threaded_mpm) == APR_SUCCESS
2599 && threaded_mpm)
2600 {
2601 apr_thread_mutex_create(&rrd_mutex, APR_THREAD_MUTEX_DEFAULT, pchild);
2602 }
2603 #endif
2604 }
2605
2606 static void *create_rrd_config(apr_pool_t *p, char *dummy)
2607 {
2608 rrd_conf *new = (rrd_conf *) apr_pcalloc(p, sizeof(rrd_conf));
2609
2610 new->options = apr_array_make(p, 10, sizeof(rrd_opt_t));
2611 new->elements = apr_array_make(p, 10, sizeof(rrd_cmd_t));
2612 new->env = apr_hash_make(p);
2613
2614 return (void *) new;
2615 }
2616
2617 static void *merge_rrd_config(apr_pool_t *p, void *basev, void *addv)
2618 {
2619 rrd_conf *new = (rrd_conf *) apr_pcalloc(p, sizeof(rrd_conf));
2620 rrd_conf *add = (rrd_conf *) addv;
2621 rrd_conf *base = (rrd_conf *) basev;
2622
2623 new->options = apr_array_append(p, add->options, base->options);
2624 new->elements = apr_array_append(p, add->elements, base->elements);
2625 new->env = apr_hash_overlay(p, add->env, base->env);
2626
2627 new->location = (add->location_set == 0) ? base->location : add->location;
2628 new->location_set = add->location_set || base->location_set;
2629
2630 new->format = (add->format_set == 0) ? base->format : add->format;
2631 new->format_set = add->format_set || base->format_set;
2632
2633 new->graph = (add->graph_set == 0) ? base->graph : add->graph;
2634 new->graph_set = add->graph_set || base->graph_set;
2635
2636 return new;
2637 }
2638
2639 static const char *set_rrd_graph_format(cmd_parms *cmd, void *dconf, const char *format)
2640 {
2641 rrd_conf *conf = dconf;
2642
2643 conf->format = format;
2644 conf->format_set = 1;
2645
2646 return NULL;
2647 }
2648
2649 static const char *set_rrd_graph_option(cmd_parms *cmd, void *dconf, const char *key, const char *val)
2650 {
2651 rrd_conf *conf = dconf;
2652 ap_expr_info_t *eval = NULL;
2653 const char *expr_err = NULL;
2654
2655 if (val) {
2656
2657 eval = ap_expr_parse_cmd(cmd, val, AP_EXPR_FLAG_STRING_RESULT,
2658 &expr_err, NULL);
2659
2660 if (expr_err) {
2661 return apr_pstrcat(cmd->temp_pool,
2662 "Cannot parse expression '", val, "': ",
2663 expr_err, NULL);
2664 }
2665
2666 }
2667
2668 if (!parse_option(cmd->pool, key, val, eval, conf->options)) {
2669 return apr_pstrcat(cmd->pool, "Could not recognise option: ", key, NULL);
2670 }
2671
2672 return NULL;
2673 }
2674
2675 static const char *set_rrd_graph_element(cmd_parms *cmd, void *dconf,
2676 const char *element, const char *val1, const char *val2)
2677 {
2678 rrd_conf *conf = dconf;
2679 ap_expr_info_t *eval1 = NULL, *eval2 = NULL;
2680 const char *expr_err = NULL;
2681
2682 if (val1) {
2683
2684 eval1 = ap_expr_parse_cmd(cmd, val1, AP_EXPR_FLAG_STRING_RESULT,
2685 &expr_err, NULL);
2686
2687 if (expr_err) {
2688 return apr_pstrcat(cmd->temp_pool,
2689 "Cannot parse expression '", val1, "': ",
2690 expr_err, NULL);
2691 }
2692
2693 }
2694
2695 if (val2) {
2696
2697 eval2 = ap_expr_parse_cmd(cmd, val2, AP_EXPR_FLAG_STRING_RESULT,
2698 &expr_err, NULL);
2699
2700 if (expr_err) {
2701 return apr_pstrcat(cmd->temp_pool,
2702 "Cannot parse expression '", val2, "': ",
2703 expr_err, NULL);
2704 }
2705
2706 }
2707
2708 if (!parse_element(cmd->pool, element, eval1, eval2, conf->elements)) {
2709 return apr_psprintf(cmd->pool,
2710 "RRDGraphElement was not recognised: %s", element);
2711 }
2712
2713 return NULL;
2714 }
2715
2716 static const char *set_rrd_graph_env(cmd_parms *cmd, void *dconf,
2717 const char *key, const char *val)
2718 {
2719 rrd_conf *conf = dconf;
2720 ap_expr_info_t *eval;
2721 const char *expr_err = NULL;
2722
2723 eval = ap_expr_parse_cmd(cmd, val, AP_EXPR_FLAG_STRING_RESULT,
2724 &expr_err, NULL);
2725
2726 if (expr_err) {
2727 return apr_pstrcat(cmd->temp_pool,
2728 "Cannot parse expression '", val, "': ",
2729 expr_err, NULL);
2730 }
2731
2732 apr_hash_set(conf->env, key, APR_HASH_KEY_STRING, eval);
2733
2734 return NULL;
2735 }
2736
2737 static const char *set_rrd_graph(cmd_parms *cmd, void *dconf, int flag)
2738 {
2739 rrd_conf *conf = dconf;
2740
2741 conf->graph = flag;
2742 conf->graph_set = 1;
2743
2744 return NULL;
2745 }
2746
2747 static const command_rec rrd_cmds[] = {
2748 AP_INIT_FLAG("RRDGraph", set_rrd_graph, NULL, RSRC_CONF | ACCESS_CONF,
2749 "Enable the rrdgraph image generator."),
2750 AP_INIT_TAKE1("RRDGraphFormat", set_rrd_graph_format, NULL, RSRC_CONF | ACCESS_CONF,
2751 "Explicitly set the image format. Takes any valid --imgformat value."),
2752 AP_INIT_TAKE12("RRDGraphOption", set_rrd_graph_option, NULL, RSRC_CONF | ACCESS_CONF,
2753 "Options for the rrdgraph image generator."),
2754 AP_INIT_TAKE123("RRDGraphElement", set_rrd_graph_element, NULL, RSRC_CONF | ACCESS_CONF,
2755 "Elements for the rrdgraph image generator. If specified, an optional expression can be set for the legend where appropriate."),
2756 AP_INIT_TAKE2("RRDGraphEnv", set_rrd_graph_env, NULL, RSRC_CONF | ACCESS_CONF,
2757 "Summarise environment variables from the RRD file requests."), { NULL }
2758 };
2759
2760 static void register_hooks(apr_pool_t *p)
2761 {
2762 ap_hook_child_init(rrd_child_init,NULL,NULL,APR_HOOK_MIDDLE);
2763 ap_hook_fixups(rrd_fixups, NULL, NULL, APR_HOOK_MIDDLE);
2764 ap_hook_handler(rrd_handler, NULL, NULL, APR_HOOK_FIRST);
2765 }
2766
2767 AP_DECLARE_MODULE(rrd) = {
2768 STANDARD20_MODULE_STUFF,
2769 create_rrd_config, /* create per-directory config structure */
2770 merge_rrd_config, /* merge per-directory config structures */
2771 NULL, /* create per-server config structure */
2772 NULL, /* merge per-server config structures */
2773 rrd_cmds, /* command apr_table_t */
2774 register_hooks /* register hooks */
2775 };