xref: /freebsd/usr.sbin/nscd/parser.c (revision 884a2a699669ec61e2366e3e358342dbc94be24a)
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 /*
171  * Hot count is actually the elements size limit.
172  */
173 static void
174 set_keep_hot_count(struct configuration *config,
175 	const char *entry_name, int count)
176 {
177 	struct configuration_entry *entry;
178 
179 	TRACE_IN(set_keep_hot_count);
180 	assert(count >= 0);
181 	assert(entry_name != NULL);
182 
183 	entry = find_create_entry(config, entry_name);
184 	assert(entry != NULL);
185 	entry->positive_cache_params.max_elemsize = count;
186 
187 	entry = find_create_entry(config, entry_name);
188 	assert(entry != NULL);
189 	entry->negative_cache_params.max_elemsize = count;
190 
191 	TRACE_OUT(set_keep_hot_count);
192 }
193 
194 static void
195 set_positive_policy(struct configuration *config,
196 	const char *entry_name, enum cache_policy_t policy)
197 {
198 	struct configuration_entry *entry;
199 
200 	TRACE_IN(set_positive_policy);
201 	assert(entry_name != NULL);
202 
203 	entry = find_create_entry(config, entry_name);
204 	assert(entry != NULL);
205 	entry->positive_cache_params.policy = policy;
206 
207 	TRACE_OUT(set_positive_policy);
208 }
209 
210 static void
211 set_negative_policy(struct configuration *config,
212 	const char *entry_name, enum cache_policy_t policy)
213 {
214 	struct configuration_entry *entry;
215 
216 	TRACE_IN(set_negative_policy);
217 	assert(entry_name != NULL);
218 
219 	entry = find_create_entry(config, entry_name);
220 	assert(entry != NULL);
221 	entry->negative_cache_params.policy = policy;
222 
223 	TRACE_OUT(set_negative_policy);
224 }
225 
226 static void
227 set_perform_actual_lookups(struct configuration *config,
228 	const char *entry_name, int flag)
229 {
230 	struct configuration_entry *entry;
231 
232 	TRACE_IN(set_perform_actual_lookups);
233 	assert(entry_name != NULL);
234 
235 	entry = find_create_entry(config, entry_name);
236 	assert(entry != NULL);
237 	entry->perform_actual_lookups = flag;
238 
239 	TRACE_OUT(set_perform_actual_lookups);
240 }
241 
242 static void
243 set_suggested_size(struct configuration *config,
244 	const char *entry_name, int size)
245 {
246 	struct configuration_entry	*entry;
247 
248 	TRACE_IN(set_suggested_size);
249 	assert(config != NULL);
250 	assert(entry_name != NULL);
251 	assert(size > 0);
252 
253 	entry = find_create_entry(config, entry_name);
254 	assert(entry != NULL);
255 	entry->positive_cache_params.cache_entries_size = size;
256 	entry->negative_cache_params.cache_entries_size = size;
257 
258 	TRACE_OUT(set_suggested_size);
259 }
260 
261 static void
262 check_files(struct configuration *config, const char *entry_name, int flag)
263 {
264 
265 	TRACE_IN(check_files);
266 	assert(entry_name != NULL);
267 	TRACE_OUT(check_files);
268 }
269 
270 static int
271 get_yesno(const char *str)
272 {
273 
274 	if (strcmp(str, "yes") == 0)
275 		return (1);
276 	else if (strcmp(str, "no") == 0)
277 		return (0);
278 	else
279 		return (-1);
280 }
281 
282 static int
283 get_number(const char *str, int low, int max)
284 {
285 
286 	char *end = NULL;
287 	int res = 0;
288 
289 	if (str[0] == '\0')
290 		return (-1);
291 
292 	res = strtol(str, &end, 10);
293 	if (*end != '\0')
294 		return (-1);
295 	else
296 		if (((res >= low) || (low == -1)) &&
297 			((res <= max) || (max == -1)))
298 			return (res);
299 		else
300 			return (-2);
301 }
302 
303 static enum cache_policy_t
304 get_policy(const char *str)
305 {
306 
307 	if (strcmp(str, "fifo") == 0)
308 		return (CPT_FIFO);
309 	else if (strcmp(str, "lru") == 0)
310 		return (CPT_LRU);
311 	else if (strcmp(str, "lfu") == 0)
312 		return (CPT_LFU);
313 
314 	return (-1);
315 }
316 
317 static int
318 check_cachename(const char *str)
319 {
320 
321 	assert(str != NULL);
322 	return ((strlen(str) > 0) ? 0 : -1);
323 }
324 
325 static void
326 set_threads_num(struct configuration *config, int value)
327 {
328 
329 	assert(config != NULL);
330 	config->threads_num = value;
331 }
332 
333 /*
334  * The main configuration routine. Its implementation is hugely inspired by the
335  * the same routine implementation in Solaris NSCD.
336  */
337 int
338 parse_config_file(struct configuration *config,
339 	const char *fname, char const **error_str, int *error_line)
340 {
341 	FILE	*fin;
342 	char	buffer[255];
343 	char	*fields[128];
344 	int	field_count, line_num, value;
345 	int	res;
346 
347 	TRACE_IN(parse_config_file);
348 	assert(config != NULL);
349 	assert(fname != NULL);
350 
351 	fin = fopen(fname, "r");
352 	if (fin == NULL) {
353 		TRACE_OUT(parse_config_file);
354 		return (-1);
355 	}
356 
357 	res = 0;
358 	line_num = 0;
359 	memset(buffer, 0, sizeof(buffer));
360 	while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
361 		field_count = strbreak(buffer, fields, sizeof(fields));
362 		++line_num;
363 
364 		if (field_count == 0)
365 			continue;
366 
367 		switch (fields[0][0]) {
368 		case '#':
369 		case '\0':
370 			continue;
371 		case 'e':
372 			if ((field_count == 3) &&
373 			(strcmp(fields[0], "enable-cache") == 0) &&
374 			(check_cachename(fields[1]) == 0) &&
375 			((value = get_yesno(fields[2])) != -1)) {
376 				enable_cache(config, fields[1], value);
377 				continue;
378 			}
379 			break;
380 		case 'd':
381 			if ((field_count == 2) &&
382 			(strcmp(fields[0], "debug-level") == 0) &&
383 			((value = get_number(fields[1], 0, 10)) != -1)) {
384 				continue;
385 			}
386 			break;
387 		case 'p':
388 			if ((field_count == 3) &&
389 			(strcmp(fields[0], "positive-time-to-live") == 0) &&
390 			(check_cachename(fields[1]) == 0) &&
391 			((value = get_number(fields[2], 0, -1)) != -1)) {
392 				set_positive_time_to_live(config,
393 					fields[1], value);
394 				continue;
395 			} else if ((field_count == 3) &&
396 			(strcmp(fields[0], "positive-policy") == 0) &&
397 			(check_cachename(fields[1]) == 0) &&
398 			((value = get_policy(fields[2])) != -1)) {
399 				set_positive_policy(config, fields[1], value);
400 				continue;
401 			} else if ((field_count == 3) &&
402 			(strcmp(fields[0], "perform-actual-lookups") == 0) &&
403 			(check_cachename(fields[1]) == 0) &&
404 			((value = get_yesno(fields[2])) != -1)) {
405 				set_perform_actual_lookups(config, fields[1],
406 					value);
407 				continue;
408 			}
409 			break;
410 		case 'n':
411 			if ((field_count == 3) &&
412 			(strcmp(fields[0], "negative-time-to-live") == 0) &&
413 			(check_cachename(fields[1]) == 0) &&
414 			((value = get_number(fields[2], 0, -1)) != -1)) {
415 				set_negative_time_to_live(config,
416 					fields[1], value);
417 				continue;
418 			} else if ((field_count == 3) &&
419 			(strcmp(fields[0], "negative-policy") == 0) &&
420 			(check_cachename(fields[1]) == 0) &&
421 			((value = get_policy(fields[2])) != -1)) {
422 				set_negative_policy(config,
423 					fields[1], value);
424 				continue;
425 			}
426 			break;
427 		case 's':
428 			if ((field_count == 3) &&
429 			(strcmp(fields[0], "suggested-size") == 0) &&
430 			(check_cachename(fields[1]) == 0) &&
431 			((value = get_number(fields[2], 1, -1)) != -1)) {
432 				set_suggested_size(config, fields[1], value);
433 				continue;
434 			}
435 			break;
436 		case 't':
437 			if ((field_count == 2) &&
438 			(strcmp(fields[0], "threads") == 0) &&
439 			((value = get_number(fields[1], 1, -1)) != -1)) {
440 				set_threads_num(config, value);
441 				continue;
442 			}
443 			break;
444 		case 'k':
445 			if ((field_count == 3) &&
446 			(strcmp(fields[0], "keep-hot-count") == 0) &&
447 			(check_cachename(fields[1]) == 0) &&
448 			((value = get_number(fields[2], 0, -1)) != -1)) {
449 				set_keep_hot_count(config,
450 					fields[1], value);
451 				continue;
452 			}
453 			break;
454 		case 'c':
455 			if ((field_count == 3) &&
456 			(strcmp(fields[0], "check-files") == 0) &&
457 			(check_cachename(fields[1]) == 0) &&
458 			((value = get_yesno(fields[2])) != -1)) {
459 				check_files(config,
460 					fields[1], value);
461 				continue;
462 			}
463 			break;
464 		default:
465 			break;
466 		}
467 
468 		LOG_ERR_2("config file parser", "error in file "
469 			"%s on line %d", fname, line_num);
470 		*error_str = "syntax error";
471 		*error_line = line_num;
472 		res = -1;
473 	}
474 	fclose(fin);
475 
476 	TRACE_OUT(parse_config_file);
477 	return (res);
478 }
479