xref: /freebsd/usr.sbin/nscd/parser.c (revision f05cddf9)
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 
379 	TRACE_IN(parse_config_file);
380 	assert(config != NULL);
381 	assert(fname != NULL);
382 
383 	fin = fopen(fname, "r");
384 	if (fin == NULL) {
385 		TRACE_OUT(parse_config_file);
386 		return (-1);
387 	}
388 
389 	res = 0;
390 	line_num = 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 				set_positive_time_to_live(config,
425 					fields[1], value);
426 				continue;
427 			} else if ((field_count == 3) &&
428 			(strcmp(fields[0], "positive-confidence-threshold") == 0) &&
429 			((value = get_number(fields[2], 1, -1)) != -1)) {
430 				set_positive_confidence_threshold(config,
431 					fields[1], value);
432 				continue;
433 			} else if ((field_count == 3) &&
434 			(strcmp(fields[0], "positive-policy") == 0) &&
435 			(check_cachename(fields[1]) == 0) &&
436 			((value = get_policy(fields[2])) != -1)) {
437 				set_positive_policy(config, fields[1], value);
438 				continue;
439 			} else if ((field_count == 3) &&
440 			(strcmp(fields[0], "perform-actual-lookups") == 0) &&
441 			(check_cachename(fields[1]) == 0) &&
442 			((value = get_yesno(fields[2])) != -1)) {
443 				set_perform_actual_lookups(config, fields[1],
444 					value);
445 				continue;
446 			}
447 			break;
448 		case 'n':
449 			if ((field_count == 3) &&
450 			(strcmp(fields[0], "negative-time-to-live") == 0) &&
451 			(check_cachename(fields[1]) == 0) &&
452 			((value = get_number(fields[2], 0, -1)) != -1)) {
453 				set_negative_time_to_live(config,
454 					fields[1], value);
455 				continue;
456 			} else if ((field_count == 3) &&
457 			(strcmp(fields[0], "negative-confidence-threshold") == 0) &&
458 			((value = get_number(fields[2], 1, -1)) != -1)) {
459 				set_negative_confidence_threshold(config,
460 					fields[1], value);
461 				continue;
462 			} else if ((field_count == 3) &&
463 			(strcmp(fields[0], "negative-policy") == 0) &&
464 			(check_cachename(fields[1]) == 0) &&
465 			((value = get_policy(fields[2])) != -1)) {
466 				set_negative_policy(config,
467 					fields[1], value);
468 				continue;
469 			}
470 			break;
471 		case 's':
472 			if ((field_count == 3) &&
473 			(strcmp(fields[0], "suggested-size") == 0) &&
474 			(check_cachename(fields[1]) == 0) &&
475 			((value = get_number(fields[2], 1, -1)) != -1)) {
476 				set_suggested_size(config, fields[1], value);
477 				continue;
478 			}
479 			break;
480 		case 't':
481 			if ((field_count == 2) &&
482 			(strcmp(fields[0], "threads") == 0) &&
483 			((value = get_number(fields[1], 1, -1)) != -1)) {
484 				set_threads_num(config, value);
485 				continue;
486 			}
487 			break;
488 		case 'k':
489 			if ((field_count == 3) &&
490 			(strcmp(fields[0], "keep-hot-count") == 0) &&
491 			(check_cachename(fields[1]) == 0) &&
492 			((value = get_number(fields[2], 0, -1)) != -1)) {
493 				set_keep_hot_count(config,
494 					fields[1], value);
495 				continue;
496 			}
497 			break;
498 		case 'c':
499 			if ((field_count == 3) &&
500 			(strcmp(fields[0], "check-files") == 0) &&
501 			(check_cachename(fields[1]) == 0) &&
502 			((value = get_yesno(fields[2])) != -1)) {
503 				check_files(config,
504 					fields[1], value);
505 				continue;
506 			}
507 			break;
508 		default:
509 			break;
510 		}
511 
512 		LOG_ERR_2("config file parser", "error in file "
513 			"%s on line %d", fname, line_num);
514 		*error_str = "syntax error";
515 		*error_line = line_num;
516 		res = -1;
517 	}
518 	fclose(fin);
519 
520 	TRACE_OUT(parse_config_file);
521 	return (res);
522 }
523