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