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 <sys/time.h> 32 33 #include <assert.h> 34 #include <stdio.h> 35 #include <stdlib.h> 36 #include <string.h> 37 38 #include "config.h" 39 #include "debug.h" 40 #include "log.h" 41 #include "parser.h" 42 43 static void enable_cache(struct configuration *,const char *, int); 44 static struct configuration_entry *find_create_entry(struct configuration *, 45 const char *); 46 static int get_number(const char *, int, int); 47 static enum cache_policy_t get_policy(const char *); 48 static int get_yesno(const char *); 49 static int check_cachename(const char *); 50 static void check_files(struct configuration *, const char *, int); 51 static void set_keep_hot_count(struct configuration *, const char *, int); 52 static void set_negative_policy(struct configuration *, const char *, 53 enum cache_policy_t); 54 static void set_negative_time_to_live(struct configuration *, 55 const char *, int); 56 static void set_positive_policy(struct configuration *, const char *, 57 enum cache_policy_t); 58 static void set_perform_actual_lookups(struct configuration *, const char *, 59 int); 60 static void set_positive_time_to_live(struct configuration *, 61 const char *, int); 62 static void set_suggested_size(struct configuration *, const char *, 63 int size); 64 static void set_threads_num(struct configuration *, int); 65 static int strbreak(char *, char **, int); 66 67 static int 68 strbreak(char *str, char **fields, int fields_size) 69 { 70 char *c = str; 71 int i, num; 72 73 TRACE_IN(strbreak); 74 num = 0; 75 for (i = 0; 76 ((*fields = 77 strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL); 78 ++i) 79 if ((*(*fields)) != '\0') { 80 ++fields; 81 ++num; 82 } 83 84 TRACE_OUT(strbreak); 85 return (num); 86 } 87 88 /* 89 * Tries to find the configuration entry with the specified name. If search 90 * fails, the new entry with the default parameters will be created. 91 */ 92 static struct configuration_entry * 93 find_create_entry(struct configuration *config, 94 const char *entry_name) 95 { 96 struct configuration_entry *entry = NULL; 97 int res; 98 99 TRACE_IN(find_create_entry); 100 entry = configuration_find_entry(config, entry_name); 101 if (entry == NULL) { 102 entry = create_def_configuration_entry(entry_name); 103 assert( entry != NULL); 104 res = add_configuration_entry(config, entry); 105 assert(res == 0); 106 } 107 108 TRACE_OUT(find_create_entry); 109 return (entry); 110 } 111 112 /* 113 * The vast majority of the functions below corresponds to the particular 114 * keywords in the configuration file. 115 */ 116 static void 117 enable_cache(struct configuration *config, const char *entry_name, int flag) 118 { 119 struct configuration_entry *entry; 120 121 TRACE_IN(enable_cache); 122 entry = find_create_entry(config, entry_name); 123 entry->enabled = flag; 124 TRACE_OUT(enable_cache); 125 } 126 127 static void 128 set_positive_time_to_live(struct configuration *config, 129 const char *entry_name, int ttl) 130 { 131 struct configuration_entry *entry; 132 struct timeval lifetime; 133 134 TRACE_IN(set_positive_time_to_live); 135 assert(ttl >= 0); 136 assert(entry_name != NULL); 137 memset(&lifetime, 0, sizeof(struct timeval)); 138 lifetime.tv_sec = ttl; 139 140 entry = find_create_entry(config, entry_name); 141 memcpy(&entry->positive_cache_params.max_lifetime, 142 &lifetime, sizeof(struct timeval)); 143 memcpy(&entry->mp_cache_params.max_lifetime, 144 &lifetime, sizeof(struct timeval)); 145 146 TRACE_OUT(set_positive_time_to_live); 147 } 148 149 static void 150 set_negative_time_to_live(struct configuration *config, 151 const char *entry_name, int nttl) 152 { 153 struct configuration_entry *entry; 154 struct timeval lifetime; 155 156 TRACE_IN(set_negative_time_to_live); 157 assert(nttl > 0); 158 assert(entry_name != NULL); 159 memset(&lifetime, 0, sizeof(struct timeval)); 160 lifetime.tv_sec = nttl; 161 162 entry = find_create_entry(config, entry_name); 163 assert(entry != NULL); 164 memcpy(&entry->negative_cache_params.max_lifetime, 165 &lifetime, sizeof(struct timeval)); 166 167 TRACE_OUT(set_negative_time_to_live); 168 } 169 170 static void 171 set_positive_confidence_threshold(struct configuration *config, 172 const char *entry_name, int conf_thresh) 173 { 174 struct configuration_entry *entry; 175 176 TRACE_IN(set_positive_conf_thresh); 177 assert(conf_thresh > 0); 178 assert(entry_name != NULL); 179 180 entry = find_create_entry(config, entry_name); 181 assert(entry != NULL); 182 entry->positive_cache_params.confidence_threshold = conf_thresh; 183 184 TRACE_OUT(set_positive_conf_thresh); 185 } 186 187 static void 188 set_negative_confidence_threshold(struct configuration *config, 189 const char *entry_name, int conf_thresh) 190 { 191 struct configuration_entry *entry; 192 193 TRACE_IN(set_negative_conf_thresh); 194 assert(conf_thresh > 0); 195 assert(entry_name != NULL); 196 entry = find_create_entry(config, entry_name); 197 assert(entry != NULL); 198 entry->negative_cache_params.confidence_threshold = conf_thresh; 199 TRACE_OUT(set_negative_conf_thresh); 200 } 201 202 /* 203 * Hot count is actually the elements size limit. 204 */ 205 static void 206 set_keep_hot_count(struct configuration *config, 207 const char *entry_name, int count) 208 { 209 struct configuration_entry *entry; 210 211 TRACE_IN(set_keep_hot_count); 212 assert(count >= 0); 213 assert(entry_name != NULL); 214 215 entry = find_create_entry(config, entry_name); 216 assert(entry != NULL); 217 entry->positive_cache_params.max_elemsize = count; 218 219 entry = find_create_entry(config, entry_name); 220 assert(entry != NULL); 221 entry->negative_cache_params.max_elemsize = count; 222 223 TRACE_OUT(set_keep_hot_count); 224 } 225 226 static void 227 set_positive_policy(struct configuration *config, 228 const char *entry_name, enum cache_policy_t policy) 229 { 230 struct configuration_entry *entry; 231 232 TRACE_IN(set_positive_policy); 233 assert(entry_name != NULL); 234 235 entry = find_create_entry(config, entry_name); 236 assert(entry != NULL); 237 entry->positive_cache_params.policy = policy; 238 239 TRACE_OUT(set_positive_policy); 240 } 241 242 static void 243 set_negative_policy(struct configuration *config, 244 const char *entry_name, enum cache_policy_t policy) 245 { 246 struct configuration_entry *entry; 247 248 TRACE_IN(set_negative_policy); 249 assert(entry_name != NULL); 250 251 entry = find_create_entry(config, entry_name); 252 assert(entry != NULL); 253 entry->negative_cache_params.policy = policy; 254 255 TRACE_OUT(set_negative_policy); 256 } 257 258 static void 259 set_perform_actual_lookups(struct configuration *config, 260 const char *entry_name, int flag) 261 { 262 struct configuration_entry *entry; 263 264 TRACE_IN(set_perform_actual_lookups); 265 assert(entry_name != NULL); 266 267 entry = find_create_entry(config, entry_name); 268 assert(entry != NULL); 269 entry->perform_actual_lookups = flag; 270 271 TRACE_OUT(set_perform_actual_lookups); 272 } 273 274 static void 275 set_suggested_size(struct configuration *config, 276 const char *entry_name, int size) 277 { 278 struct configuration_entry *entry; 279 280 TRACE_IN(set_suggested_size); 281 assert(config != NULL); 282 assert(entry_name != NULL); 283 assert(size > 0); 284 285 entry = find_create_entry(config, entry_name); 286 assert(entry != NULL); 287 entry->positive_cache_params.cache_entries_size = size; 288 entry->negative_cache_params.cache_entries_size = size; 289 290 TRACE_OUT(set_suggested_size); 291 } 292 293 static void 294 check_files(struct configuration *config, const char *entry_name, int flag) 295 { 296 297 TRACE_IN(check_files); 298 assert(entry_name != NULL); 299 TRACE_OUT(check_files); 300 } 301 302 static int 303 get_yesno(const char *str) 304 { 305 306 if (strcmp(str, "yes") == 0) 307 return (1); 308 else if (strcmp(str, "no") == 0) 309 return (0); 310 else 311 return (-1); 312 } 313 314 static int 315 get_number(const char *str, int low, int max) 316 { 317 318 char *end = NULL; 319 int res = 0; 320 321 if (str[0] == '\0') 322 return (-1); 323 324 res = strtol(str, &end, 10); 325 if (*end != '\0') 326 return (-1); 327 else 328 if (((res >= low) || (low == -1)) && 329 ((res <= max) || (max == -1))) 330 return (res); 331 else 332 return (-2); 333 } 334 335 static enum cache_policy_t 336 get_policy(const char *str) 337 { 338 339 if (strcmp(str, "fifo") == 0) 340 return (CPT_FIFO); 341 else if (strcmp(str, "lru") == 0) 342 return (CPT_LRU); 343 else if (strcmp(str, "lfu") == 0) 344 return (CPT_LFU); 345 346 return (-1); 347 } 348 349 static int 350 check_cachename(const char *str) 351 { 352 353 assert(str != NULL); 354 return ((strlen(str) > 0) ? 0 : -1); 355 } 356 357 static void 358 set_threads_num(struct configuration *config, int value) 359 { 360 361 assert(config != NULL); 362 config->threads_num = value; 363 } 364 365 /* 366 * The main configuration routine. Its implementation is hugely inspired by the 367 * the same routine implementation in Solaris NSCD. 368 */ 369 int 370 parse_config_file(struct configuration *config, 371 const char *fname, char const **error_str, int *error_line) 372 { 373 FILE *fin; 374 char buffer[255]; 375 char *fields[128]; 376 int field_count, line_num, value; 377 int res; 378 int invalid_value; 379 380 TRACE_IN(parse_config_file); 381 assert(config != NULL); 382 assert(fname != NULL); 383 384 fin = fopen(fname, "r"); 385 if (fin == NULL) { 386 TRACE_OUT(parse_config_file); 387 return (-1); 388 } 389 390 res = 0; 391 line_num = 0; 392 invalid_value = 0; 393 memset(buffer, 0, sizeof(buffer)); 394 while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { 395 field_count = strbreak(buffer, fields, sizeof(fields)); 396 ++line_num; 397 398 if (field_count == 0) 399 continue; 400 401 switch (fields[0][0]) { 402 case '#': 403 case '\0': 404 continue; 405 case 'e': 406 if ((field_count == 3) && 407 (strcmp(fields[0], "enable-cache") == 0) && 408 (check_cachename(fields[1]) == 0) && 409 ((value = get_yesno(fields[2])) != -1)) { 410 enable_cache(config, fields[1], value); 411 continue; 412 } 413 break; 414 case 'd': 415 if ((field_count == 2) && 416 (strcmp(fields[0], "debug-level") == 0) && 417 ((value = get_number(fields[1], 0, 10)) != -1)) { 418 continue; 419 } 420 break; 421 case 'p': 422 if ((field_count == 3) && 423 (strcmp(fields[0], "positive-time-to-live") == 0) && 424 (check_cachename(fields[1]) == 0) && 425 ((value = get_number(fields[2], 0, -1)) != -1)) { 426 if (value <= 0) { 427 invalid_value = 1; 428 break; 429 } 430 set_positive_time_to_live(config, 431 fields[1], value); 432 continue; 433 } else if ((field_count == 3) && 434 (strcmp(fields[0], "positive-confidence-threshold") == 0) && 435 ((value = get_number(fields[2], 1, -1)) != -1)) { 436 if (value <= 0) { 437 invalid_value = 1; 438 break; 439 } 440 set_positive_confidence_threshold(config, 441 fields[1], value); 442 continue; 443 } else if ((field_count == 3) && 444 (strcmp(fields[0], "positive-policy") == 0) && 445 (check_cachename(fields[1]) == 0) && 446 ((value = get_policy(fields[2])) != -1)) { 447 set_positive_policy(config, fields[1], value); 448 continue; 449 } else if ((field_count == 3) && 450 (strcmp(fields[0], "perform-actual-lookups") == 0) && 451 (check_cachename(fields[1]) == 0) && 452 ((value = get_yesno(fields[2])) != -1)) { 453 set_perform_actual_lookups(config, fields[1], 454 value); 455 continue; 456 } 457 break; 458 case 'n': 459 if ((field_count == 3) && 460 (strcmp(fields[0], "negative-time-to-live") == 0) && 461 (check_cachename(fields[1]) == 0) && 462 ((value = get_number(fields[2], 0, -1)) != -1)) { 463 if (value <= 0) { 464 invalid_value = 1; 465 break; 466 } 467 set_negative_time_to_live(config, 468 fields[1], value); 469 continue; 470 } else if ((field_count == 3) && 471 (strcmp(fields[0], "negative-confidence-threshold") == 0) && 472 ((value = get_number(fields[2], 1, -1)) != -1)) { 473 if (value <= 0) { 474 invalid_value = 1; 475 break; 476 } 477 set_negative_confidence_threshold(config, 478 fields[1], value); 479 continue; 480 } else if ((field_count == 3) && 481 (strcmp(fields[0], "negative-policy") == 0) && 482 (check_cachename(fields[1]) == 0) && 483 ((value = get_policy(fields[2])) != -1)) { 484 set_negative_policy(config, 485 fields[1], value); 486 continue; 487 } 488 break; 489 case 's': 490 if ((field_count == 3) && 491 (strcmp(fields[0], "suggested-size") == 0) && 492 (check_cachename(fields[1]) == 0) && 493 ((value = get_number(fields[2], 1, -1)) != -1)) { 494 if (value <= 0) { 495 invalid_value = 1; 496 break; 497 } 498 set_suggested_size(config, fields[1], value); 499 continue; 500 } 501 break; 502 case 't': 503 if ((field_count == 2) && 504 (strcmp(fields[0], "threads") == 0) && 505 ((value = get_number(fields[1], 1, -1)) != -1)) { 506 set_threads_num(config, value); 507 continue; 508 } 509 break; 510 case 'k': 511 if ((field_count == 3) && 512 (strcmp(fields[0], "keep-hot-count") == 0) && 513 (check_cachename(fields[1]) == 0) && 514 ((value = get_number(fields[2], 0, -1)) != -1)) { 515 if (value < 0) { 516 invalid_value = 1; 517 break; 518 } 519 set_keep_hot_count(config, 520 fields[1], value); 521 continue; 522 } 523 break; 524 case 'c': 525 if ((field_count == 3) && 526 (strcmp(fields[0], "check-files") == 0) && 527 (check_cachename(fields[1]) == 0) && 528 ((value = get_yesno(fields[2])) != -1)) { 529 check_files(config, 530 fields[1], value); 531 continue; 532 } 533 break; 534 default: 535 break; 536 } 537 538 if (invalid_value != 0) { 539 LOG_ERR_2("Invalid value for parameter", 540 "error in file %s on line %d", 541 fname, line_num); 542 *error_str = "invalid value"; 543 } else { 544 LOG_ERR_2("config file parser", "error in file " 545 "%s on line %d", fname, line_num); 546 *error_str = "syntax error"; 547 } 548 *error_line = line_num; 549 res = -1; 550 } 551 fclose(fin); 552 553 TRACE_OUT(parse_config_file); 554 return (res); 555 } 556