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