├── AUTHORS ├── config ├── LICENSE ├── README └── ngx_http_rrd_graph_module.c /AUTHORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/evanmiller/mod_rrd_graph/HEAD/AUTHORS -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_feature="RRDtool" 2 | ngx_feature_name= 3 | ngx_feature_run=no 4 | ngx_feature_incs="#include " 5 | ngx_feature_path= 6 | ngx_feature_libs=-lrrd 7 | ngx_feature_test="rrd_graph_v;" 8 | . auto/feature 9 | 10 | if [ $ngx_found = yes ]; then 11 | ngx_addon_name=ngx_http_rrd_graph_module 12 | HTTP_MODULES="$HTTP_MODULES ngx_http_rrd_graph_module" 13 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_rrd_graph_module.c" 14 | CORE_LIBS="$CORE_LIBS -lrrd" 15 | else 16 | cat << END 17 | $0: error: ngx_rrd_graph requires RRDtool 1.3 or later. 18 | END 19 | exit 1 20 | fi 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2011 Evan Miller (and others in AUTHORS file) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | mod_rrd_graph 2 | ============= 3 | 4 | RRDtool[1] stores and graphs time-series data. ngx_rrd_graph provides an HTTP 5 | interface to RRDtool's graphing facilities. By linking RRDtool code directly 6 | into Nginx, it is faster than scripts and CGIs with similar purposes. 7 | 8 | To install, compile nginx with this option: 9 | 10 | --add-module=/path/to/this/directory 11 | 12 | ngx_rrd_graph requires RRDtool 1.3 or later. 13 | 14 | After compiling, installing, and restarting Nginx, ngx_rrd_graph can be enabled 15 | at a particular location with the "rrd_graph" directive, like so: 16 | 17 | location /rrdtool { 18 | rrd_graph; 19 | } 20 | 21 | RRDtool graphing commands can then be appended to that location in request 22 | URLs. The syntax is just the same as the arguments to the "rrdtool graph" 23 | command, omitting the filename. (Refer to rrdgraph(1).) These commands should 24 | be URL-encoded, so that this command-line invocation: 25 | 26 | rrdtool graph --start now-300s \ 27 | --end now \ 28 | DEF:ds0=test.rrd:reading:AVERAGE \ 29 | LINE1:ds0#00FF00 30 | 31 | becomes: 32 | 33 | http://mysite.com/rrdtool--start%20now-300s%20--end%20now%20DEF%3Ads0%3Dtest.rrd%3Areading%3AAVERAGE%20LINE1%3Ads0%2300FF00 34 | 35 | If you need spaces in arguments, put quotation marks ("") around the string. 36 | 37 | The module supports all the features of your copy of RRDtool. It can output 38 | PNG, PDF, SVG, and EPS graphics (see the --imgformat option of rrdgraph(1)). 39 | 40 | If you'd prefer not to provide absolute paths to files referenced in DEF commands, 41 | you may supply a root directory with the "rrd_graph_root" directive. Files mentioned 42 | in DEF commands will be automatically prefixed with the value of rrd_graph_root. 43 | As of this writing there are no guards against relative paths (e.g. ".."). 44 | 45 | Questions/comments to emmiller@gmail.com. 46 | 47 | [1] http://oss.oetiker.ch/rrdtool/ 48 | -------------------------------------------------------------------------------- /ngx_http_rrd_graph_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mod_rrd_graph - Graph data in a Round Robin Database (RRD). 3 | * 4 | * Copyright (C) Evan Miller 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | typedef struct { 13 | ngx_str_t root; 14 | } ngx_http_rrd_graph_conf_t; 15 | 16 | static void * ngx_http_rrd_graph_create_conf(ngx_conf_t *cf); 17 | static char * ngx_http_rrd_graph_merge_conf(ngx_conf_t *cf, void *parent, void *child); 18 | static char* ngx_http_rrd_graph(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 19 | static ngx_int_t ngx_http_rrd_graph_handler(ngx_http_request_t *r); 20 | static ngx_int_t ngx_http_rrd_graph_send_image(ngx_http_request_t *r, u_char *image, size_t image_size); 21 | static ngx_int_t ngx_http_rrd_graph_parse_uri(ngx_http_request_t *r, int *argc_ptr, 22 | char ***argv_ptr, size_t **argv_len_ptr); 23 | 24 | static u_char ngx_http_png_header[] = { '\x89', 'P', 'N', 'G' }; 25 | static u_char ngx_http_pdf_header[] = { '%', 'P', 'D', 'F' }; 26 | static u_char ngx_http_svg_header[] = { '<', '?', 'x', 'm', 'l' }; 27 | static u_char ngx_http_eps_header[] = { '%', '!', 'P', 'S' }; 28 | 29 | static ngx_command_t ngx_http_rrd_graph_commands[] = { 30 | { ngx_string("rrd_graph"), 31 | NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, 32 | ngx_http_rrd_graph, 33 | 0, 34 | 0, 35 | NULL }, 36 | 37 | /* the root directory that will be prefixed to file names */ 38 | { ngx_string("rrd_graph_root"), 39 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 40 | ngx_conf_set_str_slot, 41 | NGX_HTTP_LOC_CONF_OFFSET, 42 | offsetof(ngx_http_rrd_graph_conf_t, root), 43 | NULL }, 44 | 45 | ngx_null_command 46 | }; 47 | 48 | 49 | static ngx_http_module_t ngx_http_rrd_graph_module_ctx = { 50 | NULL, /* preconfiguration */ 51 | NULL, /* postconfiguration */ 52 | 53 | NULL, /* create main configuration */ 54 | NULL, /* init main configuration */ 55 | 56 | NULL, /* create server configuration */ 57 | NULL, /* merge server configuration */ 58 | 59 | ngx_http_rrd_graph_create_conf,/* create location configuration */ 60 | ngx_http_rrd_graph_merge_conf /* merge location configuration */ 61 | }; 62 | 63 | 64 | ngx_module_t ngx_http_rrd_graph_module = { 65 | NGX_MODULE_V1, 66 | &ngx_http_rrd_graph_module_ctx, /* module context */ 67 | ngx_http_rrd_graph_commands, /* module directives */ 68 | NGX_HTTP_MODULE, /* module type */ 69 | NULL, /* init master */ 70 | NULL, /* init module */ 71 | NULL, /* init process */ 72 | NULL, /* init thread */ 73 | NULL, /* exit thread */ 74 | NULL, /* exit process */ 75 | NULL, /* exit master */ 76 | NGX_MODULE_V1_PADDING 77 | }; 78 | 79 | 80 | static ngx_int_t 81 | ngx_http_rrd_graph_handler(ngx_http_request_t *r) 82 | { 83 | ngx_int_t rc; 84 | /* image metadata */ 85 | rrd_info_t *image_info = NULL, *walker; 86 | u_char *image = NULL, *tmp; 87 | size_t image_size = 0; 88 | 89 | int argc = 3; /* two dummies + at least one real */ 90 | char **argv; 91 | size_t *argv_len; 92 | 93 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) 94 | return NGX_HTTP_NOT_ALLOWED; 95 | 96 | rc = ngx_http_discard_request_body(r); 97 | if (rc != NGX_OK && rc != NGX_AGAIN) 98 | return rc; 99 | 100 | if ((rc = ngx_http_rrd_graph_parse_uri(r, &argc, &argv, &argv_len)) != NGX_OK) 101 | return rc; 102 | 103 | image_info = rrd_graph_v(argc, argv); 104 | 105 | if (rrd_test_error()) { 106 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mod_rrd_graph: RRD graph failed: %s", rrd_get_error()); 107 | rrd_clear_error(); 108 | rrd_info_free(image_info); 109 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 110 | } 111 | 112 | for (walker = image_info; walker; walker = walker->next) { 113 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 114 | "mod_rrd_graph: found key '%s'", walker->key); 115 | if (ngx_strcmp(walker->key, "image") == 0) { 116 | image = walker->value.u_blo.ptr; 117 | image_size = walker->value.u_blo.size; 118 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 119 | "mod_rrd_graph: image size is %d bytes", image_size); 120 | } 121 | } 122 | 123 | if (image_size) { 124 | if ((tmp = ngx_palloc(r->pool, image_size)) == NULL) { 125 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 126 | "mod_rrd_graph: Failed to allocate response buffer."); 127 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 128 | } 129 | ngx_memcpy(tmp, image, image_size); 130 | rrd_info_free(image_info); 131 | return ngx_http_rrd_graph_send_image(r, tmp, image_size); 132 | } 133 | 134 | rrd_info_free(image_info); 135 | 136 | return NGX_HTTP_NOT_FOUND; 137 | } 138 | 139 | static char * 140 | ngx_http_rrd_graph(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 141 | { 142 | ngx_http_core_loc_conf_t *clcf; 143 | 144 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 145 | clcf->handler = ngx_http_rrd_graph_handler; 146 | 147 | return NGX_CONF_OK; 148 | } 149 | 150 | static ngx_int_t 151 | ngx_http_rrd_graph_parse_uri(ngx_http_request_t *r, int *argc_ptr, 152 | char ***argv_ptr, size_t **argv_len_ptr) 153 | { 154 | int i, argc = 3, in_quote = 0; 155 | char **argv; 156 | size_t *argv_len; 157 | char *tmp, *p; 158 | u_char *uri_copy; 159 | ngx_http_core_loc_conf_t *clcf; 160 | ngx_http_rrd_graph_conf_t *conf; 161 | 162 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 163 | 164 | if (r->uri.len == clcf->name.len) 165 | return NGX_HTTP_NOT_FOUND; 166 | 167 | /* tokenize */ 168 | if ((uri_copy = ngx_palloc(r->pool, (r->uri.len + 1)*sizeof(char))) == NULL) { 169 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "mod_rrd_graph: Failed to copy URI."); 170 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 171 | } 172 | 173 | ngx_memcpy(uri_copy, r->uri.data, r->uri.len); 174 | uri_copy[r->uri.len] = '\0'; /* RRDtool needs null-terminated strings */ 175 | p = (char *)uri_copy + clcf->name.len; 176 | 177 | while(*p++) { 178 | if (*p == '"') { 179 | in_quote = !in_quote; 180 | } else if (*p == ' ' && !in_quote) { 181 | argc++; 182 | } 183 | } 184 | 185 | if (in_quote) 186 | return NGX_ERROR; 187 | 188 | argv = ngx_palloc(r->pool, argc*sizeof(char *)); 189 | argv_len = ngx_pcalloc(r->pool, argc*sizeof(size_t)); 190 | argv[0] = "mod_rrd_graph"; 191 | argv[1] = "-"; 192 | argv[2] = p = (char *)uri_copy + clcf->name.len; 193 | argc = 3; 194 | while (*p) { 195 | if (*p == ' ' && !in_quote) { 196 | *p = '\0'; 197 | argv[argc++] = p+1; 198 | } else { 199 | if (*p == '"') 200 | in_quote = !in_quote; 201 | 202 | argv_len[argc-1]++; 203 | } 204 | p++; 205 | } 206 | 207 | for (i = 0; i < argc; i++) { 208 | if (*argv[i] == '\"') { 209 | argv[i] = argv[i] + 1; 210 | argv_len[i] = argv_len[i] - 2; 211 | argv[i][argv_len[i]] = '\0'; 212 | } 213 | } 214 | 215 | conf = ngx_http_get_module_loc_conf(r, ngx_http_rrd_graph_module); 216 | /* splice in the RRD directory root */ 217 | /* TODO guard against relative paths */ 218 | for (i=2; i