1 /*-
2 * Copyright (c) 2005 Michael Bushkov <[email protected]>
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
strbreak(char * str,char ** fields,int fields_size)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 *
find_create_entry(struct configuration * config,const char * entry_name)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
enable_cache(struct configuration * config,const char * entry_name,int flag)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
set_positive_time_to_live(struct configuration * config,const char * entry_name,int ttl)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
set_negative_time_to_live(struct configuration * config,const char * entry_name,int nttl)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
set_positive_confidence_threshold(struct configuration * config,const char * entry_name,int conf_thresh)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
set_negative_confidence_threshold(struct configuration * config,const char * entry_name,int conf_thresh)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
set_keep_hot_count(struct configuration * config,const char * entry_name,int count)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
set_positive_policy(struct configuration * config,const char * entry_name,enum cache_policy_t policy)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
set_negative_policy(struct configuration * config,const char * entry_name,enum cache_policy_t policy)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
set_perform_actual_lookups(struct configuration * config,const char * entry_name,int flag)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
set_suggested_size(struct configuration * config,const char * entry_name,int size)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
check_files(struct configuration * config,const char * entry_name,int flag)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
get_yesno(const char * str)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
get_number(const char * str,int low,int max)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
get_policy(const char * str)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
check_cachename(const char * str)350 check_cachename(const char *str)
351 {
352
353 assert(str != NULL);
354 return ((strlen(str) > 0) ? 0 : -1);
355 }
356
357 static void
set_threads_num(struct configuration * config,int value)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
parse_config_file(struct configuration * config,const char * fname,char const ** error_str,int * error_line)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 int invalid_value;
379
380 TRACE_IN(parse_config_file);
381 assert(config != NULL);
382 assert(fname != NULL);
383
384 fin = fopen(fname, "r");
385 if (fin == NULL) {
386 TRACE_OUT(parse_config_file);
387 return (-1);
388 }
389
390 res = 0;
391 line_num = 0;
392 invalid_value = 0;
393 memset(buffer, 0, sizeof(buffer));
394 while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
395 field_count = strbreak(buffer, fields, sizeof(fields));
396 ++line_num;
397
398 if (field_count == 0)
399 continue;
400
401 switch (fields[0][0]) {
402 case '#':
403 case '\0':
404 continue;
405 case 'e':
406 if ((field_count == 3) &&
407 (strcmp(fields[0], "enable-cache") == 0) &&
408 (check_cachename(fields[1]) == 0) &&
409 ((value = get_yesno(fields[2])) != -1)) {
410 enable_cache(config, fields[1], value);
411 continue;
412 }
413 break;
414 case 'd':
415 if ((field_count == 2) &&
416 (strcmp(fields[0], "debug-level") == 0) &&
417 ((value = get_number(fields[1], 0, 10)) != -1)) {
418 continue;
419 }
420 break;
421 case 'p':
422 if ((field_count == 3) &&
423 (strcmp(fields[0], "positive-time-to-live") == 0) &&
424 (check_cachename(fields[1]) == 0) &&
425 ((value = get_number(fields[2], 0, -1)) != -1)) {
426 if (value <= 0) {
427 invalid_value = 1;
428 break;
429 }
430 set_positive_time_to_live(config,
431 fields[1], value);
432 continue;
433 } else if ((field_count == 3) &&
434 (strcmp(fields[0], "positive-confidence-threshold") == 0) &&
435 ((value = get_number(fields[2], 1, -1)) != -1)) {
436 if (value <= 0) {
437 invalid_value = 1;
438 break;
439 }
440 set_positive_confidence_threshold(config,
441 fields[1], value);
442 continue;
443 } else if ((field_count == 3) &&
444 (strcmp(fields[0], "positive-policy") == 0) &&
445 (check_cachename(fields[1]) == 0) &&
446 ((value = get_policy(fields[2])) != -1)) {
447 set_positive_policy(config, fields[1], value);
448 continue;
449 } else if ((field_count == 3) &&
450 (strcmp(fields[0], "perform-actual-lookups") == 0) &&
451 (check_cachename(fields[1]) == 0) &&
452 ((value = get_yesno(fields[2])) != -1)) {
453 set_perform_actual_lookups(config, fields[1],
454 value);
455 continue;
456 }
457 break;
458 case 'n':
459 if ((field_count == 3) &&
460 (strcmp(fields[0], "negative-time-to-live") == 0) &&
461 (check_cachename(fields[1]) == 0) &&
462 ((value = get_number(fields[2], 0, -1)) != -1)) {
463 if (value <= 0) {
464 invalid_value = 1;
465 break;
466 }
467 set_negative_time_to_live(config,
468 fields[1], value);
469 continue;
470 } else if ((field_count == 3) &&
471 (strcmp(fields[0], "negative-confidence-threshold") == 0) &&
472 ((value = get_number(fields[2], 1, -1)) != -1)) {
473 if (value <= 0) {
474 invalid_value = 1;
475 break;
476 }
477 set_negative_confidence_threshold(config,
478 fields[1], value);
479 continue;
480 } else if ((field_count == 3) &&
481 (strcmp(fields[0], "negative-policy") == 0) &&
482 (check_cachename(fields[1]) == 0) &&
483 ((value = get_policy(fields[2])) != -1)) {
484 set_negative_policy(config,
485 fields[1], value);
486 continue;
487 }
488 break;
489 case 's':
490 if ((field_count == 3) &&
491 (strcmp(fields[0], "suggested-size") == 0) &&
492 (check_cachename(fields[1]) == 0) &&
493 ((value = get_number(fields[2], 1, -1)) != -1)) {
494 if (value <= 0) {
495 invalid_value = 1;
496 break;
497 }
498 set_suggested_size(config, fields[1], value);
499 continue;
500 }
501 break;
502 case 't':
503 if ((field_count == 2) &&
504 (strcmp(fields[0], "threads") == 0) &&
505 ((value = get_number(fields[1], 1, -1)) != -1)) {
506 set_threads_num(config, value);
507 continue;
508 }
509 break;
510 case 'k':
511 if ((field_count == 3) &&
512 (strcmp(fields[0], "keep-hot-count") == 0) &&
513 (check_cachename(fields[1]) == 0) &&
514 ((value = get_number(fields[2], 0, -1)) != -1)) {
515 if (value < 0) {
516 invalid_value = 1;
517 break;
518 }
519 set_keep_hot_count(config,
520 fields[1], value);
521 continue;
522 }
523 break;
524 case 'c':
525 if ((field_count == 3) &&
526 (strcmp(fields[0], "check-files") == 0) &&
527 (check_cachename(fields[1]) == 0) &&
528 ((value = get_yesno(fields[2])) != -1)) {
529 check_files(config,
530 fields[1], value);
531 continue;
532 }
533 break;
534 default:
535 break;
536 }
537
538 if (invalid_value != 0) {
539 LOG_ERR_2("Invalid value for parameter",
540 "error in file %s on line %d",
541 fname, line_num);
542 *error_str = "invalid value";
543 } else {
544 LOG_ERR_2("config file parser", "error in file "
545 "%s on line %d", fname, line_num);
546 *error_str = "syntax error";
547 }
548 *error_line = line_num;
549 res = -1;
550 }
551 fclose(fin);
552
553 TRACE_OUT(parse_config_file);
554 return (res);
555 }
556