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