xref: /freebsd/usr.sbin/nscd/parser.c (revision 3c4ba5f55438f7afd4f4b0b56f88f2bb505fd6a6)
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 static void
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
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
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
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
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
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
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
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
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
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
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
350 check_cachename(const char *str)
351 {
352 
353 	assert(str != NULL);
354 	return ((strlen(str) > 0) ? 0 : -1);
355 }
356 
357 static void
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
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