"Fossies" - the Fresh Open Source Software Archive

Member "mod_rrd-1.0.1/mod_rrd.c" (5 Dec 2019, 81237 Bytes) of package /linux/www/apache_httpd_modules/mod_rrd-1.0.1.tar.bz2:


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 };