]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/nscd/parser.c
Merge llvm-project release/17.x llvmorg-17.0.2-0-gb2417f51dbbd
[FreeBSD/FreeBSD.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  */
27
28 #include <sys/cdefs.h>
29 #include <sys/time.h>
30
31 #include <assert.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "config.h"
37 #include "debug.h"
38 #include "log.h"
39 #include "parser.h"
40
41 static void enable_cache(struct configuration *,const char *, int);
42 static struct configuration_entry *find_create_entry(struct configuration *,
43         const char *);
44 static int get_number(const char *, int, int);
45 static enum cache_policy_t get_policy(const char *);
46 static int get_yesno(const char *);
47 static int check_cachename(const char *);
48 static void check_files(struct configuration *, const char *, int);
49 static void set_keep_hot_count(struct configuration *, const char *, int);
50 static void set_negative_policy(struct configuration *, const char *,
51         enum cache_policy_t);
52 static void set_negative_time_to_live(struct configuration *,
53         const char *, int);
54 static void set_positive_policy(struct configuration *, const char *,
55         enum cache_policy_t);
56 static void set_perform_actual_lookups(struct configuration *, const char *,
57         int);
58 static void set_positive_time_to_live(struct configuration *,
59         const char *, int);
60 static void set_suggested_size(struct configuration *, const char *,
61         int size);
62 static void set_threads_num(struct configuration *, int);
63 static int strbreak(char *, char **, int);
64
65 static int
66 strbreak(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  */
90 static struct configuration_entry *
91 find_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  */
114 static void
115 enable_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
125 static void
126 set_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
147 static void
148 set_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
168 static void
169 set_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
185 static void
186 set_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  */
203 static void
204 set_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
224 static void
225 set_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
240 static void
241 set_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
256 static void
257 set_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
272 static void
273 set_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
291 static void
292 check_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
300 static int
301 get_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
312 static int
313 get_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
333 static enum cache_policy_t
334 get_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
347 static int
348 check_cachename(const char *str)
349 {
350
351         assert(str != NULL);
352         return ((strlen(str) > 0) ? 0 : -1);
353 }
354
355 static void
356 set_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  * the same routine implementation in Solaris NSCD.
366  */
367 int
368 parse_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 }