ecc.4: Update manual page for X3400 support.
[dragonfly.git] / usr.sbin / nscd / parser.c
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 }