d4e5723c99623032f19eba7def318ac98f8e4186
1 /*****************************************************************************
2 * RRDtool 1.3.0 Copyright by Tobi Oetiker, 1997-2008
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 static int readfile(
395 const char *file_name,
396 char **buffer,
397 int skipfirst)
398 {
399 long writecnt = 0, totalcnt = MEMBLK;
400 long offset = 0;
401 FILE *input = NULL;
402 char c;
404 if ((strcmp("-", file_name) == 0)) {
405 input = stdin;
406 } else {
407 if ((input = fopen(file_name, "rb")) == NULL) {
408 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
409 return (-1);
410 }
411 }
412 if (skipfirst) {
413 do {
414 c = getc(input);
415 offset++;
416 } while (c != '\n' && !feof(input));
417 }
418 if (strcmp("-", file_name)) {
419 fseek(input, 0, SEEK_END);
420 /* have extra space for detecting EOF without realloc */
421 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
422 if (totalcnt < MEMBLK)
423 totalcnt = MEMBLK; /* sanitize */
424 fseek(input, offset * sizeof(char), SEEK_SET);
425 }
426 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
427 perror("Allocate Buffer:");
428 exit(1);
429 };
430 do {
431 writecnt +=
432 fread((*buffer) + writecnt, 1,
433 (totalcnt - writecnt) * sizeof(char), input);
434 if (writecnt >= totalcnt) {
435 totalcnt += MEMBLK;
436 if (((*buffer) =
437 rrd_realloc((*buffer),
438 (totalcnt + 4) * sizeof(char))) == NULL) {
439 perror("Realloc Buffer:");
440 exit(1);
441 };
442 }
443 } while (!feof(input));
444 (*buffer)[writecnt] = '\0';
445 if (strcmp("-", file_name) != 0) {
446 fclose(input);
447 };
448 return writecnt;
449 }
451 int main(
452 int argc,
453 char *argv[])
454 {
455 long length;
456 char *buffer;
457 char *server_url = NULL;
458 long i;
459 long filter = 0;
460 struct option long_options[] = {
461 {"filter", no_argument, 0, 'f'},
462 {0, 0, 0, 0}
463 };
465 #ifdef MUST_DISABLE_SIGFPE
466 signal(SIGFPE, SIG_IGN);
467 #endif
468 #ifdef MUST_DISABLE_FPMASK
469 fpsetmask(0);
470 #endif
471 optind = 0;
472 opterr = 0; /* initialize getopt */
474 /* what do we get for cmdline arguments?
475 for (i=0;i<argc;i++)
476 printf("%d-'%s'\n",i,argv[i]); */
477 while (1) {
478 int option_index = 0;
479 int opt;
481 opt = getopt_long(argc, argv, "f", long_options, &option_index);
482 if (opt == EOF) {
483 break;
484 }
486 switch (opt) {
487 case 'f':
488 filter = 1;
489 break;
490 case '?':
491 printf("unknown commandline option '%s'\n", argv[optind - 1]);
492 return -1;
493 }
494 }
496 if (!filter) {
497 rrdcgiDebug(0, 0);
498 rrdcgiArg = rrdcgiInit();
499 server_url = getenv("SERVER_URL");
500 }
502 /* make sure we have one extra argument,
503 if there are others, we do not care Apache gives several */
505 /* if ( (optind != argc-2
506 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
507 && optind != argc-1) { */
509 if (optind >= argc) {
510 fprintf(stderr, "ERROR: expected a filename\n");
511 exit(1);
512 } else {
513 length = readfile(argv[optind], &buffer, 1);
514 }
516 if (rrd_test_error()) {
517 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
518 exit(1);
519 }
521 /* initialize variable heap */
522 initvar();
524 #ifdef DEBUG_PARSER
525 /* some fake header for testing */
526 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
527 #endif
530 /* expand rrd directives in buffer recursivly */
531 for (i = 0; buffer[i]; i++) {
532 if (buffer[i] != '<')
533 continue;
534 if (!filter) {
535 parse(&buffer, i, "<RRD::CV", cgiget);
536 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
537 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
538 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
539 }
540 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
541 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
542 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
543 parse(&buffer, i, "<RRD::INCLUDE", includefile);
544 parse(&buffer, i, "<RRD::PRINT", drawprint);
545 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
546 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
547 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
548 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
549 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
550 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
551 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
552 }
554 if (!filter) {
555 printf("Content-Type: text/html\n"
556 "Content-Length: %zd\n", strlen(buffer));
558 if (labs(goodfor) > 0) {
559 time_t now;
561 now = time(NULL);
562 printf("Last-Modified: %s\n", http_time(&now));
563 now += labs(goodfor);
564 printf("Expires: %s\n", http_time(&now));
565 if (goodfor < 0) {
566 printf("Refresh: %ld\n", labs(goodfor));
567 }
568 }
569 printf("\n");
570 }
572 /* output result */
573 printf("%s", buffer);
575 /* cleanup */
576 calfree();
577 if (buffer) {
578 free(buffer);
579 }
580 donevar();
581 exit(0);
582 }
584 /* remove occurrences of .. this is a general measure to make
585 paths which came in via cgi do not go UP ... */
587 char *rrdsetenv(
588 long argc,
589 const char **args)
590 {
591 if (argc >= 2) {
592 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
594 if (xyz == NULL) {
595 return stralloc("[ERROR: allocating setenv buffer]");
596 };
597 sprintf(xyz, "%s=%s", args[0], args[1]);
598 if (putenv(xyz) == -1) {
599 free(xyz);
600 return stralloc("[ERROR: failed to do putenv]");
601 };
602 return stralloc("");
603 }
604 return stralloc("[ERROR: setenv failed because not enough "
605 "arguments were defined]");
606 }
608 /* rrd interface to the variable function putvar() */
609 char *rrdsetvar(
610 long argc,
611 const char **args)
612 {
613 if (argc >= 2) {
614 const char *result = putvar(args[0], args[1], 0 /* not const */ );
616 if (result) {
617 /* setvar does not return the value set */
618 return stralloc("");
619 }
620 return stralloc("[ERROR: putvar failed]");
621 }
622 return stralloc("[ERROR: putvar failed because not enough arguments "
623 "were defined]");
624 }
626 /* rrd interface to the variable function putvar() */
627 char *rrdsetvarconst(
628 long argc,
629 const char **args)
630 {
631 if (argc >= 2) {
632 const char *result = putvar(args[0], args[1], 1 /* const */ );
634 if (result) {
635 /* setvar does not return the value set */
636 return stralloc("");
637 }
638 return stralloc("[ERROR: putvar failed]");
639 }
640 return stralloc("[ERROR: putvar failed because not enough arguments "
641 "were defined]");
642 }
644 char *rrdgetenv(
645 long argc,
646 const char **args)
647 {
648 char buf[128];
649 const char *envvar;
651 if (argc != 1) {
652 return stralloc("[ERROR: getenv failed because it did not "
653 "get 1 argument only]");
654 };
655 envvar = getenv(args[0]);
656 if (envvar) {
657 return stralloc(envvar);
658 } else {
659 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
660 return stralloc(buf);
661 }
662 }
664 char *rrdgetvar(
665 long argc,
666 const char **args)
667 {
668 char buf[128];
669 const char *value;
671 if (argc != 1) {
672 return stralloc("[ERROR: getvar failed because it did not "
673 "get 1 argument only]");
674 };
675 value = getvar(args[0]);
676 if (value) {
677 return stralloc(value);
678 } else {
679 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
680 return stralloc(buf);
681 }
682 }
684 char *rrdgoodfor(
685 long argc,
686 const char **args)
687 {
688 if (argc == 1) {
689 goodfor = atol(args[0]);
690 } else {
691 return stralloc("[ERROR: goodfor expected 1 argument]");
692 }
694 if (goodfor == 0) {
695 return stralloc("[ERROR: goodfor value must not be 0]");
696 }
698 return stralloc("");
699 }
701 char *rrdgetinternal(
702 long argc,
703 const char **args)
704 {
705 if (argc == 1) {
706 if (strcasecmp(args[0], "VERSION") == 0) {
707 return stralloc(PACKAGE_VERSION);
708 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
709 return stralloc(__DATE__ " " __TIME__);
710 } else {
711 return stralloc("[ERROR: internal unknown argument]");
712 }
713 } else {
714 return stralloc("[ERROR: internal expected 1 argument]");
715 }
716 }
718 /* Format start or end times using strftime. We always need both the
719 * start and end times, because, either might be relative to the other.
720 * */
721 #define MAX_STRFTIME_SIZE 256
722 char *printstrftime(
723 long argc,
724 const char **args)
725 {
726 rrd_time_value_t start_tv, end_tv;
727 char *parsetime_error = NULL;
728 char formatted[MAX_STRFTIME_SIZE];
729 struct tm *the_tm;
730 time_t start_tmp, end_tmp;
732 /* Make sure that we were given the right number of args */
733 if (argc != 4) {
734 rrd_set_error("wrong number of args %d", argc);
735 return stralloc("");
736 }
738 /* Init start and end time */
739 rrd_parsetime("end-24h", &start_tv);
740 rrd_parsetime("now", &end_tv);
742 /* Parse the start and end times we were given */
743 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
744 rrd_set_error("start time: %s", parsetime_error);
745 return stralloc("");
746 }
747 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
748 rrd_set_error("end time: %s", parsetime_error);
749 return stralloc("");
750 }
751 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
752 return stralloc("");
753 }
755 /* Do we do the start or end */
756 if (strcasecmp(args[0], "START") == 0) {
757 the_tm = localtime(&start_tmp);
758 } else if (strcasecmp(args[0], "END") == 0) {
759 the_tm = localtime(&end_tmp);
760 } else {
761 rrd_set_error("start/end not found in '%s'", args[0]);
762 return stralloc("");
763 }
765 /* now format it */
766 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
767 return (stralloc(formatted));
768 } else {
769 rrd_set_error("strftime failed");
770 return stralloc("");
771 }
772 }
774 char *includefile(
775 long argc,
776 const char **args)
777 {
778 char *buffer;
780 if (argc >= 1) {
781 const char *filename = args[0];
783 readfile(filename, &buffer, 0);
784 if (rrd_test_error()) {
785 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
787 sprintf(err, "[ERROR: %s]", rrd_get_error());
788 rrd_clear_error();
789 return err;
790 } else {
791 return buffer;
792 }
793 } else {
794 return stralloc("[ERROR: No Inclue file defined]");
795 }
796 }
798 /* make a copy of buf and replace open/close brackets with '_' */
799 char *rrdstrip(
800 char *buf)
801 {
802 char *p;
804 if (buf == NULL) {
805 return NULL;
806 }
807 /* make a copy of the buffer */
808 buf = stralloc(buf);
809 if (buf == NULL) {
810 return NULL;
811 }
813 p = buf;
814 while (*p) {
815 if (*p == '<' || *p == '>') {
816 *p = '_';
817 }
818 p++;
819 }
820 return buf;
821 }
823 char *cgigetq(
824 long argc,
825 const char **args)
826 {
827 if (argc >= 1) {
828 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
829 char *buf2;
830 char *c, *d;
831 int qc = 0;
833 if (buf == NULL)
834 return NULL;
836 for (c = buf; *c != '\0'; c++)
837 if (*c == '"')
838 qc++;
839 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
840 perror("Malloc Buffer");
841 exit(1);
842 };
843 c = buf;
844 d = buf2;
845 *(d++) = '"';
846 while (*c != '\0') {
847 if (*c == '"') {
848 *(d++) = '"';
849 *(d++) = '\'';
850 *(d++) = '"';
851 *(d++) = '\'';
852 }
853 *(d++) = *(c++);
854 }
855 *(d++) = '"';
856 *(d) = '\0';
857 free(buf);
858 return buf2;
859 }
861 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
862 }
864 /* remove occurrences of .. this is a general measure to make
865 paths which came in via cgi do not go UP ... */
867 char *cgigetqp(
868 long argc,
869 const char **args)
870 {
871 char *buf;
872 char *buf2;
873 char *p;
874 char *d;
876 if (argc < 1) {
877 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
878 }
880 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
881 if (!buf) {
882 return NULL;
883 }
885 buf2 = malloc(strlen(buf) + 1);
886 if (!buf2) {
887 perror("cgigetqp(): Malloc Path Buffer");
888 exit(1);
889 };
891 p = buf;
892 d = buf2;
894 while (*p) {
895 /* prevent mallicious paths from entering the system */
896 if (p[0] == '.' && p[1] == '.') {
897 p += 2;
898 *d++ = '_';
899 *d++ = '_';
900 } else {
901 *d++ = *p++;
902 }
903 }
905 *d = 0;
906 free(buf);
908 /* Make sure the path is relative, e.g. does not start with '/' */
909 p = buf2;
910 while ('/' == *p) {
911 *p++ = '_';
912 }
914 return buf2;
915 }
918 char *cgiget(
919 long argc,
920 const char **args)
921 {
922 if (argc >= 1)
923 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
924 else
925 return stralloc("[ERROR: not enough arguments for RRD::CV]");
926 }
930 char *drawgraph(
931 long argc,
932 const char **args)
933 {
934 int i, xsize, ysize;
935 double ymin, ymax;
937 for (i = 0; i < argc; i++)
938 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
939 break;
940 if (i == argc) {
941 args[argc++] = "--imginfo";
942 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
943 }
944 calfree();
945 if (rrd_graph
946 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
947 &ymax) != -1) {
948 return stralloc(calcpr[0]);
949 } else {
950 if (rrd_test_error()) {
951 char *err =
952 malloc((strlen(rrd_get_error()) +
953 DS_NAM_SIZE) * sizeof(char));
954 sprintf(err, "[ERROR: %s]", rrd_get_error());
955 rrd_clear_error();
956 calfree();
957 return err;
958 }
959 }
960 return NULL;
961 }
963 char *drawprint(
964 long argc,
965 const char **args)
966 {
967 if (argc == 1 && calcpr) {
968 long i = 0;
970 while (calcpr[i] != NULL)
971 i++; /*determine number lines in calcpr */
972 if (atol(args[0]) < i - 1)
973 return stralloc(calcpr[atol(args[0]) + 1]);
974 }
975 return stralloc("[ERROR: RRD::PRINT argument error]");
976 }
978 char *printtimelast(
979 long argc,
980 const char **args)
981 {
982 time_t last;
983 struct tm tm_last;
984 char *buf;
986 if (argc == 2) {
987 buf = malloc(255);
988 if (buf == NULL) {
989 return stralloc("[ERROR: allocating strftime buffer]");
990 };
991 last = rrd_last(argc + 1, (char **) args - 1);
992 if (rrd_test_error()) {
993 char *err =
994 malloc((strlen(rrd_get_error()) +
995 DS_NAM_SIZE) * sizeof(char));
996 sprintf(err, "[ERROR: %s]", rrd_get_error());
997 rrd_clear_error();
998 return err;
999 }
1000 tm_last = *localtime(&last);
1001 strftime(buf, 254, args[1], &tm_last);
1002 return buf;
1003 }
1004 if (argc < 2) {
1005 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
1006 }
1007 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
1008 }
1010 char *printtimenow(
1011 long argc,
1012 const char **args)
1013 {
1014 time_t now = time(NULL);
1015 struct tm tm_now;
1016 char *buf;
1018 if (argc == 1) {
1019 buf = malloc(255);
1020 if (buf == NULL) {
1021 return stralloc("[ERROR: allocating strftime buffer]");
1022 };
1023 tm_now = *localtime(&now);
1024 strftime(buf, 254, args[0], &tm_now);
1025 return buf;
1026 }
1027 if (argc < 1) {
1028 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
1029 }
1030 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
1031 }
1033 /* Scan buffer until an unescaped '>' arives.
1034 * Update argument array with arguments found.
1035 * Return end cursor where parsing stopped, or NULL in case of failure.
1036 *
1037 * FIXME:
1038 * To allow nested constructs, we call rrd_expand_vars() for arguments
1039 * that contain RRD::x directives. These introduce a small memory leak
1040 * since we have to stralloc the arguments the way parse() works.
1041 */
1042 char *scanargs(
1043 char *line,
1044 int *argument_count,
1045 char ***arguments)
1046 {
1047 char *getP; /* read cursor */
1048 char *putP; /* write cursor */
1049 char Quote; /* type of quote if in quoted string, 0 otherwise */
1050 int tagcount; /* open tag count */
1051 int in_arg; /* if we currently are parsing an argument or not */
1052 int argsz; /* argument array size */
1053 int curarg_contains_rrd_directives;
1055 /* local array of arguments while parsing */
1056 int argc = 0;
1057 char **argv;
1059 #ifdef DEBUG_PARSER
1060 printf("<-- scanargs(%s) -->\n", line);
1061 #endif
1063 *arguments = NULL;
1064 *argument_count = 0;
1066 /* create initial argument array of char pointers */
1067 argsz = 32;
1068 argv = (char **) malloc(argsz * sizeof(char *));
1069 if (!argv) {
1070 return NULL;
1071 }
1073 /* skip leading blanks */
1074 while (isspace((int) *line)) {
1075 line++;
1076 }
1078 getP = line;
1079 putP = line;
1081 Quote = 0;
1082 in_arg = 0;
1083 tagcount = 0;
1085 curarg_contains_rrd_directives = 0;
1087 /* start parsing 'line' for arguments */
1088 while (*getP) {
1089 unsigned char c = *getP++;
1091 if (c == '>' && !Quote && !tagcount) {
1092 /* this is our closing tag, quit scanning */
1093 break;
1094 }
1096 /* remove all special chars */
1097 if (c < ' ') {
1098 c = ' ';
1099 }
1101 switch (c) {
1102 case ' ':
1103 if (Quote || tagcount) {
1104 /* copy quoted/tagged (=RRD expanded) string */
1105 *putP++ = c;
1106 } else if (in_arg) {
1107 /* end argument string */
1108 *putP++ = 0;
1109 in_arg = 0;
1110 if (curarg_contains_rrd_directives) {
1111 argv[argc - 1] =
1112 rrd_expand_vars(stralloc(argv[argc - 1]));
1113 curarg_contains_rrd_directives = 0;
1114 }
1115 }
1116 break;
1118 case '"': /* Fall through */
1119 case '\'':
1120 if (Quote != 0) {
1121 if (Quote == c) {
1122 Quote = 0;
1123 } else {
1124 /* copy quoted string */
1125 *putP++ = c;
1126 }
1127 } else {
1128 if (!in_arg) {
1129 /* reference start of argument string in argument array */
1130 argv[argc++] = putP;
1131 in_arg = 1;
1132 }
1133 Quote = c;
1134 }
1135 break;
1137 default:
1138 if (!in_arg) {
1139 /* start new argument */
1140 argv[argc++] = putP;
1141 in_arg = 1;
1142 }
1143 if (c == '>') {
1144 if (tagcount) {
1145 tagcount--;
1146 }
1147 }
1148 if (c == '<') {
1149 tagcount++;
1150 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1151 curarg_contains_rrd_directives = 1;
1152 }
1153 }
1154 *putP++ = c;
1155 break;
1156 }
1158 /* check if our argument array is still large enough */
1159 if (argc == argsz) {
1160 /* resize argument array */
1161 argsz *= 2;
1162 argv = rrd_realloc(argv, argsz * sizeof(char *));
1163 if (*argv == NULL) {
1164 return NULL;
1165 }
1166 }
1167 }
1169 /* terminate last argument found */
1170 *putP = '\0';
1171 if (curarg_contains_rrd_directives) {
1172 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1173 }
1174 #ifdef DEBUG_PARSER
1175 if (argc > 0) {
1176 int n;
1178 printf("<-- arguments found [%d]\n", argc);
1179 for (n = 0; n < argc; n++) {
1180 printf("arg %02d: '%s'\n", n, argv[n]);
1181 }
1182 printf("-->\n");
1183 } else {
1184 printf("<!-- No arguments found -->\n");
1185 }
1186 #endif
1188 /* update caller's notion of the argument array and it's size */
1189 *arguments = argv;
1190 *argument_count = argc;
1192 if (Quote) {
1193 return NULL;
1194 }
1196 /* Return new scanning cursor:
1197 pointer to char after closing bracket */
1198 return getP;
1199 }
1202 /*
1203 * Parse(): scan current portion of buffer for given tag.
1204 * If found, parse tag arguments and call 'func' for it.
1205 * The result of func is inserted at the current position
1206 * in the buffer.
1207 */
1208 int parse(
1209 char **buf, /* buffer */
1210 long i, /* offset in buffer */
1211 char *tag, /* tag to handle */
1212 char * (*func) (long,
1213 const char **) /* function to call for 'tag' */
1214 )
1215 {
1216 /* the name of the vairable ... */
1217 char *val;
1218 long valln;
1219 char **args;
1220 char *end;
1221 long end_offset;
1222 int argc;
1223 size_t taglen = strlen(tag);
1225 /* Current position in buffer should start with 'tag' */
1226 if (strncmp((*buf) + i, tag, taglen) != 0) {
1227 return 0;
1228 }
1229 /* .. and match exactly (a whitespace following 'tag') */
1230 if (!isspace(*((*buf) + i + taglen))) {
1231 return 0;
1232 }
1233 #ifdef DEBUG_PARSER
1234 printf("parse(): handling tag '%s'\n", tag);
1235 #endif
1237 /* Scan for arguments following the tag;
1238 scanargs() puts \0 into *buf ... so after scanargs it is probably
1239 not a good time to use strlen on buf */
1240 end = scanargs((*buf) + i + taglen, &argc, &args);
1241 if (end) {
1242 /* got arguments, call function for 'tag' with arguments */
1243 val = func(argc, (const char **) args);
1244 free(args);
1245 } else {
1246 /* unable to parse arguments, undo 0-termination by scanargs */
1247 for (; argc > 0; argc--) {
1248 *((args[argc - 1]) - 1) = ' ';
1249 }
1251 /* next call, try parsing at current offset +1 */
1252 end = (*buf) + i + 1;
1254 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1255 " Check original file. This may have been altered "
1256 "by parsing.]\n\n");
1257 }
1259 /* remember offset where we have to continue parsing */
1260 end_offset = end - (*buf);
1262 valln = 0;
1263 if (val) {
1264 valln = strlen(val);
1265 }
1267 /* Optionally resize buffer to hold the replacement value:
1268 Calculating the new length of the buffer is simple. add current
1269 buffer pos (i) to length of string after replaced tag to length
1270 of replacement string and add 1 for the final zero ... */
1271 if (end - (*buf) < (i + valln)) {
1272 /* make sure we do not shrink the mallocd block */
1273 size_t newbufsize = i + strlen(end) + valln + 1;
1275 *buf = rrd_realloc(*buf, newbufsize);
1277 if (*buf == NULL) {
1278 perror("Realoc buf:");
1279 exit(1);
1280 };
1281 }
1283 /* Update new end pointer:
1284 make sure the 'end' pointer gets moved along with the
1285 buf pointer when realloc moves memory ... */
1286 end = (*buf) + end_offset;
1288 /* splice the variable:
1289 step 1. Shift pending data to make room for 'val' */
1290 memmove((*buf) + i + valln, end, strlen(end) + 1);
1292 /* step 2. Insert val */
1293 if (val) {
1294 memmove((*buf) + i, val, valln);
1295 free(val);
1296 }
1297 return (valln > 0 ? valln - 1 : valln);
1298 }
1300 char *http_time(
1301 time_t *now)
1302 {
1303 struct tm *tmptime;
1304 static char buf[60];
1306 tmptime = gmtime(now);
1307 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1308 return (buf);
1309 }
1311 void rrdcgiHeader(
1312 void)
1313 {
1314 if (rrdcgiType)
1315 printf("Content-type: %s\n", rrdcgiType);
1316 else
1317 printf("Content-type: text/html\n");
1318 if (rrdcgiHeaderString)
1319 printf("%s", rrdcgiHeaderString);
1320 printf("\n");
1321 }
1323 void rrdcgiDebug(
1324 int level,
1325 int where)
1326 {
1327 if (level > 0)
1328 rrdcgiDebugLevel = level;
1329 else
1330 rrdcgiDebugLevel = 0;
1331 if (where)
1332 rrdcgiDebugStderr = 0;
1333 else
1334 rrdcgiDebugStderr = 1;
1335 }
1337 char *rrdcgiDecodeString(
1338 char *text)
1339 {
1340 char *cp, *xp;
1342 for (cp = text, xp = text; *cp; cp++) {
1343 if (*cp == '%') {
1344 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1345 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1346 if (islower(*(cp + 1)))
1347 *(cp + 1) = toupper(*(cp + 1));
1348 if (islower(*(cp + 2)))
1349 *(cp + 2) = toupper(*(cp + 2));
1350 *(xp) =
1351 (*(cp + 1) >=
1352 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1353 (*(cp + 2) >=
1354 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1355 xp++;
1356 cp += 2;
1357 }
1358 } else {
1359 *(xp++) = *cp;
1360 }
1361 }
1362 memset(xp, 0, cp - xp);
1363 return text;
1364 }
1366 /* rrdcgiReadVariables()
1367 *
1368 * Read from stdin if no string is provided via CGI. Variables that
1369 * doesn't have a value associated with it doesn't get stored.
1370 */
1371 s_var **rrdcgiReadVariables(
1372 void)
1373 {
1374 int length;
1375 char *line = NULL;
1376 int numargs;
1377 char *cp, *ip, *esp, *sptr;
1378 s_var **result;
1379 int i, k, len;
1380 char tmp[101];
1382 cp = getenv("REQUEST_METHOD");
1383 ip = getenv("CONTENT_LENGTH");
1385 if (cp && !strcmp(cp, "POST")) {
1386 if (ip) {
1387 length = atoi(ip);
1388 if ((line = (char *) malloc(length + 2)) == NULL)
1389 return NULL;
1390 fgets(line, length + 1, stdin);
1391 } else
1392 return NULL;
1393 } else if (cp && !strcmp(cp, "GET")) {
1394 esp = getenv("QUERY_STRING");
1395 if (esp && strlen(esp)) {
1396 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1397 return NULL;
1398 sprintf(line, "%s", esp);
1399 } else
1400 return NULL;
1401 } else {
1402 length = 0;
1403 printf("(offline mode: enter name=value pairs on standard input)\n");
1404 memset(tmp, 0, sizeof(tmp));
1405 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1406 if (strlen(tmp)) {
1407 if (tmp[strlen(tmp) - 1] == '\n')
1408 tmp[strlen(tmp) - 1] = '&';
1409 if (length) {
1410 length += strlen(tmp);
1411 len = (length + 1) * sizeof(char);
1412 if ((line = (char *) realloc(line, len)) == NULL)
1413 return NULL;
1414 strcat(line, tmp);
1415 } else {
1416 length = strlen(tmp);
1417 len = (length + 1) * sizeof(char);
1418 if ((line = (char *) malloc(len)) == NULL)
1419 return NULL;
1420 memset(line, 0, len);
1421 strcpy(line, tmp);
1422 }
1423 }
1424 memset(tmp, 0, sizeof(tmp));
1425 }
1426 if (!line)
1427 return NULL;
1428 if (line[strlen(line) - 1] == '&')
1429 line[strlen(line) - 1] = '\0';
1430 }
1432 /*
1433 * From now on all cgi variables are stored in the variable line
1434 * and look like foo=bar&foobar=barfoo&foofoo=
1435 */
1437 if (rrdcgiDebugLevel > 0) {
1438 if (rrdcgiDebugStderr)
1439 fprintf(stderr, "Received cgi input: %s\n", line);
1440 else
1441 printf
1442 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1443 line);
1444 }
1446 for (cp = line; *cp; cp++)
1447 if (*cp == '+')
1448 *cp = ' ';
1450 if (strlen(line)) {
1451 for (numargs = 1, cp = line; *cp; cp++)
1452 if (*cp == '&')
1453 numargs++;
1454 } else
1455 numargs = 0;
1456 if (rrdcgiDebugLevel > 0) {
1457 if (rrdcgiDebugStderr)
1458 fprintf(stderr, "%d cgi variables found.\n", numargs);
1459 else
1460 printf("%d cgi variables found.<br>\n", numargs);
1461 }
1463 len = (numargs + 1) * sizeof(s_var *);
1464 if ((result = (s_var **) malloc(len)) == NULL)
1465 return NULL;
1466 memset(result, 0, len);
1468 cp = line;
1469 i = 0;
1470 while (*cp) {
1471 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1472 *ip = '\0';
1473 } else
1474 ip = cp + strlen(cp);
1476 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1477 cp = ++ip;
1478 continue;
1479 }
1481 if (!strlen(esp)) {
1482 cp = ++ip;
1483 continue;
1484 }
1486 if (i < numargs) {
1488 /* try to find out if there's already such a variable */
1489 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1490 || !(strlen(result[k]->name) ==
1491 (size_t) (esp - cp))); k++);
1493 if (k == i) { /* No such variable yet */
1494 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1495 return NULL;
1496 if ((result[i]->name =
1497 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1498 return NULL;
1499 memset(result[i]->name, 0, esp - cp + 1);
1500 strncpy(result[i]->name, cp, esp - cp);
1501 cp = ++esp;
1502 if ((result[i]->value =
1503 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1504 return NULL;
1505 memset(result[i]->value, 0, ip - esp + 1);
1506 strncpy(result[i]->value, cp, ip - esp);
1507 result[i]->value = rrdcgiDecodeString(result[i]->value);
1508 if (rrdcgiDebugLevel) {
1509 if (rrdcgiDebugStderr)
1510 fprintf(stderr, "%s: %s\n", result[i]->name,
1511 result[i]->value);
1512 else
1513 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1514 result[i]->name, result[i]->value);
1515 }
1516 i++;
1517 } else { /* There is already such a name, suppose a mutiple field */
1518 cp = ++esp;
1519 len =
1520 (strlen(result[k]->value) + (ip - esp) +
1521 2) * sizeof(char);
1522 if ((sptr = (char *) malloc(len)) == NULL)
1523 return NULL;
1524 memset(sptr, 0, len);
1525 sprintf(sptr, "%s\n", result[k]->value);
1526 strncat(sptr, cp, ip - esp);
1527 free(result[k]->value);
1528 result[k]->value = rrdcgiDecodeString(sptr);
1529 }
1530 }
1531 cp = ++ip;
1532 }
1533 return result;
1534 }
1536 /* rrdcgiInit()
1537 *
1538 * Read from stdin if no string is provided via CGI. Variables that
1539 * doesn't have a value associated with it doesn't get stored.
1540 */
1541 s_cgi *rrdcgiInit(
1542 void)
1543 {
1544 s_cgi *res;
1545 s_var **vars;
1547 vars = rrdcgiReadVariables();
1549 if (!vars)
1550 return NULL;
1552 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1553 return NULL;
1554 res->vars = vars;
1556 return res;
1557 }
1559 char *rrdcgiGetValue(
1560 s_cgi * parms,
1561 const char *name)
1562 {
1563 int i;
1565 if (!parms || !parms->vars)
1566 return NULL;
1567 for (i = 0; parms->vars[i]; i++)
1568 if (!strcmp(name, parms->vars[i]->name)) {
1569 if (rrdcgiDebugLevel > 0) {
1570 if (rrdcgiDebugStderr)
1571 fprintf(stderr, "%s found as %s\n", name,
1572 parms->vars[i]->value);
1573 else
1574 printf("%s found as %s<br>\n", name,
1575 parms->vars[i]->value);
1576 }
1577 return parms->vars[i]->value;
1578 }
1579 if (rrdcgiDebugLevel) {
1580 if (rrdcgiDebugStderr)
1581 fprintf(stderr, "%s not found\n", name);
1582 else
1583 printf("%s not found<br>\n", name);
1584 }
1585 return NULL;
1586 }
1588 void rrdcgiFreeList(
1589 char **list)
1590 {
1591 int i;
1593 for (i = 0; list[i] != NULL; i++)
1594 free(list[i]);
1595 free(list);
1596 }
1598 void rrdcgiFree(
1599 s_cgi * parms)
1600 {
1601 int i;
1603 if (!parms)
1604 return;
1605 if (parms->vars) {
1606 for (i = 0; parms->vars[i]; i++) {
1607 if (parms->vars[i]->name)
1608 free(parms->vars[i]->name);
1609 if (parms->vars[i]->value)
1610 free(parms->vars[i]->value);
1611 free(parms->vars[i]);
1612 }
1613 free(parms->vars);
1614 }
1615 free(parms);
1617 if (rrdcgiHeaderString) {
1618 free(rrdcgiHeaderString);
1619 rrdcgiHeaderString = NULL;
1620 }
1621 if (rrdcgiType) {
1622 free(rrdcgiType);
1623 rrdcgiType = NULL;
1624 }
1625 }