1 /*****************************************************************************
2 * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
7 #include "rrd_tool.h"
8 #ifdef HAVE_STDLIB_H
9 #include <stdlib.h>
10 #endif
12 #ifdef WIN32
13 #define strcasecmp stricmp
14 #define strcasencmp strnicmp
15 #endif
17 #define MEMBLK 1024
18 /*#define DEBUG_PARSER
19 #define DEBUG_VARS*/
21 typedef struct var_s {
22 char *name, *value;
23 } s_var;
25 typedef struct cgi_s {
26 s_var **vars;
27 } s_cgi;
29 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
30 and replace by result of arg[2] call */
31 int parse(
32 char **,
33 long,
34 char *,
35 char * (*)(long,
36 const char **));
38 /**************************************************/
39 /* tag replacers ... they are called from parse */
40 /* through function pointers */
41 /**************************************************/
43 /* return cgi var named arg[0] */
44 char *cgiget(
45 long,
46 const char **);
48 /* return a quoted cgi var named arg[0] */
49 char *cgigetq(
50 long,
51 const char **);
53 /* return a quoted and sanitized cgi variable */
54 char *cgigetqp(
55 long,
56 const char **);
58 /* call rrd_graph and insert appropriate image tag */
59 char *drawgraph(
60 long,
61 const char **);
63 /* return PRINT functions from last rrd_graph call */
64 char *drawprint(
65 long,
66 const char **);
68 /* pretty-print the <last></last> value for some.rrd via strftime() */
69 char *printtimelast(
70 long,
71 const char **);
73 /* pretty-print current time */
74 char *printtimenow(
75 long,
76 const char **);
78 /* set an environment variable */
79 char *rrdsetenv(
80 long,
81 const char **);
83 /* get an environment variable */
84 char *rrdgetenv(
85 long,
86 const char **);
88 /* include the named file at this point */
89 char *includefile(
90 long,
91 const char **);
93 /* for how long is the output of the cgi valid ? */
94 char *rrdgoodfor(
95 long,
96 const char **);
98 /* return rrdcgi version string */
99 char *rrdgetinternal(
100 long,
101 const char **);
103 char *rrdstrip(
104 char *buf);
105 char *scanargs(
106 char *line,
107 int *argc,
108 char ***args);
110 /* format at-time specified times using strftime */
111 char *printstrftime(
112 long,
113 const char **);
115 /** HTTP protocol needs special format, and GMT time **/
116 char *http_time(
117 time_t *);
119 /* return a pointer to newly allocated copy of this string */
120 char *stralloc(
121 const char *);
123 /* global variable for rrdcgi */
124 s_cgi *rrdcgiArg;
126 /* rrdcgiHeader
127 *
128 * Prints a valid CGI Header (Content-type...) etc.
129 */
130 void rrdcgiHeader(
131 void);
133 /* rrdcgiDecodeString
134 * decode html escapes
135 */
137 char *rrdcgiDecodeString(
138 char *text);
140 /* rrdcgiDebug
141 *
142 * Set/unsets debugging
143 */
144 void rrdcgiDebug(
145 int level,
146 int where);
148 /* rrdcgiInit
149 *
150 * Reads in variables set via POST or stdin.
151 */
152 s_cgi *rrdcgiInit(
153 void);
155 /* rrdcgiGetValue
156 *
157 * Returns the value of the specified variable or NULL if it's empty
158 * or doesn't exist.
159 */
160 char *rrdcgiGetValue(
161 s_cgi * parms,
162 const char *name);
164 /* rrdcgiFreeList
165 *
166 * Frees a list as returned by rrdcgiGetVariables()
167 */
168 void rrdcgiFreeList(
169 char **list);
171 /* rrdcgiFree
172 *
173 * Frees the internal data structures
174 */
175 void rrdcgiFree(
176 s_cgi * parms);
178 /* rrdcgiReadVariables()
179 *
180 * Read from stdin if no string is provided via CGI. Variables that
181 * doesn't have a value associated with it doesn't get stored.
182 */
183 s_var **rrdcgiReadVariables(
184 void);
187 int rrdcgiDebugLevel = 0;
188 int rrdcgiDebugStderr = 1;
189 char *rrdcgiHeaderString = NULL;
190 char *rrdcgiType = NULL;
192 /* rrd interface to the variable functions {put,get}var() */
193 char *rrdgetvar(
194 long argc,
195 const char **args);
196 char *rrdsetvar(
197 long argc,
198 const char **args);
199 char *rrdsetvarconst(
200 long argc,
201 const char **args);
204 /* variable store: put/get key-value pairs */
205 static int initvar(
206 );
207 static void donevar(
208 );
209 static const char *getvar(
210 const char *varname);
211 static const char *putvar(
212 const char *name,
213 const char *value,
214 int is_const);
216 /* key value pair that makes up an entry in the variable store */
217 typedef struct {
218 int is_const; /* const variable or not */
219 const char *name; /* variable name */
220 const char *value; /* variable value */
221 } vardata;
223 /* the variable heap:
224 start with a heapsize of 10 variables */
225 #define INIT_VARSTORE_SIZE 10
226 static vardata *varheap = NULL;
227 static size_t varheap_size = 0;
229 /* allocate and initialize variable heap */
230 static int initvar(
231 void)
232 {
233 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
234 if (varheap == NULL) {
235 fprintf(stderr, "ERROR: unable to initialize variable store\n");
236 return -1;
237 }
238 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
239 varheap_size = INIT_VARSTORE_SIZE;
240 return 0;
241 }
243 /* cleanup: free allocated memory */
244 static void donevar(
245 void)
246 {
247 int i;
249 if (varheap) {
250 for (i = 0; i < (int) varheap_size; i++) {
251 if (varheap[i].name) {
252 free((char *) varheap[i].name);
253 }
254 if (varheap[i].value) {
255 free((char *) varheap[i].value);
256 }
257 }
258 free(varheap);
259 }
260 }
262 /* Get a variable from the variable store.
263 Return NULL in case the requested variable was not found. */
264 static const char *getvar(
265 const char *name)
266 {
267 int i;
269 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
270 if (0 == strcmp(name, varheap[i].name)) {
271 #ifdef DEBUG_VARS
272 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
273 #endif
274 return varheap[i].value;
275 }
276 }
277 #ifdef DEBUG_VARS
278 printf("<!-- getvar(%s) -> Not found-->\n", name);
279 #endif
280 return NULL;
281 }
283 /* Put a variable into the variable store. If a variable by that
284 name exists, it's value is overwritten with the new value unless it was
285 marked as 'const' (initialized by RRD::SETCONSTVAR).
286 Returns a copy the newly allocated value on success, NULL on error. */
287 static const char *putvar(
288 const char *name,
289 const char *value,
290 int is_const)
291 {
292 int i;
294 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
295 if (0 == strcmp(name, varheap[i].name)) {
296 /* overwrite existing entry */
297 if (varheap[i].is_const) {
298 #ifdef DEBUG_VARS
299 printf("<!-- setver(%s, %s): not assigning: "
300 "const variable -->\n", name, value);
301 #endif
302 return varheap[i].value;
303 }
304 #ifdef DEBUG_VARS
305 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
306 name, value, varheap[i].value);
307 #endif
308 /* make it possible to promote a variable to readonly */
309 varheap[i].is_const = is_const;
310 free((char *) varheap[i].value);
311 varheap[i].value = stralloc(value);
312 return varheap[i].value;
313 }
314 }
316 /* no existing variable found by that name, add it */
317 if (i == (int) varheap_size) {
318 /* ran out of heap: resize heap to double size */
319 size_t new_size = varheap_size * 2;
321 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
322 if (!varheap) {
323 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
324 return NULL;
325 }
326 /* initialize newly allocated memory */ ;
327 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
328 varheap_size = new_size;
329 }
330 varheap[i].is_const = is_const;
331 varheap[i].name = stralloc(name);
332 varheap[i].value = stralloc(value);
334 #ifdef DEBUG_VARS
335 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
336 #endif
337 return varheap[i].value;
338 }
340 /* expand those RRD:* directives that can be used recursivly */
341 static char *rrd_expand_vars(
342 char *buffer)
343 {
344 int i;
346 #ifdef DEBUG_PARSER
347 printf("expanding variables in '%s'\n", buffer);
348 #endif
350 for (i = 0; buffer[i]; i++) {
351 if (buffer[i] != '<')
352 continue;
353 parse(&buffer, i, "<RRD::CV", cgiget);
354 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
355 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
356 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
357 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
358 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
359 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
360 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
361 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
362 }
363 return buffer;
364 }
366 static long goodfor = 0;
367 static char **calcpr = NULL;
368 static void calfree(
369 void)
370 {
371 if (calcpr) {
372 long i;
374 for (i = 0; calcpr[i]; i++) {
375 if (calcpr[i]) {
376 free(calcpr[i]);
377 }
378 }
379 if (calcpr) {
380 free(calcpr);
381 }
382 calcpr = NULL;
383 }
384 }
386 /* create freeable version of the string */
387 char *stralloc(
388 const char *str)
389 {
390 char *nstr;
392 if (!str) {
393 return NULL;
394 }
395 nstr = malloc((strlen(str) + 1));
396 strcpy(nstr, str);
397 return (nstr);
398 }
400 static int readfile(
401 const char *file_name,
402 char **buffer,
403 int skipfirst)
404 {
405 long writecnt = 0, totalcnt = MEMBLK;
406 long offset = 0;
407 FILE *input = NULL;
408 char c;
410 if ((strcmp("-", file_name) == 0)) {
411 input = stdin;
412 } else {
413 if ((input = fopen(file_name, "rb")) == NULL) {
414 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
415 return (-1);
416 }
417 }
418 if (skipfirst) {
419 do {
420 c = getc(input);
421 offset++;
422 } while (c != '\n' && !feof(input));
423 }
424 if (strcmp("-", file_name)) {
425 fseek(input, 0, SEEK_END);
426 /* have extra space for detecting EOF without realloc */
427 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
428 if (totalcnt < MEMBLK)
429 totalcnt = MEMBLK; /* sanitize */
430 fseek(input, offset * sizeof(char), SEEK_SET);
431 }
432 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
433 perror("Allocate Buffer:");
434 exit(1);
435 };
436 do {
437 writecnt +=
438 fread((*buffer) + writecnt, 1,
439 (totalcnt - writecnt) * sizeof(char), input);
440 if (writecnt >= totalcnt) {
441 totalcnt += MEMBLK;
442 if (((*buffer) =
443 rrd_realloc((*buffer),
444 (totalcnt + 4) * sizeof(char))) == NULL) {
445 perror("Realloc Buffer:");
446 exit(1);
447 };
448 }
449 } while (!feof(input));
450 (*buffer)[writecnt] = '\0';
451 if (strcmp("-", file_name) != 0) {
452 fclose(input);
453 };
454 return writecnt;
455 }
457 int main(
458 int argc,
459 char *argv[])
460 {
461 long length;
462 char *buffer;
463 char *server_url = NULL;
464 long i;
465 long filter = 0;
466 struct option long_options[] = {
467 {"filter", no_argument, 0, 'f'},
468 {0, 0, 0, 0}
469 };
471 #ifdef MUST_DISABLE_SIGFPE
472 signal(SIGFPE, SIG_IGN);
473 #endif
474 #ifdef MUST_DISABLE_FPMASK
475 fpsetmask(0);
476 #endif
477 optind = 0;
478 opterr = 0; /* initialize getopt */
480 /* what do we get for cmdline arguments?
481 for (i=0;i<argc;i++)
482 printf("%d-'%s'\n",i,argv[i]); */
483 while (1) {
484 int option_index = 0;
485 int opt;
487 opt = getopt_long(argc, argv, "f", long_options, &option_index);
488 if (opt == EOF) {
489 break;
490 }
492 switch (opt) {
493 case 'f':
494 filter = 1;
495 break;
496 case '?':
497 printf("unknown commandline option '%s'\n", argv[optind - 1]);
498 return -1;
499 }
500 }
502 if (!filter) {
503 rrdcgiDebug(0, 0);
504 rrdcgiArg = rrdcgiInit();
505 server_url = getenv("SERVER_URL");
506 }
508 /* make sure we have one extra argument,
509 if there are others, we do not care Apache gives several */
511 /* if ( (optind != argc-2
512 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
513 && optind != argc-1) { */
515 if (optind >= argc) {
516 fprintf(stderr, "ERROR: expected a filename\n");
517 exit(1);
518 } else {
519 length = readfile(argv[optind], &buffer, 1);
520 }
522 if (rrd_test_error()) {
523 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
524 exit(1);
525 }
527 /* initialize variable heap */
528 initvar();
530 #ifdef DEBUG_PARSER
531 /* some fake header for testing */
532 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
533 #endif
536 /* expand rrd directives in buffer recursivly */
537 for (i = 0; buffer[i]; i++) {
538 if (buffer[i] != '<')
539 continue;
540 if (!filter) {
541 parse(&buffer, i, "<RRD::CV", cgiget);
542 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
543 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
544 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
545 }
546 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
547 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
548 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
549 parse(&buffer, i, "<RRD::INCLUDE", includefile);
550 parse(&buffer, i, "<RRD::PRINT", drawprint);
551 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
552 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
553 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
554 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
555 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
556 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
557 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
558 }
560 if (!filter) {
561 printf("Content-Type: text/html\n"
562 "Content-Length: %zd\n", strlen(buffer));
564 if (labs(goodfor) > 0) {
565 time_t now;
567 now = time(NULL);
568 printf("Last-Modified: %s\n", http_time(&now));
569 now += labs(goodfor);
570 printf("Expires: %s\n", http_time(&now));
571 if (goodfor < 0) {
572 printf("Refresh: %ld\n", labs(goodfor));
573 }
574 }
575 printf("\n");
576 }
578 /* output result */
579 printf("%s", buffer);
581 /* cleanup */
582 calfree();
583 if (buffer) {
584 free(buffer);
585 }
586 donevar();
587 exit(0);
588 }
590 /* remove occurrences of .. this is a general measure to make
591 paths which came in via cgi do not go UP ... */
593 char *rrdsetenv(
594 long argc,
595 const char **args)
596 {
597 if (argc >= 2) {
598 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
600 if (xyz == NULL) {
601 return stralloc("[ERROR: allocating setenv buffer]");
602 };
603 sprintf(xyz, "%s=%s", args[0], args[1]);
604 if (putenv(xyz) == -1) {
605 free(xyz);
606 return stralloc("[ERROR: failed to do putenv]");
607 };
608 return stralloc("");
609 }
610 return stralloc("[ERROR: setenv failed because not enough "
611 "arguments were defined]");
612 }
614 /* rrd interface to the variable function putvar() */
615 char *rrdsetvar(
616 long argc,
617 const char **args)
618 {
619 if (argc >= 2) {
620 const char *result = putvar(args[0], args[1], 0 /* not const */ );
622 if (result) {
623 /* setvar does not return the value set */
624 return stralloc("");
625 }
626 return stralloc("[ERROR: putvar failed]");
627 }
628 return stralloc("[ERROR: putvar failed because not enough arguments "
629 "were defined]");
630 }
632 /* rrd interface to the variable function putvar() */
633 char *rrdsetvarconst(
634 long argc,
635 const char **args)
636 {
637 if (argc >= 2) {
638 const char *result = putvar(args[0], args[1], 1 /* const */ );
640 if (result) {
641 /* setvar does not return the value set */
642 return stralloc("");
643 }
644 return stralloc("[ERROR: putvar failed]");
645 }
646 return stralloc("[ERROR: putvar failed because not enough arguments "
647 "were defined]");
648 }
650 char *rrdgetenv(
651 long argc,
652 const char **args)
653 {
654 char buf[128];
655 const char *envvar;
657 if (argc != 1) {
658 return stralloc("[ERROR: getenv failed because it did not "
659 "get 1 argument only]");
660 };
661 envvar = getenv(args[0]);
662 if (envvar) {
663 return stralloc(envvar);
664 } else {
665 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
666 return stralloc(buf);
667 }
668 }
670 char *rrdgetvar(
671 long argc,
672 const char **args)
673 {
674 char buf[128];
675 const char *value;
677 if (argc != 1) {
678 return stralloc("[ERROR: getvar failed because it did not "
679 "get 1 argument only]");
680 };
681 value = getvar(args[0]);
682 if (value) {
683 return stralloc(value);
684 } else {
685 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
686 return stralloc(buf);
687 }
688 }
690 char *rrdgoodfor(
691 long argc,
692 const char **args)
693 {
694 if (argc == 1) {
695 goodfor = atol(args[0]);
696 } else {
697 return stralloc("[ERROR: goodfor expected 1 argument]");
698 }
700 if (goodfor == 0) {
701 return stralloc("[ERROR: goodfor value must not be 0]");
702 }
704 return stralloc("");
705 }
707 char *rrdgetinternal(
708 long argc,
709 const char **args)
710 {
711 if (argc == 1) {
712 if (strcasecmp(args[0], "VERSION") == 0) {
713 return stralloc(PACKAGE_VERSION);
714 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
715 return stralloc(__DATE__ " " __TIME__);
716 } else {
717 return stralloc("[ERROR: internal unknown argument]");
718 }
719 } else {
720 return stralloc("[ERROR: internal expected 1 argument]");
721 }
722 }
724 /* Format start or end times using strftime. We always need both the
725 * start and end times, because, either might be relative to the other.
726 * */
727 #define MAX_STRFTIME_SIZE 256
728 char *printstrftime(
729 long argc,
730 const char **args)
731 {
732 rrd_time_value_t start_tv, end_tv;
733 char *parsetime_error = NULL;
734 char formatted[MAX_STRFTIME_SIZE];
735 struct tm *the_tm;
736 time_t start_tmp, end_tmp;
738 /* Make sure that we were given the right number of args */
739 if (argc != 4) {
740 rrd_set_error("wrong number of args %d", argc);
741 return stralloc("");
742 }
744 /* Init start and end time */
745 rrd_parsetime("end-24h", &start_tv);
746 rrd_parsetime("now", &end_tv);
748 /* Parse the start and end times we were given */
749 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
750 rrd_set_error("start time: %s", parsetime_error);
751 return stralloc("");
752 }
753 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
754 rrd_set_error("end time: %s", parsetime_error);
755 return stralloc("");
756 }
757 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
758 return stralloc("");
759 }
761 /* Do we do the start or end */
762 if (strcasecmp(args[0], "START") == 0) {
763 the_tm = localtime(&start_tmp);
764 } else if (strcasecmp(args[0], "END") == 0) {
765 the_tm = localtime(&end_tmp);
766 } else {
767 rrd_set_error("start/end not found in '%s'", args[0]);
768 return stralloc("");
769 }
771 /* now format it */
772 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
773 return (stralloc(formatted));
774 } else {
775 rrd_set_error("strftime failed");
776 return stralloc("");
777 }
778 }
780 char *includefile(
781 long argc,
782 const char **args)
783 {
784 char *buffer;
786 if (argc >= 1) {
787 const char *filename = args[0];
789 readfile(filename, &buffer, 0);
790 if (rrd_test_error()) {
791 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
793 sprintf(err, "[ERROR: %s]", rrd_get_error());
794 rrd_clear_error();
795 return err;
796 } else {
797 return buffer;
798 }
799 } else {
800 return stralloc("[ERROR: No Inclue file defined]");
801 }
802 }
804 /* make a copy of buf and replace open/close brackets with '_' */
805 char *rrdstrip(
806 char *buf)
807 {
808 char *p;
810 if (buf == NULL) {
811 return NULL;
812 }
813 /* make a copy of the buffer */
814 buf = stralloc(buf);
815 if (buf == NULL) {
816 return NULL;
817 }
819 p = buf;
820 while (*p) {
821 if (*p == '<' || *p == '>') {
822 *p = '_';
823 }
824 p++;
825 }
826 return buf;
827 }
829 char *cgigetq(
830 long argc,
831 const char **args)
832 {
833 if (argc >= 1) {
834 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
835 char *buf2;
836 char *c, *d;
837 int qc = 0;
839 if (buf == NULL)
840 return NULL;
842 for (c = buf; *c != '\0'; c++)
843 if (*c == '"')
844 qc++;
845 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
846 perror("Malloc Buffer");
847 exit(1);
848 };
849 c = buf;
850 d = buf2;
851 *(d++) = '"';
852 while (*c != '\0') {
853 if (*c == '"') {
854 *(d++) = '"';
855 *(d++) = '\'';
856 *(d++) = '"';
857 *(d++) = '\'';
858 }
859 *(d++) = *(c++);
860 }
861 *(d++) = '"';
862 *(d) = '\0';
863 free(buf);
864 return buf2;
865 }
867 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
868 }
870 /* remove occurrences of .. this is a general measure to make
871 paths which came in via cgi do not go UP ... */
873 char *cgigetqp(
874 long argc,
875 const char **args)
876 {
877 char *buf;
878 char *buf2;
879 char *p;
880 char *d;
882 if (argc < 1) {
883 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
884 }
886 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
887 if (!buf) {
888 return NULL;
889 }
891 buf2 = malloc(strlen(buf) + 1);
892 if (!buf2) {
893 perror("cgigetqp(): Malloc Path Buffer");
894 exit(1);
895 };
897 p = buf;
898 d = buf2;
900 while (*p) {
901 /* prevent mallicious paths from entering the system */
902 if (p[0] == '.' && p[1] == '.') {
903 p += 2;
904 *d++ = '_';
905 *d++ = '_';
906 } else {
907 *d++ = *p++;
908 }
909 }
911 *d = 0;
912 free(buf);
914 /* Make sure the path is relative, e.g. does not start with '/' */
915 p = buf2;
916 while ('/' == *p) {
917 *p++ = '_';
918 }
920 return buf2;
921 }
924 char *cgiget(
925 long argc,
926 const char **args)
927 {
928 if (argc >= 1)
929 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
930 else
931 return stralloc("[ERROR: not enough arguments for RRD::CV]");
932 }
936 char *drawgraph(
937 long argc,
938 const char **args)
939 {
940 int i, xsize, ysize;
941 double ymin, ymax;
943 for (i = 0; i < argc; i++)
944 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
945 break;
946 if (i == argc) {
947 args[argc++] = "--imginfo";
948 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
949 }
950 calfree();
951 if (rrd_graph
952 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
953 &ymax) != -1) {
954 return stralloc(calcpr[0]);
955 } else {
956 if (rrd_test_error()) {
957 char *err =
958 malloc((strlen(rrd_get_error()) +
959 DS_NAM_SIZE) * sizeof(char));
960 sprintf(err, "[ERROR: %s]", rrd_get_error());
961 rrd_clear_error();
962 return err;
963 }
964 }
965 return NULL;
966 }
968 char *drawprint(
969 long argc,
970 const char **args)
971 {
972 if (argc == 1 && calcpr) {
973 long i = 0;
975 while (calcpr[i] != NULL)
976 i++; /*determine number lines in calcpr */
977 if (atol(args[0]) < i - 1)
978 return stralloc(calcpr[atol(args[0]) + 1]);
979 }
980 return stralloc("[ERROR: RRD::PRINT argument error]");
981 }
983 char *printtimelast(
984 long argc,
985 const char **args)
986 {
987 time_t last;
988 struct tm tm_last;
989 char *buf;
991 if (argc == 2) {
992 buf = malloc(255);
993 if (buf == NULL) {
994 return stralloc("[ERROR: allocating strftime buffer]");
995 };
996 /* not raising argc in step with args - 1 since the last argument
997 will be used below for strftime */
999 last = rrd_last(argc, (char **) args - 1);
1000 if (rrd_test_error()) {
1001 char *err =
1002 malloc((strlen(rrd_get_error()) +
1003 DS_NAM_SIZE) * sizeof(char));
1004 sprintf(err, "[ERROR: %s]", rrd_get_error());
1005 rrd_clear_error();
1006 return err;
1007 }
1008 tm_last = *localtime(&last);
1009 strftime(buf, 254, args[1], &tm_last);
1010 return buf;
1011 }
1012 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
1013 }
1015 char *printtimenow(
1016 long argc,
1017 const char **args)
1018 {
1019 time_t now = time(NULL);
1020 struct tm tm_now;
1021 char *buf;
1023 if (argc == 1) {
1024 buf = malloc(255);
1025 if (buf == NULL) {
1026 return stralloc("[ERROR: allocating strftime buffer]");
1027 };
1028 tm_now = *localtime(&now);
1029 strftime(buf, 254, args[0], &tm_now);
1030 return buf;
1031 }
1032 if (argc < 1) {
1033 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1034 }
1035 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1036 }
1038 /* Scan buffer until an unescaped '>' arives.
1039 * Update argument array with arguments found.
1040 * Return end cursor where parsing stopped, or NULL in case of failure.
1041 *
1042 * FIXME:
1043 * To allow nested constructs, we call rrd_expand_vars() for arguments
1044 * that contain RRD::x directives. These introduce a small memory leak
1045 * since we have to stralloc the arguments the way parse() works.
1046 */
1047 char *scanargs(
1048 char *line,
1049 int *argument_count,
1050 char ***arguments)
1051 {
1052 char *getP; /* read cursor */
1053 char *putP; /* write cursor */
1054 char Quote; /* type of quote if in quoted string, 0 otherwise */
1055 int tagcount; /* open tag count */
1056 int in_arg; /* if we currently are parsing an argument or not */
1057 int argsz; /* argument array size */
1058 int curarg_contains_rrd_directives;
1060 /* local array of arguments while parsing */
1061 int argc = 1;
1062 char **argv;
1064 #ifdef DEBUG_PARSER
1065 printf("<-- scanargs(%s) -->\n", line);
1066 #endif
1068 *arguments = NULL;
1069 *argument_count = 0;
1071 /* create initial argument array of char pointers */
1072 argsz = 32;
1073 argv = (char **) malloc(argsz * sizeof(char *));
1074 if (!argv) {
1075 return NULL;
1076 }
1077 argv[0] = "rrdcgi";
1079 /* skip leading blanks */
1080 while (isspace((int) *line)) {
1081 line++;
1082 }
1084 getP = line;
1085 putP = line;
1087 Quote = 0;
1088 in_arg = 0;
1089 tagcount = 0;
1091 curarg_contains_rrd_directives = 0;
1093 /* start parsing 'line' for arguments */
1094 while (*getP) {
1095 unsigned char c = *getP++;
1097 if (c == '>' && !Quote && !tagcount) {
1098 /* this is our closing tag, quit scanning */
1099 break;
1100 }
1102 /* remove all special chars */
1103 if (c < ' ') {
1104 c = ' ';
1105 }
1107 switch (c) {
1108 case ' ':
1109 if (Quote || tagcount) {
1110 /* copy quoted/tagged (=RRD expanded) string */
1111 *putP++ = c;
1112 } else if (in_arg) {
1113 /* end argument string */
1114 *putP++ = 0;
1115 in_arg = 0;
1116 if (curarg_contains_rrd_directives) {
1117 argv[argc - 1] =
1118 rrd_expand_vars(stralloc(argv[argc - 1]));
1119 curarg_contains_rrd_directives = 0;
1120 }
1121 }
1122 break;
1124 case '"': /* Fall through */
1125 case '\'':
1126 if (Quote != 0) {
1127 if (Quote == c) {
1128 Quote = 0;
1129 } else {
1130 /* copy quoted string */
1131 *putP++ = c;
1132 }
1133 } else {
1134 if (!in_arg) {
1135 /* reference start of argument string in argument array */
1136 argv[argc++] = putP;
1137 in_arg = 1;
1138 }
1139 Quote = c;
1140 }
1141 break;
1143 default:
1144 if (!in_arg) {
1145 /* start new argument */
1146 argv[argc++] = putP;
1147 in_arg = 1;
1148 }
1149 if (c == '>') {
1150 if (tagcount) {
1151 tagcount--;
1152 }
1153 }
1154 if (c == '<') {
1155 tagcount++;
1156 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1157 curarg_contains_rrd_directives = 1;
1158 }
1159 }
1160 *putP++ = c;
1161 break;
1162 }
1164 /* check if our argument array is still large enough */
1165 if (argc == argsz) {
1166 /* resize argument array */
1167 argsz *= 2;
1168 argv = rrd_realloc(argv, argsz * sizeof(char *));
1169 if (*argv == NULL) {
1170 return NULL;
1171 }
1172 }
1173 }
1175 /* terminate last argument found */
1176 *putP = '\0';
1177 if (curarg_contains_rrd_directives) {
1178 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1179 }
1180 #ifdef DEBUG_PARSER
1181 if (argc > 1) {
1182 int n;
1184 printf("<-- arguments found [%d]\n", argc);
1185 for (n = 0; n < argc; n++) {
1186 printf("arg %02d: '%s'\n", n, argv[n]);
1187 }
1188 printf("-->\n");
1189 } else {
1190 printf("<!-- No arguments found -->\n");
1191 }
1192 #endif
1194 /* update caller's notion of the argument array and it's size */
1196 /* note this is a bit of a hack since the rrd_cgi code used to just put
1197 its arguments into a normal array starting at 0 ... since the rrd_*
1198 commands expect and argc/argv array we used to just shift everything
1199 by -1 ... this in turn exploded when a rrd_* function tried to print
1200 argv[0] ... hence we are now doing everything in argv style but hand
1201 over seemingly the old array ... but doing argv-1 will actually end
1202 up in a 'good' place now. */
1204 *arguments = argv+1;
1205 *argument_count = argc-1;
1207 if (Quote) {
1208 return NULL;
1209 }
1211 /* Return new scanning cursor:
1212 pointer to char after closing bracket */
1213 return getP;
1214 }
1217 /*
1218 * Parse(): scan current portion of buffer for given tag.
1219 * If found, parse tag arguments and call 'func' for it.
1220 * The result of func is inserted at the current position
1221 * in the buffer.
1222 */
1223 int parse(
1224 char **buf, /* buffer */
1225 long i, /* offset in buffer */
1226 char *tag, /* tag to handle */
1227 char * (*func) (long,
1228 const char **) /* function to call for 'tag' */
1229 )
1230 {
1231 /* the name of the vairable ... */
1232 char *val;
1233 long valln;
1234 char **args;
1235 char *end;
1236 long end_offset;
1237 int argc;
1238 size_t taglen = strlen(tag);
1240 /* Current position in buffer should start with 'tag' */
1241 if (strncmp((*buf) + i, tag, taglen) != 0) {
1242 return 0;
1243 }
1244 /* .. and match exactly (a whitespace following 'tag') */
1245 if (!isspace(*((*buf) + i + taglen))) {
1246 return 0;
1247 }
1248 #ifdef DEBUG_PARSER
1249 printf("parse(): handling tag '%s'\n", tag);
1250 #endif
1252 /* Scan for arguments following the tag;
1253 scanargs() puts \0 into *buf ... so after scanargs it is probably
1254 not a good time to use strlen on buf */
1255 end = scanargs((*buf) + i + taglen, &argc, &args);
1256 if (end) {
1257 /* got arguments, call function for 'tag' with arguments */
1258 val = func(argc, (const char **) args);
1259 free(args-1);
1260 } else {
1261 /* next call, try parsing at current offset +1 */
1262 end = (*buf) + i + 1;
1264 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1265 " Check original file. This may have been altered "
1266 "by parsing.]\n\n");
1267 }
1269 /* remember offset where we have to continue parsing */
1270 end_offset = end - (*buf);
1272 valln = 0;
1273 if (val) {
1274 valln = strlen(val);
1275 }
1277 /* Optionally resize buffer to hold the replacement value:
1278 Calculating the new length of the buffer is simple. add current
1279 buffer pos (i) to length of string after replaced tag to length
1280 of replacement string and add 1 for the final zero ... */
1281 if (end - (*buf) < (i + valln)) {
1282 /* make sure we do not shrink the mallocd block */
1283 size_t newbufsize = i + strlen(end) + valln + 1;
1285 *buf = rrd_realloc(*buf, newbufsize);
1287 if (*buf == NULL) {
1288 perror("Realoc buf:");
1289 exit(1);
1290 };
1291 }
1293 /* Update new end pointer:
1294 make sure the 'end' pointer gets moved along with the
1295 buf pointer when realloc moves memory ... */
1296 end = (*buf) + end_offset;
1298 /* splice the variable:
1299 step 1. Shift pending data to make room for 'val' */
1300 memmove((*buf) + i + valln, end, strlen(end) + 1);
1302 /* step 2. Insert val */
1303 if (val) {
1304 memmove((*buf) + i, val, valln);
1305 free(val);
1306 }
1307 return (valln > 0 ? valln - 1 : valln);
1308 }
1310 char *http_time(
1311 time_t *now)
1312 {
1313 struct tm *tmptime;
1314 static char buf[60];
1316 tmptime = gmtime(now);
1317 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1318 return (buf);
1319 }
1321 void rrdcgiHeader(
1322 void)
1323 {
1324 if (rrdcgiType)
1325 printf("Content-type: %s\n", rrdcgiType);
1326 else
1327 printf("Content-type: text/html\n");
1328 if (rrdcgiHeaderString)
1329 printf("%s", rrdcgiHeaderString);
1330 printf("\n");
1331 }
1333 void rrdcgiDebug(
1334 int level,
1335 int where)
1336 {
1337 if (level > 0)
1338 rrdcgiDebugLevel = level;
1339 else
1340 rrdcgiDebugLevel = 0;
1341 if (where)
1342 rrdcgiDebugStderr = 0;
1343 else
1344 rrdcgiDebugStderr = 1;
1345 }
1347 char *rrdcgiDecodeString(
1348 char *text)
1349 {
1350 char *cp, *xp;
1352 for (cp = text, xp = text; *cp; cp++) {
1353 if (*cp == '%') {
1354 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1355 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1356 if (islower(*(cp + 1)))
1357 *(cp + 1) = toupper(*(cp + 1));
1358 if (islower(*(cp + 2)))
1359 *(cp + 2) = toupper(*(cp + 2));
1360 *(xp) =
1361 (*(cp + 1) >=
1362 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1363 (*(cp + 2) >=
1364 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1365 xp++;
1366 cp += 2;
1367 }
1368 } else {
1369 *(xp++) = *cp;
1370 }
1371 }
1372 memset(xp, 0, cp - xp);
1373 return text;
1374 }
1376 /* rrdcgiReadVariables()
1377 *
1378 * Read from stdin if no string is provided via CGI. Variables that
1379 * doesn't have a value associated with it doesn't get stored.
1380 */
1381 s_var **rrdcgiReadVariables(
1382 void)
1383 {
1384 int length;
1385 char *line = NULL;
1386 int numargs;
1387 char *cp, *ip, *esp, *sptr;
1388 s_var **result;
1389 int i, k, len;
1390 char tmp[101];
1392 cp = getenv("REQUEST_METHOD");
1393 ip = getenv("CONTENT_LENGTH");
1395 if (cp && !strcmp(cp, "POST")) {
1396 if (ip) {
1397 length = atoi(ip);
1398 if ((line = (char *) malloc(length + 2)) == NULL)
1399 return NULL;
1400 if (fgets(line, length + 1, stdin) == NULL)
1401 return NULL;
1402 } else
1403 return NULL;
1404 } else if (cp && !strcmp(cp, "GET")) {
1405 esp = getenv("QUERY_STRING");
1406 if (esp && strlen(esp)) {
1407 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1408 return NULL;
1409 sprintf(line, "%s", esp);
1410 } else
1411 return NULL;
1412 } else {
1413 length = 0;
1414 printf("(offline mode: enter name=value pairs on standard input)\n");
1415 memset(tmp, 0, sizeof(tmp));
1416 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1417 if (strlen(tmp)) {
1418 if (tmp[strlen(tmp) - 1] == '\n')
1419 tmp[strlen(tmp) - 1] = '&';
1420 if (length) {
1421 length += strlen(tmp);
1422 len = (length + 1) * sizeof(char);
1423 if ((line = (char *) realloc(line, len)) == NULL)
1424 return NULL;
1425 strcat(line, tmp);
1426 } else {
1427 length = strlen(tmp);
1428 len = (length + 1) * sizeof(char);
1429 if ((line = (char *) malloc(len)) == NULL)
1430 return NULL;
1431 memset(line, 0, len);
1432 strcpy(line, tmp);
1433 }
1434 }
1435 memset(tmp, 0, sizeof(tmp));
1436 }
1437 if (!line)
1438 return NULL;
1439 if (line[strlen(line) - 1] == '&')
1440 line[strlen(line) - 1] = '\0';
1441 }
1443 /*
1444 * From now on all cgi variables are stored in the variable line
1445 * and look like foo=bar&foobar=barfoo&foofoo=
1446 */
1448 if (rrdcgiDebugLevel > 0) {
1449 if (rrdcgiDebugStderr)
1450 fprintf(stderr, "Received cgi input: %s\n", line);
1451 else
1452 printf
1453 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1454 line);
1455 }
1457 for (cp = line; *cp; cp++)
1458 if (*cp == '+')
1459 *cp = ' ';
1461 if (strlen(line)) {
1462 for (numargs = 1, cp = line; *cp; cp++)
1463 if (*cp == '&')
1464 numargs++;
1465 } else
1466 numargs = 0;
1467 if (rrdcgiDebugLevel > 0) {
1468 if (rrdcgiDebugStderr)
1469 fprintf(stderr, "%d cgi variables found.\n", numargs);
1470 else
1471 printf("%d cgi variables found.<br>\n", numargs);
1472 }
1474 len = (numargs + 1) * sizeof(s_var *);
1475 if ((result = (s_var **) malloc(len)) == NULL)
1476 return NULL;
1477 memset(result, 0, len);
1479 cp = line;
1480 i = 0;
1481 while (*cp) {
1482 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1483 *ip = '\0';
1484 } else
1485 ip = cp + strlen(cp);
1487 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1488 cp = ++ip;
1489 continue;
1490 }
1492 if (!strlen(esp)) {
1493 cp = ++ip;
1494 continue;
1495 }
1497 if (i < numargs) {
1499 /* try to find out if there's already such a variable */
1500 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1501 || !(strlen(result[k]->name) ==
1502 (size_t) (esp - cp))); k++);
1504 if (k == i) { /* No such variable yet */
1505 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1506 return NULL;
1507 if ((result[i]->name =
1508 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1509 return NULL;
1510 memset(result[i]->name, 0, esp - cp + 1);
1511 strncpy(result[i]->name, cp, esp - cp);
1512 cp = ++esp;
1513 if ((result[i]->value =
1514 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1515 return NULL;
1516 memset(result[i]->value, 0, ip - esp + 1);
1517 strncpy(result[i]->value, cp, ip - esp);
1518 result[i]->value = rrdcgiDecodeString(result[i]->value);
1519 if (rrdcgiDebugLevel) {
1520 if (rrdcgiDebugStderr)
1521 fprintf(stderr, "%s: %s\n", result[i]->name,
1522 result[i]->value);
1523 else
1524 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1525 result[i]->name, result[i]->value);
1526 }
1527 i++;
1528 } else { /* There is already such a name, suppose a mutiple field */
1529 cp = ++esp;
1530 len =
1531 (strlen(result[k]->value) + (ip - esp) +
1532 2) * sizeof(char);
1533 if ((sptr = (char *) malloc(len)) == NULL)
1534 return NULL;
1535 memset(sptr, 0, len);
1536 sprintf(sptr, "%s\n", result[k]->value);
1537 strncat(sptr, cp, ip - esp);
1538 free(result[k]->value);
1539 result[k]->value = rrdcgiDecodeString(sptr);
1540 }
1541 }
1542 cp = ++ip;
1543 }
1544 return result;
1545 }
1547 /* rrdcgiInit()
1548 *
1549 * Read from stdin if no string is provided via CGI. Variables that
1550 * doesn't have a value associated with it doesn't get stored.
1551 */
1552 s_cgi *rrdcgiInit(
1553 void)
1554 {
1555 s_cgi *res;
1556 s_var **vars;
1558 vars = rrdcgiReadVariables();
1560 if (!vars)
1561 return NULL;
1563 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1564 return NULL;
1565 res->vars = vars;
1567 return res;
1568 }
1570 char *rrdcgiGetValue(
1571 s_cgi * parms,
1572 const char *name)
1573 {
1574 int i;
1576 if (!parms || !parms->vars)
1577 return NULL;
1578 for (i = 0; parms->vars[i]; i++)
1579 if (!strcmp(name, parms->vars[i]->name)) {
1580 if (rrdcgiDebugLevel > 0) {
1581 if (rrdcgiDebugStderr)
1582 fprintf(stderr, "%s found as %s\n", name,
1583 parms->vars[i]->value);
1584 else
1585 printf("%s found as %s<br>\n", name,
1586 parms->vars[i]->value);
1587 }
1588 return parms->vars[i]->value;
1589 }
1590 if (rrdcgiDebugLevel) {
1591 if (rrdcgiDebugStderr)
1592 fprintf(stderr, "%s not found\n", name);
1593 else
1594 printf("%s not found<br>\n", name);
1595 }
1596 return NULL;
1597 }
1599 void rrdcgiFreeList(
1600 char **list)
1601 {
1602 int i;
1604 for (i = 0; list[i] != NULL; i++)
1605 free(list[i]);
1606 free(list);
1607 }
1609 void rrdcgiFree(
1610 s_cgi * parms)
1611 {
1612 int i;
1614 if (!parms)
1615 return;
1616 if (parms->vars) {
1617 for (i = 0; parms->vars[i]; i++) {
1618 if (parms->vars[i]->name)
1619 free(parms->vars[i]->name);
1620 if (parms->vars[i]->value)
1621 free(parms->vars[i]->value);
1622 free(parms->vars[i]);
1623 }
1624 free(parms->vars);
1625 }
1626 free(parms);
1628 if (rrdcgiHeaderString) {
1629 free(rrdcgiHeaderString);
1630 rrdcgiHeaderString = NULL;
1631 }
1632 if (rrdcgiType) {
1633 free(rrdcgiType);
1634 rrdcgiType = NULL;
1635 }
1636 }