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 /* 171 * Hot count is actually the elements size limit. 172 */ 173 static void 174 set_keep_hot_count(struct configuration *config, 175 const char *entry_name, int count) 176 { 177 struct configuration_entry *entry; 178 179 TRACE_IN(set_keep_hot_count); 180 assert(count >= 0); 181 assert(entry_name != NULL); 182 183 entry = find_create_entry(config, entry_name); 184 assert(entry != NULL); 185 entry->positive_cache_params.max_elemsize = count; 186 187 entry = find_create_entry(config, entry_name); 188 assert(entry != NULL); 189 entry->negative_cache_params.max_elemsize = count; 190 191 TRACE_OUT(set_keep_hot_count); 192 } 193 194 static void 195 set_positive_policy(struct configuration *config, 196 const char *entry_name, enum cache_policy_t policy) 197 { 198 struct configuration_entry *entry; 199 200 TRACE_IN(set_positive_policy); 201 assert(entry_name != NULL); 202 203 entry = find_create_entry(config, entry_name); 204 assert(entry != NULL); 205 entry->positive_cache_params.policy = policy; 206 207 TRACE_OUT(set_positive_policy); 208 } 209 210 static void 211 set_negative_policy(struct configuration *config, 212 const char *entry_name, enum cache_policy_t policy) 213 { 214 struct configuration_entry *entry; 215 216 TRACE_IN(set_negative_policy); 217 assert(entry_name != NULL); 218 219 entry = find_create_entry(config, entry_name); 220 assert(entry != NULL); 221 entry->negative_cache_params.policy = policy; 222 223 TRACE_OUT(set_negative_policy); 224 } 225 226 static void 227 set_perform_actual_lookups(struct configuration *config, 228 const char *entry_name, int flag) 229 { 230 struct configuration_entry *entry; 231 232 TRACE_IN(set_perform_actual_lookups); 233 assert(entry_name != NULL); 234 235 entry = find_create_entry(config, entry_name); 236 assert(entry != NULL); 237 entry->perform_actual_lookups = flag; 238 239 TRACE_OUT(set_perform_actual_lookups); 240 } 241 242 static void 243 set_suggested_size(struct configuration *config, 244 const char *entry_name, int size) 245 { 246 struct configuration_entry *entry; 247 248 TRACE_IN(set_suggested_size); 249 assert(config != NULL); 250 assert(entry_name != NULL); 251 assert(size > 0); 252 253 entry = find_create_entry(config, entry_name); 254 assert(entry != NULL); 255 entry->positive_cache_params.cache_entries_size = size; 256 entry->negative_cache_params.cache_entries_size = size; 257 258 TRACE_OUT(set_suggested_size); 259 } 260 261 static void 262 check_files(struct configuration *config, const char *entry_name, int flag) 263 { 264 265 TRACE_IN(check_files); 266 assert(entry_name != NULL); 267 TRACE_OUT(check_files); 268 } 269 270 static int 271 get_yesno(const char *str) 272 { 273 274 if (strcmp(str, "yes") == 0) 275 return (1); 276 else if (strcmp(str, "no") == 0) 277 return (0); 278 else 279 return (-1); 280 } 281 282 static int 283 get_number(const char *str, int low, int max) 284 { 285 286 char *end = NULL; 287 int res = 0; 288 289 if (str[0] == '\0') 290 return (-1); 291 292 res = strtol(str, &end, 10); 293 if (*end != '\0') 294 return (-1); 295 else 296 if (((res >= low) || (low == -1)) && 297 ((res <= max) || (max == -1))) 298 return (res); 299 else 300 return (-2); 301 } 302 303 static enum cache_policy_t 304 get_policy(const char *str) 305 { 306 307 if (strcmp(str, "fifo") == 0) 308 return (CPT_FIFO); 309 else if (strcmp(str, "lru") == 0) 310 return (CPT_LRU); 311 else if (strcmp(str, "lfu") == 0) 312 return (CPT_LFU); 313 314 return (-1); 315 } 316 317 static int 318 check_cachename(const char *str) 319 { 320 321 assert(str != NULL); 322 return ((strlen(str) > 0) ? 0 : -1); 323 } 324 325 static void 326 set_threads_num(struct configuration *config, int value) 327 { 328 329 assert(config != NULL); 330 config->threads_num = value; 331 } 332 333 /* 334 * The main configuration routine. Its implementation is hugely inspired by the 335 * the same routine implementation in Solaris NSCD. 336 */ 337 int 338 parse_config_file(struct configuration *config, 339 const char *fname, char const **error_str, int *error_line) 340 { 341 FILE *fin; 342 char buffer[255]; 343 char *fields[128]; 344 int field_count, line_num, value; 345 int res; 346 347 TRACE_IN(parse_config_file); 348 assert(config != NULL); 349 assert(fname != NULL); 350 351 fin = fopen(fname, "r"); 352 if (fin == NULL) { 353 TRACE_OUT(parse_config_file); 354 return (-1); 355 } 356 357 res = 0; 358 line_num = 0; 359 memset(buffer, 0, sizeof(buffer)); 360 while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { 361 field_count = strbreak(buffer, fields, sizeof(fields)); 362 ++line_num; 363 364 if (field_count == 0) 365 continue; 366 367 switch (fields[0][0]) { 368 case '#': 369 case '\0': 370 continue; 371 case 'e': 372 if ((field_count == 3) && 373 (strcmp(fields[0], "enable-cache") == 0) && 374 (check_cachename(fields[1]) == 0) && 375 ((value = get_yesno(fields[2])) != -1)) { 376 enable_cache(config, fields[1], value); 377 continue; 378 } 379 break; 380 case 'd': 381 if ((field_count == 2) && 382 (strcmp(fields[0], "debug-level") == 0) && 383 ((value = get_number(fields[1], 0, 10)) != -1)) { 384 continue; 385 } 386 break; 387 case 'p': 388 if ((field_count == 3) && 389 (strcmp(fields[0], "positive-time-to-live") == 0) && 390 (check_cachename(fields[1]) == 0) && 391 ((value = get_number(fields[2], 0, -1)) != -1)) { 392 set_positive_time_to_live(config, 393 fields[1], value); 394 continue; 395 } else if ((field_count == 3) && 396 (strcmp(fields[0], "positive-policy") == 0) && 397 (check_cachename(fields[1]) == 0) && 398 ((value = get_policy(fields[2])) != -1)) { 399 set_positive_policy(config, fields[1], value); 400 continue; 401 } else if ((field_count == 3) && 402 (strcmp(fields[0], "perform-actual-lookups") == 0) && 403 (check_cachename(fields[1]) == 0) && 404 ((value = get_yesno(fields[2])) != -1)) { 405 set_perform_actual_lookups(config, fields[1], 406 value); 407 continue; 408 } 409 break; 410 case 'n': 411 if ((field_count == 3) && 412 (strcmp(fields[0], "negative-time-to-live") == 0) && 413 (check_cachename(fields[1]) == 0) && 414 ((value = get_number(fields[2], 0, -1)) != -1)) { 415 set_negative_time_to_live(config, 416 fields[1], value); 417 continue; 418 } else if ((field_count == 3) && 419 (strcmp(fields[0], "negative-policy") == 0) && 420 (check_cachename(fields[1]) == 0) && 421 ((value = get_policy(fields[2])) != -1)) { 422 set_negative_policy(config, 423 fields[1], value); 424 continue; 425 } 426 break; 427 case 's': 428 if ((field_count == 3) && 429 (strcmp(fields[0], "suggested-size") == 0) && 430 (check_cachename(fields[1]) == 0) && 431 ((value = get_number(fields[2], 1, -1)) != -1)) { 432 set_suggested_size(config, fields[1], value); 433 continue; 434 } 435 break; 436 case 't': 437 if ((field_count == 2) && 438 (strcmp(fields[0], "threads") == 0) && 439 ((value = get_number(fields[1], 1, -1)) != -1)) { 440 set_threads_num(config, value); 441 continue; 442 } 443 break; 444 case 'k': 445 if ((field_count == 3) && 446 (strcmp(fields[0], "keep-hot-count") == 0) && 447 (check_cachename(fields[1]) == 0) && 448 ((value = get_number(fields[2], 0, -1)) != -1)) { 449 set_keep_hot_count(config, 450 fields[1], value); 451 continue; 452 } 453 break; 454 case 'c': 455 if ((field_count == 3) && 456 (strcmp(fields[0], "check-files") == 0) && 457 (check_cachename(fields[1]) == 0) && 458 ((value = get_yesno(fields[2])) != -1)) { 459 check_files(config, 460 fields[1], value); 461 continue; 462 } 463 break; 464 default: 465 break; 466 } 467 468 LOG_ERR_2("config file parser", "error in file " 469 "%s on line %d", fname, line_num); 470 *error_str = "syntax error"; 471 *error_line = line_num; 472 res = -1; 473 } 474 fclose(fin); 475 476 TRACE_OUT(parse_config_file); 477 return (res); 478 } 479