9f0a0bd879afe822e28e9ec759a782955a597bda
1 /*****************************************************************************
2 * RRDtool 1.2.29 Copyright by Tobi Oetiker, 1997-2008
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 calcpr=NULL;
309 }
310 }
312 /* create freeable version of the string */
313 char * stralloc(const char *str){
314 char* nstr;
315 if (!str) {
316 return NULL;
317 }
318 nstr = malloc((strlen(str)+1));
319 strcpy(nstr,str);
320 return(nstr);
321 }
323 int main(int argc, char *argv[]) {
324 long length;
325 char *buffer;
326 char *server_url = NULL;
327 long i;
328 long filter=0;
329 #ifdef MUST_DISABLE_SIGFPE
330 signal(SIGFPE,SIG_IGN);
331 #endif
332 #ifdef MUST_DISABLE_FPMASK
333 fpsetmask(0);
334 #endif
335 optind = 0; opterr = 0; /* initialize getopt */
337 /* what do we get for cmdline arguments?
338 for (i=0;i<argc;i++)
339 printf("%d-'%s'\n",i,argv[i]); */
340 while (1) {
341 static struct option long_options[] = {
342 { "filter", no_argument, 0, 'f' },
343 { 0, 0, 0, 0}
344 };
345 int option_index = 0;
346 int opt;
347 opt = getopt_long(argc, argv, "f", long_options, &option_index);
348 if (opt == EOF) {
349 break;
350 }
352 switch(opt) {
353 case 'f':
354 filter=1;
355 break;
356 case '?':
357 printf("unknown commandline option '%s'\n",argv[optind-1]);
358 return -1;
359 }
360 }
362 if (!filter) {
363 rrdcgiDebug(0,0);
364 rrdcgiArg = rrdcgiInit();
365 server_url = getenv("SERVER_URL");
366 }
368 /* make sure we have one extra argument,
369 if there are others, we do not care Apache gives several */
371 /* if ( (optind != argc-2
372 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
373 && optind != argc-1) { */
375 if ( optind >= argc ) {
376 fprintf(stderr, "ERROR: expected a filename\n");
377 exit(1);
378 } else {
379 length = readfile(argv[optind], &buffer, 1);
380 }
382 if(rrd_test_error()) {
383 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
384 exit(1);
385 }
387 /* initialize variable heap */
388 initvar();
390 #ifdef DEBUG_PARSER
391 /* some fake header for testing */
392 printf ("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
393 #endif
396 /* expand rrd directives in buffer recursivly */
397 for (i=0; buffer[i]; i++) {
398 if (buffer[i] != '<')
399 continue;
400 if (!filter) {
401 parse(&buffer, i, "<RRD::CV", cgiget);
402 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
403 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
404 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
405 }
406 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
407 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
408 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
409 parse(&buffer, i, "<RRD::INCLUDE", includefile);
410 parse(&buffer, i, "<RRD::PRINT", drawprint);
411 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
412 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
413 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
414 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
415 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
416 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
417 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
418 }
420 if (!filter) {
421 printf ("Content-Type: text/html\n"
422 "Content-Length: %d\n",
423 strlen(buffer));
425 if (labs(goodfor) > 0) {
426 time_t now;
427 now = time(NULL);
428 printf("Last-Modified: %s\n", http_time(&now));
429 now += labs(goodfor);
430 printf("Expires: %s\n", http_time(&now));
431 if (goodfor < 0) {
432 printf("Refresh: %ld\n", labs(goodfor));
433 }
434 }
435 printf("\n");
436 }
438 /* output result */
439 printf("%s", buffer);
441 /* cleanup */
442 calfree();
443 if (buffer){
444 free(buffer);
445 }
446 donevar();
447 exit(0);
448 }
450 /* remove occurrences of .. this is a general measure to make
451 paths which came in via cgi do not go UP ... */
453 char* rrdsetenv(long argc, const char **args) {
454 if (argc >= 2) {
455 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
456 if (xyz == NULL) {
457 return stralloc("[ERROR: allocating setenv buffer]");
458 };
459 sprintf(xyz, "%s=%s", args[0], args[1]);
460 if(putenv(xyz) == -1) {
461 free(xyz);
462 return stralloc("[ERROR: failed to do putenv]");
463 };
464 return stralloc("");
465 }
466 return stralloc("[ERROR: setenv failed because not enough "
467 "arguments were defined]");
468 }
470 /* rrd interface to the variable function putvar() */
471 char*
472 rrdsetvar(long argc, const char **args)
473 {
474 if (argc >= 2)
475 {
476 const char* result = putvar(args[0], args[1], 0 /* not const */);
477 if (result) {
478 /* setvar does not return the value set */
479 return stralloc("");
480 }
481 return stralloc("[ERROR: putvar failed]");
482 }
483 return stralloc("[ERROR: putvar failed because not enough arguments "
484 "were defined]");
485 }
487 /* rrd interface to the variable function putvar() */
488 char*
489 rrdsetvarconst(long argc, const char **args)
490 {
491 if (argc >= 2)
492 {
493 const char* result = putvar(args[0], args[1], 1 /* const */);
494 if (result) {
495 /* setvar does not return the value set */
496 return stralloc("");
497 }
498 return stralloc("[ERROR: putvar failed]");
499 }
500 return stralloc("[ERROR: putvar failed because not enough arguments "
501 "were defined]");
502 }
504 char* rrdgetenv(long argc, const char **args) {
505 char buf[128];
506 const char* envvar;
507 if (argc != 1) {
508 return stralloc("[ERROR: getenv failed because it did not "
509 "get 1 argument only]");
510 };
511 envvar = getenv(args[0]);
512 if (envvar) {
513 return stralloc(envvar);
514 } else {
515 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
516 return stralloc(buf);
517 }
518 }
520 char* rrdgetvar(long argc, const char **args) {
521 char buf[128];
522 const char* value;
523 if (argc != 1) {
524 return stralloc("[ERROR: getvar failed because it did not "
525 "get 1 argument only]");
526 };
527 value = getvar(args[0]);
528 if (value) {
529 return stralloc(value);
530 } else {
531 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
532 return stralloc(buf);
533 }
534 }
536 char* rrdgoodfor(long argc, const char **args){
537 if (argc == 1) {
538 goodfor = atol(args[0]);
539 } else {
540 return stralloc("[ERROR: goodfor expected 1 argument]");
541 }
543 if (goodfor == 0){
544 return stralloc("[ERROR: goodfor value must not be 0]");
545 }
547 return stralloc("");
548 }
550 char* rrdgetinternal(long argc, const char **args){
551 if (argc == 1) {
552 if( strcasecmp( args[0], "VERSION") == 0) {
553 return stralloc(PACKAGE_VERSION);
554 } else if( strcasecmp( args[0], "COMPILETIME") == 0) {
555 return stralloc(__DATE__ " " __TIME__);
556 } else {
557 return stralloc("[ERROR: internal unknown argument]");
558 }
559 } else {
560 return stralloc("[ERROR: internal expected 1 argument]");
561 }
562 }
564 /* Format start or end times using strftime. We always need both the
565 * start and end times, because, either might be relative to the other.
566 * */
567 #define MAX_STRFTIME_SIZE 256
568 char* printstrftime(long argc, const char **args){
569 struct rrd_time_value start_tv, end_tv;
570 char *parsetime_error = NULL;
571 char formatted[MAX_STRFTIME_SIZE];
572 struct tm *the_tm;
573 time_t start_tmp, end_tmp;
575 /* Make sure that we were given the right number of args */
576 if( argc != 4) {
577 rrd_set_error( "wrong number of args %d", argc);
578 return stralloc("");
579 }
581 /* Init start and end time */
582 parsetime("end-24h", &start_tv);
583 parsetime("now", &end_tv);
585 /* Parse the start and end times we were given */
586 if( (parsetime_error = parsetime( args[1], &start_tv))) {
587 rrd_set_error( "start time: %s", parsetime_error);
588 return stralloc("");
589 }
590 if( (parsetime_error = parsetime( args[2], &end_tv))) {
591 rrd_set_error( "end time: %s", parsetime_error);
592 return stralloc("");
593 }
594 if( proc_start_end( &start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
595 return stralloc("");
596 }
598 /* Do we do the start or end */
599 if( strcasecmp( args[0], "START") == 0) {
600 the_tm = localtime( &start_tmp);
601 }
602 else if( strcasecmp( args[0], "END") == 0) {
603 the_tm = localtime( &end_tmp);
604 }
605 else {
606 rrd_set_error( "start/end not found in '%s'", args[0]);
607 return stralloc("");
608 }
610 /* now format it */
611 if( strftime( formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
612 return( stralloc( formatted));
613 }
614 else {
615 rrd_set_error( "strftime failed");
616 return stralloc("");
617 }
618 }
620 char* includefile(long argc, const char **args){
621 char *buffer;
622 if (argc >= 1) {
623 const char* filename = args[0];
624 readfile(filename, &buffer, 0);
625 if (rrd_test_error()) {
626 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE));
627 sprintf(err, "[ERROR: %s]",rrd_get_error());
628 rrd_clear_error();
629 return err;
630 } else {
631 return buffer;
632 }
633 }
634 else
635 {
636 return stralloc("[ERROR: No Inclue file defined]");
637 }
638 }
640 /* make a copy of buf and replace open/close brackets with '_' */
641 char* rrdstrip(char *buf) {
642 char* p;
643 if (buf == NULL) {
644 return NULL;
645 }
646 /* make a copy of the buffer */
647 buf = stralloc(buf);
648 if (buf == NULL) {
649 return NULL;
650 }
652 p = buf;
653 while (*p) {
654 if (*p == '<' || *p == '>') {
655 *p = '_';
656 }
657 p++;
658 }
659 return buf;
660 }
662 char* cgigetq(long argc, const char **args){
663 if (argc>= 1){
664 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
665 char *buf2;
666 char *c,*d;
667 int qc=0;
668 if (buf==NULL) return NULL;
670 for(c=buf;*c != '\0';c++)
671 if (*c == '"') qc++;
672 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
673 perror("Malloc Buffer");
674 exit(1);
675 };
676 c=buf;
677 d=buf2;
678 *(d++) = '"';
679 while(*c != '\0'){
680 if (*c == '"') {
681 *(d++) = '"';
682 *(d++) = '\'';
683 *(d++) = '"';
684 *(d++) = '\'';
685 }
686 *(d++) = *(c++);
687 }
688 *(d++) = '"';
689 *(d) = '\0';
690 free(buf);
691 return buf2;
692 }
694 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
695 }
697 /* remove occurrences of .. this is a general measure to make
698 paths which came in via cgi do not go UP ... */
700 char* cgigetqp(long argc, const char **args){
701 char* buf;
702 char* buf2;
703 char* p;
704 char* d;
706 if (argc < 1)
707 {
708 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
709 }
711 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
712 if (!buf)
713 {
714 return NULL;
715 }
717 buf2 = malloc(strlen(buf)+1);
718 if (!buf2)
719 {
720 perror("cgigetqp(): Malloc Path Buffer");
721 exit(1);
722 };
724 p = buf;
725 d = buf2;
727 while (*p)
728 {
729 /* prevent mallicious paths from entering the system */
730 if (p[0] == '.' && p[1] == '.')
731 {
732 p += 2;
733 *d++ = '_';
734 *d++ = '_';
735 }
736 else
737 {
738 *d++ = *p++;
739 }
740 }
742 *d = 0;
743 free(buf);
745 /* Make sure the path is relative, e.g. does not start with '/' */
746 p = buf2;
747 while ('/' == *p)
748 {
749 *p++ = '_';
750 }
752 return buf2;
753 }
756 char* cgiget(long argc, const char **args){
757 if (argc>= 1)
758 return rrdstrip(rrdcgiGetValue(rrdcgiArg,args[0]));
759 else
760 return stralloc("[ERROR: not enough arguments for RRD::CV]");
761 }
765 char* drawgraph(long argc, const char **args){
766 int i,xsize, ysize;
767 double ymin,ymax;
768 for(i=0;i<argc;i++)
769 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
770 if(i==argc) {
771 args[argc++] = "--imginfo";
772 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
773 }
774 calfree();
775 if( rrd_graph(argc+1, (char **) args-1, &calcpr, &xsize, &ysize,NULL,&ymin,&ymax) != -1 ) {
776 return stralloc(calcpr[0]);
777 } else {
778 if (rrd_test_error()) {
779 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
780 sprintf(err, "[ERROR: %s]",rrd_get_error());
781 rrd_clear_error();
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) == (size_t)(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 }