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 379 TRACE_IN(parse_config_file); 380 assert(config != NULL); 381 assert(fname != NULL); 382 383 fin = fopen(fname, "r"); 384 if (fin == NULL) { 385 TRACE_OUT(parse_config_file); 386 return (-1); 387 } 388 389 res = 0; 390 line_num = 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 set_positive_time_to_live(config, 425 fields[1], value); 426 continue; 427 } else if ((field_count == 3) && 428 (strcmp(fields[0], "positive-confidence-threshold") == 0) && 429 ((value = get_number(fields[2], 1, -1)) != -1)) { 430 set_positive_confidence_threshold(config, 431 fields[1], value); 432 continue; 433 } else if ((field_count == 3) && 434 (strcmp(fields[0], "positive-policy") == 0) && 435 (check_cachename(fields[1]) == 0) && 436 ((value = get_policy(fields[2])) != -1)) { 437 set_positive_policy(config, fields[1], value); 438 continue; 439 } else if ((field_count == 3) && 440 (strcmp(fields[0], "perform-actual-lookups") == 0) && 441 (check_cachename(fields[1]) == 0) && 442 ((value = get_yesno(fields[2])) != -1)) { 443 set_perform_actual_lookups(config, fields[1], 444 value); 445 continue; 446 } 447 break; 448 case 'n': 449 if ((field_count == 3) && 450 (strcmp(fields[0], "negative-time-to-live") == 0) && 451 (check_cachename(fields[1]) == 0) && 452 ((value = get_number(fields[2], 0, -1)) != -1)) { 453 set_negative_time_to_live(config, 454 fields[1], value); 455 continue; 456 } else if ((field_count == 3) && 457 (strcmp(fields[0], "negative-confidence-threshold") == 0) && 458 ((value = get_number(fields[2], 1, -1)) != -1)) { 459 set_negative_confidence_threshold(config, 460 fields[1], value); 461 continue; 462 } else if ((field_count == 3) && 463 (strcmp(fields[0], "negative-policy") == 0) && 464 (check_cachename(fields[1]) == 0) && 465 ((value = get_policy(fields[2])) != -1)) { 466 set_negative_policy(config, 467 fields[1], value); 468 continue; 469 } 470 break; 471 case 's': 472 if ((field_count == 3) && 473 (strcmp(fields[0], "suggested-size") == 0) && 474 (check_cachename(fields[1]) == 0) && 475 ((value = get_number(fields[2], 1, -1)) != -1)) { 476 set_suggested_size(config, fields[1], value); 477 continue; 478 } 479 break; 480 case 't': 481 if ((field_count == 2) && 482 (strcmp(fields[0], "threads") == 0) && 483 ((value = get_number(fields[1], 1, -1)) != -1)) { 484 set_threads_num(config, value); 485 continue; 486 } 487 break; 488 case 'k': 489 if ((field_count == 3) && 490 (strcmp(fields[0], "keep-hot-count") == 0) && 491 (check_cachename(fields[1]) == 0) && 492 ((value = get_number(fields[2], 0, -1)) != -1)) { 493 set_keep_hot_count(config, 494 fields[1], value); 495 continue; 496 } 497 break; 498 case 'c': 499 if ((field_count == 3) && 500 (strcmp(fields[0], "check-files") == 0) && 501 (check_cachename(fields[1]) == 0) && 502 ((value = get_yesno(fields[2])) != -1)) { 503 check_files(config, 504 fields[1], value); 505 continue; 506 } 507 break; 508 default: 509 break; 510 } 511 512 LOG_ERR_2("config file parser", "error in file " 513 "%s on line %d", fname, line_num); 514 *error_str = "syntax error"; 515 *error_line = line_num; 516 res = -1; 517 } 518 fclose(fin); 519 520 TRACE_OUT(parse_config_file); 521 return (res); 522 } 523