157434Smarkm/*-
257434Smarkm * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3255460Sdes * All rights reserved.
4255460Sdes *
557434Smarkm * Redistribution and use in source and binary forms, with or without
6197679Sdes * modification, are permitted provided that the following conditions
7158519Sdes * are met:
857434Smarkm * 1. Redistributions of source code must retain the above copyright
9255460Sdes *    notice, this list of conditions and the following disclaimer.
10255460Sdes * 2. Redistributions in binary form must reproduce the above copyright
11197679Sdes *    notice, this list of conditions and the following disclaimer in the
12255460Sdes *    documentation and/or other materials provided with the distribution.
13197679Sdes *
14255460Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15255460Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16255386Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1774818Sru * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18255460Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19255460Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20255460Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21255460Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22255460Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23255460Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24255460Sdes * SUCH DAMAGE.
25255460Sdes *
26255460Sdes */
27255460Sdes
2857434Smarkm#include <sys/cdefs.h>
2957434Smarkm__FBSDID("$FreeBSD$");
3074818Sru
31158529Sdes#include <sys/time.h>
32158529Sdes
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
43static void enable_cache(struct configuration *,const char *, int);
44static struct configuration_entry *find_create_entry(struct configuration *,
45	const char *);
46static int get_number(const char *, int, int);
47static enum cache_policy_t get_policy(const char *);
48static int get_yesno(const char *);
49static int check_cachename(const char *);
50static void check_files(struct configuration *, const char *, int);
51static void set_keep_hot_count(struct configuration *, const char *, int);
52static void set_negative_policy(struct configuration *, const char *,
53	enum cache_policy_t);
54static void set_negative_time_to_live(struct configuration *,
55	const char *, int);
56static void set_positive_policy(struct configuration *, const char *,
57	enum cache_policy_t);
58static void set_perform_actual_lookups(struct configuration *, const char *,
59	int);
60static void set_positive_time_to_live(struct configuration *,
61	const char *, int);
62static void set_suggested_size(struct configuration *, const char *,
63	int size);
64static void set_threads_num(struct configuration *, int);
65static int strbreak(char *, char **, int);
66
67static int
68strbreak(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 */
92static struct configuration_entry *
93find_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 */
116static void
117enable_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
127static void
128set_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
149static void
150set_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
170static void
171set_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
187static void
188set_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 */
205static void
206set_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
226static void
227set_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
242static void
243set_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
258static void
259set_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
274static void
275set_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
293static void
294check_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
302static int
303get_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
314static int
315get_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
335static enum cache_policy_t
336get_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
349static int
350check_cachename(const char *str)
351{
352
353	assert(str != NULL);
354	return ((strlen(str) > 0) ? 0 : -1);
355}
356
357static void
358set_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 */
369int
370parse_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