1 /*****************************************************************************
2 * RRDtool 1.2.99907080300 Copyright by Tobi Oetiker, 1997-2007
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
11 /*#define DEBUG_PARSER
14 typedef struct var_s {
18 typedef struct cgi_s {
22 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
23 and replace by result of arg[2] call */
31 /**************************************************/
32 /* tag replacers ... they are called from parse */
33 /* through function pointers */
34 /**************************************************/
36 /* return cgi var named arg[0] */
41 /* return a quoted cgi var named arg[0] */
46 /* return a quoted and sanitized cgi variable */
51 /* call rrd_graph and insert appropriate image tag */
56 /* return PRINT functions from last rrd_graph call */
61 /* pretty-print the <last></last> value for some.rrd via strftime() */
66 /* pretty-print current time */
71 /* set an environment variable */
76 /* get an environment variable */
81 /* include the named file at this point */
86 /* for how long is the output of the cgi valid ? */
91 /* return rrdcgi version string */
103 /* format at-time specified times using strftime */
108 /** HTTP protocol needs special format, and GMT time **/
112 /* return a pointer to newly allocated copy of this string */
116 /* global variable for rrdcgi */
121 * Prints a valid CGI Header (Content-type...) etc.
126 /* rrdcgiDecodeString
127 * decode html escapes
130 char *rrdcgiDecodeString(
135 * Set/unsets debugging
143 * Reads in variables set via POST or stdin.
150 * Returns the value of the specified variable or NULL if it's empty
153 char *rrdcgiGetValue(
159 * Frees a list as returned by rrdcgiGetVariables()
166 * Frees the internal data structures
171 /* rrdcgiReadVariables()
173 * Read from stdin if no string is provided via CGI. Variables that
174 * doesn't have a value associated with it doesn't get stored.
176 s_var **rrdcgiReadVariables(
180 int rrdcgiDebugLevel = 0;
181 int rrdcgiDebugStderr = 1;
182 char *rrdcgiHeaderString = NULL;
183 char *rrdcgiType = NULL;
185 /* rrd interface to the variable functions {put,get}var() */
192 char *rrdsetvarconst(
197 /* variable store: put/get key-value pairs */
202 static const char *getvar(
203 const char *varname);
204 static const char *putvar(
209 /* key value pair that makes up an entry in the variable store */
211 int is_const; /* const variable or not */
212 const char *name; /* variable name */
213 const char *value; /* variable value */
216 /* the variable heap:
217 start with a heapsize of 10 variables */
218 #define INIT_VARSTORE_SIZE 10
219 static vardata *varheap = NULL;
220 static size_t varheap_size = 0;
222 /* allocate and initialize variable heap */
226 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
227 if (varheap == NULL) {
228 fprintf(stderr, "ERROR: unable to initialize variable store\n");
231 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
232 varheap_size = INIT_VARSTORE_SIZE;
236 /* cleanup: free allocated memory */
243 for (i = 0; i < (int) varheap_size; i++) {
244 if (varheap[i].name) {
245 free((char *) varheap[i].name);
247 if (varheap[i].value) {
248 free((char *) varheap[i].value);
255 /* Get a variable from the variable store.
256 Return NULL in case the requested variable was not found. */
257 static const char *getvar(
262 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
263 if (0 == strcmp(name, varheap[i].name)) {
265 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
267 return varheap[i].value;
271 printf("<!-- getvar(%s) -> Not found-->\n", name);
276 /* Put a variable into the variable store. If a variable by that
277 name exists, it's value is overwritten with the new value unless it was
278 marked as 'const' (initialized by RRD::SETCONSTVAR).
279 Returns a copy the newly allocated value on success, NULL on error. */
280 static const char *putvar(
287 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
288 if (0 == strcmp(name, varheap[i].name)) {
289 /* overwrite existing entry */
290 if (varheap[i].is_const) {
292 printf("<!-- setver(%s, %s): not assigning: "
293 "const variable -->\n", name, value);
295 return varheap[i].value;
298 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
299 name, value, varheap[i].value);
301 /* make it possible to promote a variable to readonly */
302 varheap[i].is_const = is_const;
303 free((char *) varheap[i].value);
304 varheap[i].value = stralloc(value);
305 return varheap[i].value;
309 /* no existing variable found by that name, add it */
310 if (i == (int) varheap_size) {
311 /* ran out of heap: resize heap to double size */
312 size_t new_size = varheap_size * 2;
314 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
316 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
319 /* initialize newly allocated memory */ ;
320 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
321 varheap_size = new_size;
323 varheap[i].is_const = is_const;
324 varheap[i].name = stralloc(name);
325 varheap[i].value = stralloc(value);
328 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
330 return varheap[i].value;
333 /* expand those RRD:* directives that can be used recursivly */
334 static char *rrd_expand_vars(
340 printf("expanding variables in '%s'\n", buffer);
343 for (i = 0; buffer[i]; i++) {
344 if (buffer[i] != '<')
346 parse(&buffer, i, "<RRD::CV", cgiget);
347 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
348 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
349 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
350 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
351 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
352 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
353 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
354 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
359 static long goodfor = 0;
360 static char **calcpr = NULL;
367 for (i = 0; calcpr[i]; i++) {
378 /* create freeable version of the string */
387 nstr = malloc((strlen(str) + 1));
398 char *server_url = NULL;
401 struct option long_options[] = {
402 {"filter", no_argument, 0, 'f'},
406 #ifdef MUST_DISABLE_SIGFPE
407 signal(SIGFPE, SIG_IGN);
409 #ifdef MUST_DISABLE_FPMASK
413 opterr = 0; /* initialize getopt */
415 /* what do we get for cmdline arguments?
417 printf("%d-'%s'\n",i,argv[i]); */
419 int option_index = 0;
422 opt = getopt_long(argc, argv, "f", long_options, &option_index);
432 printf("unknown commandline option '%s'\n", argv[optind - 1]);
439 rrdcgiArg = rrdcgiInit();
440 server_url = getenv("SERVER_URL");
443 /* make sure we have one extra argument,
444 if there are others, we do not care Apache gives several */
446 /* if ( (optind != argc-2
447 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
448 && optind != argc-1) { */
450 if (optind >= argc) {
451 fprintf(stderr, "ERROR: expected a filename\n");
454 length = readfile(argv[optind], &buffer, 1);
457 if (rrd_test_error()) {
458 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
462 /* initialize variable heap */
466 /* some fake header for testing */
467 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
471 /* expand rrd directives in buffer recursivly */
472 for (i = 0; buffer[i]; i++) {
473 if (buffer[i] != '<')
476 parse(&buffer, i, "<RRD::CV", cgiget);
477 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
478 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
479 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
481 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
482 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
483 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
484 parse(&buffer, i, "<RRD::INCLUDE", includefile);
485 parse(&buffer, i, "<RRD::PRINT", drawprint);
486 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
487 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
488 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
489 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
490 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
491 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
492 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
496 printf("Content-Type: text/html\n"
497 "Content-Length: %zd\n", strlen(buffer));
499 if (labs(goodfor) > 0) {
503 printf("Last-Modified: %s\n", http_time(&now));
504 now += labs(goodfor);
505 printf("Expires: %s\n", http_time(&now));
507 printf("Refresh: %ld\n", labs(goodfor));
514 printf("%s", buffer);
525 /* remove occurrences of .. this is a general measure to make
526 paths which came in via cgi do not go UP ... */
533 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
536 return stralloc("[ERROR: allocating setenv buffer]");
538 sprintf(xyz, "%s=%s", args[0], args[1]);
539 if (putenv(xyz) == -1) {
541 return stralloc("[ERROR: failed to do putenv]");
545 return stralloc("[ERROR: setenv failed because not enough "
546 "arguments were defined]");
549 /* rrd interface to the variable function putvar() */
555 const char *result = putvar(args[0], args[1], 0 /* not const */ );
558 /* setvar does not return the value set */
561 return stralloc("[ERROR: putvar failed]");
563 return stralloc("[ERROR: putvar failed because not enough arguments "
567 /* rrd interface to the variable function putvar() */
568 char *rrdsetvarconst(
573 const char *result = putvar(args[0], args[1], 1 /* const */ );
576 /* setvar does not return the value set */
579 return stralloc("[ERROR: putvar failed]");
581 return stralloc("[ERROR: putvar failed because not enough arguments "
593 return stralloc("[ERROR: getenv failed because it did not "
594 "get 1 argument only]");
596 envvar = getenv(args[0]);
598 return stralloc(envvar);
600 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
601 return stralloc(buf);
613 return stralloc("[ERROR: getvar failed because it did not "
614 "get 1 argument only]");
616 value = getvar(args[0]);
618 return stralloc(value);
620 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
621 return stralloc(buf);
630 goodfor = atol(args[0]);
632 return stralloc("[ERROR: goodfor expected 1 argument]");
636 return stralloc("[ERROR: goodfor value must not be 0]");
642 char *rrdgetinternal(
647 if (strcasecmp(args[0], "VERSION") == 0) {
648 return stralloc(PACKAGE_VERSION);
649 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
650 return stralloc(__DATE__ " " __TIME__);
652 return stralloc("[ERROR: internal unknown argument]");
655 return stralloc("[ERROR: internal expected 1 argument]");
659 /* Format start or end times using strftime. We always need both the
660 * start and end times, because, either might be relative to the other.
662 #define MAX_STRFTIME_SIZE 256
667 struct rrd_time_value start_tv, end_tv;
668 char *parsetime_error = NULL;
669 char formatted[MAX_STRFTIME_SIZE];
671 time_t start_tmp, end_tmp;
673 /* Make sure that we were given the right number of args */
675 rrd_set_error("wrong number of args %d", argc);
679 /* Init start and end time */
680 parsetime("end-24h", &start_tv);
681 parsetime("now", &end_tv);
683 /* Parse the start and end times we were given */
684 if ((parsetime_error = parsetime(args[1], &start_tv))) {
685 rrd_set_error("start time: %s", parsetime_error);
688 if ((parsetime_error = parsetime(args[2], &end_tv))) {
689 rrd_set_error("end time: %s", parsetime_error);
692 if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
696 /* Do we do the start or end */
697 if (strcasecmp(args[0], "START") == 0) {
698 the_tm = localtime(&start_tmp);
699 } else if (strcasecmp(args[0], "END") == 0) {
700 the_tm = localtime(&end_tmp);
702 rrd_set_error("start/end not found in '%s'", args[0]);
707 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
708 return (stralloc(formatted));
710 rrd_set_error("strftime failed");
722 const char *filename = args[0];
724 readfile(filename, &buffer, 0);
725 if (rrd_test_error()) {
726 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
728 sprintf(err, "[ERROR: %s]", rrd_get_error());
735 return stralloc("[ERROR: No Inclue file defined]");
739 /* make a copy of buf and replace open/close brackets with '_' */
748 /* make a copy of the buffer */
756 if (*p == '<' || *p == '>') {
769 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
777 for (c = buf; *c != '\0'; c++)
780 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
781 perror("Malloc Buffer");
802 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
805 /* remove occurrences of .. this is a general measure to make
806 paths which came in via cgi do not go UP ... */
818 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
821 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
826 buf2 = malloc(strlen(buf) + 1);
828 perror("cgigetqp(): Malloc Path Buffer");
836 /* prevent mallicious paths from entering the system */
837 if (p[0] == '.' && p[1] == '.') {
849 /* Make sure the path is relative, e.g. does not start with '/' */
864 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
866 return stralloc("[ERROR: not enough arguments for RRD::CV]");
878 for (i = 0; i < argc; i++)
879 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
882 args[argc++] = "--imginfo";
883 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
887 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
889 return stralloc(calcpr[0]);
891 if (rrd_test_error()) {
893 malloc((strlen(rrd_get_error()) +
894 DS_NAM_SIZE) * sizeof(char));
895 sprintf(err, "[ERROR: %s]", rrd_get_error());
908 if (argc == 1 && calcpr) {
911 while (calcpr[i] != NULL)
912 i++; /*determine number lines in calcpr */
913 if (atol(args[0]) < i - 1)
914 return stralloc(calcpr[atol(args[0]) + 1]);
916 return stralloc("[ERROR: RRD::PRINT argument error]");
930 return stralloc("[ERROR: allocating strftime buffer]");
932 last = rrd_last(argc + 1, (char **) args - 1);
933 if (rrd_test_error()) {
935 malloc((strlen(rrd_get_error()) +
936 DS_NAM_SIZE) * sizeof(char));
937 sprintf(err, "[ERROR: %s]", rrd_get_error());
941 tm_last = *localtime(&last);
942 strftime(buf, 254, args[1], &tm_last);
946 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
948 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
955 time_t now = time(NULL);
962 return stralloc("[ERROR: allocating strftime buffer]");
964 tm_now = *localtime(&now);
965 strftime(buf, 254, args[0], &tm_now);
969 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
971 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
974 /* Scan buffer until an unescaped '>' arives.
975 * Update argument array with arguments found.
976 * Return end cursor where parsing stopped, or NULL in case of failure.
979 * To allow nested constructs, we call rrd_expand_vars() for arguments
980 * that contain RRD::x directives. These introduce a small memory leak
981 * since we have to stralloc the arguments the way parse() works.
988 char *getP; /* read cursor */
989 char *putP; /* write cursor */
990 char Quote; /* type of quote if in quoted string, 0 otherwise */
991 int tagcount; /* open tag count */
992 int in_arg; /* if we currently are parsing an argument or not */
993 int argsz; /* argument array size */
994 int curarg_contains_rrd_directives;
996 /* local array of arguments while parsing */
1001 printf("<-- scanargs(%s) -->\n", line);
1005 *argument_count = 0;
1007 /* create initial argument array of char pointers */
1009 argv = (char **) malloc(argsz * sizeof(char *));
1014 /* skip leading blanks */
1015 while (isspace((int) *line)) {
1026 curarg_contains_rrd_directives = 0;
1028 /* start parsing 'line' for arguments */
1030 unsigned char c = *getP++;
1032 if (c == '>' && !Quote && !tagcount) {
1033 /* this is our closing tag, quit scanning */
1037 /* remove all special chars */
1044 if (Quote || tagcount) {
1045 /* copy quoted/tagged (=RRD expanded) string */
1047 } else if (in_arg) {
1048 /* end argument string */
1051 if (curarg_contains_rrd_directives) {
1053 rrd_expand_vars(stralloc(argv[argc - 1]));
1054 curarg_contains_rrd_directives = 0;
1059 case '"': /* Fall through */
1065 /* copy quoted string */
1070 /* reference start of argument string in argument array */
1071 argv[argc++] = putP;
1080 /* start new argument */
1081 argv[argc++] = putP;
1091 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1092 curarg_contains_rrd_directives = 1;
1099 /* check if our argument array is still large enough */
1100 if (argc == argsz) {
1101 /* resize argument array */
1103 argv = rrd_realloc(argv, argsz * sizeof(char *));
1104 if (*argv == NULL) {
1110 /* terminate last argument found */
1112 if (curarg_contains_rrd_directives) {
1113 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1119 printf("<-- arguments found [%d]\n", argc);
1120 for (n = 0; n < argc; n++) {
1121 printf("arg %02d: '%s'\n", n, argv[n]);
1125 printf("<!-- No arguments found -->\n");
1129 /* update caller's notion of the argument array and it's size */
1131 *argument_count = argc;
1137 /* Return new scanning cursor:
1138 pointer to char after closing bracket */
1144 * Parse(): scan current portion of buffer for given tag.
1145 * If found, parse tag arguments and call 'func' for it.
1146 * The result of func is inserted at the current position
1150 char **buf, /* buffer */
1151 long i, /* offset in buffer */
1152 char *tag, /* tag to handle */
1153 char * (*func) (long,
1154 const char **) /* function to call for 'tag' */
1157 /* the name of the vairable ... */
1164 size_t taglen = strlen(tag);
1166 /* Current position in buffer should start with 'tag' */
1167 if (strncmp((*buf) + i, tag, taglen) != 0) {
1170 /* .. and match exactly (a whitespace following 'tag') */
1171 if (!isspace(*((*buf) + i + taglen))) {
1175 printf("parse(): handling tag '%s'\n", tag);
1178 /* Scan for arguments following the tag;
1179 scanargs() puts \0 into *buf ... so after scanargs it is probably
1180 not a good time to use strlen on buf */
1181 end = scanargs((*buf) + i + taglen, &argc, &args);
1183 /* got arguments, call function for 'tag' with arguments */
1184 val = func(argc, (const char **) args);
1187 /* unable to parse arguments, undo 0-termination by scanargs */
1188 for (; argc > 0; argc--) {
1189 *((args[argc - 1]) - 1) = ' ';
1192 /* next call, try parsing at current offset +1 */
1193 end = (*buf) + i + 1;
1195 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1196 " Check original file. This may have been altered "
1197 "by parsing.]\n\n");
1200 /* remember offset where we have to continue parsing */
1201 end_offset = end - (*buf);
1205 valln = strlen(val);
1208 /* Optionally resize buffer to hold the replacement value:
1209 Calculating the new length of the buffer is simple. add current
1210 buffer pos (i) to length of string after replaced tag to length
1211 of replacement string and add 1 for the final zero ... */
1212 if (end - (*buf) < (i + valln)) {
1213 /* make sure we do not shrink the mallocd block */
1214 size_t newbufsize = i + strlen(end) + valln + 1;
1216 *buf = rrd_realloc(*buf, newbufsize);
1219 perror("Realoc buf:");
1224 /* Update new end pointer:
1225 make sure the 'end' pointer gets moved along with the
1226 buf pointer when realloc moves memory ... */
1227 end = (*buf) + end_offset;
1229 /* splice the variable:
1230 step 1. Shift pending data to make room for 'val' */
1231 memmove((*buf) + i + valln, end, strlen(end) + 1);
1233 /* step 2. Insert val */
1235 memmove((*buf) + i, val, valln);
1238 return (valln > 0 ? valln - 1 : valln);
1245 static char buf[60];
1247 tmptime = gmtime(now);
1248 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1256 printf("Content-type: %s\n", rrdcgiType);
1258 printf("Content-type: text/html\n");
1259 if (rrdcgiHeaderString)
1260 printf("%s", rrdcgiHeaderString);
1269 rrdcgiDebugLevel = level;
1271 rrdcgiDebugLevel = 0;
1273 rrdcgiDebugStderr = 0;
1275 rrdcgiDebugStderr = 1;
1278 char *rrdcgiDecodeString(
1283 for (cp = text, xp = text; *cp; cp++) {
1285 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1286 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1287 if (islower(*(cp + 1)))
1288 *(cp + 1) = toupper(*(cp + 1));
1289 if (islower(*(cp + 2)))
1290 *(cp + 2) = toupper(*(cp + 2));
1293 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1295 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1303 memset(xp, 0, cp - xp);
1307 /* rrdcgiReadVariables()
1309 * Read from stdin if no string is provided via CGI. Variables that
1310 * doesn't have a value associated with it doesn't get stored.
1312 s_var **rrdcgiReadVariables(
1318 char *cp, *ip, *esp, *sptr;
1323 cp = getenv("REQUEST_METHOD");
1324 ip = getenv("CONTENT_LENGTH");
1326 if (cp && !strcmp(cp, "POST")) {
1329 if ((line = (char *) malloc(length + 2)) == NULL)
1331 fgets(line, length + 1, stdin);
1334 } else if (cp && !strcmp(cp, "GET")) {
1335 esp = getenv("QUERY_STRING");
1336 if (esp && strlen(esp)) {
1337 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1339 sprintf(line, "%s", esp);
1344 printf("(offline mode: enter name=value pairs on standard input)\n");
1345 memset(tmp, 0, sizeof(tmp));
1346 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1348 if (tmp[strlen(tmp) - 1] == '\n')
1349 tmp[strlen(tmp) - 1] = '&';
1351 length += strlen(tmp);
1352 len = (length + 1) * sizeof(char);
1353 if ((line = (char *) realloc(line, len)) == NULL)
1357 length = strlen(tmp);
1358 len = (length + 1) * sizeof(char);
1359 if ((line = (char *) malloc(len)) == NULL)
1361 memset(line, 0, len);
1365 memset(tmp, 0, sizeof(tmp));
1369 if (line[strlen(line) - 1] == '&')
1370 line[strlen(line) - 1] = '\0';
1374 * From now on all cgi variables are stored in the variable line
1375 * and look like foo=bar&foobar=barfoo&foofoo=
1378 if (rrdcgiDebugLevel > 0) {
1379 if (rrdcgiDebugStderr)
1380 fprintf(stderr, "Received cgi input: %s\n", line);
1383 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1387 for (cp = line; *cp; cp++)
1392 for (numargs = 1, cp = line; *cp; cp++)
1397 if (rrdcgiDebugLevel > 0) {
1398 if (rrdcgiDebugStderr)
1399 fprintf(stderr, "%d cgi variables found.\n", numargs);
1401 printf("%d cgi variables found.<br>\n", numargs);
1404 len = (numargs + 1) * sizeof(s_var *);
1405 if ((result = (s_var **) malloc(len)) == NULL)
1407 memset(result, 0, len);
1412 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1415 ip = cp + strlen(cp);
1417 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1429 /* try to find out if there's already such a variable */
1430 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1431 || !(strlen(result[k]->name) == esp - cp));
1434 if (k == i) { /* No such variable yet */
1435 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1437 if ((result[i]->name =
1438 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1440 memset(result[i]->name, 0, esp - cp + 1);
1441 strncpy(result[i]->name, cp, esp - cp);
1443 if ((result[i]->value =
1444 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1446 memset(result[i]->value, 0, ip - esp + 1);
1447 strncpy(result[i]->value, cp, ip - esp);
1448 result[i]->value = rrdcgiDecodeString(result[i]->value);
1449 if (rrdcgiDebugLevel) {
1450 if (rrdcgiDebugStderr)
1451 fprintf(stderr, "%s: %s\n", result[i]->name,
1454 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1455 result[i]->name, result[i]->value);
1458 } else { /* There is already such a name, suppose a mutiple field */
1461 (strlen(result[k]->value) + (ip - esp) +
1463 if ((sptr = (char *) malloc(len)) == NULL)
1465 memset(sptr, 0, len);
1466 sprintf(sptr, "%s\n", result[k]->value);
1467 strncat(sptr, cp, ip - esp);
1468 free(result[k]->value);
1469 result[k]->value = rrdcgiDecodeString(sptr);
1479 * Read from stdin if no string is provided via CGI. Variables that
1480 * doesn't have a value associated with it doesn't get stored.
1488 vars = rrdcgiReadVariables();
1493 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1500 char *rrdcgiGetValue(
1506 if (!parms || !parms->vars)
1508 for (i = 0; parms->vars[i]; i++)
1509 if (!strcmp(name, parms->vars[i]->name)) {
1510 if (rrdcgiDebugLevel > 0) {
1511 if (rrdcgiDebugStderr)
1512 fprintf(stderr, "%s found as %s\n", name,
1513 parms->vars[i]->value);
1515 printf("%s found as %s<br>\n", name,
1516 parms->vars[i]->value);
1518 return parms->vars[i]->value;
1520 if (rrdcgiDebugLevel) {
1521 if (rrdcgiDebugStderr)
1522 fprintf(stderr, "%s not found\n", name);
1524 printf("%s not found<br>\n", name);
1529 void rrdcgiFreeList(
1534 for (i = 0; list[i] != NULL; i++)
1547 for (i = 0; parms->vars[i]; i++) {
1548 if (parms->vars[i]->name)
1549 free(parms->vars[i]->name);
1550 if (parms->vars[i]->value)
1551 free(parms->vars[i]->value);
1552 free(parms->vars[i]);
1558 if (rrdcgiHeaderString) {
1559 free(rrdcgiHeaderString);
1560 rrdcgiHeaderString = NULL;