xref: /dragonfly/usr.sbin/nscd/parser.c (revision 9348a738)
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  * $FreeBSD: src/usr.sbin/nscd/parser.c,v 1.2 2007/09/27 12:30:11 bushman Exp $
27  */
28 
29 #include <assert.h>
30 #include <stdio.h>
31 #include <string.h>
32 #include "config.h"
33 #include "debug.h"
34 #include "log.h"
35 #include "parser.h"
36 
37 static void enable_cache(struct configuration *,const char *, int);
38 static struct configuration_entry *find_create_entry(struct configuration *,
39 	const char *);
40 static int get_number(const char *, int, int);
41 static enum cache_policy_t get_policy(const char *);
42 static int get_yesno(const char *);
43 static int check_cachename(const char *);
44 static void check_files(struct configuration *, const char *, int);
45 static void set_keep_hot_count(struct configuration *, const char *, int);
46 static void set_negative_policy(struct configuration *, const char *,
47 	enum cache_policy_t);
48 static void set_negative_time_to_live(struct configuration *,
49 	const char *, int);
50 static void set_positive_policy(struct configuration *, const char *,
51 	enum cache_policy_t);
52 static void set_perform_actual_lookups(struct configuration *, const char *,
53 	int);
54 static void set_positive_time_to_live(struct configuration *,
55 	const char *, int);
56 static void set_suggested_size(struct configuration *, const char *,
57 	int size);
58 static void set_threads_num(struct configuration *, int);
59 static int strbreak(char *, char **, int);
60 
61 static int
62 strbreak(char *str, char **fields, int fields_size)
63 {
64 	char	*c = str;
65 	int	i, num;
66 
67 	TRACE_IN(strbreak);
68 	num = 0;
69 	for (i = 0;
70 	     ((*fields =
71 		strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
72 	     ++i)
73 		if ((*(*fields)) != '\0') {
74 			++fields;
75 			++num;
76 		}
77 
78 	TRACE_OUT(strbreak);
79 	return (num);
80 }
81 
82 /*
83  * Tries to find the configuration entry with the specified name. If search
84  * fails, the new entry with the default parameters will be created.
85  */
86 static struct configuration_entry *
87 find_create_entry(struct configuration *config,
88 	const char *entry_name)
89 {
90 	struct configuration_entry *entry = NULL;
91 	int res;
92 
93 	TRACE_IN(find_create_entry);
94 	entry = configuration_find_entry(config, entry_name);
95 	if (entry == NULL) {
96 		entry = create_def_configuration_entry(entry_name);
97 		assert( entry != NULL);
98 		res = add_configuration_entry(config, entry);
99 		assert(res == 0);
100 	}
101 
102 	TRACE_OUT(find_create_entry);
103 	return (entry);
104 }
105 
106 /*
107  * The vast majority of the functions below corresponds to the particular
108  * keywords in the configuration file.
109  */
110 static void
111 enable_cache(struct configuration *config, const char *entry_name, int flag)
112 {
113 	struct configuration_entry	*entry;
114 
115 	TRACE_IN(enable_cache);
116 	entry = find_create_entry(config, entry_name);
117 	entry->enabled = flag;
118 	TRACE_OUT(enable_cache);
119 }
120 
121 static void
122 set_positive_time_to_live(struct configuration *config,
123 	const char *entry_name, int ttl)
124 {
125 	struct configuration_entry *entry;
126 	struct timeval lifetime;
127 
128 	TRACE_IN(set_positive_time_to_live);
129 	assert(ttl >= 0);
130 	assert(entry_name != NULL);
131 	memset(&lifetime, 0, sizeof(struct timeval));
132 	lifetime.tv_sec = ttl;
133 
134 	entry = find_create_entry(config, entry_name);
135 	memcpy(&entry->positive_cache_params.max_lifetime,
136 		&lifetime, sizeof(struct timeval));
137 	memcpy(&entry->mp_cache_params.max_lifetime,
138 		&lifetime, sizeof(struct timeval));
139 
140 	TRACE_OUT(set_positive_time_to_live);
141 }
142 
143 static void
144 set_negative_time_to_live(struct configuration *config,
145 	const char *entry_name, int nttl)
146 {
147 	struct configuration_entry *entry;
148 	struct timeval lifetime;
149 
150 	TRACE_IN(set_negative_time_to_live);
151 	assert(nttl > 0);
152 	assert(entry_name != NULL);
153 	memset(&lifetime, 0, sizeof(struct timeval));
154 	lifetime.tv_sec = nttl;
155 
156 	entry = find_create_entry(config, entry_name);
157 	assert(entry != NULL);
158 	memcpy(&entry->negative_cache_params.max_lifetime,
159 		&lifetime, sizeof(struct timeval));
160 
161 	TRACE_OUT(set_negative_time_to_live);
162 }
163 
164 /*
165  * Hot count is actually the elements size limit.
166  */
167 static void
168 set_keep_hot_count(struct configuration *config,
169 	const char *entry_name, int count)
170 {
171 	struct configuration_entry *entry;
172 
173 	TRACE_IN(set_keep_hot_count);
174 	assert(count >= 0);
175 	assert(entry_name != NULL);
176 
177 	entry = find_create_entry(config, entry_name);
178 	assert(entry != NULL);
179 	entry->positive_cache_params.max_elemsize = count;
180 
181 	entry = find_create_entry(config, entry_name);
182 	assert(entry != NULL);
183 	entry->negative_cache_params.max_elemsize = count;
184 
185 	TRACE_OUT(set_keep_hot_count);
186 }
187 
188 static void
189 set_positive_policy(struct configuration *config,
190 	const char *entry_name, enum cache_policy_t policy)
191 {
192 	struct configuration_entry *entry;
193 
194 	TRACE_IN(set_positive_policy);
195 	assert(entry_name != NULL);
196 
197 	entry = find_create_entry(config, entry_name);
198 	assert(entry != NULL);
199 	entry->positive_cache_params.policy = policy;
200 
201 	TRACE_OUT(set_positive_policy);
202 }
203 
204 static void
205 set_negative_policy(struct configuration *config,
206 	const char *entry_name, enum cache_policy_t policy)
207 {
208 	struct configuration_entry *entry;
209 
210 	TRACE_IN(set_negative_policy);
211 	assert(entry_name != NULL);
212 
213 	entry = find_create_entry(config, entry_name);
214 	assert(entry != NULL);
215 	entry->negative_cache_params.policy = policy;
216 
217 	TRACE_OUT(set_negative_policy);
218 }
219 
220 static void
221 set_perform_actual_lookups(struct configuration *config,
222 	const char *entry_name, int flag)
223 {
224 	struct configuration_entry *entry;
225 
226 	TRACE_IN(set_perform_actual_lookups);
227 	assert(entry_name != NULL);
228 
229 	entry = find_create_entry(config, entry_name);
230 	assert(entry != NULL);
231 	entry->perform_actual_lookups = flag;
232 
233 	TRACE_OUT(set_perform_actual_lookups);
234 }
235 
236 static void
237 set_suggested_size(struct configuration *config,
238 	const char *entry_name, int size)
239 {
240 	struct configuration_entry	*entry;
241 
242 	TRACE_IN(set_suggested_size);
243 	assert(config != NULL);
244 	assert(entry_name != NULL);
245 	assert(size > 0);
246 
247 	entry = find_create_entry(config, entry_name);
248 	assert(entry != NULL);
249 	entry->positive_cache_params.cache_entries_size = size;
250 	entry->negative_cache_params.cache_entries_size = size;
251 
252 	TRACE_OUT(set_suggested_size);
253 }
254 
255 static void
256 check_files(struct configuration *config, const char *entry_name, int flag)
257 {
258 
259 	TRACE_IN(check_files);
260 	assert(entry_name != NULL);
261 	TRACE_OUT(check_files);
262 }
263 
264 static int
265 get_yesno(const char *str)
266 {
267 
268 	if (strcmp(str, "yes") == 0)
269 		return (1);
270 	else if (strcmp(str, "no") == 0)
271 		return (0);
272 	else
273 		return (-1);
274 }
275 
276 static int
277 get_number(const char *str, int low, int max)
278 {
279 
280 	char *end = NULL;
281 	int res = 0;
282 
283 	if (str[0] == '\0')
284 		return (-1);
285 
286 	res = strtol(str, &end, 10);
287 	if (*end != '\0')
288 		return (-1);
289 	else
290 		if (((res >= low) || (low == -1)) &&
291 			((res <= max) || (max == -1)))
292 			return (res);
293 		else
294 			return (-2);
295 }
296 
297 static enum cache_policy_t
298 get_policy(const char *str)
299 {
300 
301 	if (strcmp(str, "fifo") == 0)
302 		return (CPT_FIFO);
303 	else if (strcmp(str, "lru") == 0)
304 		return (CPT_LRU);
305 	else if (strcmp(str, "lfu") == 0)
306 		return (CPT_LFU);
307 
308 	return (-1);
309 }
310 
311 static int
312 check_cachename(const char *str)
313 {
314 
315 	assert(str != NULL);
316 	return ((strlen(str) > 0) ? 0 : -1);
317 }
318 
319 static void
320 set_threads_num(struct configuration *config, int value)
321 {
322 
323 	assert(config != NULL);
324 	config->threads_num = value;
325 }
326 
327 /*
328  * The main configuration routine. Its implementation is hugely inspired by the
329  * the same routine implementation in Solaris NSCD.
330  */
331 int
332 parse_config_file(struct configuration *config,
333 	const char *fname, char const **error_str, int *error_line)
334 {
335 	FILE	*fin;
336 	char	buffer[255];
337 	char	*fields[128];
338 	int	field_count, line_num, value;
339 	int	res;
340 
341 	TRACE_IN(parse_config_file);
342 	assert(config != NULL);
343 	assert(fname != NULL);
344 
345 	fin = fopen(fname, "r");
346 	if (fin == NULL) {
347 		TRACE_OUT(parse_config_file);
348 		return (-1);
349 	}
350 
351 	res = 0;
352 	line_num = 0;
353 	memset(buffer, 0, sizeof(buffer));
354 	while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
355 		field_count = strbreak(buffer, fields, sizeof(fields));
356 		++line_num;
357 
358 		if (field_count == 0)
359 			continue;
360 
361 		switch (fields[0][0]) {
362 		case '#':
363 		case '\0':
364 			continue;
365 		case 'e':
366 			if ((field_count == 3) &&
367 			(strcmp(fields[0], "enable-cache") == 0) &&
368 			(check_cachename(fields[1]) == 0) &&
369 			((value = get_yesno(fields[2])) != -1)) {
370 				enable_cache(config, fields[1], value);
371 				continue;
372 			}
373 			break;
374 		case 'd':
375 			if ((field_count == 2) &&
376 			(strcmp(fields[0], "debug-level") == 0) &&
377 			((value = get_number(fields[1], 0, 10)) != -1)) {
378 				continue;
379 			}
380 			break;
381 		case 'p':
382 			if ((field_count == 3) &&
383 			(strcmp(fields[0], "positive-time-to-live") == 0) &&
384 			(check_cachename(fields[1]) == 0) &&
385 			((value = get_number(fields[2], 0, -1)) != -1)) {
386 				set_positive_time_to_live(config,
387 					fields[1], value);
388 				continue;
389 			} else if ((field_count == 3) &&
390 			(strcmp(fields[0], "positive-policy") == 0) &&
391 			(check_cachename(fields[1]) == 0) &&
392 			((value = get_policy(fields[2])) != -1)) {
393 				set_positive_policy(config, fields[1], value);
394 				continue;
395 			} else if ((field_count == 3) &&
396 			(strcmp(fields[0], "perform-actual-lookups") == 0) &&
397 			(check_cachename(fields[1]) == 0) &&
398 			((value = get_yesno(fields[2])) != -1)) {
399 				set_perform_actual_lookups(config, fields[1],
400 					value);
401 				continue;
402 			}
403 			break;
404 		case 'n':
405 			if ((field_count == 3) &&
406 			(strcmp(fields[0], "negative-time-to-live") == 0) &&
407 			(check_cachename(fields[1]) == 0) &&
408 			((value = get_number(fields[2], 0, -1)) != -1)) {
409 				set_negative_time_to_live(config,
410 					fields[1], value);
411 				continue;
412 			} else if ((field_count == 3) &&
413 			(strcmp(fields[0], "negative-policy") == 0) &&
414 			(check_cachename(fields[1]) == 0) &&
415 			((value = get_policy(fields[2])) != -1)) {
416 				set_negative_policy(config,
417 					fields[1], value);
418 				continue;
419 			}
420 			break;
421 		case 's':
422 			if ((field_count == 3) &&
423 			(strcmp(fields[0], "suggested-size") == 0) &&
424 			(check_cachename(fields[1]) == 0) &&
425 			((value = get_number(fields[2], 1, -1)) != -1)) {
426 				set_suggested_size(config, fields[1], value);
427 				continue;
428 			}
429 			break;
430 		case 't':
431 			if ((field_count == 2) &&
432 			(strcmp(fields[0], "threads") == 0) &&
433 			((value = get_number(fields[1], 1, -1)) != -1)) {
434 				set_threads_num(config, value);
435 				continue;
436 			}
437 			break;
438 		case 'k':
439 			if ((field_count == 3) &&
440 			(strcmp(fields[0], "keep-hot-count") == 0) &&
441 			(check_cachename(fields[1]) == 0) &&
442 			((value = get_number(fields[2], 0, -1)) != -1)) {
443 				set_keep_hot_count(config,
444 					fields[1], value);
445 				continue;
446 			}
447 			break;
448 		case 'c':
449 			if ((field_count == 3) &&
450 			(strcmp(fields[0], "check-files") == 0) &&
451 			(check_cachename(fields[1]) == 0) &&
452 			((value = get_yesno(fields[2])) != -1)) {
453 				check_files(config,
454 					fields[1], value);
455 				continue;
456 			}
457 			break;
458 		default:
459 			break;
460 		}
461 
462 		LOG_ERR_2("config file parser", "error in file "
463 			"%s on line %d", fname, line_num);
464 		*error_str = "syntax error";
465 		*error_line = line_num;
466 		res = -1;
467 	}
468 	fclose(fin);
469 
470 	TRACE_OUT(parse_config_file);
471 	return (res);
472 }
473