parser.c revision 172344
159874Speter/*-
228861Skato * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
3139743Simp * All rights reserved.
428861Skato *
528861Skato * Redistribution and use in source and binary forms, with or without
628861Skato * modification, are permitted provided that the following conditions
728861Skato * are met:
828861Skato * 1. Redistributions of source code must retain the above copyright
928861Skato *    notice, this list of conditions and the following disclaimer.
1028861Skato * 2. Redistributions in binary form must reproduce the above copyright
1128861Skato *    notice, this list of conditions and the following disclaimer in the
1228861Skato *    documentation and/or other materials provided with the distribution.
1328861Skato *
1428861Skato * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1528861Skato * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1628861Skato * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1728861Skato * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1828861Skato * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1928861Skato * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2028861Skato * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2128861Skato * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2228861Skato * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2328861Skato * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2428861Skato * SUCH DAMAGE.
2528861Skato *
2628861Skato */
2728861Skato
2828861Skato#include <sys/cdefs.h>
2928861Skato__FBSDID("$FreeBSD: head/usr.sbin/nscd/parser.c 158115 2006-04-28 12:03:38Z ume $");
3028861Skato
3128861Skato#include <assert.h>
3228861Skato#include <stdio.h>
3328861Skato#include <string.h>
3428861Skato#include "config.h"
35126928Speter#include "debug.h"
36126928Speter#include "log.h"
3728861Skato#include "parser.h"
3828861Skato
3928861Skatostatic void enable_cache(struct configuration *,const char *, int);
4028861Skatostatic struct configuration_entry *find_create_entry(struct configuration *,
4128861Skato	const char *);
4228861Skatostatic int get_number(const char *, int, int);
4328861Skatostatic enum cache_policy_t get_policy(const char *);
4428861Skatostatic int get_yesno(const char *);
4528861Skatostatic int check_cachename(const char *);
4628861Skatostatic void check_files(struct configuration *, const char *, int);
4728861Skatostatic void set_keep_hot_count(struct configuration *, const char *, int);
4828861Skatostatic void set_negative_policy(struct configuration *, const char *,
4928861Skato	enum cache_policy_t);
5028861Skatostatic void set_negative_time_to_live(struct configuration *,
5128861Skato	const char *, int);
5228861Skatostatic void set_positive_policy(struct configuration *, const char *,
5392761Salfred	enum cache_policy_t);
5428861Skatostatic void set_perform_actual_lookups(struct configuration *, const char *,
5533044Sbde	int);
5628861Skatostatic void set_positive_time_to_live(struct configuration *,
5728861Skato	const char *, int);
5828861Skatostatic void set_suggested_size(struct configuration *, const char *,
5928861Skato	int size);
6028861Skatostatic void set_threads_num(struct configuration *, int);
6133044Sbdestatic int strbreak(char *, char **, int);
6228861Skato
6328861Skatostatic int
6428861Skatostrbreak(char *str, char **fields, int fields_size)
65126928Speter{
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 */
88static struct configuration_entry *
89find_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 */
112static void
113enable_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
123static void
124set_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
145static void
146set_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 */
169static void
170set_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
190static void
191set_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
206static void
207set_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
222static void
223set_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
238static void
239set_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
257static void
258check_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
266static int
267get_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
278static int
279get_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
299static enum cache_policy_t
300get_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
313static int
314check_cachename(const char *str)
315{
316
317	assert(str != NULL);
318	return ((strlen(str) > 0) ? 0 : -1);
319}
320
321static void
322set_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 */
333int
334parse_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