xref: /freebsd/usr.sbin/nscd/parser.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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
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 *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
348 check_cachename(const char *str)
349 {
350 
351 	assert(str != NULL);
352 	return ((strlen(str) > 0) ? 0 : -1);
353 }
354 
355 static void
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  * the same routine implementation in Solaris NSCD.
366  */
367 int
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