1 /*- 2 * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28 #include <sys/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <assert.h> 32 #include <stdio.h> 33 #include <string.h> 34 #include "config.h" 35 #include "debug.h" 36 #include "log.h" 37 #include "parser.h" 38 39 static void enable_cache(struct configuration *,const char *, int); 40 static struct configuration_entry *find_create_entry(struct configuration *, 41 const char *); 42 static int get_number(const char *, int, int); 43 static enum cache_policy_t get_policy(const char *); 44 static int get_yesno(const char *); 45 static int check_cachename(const char *); 46 static void check_files(struct configuration *, const char *, int); 47 static void set_keep_hot_count(struct configuration *, const char *, int); 48 static void set_negative_policy(struct configuration *, const char *, 49 enum cache_policy_t); 50 static void set_negative_time_to_live(struct configuration *, 51 const char *, int); 52 static void set_positive_policy(struct configuration *, const char *, 53 enum cache_policy_t); 54 static void set_perform_actual_lookups(struct configuration *, const char *, 55 int); 56 static void set_positive_time_to_live(struct configuration *, 57 const char *, int); 58 static void set_suggested_size(struct configuration *, const char *, 59 int size); 60 static void set_threads_num(struct configuration *, int); 61 static int strbreak(char *, char **, int); 62 63 static int 64 strbreak(char *str, char **fields, int fields_size) 65 { 66 char *c = str; 67 int i, num; 68 69 TRACE_IN(strbreak); 70 num = 0; 71 for (i = 0; 72 ((*fields = 73 strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL); 74 ++i) 75 if ((*(*fields)) != '\0') { 76 ++fields; 77 ++num; 78 } 79 80 TRACE_OUT(strbreak); 81 return (num); 82 } 83 84 /* 85 * Tries to find the configuration entry with the specified name. If search 86 * fails, the new entry with the default parameters will be created. 87 */ 88 static struct configuration_entry * 89 find_create_entry(struct configuration *config, 90 const char *entry_name) 91 { 92 struct configuration_entry *entry = NULL; 93 int res; 94 95 TRACE_IN(find_create_entry); 96 entry = configuration_find_entry(config, entry_name); 97 if (entry == NULL) { 98 entry = create_def_configuration_entry(entry_name); 99 assert( entry != NULL); 100 res = add_configuration_entry(config, entry); 101 assert(res == 0); 102 } 103 104 TRACE_OUT(find_create_entry); 105 return (entry); 106 } 107 108 /* 109 * The vast majority of the functions below corresponds to the particular 110 * keywords in the configuration file. 111 */ 112 static void 113 enable_cache(struct configuration *config, const char *entry_name, int flag) 114 { 115 struct configuration_entry *entry; 116 117 TRACE_IN(enable_cache); 118 entry = find_create_entry(config, entry_name); 119 entry->enabled = flag; 120 TRACE_OUT(enable_cache); 121 } 122 123 static void 124 set_positive_time_to_live(struct configuration *config, 125 const char *entry_name, int ttl) 126 { 127 struct configuration_entry *entry; 128 struct timeval lifetime; 129 130 TRACE_IN(set_positive_time_to_live); 131 assert(ttl >= 0); 132 assert(entry_name != NULL); 133 memset(&lifetime, 0, sizeof(struct timeval)); 134 lifetime.tv_sec = ttl; 135 136 entry = find_create_entry(config, entry_name); 137 memcpy(&entry->positive_cache_params.max_lifetime, 138 &lifetime, sizeof(struct timeval)); 139 memcpy(&entry->mp_cache_params.max_lifetime, 140 &lifetime, sizeof(struct timeval)); 141 142 TRACE_OUT(set_positive_time_to_live); 143 } 144 145 static void 146 set_negative_time_to_live(struct configuration *config, 147 const char *entry_name, int nttl) 148 { 149 struct configuration_entry *entry; 150 struct timeval lifetime; 151 152 TRACE_IN(set_negative_time_to_live); 153 assert(nttl > 0); 154 assert(entry_name != NULL); 155 memset(&lifetime, 0, sizeof(struct timeval)); 156 lifetime.tv_sec = nttl; 157 158 entry = find_create_entry(config, entry_name); 159 assert(entry != NULL); 160 memcpy(&entry->negative_cache_params.max_lifetime, 161 &lifetime, sizeof(struct timeval)); 162 163 TRACE_OUT(set_negative_time_to_live); 164 } 165 166 /* 167 * Hot count is actually the elements size limit. 168 */ 169 static void 170 set_keep_hot_count(struct configuration *config, 171 const char *entry_name, int count) 172 { 173 struct configuration_entry *entry; 174 175 TRACE_IN(set_keep_hot_count); 176 assert(count >= 0); 177 assert(entry_name != NULL); 178 179 entry = find_create_entry(config, entry_name); 180 assert(entry != NULL); 181 entry->positive_cache_params.max_elemsize = count; 182 183 entry = find_create_entry(config, entry_name); 184 assert(entry != NULL); 185 entry->negative_cache_params.max_elemsize = count; 186 187 TRACE_OUT(set_keep_hot_count); 188 } 189 190 static void 191 set_positive_policy(struct configuration *config, 192 const char *entry_name, enum cache_policy_t policy) 193 { 194 struct configuration_entry *entry; 195 196 TRACE_IN(set_positive_policy); 197 assert(entry_name != NULL); 198 199 entry = find_create_entry(config, entry_name); 200 assert(entry != NULL); 201 entry->positive_cache_params.policy = policy; 202 203 TRACE_OUT(set_positive_policy); 204 } 205 206 static void 207 set_negative_policy(struct configuration *config, 208 const char *entry_name, enum cache_policy_t policy) 209 { 210 struct configuration_entry *entry; 211 212 TRACE_IN(set_negative_policy); 213 assert(entry_name != NULL); 214 215 entry = find_create_entry(config, entry_name); 216 assert(entry != NULL); 217 entry->negative_cache_params.policy = policy; 218 219 TRACE_OUT(set_negative_policy); 220 } 221 222 static void 223 set_perform_actual_lookups(struct configuration *config, 224 const char *entry_name, int flag) 225 { 226 struct configuration_entry *entry; 227 228 TRACE_IN(set_perform_actual_lookups); 229 assert(entry_name != NULL); 230 231 entry = find_create_entry(config, entry_name); 232 assert(entry != NULL); 233 entry->perform_actual_lookups = flag; 234 235 TRACE_OUT(set_perform_actual_lookups); 236 } 237 238 static void 239 set_suggested_size(struct configuration *config, 240 const char *entry_name, int size) 241 { 242 struct configuration_entry *entry; 243 244 TRACE_IN(set_suggested_size); 245 assert(config != NULL); 246 assert(entry_name != NULL); 247 assert(size > 0); 248 249 entry = find_create_entry(config, entry_name); 250 assert(entry != NULL); 251 entry->positive_cache_params.cache_entries_size = size; 252 entry->negative_cache_params.cache_entries_size = size; 253 254 TRACE_OUT(set_suggested_size); 255 } 256 257 static void 258 check_files(struct configuration *config, const char *entry_name, int flag) 259 { 260 261 TRACE_IN(check_files); 262 assert(entry_name != NULL); 263 TRACE_OUT(check_files); 264 } 265 266 static int 267 get_yesno(const char *str) 268 { 269 270 if (strcmp(str, "yes") == 0) 271 return (1); 272 else if (strcmp(str, "no") == 0) 273 return (0); 274 else 275 return (-1); 276 } 277 278 static int 279 get_number(const char *str, int low, int max) 280 { 281 282 char *end = NULL; 283 int res = 0; 284 285 if (str[0] == '\0') 286 return (-1); 287 288 res = strtol(str, &end, 10); 289 if (*end != '\0') 290 return (-1); 291 else 292 if (((res >= low) || (low == -1)) && 293 ((res <= max) || (max == -1))) 294 return (res); 295 else 296 return (-2); 297 } 298 299 static enum cache_policy_t 300 get_policy(const char *str) 301 { 302 303 if (strcmp(str, "fifo") == 0) 304 return (CPT_FIFO); 305 else if (strcmp(str, "lru") == 0) 306 return (CPT_LRU); 307 else if (strcmp(str, "lfu") == 0) 308 return (CPT_LFU); 309 310 return (-1); 311 } 312 313 static int 314 check_cachename(const char *str) 315 { 316 317 assert(str != NULL); 318 return ((strlen(str) > 0) ? 0 : -1); 319 } 320 321 static void 322 set_threads_num(struct configuration *config, int value) 323 { 324 325 assert(config != NULL); 326 config->threads_num = value; 327 } 328 329 /* 330 * The main configuration routine. Its implementation is hugely inspired by the 331 * the same routine implementation in Solaris NSCD. 332 */ 333 int 334 parse_config_file(struct configuration *config, 335 const char *fname, char const **error_str, int *error_line) 336 { 337 FILE *fin; 338 char buffer[255]; 339 char *fields[128]; 340 int field_count, line_num, value; 341 int res; 342 343 TRACE_IN(parse_config_file); 344 assert(config != NULL); 345 assert(fname != NULL); 346 347 fin = fopen(fname, "r"); 348 if (fin == NULL) { 349 TRACE_OUT(parse_config_file); 350 return (-1); 351 } 352 353 res = 0; 354 line_num = 0; 355 memset(buffer, 0, sizeof(buffer)); 356 while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { 357 field_count = strbreak(buffer, fields, sizeof(fields)); 358 ++line_num; 359 360 if (field_count == 0) 361 continue; 362 363 switch (fields[0][0]) { 364 case '#': 365 case '\0': 366 continue; 367 case 'e': 368 if ((field_count == 3) && 369 (strcmp(fields[0], "enable-cache") == 0) && 370 (check_cachename(fields[1]) == 0) && 371 ((value = get_yesno(fields[2])) != -1)) { 372 enable_cache(config, fields[1], value); 373 continue; 374 } 375 break; 376 case 'd': 377 if ((field_count == 2) && 378 (strcmp(fields[0], "debug-level") == 0) && 379 ((value = get_number(fields[1], 0, 10)) != -1)) { 380 continue; 381 } 382 break; 383 case 'p': 384 if ((field_count == 3) && 385 (strcmp(fields[0], "positive-time-to-live") == 0) && 386 (check_cachename(fields[1]) == 0) && 387 ((value = get_number(fields[2], 0, -1)) != -1)) { 388 set_positive_time_to_live(config, 389 fields[1], value); 390 continue; 391 } else if ((field_count == 3) && 392 (strcmp(fields[0], "positive-policy") == 0) && 393 (check_cachename(fields[1]) == 0) && 394 ((value = get_policy(fields[2])) != -1)) { 395 set_positive_policy(config, fields[1], value); 396 continue; 397 } else if ((field_count == 3) && 398 (strcmp(fields[0], "perform-actual-lookups") == 0) && 399 (check_cachename(fields[1]) == 0) && 400 ((value = get_yesno(fields[2])) != -1)) { 401 set_perform_actual_lookups(config, fields[1], 402 value); 403 continue; 404 } 405 break; 406 case 'n': 407 if ((field_count == 3) && 408 (strcmp(fields[0], "negative-time-to-live") == 0) && 409 (check_cachename(fields[1]) == 0) && 410 ((value = get_number(fields[2], 0, -1)) != -1)) { 411 set_negative_time_to_live(config, 412 fields[1], value); 413 continue; 414 } else if ((field_count == 3) && 415 (strcmp(fields[0], "negative-policy") == 0) && 416 (check_cachename(fields[1]) == 0) && 417 ((value = get_policy(fields[2])) != -1)) { 418 set_negative_policy(config, 419 fields[1], value); 420 continue; 421 } 422 break; 423 case 's': 424 if ((field_count == 3) && 425 (strcmp(fields[0], "suggested-size") == 0) && 426 (check_cachename(fields[1]) == 0) && 427 ((value = get_number(fields[2], 1, -1)) != -1)) { 428 set_suggested_size(config, fields[1], value); 429 continue; 430 } 431 break; 432 case 't': 433 if ((field_count == 2) && 434 (strcmp(fields[0], "threads") == 0) && 435 ((value = get_number(fields[1], 1, -1)) != -1)) { 436 set_threads_num(config, value); 437 continue; 438 } 439 break; 440 case 'k': 441 if ((field_count == 3) && 442 (strcmp(fields[0], "keep-hot-count") == 0) && 443 (check_cachename(fields[1]) == 0) && 444 ((value = get_number(fields[2], 0, -1)) != -1)) { 445 set_keep_hot_count(config, 446 fields[1], value); 447 continue; 448 } 449 break; 450 case 'c': 451 if ((field_count == 3) && 452 (strcmp(fields[0], "check-files") == 0) && 453 (check_cachename(fields[1]) == 0) && 454 ((value = get_yesno(fields[2])) != -1)) { 455 check_files(config, 456 fields[1], value); 457 continue; 458 } 459 break; 460 default: 461 break; 462 } 463 464 LOG_ERR_2("config file parser", "error in file " 465 "%s on line %d", fname, line_num); 466 *error_str = "syntax error"; 467 *error_line = line_num; 468 res = -1; 469 } 470 fclose(fin); 471 472 TRACE_OUT(parse_config_file); 473 return (res); 474 } 475