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