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(void)
224 {
225 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
226 if (varheap == NULL) {
227 fprintf(stderr, "ERROR: unable to initialize variable store\n");
228 return -1;
229 }
230 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
231 varheap_size = INIT_VARSTORE_SIZE;
232 return 0;
233 }
235 /* cleanup: free allocated memory */
236 static void donevar(void)
237 {
238 int i;
240 if (varheap) {
241 for (i = 0; i < (int) varheap_size; i++) {
242 if (varheap[i].name) {
243 free((char *) varheap[i].name);
244 }
245 if (varheap[i].value) {
246 free((char *) varheap[i].value);
247 }
248 }
249 free(varheap);
250 }
251 }
253 /* Get a variable from the variable store.
254 Return NULL in case the requested variable was not found. */
255 static const char *getvar(
256 const char *name)
257 {
258 int i;
260 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
261 if (0 == strcmp(name, varheap[i].name)) {
262 #ifdef DEBUG_VARS
263 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
264 #endif
265 return varheap[i].value;
266 }
267 }
268 #ifdef DEBUG_VARS
269 printf("<!-- getvar(%s) -> Not found-->\n", name);
270 #endif
271 return NULL;
272 }
274 /* Put a variable into the variable store. If a variable by that
275 name exists, it's value is overwritten with the new value unless it was
276 marked as 'const' (initialized by RRD::SETCONSTVAR).
277 Returns a copy the newly allocated value on success, NULL on error. */
278 static const char *putvar(
279 const char *name,
280 const char *value,
281 int is_const)
282 {
283 int i;
285 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
286 if (0 == strcmp(name, varheap[i].name)) {
287 /* overwrite existing entry */
288 if (varheap[i].is_const) {
289 #ifdef DEBUG_VARS
290 printf("<!-- setver(%s, %s): not assigning: "
291 "const variable -->\n", name, value);
292 #endif
293 return varheap[i].value;
294 }
295 #ifdef DEBUG_VARS
296 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
297 name, value, varheap[i].value);
298 #endif
299 /* make it possible to promote a variable to readonly */
300 varheap[i].is_const = is_const;
301 free((char *) varheap[i].value);
302 varheap[i].value = stralloc(value);
303 return varheap[i].value;
304 }
305 }
307 /* no existing variable found by that name, add it */
308 if (i == (int) varheap_size) {
309 /* ran out of heap: resize heap to double size */
310 size_t new_size = varheap_size * 2;
312 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
313 if (!varheap) {
314 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
315 return NULL;
316 }
317 /* initialize newly allocated memory */ ;
318 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
319 varheap_size = new_size;
320 }
321 varheap[i].is_const = is_const;
322 varheap[i].name = stralloc(name);
323 varheap[i].value = stralloc(value);
325 #ifdef DEBUG_VARS
326 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
327 #endif
328 return varheap[i].value;
329 }
331 /* expand those RRD:* directives that can be used recursivly */
332 static char *rrd_expand_vars(
333 char *buffer)
334 {
335 int i;
337 #ifdef DEBUG_PARSER
338 printf("expanding variables in '%s'\n", buffer);
339 #endif
341 for (i = 0; buffer[i]; i++) {
342 if (buffer[i] != '<')
343 continue;
344 parse(&buffer, i, "<RRD::CV", cgiget);
345 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
346 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
347 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
348 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
349 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
350 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
351 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
352 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
353 }
354 return buffer;
355 }
357 static long goodfor = 0;
358 static char **calcpr = NULL;
359 static void calfree(
360 void)
361 {
362 if (calcpr) {
363 long i;
365 for (i = 0; calcpr[i]; i++) {
366 if (calcpr[i]) {
367 free(calcpr[i]);
368 }
369 }
370 if (calcpr) {
371 free(calcpr);
372 }
373 }
374 }
376 /* create freeable version of the string */
377 char *stralloc(
378 const char *str)
379 {
380 char *nstr;
382 if (!str) {
383 return NULL;
384 }
385 nstr = malloc((strlen(str) + 1));
386 strcpy(nstr, str);
387 return (nstr);
388 }
390 int main(
391 int argc,
392 char *argv[])
393 {
394 long length;
395 char *buffer;
396 char *server_url = NULL;
397 long i;
398 long filter = 0;
399 struct option long_options[] = {
400 {"filter", no_argument, 0, 'f'},
401 {0, 0, 0, 0}
402 };
404 #ifdef MUST_DISABLE_SIGFPE
405 signal(SIGFPE, SIG_IGN);
406 #endif
407 #ifdef MUST_DISABLE_FPMASK
408 fpsetmask(0);
409 #endif
410 optind = 0;
411 opterr = 0; /* initialize getopt */
413 /* what do we get for cmdline arguments?
414 for (i=0;i<argc;i++)
415 printf("%d-'%s'\n",i,argv[i]); */
416 while (1) {
417 int option_index = 0;
418 int opt;
420 opt = getopt_long(argc, argv, "f", long_options, &option_index);
421 if (opt == EOF) {
422 break;
423 }
425 switch (opt) {
426 case 'f':
427 filter = 1;
428 break;
429 case '?':
430 printf("unknown commandline option '%s'\n", argv[optind - 1]);
431 return -1;
432 }
433 }
435 if (!filter) {
436 rrdcgiDebug(0, 0);
437 rrdcgiArg = rrdcgiInit();
438 server_url = getenv("SERVER_URL");
439 }
441 /* make sure we have one extra argument,
442 if there are others, we do not care Apache gives several */
444 /* if ( (optind != argc-2
445 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
446 && optind != argc-1) { */
448 if (optind >= argc) {
449 fprintf(stderr, "ERROR: expected a filename\n");
450 exit(1);
451 } else {
452 length = readfile(argv[optind], &buffer, 1);
453 }
455 if (rrd_test_error()) {
456 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
457 exit(1);
458 }
460 /* initialize variable heap */
461 initvar();
463 #ifdef DEBUG_PARSER
464 /* some fake header for testing */
465 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
466 #endif
469 /* expand rrd directives in buffer recursivly */
470 for (i = 0; buffer[i]; i++) {
471 if (buffer[i] != '<')
472 continue;
473 if (!filter) {
474 parse(&buffer, i, "<RRD::CV", cgiget);
475 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
476 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
477 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
478 }
479 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
480 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
481 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
482 parse(&buffer, i, "<RRD::INCLUDE", includefile);
483 parse(&buffer, i, "<RRD::PRINT", drawprint);
484 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
485 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
486 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
487 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
488 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
489 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
490 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
491 }
493 if (!filter) {
494 printf("Content-Type: text/html\n"
495 "Content-Length: %zd\n", strlen(buffer));
497 if (labs(goodfor) > 0) {
498 time_t now;
500 now = time(NULL);
501 printf("Last-Modified: %s\n", http_time(&now));
502 now += labs(goodfor);
503 printf("Expires: %s\n", http_time(&now));
504 if (goodfor < 0) {
505 printf("Refresh: %ld\n", labs(goodfor));
506 }
507 }
508 printf("\n");
509 }
511 /* output result */
512 printf("%s", buffer);
514 /* cleanup */
515 calfree();
516 if (buffer) {
517 free(buffer);
518 }
519 donevar();
520 exit(0);
521 }
523 /* remove occurrences of .. this is a general measure to make
524 paths which came in via cgi do not go UP ... */
526 char *rrdsetenv(
527 long argc,
528 const char **args)
529 {
530 if (argc >= 2) {
531 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
533 if (xyz == NULL) {
534 return stralloc("[ERROR: allocating setenv buffer]");
535 };
536 sprintf(xyz, "%s=%s", args[0], args[1]);
537 if (putenv(xyz) == -1) {
538 free(xyz);
539 return stralloc("[ERROR: failed to do putenv]");
540 };
541 return stralloc("");
542 }
543 return stralloc("[ERROR: setenv failed because not enough "
544 "arguments were defined]");
545 }
547 /* rrd interface to the variable function putvar() */
548 char *rrdsetvar(
549 long argc,
550 const char **args)
551 {
552 if (argc >= 2) {
553 const char *result = putvar(args[0], args[1], 0 /* not const */ );
555 if (result) {
556 /* setvar does not return the value set */
557 return stralloc("");
558 }
559 return stralloc("[ERROR: putvar failed]");
560 }
561 return stralloc("[ERROR: putvar failed because not enough arguments "
562 "were defined]");
563 }
565 /* rrd interface to the variable function putvar() */
566 char *rrdsetvarconst(
567 long argc,
568 const char **args)
569 {
570 if (argc >= 2) {
571 const char *result = putvar(args[0], args[1], 1 /* const */ );
573 if (result) {
574 /* setvar does not return the value set */
575 return stralloc("");
576 }
577 return stralloc("[ERROR: putvar failed]");
578 }
579 return stralloc("[ERROR: putvar failed because not enough arguments "
580 "were defined]");
581 }
583 char *rrdgetenv(
584 long argc,
585 const char **args)
586 {
587 char buf[128];
588 const char *envvar;
590 if (argc != 1) {
591 return stralloc("[ERROR: getenv failed because it did not "
592 "get 1 argument only]");
593 };
594 envvar = getenv(args[0]);
595 if (envvar) {
596 return stralloc(envvar);
597 } else {
598 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
599 return stralloc(buf);
600 }
601 }
603 char *rrdgetvar(
604 long argc,
605 const char **args)
606 {
607 char buf[128];
608 const char *value;
610 if (argc != 1) {
611 return stralloc("[ERROR: getvar failed because it did not "
612 "get 1 argument only]");
613 };
614 value = getvar(args[0]);
615 if (value) {
616 return stralloc(value);
617 } else {
618 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
619 return stralloc(buf);
620 }
621 }
623 char *rrdgoodfor(
624 long argc,
625 const char **args)
626 {
627 if (argc == 1) {
628 goodfor = atol(args[0]);
629 } else {
630 return stralloc("[ERROR: goodfor expected 1 argument]");
631 }
633 if (goodfor == 0) {
634 return stralloc("[ERROR: goodfor value must not be 0]");
635 }
637 return stralloc("");
638 }
640 char *rrdgetinternal(
641 long argc,
642 const char **args)
643 {
644 if (argc == 1) {
645 if (strcasecmp(args[0], "VERSION") == 0) {
646 return stralloc(PACKAGE_VERSION);
647 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
648 return stralloc(__DATE__ " " __TIME__);
649 } else {
650 return stralloc("[ERROR: internal unknown argument]");
651 }
652 } else {
653 return stralloc("[ERROR: internal expected 1 argument]");
654 }
655 }
657 /* Format start or end times using strftime. We always need both the
658 * start and end times, because, either might be relative to the other.
659 * */
660 #define MAX_STRFTIME_SIZE 256
661 char *printstrftime(
662 long argc,
663 const char **args)
664 {
665 struct rrd_time_value start_tv, end_tv;
666 char *parsetime_error = NULL;
667 char formatted[MAX_STRFTIME_SIZE];
668 struct tm *the_tm;
669 time_t start_tmp, end_tmp;
671 /* Make sure that we were given the right number of args */
672 if (argc != 4) {
673 rrd_set_error("wrong number of args %d", argc);
674 return stralloc("");
675 }
677 /* Init start and end time */
678 parsetime("end-24h", &start_tv);
679 parsetime("now", &end_tv);
681 /* Parse the start and end times we were given */
682 if ((parsetime_error = parsetime(args[1], &start_tv))) {
683 rrd_set_error("start time: %s", parsetime_error);
684 return stralloc("");
685 }
686 if ((parsetime_error = parsetime(args[2], &end_tv))) {
687 rrd_set_error("end time: %s", parsetime_error);
688 return stralloc("");
689 }
690 if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
691 return stralloc("");
692 }
694 /* Do we do the start or end */
695 if (strcasecmp(args[0], "START") == 0) {
696 the_tm = localtime(&start_tmp);
697 } else if (strcasecmp(args[0], "END") == 0) {
698 the_tm = localtime(&end_tmp);
699 } else {
700 rrd_set_error("start/end not found in '%s'", args[0]);
701 return stralloc("");
702 }
704 /* now format it */
705 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
706 return (stralloc(formatted));
707 } else {
708 rrd_set_error("strftime failed");
709 return stralloc("");
710 }
711 }
713 char *includefile(
714 long argc,
715 const char **args)
716 {
717 char *buffer;
719 if (argc >= 1) {
720 const char *filename = args[0];
722 readfile(filename, &buffer, 0);
723 if (rrd_test_error()) {
724 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
726 sprintf(err, "[ERROR: %s]", rrd_get_error());
727 rrd_clear_error();
728 return err;
729 } else {
730 return buffer;
731 }
732 } else {
733 return stralloc("[ERROR: No Inclue file defined]");
734 }
735 }
737 /* make a copy of buf and replace open/close brackets with '_' */
738 char *rrdstrip(
739 char *buf)
740 {
741 char *p;
743 if (buf == NULL) {
744 return NULL;
745 }
746 /* make a copy of the buffer */
747 buf = stralloc(buf);
748 if (buf == NULL) {
749 return NULL;
750 }
752 p = buf;
753 while (*p) {
754 if (*p == '<' || *p == '>') {
755 *p = '_';
756 }
757 p++;
758 }
759 return buf;
760 }
762 char *cgigetq(
763 long argc,
764 const char **args)
765 {
766 if (argc >= 1) {
767 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
768 char *buf2;
769 char *c, *d;
770 int qc = 0;
772 if (buf == NULL)
773 return NULL;
775 for (c = buf; *c != '\0'; c++)
776 if (*c == '"')
777 qc++;
778 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
779 perror("Malloc Buffer");
780 exit(1);
781 };
782 c = buf;
783 d = buf2;
784 *(d++) = '"';
785 while (*c != '\0') {
786 if (*c == '"') {
787 *(d++) = '"';
788 *(d++) = '\'';
789 *(d++) = '"';
790 *(d++) = '\'';
791 }
792 *(d++) = *(c++);
793 }
794 *(d++) = '"';
795 *(d) = '\0';
796 free(buf);
797 return buf2;
798 }
800 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
801 }
803 /* remove occurrences of .. this is a general measure to make
804 paths which came in via cgi do not go UP ... */
806 char *cgigetqp(
807 long argc,
808 const char **args)
809 {
810 char *buf;
811 char *buf2;
812 char *p;
813 char *d;
815 if (argc < 1) {
816 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
817 }
819 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
820 if (!buf) {
821 return NULL;
822 }
824 buf2 = malloc(strlen(buf) + 1);
825 if (!buf2) {
826 perror("cgigetqp(): Malloc Path Buffer");
827 exit(1);
828 };
830 p = buf;
831 d = buf2;
833 while (*p) {
834 /* prevent mallicious paths from entering the system */
835 if (p[0] == '.' && p[1] == '.') {
836 p += 2;
837 *d++ = '_';
838 *d++ = '_';
839 } else {
840 *d++ = *p++;
841 }
842 }
844 *d = 0;
845 free(buf);
847 /* Make sure the path is relative, e.g. does not start with '/' */
848 p = buf2;
849 while ('/' == *p) {
850 *p++ = '_';
851 }
853 return buf2;
854 }
857 char *cgiget(
858 long argc,
859 const char **args)
860 {
861 if (argc >= 1)
862 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
863 else
864 return stralloc("[ERROR: not enough arguments for RRD::CV]");
865 }
869 char *drawgraph(
870 long argc,
871 const char **args)
872 {
873 int i, xsize, ysize;
874 double ymin, ymax;
876 for (i = 0; i < argc; i++)
877 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
878 break;
879 if (i == argc) {
880 args[argc++] = "--imginfo";
881 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
882 }
883 calfree();
884 if (rrd_graph
885 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
886 &ymax) != -1) {
887 return stralloc(calcpr[0]);
888 } else {
889 if (rrd_test_error()) {
890 char *err =
891 malloc((strlen(rrd_get_error()) +
892 DS_NAM_SIZE) * sizeof(char));
893 sprintf(err, "[ERROR: %s]", rrd_get_error());
894 rrd_clear_error();
895 calfree();
896 return err;
897 }
898 }
899 return NULL;
900 }
902 char *drawprint(
903 long argc,
904 const char **args)
905 {
906 if (argc == 1 && calcpr) {
907 long i = 0;
909 while (calcpr[i] != NULL)
910 i++; /*determine number lines in calcpr */
911 if (atol(args[0]) < i - 1)
912 return stralloc(calcpr[atol(args[0]) + 1]);
913 }
914 return stralloc("[ERROR: RRD::PRINT argument error]");
915 }
917 char *printtimelast(
918 long argc,
919 const char **args)
920 {
921 time_t last;
922 struct tm tm_last;
923 char *buf;
925 if (argc == 2) {
926 buf = malloc(255);
927 if (buf == NULL) {
928 return stralloc("[ERROR: allocating strftime buffer]");
929 };
930 last = rrd_last(argc + 1, (char **) args - 1);
931 if (rrd_test_error()) {
932 char *err =
933 malloc((strlen(rrd_get_error()) +
934 DS_NAM_SIZE) * sizeof(char));
935 sprintf(err, "[ERROR: %s]", rrd_get_error());
936 rrd_clear_error();
937 return err;
938 }
939 tm_last = *localtime(&last);
940 strftime(buf, 254, args[1], &tm_last);
941 return buf;
942 }
943 if (argc < 2) {
944 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
945 }
946 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
947 }
949 char *printtimenow(
950 long argc,
951 const char **args)
952 {
953 time_t now = time(NULL);
954 struct tm tm_now;
955 char *buf;
957 if (argc == 1) {
958 buf = malloc(255);
959 if (buf == NULL) {
960 return stralloc("[ERROR: allocating strftime buffer]");
961 };
962 tm_now = *localtime(&now);
963 strftime(buf, 254, args[0], &tm_now);
964 return buf;
965 }
966 if (argc < 1) {
967 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
968 }
969 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
970 }
972 /* Scan buffer until an unescaped '>' arives.
973 * Update argument array with arguments found.
974 * Return end cursor where parsing stopped, or NULL in case of failure.
975 *
976 * FIXME:
977 * To allow nested constructs, we call rrd_expand_vars() for arguments
978 * that contain RRD::x directives. These introduce a small memory leak
979 * since we have to stralloc the arguments the way parse() works.
980 */
981 char *scanargs(
982 char *line,
983 int *argument_count,
984 char ***arguments)
985 {
986 char *getP; /* read cursor */
987 char *putP; /* write cursor */
988 char Quote; /* type of quote if in quoted string, 0 otherwise */
989 int tagcount; /* open tag count */
990 int in_arg; /* if we currently are parsing an argument or not */
991 int argsz; /* argument array size */
992 int curarg_contains_rrd_directives;
994 /* local array of arguments while parsing */
995 int argc = 0;
996 char **argv;
998 #ifdef DEBUG_PARSER
999 printf("<-- scanargs(%s) -->\n", line);
1000 #endif
1002 *arguments = NULL;
1003 *argument_count = 0;
1005 /* create initial argument array of char pointers */
1006 argsz = 32;
1007 argv = (char **) malloc(argsz * sizeof(char *));
1008 if (!argv) {
1009 return NULL;
1010 }
1012 /* skip leading blanks */
1013 while (isspace((int) *line)) {
1014 line++;
1015 }
1017 getP = line;
1018 putP = line;
1020 Quote = 0;
1021 in_arg = 0;
1022 tagcount = 0;
1024 curarg_contains_rrd_directives = 0;
1026 /* start parsing 'line' for arguments */
1027 while (*getP) {
1028 unsigned char c = *getP++;
1030 if (c == '>' && !Quote && !tagcount) {
1031 /* this is our closing tag, quit scanning */
1032 break;
1033 }
1035 /* remove all special chars */
1036 if (c < ' ') {
1037 c = ' ';
1038 }
1040 switch (c) {
1041 case ' ':
1042 if (Quote || tagcount) {
1043 /* copy quoted/tagged (=RRD expanded) string */
1044 *putP++ = c;
1045 } else if (in_arg) {
1046 /* end argument string */
1047 *putP++ = 0;
1048 in_arg = 0;
1049 if (curarg_contains_rrd_directives) {
1050 argv[argc - 1] =
1051 rrd_expand_vars(stralloc(argv[argc - 1]));
1052 curarg_contains_rrd_directives = 0;
1053 }
1054 }
1055 break;
1057 case '"': /* Fall through */
1058 case '\'':
1059 if (Quote != 0) {
1060 if (Quote == c) {
1061 Quote = 0;
1062 } else {
1063 /* copy quoted string */
1064 *putP++ = c;
1065 }
1066 } else {
1067 if (!in_arg) {
1068 /* reference start of argument string in argument array */
1069 argv[argc++] = putP;
1070 in_arg = 1;
1071 }
1072 Quote = c;
1073 }
1074 break;
1076 default:
1077 if (!in_arg) {
1078 /* start new argument */
1079 argv[argc++] = putP;
1080 in_arg = 1;
1081 }
1082 if (c == '>') {
1083 if (tagcount) {
1084 tagcount--;
1085 }
1086 }
1087 if (c == '<') {
1088 tagcount++;
1089 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1090 curarg_contains_rrd_directives = 1;
1091 }
1092 }
1093 *putP++ = c;
1094 break;
1095 }
1097 /* check if our argument array is still large enough */
1098 if (argc == argsz) {
1099 /* resize argument array */
1100 argsz *= 2;
1101 argv = rrd_realloc(argv, argsz * sizeof(char *));
1102 if (*argv == NULL) {
1103 return NULL;
1104 }
1105 }
1106 }
1108 /* terminate last argument found */
1109 *putP = '\0';
1110 if (curarg_contains_rrd_directives) {
1111 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1112 }
1113 #ifdef DEBUG_PARSER
1114 if (argc > 0) {
1115 int n;
1117 printf("<-- arguments found [%d]\n", argc);
1118 for (n = 0; n < argc; n++) {
1119 printf("arg %02d: '%s'\n", n, argv[n]);
1120 }
1121 printf("-->\n");
1122 } else {
1123 printf("<!-- No arguments found -->\n");
1124 }
1125 #endif
1127 /* update caller's notion of the argument array and it's size */
1128 *arguments = argv;
1129 *argument_count = argc;
1131 if (Quote) {
1132 return NULL;
1133 }
1135 /* Return new scanning cursor:
1136 pointer to char after closing bracket */
1137 return getP;
1138 }
1141 /*
1142 * Parse(): scan current portion of buffer for given tag.
1143 * If found, parse tag arguments and call 'func' for it.
1144 * The result of func is inserted at the current position
1145 * in the buffer.
1146 */
1147 int parse(
1148 char **buf, /* buffer */
1149 long i, /* offset in buffer */
1150 char *tag, /* tag to handle */
1151 char * (*func) (long,
1152 const char **) /* function to call for 'tag' */
1153 )
1154 {
1155 /* the name of the vairable ... */
1156 char *val;
1157 long valln;
1158 char **args;
1159 char *end;
1160 long end_offset;
1161 int argc;
1162 size_t taglen = strlen(tag);
1164 /* Current position in buffer should start with 'tag' */
1165 if (strncmp((*buf) + i, tag, taglen) != 0) {
1166 return 0;
1167 }
1168 /* .. and match exactly (a whitespace following 'tag') */
1169 if (!isspace(*((*buf) + i + taglen))) {
1170 return 0;
1171 }
1172 #ifdef DEBUG_PARSER
1173 printf("parse(): handling tag '%s'\n", tag);
1174 #endif
1176 /* Scan for arguments following the tag;
1177 scanargs() puts \0 into *buf ... so after scanargs it is probably
1178 not a good time to use strlen on buf */
1179 end = scanargs((*buf) + i + taglen, &argc, &args);
1180 if (end) {
1181 /* got arguments, call function for 'tag' with arguments */
1182 val = func(argc, (const char **) args);
1183 free(args);
1184 } else {
1185 /* unable to parse arguments, undo 0-termination by scanargs */
1186 for (; argc > 0; argc--) {
1187 *((args[argc - 1]) - 1) = ' ';
1188 }
1190 /* next call, try parsing at current offset +1 */
1191 end = (*buf) + i + 1;
1193 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1194 " Check original file. This may have been altered "
1195 "by parsing.]\n\n");
1196 }
1198 /* remember offset where we have to continue parsing */
1199 end_offset = end - (*buf);
1201 valln = 0;
1202 if (val) {
1203 valln = strlen(val);
1204 }
1206 /* Optionally resize buffer to hold the replacement value:
1207 Calculating the new length of the buffer is simple. add current
1208 buffer pos (i) to length of string after replaced tag to length
1209 of replacement string and add 1 for the final zero ... */
1210 if (end - (*buf) < (i + valln)) {
1211 /* make sure we do not shrink the mallocd block */
1212 size_t newbufsize = i + strlen(end) + valln + 1;
1214 *buf = rrd_realloc(*buf, newbufsize);
1216 if (*buf == NULL) {
1217 perror("Realoc buf:");
1218 exit(1);
1219 };
1220 }
1222 /* Update new end pointer:
1223 make sure the 'end' pointer gets moved along with the
1224 buf pointer when realloc moves memory ... */
1225 end = (*buf) + end_offset;
1227 /* splice the variable:
1228 step 1. Shift pending data to make room for 'val' */
1229 memmove((*buf) + i + valln, end, strlen(end) + 1);
1231 /* step 2. Insert val */
1232 if (val) {
1233 memmove((*buf) + i, val, valln);
1234 free(val);
1235 }
1236 return (valln > 0 ? valln - 1 : valln);
1237 }
1239 char *http_time(
1240 time_t *now)
1241 {
1242 struct tm *tmptime;
1243 static char buf[60];
1245 tmptime = gmtime(now);
1246 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1247 return (buf);
1248 }
1250 void rrdcgiHeader(
1251 void)
1252 {
1253 if (rrdcgiType)
1254 printf("Content-type: %s\n", rrdcgiType);
1255 else
1256 printf("Content-type: text/html\n");
1257 if (rrdcgiHeaderString)
1258 printf("%s", rrdcgiHeaderString);
1259 printf("\n");
1260 }
1262 void rrdcgiDebug(
1263 int level,
1264 int where)
1265 {
1266 if (level > 0)
1267 rrdcgiDebugLevel = level;
1268 else
1269 rrdcgiDebugLevel = 0;
1270 if (where)
1271 rrdcgiDebugStderr = 0;
1272 else
1273 rrdcgiDebugStderr = 1;
1274 }
1276 char *rrdcgiDecodeString(
1277 char *text)
1278 {
1279 char *cp, *xp;
1281 for (cp = text, xp = text; *cp; cp++) {
1282 if (*cp == '%') {
1283 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1284 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1285 if (islower(*(cp + 1)))
1286 *(cp + 1) = toupper(*(cp + 1));
1287 if (islower(*(cp + 2)))
1288 *(cp + 2) = toupper(*(cp + 2));
1289 *(xp) =
1290 (*(cp + 1) >=
1291 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1292 (*(cp + 2) >=
1293 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1294 xp++;
1295 cp += 2;
1296 }
1297 } else {
1298 *(xp++) = *cp;
1299 }
1300 }
1301 memset(xp, 0, cp - xp);
1302 return text;
1303 }
1305 /* rrdcgiReadVariables()
1306 *
1307 * Read from stdin if no string is provided via CGI. Variables that
1308 * doesn't have a value associated with it doesn't get stored.
1309 */
1310 s_var **rrdcgiReadVariables(
1311 void)
1312 {
1313 int length;
1314 char *line = NULL;
1315 int numargs;
1316 char *cp, *ip, *esp, *sptr;
1317 s_var **result;
1318 int i, k, len;
1319 char tmp[101];
1321 cp = getenv("REQUEST_METHOD");
1322 ip = getenv("CONTENT_LENGTH");
1324 if (cp && !strcmp(cp, "POST")) {
1325 if (ip) {
1326 length = atoi(ip);
1327 if ((line = (char *) malloc(length + 2)) == NULL)
1328 return NULL;
1329 fgets(line, length + 1, stdin);
1330 } else
1331 return NULL;
1332 } else if (cp && !strcmp(cp, "GET")) {
1333 esp = getenv("QUERY_STRING");
1334 if (esp && strlen(esp)) {
1335 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1336 return NULL;
1337 sprintf(line, "%s", esp);
1338 } else
1339 return NULL;
1340 } else {
1341 length = 0;
1342 printf("(offline mode: enter name=value pairs on standard input)\n");
1343 memset(tmp, 0, sizeof(tmp));
1344 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1345 if (strlen(tmp)) {
1346 if (tmp[strlen(tmp) - 1] == '\n')
1347 tmp[strlen(tmp) - 1] = '&';
1348 if (length) {
1349 length += strlen(tmp);
1350 len = (length + 1) * sizeof(char);
1351 if ((line = (char *) realloc(line, len)) == NULL)
1352 return NULL;
1353 strcat(line, tmp);
1354 } else {
1355 length = strlen(tmp);
1356 len = (length + 1) * sizeof(char);
1357 if ((line = (char *) malloc(len)) == NULL)
1358 return NULL;
1359 memset(line, 0, len);
1360 strcpy(line, tmp);
1361 }
1362 }
1363 memset(tmp, 0, sizeof(tmp));
1364 }
1365 if (!line)
1366 return NULL;
1367 if (line[strlen(line) - 1] == '&')
1368 line[strlen(line) - 1] = '\0';
1369 }
1371 /*
1372 * From now on all cgi variables are stored in the variable line
1373 * and look like foo=bar&foobar=barfoo&foofoo=
1374 */
1376 if (rrdcgiDebugLevel > 0) {
1377 if (rrdcgiDebugStderr)
1378 fprintf(stderr, "Received cgi input: %s\n", line);
1379 else
1380 printf
1381 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1382 line);
1383 }
1385 for (cp = line; *cp; cp++)
1386 if (*cp == '+')
1387 *cp = ' ';
1389 if (strlen(line)) {
1390 for (numargs = 1, cp = line; *cp; cp++)
1391 if (*cp == '&')
1392 numargs++;
1393 } else
1394 numargs = 0;
1395 if (rrdcgiDebugLevel > 0) {
1396 if (rrdcgiDebugStderr)
1397 fprintf(stderr, "%d cgi variables found.\n", numargs);
1398 else
1399 printf("%d cgi variables found.<br>\n", numargs);
1400 }
1402 len = (numargs + 1) * sizeof(s_var *);
1403 if ((result = (s_var **) malloc(len)) == NULL)
1404 return NULL;
1405 memset(result, 0, len);
1407 cp = line;
1408 i = 0;
1409 while (*cp) {
1410 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1411 *ip = '\0';
1412 } else
1413 ip = cp + strlen(cp);
1415 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1416 cp = ++ip;
1417 continue;
1418 }
1420 if (!strlen(esp)) {
1421 cp = ++ip;
1422 continue;
1423 }
1425 if (i < numargs) {
1427 /* try to find out if there's already such a variable */
1428 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1429 || !(strlen(result[k]->name) == esp - cp));
1430 k++);
1432 if (k == i) { /* No such variable yet */
1433 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1434 return NULL;
1435 if ((result[i]->name =
1436 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1437 return NULL;
1438 memset(result[i]->name, 0, esp - cp + 1);
1439 strncpy(result[i]->name, cp, esp - cp);
1440 cp = ++esp;
1441 if ((result[i]->value =
1442 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1443 return NULL;
1444 memset(result[i]->value, 0, ip - esp + 1);
1445 strncpy(result[i]->value, cp, ip - esp);
1446 result[i]->value = rrdcgiDecodeString(result[i]->value);
1447 if (rrdcgiDebugLevel) {
1448 if (rrdcgiDebugStderr)
1449 fprintf(stderr, "%s: %s\n", result[i]->name,
1450 result[i]->value);
1451 else
1452 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1453 result[i]->name, result[i]->value);
1454 }
1455 i++;
1456 } else { /* There is already such a name, suppose a mutiple field */
1457 cp = ++esp;
1458 len =
1459 (strlen(result[k]->value) + (ip - esp) +
1460 2) * sizeof(char);
1461 if ((sptr = (char *) malloc(len)) == NULL)
1462 return NULL;
1463 memset(sptr, 0, len);
1464 sprintf(sptr, "%s\n", result[k]->value);
1465 strncat(sptr, cp, ip - esp);
1466 free(result[k]->value);
1467 result[k]->value = rrdcgiDecodeString(sptr);
1468 }
1469 }
1470 cp = ++ip;
1471 }
1472 return result;
1473 }
1475 /* rrdcgiInit()
1476 *
1477 * Read from stdin if no string is provided via CGI. Variables that
1478 * doesn't have a value associated with it doesn't get stored.
1479 */
1480 s_cgi *rrdcgiInit(
1481 void)
1482 {
1483 s_cgi *res;
1484 s_var **vars;
1486 vars = rrdcgiReadVariables();
1488 if (!vars)
1489 return NULL;
1491 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1492 return NULL;
1493 res->vars = vars;
1495 return res;
1496 }
1498 char *rrdcgiGetValue(
1499 s_cgi * parms,
1500 const char *name)
1501 {
1502 int i;
1504 if (!parms || !parms->vars)
1505 return NULL;
1506 for (i = 0; parms->vars[i]; i++)
1507 if (!strcmp(name, parms->vars[i]->name)) {
1508 if (rrdcgiDebugLevel > 0) {
1509 if (rrdcgiDebugStderr)
1510 fprintf(stderr, "%s found as %s\n", name,
1511 parms->vars[i]->value);
1512 else
1513 printf("%s found as %s<br>\n", name,
1514 parms->vars[i]->value);
1515 }
1516 return parms->vars[i]->value;
1517 }
1518 if (rrdcgiDebugLevel) {
1519 if (rrdcgiDebugStderr)
1520 fprintf(stderr, "%s not found\n", name);
1521 else
1522 printf("%s not found<br>\n", name);
1523 }
1524 return NULL;
1525 }
1527 void rrdcgiFreeList(
1528 char **list)
1529 {
1530 int i;
1532 for (i = 0; list[i] != NULL; i++)
1533 free(list[i]);
1534 free(list);
1535 }
1537 void rrdcgiFree(
1538 s_cgi * parms)
1539 {
1540 int i;
1542 if (!parms)
1543 return;
1544 if (parms->vars) {
1545 for (i = 0; parms->vars[i]; i++) {
1546 if (parms->vars[i]->name)
1547 free(parms->vars[i]->name);
1548 if (parms->vars[i]->value)
1549 free(parms->vars[i]->value);
1550 free(parms->vars[i]);
1551 }
1552 free(parms->vars);
1553 }
1554 free(parms);
1556 if (rrdcgiHeaderString) {
1557 free(rrdcgiHeaderString);
1558 rrdcgiHeaderString = NULL;
1559 }
1560 if (rrdcgiType) {
1561 free(rrdcgiType);
1562 rrdcgiType = NULL;
1563 }
1564 }