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