1 /*****************************************************************************
2 * RRDtool 1.1.x Copyright Tobias Oetiker, 1997 - 2002
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
7 #include "rrd_tool.h"
8 #include <cgi.h>
9 #include <time.h>
12 #define MEMBLK 1024
14 /* global variable for libcgi */
15 s_cgi **cgiArg;
17 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
18 and replace by result of arg[2] call */
19 int parse(char **, long, char *, char *(*)(long , char **));
21 /**************************************************/
22 /* tag replacers ... they are called from parse */
23 /* through function pointers */
24 /**************************************************/
26 /* return cgi var named arg[0] */
27 char* cgiget(long , char **);
29 /* return a quoted cgi var named arg[0] */
30 char* cgigetq(long , char **);
32 /* return a quoted and sanitized cgi variable */
33 char* cgigetqp(long , char **);
35 /* call rrd_graph and insert apropriate image tag */
36 char* drawgraph(long, char **);
38 /* return PRINT functions from last rrd_graph call */
39 char* drawprint(long, char **);
41 /* pretty-print the <last></last> value for some.rrd via strftime() */
42 char* printtimelast(long, char **);
44 /* pretty-print current time */
45 char* printtimenow(long,char **);
47 /* set an evironment variable */
48 char* rrdsetenv(long, char **);
50 /* get an evironment variable */
51 char* rrdgetenv(long, char **);
53 /* include the named file at this point */
54 char* includefile(long, char **);
56 /* for how long is the output of the cgi valid ? */
57 char* rrdgoodfor(long, char **);
59 /** http protocol needs special format, and GMT time **/
60 char *http_time(time_t *);
62 /* return a pointer to newly alocated copy of this string */
63 char *stralloc(char *);
65 static long goodfor=0;
66 static char **calcpr=NULL;
67 static void calfree (void){
68 if (calcpr) {
69 long i;
70 for(i=0;calcpr[i];i++){
71 if (calcpr[i]){
72 free(calcpr[i]);
73 }
74 }
75 if (calcpr) {
76 free(calcpr);
77 }
78 }
79 }
81 /* create freeable version of the string */
82 char * stralloc(char *str){
83 char *nstr = malloc((strlen(str)+1)*sizeof(char));
84 strcpy(nstr,str);
85 return(nstr);
86 }
88 int main(int argc, char *argv[]) {
89 long length;
90 char *buffer;
91 char *server_url = NULL;
92 long i;
93 long filter=0;
94 #ifdef MUST_DISABLE_SIGFPE
95 signal(SIGFPE,SIG_IGN);
96 #endif
97 #ifdef MUST_DISABLE_FPMASK
98 fpsetmask(0);
99 #endif
100 /* what do we get for cmdline arguments?
101 for (i=0;i<argc;i++)
102 printf("%d-'%s'\n",i,argv[i]); */
103 while (1){
104 static struct option long_options[] =
105 {
106 {"filter", no_argument, 0, 'f'},
107 {0,0,0,0}
108 };
109 int option_index = 0;
110 int opt;
111 opt = getopt_long(argc, argv, "f",
112 long_options, &option_index);
113 if (opt == EOF)
114 break;
115 switch(opt) {
116 case 'f':
117 filter=1;
118 break;
119 case '?':
120 printf("unknown commandline option '%s'\n",argv[optind-1]);
121 return -1;
122 }
123 }
125 if(filter==0) {
126 cgiDebug(0,0);
127 cgiArg = cgiInit ();
128 server_url = getenv("SERVER_URL");
129 }
131 if (optind != argc-1) {
132 fprintf(stderr, "ERROR: expected a filename\n");
133 exit(1);
134 } else {
135 length = readfile(argv[optind], &buffer, 1);
136 }
138 if(rrd_test_error()){
139 fprintf(stderr, "ERROR: %s\n",rrd_get_error());
140 exit(1);
141 }
144 if(filter==0) {
145 /* pass 1 makes only sense in cgi mode */
146 for (i=0;buffer[i] != '\0'; i++){
147 i +=parse(&buffer,i,"<RRD::CV",cgiget);
148 i +=parse(&buffer,i,"<RRD::CV::QUOTE",cgigetq);
149 i +=parse(&buffer,i,"<RRD::CV::PATH",cgigetqp);
150 i +=parse(&buffer,i,"<RRD::GETENV",rrdgetenv);
151 }
152 }
154 /* pass 2 */
155 for (i=0;buffer[i] != '\0'; i++){
156 i += parse(&buffer,i,"<RRD::GOODFOR",rrdgoodfor);
157 i += parse(&buffer,i,"<RRD::SETENV",rrdsetenv);
158 i += parse(&buffer,i,"<RRD::INCLUDE",includefile);
159 i += parse(&buffer,i,"<RRD::TIME::LAST",printtimelast);
160 i += parse(&buffer,i,"<RRD::TIME::NOW",printtimenow);
161 }
163 /* pass 3 */
164 for (i=0;buffer[i] != '\0'; i++){
165 i += parse(&buffer,i,"<RRD::GRAPH",drawgraph);
166 i += parse(&buffer,i,"<RRD::PRINT",drawprint);
167 }
169 if (filter==0){
170 printf ("Content-Type: text/html\n"
171 "Content-Length: %d\n", strlen(buffer));
172 if (labs(goodfor) > 0){
173 time_t now;
174 now = time(NULL);
175 printf ("Last-Modified: %s\n",http_time(&now));
176 now += labs(goodfor);
177 printf ("Expires: %s\n",http_time(&now));
178 if (goodfor < 0) {
179 printf("Refresh: %ld\n", labs(goodfor));
180 }
181 }
182 printf ("\n");
183 }
184 printf ("%s", buffer);
185 calfree();
186 if (buffer){
187 free(buffer);
188 }
189 exit(0);
190 }
192 /* remove occurences of .. this is a general measure to make
193 paths which came in via cgi do not go UP ... */
195 char* rrdsetenv(long argc, char **args){
196 if (argc >= 2) {
197 char *xyz=malloc((strlen(args[0])+strlen(args[1])+3)*sizeof(char));
198 if (xyz == NULL){
199 return stralloc("[ERROR: allocating setenv buffer]");
200 };
201 sprintf(xyz,"%s=%s",args[0],args[1]);
202 if( putenv(xyz) == -1) {
203 return stralloc("[ERROR: faild to do putenv]");
204 };
205 } else {
206 return stralloc("[ERROR: setenv faild because not enough arguments were defined]");
207 }
208 return stralloc("");
209 }
211 char* rrdgetenv(long argc, char **args){
212 if (argc != 1) {
213 return stralloc("[ERROR: getenv faild because it did not get 1 argument only]");
214 }
215 else if (getenv(args[0]) == NULL) {
216 return stralloc("");
217 }
218 else {
219 return stralloc(getenv(args[0]));
220 }
221 }
223 char* rrdgoodfor(long argc, char **args){
224 if (argc == 1) {
225 goodfor = atol(args[0]);
226 } else {
227 return stralloc("[ERROR: goodfor expected 1 argument]");
228 }
230 if (goodfor == 0){
231 return stralloc("[ERROR: goodfor value must not be 0]");
232 }
234 return stralloc("");
235 }
237 char* includefile(long argc, char **args){
238 char *buffer;
239 if (argc >= 1) {
240 readfile(args[0], &buffer, 0);
241 if (rrd_test_error()) {
242 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
243 sprintf(err, "[ERROR: %s]",rrd_get_error());
244 rrd_clear_error();
245 return err;
246 } else {
247 return buffer;
248 }
249 }
250 else
251 {
252 return stralloc("[ERROR: No Inclue file defined]");
253 }
254 }
256 static
257 char* rrdstrip(char *buf){
258 char *start;
259 if (buf == NULL) return NULL;
260 buf = stralloc(buf); /* make a copy of the buffer */
261 if (buf == NULL) return NULL;
262 while ((start = strstr(buf,"<"))){
263 *start = '_';
264 }
265 while ((start = strstr(buf,">"))){
266 *start = '_';
267 }
268 return buf;
269 }
271 char* cgigetq(long argc, char **args){
272 if (argc>= 1){
273 char *buf = rrdstrip(cgiGetValue(cgiArg,args[0]));
274 char *buf2;
275 char *c,*d;
276 int qc=0;
277 if (buf==NULL) return NULL;
279 for(c=buf;*c != '\0';c++)
280 if (*c == '"') qc++;
281 if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){
282 perror("Malloc Buffer");
283 exit(1);
284 };
285 c=buf;
286 d=buf2;
287 *(d++) = '"';
288 while(*c != '\0'){
289 if (*c == '"') {
290 *(d++) = '"';
291 *(d++) = '\'';
292 *(d++) = '"';
293 *(d++) = '\'';
294 }
295 *(d++) = *(c++);
296 }
297 *(d++) = '"';
298 *(d) = '\0';
299 free(buf);
300 return buf2;
301 }
303 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
304 }
306 /* remove occurences of .. this is a general measure to make
307 paths which came in via cgi do not go UP ... */
309 char* cgigetqp(long argc, char **args){
310 if (argc>= 1){
311 char *buf = rrdstrip(cgiGetValue(cgiArg,args[0]));
312 char *buf2;
313 char *c,*d;
314 int qc=0;
315 if (buf==NULL) return NULL;
317 for(c=buf;*c != '\0';c++)
318 if (*c == '"') qc++;
319 if((buf2=malloc((strlen(buf) + qc*4 +4) * sizeof(char)))==NULL){
320 perror("Malloc Buffer");
321 exit(1);
322 };
323 c=buf;
324 d=buf2;
325 *(d++) = '"';
326 while(*c != '\0'){
327 if (*c == '"') {
328 *(d++) = '"';
329 *(d++) = '\'';
330 *(d++) = '"';
331 *(d++) = '\'';
332 }
333 if(*c == '/') {
334 *(d++) = '_';c++;
335 } else {
336 if (*c=='.' && *(c+1) == '.'){
337 c += 2;
338 *(d++) = '_'; *(d++) ='_';
339 } else {
341 *(d++) = *(c++);
342 }
343 }
344 }
345 *(d++) = '"';
346 *(d) = '\0';
347 free(buf);
348 return buf2;
349 }
351 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
353 }
356 char* cgiget(long argc, char **args){
357 if (argc>= 1)
358 return rrdstrip(cgiGetValue(cgiArg,args[0]));
359 else
360 return stralloc("[ERROR: not enough arguments for RRD::CV]");
361 }
365 char* drawgraph(long argc, char **args){
366 int i,xsize, ysize;
367 for(i=0;i<argc;i++)
368 if(strcmp(args[i],"--imginfo")==0 || strcmp(args[i],"-g")==0) break;
369 if(i==argc) {
370 args[argc++] = "--imginfo";
371 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
372 }
373 optind=0; /* reset gnu getopt */
374 opterr=0; /* reset gnu getopt */
375 calfree();
376 if( rrd_graph(argc+1, args-1, &calcpr, &xsize, &ysize) != -1 ) {
377 return stralloc(calcpr[0]);
378 } else {
379 if (rrd_test_error()) {
380 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
381 sprintf(err, "[ERROR: %s]",rrd_get_error());
382 rrd_clear_error();
383 calfree();
384 return err;
385 }
386 }
387 return NULL;
388 }
390 char* drawprint(long argc, char **args){
391 if (argc==1 && calcpr){
392 long i=0;
393 while (calcpr[i] != NULL) i++; /*determine number lines in calcpr*/
394 if (atol(args[0])<i-1)
395 return stralloc(calcpr[atol(args[0])+1]);
396 }
397 return stralloc("[ERROR: RRD::PRINT argument error]");
398 }
400 char* printtimelast(long argc, char **args) {
401 time_t last;
402 struct tm tm_last;
403 char *buf;
404 if ( argc == 2 ) {
405 buf = malloc(255);
406 if (buf == NULL){
407 return stralloc("[ERROR: allocating strftime buffer]");
408 };
409 last = rrd_last(argc+1, args-1);
410 if (rrd_test_error()) {
411 char *err = malloc((strlen(rrd_get_error())+DS_NAM_SIZE)*sizeof(char));
412 sprintf(err, "[ERROR: %s]",rrd_get_error());
413 rrd_clear_error();
414 return err;
415 }
416 tm_last = *localtime(&last);
417 strftime(buf,254,args[1],&tm_last);
418 return buf;
419 }
420 if ( argc < 2 ) {
421 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
422 }
423 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
424 }
426 char* printtimenow(long argc, char **args) {
427 time_t now = time(NULL);
428 struct tm tm_now;
429 char *buf;
430 if ( argc == 1 ) {
431 buf = malloc(255);
432 if (buf == NULL){
433 return stralloc("[ERROR: allocating strftime buffer]");
434 };
435 tm_now = *localtime(&now);
436 strftime(buf,254,args[0],&tm_now);
437 return buf;
438 }
439 if ( argc < 1 ) {
440 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
441 }
442 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
443 }
445 /* scan aLine until an unescaped '>' arives */
446 static
447 char* scanargs(char *aLine, long *argc, char ***args)
448 {
449 char *getP, *putP;
450 char Quote = 0;
451 int argal = MEMBLK;
452 int braket = 0;
453 int inArg = 0;
454 if (((*args) = (char **) malloc(MEMBLK*sizeof(char *))) == NULL) {
455 return NULL;
456 }
457 /* sikp leading blanks */
458 while (*aLine && *aLine <= ' ') aLine++;
460 *argc = 0;
461 getP = aLine;
462 putP = aLine;
463 while (*getP && !( !Quote && (braket == 0) && ((*getP) == '>'))){
464 if ((unsigned)*getP < ' ') *getP = ' '; /*remove all special chars*/
465 switch (*getP) {
466 case ' ':
467 if (Quote){
468 *(putP++)=*getP;
469 } else
470 if(inArg) {
471 *(putP++) = 0;
472 inArg = 0;
473 }
474 break;
475 case '"':
476 case '\'':
477 if (Quote != 0) {
478 if (Quote == *getP)
479 Quote = 0;
480 else {
481 *(putP++)=*getP;
482 }
483 } else {
484 if(!inArg){
485 (*args)[++(*argc)] = putP;
486 inArg=1;
487 }
488 Quote = *getP;
489 }
490 break;
491 default:
492 if (Quote == 0 && (*getP) == '<') {
493 braket++;
494 }
495 if (Quote == 0 && (*getP) == '>') {
496 braket--;
497 }
499 if(!inArg){
500 (*args)[++(*argc)] = putP;
501 inArg=1;
502 }
503 *(putP++)=*getP;
504 break;
505 }
506 if ((*argc) >= argal-10 ) {
507 argal += MEMBLK;
508 if (((*args)=rrd_realloc((*args),(argal)*sizeof(char *))) == NULL) {
509 return NULL;
510 }
511 }
512 getP++;
513 }
515 *putP = '\0';
516 (*argc)++;
517 if (Quote)
518 return NULL;
519 else
520 return getP+1; /* pointer to next char after parameter */
521 }
525 int parse(char **buf, long i, char *tag,
526 char *(*func)(long argc, char **args)){
528 /* the name of the vairable ... */
529 char *val;
530 long valln;
531 char **args;
532 char *end;
533 long end_offset;
534 long argc;
535 /* do we like it ? */
536 if (strncmp((*buf)+i, tag, strlen(tag))!=0) return 0;
537 if (! isspace(*( (*buf) + i + strlen(tag) )) ) return 0;
538 /* scanargs puts \0 into *buf ... so after scanargs it is probably
539 not a good time to use strlen on buf */
540 end = scanargs((*buf)+i+strlen(tag),&argc,&args);
541 if (! end) {
542 for (;argc>2;argc--){
543 *((args[argc-1])-1)=' ';
544 }
545 val = stralloc("[ERROR: Parsing Problem with the following text\n"
546 " Check original file. This may have been altered by parsing.]\n\n");
547 end = (*buf)+i+1;
548 } else {
549 val = func(argc-1,args+1);
550 free (args);
551 }
552 /* for (ii=0;ii<argc;ii++) printf("'%s'\n", args[ii]); */
553 if (val != NULL) {
554 valln = strlen(val);
555 } else { valln = 0;}
557 /* make enough room for replacement */
558 end_offset = end - (*buf);
559 if(end-(*buf) < i + valln){ /* make sure we do not shrink the mallocd block */
560 /* calculating the new length of the buffer is simple. add current
561 buffer pos (i) to length of string after replaced tag to length
562 of replacement string and add 1 for the final zero ... */
563 if(((*buf) = rrd_realloc((*buf),
564 (i+strlen(end) + valln +1) * sizeof(char)))==NULL){
565 perror("Realoc buf:");
566 exit(1);
567 };
568 }
569 end = (*buf) + end_offset; /* make sure the 'end' pointer gets moved
570 along with the buf pointer when realoc
571 moves memmory ... */
572 /* splice the variable */
573 memmove ((*buf)+i+valln,end,strlen(end)+1);
574 if (val != NULL ) memmove ((*buf)+i,val, valln);
575 if (val){ free(val);}
576 return valln > 0 ? valln-1: valln;
577 }
579 char *
580 http_time(time_t *now) {
581 struct tm *tmptime;
582 static char buf[60];
584 tmptime=gmtime(now);
585 strftime(buf,sizeof(buf),"%a, %d %b %Y %H:%M:%S GMT",tmptime);
586 return(buf);
587 }