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