1 /*****************************************************************************
2 * RRDtool 1.2.23 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 */
223 static int initvar(void)
225 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
226 if (varheap == NULL) {
227 fprintf(stderr, "ERROR: unable to initialize variable store\n");
230 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
231 varheap_size = INIT_VARSTORE_SIZE;
235 /* cleanup: free allocated memory */
236 static void donevar(void)
241 for (i = 0; i < (int) varheap_size; i++) {
242 if (varheap[i].name) {
243 free((char *) varheap[i].name);
245 if (varheap[i].value) {
246 free((char *) varheap[i].value);
253 /* Get a variable from the variable store.
254 Return NULL in case the requested variable was not found. */
255 static const char *getvar(
260 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
261 if (0 == strcmp(name, varheap[i].name)) {
263 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
265 return varheap[i].value;
269 printf("<!-- getvar(%s) -> Not found-->\n", name);
274 /* Put a variable into the variable store. If a variable by that
275 name exists, it's value is overwritten with the new value unless it was
276 marked as 'const' (initialized by RRD::SETCONSTVAR).
277 Returns a copy the newly allocated value on success, NULL on error. */
278 static const char *putvar(
285 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
286 if (0 == strcmp(name, varheap[i].name)) {
287 /* overwrite existing entry */
288 if (varheap[i].is_const) {
290 printf("<!-- setver(%s, %s): not assigning: "
291 "const variable -->\n", name, value);
293 return varheap[i].value;
296 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
297 name, value, varheap[i].value);
299 /* make it possible to promote a variable to readonly */
300 varheap[i].is_const = is_const;
301 free((char *) varheap[i].value);
302 varheap[i].value = stralloc(value);
303 return varheap[i].value;
307 /* no existing variable found by that name, add it */
308 if (i == (int) varheap_size) {
309 /* ran out of heap: resize heap to double size */
310 size_t new_size = varheap_size * 2;
312 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
314 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
317 /* initialize newly allocated memory */ ;
318 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
319 varheap_size = new_size;
321 varheap[i].is_const = is_const;
322 varheap[i].name = stralloc(name);
323 varheap[i].value = stralloc(value);
326 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
328 return varheap[i].value;
331 /* expand those RRD:* directives that can be used recursivly */
332 static char *rrd_expand_vars(
338 printf("expanding variables in '%s'\n", buffer);
341 for (i = 0; buffer[i]; i++) {
342 if (buffer[i] != '<')
344 parse(&buffer, i, "<RRD::CV", cgiget);
345 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
346 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
347 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
348 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
349 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
350 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
351 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
352 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
357 static long goodfor = 0;
358 static char **calcpr = NULL;
365 for (i = 0; calcpr[i]; i++) {
376 /* create freeable version of the string */
385 nstr = malloc((strlen(str) + 1));
396 char *server_url = NULL;
399 struct option long_options[] = {
400 {"filter", no_argument, 0, 'f'},
404 #ifdef MUST_DISABLE_SIGFPE
405 signal(SIGFPE, SIG_IGN);
407 #ifdef MUST_DISABLE_FPMASK
411 opterr = 0; /* initialize getopt */
413 /* what do we get for cmdline arguments?
415 printf("%d-'%s'\n",i,argv[i]); */
417 int option_index = 0;
420 opt = getopt_long(argc, argv, "f", long_options, &option_index);
430 printf("unknown commandline option '%s'\n", argv[optind - 1]);
437 rrdcgiArg = rrdcgiInit();
438 server_url = getenv("SERVER_URL");
441 /* make sure we have one extra argument,
442 if there are others, we do not care Apache gives several */
444 /* if ( (optind != argc-2
445 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
446 && optind != argc-1) { */
448 if (optind >= argc) {
449 fprintf(stderr, "ERROR: expected a filename\n");
452 length = readfile(argv[optind], &buffer, 1);
455 if (rrd_test_error()) {
456 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
460 /* initialize variable heap */
464 /* some fake header for testing */
465 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
469 /* expand rrd directives in buffer recursivly */
470 for (i = 0; buffer[i]; i++) {
471 if (buffer[i] != '<')
474 parse(&buffer, i, "<RRD::CV", cgiget);
475 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
476 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
477 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
479 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
480 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
481 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
482 parse(&buffer, i, "<RRD::INCLUDE", includefile);
483 parse(&buffer, i, "<RRD::PRINT", drawprint);
484 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
485 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
486 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
487 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
488 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
489 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
490 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
494 printf("Content-Type: text/html\n"
495 "Content-Length: %zd\n", strlen(buffer));
497 if (labs(goodfor) > 0) {
501 printf("Last-Modified: %s\n", http_time(&now));
502 now += labs(goodfor);
503 printf("Expires: %s\n", http_time(&now));
505 printf("Refresh: %ld\n", labs(goodfor));
512 printf("%s", buffer);
523 /* remove occurrences of .. this is a general measure to make
524 paths which came in via cgi do not go UP ... */
531 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
534 return stralloc("[ERROR: allocating setenv buffer]");
536 sprintf(xyz, "%s=%s", args[0], args[1]);
537 if (putenv(xyz) == -1) {
539 return stralloc("[ERROR: failed to do putenv]");
543 return stralloc("[ERROR: setenv failed because not enough "
544 "arguments were defined]");
547 /* rrd interface to the variable function putvar() */
553 const char *result = putvar(args[0], args[1], 0 /* not const */ );
556 /* setvar does not return the value set */
559 return stralloc("[ERROR: putvar failed]");
561 return stralloc("[ERROR: putvar failed because not enough arguments "
565 /* rrd interface to the variable function putvar() */
566 char *rrdsetvarconst(
571 const char *result = putvar(args[0], args[1], 1 /* const */ );
574 /* setvar does not return the value set */
577 return stralloc("[ERROR: putvar failed]");
579 return stralloc("[ERROR: putvar failed because not enough arguments "
591 return stralloc("[ERROR: getenv failed because it did not "
592 "get 1 argument only]");
594 envvar = getenv(args[0]);
596 return stralloc(envvar);
598 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
599 return stralloc(buf);
611 return stralloc("[ERROR: getvar failed because it did not "
612 "get 1 argument only]");
614 value = getvar(args[0]);
616 return stralloc(value);
618 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
619 return stralloc(buf);
628 goodfor = atol(args[0]);
630 return stralloc("[ERROR: goodfor expected 1 argument]");
634 return stralloc("[ERROR: goodfor value must not be 0]");
640 char *rrdgetinternal(
645 if (strcasecmp(args[0], "VERSION") == 0) {
646 return stralloc(PACKAGE_VERSION);
647 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
648 return stralloc(__DATE__ " " __TIME__);
650 return stralloc("[ERROR: internal unknown argument]");
653 return stralloc("[ERROR: internal expected 1 argument]");
657 /* Format start or end times using strftime. We always need both the
658 * start and end times, because, either might be relative to the other.
660 #define MAX_STRFTIME_SIZE 256
665 struct rrd_time_value start_tv, end_tv;
666 char *parsetime_error = NULL;
667 char formatted[MAX_STRFTIME_SIZE];
669 time_t start_tmp, end_tmp;
671 /* Make sure that we were given the right number of args */
673 rrd_set_error("wrong number of args %d", argc);
677 /* Init start and end time */
678 parsetime("end-24h", &start_tv);
679 parsetime("now", &end_tv);
681 /* Parse the start and end times we were given */
682 if ((parsetime_error = parsetime(args[1], &start_tv))) {
683 rrd_set_error("start time: %s", parsetime_error);
686 if ((parsetime_error = parsetime(args[2], &end_tv))) {
687 rrd_set_error("end time: %s", parsetime_error);
690 if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
694 /* Do we do the start or end */
695 if (strcasecmp(args[0], "START") == 0) {
696 the_tm = localtime(&start_tmp);
697 } else if (strcasecmp(args[0], "END") == 0) {
698 the_tm = localtime(&end_tmp);
700 rrd_set_error("start/end not found in '%s'", args[0]);
705 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
706 return (stralloc(formatted));
708 rrd_set_error("strftime failed");
720 const char *filename = args[0];
722 readfile(filename, &buffer, 0);
723 if (rrd_test_error()) {
724 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
726 sprintf(err, "[ERROR: %s]", rrd_get_error());
733 return stralloc("[ERROR: No Inclue file defined]");
737 /* make a copy of buf and replace open/close brackets with '_' */
746 /* make a copy of the buffer */
754 if (*p == '<' || *p == '>') {
767 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
775 for (c = buf; *c != '\0'; c++)
778 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
779 perror("Malloc Buffer");
800 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
803 /* remove occurrences of .. this is a general measure to make
804 paths which came in via cgi do not go UP ... */
816 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
819 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
824 buf2 = malloc(strlen(buf) + 1);
826 perror("cgigetqp(): Malloc Path Buffer");
834 /* prevent mallicious paths from entering the system */
835 if (p[0] == '.' && p[1] == '.') {
847 /* Make sure the path is relative, e.g. does not start with '/' */
862 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
864 return stralloc("[ERROR: not enough arguments for RRD::CV]");
876 for (i = 0; i < argc; i++)
877 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
880 args[argc++] = "--imginfo";
881 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
885 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
887 return stralloc(calcpr[0]);
889 if (rrd_test_error()) {
891 malloc((strlen(rrd_get_error()) +
892 DS_NAM_SIZE) * sizeof(char));
893 sprintf(err, "[ERROR: %s]", rrd_get_error());
906 if (argc == 1 && calcpr) {
909 while (calcpr[i] != NULL)
910 i++; /*determine number lines in calcpr */
911 if (atol(args[0]) < i - 1)
912 return stralloc(calcpr[atol(args[0]) + 1]);
914 return stralloc("[ERROR: RRD::PRINT argument error]");
928 return stralloc("[ERROR: allocating strftime buffer]");
930 last = rrd_last(argc + 1, (char **) args - 1);
931 if (rrd_test_error()) {
933 malloc((strlen(rrd_get_error()) +
934 DS_NAM_SIZE) * sizeof(char));
935 sprintf(err, "[ERROR: %s]", rrd_get_error());
939 tm_last = *localtime(&last);
940 strftime(buf, 254, args[1], &tm_last);
944 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
946 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
953 time_t now = time(NULL);
960 return stralloc("[ERROR: allocating strftime buffer]");
962 tm_now = *localtime(&now);
963 strftime(buf, 254, args[0], &tm_now);
967 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
969 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
972 /* Scan buffer until an unescaped '>' arives.
973 * Update argument array with arguments found.
974 * Return end cursor where parsing stopped, or NULL in case of failure.
977 * To allow nested constructs, we call rrd_expand_vars() for arguments
978 * that contain RRD::x directives. These introduce a small memory leak
979 * since we have to stralloc the arguments the way parse() works.
986 char *getP; /* read cursor */
987 char *putP; /* write cursor */
988 char Quote; /* type of quote if in quoted string, 0 otherwise */
989 int tagcount; /* open tag count */
990 int in_arg; /* if we currently are parsing an argument or not */
991 int argsz; /* argument array size */
992 int curarg_contains_rrd_directives;
994 /* local array of arguments while parsing */
999 printf("<-- scanargs(%s) -->\n", line);
1003 *argument_count = 0;
1005 /* create initial argument array of char pointers */
1007 argv = (char **) malloc(argsz * sizeof(char *));
1012 /* skip leading blanks */
1013 while (isspace((int) *line)) {
1024 curarg_contains_rrd_directives = 0;
1026 /* start parsing 'line' for arguments */
1028 unsigned char c = *getP++;
1030 if (c == '>' && !Quote && !tagcount) {
1031 /* this is our closing tag, quit scanning */
1035 /* remove all special chars */
1042 if (Quote || tagcount) {
1043 /* copy quoted/tagged (=RRD expanded) string */
1045 } else if (in_arg) {
1046 /* end argument string */
1049 if (curarg_contains_rrd_directives) {
1051 rrd_expand_vars(stralloc(argv[argc - 1]));
1052 curarg_contains_rrd_directives = 0;
1057 case '"': /* Fall through */
1063 /* copy quoted string */
1068 /* reference start of argument string in argument array */
1069 argv[argc++] = putP;
1078 /* start new argument */
1079 argv[argc++] = putP;
1089 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1090 curarg_contains_rrd_directives = 1;
1097 /* check if our argument array is still large enough */
1098 if (argc == argsz) {
1099 /* resize argument array */
1101 argv = rrd_realloc(argv, argsz * sizeof(char *));
1102 if (*argv == NULL) {
1108 /* terminate last argument found */
1110 if (curarg_contains_rrd_directives) {
1111 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1117 printf("<-- arguments found [%d]\n", argc);
1118 for (n = 0; n < argc; n++) {
1119 printf("arg %02d: '%s'\n", n, argv[n]);
1123 printf("<!-- No arguments found -->\n");
1127 /* update caller's notion of the argument array and it's size */
1129 *argument_count = argc;
1135 /* Return new scanning cursor:
1136 pointer to char after closing bracket */
1142 * Parse(): scan current portion of buffer for given tag.
1143 * If found, parse tag arguments and call 'func' for it.
1144 * The result of func is inserted at the current position
1148 char **buf, /* buffer */
1149 long i, /* offset in buffer */
1150 char *tag, /* tag to handle */
1151 char * (*func) (long,
1152 const char **) /* function to call for 'tag' */
1155 /* the name of the vairable ... */
1162 size_t taglen = strlen(tag);
1164 /* Current position in buffer should start with 'tag' */
1165 if (strncmp((*buf) + i, tag, taglen) != 0) {
1168 /* .. and match exactly (a whitespace following 'tag') */
1169 if (!isspace(*((*buf) + i + taglen))) {
1173 printf("parse(): handling tag '%s'\n", tag);
1176 /* Scan for arguments following the tag;
1177 scanargs() puts \0 into *buf ... so after scanargs it is probably
1178 not a good time to use strlen on buf */
1179 end = scanargs((*buf) + i + taglen, &argc, &args);
1181 /* got arguments, call function for 'tag' with arguments */
1182 val = func(argc, (const char **) args);
1185 /* unable to parse arguments, undo 0-termination by scanargs */
1186 for (; argc > 0; argc--) {
1187 *((args[argc - 1]) - 1) = ' ';
1190 /* next call, try parsing at current offset +1 */
1191 end = (*buf) + i + 1;
1193 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1194 " Check original file. This may have been altered "
1195 "by parsing.]\n\n");
1198 /* remember offset where we have to continue parsing */
1199 end_offset = end - (*buf);
1203 valln = strlen(val);
1206 /* Optionally resize buffer to hold the replacement value:
1207 Calculating the new length of the buffer is simple. add current
1208 buffer pos (i) to length of string after replaced tag to length
1209 of replacement string and add 1 for the final zero ... */
1210 if (end - (*buf) < (i + valln)) {
1211 /* make sure we do not shrink the mallocd block */
1212 size_t newbufsize = i + strlen(end) + valln + 1;
1214 *buf = rrd_realloc(*buf, newbufsize);
1217 perror("Realoc buf:");
1222 /* Update new end pointer:
1223 make sure the 'end' pointer gets moved along with the
1224 buf pointer when realloc moves memory ... */
1225 end = (*buf) + end_offset;
1227 /* splice the variable:
1228 step 1. Shift pending data to make room for 'val' */
1229 memmove((*buf) + i + valln, end, strlen(end) + 1);
1231 /* step 2. Insert val */
1233 memmove((*buf) + i, val, valln);
1236 return (valln > 0 ? valln - 1 : valln);
1243 static char buf[60];
1245 tmptime = gmtime(now);
1246 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1254 printf("Content-type: %s\n", rrdcgiType);
1256 printf("Content-type: text/html\n");
1257 if (rrdcgiHeaderString)
1258 printf("%s", rrdcgiHeaderString);
1267 rrdcgiDebugLevel = level;
1269 rrdcgiDebugLevel = 0;
1271 rrdcgiDebugStderr = 0;
1273 rrdcgiDebugStderr = 1;
1276 char *rrdcgiDecodeString(
1281 for (cp = text, xp = text; *cp; cp++) {
1283 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1284 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1285 if (islower(*(cp + 1)))
1286 *(cp + 1) = toupper(*(cp + 1));
1287 if (islower(*(cp + 2)))
1288 *(cp + 2) = toupper(*(cp + 2));
1291 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1293 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1301 memset(xp, 0, cp - xp);
1305 /* rrdcgiReadVariables()
1307 * Read from stdin if no string is provided via CGI. Variables that
1308 * doesn't have a value associated with it doesn't get stored.
1310 s_var **rrdcgiReadVariables(
1316 char *cp, *ip, *esp, *sptr;
1321 cp = getenv("REQUEST_METHOD");
1322 ip = getenv("CONTENT_LENGTH");
1324 if (cp && !strcmp(cp, "POST")) {
1327 if ((line = (char *) malloc(length + 2)) == NULL)
1329 fgets(line, length + 1, stdin);
1332 } else if (cp && !strcmp(cp, "GET")) {
1333 esp = getenv("QUERY_STRING");
1334 if (esp && strlen(esp)) {
1335 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1337 sprintf(line, "%s", esp);
1342 printf("(offline mode: enter name=value pairs on standard input)\n");
1343 memset(tmp, 0, sizeof(tmp));
1344 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1346 if (tmp[strlen(tmp) - 1] == '\n')
1347 tmp[strlen(tmp) - 1] = '&';
1349 length += strlen(tmp);
1350 len = (length + 1) * sizeof(char);
1351 if ((line = (char *) realloc(line, len)) == NULL)
1355 length = strlen(tmp);
1356 len = (length + 1) * sizeof(char);
1357 if ((line = (char *) malloc(len)) == NULL)
1359 memset(line, 0, len);
1363 memset(tmp, 0, sizeof(tmp));
1367 if (line[strlen(line) - 1] == '&')
1368 line[strlen(line) - 1] = '\0';
1372 * From now on all cgi variables are stored in the variable line
1373 * and look like foo=bar&foobar=barfoo&foofoo=
1376 if (rrdcgiDebugLevel > 0) {
1377 if (rrdcgiDebugStderr)
1378 fprintf(stderr, "Received cgi input: %s\n", line);
1381 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1385 for (cp = line; *cp; cp++)
1390 for (numargs = 1, cp = line; *cp; cp++)
1395 if (rrdcgiDebugLevel > 0) {
1396 if (rrdcgiDebugStderr)
1397 fprintf(stderr, "%d cgi variables found.\n", numargs);
1399 printf("%d cgi variables found.<br>\n", numargs);
1402 len = (numargs + 1) * sizeof(s_var *);
1403 if ((result = (s_var **) malloc(len)) == NULL)
1405 memset(result, 0, len);
1410 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1413 ip = cp + strlen(cp);
1415 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1427 /* try to find out if there's already such a variable */
1428 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1429 || !(strlen(result[k]->name) == esp - cp));
1432 if (k == i) { /* No such variable yet */
1433 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1435 if ((result[i]->name =
1436 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1438 memset(result[i]->name, 0, esp - cp + 1);
1439 strncpy(result[i]->name, cp, esp - cp);
1441 if ((result[i]->value =
1442 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1444 memset(result[i]->value, 0, ip - esp + 1);
1445 strncpy(result[i]->value, cp, ip - esp);
1446 result[i]->value = rrdcgiDecodeString(result[i]->value);
1447 if (rrdcgiDebugLevel) {
1448 if (rrdcgiDebugStderr)
1449 fprintf(stderr, "%s: %s\n", result[i]->name,
1452 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1453 result[i]->name, result[i]->value);
1456 } else { /* There is already such a name, suppose a mutiple field */
1459 (strlen(result[k]->value) + (ip - esp) +
1461 if ((sptr = (char *) malloc(len)) == NULL)
1463 memset(sptr, 0, len);
1464 sprintf(sptr, "%s\n", result[k]->value);
1465 strncat(sptr, cp, ip - esp);
1466 free(result[k]->value);
1467 result[k]->value = rrdcgiDecodeString(sptr);
1477 * Read from stdin if no string is provided via CGI. Variables that
1478 * doesn't have a value associated with it doesn't get stored.
1486 vars = rrdcgiReadVariables();
1491 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1498 char *rrdcgiGetValue(
1504 if (!parms || !parms->vars)
1506 for (i = 0; parms->vars[i]; i++)
1507 if (!strcmp(name, parms->vars[i]->name)) {
1508 if (rrdcgiDebugLevel > 0) {
1509 if (rrdcgiDebugStderr)
1510 fprintf(stderr, "%s found as %s\n", name,
1511 parms->vars[i]->value);
1513 printf("%s found as %s<br>\n", name,
1514 parms->vars[i]->value);
1516 return parms->vars[i]->value;
1518 if (rrdcgiDebugLevel) {
1519 if (rrdcgiDebugStderr)
1520 fprintf(stderr, "%s not found\n", name);
1522 printf("%s not found<br>\n", name);
1527 void rrdcgiFreeList(
1532 for (i = 0; list[i] != NULL; i++)
1545 for (i = 0; parms->vars[i]; i++) {
1546 if (parms->vars[i]->name)
1547 free(parms->vars[i]->name);
1548 if (parms->vars[i]->value)
1549 free(parms->vars[i]->value);
1550 free(parms->vars[i]);
1556 if (rrdcgiHeaderString) {
1557 free(rrdcgiHeaderString);
1558 rrdcgiHeaderString = NULL;