1 /*****************************************************************************
2 * RRDtool 1.3rc5 Copyright by Tobi Oetiker, 1997-2008
3 *****************************************************************************
4 * rrd_cgi.c RRD Web Page Generator
5 *****************************************************************************/
13 /*#define DEBUG_PARSER
16 typedef struct var_s {
20 typedef struct cgi_s {
24 /* in arg[0] find tags beginning with arg[1] call arg[2] on them
25 and replace by result of arg[2] call */
33 /**************************************************/
34 /* tag replacers ... they are called from parse */
35 /* through function pointers */
36 /**************************************************/
38 /* return cgi var named arg[0] */
43 /* return a quoted cgi var named arg[0] */
48 /* return a quoted and sanitized cgi variable */
53 /* call rrd_graph and insert appropriate image tag */
58 /* return PRINT functions from last rrd_graph call */
63 /* pretty-print the <last></last> value for some.rrd via strftime() */
68 /* pretty-print current time */
73 /* set an environment variable */
78 /* get an environment variable */
83 /* include the named file at this point */
88 /* for how long is the output of the cgi valid ? */
93 /* return rrdcgi version string */
105 /* format at-time specified times using strftime */
110 /** HTTP protocol needs special format, and GMT time **/
114 /* return a pointer to newly allocated copy of this string */
118 /* global variable for rrdcgi */
123 * Prints a valid CGI Header (Content-type...) etc.
128 /* rrdcgiDecodeString
129 * decode html escapes
132 char *rrdcgiDecodeString(
137 * Set/unsets debugging
145 * Reads in variables set via POST or stdin.
152 * Returns the value of the specified variable or NULL if it's empty
155 char *rrdcgiGetValue(
161 * Frees a list as returned by rrdcgiGetVariables()
168 * Frees the internal data structures
173 /* rrdcgiReadVariables()
175 * Read from stdin if no string is provided via CGI. Variables that
176 * doesn't have a value associated with it doesn't get stored.
178 s_var **rrdcgiReadVariables(
182 int rrdcgiDebugLevel = 0;
183 int rrdcgiDebugStderr = 1;
184 char *rrdcgiHeaderString = NULL;
185 char *rrdcgiType = NULL;
187 /* rrd interface to the variable functions {put,get}var() */
194 char *rrdsetvarconst(
199 /* variable store: put/get key-value pairs */
204 static const char *getvar(
205 const char *varname);
206 static const char *putvar(
211 /* key value pair that makes up an entry in the variable store */
213 int is_const; /* const variable or not */
214 const char *name; /* variable name */
215 const char *value; /* variable value */
218 /* the variable heap:
219 start with a heapsize of 10 variables */
220 #define INIT_VARSTORE_SIZE 10
221 static vardata *varheap = NULL;
222 static size_t varheap_size = 0;
224 /* allocate and initialize variable heap */
228 varheap = (vardata *) malloc(sizeof(vardata) * INIT_VARSTORE_SIZE);
229 if (varheap == NULL) {
230 fprintf(stderr, "ERROR: unable to initialize variable store\n");
233 memset(varheap, 0, sizeof(vardata) * INIT_VARSTORE_SIZE);
234 varheap_size = INIT_VARSTORE_SIZE;
238 /* cleanup: free allocated memory */
245 for (i = 0; i < (int) varheap_size; i++) {
246 if (varheap[i].name) {
247 free((char *) varheap[i].name);
249 if (varheap[i].value) {
250 free((char *) varheap[i].value);
257 /* Get a variable from the variable store.
258 Return NULL in case the requested variable was not found. */
259 static const char *getvar(
264 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
265 if (0 == strcmp(name, varheap[i].name)) {
267 printf("<!-- getvar(%s) -> %s -->\n", name, varheap[i].value);
269 return varheap[i].value;
273 printf("<!-- getvar(%s) -> Not found-->\n", name);
278 /* Put a variable into the variable store. If a variable by that
279 name exists, it's value is overwritten with the new value unless it was
280 marked as 'const' (initialized by RRD::SETCONSTVAR).
281 Returns a copy the newly allocated value on success, NULL on error. */
282 static const char *putvar(
289 for (i = 0; i < (int) varheap_size && varheap[i].name; i++) {
290 if (0 == strcmp(name, varheap[i].name)) {
291 /* overwrite existing entry */
292 if (varheap[i].is_const) {
294 printf("<!-- setver(%s, %s): not assigning: "
295 "const variable -->\n", name, value);
297 return varheap[i].value;
300 printf("<!-- setvar(%s, %s): overwriting old value (%s) -->\n",
301 name, value, varheap[i].value);
303 /* make it possible to promote a variable to readonly */
304 varheap[i].is_const = is_const;
305 free((char *) varheap[i].value);
306 varheap[i].value = stralloc(value);
307 return varheap[i].value;
311 /* no existing variable found by that name, add it */
312 if (i == (int) varheap_size) {
313 /* ran out of heap: resize heap to double size */
314 size_t new_size = varheap_size * 2;
316 varheap = (vardata *) (realloc(varheap, sizeof(vardata) * new_size));
318 fprintf(stderr, "ERROR: Unable to realloc variable heap\n");
321 /* initialize newly allocated memory */ ;
322 memset(&varheap[varheap_size], 0, sizeof(vardata) * varheap_size);
323 varheap_size = new_size;
325 varheap[i].is_const = is_const;
326 varheap[i].name = stralloc(name);
327 varheap[i].value = stralloc(value);
330 printf("<!-- setvar(%s, %s): adding new variable -->\n", name, value);
332 return varheap[i].value;
335 /* expand those RRD:* directives that can be used recursivly */
336 static char *rrd_expand_vars(
342 printf("expanding variables in '%s'\n", buffer);
345 for (i = 0; buffer[i]; i++) {
346 if (buffer[i] != '<')
348 parse(&buffer, i, "<RRD::CV", cgiget);
349 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
350 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
351 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
352 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
353 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
354 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
355 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
356 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
361 static long goodfor = 0;
362 static char **calcpr = NULL;
369 for (i = 0; calcpr[i]; i++) {
380 /* create freeable version of the string */
389 nstr = malloc((strlen(str) + 1));
400 char *server_url = NULL;
403 struct option long_options[] = {
404 {"filter", no_argument, 0, 'f'},
408 #ifdef MUST_DISABLE_SIGFPE
409 signal(SIGFPE, SIG_IGN);
411 #ifdef MUST_DISABLE_FPMASK
415 opterr = 0; /* initialize getopt */
417 /* what do we get for cmdline arguments?
419 printf("%d-'%s'\n",i,argv[i]); */
421 int option_index = 0;
424 opt = getopt_long(argc, argv, "f", long_options, &option_index);
434 printf("unknown commandline option '%s'\n", argv[optind - 1]);
441 rrdcgiArg = rrdcgiInit();
442 server_url = getenv("SERVER_URL");
445 /* make sure we have one extra argument,
446 if there are others, we do not care Apache gives several */
448 /* if ( (optind != argc-2
449 && strstr( getenv("SERVER_SOFTWARE"),"Apache/2") != NULL)
450 && optind != argc-1) { */
452 if (optind >= argc) {
453 fprintf(stderr, "ERROR: expected a filename\n");
456 length = readfile(argv[optind], &buffer, 1);
459 if (rrd_test_error()) {
460 fprintf(stderr, "ERROR: %s\n", rrd_get_error());
464 /* initialize variable heap */
468 /* some fake header for testing */
469 printf("Content-Type: text/html\nContent-Length: 10000000\n\n\n");
473 /* expand rrd directives in buffer recursivly */
474 for (i = 0; buffer[i]; i++) {
475 if (buffer[i] != '<')
478 parse(&buffer, i, "<RRD::CV", cgiget);
479 parse(&buffer, i, "<RRD::CV::PATH", cgigetqp);
480 parse(&buffer, i, "<RRD::CV::QUOTE", cgigetq);
481 parse(&buffer, i, "<RRD::GETENV", rrdgetenv);
483 parse(&buffer, i, "<RRD::GETVAR", rrdgetvar);
484 parse(&buffer, i, "<RRD::GOODFOR", rrdgoodfor);
485 parse(&buffer, i, "<RRD::GRAPH", drawgraph);
486 parse(&buffer, i, "<RRD::INCLUDE", includefile);
487 parse(&buffer, i, "<RRD::PRINT", drawprint);
488 parse(&buffer, i, "<RRD::SETCONSTVAR", rrdsetvarconst);
489 parse(&buffer, i, "<RRD::SETENV", rrdsetenv);
490 parse(&buffer, i, "<RRD::SETVAR", rrdsetvar);
491 parse(&buffer, i, "<RRD::TIME::LAST", printtimelast);
492 parse(&buffer, i, "<RRD::TIME::NOW", printtimenow);
493 parse(&buffer, i, "<RRD::TIME::STRFTIME", printstrftime);
494 parse(&buffer, i, "<RRD::INTERNAL", rrdgetinternal);
498 printf("Content-Type: text/html\n"
499 "Content-Length: %zd\n", strlen(buffer));
501 if (labs(goodfor) > 0) {
505 printf("Last-Modified: %s\n", http_time(&now));
506 now += labs(goodfor);
507 printf("Expires: %s\n", http_time(&now));
509 printf("Refresh: %ld\n", labs(goodfor));
516 printf("%s", buffer);
527 /* remove occurrences of .. this is a general measure to make
528 paths which came in via cgi do not go UP ... */
535 char *xyz = malloc((strlen(args[0]) + strlen(args[1]) + 2));
538 return stralloc("[ERROR: allocating setenv buffer]");
540 sprintf(xyz, "%s=%s", args[0], args[1]);
541 if (putenv(xyz) == -1) {
543 return stralloc("[ERROR: failed to do putenv]");
547 return stralloc("[ERROR: setenv failed because not enough "
548 "arguments were defined]");
551 /* rrd interface to the variable function putvar() */
557 const char *result = putvar(args[0], args[1], 0 /* not const */ );
560 /* setvar does not return the value set */
563 return stralloc("[ERROR: putvar failed]");
565 return stralloc("[ERROR: putvar failed because not enough arguments "
569 /* rrd interface to the variable function putvar() */
570 char *rrdsetvarconst(
575 const char *result = putvar(args[0], args[1], 1 /* const */ );
578 /* setvar does not return the value set */
581 return stralloc("[ERROR: putvar failed]");
583 return stralloc("[ERROR: putvar failed because not enough arguments "
595 return stralloc("[ERROR: getenv failed because it did not "
596 "get 1 argument only]");
598 envvar = getenv(args[0]);
600 return stralloc(envvar);
602 snprintf(buf, sizeof(buf), "[ERROR:_getenv_'%s'_failed", args[0]);
603 return stralloc(buf);
615 return stralloc("[ERROR: getvar failed because it did not "
616 "get 1 argument only]");
618 value = getvar(args[0]);
620 return stralloc(value);
622 snprintf(buf, sizeof(buf), "[ERROR:_getvar_'%s'_failed", args[0]);
623 return stralloc(buf);
632 goodfor = atol(args[0]);
634 return stralloc("[ERROR: goodfor expected 1 argument]");
638 return stralloc("[ERROR: goodfor value must not be 0]");
644 char *rrdgetinternal(
649 if (strcasecmp(args[0], "VERSION") == 0) {
650 return stralloc(PACKAGE_VERSION);
651 } else if (strcasecmp(args[0], "COMPILETIME") == 0) {
652 return stralloc(__DATE__ " " __TIME__);
654 return stralloc("[ERROR: internal unknown argument]");
657 return stralloc("[ERROR: internal expected 1 argument]");
661 /* Format start or end times using strftime. We always need both the
662 * start and end times, because, either might be relative to the other.
664 #define MAX_STRFTIME_SIZE 256
669 struct rrd_time_value start_tv, end_tv;
670 char *parsetime_error = NULL;
671 char formatted[MAX_STRFTIME_SIZE];
673 time_t start_tmp, end_tmp;
675 /* Make sure that we were given the right number of args */
677 rrd_set_error("wrong number of args %d", argc);
681 /* Init start and end time */
682 parsetime("end-24h", &start_tv);
683 parsetime("now", &end_tv);
685 /* Parse the start and end times we were given */
686 if ((parsetime_error = parsetime(args[1], &start_tv))) {
687 rrd_set_error("start time: %s", parsetime_error);
690 if ((parsetime_error = parsetime(args[2], &end_tv))) {
691 rrd_set_error("end time: %s", parsetime_error);
694 if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
698 /* Do we do the start or end */
699 if (strcasecmp(args[0], "START") == 0) {
700 the_tm = localtime(&start_tmp);
701 } else if (strcasecmp(args[0], "END") == 0) {
702 the_tm = localtime(&end_tmp);
704 rrd_set_error("start/end not found in '%s'", args[0]);
709 if (strftime(formatted, MAX_STRFTIME_SIZE, args[3], the_tm)) {
710 return (stralloc(formatted));
712 rrd_set_error("strftime failed");
724 const char *filename = args[0];
726 readfile(filename, &buffer, 0);
727 if (rrd_test_error()) {
728 char *err = malloc((strlen(rrd_get_error()) + DS_NAM_SIZE));
730 sprintf(err, "[ERROR: %s]", rrd_get_error());
737 return stralloc("[ERROR: No Inclue file defined]");
741 /* make a copy of buf and replace open/close brackets with '_' */
750 /* make a copy of the buffer */
758 if (*p == '<' || *p == '>') {
771 char *buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
779 for (c = buf; *c != '\0'; c++)
782 if ((buf2 = malloc((strlen(buf) + 4 * qc + 4))) == NULL) {
783 perror("Malloc Buffer");
804 return stralloc("[ERROR: not enough argument for RRD::CV::QUOTE]");
807 /* remove occurrences of .. this is a general measure to make
808 paths which came in via cgi do not go UP ... */
820 return stralloc("[ERROR: not enough arguments for RRD::CV::PATH]");
823 buf = rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
828 buf2 = malloc(strlen(buf) + 1);
830 perror("cgigetqp(): Malloc Path Buffer");
838 /* prevent mallicious paths from entering the system */
839 if (p[0] == '.' && p[1] == '.') {
851 /* Make sure the path is relative, e.g. does not start with '/' */
866 return rrdstrip(rrdcgiGetValue(rrdcgiArg, args[0]));
868 return stralloc("[ERROR: not enough arguments for RRD::CV]");
880 for (i = 0; i < argc; i++)
881 if (strcmp(args[i], "--imginfo") == 0 || strcmp(args[i], "-g") == 0)
884 args[argc++] = "--imginfo";
885 args[argc++] = "<IMG SRC=\"./%s\" WIDTH=\"%lu\" HEIGHT=\"%lu\">";
889 (argc + 1, (char **) args - 1, &calcpr, &xsize, &ysize, NULL, &ymin,
891 return stralloc(calcpr[0]);
893 if (rrd_test_error()) {
895 malloc((strlen(rrd_get_error()) +
896 DS_NAM_SIZE) * sizeof(char));
897 sprintf(err, "[ERROR: %s]", rrd_get_error());
910 if (argc == 1 && calcpr) {
913 while (calcpr[i] != NULL)
914 i++; /*determine number lines in calcpr */
915 if (atol(args[0]) < i - 1)
916 return stralloc(calcpr[atol(args[0]) + 1]);
918 return stralloc("[ERROR: RRD::PRINT argument error]");
932 return stralloc("[ERROR: allocating strftime buffer]");
934 last = rrd_last(argc + 1, (char **) args - 1);
935 if (rrd_test_error()) {
937 malloc((strlen(rrd_get_error()) +
938 DS_NAM_SIZE) * sizeof(char));
939 sprintf(err, "[ERROR: %s]", rrd_get_error());
943 tm_last = *localtime(&last);
944 strftime(buf, 254, args[1], &tm_last);
948 return stralloc("[ERROR: too few arguments for RRD::TIME::LAST]");
950 return stralloc("[ERROR: not enough arguments for RRD::TIME::LAST]");
957 time_t now = time(NULL);
964 return stralloc("[ERROR: allocating strftime buffer]");
966 tm_now = *localtime(&now);
967 strftime(buf, 254, args[0], &tm_now);
971 return stralloc("[ERROR: too few arguments for RRD::TIME::NOW]");
973 return stralloc("[ERROR: not enough arguments for RRD::TIME::NOW]");
976 /* Scan buffer until an unescaped '>' arives.
977 * Update argument array with arguments found.
978 * Return end cursor where parsing stopped, or NULL in case of failure.
981 * To allow nested constructs, we call rrd_expand_vars() for arguments
982 * that contain RRD::x directives. These introduce a small memory leak
983 * since we have to stralloc the arguments the way parse() works.
990 char *getP; /* read cursor */
991 char *putP; /* write cursor */
992 char Quote; /* type of quote if in quoted string, 0 otherwise */
993 int tagcount; /* open tag count */
994 int in_arg; /* if we currently are parsing an argument or not */
995 int argsz; /* argument array size */
996 int curarg_contains_rrd_directives;
998 /* local array of arguments while parsing */
1003 printf("<-- scanargs(%s) -->\n", line);
1007 *argument_count = 0;
1009 /* create initial argument array of char pointers */
1011 argv = (char **) malloc(argsz * sizeof(char *));
1016 /* skip leading blanks */
1017 while (isspace((int) *line)) {
1028 curarg_contains_rrd_directives = 0;
1030 /* start parsing 'line' for arguments */
1032 unsigned char c = *getP++;
1034 if (c == '>' && !Quote && !tagcount) {
1035 /* this is our closing tag, quit scanning */
1039 /* remove all special chars */
1046 if (Quote || tagcount) {
1047 /* copy quoted/tagged (=RRD expanded) string */
1049 } else if (in_arg) {
1050 /* end argument string */
1053 if (curarg_contains_rrd_directives) {
1055 rrd_expand_vars(stralloc(argv[argc - 1]));
1056 curarg_contains_rrd_directives = 0;
1061 case '"': /* Fall through */
1067 /* copy quoted string */
1072 /* reference start of argument string in argument array */
1073 argv[argc++] = putP;
1082 /* start new argument */
1083 argv[argc++] = putP;
1093 if (0 == strncmp(getP, "RRD::", strlen("RRD::"))) {
1094 curarg_contains_rrd_directives = 1;
1101 /* check if our argument array is still large enough */
1102 if (argc == argsz) {
1103 /* resize argument array */
1105 argv = rrd_realloc(argv, argsz * sizeof(char *));
1106 if (*argv == NULL) {
1112 /* terminate last argument found */
1114 if (curarg_contains_rrd_directives) {
1115 argv[argc - 1] = rrd_expand_vars(stralloc(argv[argc - 1]));
1121 printf("<-- arguments found [%d]\n", argc);
1122 for (n = 0; n < argc; n++) {
1123 printf("arg %02d: '%s'\n", n, argv[n]);
1127 printf("<!-- No arguments found -->\n");
1131 /* update caller's notion of the argument array and it's size */
1133 *argument_count = argc;
1139 /* Return new scanning cursor:
1140 pointer to char after closing bracket */
1146 * Parse(): scan current portion of buffer for given tag.
1147 * If found, parse tag arguments and call 'func' for it.
1148 * The result of func is inserted at the current position
1152 char **buf, /* buffer */
1153 long i, /* offset in buffer */
1154 char *tag, /* tag to handle */
1155 char * (*func) (long,
1156 const char **) /* function to call for 'tag' */
1159 /* the name of the vairable ... */
1166 size_t taglen = strlen(tag);
1168 /* Current position in buffer should start with 'tag' */
1169 if (strncmp((*buf) + i, tag, taglen) != 0) {
1172 /* .. and match exactly (a whitespace following 'tag') */
1173 if (!isspace(*((*buf) + i + taglen))) {
1177 printf("parse(): handling tag '%s'\n", tag);
1180 /* Scan for arguments following the tag;
1181 scanargs() puts \0 into *buf ... so after scanargs it is probably
1182 not a good time to use strlen on buf */
1183 end = scanargs((*buf) + i + taglen, &argc, &args);
1185 /* got arguments, call function for 'tag' with arguments */
1186 val = func(argc, (const char **) args);
1189 /* unable to parse arguments, undo 0-termination by scanargs */
1190 for (; argc > 0; argc--) {
1191 *((args[argc - 1]) - 1) = ' ';
1194 /* next call, try parsing at current offset +1 */
1195 end = (*buf) + i + 1;
1197 val = stralloc("[ERROR: Parsing Problem with the following text\n"
1198 " Check original file. This may have been altered "
1199 "by parsing.]\n\n");
1202 /* remember offset where we have to continue parsing */
1203 end_offset = end - (*buf);
1207 valln = strlen(val);
1210 /* Optionally resize buffer to hold the replacement value:
1211 Calculating the new length of the buffer is simple. add current
1212 buffer pos (i) to length of string after replaced tag to length
1213 of replacement string and add 1 for the final zero ... */
1214 if (end - (*buf) < (i + valln)) {
1215 /* make sure we do not shrink the mallocd block */
1216 size_t newbufsize = i + strlen(end) + valln + 1;
1218 *buf = rrd_realloc(*buf, newbufsize);
1221 perror("Realoc buf:");
1226 /* Update new end pointer:
1227 make sure the 'end' pointer gets moved along with the
1228 buf pointer when realloc moves memory ... */
1229 end = (*buf) + end_offset;
1231 /* splice the variable:
1232 step 1. Shift pending data to make room for 'val' */
1233 memmove((*buf) + i + valln, end, strlen(end) + 1);
1235 /* step 2. Insert val */
1237 memmove((*buf) + i, val, valln);
1240 return (valln > 0 ? valln - 1 : valln);
1247 static char buf[60];
1249 tmptime = gmtime(now);
1250 strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S GMT", tmptime);
1258 printf("Content-type: %s\n", rrdcgiType);
1260 printf("Content-type: text/html\n");
1261 if (rrdcgiHeaderString)
1262 printf("%s", rrdcgiHeaderString);
1271 rrdcgiDebugLevel = level;
1273 rrdcgiDebugLevel = 0;
1275 rrdcgiDebugStderr = 0;
1277 rrdcgiDebugStderr = 1;
1280 char *rrdcgiDecodeString(
1285 for (cp = text, xp = text; *cp; cp++) {
1287 if (strchr("0123456789ABCDEFabcdef", *(cp + 1))
1288 && strchr("0123456789ABCDEFabcdef", *(cp + 2))) {
1289 if (islower(*(cp + 1)))
1290 *(cp + 1) = toupper(*(cp + 1));
1291 if (islower(*(cp + 2)))
1292 *(cp + 2) = toupper(*(cp + 2));
1295 'A' ? *(cp + 1) - 'A' + 10 : *(cp + 1) - '0') * 16 +
1297 'A' ? *(cp + 2) - 'A' + 10 : *(cp + 2) - '0');
1305 memset(xp, 0, cp - xp);
1309 /* rrdcgiReadVariables()
1311 * Read from stdin if no string is provided via CGI. Variables that
1312 * doesn't have a value associated with it doesn't get stored.
1314 s_var **rrdcgiReadVariables(
1320 char *cp, *ip, *esp, *sptr;
1325 cp = getenv("REQUEST_METHOD");
1326 ip = getenv("CONTENT_LENGTH");
1328 if (cp && !strcmp(cp, "POST")) {
1331 if ((line = (char *) malloc(length + 2)) == NULL)
1333 fgets(line, length + 1, stdin);
1336 } else if (cp && !strcmp(cp, "GET")) {
1337 esp = getenv("QUERY_STRING");
1338 if (esp && strlen(esp)) {
1339 if ((line = (char *) malloc(strlen(esp) + 2)) == NULL)
1341 sprintf(line, "%s", esp);
1346 printf("(offline mode: enter name=value pairs on standard input)\n");
1347 memset(tmp, 0, sizeof(tmp));
1348 while ((cp = fgets(tmp, 100, stdin)) != NULL) {
1350 if (tmp[strlen(tmp) - 1] == '\n')
1351 tmp[strlen(tmp) - 1] = '&';
1353 length += strlen(tmp);
1354 len = (length + 1) * sizeof(char);
1355 if ((line = (char *) realloc(line, len)) == NULL)
1359 length = strlen(tmp);
1360 len = (length + 1) * sizeof(char);
1361 if ((line = (char *) malloc(len)) == NULL)
1363 memset(line, 0, len);
1367 memset(tmp, 0, sizeof(tmp));
1371 if (line[strlen(line) - 1] == '&')
1372 line[strlen(line) - 1] = '\0';
1376 * From now on all cgi variables are stored in the variable line
1377 * and look like foo=bar&foobar=barfoo&foofoo=
1380 if (rrdcgiDebugLevel > 0) {
1381 if (rrdcgiDebugStderr)
1382 fprintf(stderr, "Received cgi input: %s\n", line);
1385 ("<b>Received cgi input</b><br>\n<pre>\n--\n%s\n--\n</pre>\n\n",
1389 for (cp = line; *cp; cp++)
1394 for (numargs = 1, cp = line; *cp; cp++)
1399 if (rrdcgiDebugLevel > 0) {
1400 if (rrdcgiDebugStderr)
1401 fprintf(stderr, "%d cgi variables found.\n", numargs);
1403 printf("%d cgi variables found.<br>\n", numargs);
1406 len = (numargs + 1) * sizeof(s_var *);
1407 if ((result = (s_var **) malloc(len)) == NULL)
1409 memset(result, 0, len);
1414 if ((ip = (char *) strchr(cp, '&')) != NULL) {
1417 ip = cp + strlen(cp);
1419 if ((esp = (char *) strchr(cp, '=')) == NULL) {
1431 /* try to find out if there's already such a variable */
1432 for (k = 0; k < i && (strncmp(result[k]->name, cp, esp - cp)
1433 || !(strlen(result[k]->name) ==
1434 (size_t) (esp - cp))); k++);
1436 if (k == i) { /* No such variable yet */
1437 if ((result[i] = (s_var *) malloc(sizeof(s_var))) == NULL)
1439 if ((result[i]->name =
1440 (char *) malloc((esp - cp + 1) * sizeof(char))) == NULL)
1442 memset(result[i]->name, 0, esp - cp + 1);
1443 strncpy(result[i]->name, cp, esp - cp);
1445 if ((result[i]->value =
1446 (char *) malloc((ip - esp + 1) * sizeof(char))) == NULL)
1448 memset(result[i]->value, 0, ip - esp + 1);
1449 strncpy(result[i]->value, cp, ip - esp);
1450 result[i]->value = rrdcgiDecodeString(result[i]->value);
1451 if (rrdcgiDebugLevel) {
1452 if (rrdcgiDebugStderr)
1453 fprintf(stderr, "%s: %s\n", result[i]->name,
1456 printf("<h3>Variable %s</h3>\n<pre>\n%s\n</pre>\n\n",
1457 result[i]->name, result[i]->value);
1460 } else { /* There is already such a name, suppose a mutiple field */
1463 (strlen(result[k]->value) + (ip - esp) +
1465 if ((sptr = (char *) malloc(len)) == NULL)
1467 memset(sptr, 0, len);
1468 sprintf(sptr, "%s\n", result[k]->value);
1469 strncat(sptr, cp, ip - esp);
1470 free(result[k]->value);
1471 result[k]->value = rrdcgiDecodeString(sptr);
1481 * Read from stdin if no string is provided via CGI. Variables that
1482 * doesn't have a value associated with it doesn't get stored.
1490 vars = rrdcgiReadVariables();
1495 if ((res = (s_cgi *) malloc(sizeof(s_cgi))) == NULL)
1502 char *rrdcgiGetValue(
1508 if (!parms || !parms->vars)
1510 for (i = 0; parms->vars[i]; i++)
1511 if (!strcmp(name, parms->vars[i]->name)) {
1512 if (rrdcgiDebugLevel > 0) {
1513 if (rrdcgiDebugStderr)
1514 fprintf(stderr, "%s found as %s\n", name,
1515 parms->vars[i]->value);
1517 printf("%s found as %s<br>\n", name,
1518 parms->vars[i]->value);
1520 return parms->vars[i]->value;
1522 if (rrdcgiDebugLevel) {
1523 if (rrdcgiDebugStderr)
1524 fprintf(stderr, "%s not found\n", name);
1526 printf("%s not found<br>\n", name);
1531 void rrdcgiFreeList(
1536 for (i = 0; list[i] != NULL; i++)
1549 for (i = 0; parms->vars[i]; i++) {
1550 if (parms->vars[i]->name)
1551 free(parms->vars[i]->name);
1552 if (parms->vars[i]->value)
1553 free(parms->vars[i]->value);
1554 free(parms->vars[i]);
1560 if (rrdcgiHeaderString) {
1561 free(rrdcgiHeaderString);
1562 rrdcgiHeaderString = NULL;