1 #include <assert.h>
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <strings.h>
6 #include <string.h>
7 #include <ctype.h>
8 
9 #ifndef bool
10 #define bool char
11 #define false 0
12 #define true 1
13 #endif
14 
15 #include <memcached/config_parser.h>
16 #include <memcached/util.h>
17 
18 static int read_config_file(const char *fname, struct config_item items[],
19                             FILE *error);
20 
21 /**
22  * Copy a string and trim of leading and trailing white space characters.
23  * Allow the user to escape out the stop character by putting a backslash before
24  * the character.
25  * @param dest where to store the result
26  * @param size size of the result buffer
27  * @param src where to copy data from
28  * @param end the last character parsed is returned here
29  * @param stop the character to stop copying.
30  * @return 0 if success, -1 otherwise
31  */
trim_copy(char * dest,size_t size,const char * src,const char ** end,char stop)32 static int trim_copy(char *dest, size_t size, const char *src,
33                      const char **end, char stop) {
34    while (isspace(*src)) {
35       ++src;
36    }
37    size_t n = 0;
38    bool escape = false;
39    int ret = 0;
40 
41    /* Find the last non-escaped non-space character */
42    const char *lastchar = src + strlen(src) - 1;
43    while (lastchar > src && isspace(*lastchar)) {
44        lastchar--;
45    }
46    if (lastchar < src || *lastchar == '\\') {
47        lastchar++;
48    }
49    assert(lastchar >= src);
50 
51    do {
52       if ((*dest = *src) == '\\') {
53          escape = true;
54       } else {
55          escape = false;
56          ++dest;
57       }
58       ++n;
59       ++src;
60 
61    } while (!(n == size || src > lastchar || ((*src == stop) && !escape) || *src == '\0'));
62    *end = src;
63 
64    if (n == size) {
65        --dest;
66        ret = -1;
67    }
68    *dest = '\0';
69 
70    return ret;
71 }
72 
73 
parse_config(const char * str,struct config_item * items,FILE * error)74 int parse_config(const char *str, struct config_item *items, FILE *error) {
75    int ret = 0;
76    const char *ptr = str;
77 
78    while (*ptr != '\0') {
79       while (isspace(*ptr)) {
80          ++ptr;
81       }
82       if (*ptr == '\0') {
83          /* end of parameters */
84          return 0;
85       }
86 
87       const char *end;
88       char key[80];
89       if (trim_copy(key, sizeof(key), ptr, &end, '=') == -1) {
90          if (error != NULL) {
91             fprintf(error, "ERROR: Invalid key, starting at: <%s>\n", ptr);
92          }
93          return -1;
94       }
95 
96       ptr = end + 1;
97       char value[1024];
98       if (trim_copy(value, sizeof(value), ptr, &end, ';') == -1) {
99          if (error != NULL) {
100             fprintf(error, "ERROR: Invalid value, starting at: <%s>\n", ptr);
101          }
102          return -1;
103       }
104       if (*end == ';') {
105          ptr = end + 1;
106       } else {
107          ptr = end;
108       }
109 
110       int ii = 0;
111       while (items[ii].key != NULL) {
112          if (strcmp(key, items[ii].key) == 0) {
113             if (items[ii].found) {
114                if (error != NULL) {
115                   fprintf(error, "WARNING: Found duplicate entry for \"%s\"\n",
116                           items[ii].key);
117                }
118             }
119 
120             switch (items[ii].datatype) {
121             case DT_SIZE:
122                {
123                   const char *sfx = "kmgt";
124                   int multiplier = 1;
125                   int m = 1;
126                   for (const char *p = sfx; *p != '\0'; ++p) {
127                      m *= 1024;
128                      char *ptr = strchr(value, *p);
129                      if (ptr == NULL) {
130                         ptr = strchr(value, toupper(*p));
131                      }
132                      if (ptr != NULL) {
133                         multiplier = m;
134                         *ptr = '\0';
135                         break;
136                      }
137                   }
138 
139                   uint64_t val;
140                   if (safe_strtoull(value, &val)) {
141                      *items[ii].value.dt_size = (size_t)(val * multiplier);
142                      items[ii].found = true;
143                   } else {
144                      ret = -1;
145                   }
146                }
147                break;
148             case DT_FLOAT:
149                {
150                   float val;
151                   if (safe_strtof(value, &val)) {
152                      *items[ii].value.dt_float = val;
153                      items[ii].found = true;
154                   } else {
155                      ret = -1;
156                   }
157                }
158                break;
159             case DT_STRING:
160                *items[ii].value.dt_string = strdup(value);
161                items[ii].found = true;
162                break;
163             case DT_BOOL:
164                if (strcasecmp(value, "true") == 0 || strcasecmp(value, "on") == 0) {
165                   *items[ii].value.dt_bool = true;
166                   items[ii].found = true;
167                } else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "off") == 0) {
168                   *items[ii].value.dt_bool = false;
169                   items[ii].found = true;
170                } else {
171                   ret = -1;
172                }
173                break;
174             case DT_CONFIGFILE:
175                {
176                   int r = read_config_file(value, items, error);
177                   if (r != 0) {
178                      ret = r;
179                   }
180                }
181                break;
182             default:
183                /* You need to fix your code!!! */
184                abort();
185             }
186             if (ret == -1) {
187                if (error != NULL) {
188                   fprintf(error, "Invalid entry, Key: <%s> Value: <%s>\n",
189                           key, value);
190                }
191                return ret;
192             }
193             break;
194          }
195          ++ii;
196       }
197 
198       if (items[ii].key == NULL) {
199          if (error != NULL) {
200             fprintf(error, "Unsupported key: <%s>\n", key);
201          }
202          ret = 1;
203       }
204    }
205    return ret;
206 }
207 
read_config_file(const char * fname,struct config_item items[],FILE * error)208 static int read_config_file(const char *fname, struct config_item items[],
209                             FILE *error) {
210    FILE *fp = fopen(fname, "r");
211    if (fp == NULL) {
212       if (error != NULL) {
213          fprintf(error, "Failed to open file: %s\n", fname);
214       }
215       return -1;
216    }
217 
218    int ret = 0;
219    char line[1024];
220    while (fgets(line, sizeof(line), fp) != NULL && ret != -1L) {
221       if (line[0] == '#') {
222          /* Ignore comment line */
223          continue;
224       }
225 
226       int r = parse_config(line, items, error);
227       if (r != 0) {
228          ret = r;
229       }
230    }
231 
232    (void)fclose(fp);
233 
234    return ret;
235 }
236