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