1 /*****************************************************************************
2 * RRDtool 1.4.2 Copyright by Tobi Oetiker, 1997-2009
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 calcpr = NULL;
378 }
379 }
381 /* create freeable version of the string */
382 char *stralloc(
383 const char *str)
384 {
385 char *nstr;
387 if (!str) {
388 return NULL;
389 }
390 nstr = malloc((strlen(str) + 1));
391 strcpy(nstr, str);
392 return (nstr);
393 }
395 static int readfile(
396 const char *file_name,
397 char **buffer,
398 int skipfirst)
399 {
400 long writecnt = 0, totalcnt = MEMBLK;
401 long offset = 0;
402 FILE *input = NULL;
403 char c;
405 if ((strcmp("-", file_name) == 0)) {
406 input = stdin;
407 } else {
408 if ((input = fopen(file_name, "rb")) == NULL) {
409 rrd_set_error("opening '%s': %s", file_name, rrd_strerror(errno));
410 return (-1);
411 }
412 }
413 if (skipfirst) {
414 do {
415 c = getc(input);
416 offset++;
417 } while (c != '\n' && !feof(input));
418 }
419 if (strcmp("-", file_name)) {
420 fseek(input, 0, SEEK_END);
421 /* have extra space for detecting EOF without realloc */
422 totalcnt = (ftell(input) + 1) / sizeof(char) - offset;
423 if (totalcnt < MEMBLK)
424 totalcnt = MEMBLK; /* sanitize */
425 fseek(input, offset * sizeof(char), SEEK_SET);
426 }
427 if (((*buffer) = (char *) malloc((totalcnt + 4) * sizeof(char))) == NULL) {
428 perror("Allocate Buffer:");
429 exit(1);
430 };
431 do {
432 writecnt +=
433 fread((*buffer) + writecnt, 1,
434 (totalcnt - writecnt) * sizeof(char), input);
435 if (writecnt >= totalcnt) {
436 totalcnt += MEMBLK;
437 if (((*buffer) =
438 rrd_realloc((*buffer),
439 (totalcnt + 4) * sizeof(char))) == NULL) {
440 perror("Realloc Buffer:");
441 exit(1);
442 };
443 }
444 } while (!feof(input));
445 (*buffer)[writecnt] = '\0';
446 if (strcmp("-", file_name) != 0) {
447 fclose(input);
448 };
449 return writecnt;
450 }
452 int main(
453 int argc,
454 char *argv[])
455 {
456 long length;
457 char *buffer;
458 char *server_url = NULL;
459 long i;
460 long filter = 0;
461 struct option long_options[] = {
462 {"filter", no_argument, 0, 'f'},
463 {0, 0, 0, 0}
464 };
466 #ifdef MUST_DISABLE_SIGFPE
467 signal(SIGFPE, SIG_IGN);
468 #endif
469 #ifdef MUST_DISABLE_FPMASK
470 fpsetmask(0);
471 #endif
472 optind = 0;
473 opterr = 0; /* initialize getopt */
475 /* what do we get for cmdline arguments?
476 for (i=0;i<argc;i++)
477 printf("%d-'%s'\n",i,argv[i]); */
478 while (1) {
479 int option_index = 0;
480 int opt;
482 opt = getopt_long(argc, argv, "f", long_options, &option_index);
483 if (opt == EOF) {
484 break;
485 }
487 switch (opt) {
488 case 'f':
489 filter = 1;
490 break;
491 case '?':
492 printf("unknown commandline option '%s'\n", argv[optind - 1]);
493 return -1;
494 }
495 }
497 if (!filter) {
498 rrdcgiDebug(0, 0);
499 rrdcgiArg = rrdcgiInit();
500 server_url = getenv("SERVER_URL");
501 }
503 /* make sure we have one extra argument,
504 if there are others, we do not care Apache gives several */
506 /* if ( (optind != argc-2
507 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
508 && optind != argc-1) { */
510 if (optind >= argc) {
511 fprintf(stderr, "ERROR: expected a filename\n");
512 exit(1);
513 } else {
514 length = readfile(argv[optind], &buffer, 1);
515 }
517 if (rrd_test_error()) {
518 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
519 exit(1);
520 }
522 /* initialize variable heap */
523 initvar();
525 #ifdef DEBUG_PARSER
526 /* some fake header for testing */
527 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
528 #endif
531 /* expand rrd directives in buffer recursivly */
532 for (i = 0; buffer[i]; i++) {
533 if (buffer[i] != '<')
534 continue;
535 if (!filter) {
536 parse(&buffer, i, "<RRD::CV", cgiget);
537 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
538 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
539 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
540 }
541 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
542 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
543 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
544 parse(&buffer, i, "<RRD::INCLUDE", includefile);
545 parse(&buffer, i, "<RRD::PRINT", drawprint);
546 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
547 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
548 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
549 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
550 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
551 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
552 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
553 }
555 if (!filter) {
556 printf("Content-Type: text/html\n"
557 "Content-Length: %zd\n", strlen(buffer));
559 if (labs(goodfor) > 0) {
560 time_t now;
562 now = time(NULL);
563 printf("Last-Modified: %s\n", http_time(&now));
564 now += labs(goodfor);
565 printf("Expires: %s\n", http_time(&now));
566 if (goodfor < 0) {
567 printf("Refresh: %ld\n", labs(goodfor));
568 }
569 }
570 printf("\n");
571 }
573 /* output result */
574 printf("%s", buffer);
576 /* cleanup */
577 calfree();
578 if (buffer) {
579 free(buffer);
580 }
581 donevar();
582 exit(0);
583 }
585 /* remove occurrences of .. this is a general measure to make
586 paths which came in via cgi do not go UP ... */
588 char *rrdsetenv(
589 long argc,
590 const char **args)
591 {
592 if (argc >= 2) {
593 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
595 if (xyz == NULL) {
596 return stralloc("[ERROR: allocating setenv buffer]");
597 };
598 sprintf(xyz, "%s=%s", args[0], args[1]);
599 if (putenv(xyz) == -1) {
600 free(xyz);
601 return stralloc("[ERROR: failed to do putenv]");
602 };
603 return stralloc("");
604 }
605 return stralloc("[ERROR: setenv failed because not enough "
606 "arguments were defined]");
607 }
609 /* rrd interface to the variable function putvar() */
610 char *rrdsetvar(
611 long argc,
612 const char **args)
613 {
614 if (argc >= 2) {
615 const char *result = putvar(args[0], args[1], 0 /* not const */ );
617 if (result) {
618 /* setvar does not return the value set */
619 return stralloc("");
620 }
621 return stralloc("[ERROR: putvar failed]");
622 }
623 return stralloc("[ERROR: putvar failed because not enough arguments "
624 "were defined]");
625 }
627 /* rrd interface to the variable function putvar() */
628 char *rrdsetvarconst(
629 long argc,
630 const char **args)
631 {
632 if (argc >= 2) {
633 const char *result = putvar(args[0], args[1], 1 /* const */ );
635 if (result) {
636 /* setvar does not return the value set */
637 return stralloc("");
638 }
639 return stralloc("[ERROR: putvar failed]");
640 }
641 return stralloc("[ERROR: putvar failed because not enough arguments "
642 "were defined]");
643 }
645 char *rrdgetenv(
646 long argc,
647 const char **args)
648 {
649 char buf[128];
650 const char *envvar;
652 if (argc != 1) {
653 return stralloc("[ERROR: getenv failed because it did not "
654 "get 1 argument only]");
655 };
656 envvar = getenv(args[0]);
657 if (envvar) {
658 return stralloc(envvar);
659 } else {
660 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
661 return stralloc(buf);
662 }
663 }
665 char *rrdgetvar(
666 long argc,
667 const char **args)
668 {
669 char buf[128];
670 const char *value;
672 if (argc != 1) {
673 return stralloc("[ERROR: getvar failed because it did not "
674 "get 1 argument only]");
675 };
676 value = getvar(args[0]);
677 if (value) {
678 return stralloc(value);
679 } else {
680 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
681 return stralloc(buf);
682 }
683 }
685 char *rrdgoodfor(
686 long argc,
687 const char **args)
688 {
689 if (argc == 1) {
690 goodfor = atol(args[0]);
691 } else {
692 return stralloc("[ERROR: goodfor expected 1 argument]");
693 }
695 if (goodfor == 0) {
696 return stralloc("[ERROR: goodfor value must not be 0]");
697 }
699 return stralloc("");
700 }
702 char *rrdgetinternal(
703 long argc,
704 const char **args)
705 {
706 if (argc == 1) {
707 if (strcasecmp(args[0], "VERSION") == 0) {
708 return stralloc(PACKAGE_VERSION);
709 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
710 return stralloc(__DATE__ " " __TIME__);
711 } else {
712 return stralloc("[ERROR: internal unknown argument]");
713 }
714 } else {
715 return stralloc("[ERROR: internal expected 1 argument]");
716 }
717 }
719 /* Format start or end times using strftime. We always need both the
720 * start and end times, because, either might be relative to the other.
721 * */
722 #define MAX_STRFTIME_SIZE 256
723 char *printstrftime(
724 long argc,
725 const char **args)
726 {
727 rrd_time_value_t start_tv, end_tv;
728 char *parsetime_error = NULL;
729 char formatted[MAX_STRFTIME_SIZE];
730 struct tm *the_tm;
731 time_t start_tmp, end_tmp;
733 /* Make sure that we were given the right number of args */
734 if (argc != 4) {
735 rrd_set_error("wrong number of args %d", argc);
736 return stralloc("");
737 }
739 /* Init start and end time */
740 rrd_parsetime("end-24h", &start_tv);
741 rrd_parsetime("now", &end_tv);
743 /* Parse the start and end times we were given */
744 if ((parsetime_error = rrd_parsetime(args[1], &start_tv))) {
745 rrd_set_error("start time: %s", parsetime_error);
746 return stralloc("");
747 }
748 if ((parsetime_error = rrd_parsetime(args[2], &end_tv))) {
749 rrd_set_error("end time: %s", parsetime_error);
750 return stralloc("");
751 }
752 if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
753 return stralloc("");
754 }
756 /* Do we do the start or end */
757 if (strcasecmp(args[0], "START") == 0) {
758 the_tm = localtime(&start_tmp);
759 } else if (strcasecmp(args[0], "END") == 0) {
760 the_tm = localtime(&end_tmp);
761 } else {
762 rrd_set_error("start/end not found in '%s'", args[0]);
763 return stralloc("");
764 }
766 /* now format it */
767 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
768 return (stralloc(formatted));
769 } else {
770 rrd_set_error("strftime failed");
771 return stralloc("");
772 }
773 }
775 char *includefile(
776 long argc,
777 const char **args)
778 {
779 char *buffer;
781 if (argc >= 1) {
782 const char *filename = args[0];
784 readfile(filename, &buffer, 0);
785 if (rrd_test_error()) {
786 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
788 sprintf(err, "[ERROR: %s]", rrd_get_error());
789 rrd_clear_error();
790 return err;
791 } else {
792 return buffer;
793 }
794 } else {
795 return stralloc("[ERROR: No Inclue file defined]");
796 }
797 }
799 /* make a copy of buf and replace open/close brackets with '_' */
800 char *rrdstrip(
801 char *buf)
802 {
803 char *p;
805 if (buf == NULL) {
806 return NULL;
807 }
808 /* make a copy of the buffer */
809 buf = stralloc(buf);
810 if (buf == NULL) {
811 return NULL;
812 }
814 p = buf;
815 while (*p) {
816 if (*p == '<' || *p == '>') {
817 *p = '_';
818 }
819 p++;
820 }
821 return buf;
822 }
824 char *cgigetq(
825 long argc,
826 const char **args)
827 {
828 if (argc >= 1) {
829 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
830 char *buf2;
831 char *c, *d;
832 int qc = 0;
834 if (buf == NULL)
835 return NULL;
837 for (c = buf; *c != '\0'; c++)
838 if (*c == '"')
839 qc++;
840 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
841 perror("Malloc Buffer");
842 exit(1);
843 };
844 c = buf;
845 d = buf2;
846 *(d++) = '"';
847 while (*c != '\0') {
848 if (*c == '"') {
849 *(d++) = '"';
850 *(d++) = '\'';
851 *(d++) = '"';
852 *(d++) = '\'';
853 }
854 *(d++) = *(c++);
855 }
856 *(d++) = '"';
857 *(d) = '\0';
858 free(buf);
859 return buf2;
860 }
862 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
863 }
865 /* remove occurrences of .. this is a general measure to make
866 paths which came in via cgi do not go UP ... */
868 char *cgigetqp(
869 long argc,
870 const char **args)
871 {
872 char *buf;
873 char *buf2;
874 char *p;
875 char *d;
877 if (argc < 1) {
878 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
879 }
881 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
882 if (!buf) {
883 return NULL;
884 }
886 buf2 = malloc(strlen(buf) + 1);
887 if (!buf2) {
888 perror("cgigetqp(): Malloc Path Buffer");
889 exit(1);
890 };
892 p = buf;
893 d = buf2;
895 while (*p) {
896 /* prevent mallicious paths from entering the system */
897 if (p[0] == '.' && p[1] == '.') {
898 p += 2;
899 *d++ = '_';
900 *d++ = '_';
901 } else {
902 *d++ = *p++;
903 }
904 }
906 *d = 0;
907 free(buf);
909 /* Make sure the path is relative, e.g. does not start with '/' */
910 p = buf2;
911 while ('/' == *p) {
912 *p++ = '_';
913 }
915 return buf2;
916 }
919 char *cgiget(
920 long argc,
921 const char **args)
922 {
923 if (argc >= 1)
924 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
925 else
926 return stralloc("[ERROR: not enough arguments for RRD::CV]");
927 }
931 char *drawgraph(
932 long argc,
933 const char **args)
934 {
935 int i, xsize, ysize;
936 double ymin, ymax;
938 for (i = 0; i < argc; i++)
939 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
940 break;
941 if (i == argc) {
942 args[argc++] = "--imginfo";
943 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
944 }
945 calfree();
946 if (rrd_graph
947 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
948 &ymax) != -1) {
949 return stralloc(calcpr[0]);
950 } else {
951 if (rrd_test_error()) {
952 char *err =
953 malloc((strlen(rrd_get_error()) +
954 DS_NAM_SIZE) * sizeof(char));
955 sprintf(err, "[ERROR: %s]", rrd_get_error());
956 rrd_clear_error();
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 /* not raising argc in step with args - 1 since the last argument
992 will be used below for strftime */
994 last = rrd_last(argc, (char **) args - 1);
995 if (rrd_test_error()) {
996 char *err =
997 malloc((strlen(rrd_get_error()) +
998 DS_NAM_SIZE) * sizeof(char));
999 sprintf(err, "[ERROR: %s]", rrd_get_error());
1000 rrd_clear_error();
1001 return err;
1002 }
1003 tm_last = *localtime(&last);
1004 strftime(buf, 254, args[1], &tm_last);
1005 return buf;
1006 }
1007 return stralloc("[ERROR: expected <RRD::TIME::LAST file.rrd strftime-format>]");
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 = 1;
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 }
1072 argv[0] = "rrdcgi";
1074 /* skip leading blanks */
1075 while (isspace((int) *line)) {
1076 line++;
1077 }
1079 getP = line;
1080 putP = line;
1082 Quote = 0;
1083 in_arg = 0;
1084 tagcount = 0;
1086 curarg_contains_rrd_directives = 0;
1088 /* start parsing 'line' for arguments */
1089 while (*getP) {
1090 unsigned char c = *getP++;
1092 if (c == '>' && !Quote && !tagcount) {
1093 /* this is our closing tag, quit scanning */
1094 break;
1095 }
1097 /* remove all special chars */
1098 if (c < ' ') {
1099 c = ' ';
1100 }
1102 switch (c) {
1103 case ' ':
1104 if (Quote || tagcount) {
1105 /* copy quoted/tagged (=RRD expanded) string */
1106 *putP++ = c;
1107 } else if (in_arg) {
1108 /* end argument string */
1109 *putP++ = 0;
1110 in_arg = 0;
1111 if (curarg_contains_rrd_directives) {
1112 argv[argc - 1] =
1113 rrd_expand_vars(stralloc(argv[argc - 1]));
1114 curarg_contains_rrd_directives = 0;
1115 }
1116 }
1117 break;
1119 case '"': /* Fall through */
1120 case '\'':
1121 if (Quote != 0) {
1122 if (Quote == c) {
1123 Quote = 0;
1124 } else {
1125 /* copy quoted string */
1126 *putP++ = c;
1127 }
1128 } else {
1129 if (!in_arg) {
1130 /* reference start of argument string in argument array */
1131 argv[argc++] = putP;
1132 in_arg = 1;
1133 }
1134 Quote = c;
1135 }
1136 break;
1138 default:
1139 if (!in_arg) {
1140 /* start new argument */
1141 argv[argc++] = putP;
1142 in_arg = 1;
1143 }
1144 if (c == '>') {
1145 if (tagcount) {
1146 tagcount--;
1147 }
1148 }
1149 if (c == '<') {
1150 tagcount++;
1151 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1152 curarg_contains_rrd_directives = 1;
1153 }
1154 }
1155 *putP++ = c;
1156 break;
1157 }
1159 /* check if our argument array is still large enough */
1160 if (argc == argsz) {
1161 /* resize argument array */
1162 argsz *= 2;
1163 argv = rrd_realloc(argv, argsz * sizeof(char *));
1164 if (*argv == NULL) {
1165 return NULL;
1166 }
1167 }
1168 }
1170 /* terminate last argument found */
1171 *putP = '\0';
1172 if (curarg_contains_rrd_directives) {
1173 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1174 }
1175 #ifdef DEBUG_PARSER
1176 if (argc > 1) {
1177 int n;
1179 printf("<-- arguments found [%d]\n", argc);
1180 for (n = 0; n < argc; n++) {
1181 printf("arg %02d: '%s'\n", n, argv[n]);
1182 }
1183 printf("-->\n");
1184 } else {
1185 printf("<!-- No arguments found -->\n");
1186 }
1187 #endif
1189 /* update caller's notion of the argument array and it's size */
1191 /* note this is a bit of a hack since the rrd_cgi code used to just put
1192 its arguments into a normal array starting at 0 ... since the rrd_*
1193 commands expect and argc/argv array we used to just shift everything
1194 by -1 ... this in turn exploded when a rrd_* function tried to print
1195 argv[0] ... hence we are now doing everything in argv style but hand
1196 over seemingly the old array ... but doing argv-1 will actually end
1197 up in a 'good' place now. */
1199 *arguments = argv+1;
1200 *argument_count = argc-1;
1202 if (Quote) {
1203 return NULL;
1204 }
1206 /* Return new scanning cursor:
1207 pointer to char after closing bracket */
1208 return getP;
1209 }
1212 /*
1213 * Parse(): scan current portion of buffer for given tag.
1214 * If found, parse tag arguments and call 'func' for it.
1215 * The result of func is inserted at the current position
1216 * in the buffer.
1217 */
1218 int parse(
1219 char **buf, /* buffer */
1220 long i, /* offset in buffer */
1221 char *tag, /* tag to handle */
1222 char * (*func) (long,
1223 const char **) /* function to call for 'tag' */
1224 )
1225 {
1226 /* the name of the vairable ... */
1227 char *val;
1228 long valln;
1229 char **args;
1230 char *end;
1231 long end_offset;
1232 int argc;
1233 size_t taglen = strlen(tag);
1235 /* Current position in buffer should start with 'tag' */
1236 if (strncmp((*buf) + i, tag, taglen) != 0) {
1237 return 0;
1238 }
1239 /* .. and match exactly (a whitespace following 'tag') */
1240 if (!isspace(*((*buf) + i + taglen))) {
1241 return 0;
1242 }
1243 #ifdef DEBUG_PARSER
1244 printf("parse(): handling tag '%s'\n", tag);
1245 #endif
1247 /* Scan for arguments following the tag;
1248 scanargs() puts \0 into *buf ... so after scanargs it is probably
1249 not a good time to use strlen on buf */
1250 end = scanargs((*buf) + i + taglen, &argc, &args);
1251 if (end) {
1252 /* got arguments, call function for 'tag' with arguments */
1253 val = func(argc, (const char **) args);
1254 free(args-1);
1255 } else {
1256 /* unable to parse arguments, undo 0-termination by scanargs */
1257 for (; argc > 0; argc--) {
1258 *((args[argc - 1]) - 1) = ' ';
1259 }
1261 /* next call, try parsing at current offset +1 */
1262 end = (*buf) + i + 1;
1264 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1265 " Check original file. This may have been altered "
1266 "by parsing.]\n\n");
1267 }
1269 /* remember offset where we have to continue parsing */
1270 end_offset = end - (*buf);
1272 valln = 0;
1273 if (val) {
1274 valln = strlen(val);
1275 }
1277 /* Optionally resize buffer to hold the replacement value:
1278 Calculating the new length of the buffer is simple. add current
1279 buffer pos (i) to length of string after replaced tag to length
1280 of replacement string and add 1 for the final zero ... */
1281 if (end - (*buf) < (i + valln)) {
1282 /* make sure we do not shrink the mallocd block */
1283 size_t newbufsize = i + strlen(end) + valln + 1;
1285 *buf = rrd_realloc(*buf, newbufsize);
1287 if (*buf == NULL) {
1288 perror("Realoc buf:");
1289 exit(1);
1290 };
1291 }
1293 /* Update new end pointer:
1294 make sure the 'end' pointer gets moved along with the
1295 buf pointer when realloc moves memory ... */
1296 end = (*buf) + end_offset;
1298 /* splice the variable:
1299 step 1. Shift pending data to make room for 'val' */
1300 memmove((*buf) + i + valln, end, strlen(end) + 1);
1302 /* step 2. Insert val */
1303 if (val) {
1304 memmove((*buf) + i, val, valln);
1305 free(val);
1306 }
1307 return (valln > 0 ? valln - 1 : valln);
1308 }
1310 char *http_time(
1311 time_t *now)
1312 {
1313 struct tm *tmptime;
1314 static char buf[60];
1316 tmptime = gmtime(now);
1317 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1318 return (buf);
1319 }
1321 void rrdcgiHeader(
1322 void)
1323 {
1324 if (rrdcgiType)
1325 printf("Content-type: %s\n", rrdcgiType);
1326 else
1327 printf("Content-type: text/html\n");
1328 if (rrdcgiHeaderString)
1329 printf("%s", rrdcgiHeaderString);
1330 printf("\n");
1331 }
1333 void rrdcgiDebug(
1334 int level,
1335 int where)
1336 {
1337 if (level > 0)
1338 rrdcgiDebugLevel = level;
1339 else
1340 rrdcgiDebugLevel = 0;
1341 if (where)
1342 rrdcgiDebugStderr = 0;
1343 else
1344 rrdcgiDebugStderr = 1;
1345 }
1347 char *rrdcgiDecodeString(
1348 char *text)
1349 {
1350 char *cp, *xp;
1352 for (cp = text, xp = text; *cp; cp++) {
1353 if (*cp == '%') {
1354 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1355 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1356 if (islower(*(cp + 1)))
1357 *(cp + 1) = toupper(*(cp + 1));
1358 if (islower(*(cp + 2)))
1359 *(cp + 2) = toupper(*(cp + 2));
1360 *(xp) =
1361 (*(cp + 1) >=
1362 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1363 (*(cp + 2) >=
1364 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1365 xp++;
1366 cp += 2;
1367 }
1368 } else {
1369 *(xp++) = *cp;
1370 }
1371 }
1372 memset(xp, 0, cp - xp);
1373 return text;
1374 }
1376 /* rrdcgiReadVariables()
1377 *
1378 * Read from stdin if no string is provided via CGI. Variables that
1379 * doesn't have a value associated with it doesn't get stored.
1380 */
1381 s_var **rrdcgiReadVariables(
1382 void)
1383 {
1384 int length;
1385 char *line = NULL;
1386 int numargs;
1387 char *cp, *ip, *esp, *sptr;
1388 s_var **result;
1389 int i, k, len;
1390 char tmp[101];
1392 cp = getenv("REQUEST_METHOD");
1393 ip = getenv("CONTENT_LENGTH");
1395 if (cp && !strcmp(cp, "POST")) {
1396 if (ip) {
1397 length = atoi(ip);
1398 if ((line = (char *) malloc(length + 2)) == NULL)
1399 return NULL;
1400 if (fgets(line, length + 1, stdin) == NULL)
1401 return NULL;
1402 } else
1403 return NULL;
1404 } else if (cp && !strcmp(cp, "GET")) {
1405 esp = getenv("QUERY_STRING");
1406 if (esp && strlen(esp)) {
1407 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1408 return NULL;
1409 sprintf(line, "%s", esp);
1410 } else
1411 return NULL;
1412 } else {
1413 length = 0;
1414 printf("(offline mode: enter name=value pairs on standard input)\n");
1415 memset(tmp, 0, sizeof(tmp));
1416 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1417 if (strlen(tmp)) {
1418 if (tmp[strlen(tmp) - 1] == '\n')
1419 tmp[strlen(tmp) - 1] = '&';
1420 if (length) {
1421 length += strlen(tmp);
1422 len = (length + 1) * sizeof(char);
1423 if ((line = (char *) realloc(line, len)) == NULL)
1424 return NULL;
1425 strcat(line, tmp);
1426 } else {
1427 length = strlen(tmp);
1428 len = (length + 1) * sizeof(char);
1429 if ((line = (char *) malloc(len)) == NULL)
1430 return NULL;
1431 memset(line, 0, len);
1432 strcpy(line, tmp);
1433 }
1434 }
1435 memset(tmp, 0, sizeof(tmp));
1436 }
1437 if (!line)
1438 return NULL;
1439 if (line[strlen(line) - 1] == '&')
1440 line[strlen(line) - 1] = '\0';
1441 }
1443 /*
1444 * From now on all cgi variables are stored in the variable line
1445 * and look like foo=bar&foobar=barfoo&foofoo=
1446 */
1448 if (rrdcgiDebugLevel > 0) {
1449 if (rrdcgiDebugStderr)
1450 fprintf(stderr, "Received cgi input: %s\n", line);
1451 else
1452 printf
1453 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1454 line);
1455 }
1457 for (cp = line; *cp; cp++)
1458 if (*cp == '+')
1459 *cp = ' ';
1461 if (strlen(line)) {
1462 for (numargs = 1, cp = line; *cp; cp++)
1463 if (*cp == '&')
1464 numargs++;
1465 } else
1466 numargs = 0;
1467 if (rrdcgiDebugLevel > 0) {
1468 if (rrdcgiDebugStderr)
1469 fprintf(stderr, "%d cgi variables found.\n", numargs);
1470 else
1471 printf("%d cgi variables found.<br>\n", numargs);
1472 }
1474 len = (numargs + 1) * sizeof(s_var *);
1475 if ((result = (s_var **) malloc(len)) == NULL)
1476 return NULL;
1477 memset(result, 0, len);
1479 cp = line;
1480 i = 0;
1481 while (*cp) {
1482 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1483 *ip = '\0';
1484 } else
1485 ip = cp + strlen(cp);
1487 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1488 cp = ++ip;
1489 continue;
1490 }
1492 if (!strlen(esp)) {
1493 cp = ++ip;
1494 continue;
1495 }
1497 if (i < numargs) {
1499 /* try to find out if there's already such a variable */
1500 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1501 || !(strlen(result[k]->name) ==
1502 (size_t) (esp - cp))); k++);
1504 if (k == i) { /* No such variable yet */
1505 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1506 return NULL;
1507 if ((result[i]->name =
1508 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1509 return NULL;
1510 memset(result[i]->name, 0, esp - cp + 1);
1511 strncpy(result[i]->name, cp, esp - cp);
1512 cp = ++esp;
1513 if ((result[i]->value =
1514 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1515 return NULL;
1516 memset(result[i]->value, 0, ip - esp + 1);
1517 strncpy(result[i]->value, cp, ip - esp);
1518 result[i]->value = rrdcgiDecodeString(result[i]->value);
1519 if (rrdcgiDebugLevel) {
1520 if (rrdcgiDebugStderr)
1521 fprintf(stderr, "%s: %s\n", result[i]->name,
1522 result[i]->value);
1523 else
1524 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1525 result[i]->name, result[i]->value);
1526 }
1527 i++;
1528 } else { /* There is already such a name, suppose a mutiple field */
1529 cp = ++esp;
1530 len =
1531 (strlen(result[k]->value) + (ip - esp) +
1532 2) * sizeof(char);
1533 if ((sptr = (char *) malloc(len)) == NULL)
1534 return NULL;
1535 memset(sptr, 0, len);
1536 sprintf(sptr, "%s\n", result[k]->value);
1537 strncat(sptr, cp, ip - esp);
1538 free(result[k]->value);
1539 result[k]->value = rrdcgiDecodeString(sptr);
1540 }
1541 }
1542 cp = ++ip;
1543 }
1544 return result;
1545 }
1547 /* rrdcgiInit()
1548 *
1549 * Read from stdin if no string is provided via CGI. Variables that
1550 * doesn't have a value associated with it doesn't get stored.
1551 */
1552 s_cgi *rrdcgiInit(
1553 void)
1554 {
1555 s_cgi *res;
1556 s_var **vars;
1558 vars = rrdcgiReadVariables();
1560 if (!vars)
1561 return NULL;
1563 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1564 return NULL;
1565 res->vars = vars;
1567 return res;
1568 }
1570 char *rrdcgiGetValue(
1571 s_cgi * parms,
1572 const char *name)
1573 {
1574 int i;
1576 if (!parms || !parms->vars)
1577 return NULL;
1578 for (i = 0; parms->vars[i]; i++)
1579 if (!strcmp(name, parms->vars[i]->name)) {
1580 if (rrdcgiDebugLevel > 0) {
1581 if (rrdcgiDebugStderr)
1582 fprintf(stderr, "%s found as %s\n", name,
1583 parms->vars[i]->value);
1584 else
1585 printf("%s found as %s<br>\n", name,
1586 parms->vars[i]->value);
1587 }
1588 return parms->vars[i]->value;
1589 }
1590 if (rrdcgiDebugLevel) {
1591 if (rrdcgiDebugStderr)
1592 fprintf(stderr, "%s not found\n", name);
1593 else
1594 printf("%s not found<br>\n", name);
1595 }
1596 return NULL;
1597 }
1599 void rrdcgiFreeList(
1600 char **list)
1601 {
1602 int i;
1604 for (i = 0; list[i] != NULL; i++)
1605 free(list[i]);
1606 free(list);
1607 }
1609 void rrdcgiFree(
1610 s_cgi * parms)
1611 {
1612 int i;
1614 if (!parms)
1615 return;
1616 if (parms->vars) {
1617 for (i = 0; parms->vars[i]; i++) {
1618 if (parms->vars[i]->name)
1619 free(parms->vars[i]->name);
1620 if (parms->vars[i]->value)
1621 free(parms->vars[i]->value);
1622 free(parms->vars[i]);
1623 }
1624 free(parms->vars);
1625 }
1626 free(parms);
1628 if (rrdcgiHeaderString) {
1629 free(rrdcgiHeaderString);
1630 rrdcgiHeaderString = NULL;
1631 }
1632 if (rrdcgiType) {
1633 free(rrdcgiType);
1634 rrdcgiType = NULL;
1635 }
1636 }