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