1 /*****************************************************************************
2 * RRDtool 1.2.12 Copyright by Tobi Oetiker, 1997-2005
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
7 #include "rrd_tool.h"
10 #define MEMBLK 1024
11 /*#define DEBUG_PARSER
12 #define DEBUG_VARS*/
14 typedef struct var_s {
15 char *name, *value;
16 } s_var;
18 typedef struct cgi_s {
19 s_var **vars;
20 } s_cgi;
22 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
23 and replace by result of arg[2] call */
24 int parse(char **, long, char *, char *(*)(long , const char **));
26 /**************************************************/
27 /* tag replacers ... they are called from parse */
28 /* through function pointers */
29 /**************************************************/
31 /* return cgi var named arg[0] */
32 char* cgiget(long , const char **);
34 /* return a quoted cgi var named arg[0] */
35 char* cgigetq(long , const char **);
37 /* return a quoted and sanitized cgi variable */
38 char* cgigetqp(long , const char **);
40 /* call rrd_graph and insert appropriate image tag */
41 char* drawgraph(long, const char **);
43 /* return PRINT functions from last rrd_graph call */
44 char* drawprint(long, const char **);
46 /* pretty-print the <last></last> value for some.rrd via strftime() */
47 char* printtimelast(long, const char **);
49 /* pretty-print current time */
50 char* printtimenow(long, const char **);
52 /* set an environment variable */
53 char* rrdsetenv(long, const char **);
55 /* get an environment variable */
56 char* rrdgetenv(long, const char **);
58 /* include the named file at this point */
59 char* includefile(long, const char **);
61 /* for how long is the output of the cgi valid ? */
62 char* rrdgoodfor(long, const char **);
64 /* return rrdcgi version string */
65 char* rrdgetinternal(long, const char **);
67 char* rrdstrip(char *buf);
68 char* scanargs(char *line, int *argc, char ***args);
70 /* format at-time specified times using strftime */
71 char* printstrftime(long, const char**);
73 /** HTTP protocol needs special format, and GMT time **/
74 char *http_time(time_t *);
76 /* return a pointer to newly allocated copy of this string */
77 char *stralloc(const char *);
79 /* global variable for rrdcgi */
80 s_cgi *rrdcgiArg;
82 /* rrdcgiHeader
83 *
84 * Prints a valid CGI Header (Content-type...) etc.
85 */
86 void rrdcgiHeader(void);
88 /* rrdcgiDecodeString
89 * decode html escapes
90 */
92 char *rrdcgiDecodeString(char *text);
94 /* rrdcgiDebug
95 *
96 * Set/unsets debugging
97 */
98 void rrdcgiDebug(int level, int where);
100 /* rrdcgiInit
101 *
102 * Reads in variables set via POST or stdin.
103 */
104 s_cgi *rrdcgiInit (void);
106 /* rrdcgiGetValue
107 *
108 * Returns the value of the specified variable or NULL if it's empty
109 * or doesn't exist.
110 */
111 char *rrdcgiGetValue (s_cgi *parms, const char *name);
113 /* rrdcgiFreeList
114 *
115 * Frees a list as returned by rrdcgiGetVariables()
116 */
117 void rrdcgiFreeList (char **list);
119 /* rrdcgiFree
120 *
121 * Frees the internal data structures
122 */
123 void rrdcgiFree (s_cgi *parms);
125 /* rrdcgiReadVariables()
126 *
127 * Read from stdin if no string is provided via CGI. Variables that
128 * doesn't have a value associated with it doesn't get stored.
129 */
130 s_var **rrdcgiReadVariables(void);
133 int rrdcgiDebugLevel = 0;
134 int rrdcgiDebugStderr = 1;
135 char *rrdcgiHeaderString = NULL;
136 char *rrdcgiType = NULL;
138 /* rrd interface to the variable functions {put,get}var() */
139 char* rrdgetvar(long argc, const char **args);
140 char* rrdsetvar(long argc, const char **args);
141 char* rrdsetvarconst(long argc, const char **args);
144 /* variable store: put/get key-value pairs */
145 static int initvar();
146 static void donevar();
147 static const char* getvar(const char* varname);
148 static const char* putvar(const char* name, const char* value, int is_const);
150 /* key value pair that makes up an entry in the variable store */
151 typedef struct
152 {
153 int is_const; /* const variable or not */
154 const char* name; /* variable name */
155 const char* value; /* variable value */
156 } vardata;
158 /* the variable heap:
159 start with a heapsize of 10 variables */
160 #define INIT_VARSTORE_SIZE 10
161 static vardata* varheap = NULL;
162 static size_t varheap_size = 0;
164 /* allocate and initialize variable heap */
165 static int
166 initvar()
167 {
168 varheap = (vardata*)malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
169 if (varheap == NULL) {
170 fprintf(stderr, "ERROR: unable to initialize variable store\n");
171 return -1;
172 }
173 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
174 varheap_size = INIT_VARSTORE_SIZE;
175 return 0;
176 }
178 /* cleanup: free allocated memory */
179 static void
180 donevar()
181 {
182 int i;
183 if (varheap) {
184 for (i=0; i<(int)varheap_size; i++) {
185 if (varheap[i].name) {
186 free((char*)varheap[i].name);
187 }
188 if (varheap[i].value) {
189 free((char*)varheap[i].value);
190 }
191 }
192 free(varheap);
193 }
194 }
196 /* Get a variable from the variable store.
197 Return NULL in case the requested variable was not found. */
198 static const char*
199 getvar(const char* name)
200 {
201 int i;
202 for (i=0; i<(int)varheap_size && varheap[i].name; i++) {
203 if (0 == strcmp(name, varheap[i].name)) {
204 #ifdef DEBUG_VARS
205 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
206 #endif
207 return varheap[i].value;
208 }
209 }
210 #ifdef DEBUG_VARS
211 printf("<!-- getvar(%s) -> Not found-->\n", name);
212 #endif
213 return NULL;
214 }
216 /* Put a variable into the variable store. If a variable by that
217 name exists, it's value is overwritten with the new value unless it was
218 marked as 'const' (initialized by RRD::SETCONSTVAR).
219 Returns a copy the newly allocated value on success, NULL on error. */
220 static const char*
221 putvar(const char* name, const char* value, int is_const)
222 {
223 int i;
224 for (i=0; i < (int)varheap_size && varheap[i].name; i++) {
225 if (0 == strcmp(name, varheap[i].name)) {
226 /* overwrite existing entry */
227 if (varheap[i].is_const) {
228 #ifdef DEBUG_VARS
229 printf("<!-- setver(%s, %s): not assigning: "
230 "const variable -->\n", name, value);
231 # endif
232 return varheap[i].value;
233 }
234 #ifdef DEBUG_VARS
235 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
236 name, value, varheap[i].value);
237 #endif
238 /* make it possible to promote a variable to readonly */
239 varheap[i].is_const = is_const;
240 free((char*)varheap[i].value);
241 varheap[i].value = stralloc(value);
242 return varheap[i].value;
243 }
244 }
246 /* no existing variable found by that name, add it */
247 if (i == (int)varheap_size) {
248 /* ran out of heap: resize heap to double size */
249 size_t new_size = varheap_size * 2;
250 varheap = (vardata*)(realloc(varheap, sizeof(vardata) * new_size));
251 if (!varheap) {
252 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
253 return NULL;
254 }
255 /* initialize newly allocated memory */;
256 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
257 varheap_size = new_size;
258 }
259 varheap[i].is_const = is_const;
260 varheap[i].name = stralloc(name);
261 varheap[i].value = stralloc(value);
263 #ifdef DEBUG_VARS
264 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
265 #endif
266 return varheap[i].value;
267 }
269 /* expand those RRD:* directives that can be used recursivly */
270 static char*
271 rrd_expand_vars(char* buffer)
272 {
273 int i;
275 #ifdef DEBUG_PARSER
276 printf("expanding variables in '%s'\n", buffer);
277 #endif
279 for (i=0; buffer[i]; i++) {
280 if (buffer[i] != '<')
281 continue;
282 parse(&buffer, i, "<RRD::CV", cgiget);
283 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
284 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
285 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
286 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
287 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
288 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
289 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
290 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
291 }
292 return buffer;
293 }
295 static long goodfor=0;
296 static char **calcpr=NULL;
297 static void calfree (void){
298 if (calcpr) {
299 long i;
300 for(i=0;calcpr[i];i++){
301 if (calcpr[i]){
302 free(calcpr[i]);
303 }
304 }
305 if (calcpr) {
306 free(calcpr);
307 }
308 }
309 }
311 /* create freeable version of the string */
312 char * stralloc(const char *str){
313 char* nstr;
314 if (!str) {
315 return NULL;
316 }
317 nstr = malloc((strlen(str)+1));
318 strcpy(nstr,str);
319 return(nstr);
320 }
322 int main(int argc, char *argv[]) {
323 long length;
324 char *buffer;
325 char *server_url = NULL;
326 long i;
327 long filter=0;
328 #ifdef MUST_DISABLE_SIGFPE
329 signal(SIGFPE,SIG_IGN);
330 #endif
331 #ifdef MUST_DISABLE_FPMASK
332 fpsetmask(0);
333 #endif
334 optind = 0; opterr = 0; /* initialize getopt */
336 /* what do we get for cmdline arguments?
337 for (i=0;i<argc;i++)
338 printf("%d-'%s'\n",i,argv[i]); */
339 while (1) {
340 static struct option long_options[] = {
341 { "filter", no_argument, 0, 'f' },
342 { 0, 0, 0, 0}
343 };
344 int option_index = 0;
345 int opt;
346 opt = getopt_long(argc, argv, "f", long_options, &option_index);
347 if (opt == EOF) {
348 break;
349 }
351 switch(opt) {
352 case 'f':
353 filter=1;
354 break;
355 case '?':
356 printf("unknown commandline option '%s'\n",argv[optind-1]);
357 return -1;
358 }
359 }
361 if (!filter) {
362 rrdcgiDebug(0,0);
363 rrdcgiArg = rrdcgiInit();
364 server_url = getenv("SERVER_URL");
365 }
367 /* make sure we have one extra argument,
368 if there are others, we do not care Apache gives several */
370 /* if ( (optind != argc-2
371 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
372 && optind != argc-1) { */
374 if ( optind >= argc ) {
375 fprintf(stderr, "ERROR: expected a filename\n");
376 exit(1);
377 } else {
378 length = readfile(argv[optind], &buffer, 1);
379 }
381 if(rrd_test_error()) {
382 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
383 exit(1);
384 }
386 /* initialize variable heap */
387 initvar();
389 #ifdef DEBUG_PARSER
390 /* some fake header for testing */
391 printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
392 #endif
395 /* expand rrd directives in buffer recursivly */
396 for (i=0; buffer[i]; i++) {
397 if (buffer[i] != '<')
398 continue;
399 if (!filter) {
400 parse(&buffer, i, "<RRD::CV", cgiget);
401 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
402 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
403 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
404 }
405 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
406 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
407 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
408 parse(&buffer, i, "<RRD::INCLUDE", includefile);
409 parse(&buffer, i, "<RRD::PRINT", drawprint);
410 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
411 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
412 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
413 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
414 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
415 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
416 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
417 }
419 if (!filter) {
420 printf ("Content-Type: text/html\n"
421 "Content-Length: %d\n",
422 strlen(buffer));
424 if (labs(goodfor) > 0) {
425 time_t now;
426 now = time(NULL);
427 printf("Last-Modified: %s\n", http_time(&now));
428 now += labs(goodfor);
429 printf("Expires: %s\n", http_time(&now));
430 if (goodfor < 0) {
431 printf("Refresh: %ld\n", labs(goodfor));
432 }
433 }
434 printf("\n");
435 }
437 /* output result */
438 printf("%s", buffer);
440 /* cleanup */
441 calfree();
442 if (buffer){
443 free(buffer);
444 }
445 donevar();
446 exit(0);
447 }
449 /* remove occurrences of .. this is a general measure to make
450 paths which came in via cgi do not go UP ... */
452 char* rrdsetenv(long argc, const char **args) {
453 if (argc >= 2) {
454 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
455 if (xyz == NULL) {
456 return stralloc("[ERROR: allocating setenv buffer]");
457 };
458 sprintf(xyz, "%s=%s", args[0], args[1]);
459 if(putenv(xyz) == -1) {
460 free(xyz);
461 return stralloc("[ERROR: failed to do putenv]");
462 };
463 return stralloc("");
464 }
465 return stralloc("[ERROR: setenv failed because not enough "
466 "arguments were defined]");
467 }
469 /* rrd interface to the variable function putvar() */
470 char*
471 rrdsetvar(long argc, const char **args)
472 {
473 if (argc >= 2)
474 {
475 const char* result = putvar(args[0], args[1], 0 /* not const */);
476 if (result) {
477 /* setvar does not return the value set */
478 return stralloc("");
479 }
480 return stralloc("[ERROR: putvar failed]");
481 }
482 return stralloc("[ERROR: putvar failed because not enough arguments "
483 "were defined]");
484 }
486 /* rrd interface to the variable function putvar() */
487 char*
488 rrdsetvarconst(long argc, const char **args)
489 {
490 if (argc >= 2)
491 {
492 const char* result = putvar(args[0], args[1], 1 /* const */);
493 if (result) {
494 /* setvar does not return the value set */
495 return stralloc("");
496 }
497 return stralloc("[ERROR: putvar failed]");
498 }
499 return stralloc("[ERROR: putvar failed because not enough arguments "
500 "were defined]");
501 }
503 char* rrdgetenv(long argc, const char **args) {
504 char buf[128];
505 const char* envvar;
506 if (argc != 1) {
507 return stralloc("[ERROR: getenv failed because it did not "
508 "get 1 argument only]");
509 };
510 envvar = getenv(args[0]);
511 if (envvar) {
512 return stralloc(envvar);
513 } else {
514 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
515 return stralloc(buf);
516 }
517 }
519 char* rrdgetvar(long argc, const char **args) {
520 char buf[128];
521 const char* value;
522 if (argc != 1) {
523 return stralloc("[ERROR: getvar failed because it did not "
524 "get 1 argument only]");
525 };
526 value = getvar(args[0]);
527 if (value) {
528 return stralloc(value);
529 } else {
530 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
531 return stralloc(buf);
532 }
533 }
535 char* rrdgoodfor(long argc, const char **args){
536 if (argc == 1) {
537 goodfor = atol(args[0]);
538 } else {
539 return stralloc("[ERROR: goodfor expected 1 argument]");
540 }
542 if (goodfor == 0){
543 return stralloc("[ERROR: goodfor value must not be 0]");
544 }
546 return stralloc("");
547 }
549 char* rrdgetinternal(long argc, const char **args){
550 if (argc == 1) {
551 if( strcasecmp( args[0], "VERSION") == 0) {
552 return stralloc(PACKAGE_VERSION);
553 } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
554 return stralloc(__DATE__ " " __TIME__);
555 } else {
556 return stralloc("[ERROR: internal unknown argument]");
557 }
558 } else {
559 return stralloc("[ERROR: internal expected 1 argument]");
560 }
561 }
563 /* Format start or end times using strftime. We always need both the
564 * start and end times, because, either might be relative to the other.
565 * */
566 #define MAX_STRFTIME_SIZE 256
567 char* printstrftime(long argc, const char **args){
568 struct rrd_time_value start_tv, end_tv;
569 char *parsetime_error = NULL;
570 char formatted[MAX_STRFTIME_SIZE];
571 struct tm *the_tm;
572 time_t start_tmp, end_tmp;
574 /* Make sure that we were given the right number of args */
575 if( argc != 4) {
576 rrd_set_error( "wrong number of args %d", argc);
577 return (char *) -1;
578 }
580 /* Init start and end time */
581 parsetime("end-24h", &start_tv);
582 parsetime("now", &end_tv);
584 /* Parse the start and end times we were given */
585 if( (parsetime_error = parsetime( args[1], &start_tv))) {
586 rrd_set_error( "start time: %s", parsetime_error);
587 return (char *) -1;
588 }
589 if( (parsetime_error = parsetime( args[2], &end_tv))) {
590 rrd_set_error( "end time: %s", parsetime_error);
591 return (char *) -1;
592 }
593 if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
594 return (char *) -1;
595 }
597 /* Do we do the start or end */
598 if( strcasecmp( args[0], "START") == 0) {
599 the_tm = localtime( &start_tmp);
600 }
601 else if( strcasecmp( args[0], "END") == 0) {
602 the_tm = localtime( &end_tmp);
603 }
604 else {
605 rrd_set_error( "start/end not found in '%s'", args[0]);
606 return (char *) -1;
607 }
609 /* now format it */
610 if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
611 return( stralloc( formatted));
612 }
613 else {
614 rrd_set_error( "strftime failed");
615 return (char *) -1;
616 }
617 }
619 char* includefile(long argc, const char **args){
620 char *buffer;
621 if (argc >= 1) {
622 const char* filename = args[0];
623 readfile(filename, &buffer, 0);
624 if (rrd_test_error()) {
625 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
626 sprintf(err, "[ERROR: %s]",rrd_get_error());
627 rrd_clear_error();
628 return err;
629 } else {
630 return buffer;
631 }
632 }
633 else
634 {
635 return stralloc("[ERROR: No Inclue file defined]");
636 }
637 }
639 /* make a copy of buf and replace open/close brackets with '_' */
640 char* rrdstrip(char *buf) {
641 char* p;
642 if (buf == NULL) {
643 return NULL;
644 }
645 /* make a copy of the buffer */
646 buf = stralloc(buf);
647 if (buf == NULL) {
648 return NULL;
649 }
651 p = buf;
652 while (*p) {
653 if (*p == '<' || *p == '>') {
654 *p = '_';
655 }
656 p++;
657 }
658 return buf;
659 }
661 char* cgigetq(long argc, const char **args){
662 if (argc>= 1){
663 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
664 char *buf2;
665 char *c,*d;
666 int qc=0;
667 if (buf==NULL) return NULL;
669 for(c=buf;*c != '\0';c++)
670 if (*c == '"') qc++;
671 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
672 perror("Malloc Buffer");
673 exit(1);
674 };
675 c=buf;
676 d=buf2;
677 *(d++) = '"';
678 while(*c != '\0'){
679 if (*c == '"') {
680 *(d++) = '"';
681 *(d++) = '\'';
682 *(d++) = '"';
683 *(d++) = '\'';
684 }
685 *(d++) = *(c++);
686 }
687 *(d++) = '"';
688 *(d) = '\0';
689 free(buf);
690 return buf2;
691 }
693 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
694 }
696 /* remove occurrences of .. this is a general measure to make
697 paths which came in via cgi do not go UP ... */
699 char* cgigetqp(long argc, const char **args){
700 char* buf;
701 char* buf2;
702 char* p;
703 char* d;
705 if (argc < 1)
706 {
707 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
708 }
710 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
711 if (!buf)
712 {
713 return NULL;
714 }
716 buf2 = malloc(strlen(buf)+1);
717 if (!buf2)
718 {
719 perror("cgigetqp(): Malloc Path Buffer");
720 exit(1);
721 };
723 p = buf;
724 d = buf2;
726 while (*p)
727 {
728 /* prevent mallicious paths from entering the system */
729 if (p[0] == '.' && p[1] == '.')
730 {
731 p += 2;
732 *d++ = '_';
733 *d++ = '_';
734 }
735 else
736 {
737 *d++ = *p++;
738 }
739 }
741 *d = 0;
742 free(buf);
744 /* Make sure the path is relative, e.g. does not start with '/' */
745 p = buf2;
746 while ('/' == *p)
747 {
748 *p++ = '_';
749 }
751 return buf2;
752 }
755 char* cgiget(long argc, const char **args){
756 if (argc>= 1)
757 return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
758 else
759 return stralloc("[ERROR: not enough arguments for RRD::CV]");
760 }
764 char* drawgraph(long argc, const char **args){
765 int i,xsize, ysize;
766 double ymin,ymax;
767 for(i=0;i<argc;i++)
768 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
769 if(i==argc) {
770 args[argc++] = "--imginfo";
771 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
772 }
773 calfree();
774 if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
775 return stralloc(calcpr[0]);
776 } else {
777 if (rrd_test_error()) {
778 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
779 sprintf(err, "[ERROR: %s]",rrd_get_error());
780 rrd_clear_error();
781 calfree();
782 return err;
783 }
784 }
785 return NULL;
786 }
788 char* drawprint(long argc, const char **args){
789 if (argc==1 && calcpr){
790 long i=0;
791 while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
792 if (atol(args[0])<i-1)
793 return stralloc(calcpr[atol(args[0])+1]);
794 }
795 return stralloc("[ERROR: RRD::PRINT argument error]");
796 }
798 char* printtimelast(long argc, const char **args) {
799 time_t last;
800 struct tm tm_last;
801 char *buf;
802 if ( argc == 2 ) {
803 buf = malloc(255);
804 if (buf == NULL){
805 return stralloc("[ERROR: allocating strftime buffer]");
806 };
807 last = rrd_last(argc+1, (char **) args-1);
808 if (rrd_test_error()) {
809 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
810 sprintf(err, "[ERROR: %s]",rrd_get_error());
811 rrd_clear_error();
812 return err;
813 }
814 tm_last = *localtime(&last);
815 strftime(buf,254,args[1],&tm_last);
816 return buf;
817 }
818 if ( argc < 2 ) {
819 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
820 }
821 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
822 }
824 char* printtimenow(long argc, const char **args) {
825 time_t now = time(NULL);
826 struct tm tm_now;
827 char *buf;
828 if ( argc == 1 ) {
829 buf = malloc(255);
830 if (buf == NULL){
831 return stralloc("[ERROR: allocating strftime buffer]");
832 };
833 tm_now = *localtime(&now);
834 strftime(buf,254,args[0],&tm_now);
835 return buf;
836 }
837 if ( argc < 1 ) {
838 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
839 }
840 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
841 }
843 /* Scan buffer until an unescaped '>' arives.
844 * Update argument array with arguments found.
845 * Return end cursor where parsing stopped, or NULL in case of failure.
846 *
847 * FIXME:
848 * To allow nested constructs, we call rrd_expand_vars() for arguments
849 * that contain RRD::x directives. These introduce a small memory leak
850 * since we have to stralloc the arguments the way parse() works.
851 */
852 char*
853 scanargs(char *line, int *argument_count, char ***arguments)
854 {
855 char *getP; /* read cursor */
856 char *putP; /* write cursor */
857 char Quote; /* type of quote if in quoted string, 0 otherwise */
858 int tagcount; /* open tag count */
859 int in_arg; /* if we currently are parsing an argument or not */
860 int argsz; /* argument array size */
861 int curarg_contains_rrd_directives;
863 /* local array of arguments while parsing */
864 int argc = 0;
865 char** argv;
867 #ifdef DEBUG_PARSER
868 printf("<-- scanargs(%s) -->\n", line);
869 #endif
871 *arguments = NULL;
872 *argument_count = 0;
874 /* create initial argument array of char pointers */
875 argsz = 32;
876 argv = (char **)malloc(argsz * sizeof(char *));
877 if (!argv) {
878 return NULL;
879 }
881 /* skip leading blanks */
882 while (isspace((int)*line)) {
883 line++;
884 }
886 getP = line;
887 putP = line;
889 Quote = 0;
890 in_arg = 0;
891 tagcount = 0;
893 curarg_contains_rrd_directives = 0;
895 /* start parsing 'line' for arguments */
896 while (*getP)
897 {
898 unsigned char c = *getP++;
900 if (c == '>' && !Quote && !tagcount) {
901 /* this is our closing tag, quit scanning */
902 break;
903 }
905 /* remove all special chars */
906 if (c < ' ') {
907 c = ' ';
908 }
910 switch (c)
911 {
912 case ' ':
913 if (Quote || tagcount) {
914 /* copy quoted/tagged (=RRD expanded) string */
915 *putP++ = c;
916 }
917 else if (in_arg)
918 {
919 /* end argument string */
920 *putP++ = 0;
921 in_arg = 0;
922 if (curarg_contains_rrd_directives) {
923 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
924 curarg_contains_rrd_directives = 0;
925 }
926 }
927 break;
929 case '"': /* Fall through */
930 case '\'':
931 if (Quote != 0) {
932 if (Quote == c) {
933 Quote = 0;
934 } else {
935 /* copy quoted string */
936 *putP++ = c;
937 }
938 } else {
939 if (!in_arg) {
940 /* reference start of argument string in argument array */
941 argv[argc++] = putP;
942 in_arg=1;
943 }
944 Quote = c;
945 }
946 break;
948 default:
949 if (!in_arg) {
950 /* start new argument */
951 argv[argc++] = putP;
952 in_arg = 1;
953 }
954 if (c == '>') {
955 if (tagcount) {
956 tagcount--;
957 }
958 }
959 if (c == '<') {
960 tagcount++;
961 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
962 curarg_contains_rrd_directives = 1;
963 }
964 }
965 *putP++ = c;
966 break;
967 }
969 /* check if our argument array is still large enough */
970 if (argc == argsz) {
971 /* resize argument array */
972 argsz *= 2;
973 argv = rrd_realloc(argv, argsz * sizeof(char *));
974 if (*argv == NULL) {
975 return NULL;
976 }
977 }
978 }
980 /* terminate last argument found */
981 *putP = '\0';
982 if (curarg_contains_rrd_directives) {
983 argv[argc-1] = rrd_expand_vars(stralloc(argv[argc-1]));
984 }
986 #ifdef DEBUG_PARSER
987 if (argc > 0) {
988 int n;
989 printf("<-- arguments found [%d]\n", argc);
990 for (n=0; n<argc; n++) {
991 printf("arg %02d: '%s'\n", n, argv[n]);
992 }
993 printf("-->\n");
994 } else {
995 printf("<!-- No arguments found -->\n");
996 }
997 #endif
999 /* update caller's notion of the argument array and it's size */
1000 *arguments = argv;
1001 *argument_count = argc;
1003 if (Quote) {
1004 return NULL;
1005 }
1007 /* Return new scanning cursor:
1008 pointer to char after closing bracket */
1009 return getP;
1010 }
1013 /*
1014 * Parse(): scan current portion of buffer for given tag.
1015 * If found, parse tag arguments and call 'func' for it.
1016 * The result of func is inserted at the current position
1017 * in the buffer.
1018 */
1019 int
1020 parse(
1021 char **buf, /* buffer */
1022 long i, /* offset in buffer */
1023 char *tag, /* tag to handle */
1024 char *(*func)(long , const char **) /* function to call for 'tag' */
1025 )
1026 {
1027 /* the name of the vairable ... */
1028 char *val;
1029 long valln;
1030 char **args;
1031 char *end;
1032 long end_offset;
1033 int argc;
1034 size_t taglen = strlen(tag);
1036 /* Current position in buffer should start with 'tag' */
1037 if (strncmp((*buf)+i, tag, taglen) != 0) {
1038 return 0;
1039 }
1040 /* .. and match exactly (a whitespace following 'tag') */
1041 if (! isspace(*((*buf) + i + taglen)) ) {
1042 return 0;
1043 }
1045 #ifdef DEBUG_PARSER
1046 printf("parse(): handling tag '%s'\n", tag);
1047 #endif
1049 /* Scan for arguments following the tag;
1050 scanargs() puts \0 into *buf ... so after scanargs it is probably
1051 not a good time to use strlen on buf */
1052 end = scanargs((*buf) + i + taglen, &argc, &args);
1053 if (end)
1054 {
1055 /* got arguments, call function for 'tag' with arguments */
1056 val = func(argc, (const char **) args);
1057 free(args);
1058 }
1059 else
1060 {
1061 /* unable to parse arguments, undo 0-termination by scanargs */
1062 for (; argc > 0; argc--) {
1063 *((args[argc-1])-1) = ' ';
1064 }
1066 /* next call, try parsing at current offset +1 */
1067 end = (*buf) + i + 1;
1069 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1070 " Check original file. This may have been altered "
1071 "by parsing.]\n\n");
1072 }
1074 /* remember offset where we have to continue parsing */
1075 end_offset = end - (*buf);
1077 valln = 0;
1078 if (val) {
1079 valln = strlen(val);
1080 }
1082 /* Optionally resize buffer to hold the replacement value:
1083 Calculating the new length of the buffer is simple. add current
1084 buffer pos (i) to length of string after replaced tag to length
1085 of replacement string and add 1 for the final zero ... */
1086 if (end - (*buf) < (i + valln)) {
1087 /* make sure we do not shrink the mallocd block */
1088 size_t newbufsize = i + strlen(end) + valln + 1;
1089 *buf = rrd_realloc(*buf, newbufsize);
1091 if (*buf == NULL) {
1092 perror("Realoc buf:");
1093 exit(1);
1094 };
1095 }
1097 /* Update new end pointer:
1098 make sure the 'end' pointer gets moved along with the
1099 buf pointer when realloc moves memory ... */
1100 end = (*buf) + end_offset;
1102 /* splice the variable:
1103 step 1. Shift pending data to make room for 'val' */
1104 memmove((*buf) + i + valln, end, strlen(end) + 1);
1106 /* step 2. Insert val */
1107 if (val) {
1108 memmove((*buf)+i, val, valln);
1109 free(val);
1110 }
1111 return (valln > 0 ? valln-1: valln);
1112 }
1114 char *
1115 http_time(time_t *now) {
1116 struct tm *tmptime;
1117 static char buf[60];
1119 tmptime=gmtime(now);
1120 strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
1121 return(buf);
1122 }
1124 void rrdcgiHeader(void)
1125 {
1126 if (rrdcgiType)
1127 printf ("Content-type: %s\n", rrdcgiType);
1128 else
1129 printf ("Content-type: text/html\n");
1130 if (rrdcgiHeaderString)
1131 printf ("%s", rrdcgiHeaderString);
1132 printf ("\n");
1133 }
1135 void rrdcgiDebug(int level, int where)
1136 {
1137 if (level > 0)
1138 rrdcgiDebugLevel = level;
1139 else
1140 rrdcgiDebugLevel = 0;
1141 if (where)
1142 rrdcgiDebugStderr = 0;
1143 else
1144 rrdcgiDebugStderr = 1;
1145 }
1147 char *rrdcgiDecodeString(char *text)
1148 {
1149 char *cp, *xp;
1151 for (cp=text,xp=text; *cp; cp++) {
1152 if (*cp == '%') {
1153 if (strchr("0123456789ABCDEFabcdef", *(cp+1))
1154 && strchr("0123456789ABCDEFabcdef", *(cp+2))) {
1155 if (islower(*(cp+1)))
1156 *(cp+1) = toupper(*(cp+1));
1157 if (islower(*(cp+2)))
1158 *(cp+2) = toupper(*(cp+2));
1159 *(xp) = (*(cp+1) >= 'A' ? *(cp+1) - 'A' + 10 : *(cp+1) - '0' ) * 16
1160 + (*(cp+2) >= 'A' ? *(cp+2) - 'A' + 10 : *(cp+2) - '0');
1161 xp++;cp+=2;
1162 }
1163 } else {
1164 *(xp++) = *cp;
1165 }
1166 }
1167 memset(xp, 0, cp-xp);
1168 return text;
1169 }
1171 /* rrdcgiReadVariables()
1172 *
1173 * Read from stdin if no string is provided via CGI. Variables that
1174 * doesn't have a value associated with it doesn't get stored.
1175 */
1176 s_var **rrdcgiReadVariables(void)
1177 {
1178 int length;
1179 char *line = NULL;
1180 int numargs;
1181 char *cp, *ip, *esp, *sptr;
1182 s_var **result;
1183 int i, k, len;
1184 char tmp[101];
1186 cp = getenv("REQUEST_METHOD");
1187 ip = getenv("CONTENT_LENGTH");
1189 if (cp && !strcmp(cp, "POST")) {
1190 if (ip) {
1191 length = atoi(ip);
1192 if ((line = (char *)malloc (length+2)) == NULL)
1193 return NULL;
1194 fgets(line, length+1, stdin);
1195 } else
1196 return NULL;
1197 } else if (cp && !strcmp(cp, "GET")) {
1198 esp = getenv("QUERY_STRING");
1199 if (esp && strlen(esp)) {
1200 if ((line = (char *)malloc (strlen(esp)+2)) == NULL)
1201 return NULL;
1202 sprintf (line, "%s", esp);
1203 } else
1204 return NULL;
1205 } else {
1206 length = 0;
1207 printf ("(offline mode: enter name=value pairs on standard input)\n");
1208 memset (tmp, 0, sizeof(tmp));
1209 while((cp = fgets (tmp, 100, stdin)) != NULL) {
1210 if (strlen(tmp)) {
1211 if (tmp[strlen(tmp)-1] == '\n')
1212 tmp[strlen(tmp)-1] = '&';
1213 if (length) {
1214 length += strlen(tmp);
1215 len = (length+1) * sizeof(char);
1216 if ((line = (char *)realloc (line, len)) == NULL)
1217 return NULL;
1218 strcat (line, tmp);
1219 } else {
1220 length = strlen(tmp);
1221 len = (length+1) * sizeof(char);
1222 if ((line = (char *)malloc (len)) == NULL)
1223 return NULL;
1224 memset (line, 0, len);
1225 strcpy (line, tmp);
1226 }
1227 }
1228 memset (tmp, 0, sizeof(tmp));
1229 }
1230 if (!line)
1231 return NULL;
1232 if (line[strlen(line)-1] == '&')
1233 line[strlen(line)-1] = '\0';
1234 }
1236 /*
1237 * From now on all cgi variables are stored in the variable line
1238 * and look like foo=bar&foobar=barfoo&foofoo=
1239 */
1241 if (rrdcgiDebugLevel > 0) {
1242 if (rrdcgiDebugStderr)
1243 fprintf (stderr, "Received cgi input: %s\n", line);
1244 else
1245 printf ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n", line);
1246 }
1248 for (cp=line; *cp; cp++)
1249 if (*cp == '+')
1250 *cp = ' ';
1252 if (strlen(line)) {
1253 for (numargs=1,cp=line; *cp; cp++)
1254 if (*cp == '&') numargs++;
1255 } else
1256 numargs = 0;
1257 if (rrdcgiDebugLevel > 0) {
1258 if (rrdcgiDebugStderr)
1259 fprintf (stderr, "%d cgi variables found.\n", numargs);
1260 else
1261 printf ("%d cgi variables found.<br>\n", numargs);
1262 }
1264 len = (numargs+1) * sizeof(s_var *);
1265 if ((result = (s_var **)malloc (len)) == NULL)
1266 return NULL;
1267 memset (result, 0, len);
1269 cp = line;
1270 i=0;
1271 while (*cp) {
1272 if ((ip = (char *)strchr(cp, '&')) != NULL) {
1273 *ip = '\0';
1274 }else
1275 ip = cp + strlen(cp);
1277 if ((esp=(char *)strchr(cp, '=')) == NULL) {
1278 cp = ++ip;
1279 continue;
1280 }
1282 if (!strlen(esp)) {
1283 cp = ++ip;
1284 continue;
1285 }
1287 if (i<numargs) {
1289 /* try to find out if there's already such a variable */
1290 for (k=0; k<i && (strncmp (result[k]->name,cp, esp-cp) || !(strlen (result[k]->name) == esp-cp)); k++);
1292 if (k == i) { /* No such variable yet */
1293 if ((result[i] = (s_var *)malloc(sizeof(s_var))) == NULL)
1294 return NULL;
1295 if ((result[i]->name = (char *)malloc((esp-cp+1) * sizeof(char))) == NULL)
1296 return NULL;
1297 memset (result[i]->name, 0, esp-cp+1);
1298 strncpy(result[i]->name, cp, esp-cp);
1299 cp = ++esp;
1300 if ((result[i]->value = (char *)malloc((ip-esp+1) * sizeof(char))) == NULL)
1301 return NULL;
1302 memset (result[i]->value, 0, ip-esp+1);
1303 strncpy(result[i]->value, cp, ip-esp);
1304 result[i]->value = rrdcgiDecodeString(result[i]->value);
1305 if (rrdcgiDebugLevel) {
1306 if (rrdcgiDebugStderr)
1307 fprintf (stderr, "%s: %s\n", result[i]->name, result[i]->value);
1308 else
1309 printf ("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n", result[i]->name, result[i]->value);
1310 }
1311 i++;
1312 } else { /* There is already such a name, suppose a mutiple field */
1313 cp = ++esp;
1314 len = (strlen(result[k]->value)+(ip-esp)+2) * sizeof (char);
1315 if ((sptr = (char *)malloc(len)) == NULL)
1316 return NULL;
1317 memset (sptr, 0, len);
1318 sprintf (sptr, "%s\n", result[k]->value);
1319 strncat(sptr, cp, ip-esp);
1320 free(result[k]->value);
1321 result[k]->value = rrdcgiDecodeString (sptr);
1322 }
1323 }
1324 cp = ++ip;
1325 }
1326 return result;
1327 }
1329 /* rrdcgiInit()
1330 *
1331 * Read from stdin if no string is provided via CGI. Variables that
1332 * doesn't have a value associated with it doesn't get stored.
1333 */
1334 s_cgi *rrdcgiInit(void)
1335 {
1336 s_cgi *res;
1337 s_var **vars;
1339 vars = rrdcgiReadVariables();
1341 if (!vars)
1342 return NULL;
1344 if ((res = (s_cgi *)malloc (sizeof (s_cgi))) == NULL)
1345 return NULL;
1346 res->vars = vars;
1348 return res;
1349 }
1351 char *rrdcgiGetValue(s_cgi *parms, const char *name)
1352 {
1353 int i;
1355 if (!parms || !parms->vars)
1356 return NULL;
1357 for (i=0;parms->vars[i]; i++)
1358 if (!strcmp(name,parms->vars[i]->name)) {
1359 if (rrdcgiDebugLevel > 0) {
1360 if (rrdcgiDebugStderr)
1361 fprintf (stderr, "%s found as %s\n", name, parms->vars[i]->value);
1362 else
1363 printf ("%s found as %s<br>\n", name, parms->vars[i]->value);
1364 }
1365 return parms->vars[i]->value;
1366 }
1367 if (rrdcgiDebugLevel) {
1368 if (rrdcgiDebugStderr)
1369 fprintf (stderr, "%s not found\n", name);
1370 else
1371 printf ("%s not found<br>\n", name);
1372 }
1373 return NULL;
1374 }
1376 void rrdcgiFreeList (char **list)
1377 {
1378 int i;
1380 for (i=0; list[i] != NULL; i++)
1381 free (list[i]);
1382 free (list);
1383 }
1385 void rrdcgiFree (s_cgi *parms)
1386 {
1387 int i;
1389 if (!parms)
1390 return;
1391 if (parms->vars) {
1392 for (i=0;parms->vars[i]; i++) {
1393 if (parms->vars[i]->name)
1394 free (parms->vars[i]->name);
1395 if (parms->vars[i]->value)
1396 free (parms->vars[i]->value);
1397 free (parms->vars[i]);
1398 }
1399 free (parms->vars);
1400 }
1401 free (parms);
1403 if (rrdcgiHeaderString) {
1404 free (rrdcgiHeaderString);
1405 rrdcgiHeaderString = NULL;
1406 }
1407 if (rrdcgiType) {
1408 free (rrdcgiType);
1409 rrdcgiType = NULL;
1410 }
1411 }