1 /*****************************************************************************
2 * RRDtool 1.4.8 Copyright by Tobi Oetiker, 1997-2013
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 char *buffer;
462 long i;
463 long filter = 0;
464 struct option long_options[] = {
465 {"filter", no_argument, 0, 'f'},
466 {0, 0, 0, 0}
467 };
469 #ifdef MUST_DISABLE_SIGFPE
470 signal(SIGFPE, SIG_IGN);
471 #endif
472 #ifdef MUST_DISABLE_FPMASK
473 fpsetmask(0);
474 #endif
475 optind = 0;
476 opterr = 0; /* initialize getopt */
478 /* what do we get for cmdline arguments?
479 for (i=0;i<argc;i++)
480 printf("%d-'%s'\n",i,argv[i]); */
481 while (1) {
482 int option_index = 0;
483 int opt;
485 opt = getopt_long(argc, argv, "f", long_options, &option_index);
486 if (opt == EOF) {
487 break;
488 }
490 switch (opt) {
491 case 'f':
492 filter = 1;
493 break;
494 case '?':
495 printf("unknown commandline option '%s'\n", argv[optind - 1]);
496 return -1;
497 }
498 }
500 if (!filter) {
501 rrdcgiDebug(0, 0);
502 rrdcgiArg = rrdcgiInit();
503 }
505 /* make sure we have one extra argument,
506 if there are others, we do not care Apache gives several */
508 /* if ( (optind != argc-2
509 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
510 && optind != argc-1) { */
512 if (optind >= argc) {
513 fprintf(stderr, "ERROR: expected a filename\n");
514 exit(1);
515 } else {
516 readfile(argv[optind], &buffer, 1);
517 }
519 if (rrd_test_error()) {
520 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
521 exit(1);
522 }
524 /* initialize variable heap */
525 initvar();
527 #ifdef DEBUG_PARSER
528 /* some fake header for testing */
529 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
530 #endif
533 /* expand rrd directives in buffer recursivly */
534 for (i = 0; buffer[i]; i++) {
535 if (buffer[i] != '<')
536 continue;
537 if (!filter) {
538 parse(&buffer, i, "<RRD::CV", cgiget);
539 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
540 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
541 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
542 }
543 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
544 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
545 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
546 parse(&buffer, i, "<RRD::INCLUDE", includefile);
547 parse(&buffer, i, "<RRD::PRINT", drawprint);
548 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
549 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
550 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
551 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
552 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
553 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
554 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
555 }
557 if (!filter) {
558 printf("Content-Type: text/html\n"
559 "Content-Length: %zd\n", strlen(buffer));
561 if (labs(goodfor) > 0) {
562 time_t now;
564 now = time(NULL);
565 printf("Last-Modified: %s\n", http_time(&now));
566 now += labs(goodfor);
567 printf("Expires: %s\n", http_time(&now));
568 if (goodfor < 0) {
569 printf("Refresh: %ld\n", labs(goodfor));
570 }
571 }
572 printf("\n");
573 }
575 /* output result */
576 printf("%s", buffer);
578 /* cleanup */
579 calfree();
580 if (buffer) {
581 free(buffer);
582 }
583 donevar();
584 exit(0);
585 }
587 /* remove occurrences of .. this is a general measure to make
588 paths which came in via cgi do not go UP ... */
590 char *rrdsetenv(
591 long argc,
592 const char **args)
593 {
594 if (argc >= 2) {
595 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
597 if (xyz == NULL) {
598 return stralloc("[ERROR: allocating setenv buffer]");
599 };
600 sprintf(xyz, "%s=%s", args[0], args[1]);
601 if (putenv(xyz) == -1) {
602 free(xyz);
603 return stralloc("[ERROR: failed to do putenv]");
604 };
605 return stralloc("");
606 }
607 return stralloc("[ERROR: setenv failed because not enough "
608 "arguments were defined]");
609 }
611 /* rrd interface to the variable function putvar() */
612 char *rrdsetvar(
613 long argc,
614 const char **args)
615 {
616 if (argc >= 2) {
617 const char *result = putvar(args[0], args[1], 0 /* not const */ );
619 if (result) {
620 /* setvar does not return the value set */
621 return stralloc("");
622 }
623 return stralloc("[ERROR: putvar failed]");
624 }
625 return stralloc("[ERROR: putvar failed because not enough arguments "
626 "were defined]");
627 }
629 /* rrd interface to the variable function putvar() */
630 char *rrdsetvarconst(
631 long argc,
632 const char **args)
633 {
634 if (argc >= 2) {
635 const char *result = putvar(args[0], args[1], 1 /* const */ );
637 if (result) {
638 /* setvar does not return the value set */
639 return stralloc("");
640 }
641 return stralloc("[ERROR: putvar failed]");
642 }
643 return stralloc("[ERROR: putvar failed because not enough arguments "
644 "were defined]");
645 }
647 char *rrdgetenv(
648 long argc,
649 const char **args)
650 {
651 char buf[128];
652 const char *envvar;
654 if (argc != 1) {
655 return stralloc("[ERROR: getenv failed because it did not "
656 "get 1 argument only]");
657 };
658 envvar = getenv(args[0]);
659 if (envvar) {
660 return stralloc(envvar);
661 } else {
662 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
663 return stralloc(buf);
664 }
665 }
667 char *rrdgetvar(
668 long argc,
669 const char **args)
670 {
671 char buf[128];
672 const char *value;
674 if (argc != 1) {
675 return stralloc("[ERROR: getvar failed because it did not "
676 "get 1 argument only]");
677 };
678 value = getvar(args[0]);
679 if (value) {
680 return stralloc(value);
681 } else {
682 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
683 return stralloc(buf);
684 }
685 }
687 char *rrdgoodfor(
688 long argc,
689 const char **args)
690 {
691 if (argc == 1) {
692 goodfor = atol(args[0]);
693 } else {
694 return stralloc("[ERROR: goodfor expected 1 argument]");
695 }
697 if (goodfor == 0) {
698 return stralloc("[ERROR: goodfor value must not be 0]");
699 }
701 return stralloc("");
702 }
704 char *rrdgetinternal(
705 long argc,
706 const char **args)
707 {
708 if (argc == 1) {
709 if (strcasecmp(args[0], "VERSION") == 0) {
710 return stralloc(PACKAGE_VERSION);
711 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
712 return stralloc(__DATE__ " " __TIME__);
713 } else {
714 return stralloc("[ERROR: internal unknown argument]");
715 }
716 } else {
717 return stralloc("[ERROR: internal expected 1 argument]");
718 }
719 }
721 /* Format start or end times using strftime. We always need both the
722 * start and end times, because, either might be relative to the other.
723 * */
724 #define MAX_STRFTIME_SIZE 256
725 char *printstrftime(
726 long argc,
727 const char **args)
728 {
729 rrd_time_value_t start_tv, end_tv;
730 char *parsetime_error = NULL;
731 char formatted[MAX_STRFTIME_SIZE];
732 struct tm *the_tm;
733 time_t start_tmp, end_tmp;
735 /* Make sure that we were given the right number of args */
736 if (argc != 4) {
737 rrd_set_error("wrong number of args %d", argc);
738 return stralloc("");
739 }
741 /* Init start and end time */
742 rrd_parsetime("end-24h", &start_tv);
743 rrd_parsetime("now", &end_tv);
745 /* Parse the start and end times we were given */
746 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
747 rrd_set_error("start time: %s", parsetime_error);
748 return stralloc("");
749 }
750 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
751 rrd_set_error("end time: %s", parsetime_error);
752 return stralloc("");
753 }
754 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
755 return stralloc("");
756 }
758 /* Do we do the start or end */
759 if (strcasecmp(args[0], "START") == 0) {
760 the_tm = localtime(&start_tmp);
761 } else if (strcasecmp(args[0], "END") == 0) {
762 the_tm = localtime(&end_tmp);
763 } else {
764 rrd_set_error("start/end not found in '%s'", args[0]);
765 return stralloc("");
766 }
768 /* now format it */
769 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
770 return (stralloc(formatted));
771 } else {
772 rrd_set_error("strftime failed");
773 return stralloc("");
774 }
775 }
777 char *includefile(
778 long argc,
779 const char **args)
780 {
781 char *buffer;
783 if (argc >= 1) {
784 const char *filename = args[0];
786 readfile(filename, &buffer, 0);
787 if (rrd_test_error()) {
788 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
790 sprintf(err, "[ERROR: %s]", rrd_get_error());
791 rrd_clear_error();
792 return err;
793 } else {
794 return buffer;
795 }
796 } else {
797 return stralloc("[ERROR: No Inclue file defined]");
798 }
799 }
801 /* make a copy of buf and replace open/close brackets with '_' */
802 char *rrdstrip(
803 char *buf)
804 {
805 char *p;
807 if (buf == NULL) {
808 return NULL;
809 }
810 /* make a copy of the buffer */
811 buf = stralloc(buf);
812 if (buf == NULL) {
813 return NULL;
814 }
816 p = buf;
817 while (*p) {
818 if (*p == '<' || *p == '>') {
819 *p = '_';
820 }
821 p++;
822 }
823 return buf;
824 }
826 char *cgigetq(
827 long argc,
828 const char **args)
829 {
830 if (argc >= 1) {
831 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
832 char *buf2;
833 char *c, *d;
834 int qc = 0;
836 if (buf == NULL)
837 return NULL;
839 for (c = buf; *c != '\0'; c++)
840 if (*c == '"')
841 qc++;
842 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
843 perror("Malloc Buffer");
844 exit(1);
845 };
846 c = buf;
847 d = buf2;
848 *(d++) = '"';
849 while (*c != '\0') {
850 if (*c == '"') {
851 *(d++) = '"';
852 *(d++) = '\'';
853 *(d++) = '"';
854 *(d++) = '\'';
855 }
856 *(d++) = *(c++);
857 }
858 *(d++) = '"';
859 *(d) = '\0';
860 free(buf);
861 return buf2;
862 }
864 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
865 }
867 /* remove occurrences of .. this is a general measure to make
868 paths which came in via cgi do not go UP ... */
870 char *cgigetqp(
871 long argc,
872 const char **args)
873 {
874 char *buf;
875 char *buf2;
876 char *p;
877 char *d;
879 if (argc < 1) {
880 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
881 }
883 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
884 if (!buf) {
885 return NULL;
886 }
888 buf2 = malloc(strlen(buf) + 1);
889 if (!buf2) {
890 perror("cgigetqp(): Malloc Path Buffer");
891 exit(1);
892 };
894 p = buf;
895 d = buf2;
897 while (*p) {
898 /* prevent mallicious paths from entering the system */
899 if (p[0] == '.' && p[1] == '.') {
900 p += 2;
901 *d++ = '_';
902 *d++ = '_';
903 } else {
904 *d++ = *p++;
905 }
906 }
908 *d = 0;
909 free(buf);
911 /* Make sure the path is relative, e.g. does not start with '/' */
912 p = buf2;
913 while ('/' == *p) {
914 *p++ = '_';
915 }
917 return buf2;
918 }
921 char *cgiget(
922 long argc,
923 const char **args)
924 {
925 if (argc >= 1)
926 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
927 else
928 return stralloc("[ERROR: not enough arguments for RRD::CV]");
929 }
933 char *drawgraph(
934 long argc,
935 const char **args)
936 {
937 int i, xsize, ysize;
938 double ymin, ymax;
940 for (i = 0; i < argc; i++)
941 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
942 break;
943 if (i == argc) {
944 args[argc++] = "--imginfo";
945 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
946 }
947 calfree();
948 if (rrd_graph
949 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
950 &ymax) != -1) {
951 return stralloc(calcpr[0]);
952 } else {
953 if (rrd_test_error()) {
954 char *err =
955 malloc((strlen(rrd_get_error()) +
956 DS_NAM_SIZE) * sizeof(char));
957 sprintf(err, "[ERROR: %s]", rrd_get_error());
958 rrd_clear_error();
959 return err;
960 }
961 }
962 return NULL;
963 }
965 char *drawprint(
966 long argc,
967 const char **args)
968 {
969 if (argc == 1 && calcpr) {
970 long i = 0;
972 while (calcpr[i] != NULL)
973 i++; /*determine number lines in calcpr */
974 if (atol(args[0]) < i - 1)
975 return stralloc(calcpr[atol(args[0]) + 1]);
976 }
977 return stralloc("[ERROR: RRD::PRINT argument error]");
978 }
980 char *printtimelast(
981 long argc,
982 const char **args)
983 {
984 time_t last;
985 struct tm tm_last;
986 char *buf;
988 if (argc == 2) {
989 buf = malloc(255);
990 if (buf == NULL) {
991 return stralloc("[ERROR: allocating strftime buffer]");
992 };
993 /* not raising argc in step with args - 1 since the last argument
994 will be used below for strftime */
996 last = rrd_last(argc, (char **) args - 1);
997 if (rrd_test_error()) {
998 char *err =
999 malloc((strlen(rrd_get_error()) +
1000 DS_NAM_SIZE) * sizeof(char));
1001 sprintf(err, "[ERROR: %s]", rrd_get_error());
1002 rrd_clear_error();
1003 return err;
1004 }
1005 tm_last = *localtime(&last);
1006 strftime(buf, 254, args[1], &tm_last);
1007 return buf;
1008 }
1009 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
1010 }
1012 char *printtimenow(
1013 long argc,
1014 const char **args)
1015 {
1016 time_t now = time(NULL);
1017 struct tm tm_now;
1018 char *buf;
1020 if (argc == 1) {
1021 buf = malloc(255);
1022 if (buf == NULL) {
1023 return stralloc("[ERROR: allocating strftime buffer]");
1024 };
1025 tm_now = *localtime(&now);
1026 strftime(buf, 254, args[0], &tm_now);
1027 return buf;
1028 }
1029 if (argc < 1) {
1030 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1031 }
1032 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1033 }
1035 /* Scan buffer until an unescaped '>' arives.
1036 * Update argument array with arguments found.
1037 * Return end cursor where parsing stopped, or NULL in case of failure.
1038 *
1039 * FIXME:
1040 * To allow nested constructs, we call rrd_expand_vars() for arguments
1041 * that contain RRD::x directives. These introduce a small memory leak
1042 * since we have to stralloc the arguments the way parse() works.
1043 */
1044 char *scanargs(
1045 char *line,
1046 int *argument_count,
1047 char ***arguments)
1048 {
1049 char *getP; /* read cursor */
1050 char *putP; /* write cursor */
1051 char Quote; /* type of quote if in quoted string, 0 otherwise */
1052 int tagcount; /* open tag count */
1053 int in_arg; /* if we currently are parsing an argument or not */
1054 int argsz; /* argument array size */
1055 int curarg_contains_rrd_directives;
1057 /* local array of arguments while parsing */
1058 int argc = 1;
1059 char **argv;
1061 #ifdef DEBUG_PARSER
1062 printf("<-- scanargs(%s) -->\n", line);
1063 #endif
1065 *arguments = NULL;
1066 *argument_count = 0;
1068 /* create initial argument array of char pointers */
1069 argsz = 32;
1070 argv = (char **) malloc(argsz * sizeof(char *));
1071 if (!argv) {
1072 return NULL;
1073 }
1074 argv[0] = "rrdcgi";
1076 /* skip leading blanks */
1077 while (isspace((int) *line)) {
1078 line++;
1079 }
1081 getP = line;
1082 putP = line;
1084 Quote = 0;
1085 in_arg = 0;
1086 tagcount = 0;
1088 curarg_contains_rrd_directives = 0;
1090 /* start parsing 'line' for arguments */
1091 while (*getP) {
1092 unsigned char c = *getP++;
1094 if (c == '>' && !Quote && !tagcount) {
1095 /* this is our closing tag, quit scanning */
1096 break;
1097 }
1099 /* remove all special chars */
1100 if (c < ' ') {
1101 c = ' ';
1102 }
1104 switch (c) {
1105 case ' ':
1106 if (Quote || tagcount) {
1107 /* copy quoted/tagged (=RRD expanded) string */
1108 *putP++ = c;
1109 } else if (in_arg) {
1110 /* end argument string */
1111 *putP++ = 0;
1112 in_arg = 0;
1113 if (curarg_contains_rrd_directives) {
1114 argv[argc - 1] =
1115 rrd_expand_vars(stralloc(argv[argc - 1]));
1116 curarg_contains_rrd_directives = 0;
1117 }
1118 }
1119 break;
1121 case '"': /* Fall through */
1122 case '\'':
1123 if (Quote != 0) {
1124 if (Quote == c) {
1125 Quote = 0;
1126 } else {
1127 /* copy quoted string */
1128 *putP++ = c;
1129 }
1130 } else {
1131 if (!in_arg) {
1132 /* reference start of argument string in argument array */
1133 argv[argc++] = putP;
1134 in_arg = 1;
1135 }
1136 Quote = c;
1137 }
1138 break;
1140 default:
1141 if (!in_arg) {
1142 /* start new argument */
1143 argv[argc++] = putP;
1144 in_arg = 1;
1145 }
1146 if (c == '>') {
1147 if (tagcount) {
1148 tagcount--;
1149 }
1150 }
1151 if (c == '<') {
1152 tagcount++;
1153 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1154 curarg_contains_rrd_directives = 1;
1155 }
1156 }
1157 *putP++ = c;
1158 break;
1159 }
1161 /* check if our argument array is still large enough */
1162 if (argc == argsz) {
1163 /* resize argument array */
1164 argsz *= 2;
1165 argv = rrd_realloc(argv, argsz * sizeof(char *));
1166 if (*argv == NULL) {
1167 return NULL;
1168 }
1169 }
1170 }
1172 /* terminate last argument found */
1173 *putP = '\0';
1174 if (curarg_contains_rrd_directives) {
1175 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1176 }
1177 #ifdef DEBUG_PARSER
1178 if (argc > 1) {
1179 int n;
1181 printf("<-- arguments found [%d]\n", argc);
1182 for (n = 0; n < argc; n++) {
1183 printf("arg %02d: '%s'\n", n, argv[n]);
1184 }
1185 printf("-->\n");
1186 } else {
1187 printf("<!-- No arguments found -->\n");
1188 }
1189 #endif
1191 /* update caller's notion of the argument array and it's size */
1193 /* note this is a bit of a hack since the rrd_cgi code used to just put
1194 its arguments into a normal array starting at 0 ... since the rrd_*
1195 commands expect and argc/argv array we used to just shift everything
1196 by -1 ... this in turn exploded when a rrd_* function tried to print
1197 argv[0] ... hence we are now doing everything in argv style but hand
1198 over seemingly the old array ... but doing argv-1 will actually end
1199 up in a 'good' place now. */
1201 *arguments = argv+1;
1202 *argument_count = argc-1;
1204 if (Quote) {
1205 return NULL;
1206 }
1208 /* Return new scanning cursor:
1209 pointer to char after closing bracket */
1210 return getP;
1211 }
1214 /*
1215 * Parse(): scan current portion of buffer for given tag.
1216 * If found, parse tag arguments and call 'func' for it.
1217 * The result of func is inserted at the current position
1218 * in the buffer.
1219 */
1220 int parse(
1221 char **buf, /* buffer */
1222 long i, /* offset in buffer */
1223 char *tag, /* tag to handle */
1224 char * (*func) (long,
1225 const char **) /* function to call for 'tag' */
1226 )
1227 {
1228 /* the name of the vairable ... */
1229 char *val;
1230 long valln;
1231 char **args;
1232 char *end;
1233 long end_offset;
1234 int argc;
1235 size_t taglen = strlen(tag);
1237 /* Current position in buffer should start with 'tag' */
1238 if (strncmp((*buf) + i, tag, taglen) != 0) {
1239 return 0;
1240 }
1241 /* .. and match exactly (a whitespace following 'tag') */
1242 if (!isspace(*((*buf) + i + taglen))) {
1243 return 0;
1244 }
1245 #ifdef DEBUG_PARSER
1246 printf("parse(): handling tag '%s'\n", tag);
1247 #endif
1249 /* Scan for arguments following the tag;
1250 scanargs() puts \0 into *buf ... so after scanargs it is probably
1251 not a good time to use strlen on buf */
1252 end = scanargs((*buf) + i + taglen, &argc, &args);
1253 if (end) {
1254 /* got arguments, call function for 'tag' with arguments */
1255 val = func(argc, (const char **) args);
1256 free(args-1);
1257 } else {
1258 /* next call, try parsing at current offset +1 */
1259 end = (*buf) + i + 1;
1261 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1262 " Check original file. This may have been altered "
1263 "by parsing.]\n\n");
1264 }
1266 /* remember offset where we have to continue parsing */
1267 end_offset = end - (*buf);
1269 valln = 0;
1270 if (val) {
1271 valln = strlen(val);
1272 }
1274 /* Optionally resize buffer to hold the replacement value:
1275 Calculating the new length of the buffer is simple. add current
1276 buffer pos (i) to length of string after replaced tag to length
1277 of replacement string and add 1 for the final zero ... */
1278 if (end - (*buf) < (i + valln)) {
1279 /* make sure we do not shrink the mallocd block */
1280 size_t newbufsize = i + strlen(end) + valln + 1;
1282 *buf = rrd_realloc(*buf, newbufsize);
1284 if (*buf == NULL) {
1285 perror("Realoc buf:");
1286 exit(1);
1287 };
1288 }
1290 /* Update new end pointer:
1291 make sure the 'end' pointer gets moved along with the
1292 buf pointer when realloc moves memory ... */
1293 end = (*buf) + end_offset;
1295 /* splice the variable:
1296 step 1. Shift pending data to make room for 'val' */
1297 memmove((*buf) + i + valln, end, strlen(end) + 1);
1299 /* step 2. Insert val */
1300 if (val) {
1301 memmove((*buf) + i, val, valln);
1302 free(val);
1303 }
1304 return (valln > 0 ? valln - 1 : valln);
1305 }
1307 char *http_time(
1308 time_t *now)
1309 {
1310 struct tm *tmptime;
1311 static char buf[60];
1313 tmptime = gmtime(now);
1314 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1315 return (buf);
1316 }
1318 void rrdcgiHeader(
1319 void)
1320 {
1321 if (rrdcgiType)
1322 printf("Content-type: %s\n", rrdcgiType);
1323 else
1324 printf("Content-type: text/html\n");
1325 if (rrdcgiHeaderString)
1326 printf("%s", rrdcgiHeaderString);
1327 printf("\n");
1328 }
1330 void rrdcgiDebug(
1331 int level,
1332 int where)
1333 {
1334 if (level > 0)
1335 rrdcgiDebugLevel = level;
1336 else
1337 rrdcgiDebugLevel = 0;
1338 if (where)
1339 rrdcgiDebugStderr = 0;
1340 else
1341 rrdcgiDebugStderr = 1;
1342 }
1344 char *rrdcgiDecodeString(
1345 char *text)
1346 {
1347 char *cp, *xp;
1349 for (cp = text, xp = text; *cp; cp++) {
1350 if (*cp == '%') {
1351 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1352 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1353 if (islower(*(cp + 1)))
1354 *(cp + 1) = toupper(*(cp + 1));
1355 if (islower(*(cp + 2)))
1356 *(cp + 2) = toupper(*(cp + 2));
1357 *(xp) =
1358 (*(cp + 1) >=
1359 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1360 (*(cp + 2) >=
1361 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1362 xp++;
1363 cp += 2;
1364 }
1365 } else {
1366 *(xp++) = *cp;
1367 }
1368 }
1369 memset(xp, 0, cp - xp);
1370 return text;
1371 }
1373 /* rrdcgiReadVariables()
1374 *
1375 * Read from stdin if no string is provided via CGI. Variables that
1376 * doesn't have a value associated with it doesn't get stored.
1377 */
1378 s_var **rrdcgiReadVariables(
1379 void)
1380 {
1381 int length;
1382 char *line = NULL;
1383 int numargs;
1384 char *cp, *ip, *esp, *sptr;
1385 s_var **result;
1386 int i, k, len;
1387 char tmp[101];
1389 cp = getenv("REQUEST_METHOD");
1390 ip = getenv("CONTENT_LENGTH");
1392 if (cp && !strcmp(cp, "POST")) {
1393 if (ip) {
1394 length = atoi(ip);
1395 if ((line = (char *) malloc(length + 2)) == NULL)
1396 return NULL;
1397 if (fgets(line, length + 1, stdin) == NULL)
1398 return NULL;
1399 } else
1400 return NULL;
1401 } else if (cp && !strcmp(cp, "GET")) {
1402 esp = getenv("QUERY_STRING");
1403 if (esp && strlen(esp)) {
1404 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1405 return NULL;
1406 sprintf(line, "%s", esp);
1407 } else
1408 return NULL;
1409 } else {
1410 length = 0;
1411 printf("(offline mode: enter name=value pairs on standard input)\n");
1412 memset(tmp, 0, sizeof(tmp));
1413 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1414 if (strlen(tmp)) {
1415 if (tmp[strlen(tmp) - 1] == '\n')
1416 tmp[strlen(tmp) - 1] = '&';
1417 if (length) {
1418 length += strlen(tmp);
1419 len = (length + 1) * sizeof(char);
1420 if ((line = (char *) realloc(line, len)) == NULL)
1421 return NULL;
1422 strcat(line, tmp);
1423 } else {
1424 length = strlen(tmp);
1425 len = (length + 1) * sizeof(char);
1426 if ((line = (char *) malloc(len)) == NULL)
1427 return NULL;
1428 memset(line, 0, len);
1429 strcpy(line, tmp);
1430 }
1431 }
1432 memset(tmp, 0, sizeof(tmp));
1433 }
1434 if (!line)
1435 return NULL;
1436 if (line[strlen(line) - 1] == '&')
1437 line[strlen(line) - 1] = '\0';
1438 }
1440 /*
1441 * From now on all cgi variables are stored in the variable line
1442 * and look like foo=bar&foobar=barfoo&foofoo=
1443 */
1445 if (rrdcgiDebugLevel > 0) {
1446 if (rrdcgiDebugStderr)
1447 fprintf(stderr, "Received cgi input: %s\n", line);
1448 else
1449 printf
1450 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1451 line);
1452 }
1454 for (cp = line; *cp; cp++)
1455 if (*cp == '+')
1456 *cp = ' ';
1458 if (strlen(line)) {
1459 for (numargs = 1, cp = line; *cp; cp++)
1460 if (*cp == '&')
1461 numargs++;
1462 } else
1463 numargs = 0;
1464 if (rrdcgiDebugLevel > 0) {
1465 if (rrdcgiDebugStderr)
1466 fprintf(stderr, "%d cgi variables found.\n", numargs);
1467 else
1468 printf("%d cgi variables found.<br>\n", numargs);
1469 }
1471 len = (numargs + 1) * sizeof(s_var *);
1472 if ((result = (s_var **) malloc(len)) == NULL)
1473 return NULL;
1474 memset(result, 0, len);
1476 cp = line;
1477 i = 0;
1478 while (*cp) {
1479 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1480 *ip = '\0';
1481 } else
1482 ip = cp + strlen(cp);
1484 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1485 cp = ++ip;
1486 continue;
1487 }
1489 if (!strlen(esp)) {
1490 cp = ++ip;
1491 continue;
1492 }
1494 if (i < numargs) {
1496 /* try to find out if there's already such a variable */
1497 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1498 || !(strlen(result[k]->name) ==
1499 (size_t) (esp - cp))); k++);
1501 if (k == i) { /* No such variable yet */
1502 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1503 return NULL;
1504 if ((result[i]->name =
1505 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1506 return NULL;
1507 memset(result[i]->name, 0, esp - cp + 1);
1508 strncpy(result[i]->name, cp, esp - cp);
1509 cp = ++esp;
1510 if ((result[i]->value =
1511 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1512 return NULL;
1513 memset(result[i]->value, 0, ip - esp + 1);
1514 strncpy(result[i]->value, cp, ip - esp);
1515 result[i]->value = rrdcgiDecodeString(result[i]->value);
1516 if (rrdcgiDebugLevel) {
1517 if (rrdcgiDebugStderr)
1518 fprintf(stderr, "%s: %s\n", result[i]->name,
1519 result[i]->value);
1520 else
1521 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1522 result[i]->name, result[i]->value);
1523 }
1524 i++;
1525 } else { /* There is already such a name, suppose a mutiple field */
1526 cp = ++esp;
1527 len =
1528 (strlen(result[k]->value) + (ip - esp) +
1529 2) * sizeof(char);
1530 if ((sptr = (char *) malloc(len)) == NULL)
1531 return NULL;
1532 memset(sptr, 0, len);
1533 sprintf(sptr, "%s\n", result[k]->value);
1534 strncat(sptr, cp, ip - esp);
1535 free(result[k]->value);
1536 result[k]->value = rrdcgiDecodeString(sptr);
1537 }
1538 }
1539 cp = ++ip;
1540 }
1541 return result;
1542 }
1544 /* rrdcgiInit()
1545 *
1546 * Read from stdin if no string is provided via CGI. Variables that
1547 * doesn't have a value associated with it doesn't get stored.
1548 */
1549 s_cgi *rrdcgiInit(
1550 void)
1551 {
1552 s_cgi *res;
1553 s_var **vars;
1555 vars = rrdcgiReadVariables();
1557 if (!vars)
1558 return NULL;
1560 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1561 return NULL;
1562 res->vars = vars;
1564 return res;
1565 }
1567 char *rrdcgiGetValue(
1568 s_cgi * parms,
1569 const char *name)
1570 {
1571 int i;
1573 if (!parms || !parms->vars)
1574 return NULL;
1575 for (i = 0; parms->vars[i]; i++)
1576 if (!strcmp(name, parms->vars[i]->name)) {
1577 if (rrdcgiDebugLevel > 0) {
1578 if (rrdcgiDebugStderr)
1579 fprintf(stderr, "%s found as %s\n", name,
1580 parms->vars[i]->value);
1581 else
1582 printf("%s found as %s<br>\n", name,
1583 parms->vars[i]->value);
1584 }
1585 return parms->vars[i]->value;
1586 }
1587 if (rrdcgiDebugLevel) {
1588 if (rrdcgiDebugStderr)
1589 fprintf(stderr, "%s not found\n", name);
1590 else
1591 printf("%s not found<br>\n", name);
1592 }
1593 return NULL;
1594 }
1596 void rrdcgiFreeList(
1597 char **list)
1598 {
1599 int i;
1601 for (i = 0; list[i] != NULL; i++)
1602 free(list[i]);
1603 free(list);
1604 }
1606 void rrdcgiFree(
1607 s_cgi * parms)
1608 {
1609 int i;
1611 if (!parms)
1612 return;
1613 if (parms->vars) {
1614 for (i = 0; parms->vars[i]; i++) {
1615 if (parms->vars[i]->name)
1616 free(parms->vars[i]->name);
1617 if (parms->vars[i]->value)
1618 free(parms->vars[i]->value);
1619 free(parms->vars[i]);
1620 }
1621 free(parms->vars);
1622 }
1623 free(parms);
1625 if (rrdcgiHeaderString) {
1626 free(rrdcgiHeaderString);
1627 rrdcgiHeaderString = NULL;
1628 }
1629 if (rrdcgiType) {
1630 free(rrdcgiType);
1631 rrdcgiType = NULL;
1632 }
1633 }