parser.c revision 172344
159874Speter/*- 228861Skato * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru> 3139743Simp * All rights reserved. 428861Skato * 528861Skato * Redistribution and use in source and binary forms, with or without 628861Skato * modification, are permitted provided that the following conditions 728861Skato * are met: 828861Skato * 1. Redistributions of source code must retain the above copyright 928861Skato * notice, this list of conditions and the following disclaimer. 1028861Skato * 2. Redistributions in binary form must reproduce the above copyright 1128861Skato * notice, this list of conditions and the following disclaimer in the 1228861Skato * documentation and/or other materials provided with the distribution. 1328861Skato * 1428861Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1528861Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1628861Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1728861Skato * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1828861Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1928861Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2028861Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2128861Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2228861Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2328861Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2428861Skato * SUCH DAMAGE. 2528861Skato * 2628861Skato */ 2728861Skato 2828861Skato#include <sys/cdefs.h> 2928861Skato__FBSDID("$FreeBSD: head/usr.sbin/nscd/parser.c 158115 2006-04-28 12:03:38Z ume $"); 3028861Skato 3128861Skato#include <assert.h> 3228861Skato#include <stdio.h> 3328861Skato#include <string.h> 3428861Skato#include "config.h" 35126928Speter#include "debug.h" 36126928Speter#include "log.h" 3728861Skato#include "parser.h" 3828861Skato 3928861Skatostatic void enable_cache(struct configuration *,const char *, int); 4028861Skatostatic struct configuration_entry *find_create_entry(struct configuration *, 4128861Skato const char *); 4228861Skatostatic int get_number(const char *, int, int); 4328861Skatostatic enum cache_policy_t get_policy(const char *); 4428861Skatostatic int get_yesno(const char *); 4528861Skatostatic int check_cachename(const char *); 4628861Skatostatic void check_files(struct configuration *, const char *, int); 4728861Skatostatic void set_keep_hot_count(struct configuration *, const char *, int); 4828861Skatostatic void set_negative_policy(struct configuration *, const char *, 4928861Skato enum cache_policy_t); 5028861Skatostatic void set_negative_time_to_live(struct configuration *, 5128861Skato const char *, int); 5228861Skatostatic void set_positive_policy(struct configuration *, const char *, 5392761Salfred enum cache_policy_t); 5428861Skatostatic void set_perform_actual_lookups(struct configuration *, const char *, 5533044Sbde int); 5628861Skatostatic void set_positive_time_to_live(struct configuration *, 5728861Skato const char *, int); 5828861Skatostatic void set_suggested_size(struct configuration *, const char *, 5928861Skato int size); 6028861Skatostatic void set_threads_num(struct configuration *, int); 6133044Sbdestatic int strbreak(char *, char **, int); 6228861Skato 6328861Skatostatic int 6428861Skatostrbreak(char *str, char **fields, int fields_size) 65126928Speter{ 66 char *c = str; 67 int i, num; 68 69 TRACE_IN(strbreak); 70 num = 0; 71 for (i = 0; 72 ((*fields = 73 strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL); 74 ++i) 75 if ((*(*fields)) != '\0') { 76 ++fields; 77 ++num; 78 } 79 80 TRACE_OUT(strbreak); 81 return (num); 82} 83 84/* 85 * Tries to find the configuration entry with the specified name. If search 86 * fails, the new entry with the default parameters will be created. 87 */ 88static struct configuration_entry * 89find_create_entry(struct configuration *config, 90 const char *entry_name) 91{ 92 struct configuration_entry *entry = NULL; 93 int res; 94 95 TRACE_IN(find_create_entry); 96 entry = configuration_find_entry(config, entry_name); 97 if (entry == NULL) { 98 entry = create_def_configuration_entry(entry_name); 99 assert( entry != NULL); 100 res = add_configuration_entry(config, entry); 101 assert(res == 0); 102 } 103 104 TRACE_OUT(find_create_entry); 105 return (entry); 106} 107 108/* 109 * The vast majority of the functions below corresponds to the particular 110 * keywords in the configuration file. 111 */ 112static void 113enable_cache(struct configuration *config, const char *entry_name, int flag) 114{ 115 struct configuration_entry *entry; 116 117 TRACE_IN(enable_cache); 118 entry = find_create_entry(config, entry_name); 119 entry->enabled = flag; 120 TRACE_OUT(enable_cache); 121} 122 123static void 124set_positive_time_to_live(struct configuration *config, 125 const char *entry_name, int ttl) 126{ 127 struct configuration_entry *entry; 128 struct timeval lifetime; 129 130 TRACE_IN(set_positive_time_to_live); 131 assert(ttl >= 0); 132 assert(entry_name != NULL); 133 memset(&lifetime, 0, sizeof(struct timeval)); 134 lifetime.tv_sec = ttl; 135 136 entry = find_create_entry(config, entry_name); 137 memcpy(&entry->positive_cache_params.max_lifetime, 138 &lifetime, sizeof(struct timeval)); 139 memcpy(&entry->mp_cache_params.max_lifetime, 140 &lifetime, sizeof(struct timeval)); 141 142 TRACE_OUT(set_positive_time_to_live); 143} 144 145static void 146set_negative_time_to_live(struct configuration *config, 147 const char *entry_name, int nttl) 148{ 149 struct configuration_entry *entry; 150 struct timeval lifetime; 151 152 TRACE_IN(set_negative_time_to_live); 153 assert(nttl > 0); 154 assert(entry_name != NULL); 155 memset(&lifetime, 0, sizeof(struct timeval)); 156 lifetime.tv_sec = nttl; 157 158 entry = find_create_entry(config, entry_name); 159 assert(entry != NULL); 160 memcpy(&entry->negative_cache_params.max_lifetime, 161 &lifetime, sizeof(struct timeval)); 162 163 TRACE_OUT(set_negative_time_to_live); 164} 165 166/* 167 * Hot count is actually the elements size limit. 168 */ 169static void 170set_keep_hot_count(struct configuration *config, 171 const char *entry_name, int count) 172{ 173 struct configuration_entry *entry; 174 175 TRACE_IN(set_keep_hot_count); 176 assert(count >= 0); 177 assert(entry_name != NULL); 178 179 entry = find_create_entry(config, entry_name); 180 assert(entry != NULL); 181 entry->positive_cache_params.max_elemsize = count; 182 183 entry = find_create_entry(config, entry_name); 184 assert(entry != NULL); 185 entry->negative_cache_params.max_elemsize = count; 186 187 TRACE_OUT(set_keep_hot_count); 188} 189 190static void 191set_positive_policy(struct configuration *config, 192 const char *entry_name, enum cache_policy_t policy) 193{ 194 struct configuration_entry *entry; 195 196 TRACE_IN(set_positive_policy); 197 assert(entry_name != NULL); 198 199 entry = find_create_entry(config, entry_name); 200 assert(entry != NULL); 201 entry->positive_cache_params.policy = policy; 202 203 TRACE_OUT(set_positive_policy); 204} 205 206static void 207set_negative_policy(struct configuration *config, 208 const char *entry_name, enum cache_policy_t policy) 209{ 210 struct configuration_entry *entry; 211 212 TRACE_IN(set_negative_policy); 213 assert(entry_name != NULL); 214 215 entry = find_create_entry(config, entry_name); 216 assert(entry != NULL); 217 entry->negative_cache_params.policy = policy; 218 219 TRACE_OUT(set_negative_policy); 220} 221 222static void 223set_perform_actual_lookups(struct configuration *config, 224 const char *entry_name, int flag) 225{ 226 struct configuration_entry *entry; 227 228 TRACE_IN(set_perform_actual_lookups); 229 assert(entry_name != NULL); 230 231 entry = find_create_entry(config, entry_name); 232 assert(entry != NULL); 233 entry->perform_actual_lookups = flag; 234 235 TRACE_OUT(set_perform_actual_lookups); 236} 237 238static void 239set_suggested_size(struct configuration *config, 240 const char *entry_name, int size) 241{ 242 struct configuration_entry *entry; 243 244 TRACE_IN(set_suggested_size); 245 assert(config != NULL); 246 assert(entry_name != NULL); 247 assert(size > 0); 248 249 entry = find_create_entry(config, entry_name); 250 assert(entry != NULL); 251 entry->positive_cache_params.cache_entries_size = size; 252 entry->negative_cache_params.cache_entries_size = size; 253 254 TRACE_OUT(set_suggested_size); 255} 256 257static void 258check_files(struct configuration *config, const char *entry_name, int flag) 259{ 260 261 TRACE_IN(check_files); 262 assert(entry_name != NULL); 263 TRACE_OUT(check_files); 264} 265 266static int 267get_yesno(const char *str) 268{ 269 270 if (strcmp(str, "yes") == 0) 271 return (1); 272 else if (strcmp(str, "no") == 0) 273 return (0); 274 else 275 return (-1); 276} 277 278static int 279get_number(const char *str, int low, int max) 280{ 281 282 char *end = NULL; 283 int res = 0; 284 285 if (str[0] == '\0') 286 return (-1); 287 288 res = strtol(str, &end, 10); 289 if (*end != '\0') 290 return (-1); 291 else 292 if (((res >= low) || (low == -1)) && 293 ((res <= max) || (max == -1))) 294 return (res); 295 else 296 return (-2); 297} 298 299static enum cache_policy_t 300get_policy(const char *str) 301{ 302 303 if (strcmp(str, "fifo") == 0) 304 return (CPT_FIFO); 305 else if (strcmp(str, "lru") == 0) 306 return (CPT_LRU); 307 else if (strcmp(str, "lfu") == 0) 308 return (CPT_LFU); 309 310 return (-1); 311} 312 313static int 314check_cachename(const char *str) 315{ 316 317 assert(str != NULL); 318 return ((strlen(str) > 0) ? 0 : -1); 319} 320 321static void 322set_threads_num(struct configuration *config, int value) 323{ 324 325 assert(config != NULL); 326 config->threads_num = value; 327} 328 329/* 330 * The main configuration routine. Its implementation is hugely inspired by the 331 * the same routine implementation in Solaris NSCD. 332 */ 333int 334parse_config_file(struct configuration *config, 335 const char *fname, char const **error_str, int *error_line) 336{ 337 FILE *fin; 338 char buffer[255]; 339 char *fields[128]; 340 int field_count, line_num, value; 341 int res; 342 343 TRACE_IN(parse_config_file); 344 assert(config != NULL); 345 assert(fname != NULL); 346 347 fin = fopen(fname, "r"); 348 if (fin == NULL) { 349 TRACE_OUT(parse_config_file); 350 return (-1); 351 } 352 353 res = 0; 354 line_num = 0; 355 memset(buffer, 0, sizeof(buffer)); 356 while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) { 357 field_count = strbreak(buffer, fields, sizeof(fields)); 358 ++line_num; 359 360 if (field_count == 0) 361 continue; 362 363 switch (fields[0][0]) { 364 case '#': 365 case '\0': 366 continue; 367 case 'e': 368 if ((field_count == 3) && 369 (strcmp(fields[0], "enable-cache") == 0) && 370 (check_cachename(fields[1]) == 0) && 371 ((value = get_yesno(fields[2])) != -1)) { 372 enable_cache(config, fields[1], value); 373 continue; 374 } 375 break; 376 case 'd': 377 if ((field_count == 2) && 378 (strcmp(fields[0], "debug-level") == 0) && 379 ((value = get_number(fields[1], 0, 10)) != -1)) { 380 continue; 381 } 382 break; 383 case 'p': 384 if ((field_count == 3) && 385 (strcmp(fields[0], "positive-time-to-live") == 0) && 386 (check_cachename(fields[1]) == 0) && 387 ((value = get_number(fields[2], 0, -1)) != -1)) { 388 set_positive_time_to_live(config, 389 fields[1], value); 390 continue; 391 } else if ((field_count == 3) && 392 (strcmp(fields[0], "positive-policy") == 0) && 393 (check_cachename(fields[1]) == 0) && 394 ((value = get_policy(fields[2])) != -1)) { 395 set_positive_policy(config, fields[1], value); 396 continue; 397 } else if ((field_count == 3) && 398 (strcmp(fields[0], "perform-actual-lookups") == 0) && 399 (check_cachename(fields[1]) == 0) && 400 ((value = get_yesno(fields[2])) != -1)) { 401 set_perform_actual_lookups(config, fields[1], 402 value); 403 continue; 404 } 405 break; 406 case 'n': 407 if ((field_count == 3) && 408 (strcmp(fields[0], "negative-time-to-live") == 0) && 409 (check_cachename(fields[1]) == 0) && 410 ((value = get_number(fields[2], 0, -1)) != -1)) { 411 set_negative_time_to_live(config, 412 fields[1], value); 413 continue; 414 } else if ((field_count == 3) && 415 (strcmp(fields[0], "negative-policy") == 0) && 416 (check_cachename(fields[1]) == 0) && 417 ((value = get_policy(fields[2])) != -1)) { 418 set_negative_policy(config, 419 fields[1], value); 420 continue; 421 } 422 break; 423 case 's': 424 if ((field_count == 3) && 425 (strcmp(fields[0], "suggested-size") == 0) && 426 (check_cachename(fields[1]) == 0) && 427 ((value = get_number(fields[2], 1, -1)) != -1)) { 428 set_suggested_size(config, fields[1], value); 429 continue; 430 } 431 break; 432 case 't': 433 if ((field_count == 2) && 434 (strcmp(fields[0], "threads") == 0) && 435 ((value = get_number(fields[1], 1, -1)) != -1)) { 436 set_threads_num(config, value); 437 continue; 438 } 439 break; 440 case 'k': 441 if ((field_count == 3) && 442 (strcmp(fields[0], "keep-hot-count") == 0) && 443 (check_cachename(fields[1]) == 0) && 444 ((value = get_number(fields[2], 0, -1)) != -1)) { 445 set_keep_hot_count(config, 446 fields[1], value); 447 continue; 448 } 449 break; 450 case 'c': 451 if ((field_count == 3) && 452 (strcmp(fields[0], "check-files") == 0) && 453 (check_cachename(fields[1]) == 0) && 454 ((value = get_yesno(fields[2])) != -1)) { 455 check_files(config, 456 fields[1], value); 457 continue; 458 } 459 break; 460 default: 461 break; 462 } 463 464 LOG_ERR_2("config file parser", "error in file " 465 "%s on line %d", fname, line_num); 466 *error_str = "syntax error"; 467 *error_line = line_num; 468 res = -1; 469 } 470 fclose(fin); 471 472 TRACE_OUT(parse_config_file); 473 return (res); 474} 475