10SN/A/*-
2157SN/A * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
30SN/A * All rights reserved.
40SN/A *
50SN/A * Redistribution and use in source and binary forms, with or without
60SN/A * modification, are permitted provided that the following conditions
7157SN/A * are met:
80SN/A * 1. Redistributions of source code must retain the above copyright
9157SN/A *    notice, this list of conditions and the following disclaimer.
100SN/A * 2. Redistributions in binary form must reproduce the above copyright
110SN/A *    notice, this list of conditions and the following disclaimer in the
120SN/A *    documentation and/or other materials provided with the distribution.
130SN/A *
140SN/A * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
150SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
170SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
180SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
190SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
200SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21157SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22157SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23157SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
240SN/A * SUCH DAMAGE.
250SN/A *
260SN/A */
270SN/A
280SN/A#include <sys/cdefs.h>
290SN/A#include <sys/time.h>
300SN/A
310SN/A#include <assert.h>
320SN/A#include <stdio.h>
330SN/A#include <stdlib.h>
340SN/A#include <string.h>
350SN/A
360SN/A#include "config.h"
370SN/A#include "debug.h"
380SN/A#include "log.h"
390SN/A#include "parser.h"
400SN/A
410SN/Astatic void enable_cache(struct configuration *,const char *, int);
420SN/Astatic struct configuration_entry *find_create_entry(struct configuration *,
430SN/A	const char *);
440SN/Astatic int get_number(const char *, int, int);
450SN/Astatic enum cache_policy_t get_policy(const char *);
460SN/Astatic int get_yesno(const char *);
470SN/Astatic int check_cachename(const char *);
480SN/Astatic void check_files(struct configuration *, const char *, int);
490SN/Astatic void set_keep_hot_count(struct configuration *, const char *, int);
500SN/Astatic void set_negative_policy(struct configuration *, const char *,
510SN/A	enum cache_policy_t);
520SN/Astatic void set_negative_time_to_live(struct configuration *,
530SN/A	const char *, int);
540SN/Astatic void set_positive_policy(struct configuration *, const char *,
550SN/A	enum cache_policy_t);
560SN/Astatic void set_perform_actual_lookups(struct configuration *, const char *,
570SN/A	int);
580SN/Astatic void set_positive_time_to_live(struct configuration *,
59	const char *, int);
60static void set_suggested_size(struct configuration *, const char *,
61	int size);
62static void set_threads_num(struct configuration *, int);
63static int strbreak(char *, char **, int);
64
65static int
66strbreak(char *str, char **fields, int fields_size)
67{
68	char	*c = str;
69	int	i, num;
70
71	TRACE_IN(strbreak);
72	num = 0;
73	for (i = 0;
74	     ((*fields =
75	     	strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
76	     ++i)
77		if ((*(*fields)) != '\0') {
78			++fields;
79			++num;
80		}
81
82	TRACE_OUT(strbreak);
83	return (num);
84}
85
86/*
87 * Tries to find the configuration entry with the specified name. If search
88 * fails, the new entry with the default parameters will be created.
89 */
90static struct configuration_entry *
91find_create_entry(struct configuration *config,
92	const char *entry_name)
93{
94	struct configuration_entry *entry = NULL;
95	int res;
96
97	TRACE_IN(find_create_entry);
98	entry = configuration_find_entry(config, entry_name);
99	if (entry == NULL) {
100		entry = create_def_configuration_entry(entry_name);
101		assert( entry != NULL);
102		res = add_configuration_entry(config, entry);
103		assert(res == 0);
104	}
105
106	TRACE_OUT(find_create_entry);
107	return (entry);
108}
109
110/*
111 * The vast majority of the functions below corresponds to the particular
112 * keywords in the configuration file.
113 */
114static void
115enable_cache(struct configuration *config, const char *entry_name, int flag)
116{
117	struct configuration_entry	*entry;
118
119	TRACE_IN(enable_cache);
120	entry = find_create_entry(config, entry_name);
121	entry->enabled = flag;
122	TRACE_OUT(enable_cache);
123}
124
125static void
126set_positive_time_to_live(struct configuration *config,
127	const char *entry_name, int ttl)
128{
129	struct configuration_entry *entry;
130	struct timeval lifetime;
131
132	TRACE_IN(set_positive_time_to_live);
133	assert(ttl >= 0);
134	assert(entry_name != NULL);
135	memset(&lifetime, 0, sizeof(struct timeval));
136	lifetime.tv_sec = ttl;
137
138	entry = find_create_entry(config, entry_name);
139	memcpy(&entry->positive_cache_params.max_lifetime,
140		&lifetime, sizeof(struct timeval));
141	memcpy(&entry->mp_cache_params.max_lifetime,
142		&lifetime, sizeof(struct timeval));
143
144	TRACE_OUT(set_positive_time_to_live);
145}
146
147static void
148set_negative_time_to_live(struct configuration *config,
149	const char *entry_name, int nttl)
150{
151	struct configuration_entry *entry;
152	struct timeval lifetime;
153
154	TRACE_IN(set_negative_time_to_live);
155	assert(nttl > 0);
156	assert(entry_name != NULL);
157	memset(&lifetime, 0, sizeof(struct timeval));
158	lifetime.tv_sec = nttl;
159
160	entry = find_create_entry(config, entry_name);
161	assert(entry != NULL);
162	memcpy(&entry->negative_cache_params.max_lifetime,
163		&lifetime, sizeof(struct timeval));
164
165	TRACE_OUT(set_negative_time_to_live);
166}
167
168static void
169set_positive_confidence_threshold(struct configuration *config,
170	const char *entry_name, int conf_thresh)
171{
172	struct configuration_entry *entry;
173
174	TRACE_IN(set_positive_conf_thresh);
175	assert(conf_thresh > 0);
176	assert(entry_name != NULL);
177
178	entry = find_create_entry(config, entry_name);
179	assert(entry != NULL);
180	entry->positive_cache_params.confidence_threshold = conf_thresh;
181
182	TRACE_OUT(set_positive_conf_thresh);
183}
184
185static void
186set_negative_confidence_threshold(struct configuration *config,
187	const char *entry_name, int conf_thresh)
188{
189	struct configuration_entry *entry;
190
191	TRACE_IN(set_negative_conf_thresh);
192	assert(conf_thresh > 0);
193	assert(entry_name != NULL);
194	entry = find_create_entry(config, entry_name);
195	assert(entry != NULL);
196	entry->negative_cache_params.confidence_threshold = conf_thresh;
197	TRACE_OUT(set_negative_conf_thresh);
198}
199
200/*
201 * Hot count is actually the elements size limit.
202 */
203static void
204set_keep_hot_count(struct configuration *config,
205	const char *entry_name, int count)
206{
207	struct configuration_entry *entry;
208
209	TRACE_IN(set_keep_hot_count);
210	assert(count >= 0);
211	assert(entry_name != NULL);
212
213	entry = find_create_entry(config, entry_name);
214	assert(entry != NULL);
215	entry->positive_cache_params.max_elemsize = count;
216
217	entry = find_create_entry(config, entry_name);
218	assert(entry != NULL);
219	entry->negative_cache_params.max_elemsize = count;
220
221	TRACE_OUT(set_keep_hot_count);
222}
223
224static void
225set_positive_policy(struct configuration *config,
226	const char *entry_name, enum cache_policy_t policy)
227{
228	struct configuration_entry *entry;
229
230	TRACE_IN(set_positive_policy);
231	assert(entry_name != NULL);
232
233	entry = find_create_entry(config, entry_name);
234	assert(entry != NULL);
235	entry->positive_cache_params.policy = policy;
236
237	TRACE_OUT(set_positive_policy);
238}
239
240static void
241set_negative_policy(struct configuration *config,
242	const char *entry_name, enum cache_policy_t policy)
243{
244	struct configuration_entry *entry;
245
246	TRACE_IN(set_negative_policy);
247	assert(entry_name != NULL);
248
249	entry = find_create_entry(config, entry_name);
250	assert(entry != NULL);
251	entry->negative_cache_params.policy = policy;
252
253	TRACE_OUT(set_negative_policy);
254}
255
256static void
257set_perform_actual_lookups(struct configuration *config,
258	const char *entry_name, int flag)
259{
260	struct configuration_entry *entry;
261
262	TRACE_IN(set_perform_actual_lookups);
263	assert(entry_name != NULL);
264
265	entry = find_create_entry(config, entry_name);
266	assert(entry != NULL);
267	entry->perform_actual_lookups = flag;
268
269	TRACE_OUT(set_perform_actual_lookups);
270}
271
272static void
273set_suggested_size(struct configuration *config,
274	const char *entry_name, int size)
275{
276	struct configuration_entry	*entry;
277
278	TRACE_IN(set_suggested_size);
279	assert(config != NULL);
280	assert(entry_name != NULL);
281	assert(size > 0);
282
283	entry = find_create_entry(config, entry_name);
284	assert(entry != NULL);
285	entry->positive_cache_params.cache_entries_size = size;
286	entry->negative_cache_params.cache_entries_size = size;
287
288	TRACE_OUT(set_suggested_size);
289}
290
291static void
292check_files(struct configuration *config, const char *entry_name, int flag)
293{
294
295	TRACE_IN(check_files);
296	assert(entry_name != NULL);
297	TRACE_OUT(check_files);
298}
299
300static int
301get_yesno(const char *str)
302{
303
304	if (strcmp(str, "yes") == 0)
305		return (1);
306	else if (strcmp(str, "no") == 0)
307		return (0);
308	else
309		return (-1);
310}
311
312static int
313get_number(const char *str, int low, int max)
314{
315
316	char *end = NULL;
317	int res = 0;
318
319	if (str[0] == '\0')
320		return (-1);
321
322	res = strtol(str, &end, 10);
323	if (*end != '\0')
324		return (-1);
325	else
326		if (((res >= low) || (low == -1)) &&
327			((res <= max) || (max == -1)))
328			return (res);
329		else
330			return (-2);
331}
332
333static enum cache_policy_t
334get_policy(const char *str)
335{
336
337	if (strcmp(str, "fifo") == 0)
338		return (CPT_FIFO);
339	else if (strcmp(str, "lru") == 0)
340		return (CPT_LRU);
341	else if (strcmp(str, "lfu") == 0)
342		return (CPT_LFU);
343
344	return (-1);
345}
346
347static int
348check_cachename(const char *str)
349{
350
351	assert(str != NULL);
352	return ((strlen(str) > 0) ? 0 : -1);
353}
354
355static void
356set_threads_num(struct configuration *config, int value)
357{
358
359	assert(config != NULL);
360	config->threads_num = value;
361}
362
363/*
364 * The main configuration routine. Its implementation is hugely inspired by the
365 * same routine implementation in Solaris NSCD.
366 */
367int
368parse_config_file(struct configuration *config,
369	const char *fname, char const **error_str, int *error_line)
370{
371	FILE	*fin;
372	char	buffer[255];
373	char	*fields[128];
374	int	field_count, line_num, value;
375	int	res;
376	int	invalid_value;
377
378	TRACE_IN(parse_config_file);
379	assert(config != NULL);
380	assert(fname != NULL);
381
382	fin = fopen(fname, "r");
383	if (fin == NULL) {
384		TRACE_OUT(parse_config_file);
385		return (-1);
386	}
387
388	res = 0;
389	line_num = 0;
390	invalid_value = 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				if (value <= 0) {
425					invalid_value = 1;
426					break;
427				}
428				set_positive_time_to_live(config,
429					fields[1], value);
430				continue;
431			} else if ((field_count == 3) &&
432			(strcmp(fields[0], "positive-confidence-threshold") == 0) &&
433			((value = get_number(fields[2], 1, -1)) != -1)) {
434				if (value <= 0) {
435					invalid_value = 1;
436					break;
437				}
438				set_positive_confidence_threshold(config,
439					fields[1], value);
440				continue;
441			} else if ((field_count == 3) &&
442			(strcmp(fields[0], "positive-policy") == 0) &&
443			(check_cachename(fields[1]) == 0) &&
444			((value = get_policy(fields[2])) != -1)) {
445				set_positive_policy(config, fields[1], value);
446				continue;
447			} else if ((field_count == 3) &&
448			(strcmp(fields[0], "perform-actual-lookups") == 0) &&
449			(check_cachename(fields[1]) == 0) &&
450			((value = get_yesno(fields[2])) != -1)) {
451				set_perform_actual_lookups(config, fields[1],
452					value);
453				continue;
454			}
455			break;
456		case 'n':
457			if ((field_count == 3) &&
458			(strcmp(fields[0], "negative-time-to-live") == 0) &&
459			(check_cachename(fields[1]) == 0) &&
460			((value = get_number(fields[2], 0, -1)) != -1)) {
461				if (value <= 0) {
462					invalid_value = 1;
463					break;
464				}
465				set_negative_time_to_live(config,
466					fields[1], value);
467				continue;
468			} else if ((field_count == 3) &&
469			(strcmp(fields[0], "negative-confidence-threshold") == 0) &&
470			((value = get_number(fields[2], 1, -1)) != -1)) {
471				if (value <= 0) {
472					invalid_value = 1;
473					break;
474				}
475				set_negative_confidence_threshold(config,
476					fields[1], value);
477				continue;
478			} else if ((field_count == 3) &&
479			(strcmp(fields[0], "negative-policy") == 0) &&
480			(check_cachename(fields[1]) == 0) &&
481			((value = get_policy(fields[2])) != -1)) {
482				set_negative_policy(config,
483					fields[1], value);
484				continue;
485			}
486			break;
487		case 's':
488			if ((field_count == 3) &&
489			(strcmp(fields[0], "suggested-size") == 0) &&
490			(check_cachename(fields[1]) == 0) &&
491			((value = get_number(fields[2], 1, -1)) != -1)) {
492				if (value <= 0) {
493					invalid_value = 1;
494					break;
495				}
496				set_suggested_size(config, fields[1], value);
497				continue;
498			}
499			break;
500		case 't':
501			if ((field_count == 2) &&
502			(strcmp(fields[0], "threads") == 0) &&
503			((value = get_number(fields[1], 1, -1)) != -1)) {
504				set_threads_num(config, value);
505				continue;
506			}
507			break;
508		case 'k':
509			if ((field_count == 3) &&
510			(strcmp(fields[0], "keep-hot-count") == 0) &&
511			(check_cachename(fields[1]) == 0) &&
512			((value = get_number(fields[2], 0, -1)) != -1)) {
513				if (value < 0) {
514					invalid_value = 1;
515					break;
516				}
517				set_keep_hot_count(config,
518					fields[1], value);
519				continue;
520			}
521			break;
522		case 'c':
523			if ((field_count == 3) &&
524			(strcmp(fields[0], "check-files") == 0) &&
525			(check_cachename(fields[1]) == 0) &&
526			((value = get_yesno(fields[2])) != -1)) {
527				check_files(config,
528					fields[1], value);
529				continue;
530			}
531			break;
532		default:
533			break;
534		}
535
536		if (invalid_value != 0) {
537			LOG_ERR_2("Invalid value for parameter",
538				"error in file %s on line %d",
539				fname, line_num);
540			*error_str = "invalid value";
541		} else {
542			LOG_ERR_2("config file parser", "error in file "
543				"%s on line %d", fname, line_num);
544			*error_str = "syntax error";
545		}
546		*error_line = line_num;
547		res = -1;
548	}
549	fclose(fin);
550
551	TRACE_OUT(parse_config_file);
552	return (res);
553}
554