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 14static 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 */ 28static 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 72int 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 209static 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