1f603fdb6STrond Norbye#include "config.h"
2f603fdb6STrond Norbye
32bd6278aSTrond Norbye#include <errno.h>
42bd6278aSTrond Norbye#include <stdlib.h>
52bd6278aSTrond Norbye#include <stdio.h>
62bd6278aSTrond Norbye#include <strings.h>
72bd6278aSTrond Norbye#include <string.h>
82bd6278aSTrond Norbye#include <ctype.h>
92bd6278aSTrond Norbye#include <stdbool.h>
102bd6278aSTrond Norbye
115011fc53SSean Lynch#include <memcached/config_parser.h>
125011fc53SSean Lynch#include <memcached/util.h>
132bd6278aSTrond Norbye
142bd6278aSTrond Norbyestatic int read_config_file(const char *fname, struct config_item items[],
152bd6278aSTrond Norbye                            FILE *error);
162bd6278aSTrond Norbye
172bd6278aSTrond Norbye/**
182bd6278aSTrond Norbye * Copy a string and trim of leading and trailing white space characters.
192bd6278aSTrond Norbye * Allow the user to escape out the stop character by putting a backslash before
202bd6278aSTrond Norbye * the character.
212bd6278aSTrond Norbye * @param dest where to store the result
222bd6278aSTrond Norbye * @param size size of the result buffer
232bd6278aSTrond Norbye * @param src where to copy data from
242bd6278aSTrond Norbye * @param end the last character parsed is returned here
252bd6278aSTrond Norbye * @param stop the character to stop copying.
262bd6278aSTrond Norbye * @return 0 if success, -1 otherwise
272bd6278aSTrond Norbye */
282bd6278aSTrond Norbyestatic int trim_copy(char *dest, size_t size, const char *src,
292bd6278aSTrond Norbye                     const char **end, char stop) {
302bd6278aSTrond Norbye   size_t n = 0;
312bd6278aSTrond Norbye   bool escape = false;
322bd6278aSTrond Norbye   int ret = 0;
33f603fdb6STrond Norbye   const char *lastchar;
34f603fdb6STrond Norbye
35f603fdb6STrond Norbye   while (isspace(*src)) {
36f603fdb6STrond Norbye      ++src;
37f603fdb6STrond Norbye   }
382bd6278aSTrond Norbye
391ba7a69bSDustin Sallings   /* Find the last non-escaped non-space character */
40f603fdb6STrond Norbye   lastchar = src + strlen(src) - 1;
411ba7a69bSDustin Sallings   while (lastchar > src && isspace(*lastchar)) {
421ba7a69bSDustin Sallings       lastchar--;
431ba7a69bSDustin Sallings   }
441ba7a69bSDustin Sallings   if (lastchar < src || *lastchar == '\\') {
451ba7a69bSDustin Sallings       lastchar++;
461ba7a69bSDustin Sallings   }
47464d6f0bSTrond Norbye   cb_assert(lastchar >= src);
481ba7a69bSDustin Sallings
492bd6278aSTrond Norbye   do {
502bd6278aSTrond Norbye      if ((*dest = *src) == '\\') {
512bd6278aSTrond Norbye         escape = true;
522bd6278aSTrond Norbye      } else {
532bd6278aSTrond Norbye         escape = false;
542bd6278aSTrond Norbye         ++dest;
552bd6278aSTrond Norbye      }
562bd6278aSTrond Norbye      ++n;
572bd6278aSTrond Norbye      ++src;
582bd6278aSTrond Norbye
591ba7a69bSDustin Sallings   } while (!(n == size || src > lastchar || ((*src == stop) && !escape) || *src == '\0'));
602bd6278aSTrond Norbye   *end = src;
612bd6278aSTrond Norbye
621ba7a69bSDustin Sallings   if (n == size) {
631ba7a69bSDustin Sallings       --dest;
641ba7a69bSDustin Sallings       ret = -1;
652bd6278aSTrond Norbye   }
661ba7a69bSDustin Sallings   *dest = '\0';
672bd6278aSTrond Norbye
682bd6278aSTrond Norbye   return ret;
692bd6278aSTrond Norbye}
702bd6278aSTrond Norbye
712bd6278aSTrond Norbye
722bd6278aSTrond Norbyeint parse_config(const char *str, struct config_item *items, FILE *error) {
73f603fdb6STrond Norbye   const char *end;
74f603fdb6STrond Norbye   char key[80];
75f603fdb6STrond Norbye      char value[1024];
762bd6278aSTrond Norbye   int ret = 0;
772bd6278aSTrond Norbye   const char *ptr = str;
78f603fdb6STrond Norbye   int ii;
792bd6278aSTrond Norbye
802bd6278aSTrond Norbye   while (*ptr != '\0') {
812bd6278aSTrond Norbye      while (isspace(*ptr)) {
822bd6278aSTrond Norbye         ++ptr;
832bd6278aSTrond Norbye      }
842bd6278aSTrond Norbye      if (*ptr == '\0') {
852bd6278aSTrond Norbye         /* end of parameters */
862bd6278aSTrond Norbye         return 0;
872bd6278aSTrond Norbye      }
882bd6278aSTrond Norbye
892bd6278aSTrond Norbye      if (trim_copy(key, sizeof(key), ptr, &end, '=') == -1) {
90a233318dSTrond Norbye         if (error != NULL) {
91a233318dSTrond Norbye            fprintf(error, "ERROR: Invalid key, starting at: <%s>\n", ptr);
92a233318dSTrond Norbye         }
932bd6278aSTrond Norbye         return -1;
942bd6278aSTrond Norbye      }
952bd6278aSTrond Norbye
962bd6278aSTrond Norbye      ptr = end + 1;
972bd6278aSTrond Norbye      if (trim_copy(value, sizeof(value), ptr, &end, ';') == -1) {
98a233318dSTrond Norbye         if (error != NULL) {
99a233318dSTrond Norbye            fprintf(error, "ERROR: Invalid value, starting at: <%s>\n", ptr);
100a233318dSTrond Norbye         }
1012bd6278aSTrond Norbye         return -1;
1022bd6278aSTrond Norbye      }
1032bd6278aSTrond Norbye      if (*end == ';') {
1042bd6278aSTrond Norbye         ptr = end + 1;
1052bd6278aSTrond Norbye      } else {
1062bd6278aSTrond Norbye         ptr = end;
1072bd6278aSTrond Norbye      }
1082bd6278aSTrond Norbye
109f603fdb6STrond Norbye      ii = 0;
1102bd6278aSTrond Norbye      while (items[ii].key != NULL) {
1112bd6278aSTrond Norbye         if (strcmp(key, items[ii].key) == 0) {
1122bd6278aSTrond Norbye            if (items[ii].found) {
113a233318dSTrond Norbye               if (error != NULL) {
114a233318dSTrond Norbye                  fprintf(error, "WARNING: Found duplicate entry for \"%s\"\n",
115a233318dSTrond Norbye                          items[ii].key);
116a233318dSTrond Norbye               }
1172bd6278aSTrond Norbye            }
1182bd6278aSTrond Norbye
1192bd6278aSTrond Norbye            switch (items[ii].datatype) {
1202bd6278aSTrond Norbye            case DT_SIZE:
1212bd6278aSTrond Norbye               {
12295e72a8dSTrond Norbye                  char *sfx = "kmgt";
12395e72a8dSTrond Norbye                  int multiplier = 1;
12495e72a8dSTrond Norbye                  int m = 1;
125f603fdb6STrond Norbye                  char *p;
126f603fdb6STrond Norbye                  uint64_t val;
127f603fdb6STrond Norbye
128f603fdb6STrond Norbye                  for (p = sfx; *p != '\0'; ++p) {
12995e72a8dSTrond Norbye                     char *ptr = strchr(value, *p);
130f603fdb6STrond Norbye                     m *= 1024;
13195e72a8dSTrond Norbye                     if (ptr == NULL) {
13295e72a8dSTrond Norbye                        ptr = strchr(value, toupper(*p));
13395e72a8dSTrond Norbye                     }
13495e72a8dSTrond Norbye                     if (ptr != NULL) {
13595e72a8dSTrond Norbye                        multiplier = m;
13695e72a8dSTrond Norbye                        *ptr = '\0';
13795e72a8dSTrond Norbye                        break;
13895e72a8dSTrond Norbye                     }
13995e72a8dSTrond Norbye                  }
14095e72a8dSTrond Norbye
1412bd6278aSTrond Norbye                  if (safe_strtoull(value, &val)) {
14295e72a8dSTrond Norbye                     *items[ii].value.dt_size = (size_t)(val * multiplier);
1432bd6278aSTrond Norbye                     items[ii].found = true;
1442bd6278aSTrond Norbye                  } else {
1452bd6278aSTrond Norbye                     ret = -1;
1462bd6278aSTrond Norbye                  }
1472bd6278aSTrond Norbye               }
1482bd6278aSTrond Norbye               break;
1492bd6278aSTrond Norbye            case DT_FLOAT:
1502bd6278aSTrond Norbye               {
1512bd6278aSTrond Norbye                  float val;
1522bd6278aSTrond Norbye                  if (safe_strtof(value, &val)) {
1532bd6278aSTrond Norbye                     *items[ii].value.dt_float = val;
1542bd6278aSTrond Norbye                     items[ii].found = true;
1552bd6278aSTrond Norbye                  } else {
1562bd6278aSTrond Norbye                     ret = -1;
1572bd6278aSTrond Norbye                  }
1582bd6278aSTrond Norbye               }
1592bd6278aSTrond Norbye               break;
160ec5ceaedSDustin Sallings            case DT_STRING:
161ec5ceaedSDustin Sallings               *items[ii].value.dt_string = strdup(value);
162ec5ceaedSDustin Sallings               items[ii].found = true;
163ec5ceaedSDustin Sallings               break;
1642bd6278aSTrond Norbye            case DT_BOOL:
1652bd6278aSTrond Norbye               if (strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0) {
1662bd6278aSTrond Norbye                  *items[ii].value.dt_bool = true;
1672bd6278aSTrond Norbye                  items[ii].found = true;
1682bd6278aSTrond Norbye               } else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "off") == 0) {
1692bd6278aSTrond Norbye                  *items[ii].value.dt_bool = false;
1702bd6278aSTrond Norbye                  items[ii].found = true;
1712bd6278aSTrond Norbye               } else {
1722bd6278aSTrond Norbye                  ret = -1;
1732bd6278aSTrond Norbye               }
1742bd6278aSTrond Norbye               break;
1752bd6278aSTrond Norbye            case DT_CONFIGFILE:
1762bd6278aSTrond Norbye               {
1772bd6278aSTrond Norbye                  int r = read_config_file(value, items, error);
1782bd6278aSTrond Norbye                  if (r != 0) {
1792bd6278aSTrond Norbye                     ret = r;
1802bd6278aSTrond Norbye                  }
1812bd6278aSTrond Norbye               }
1822bd6278aSTrond Norbye               break;
1832bd6278aSTrond Norbye            default:
1842bd6278aSTrond Norbye               /* You need to fix your code!!! */
1852bd6278aSTrond Norbye               abort();
1862bd6278aSTrond Norbye            }
1872bd6278aSTrond Norbye            if (ret == -1) {
188a233318dSTrond Norbye               if (error != NULL) {
189a233318dSTrond Norbye                  fprintf(error, "Invalid entry, Key: <%s> Value: <%s>\n",
190a233318dSTrond Norbye                          key, value);
191a233318dSTrond Norbye               }
1922bd6278aSTrond Norbye               return ret;
1932bd6278aSTrond Norbye            }
1942bd6278aSTrond Norbye            break;
1952bd6278aSTrond Norbye         }
1962bd6278aSTrond Norbye         ++ii;
1972bd6278aSTrond Norbye      }
1982bd6278aSTrond Norbye
1992bd6278aSTrond Norbye      if (items[ii].key == NULL) {
200a233318dSTrond Norbye         if (error != NULL) {
201a233318dSTrond Norbye            fprintf(error, "Unsupported key: <%s>\n", key);
202a233318dSTrond Norbye         }
2032bd6278aSTrond Norbye         ret = 1;
2042bd6278aSTrond Norbye      }
2052bd6278aSTrond Norbye   }
2062bd6278aSTrond Norbye   return ret;
2072bd6278aSTrond Norbye}
2082bd6278aSTrond Norbye
2092bd6278aSTrond Norbyestatic int read_config_file(const char *fname, struct config_item items[],
2102bd6278aSTrond Norbye                            FILE *error) {
211f603fdb6STrond Norbye   char line[1024];
212f603fdb6STrond Norbye   int ret = 0;
2132bd6278aSTrond Norbye   FILE *fp = fopen(fname, "r");
2142bd6278aSTrond Norbye   if (fp == NULL) {
215a233318dSTrond Norbye      if (error != NULL) {
216a233318dSTrond Norbye         fprintf(error, "Failed to open file: %s\n", fname);
217a233318dSTrond Norbye      }
2182bd6278aSTrond Norbye      return -1;
2192bd6278aSTrond Norbye   }
2202bd6278aSTrond Norbye
221f603fdb6STrond Norbye   while (fgets(line, sizeof(line), fp) != NULL && ret != -1) {
222f603fdb6STrond Norbye      int r;
2232bd6278aSTrond Norbye      if (line[0] == '#') {
2242bd6278aSTrond Norbye         /* Ignore comment line */
2252bd6278aSTrond Norbye         continue;
2262bd6278aSTrond Norbye      }
2272bd6278aSTrond Norbye
228f603fdb6STrond Norbye      r = parse_config(line, items, error);
2292bd6278aSTrond Norbye      if (r != 0) {
2302bd6278aSTrond Norbye         ret = r;
2312bd6278aSTrond Norbye      }
2322bd6278aSTrond Norbye   }
2332bd6278aSTrond Norbye
2342bd6278aSTrond Norbye   (void)fclose(fp);
2352bd6278aSTrond Norbye
2362bd6278aSTrond Norbye   return ret;
2372bd6278aSTrond Norbye}
238