1 /*-
2 * Copyright (c) 2004 Andrey Simonenko
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 #include "config.h"
28
29 #ifndef lint
30 static const char rcsid[] ATTR_UNUSED =
31 "@(#)$Id: ipastat_conf.c,v 1.3.2.1 2011/11/15 18:12:29 simon Exp $";
32 #endif /* !lint */
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include <ctype.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <fnmatch.h>
41 #include <regex.h>
42 #include <stdarg.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46
47 #include "ipa_mod.h"
48
49 #include "queue.h"
50
51 #include "dlapi.h"
52 #include "confcommon.h"
53 #include "memfunc.h"
54 #include "parser.h"
55 #include "pathnames.h"
56
57 #include "ipastat_conf.h"
58 #include "ipastat_log.h"
59 #include "ipastat_rules.h"
60 #include "ipastat_st.h"
61 #include "ipastat_main.h"
62
63 char *ipastat_conf_file = IPASTAT_CONF_FILE; /* -f conf_file */
64
65 char mimic_real_config = 0; /* Set if -tt. */
66
67 /*
68 * Give limitation on the depth of included files.
69 */
70 #define INCLUDE_DEPTH_MAX 100
71 static unsigned int include_depth;
72
73 static unsigned int section; /* Current section ID. */
74
75 static char global_section_set; /* Set if has global{}. */
76
77 static unsigned int conf_event_no; /* Current section ordinal number. */
78 static const void *conf_event_arg; /* Current argument for conf_event(). */
79
80 static const unsigned char conf_event_begin[] = {
81 0, /* Does not exist. */
82 0, /* Root section. */
83 IPA_CONF_EVENT_GLOBAL_BEGIN, /* global{} */
84 IPA_CONF_EVENT_RULE_BEGIN, /* rule{} */
85 IPA_CONF_EVENT_LIMIT_BEGIN, /* limit{} */
86 IPA_CONF_EVENT_THRESHOLD_BEGIN, /* threshold{} */
87 0, /* Not my section. */
88 IPA_CONF_EVENT_RULEPAT_BEGIN /* rulepat{} */
89 };
90
91 static regex_t reg_list;
92
93 static struct rule *currule; /* Current rule. */
94
95 static struct rulepat *currulepat; /* Current rulepat. */
96 static unsigned int rulepatno = 0;
97
98 static signed char posix_re_pattern; /* posix_re_pattern parameter. */
99
100 #ifdef WITH_LIMITS
101 static struct limit *curlimit; /* Current limit. */
102 static unsigned int limitno; /* Current limit ordinal number. */
103 static struct limits_list *limits_list;
104 #endif
105
106 #ifdef WITH_THRESHOLDS
107 static struct threshold *curthreshold; /* Current threshold. */
108 static unsigned int thresholdno; /* Current threshold ordinal number. */
109 static struct thresholds_list *thresholds_list;
110 #endif
111
112 static const struct {
113 const char *str;
114 int val;
115 } value_units_tbl[] = {
116 { "bytes", IPA_CONF_TYPE_BYTES },
117 { "time", IPA_CONF_TYPE_TIME },
118 { "number", IPA_CONF_TYPE_UINT64 },
119 { "any", 0 }
120 };
121
122 #define VALUE_UNITS_TBL_SIZE \
123 (sizeof(value_units_tbl) / sizeof(value_units_tbl[0]))
124
125 static char use_log; /* 0, if -t or -x switch was used. */
126
127 static void confvlogmsgx(int, const char *, const char *, va_list)
128 ATTR_FORMAT(printf, 3, 0);
129
130 /*
131 * Exported support functions for modules.
132 */
133 static const ipa_suppfunc suppfunc = {
134 print_string, /* print_string */
135 print_bytes, /* print_bytes */
136 print_time, /* print_time */
137 print_value, /* print_value */
138 print_boolean, /* print_boolean */
139 print_space, /* print_space */
140 mod_print_param_name, /* print_param_name */
141 mod_print_args, /* print_param_args */
142 mod_print_param_end, /* print_param_end */
143 mod_print_sect_name, /* print_sect_name */
144 mod_print_args, /* print_sect_args */
145 print_sect_begin, /* print_sect_begin */
146 mod_print_sect_end, /* print_sect_end */
147 open_close_log, /* open_log */
148 open_close_log, /* close_log */
149 mod_logmsg, /* logmsg */
150 mod_logconferr, /* logconferr */
151 parser_local_sym_add, /* local_sym_add */
152 parser_local_sym_del, /* local_sym_del */
153 parser_global_sym_add, /* global_sym_add */
154 parser_global_sym_del /* global_sym_del */
155 };
156
157 /*
158 * Log message prepending it with the prefix.
159 * If use_log is set, then log is used, else printf(3) is used.
160 * Do not use mem_*() functions in this function.
161 */
162 static void
confvlogmsgx(int priority,const char * prefix,const char * format,va_list ap)163 confvlogmsgx(int priority, const char *prefix, const char *format, va_list ap)
164 {
165 if (use_log) {
166 char buf[LOG_BUF_SIZE];
167 char *msg;
168 int rv;
169
170 rv = vsnprintf(buf, sizeof(buf), format, ap);
171 if (rv < 0) {
172 logmsg(IPA_LOG_ERR, "confvlogmsgx: vsnprintf failed");
173 goto log_unformated;
174 }
175 if (rv < sizeof(buf)) {
176 logmsgx(priority, "%s: %s", prefix, buf);
177 return;
178 }
179 msg = malloc(++rv);
180 if (msg == NULL) {
181 logmsgx(IPA_LOG_ERR, "confvlogmsgx: malloc failed");
182 goto log_unformated;
183 }
184 if (vsnprintf(msg, rv, format, ap) < 0) {
185 logmsg(IPA_LOG_ERR, "confvlogmsgx: vsnprintf failed");
186 free(msg);
187 goto log_unformated;
188 }
189 logmsgx(priority, "%s: %s", prefix, msg);
190 free(msg);
191 return;
192 log_unformated:
193 logmsgx(priority, "%s: unformated message: %s",
194 prefix, format);
195 } else {
196 fflush(stdout);
197 fprintf(stderr, "%s: ", prefix);
198 vfprintf(stderr, format, ap);
199 fprintf(stderr, "\n");
200 }
201 }
202
203 /*
204 * The wrapper for parser_vlogmsgx.
205 */
206 static void
parser_vlogmsgx_wrapper(const char * format,va_list ap)207 parser_vlogmsgx_wrapper(const char *format, va_list ap)
208 {
209 confvlogmsgx(IPA_LOG_ERR, "parsing error", format, ap);
210 }
211
212 /*
213 * Wrappers for logging about configuration errors.
214 */
215 static void
vlogconfe(const char * format,va_list ap)216 vlogconfe(const char *format, va_list ap)
217 {
218 confvlogmsgx(IPA_LOG_ERR, "configuration error", format, ap);
219 }
220
221 void
logconfe(const char * format,...)222 logconfe(const char *format, ...)
223 {
224 va_list ap;
225
226 va_start(ap, format);
227 vlogconfe(format, ap);
228 va_end(ap);
229 }
230
231 /*
232 * The same as above one, but with priority argument and va_list.
233 */
234 static void
vlogconfx_priority(int priority,const char * format,va_list ap)235 vlogconfx_priority(int priority, const char *format, va_list ap)
236 {
237 confvlogmsgx(priority, "configuration error", format, ap);
238 }
239
240 /*
241 * Register a configuration event in st_mod module.
242 */
243 static int
st_mod_conf_event(const struct st_mod * st_mod,unsigned int event,unsigned int no,const void * arg)244 st_mod_conf_event(const struct st_mod *st_mod, unsigned int event,
245 unsigned int no, const void *arg)
246 {
247 if (st_mod->ipa_st_mod->conf_event(event, no, arg) < 0) {
248 logconfx("module %s: conf_event(IPA_CONF_EVENT_%s) failed",
249 st_mod->mod_file, conf_event_msg[event]);
250 return (-1);
251 }
252 return (0);
253 }
254
255 /*
256 * Register a configuration event in each module.
257 */
258 static int
mod_conf_event(unsigned int event,unsigned int no,const void * arg)259 mod_conf_event(unsigned int event, unsigned int no, const void *arg)
260 {
261 const struct st_mod *st_mod;
262
263 SLIST_FOREACH(st_mod, &st_mod_list, link)
264 if (st_mod_conf_event(st_mod, event, no, arg) < 0)
265 return (-1);
266 return (0);
267 }
268
269 /*
270 * Parse the "global" section.
271 */
272 /* ARGSUSED */
273 static int
parse_global(void * arg ATTR_UNUSED)274 parse_global(void *arg ATTR_UNUSED)
275 {
276 if (global_section_set) {
277 logconfx("this section is duplicated");
278 return (-1);
279 }
280 global_section_set = 1;
281 return (0);
282 }
283
284 /*
285 * Parse the "rule" section.
286 */
287 static int
parse_rule(void * arg)288 parse_rule(void *arg)
289 {
290 struct rule *rule;
291 char *name;
292
293 name = *(char **)arg;
294 if (conf_validate_name(name) < 0)
295 return (-1);
296 if (rule_by_name(name) != NULL) {
297 logconfx("this section is duplicated");
298 return (-1);
299 }
300
301 name = mem_strdup(name, m_anon);
302 if (name == NULL) {
303 logconfx("mem_strdup failed");
304 return (-1);
305 }
306 rule = alloc_rule(name);
307 if (rule == NULL) {
308 logconfx("alloc_rule failed");
309 return (-1);
310 }
311 rule->free_mask = RULE_FREE_NAME;
312
313 #ifdef WITH_LIMITS
314 limits_list = &rule->limits;
315 STAILQ_INIT(limits_list);
316 limitno = 0;
317 #endif
318
319 #ifdef WITH_THRESHOLDS
320 thresholds_list = &rule->thresholds;
321 STAILQ_INIT(thresholds_list);
322 thresholdno = 0;
323 #endif
324
325 if (parser_local_sym_add("rule", rule->name, 0) < 0)
326 return (-1);
327
328 currule = rule;
329 conf_event_no = rule->no;
330 conf_event_arg = rule->name;
331
332 return (0);
333 }
334
335 /*
336 * Parse the "dynamic_rules" parameter.
337 */
338 static int
parse_dynamic_rules(void * arg)339 parse_dynamic_rules(void *arg)
340 {
341 dynamic_rules = (signed char)*(int *)arg;
342 return (0);
343 }
344
345 /*
346 * Parse the "rulepat" section.
347 */
348 static int
parse_rulepat(void * arg)349 parse_rulepat(void *arg)
350 {
351 struct rulepat *rulepat;
352 char *pat;
353 int error;
354
355 pat = *(char **)arg;
356 STAILQ_FOREACH(rulepat, &rulepats_list, link)
357 if (strcmp(rulepat->pat, pat) == 0) {
358 logconfx("this section is duplicated");
359 return (-1);
360 }
361
362 rulepat = mzone_alloc(rulepat_mzone);
363 if (rulepat == NULL) {
364 logconfx("mzone_alloc failed");
365 return (-1);
366 }
367 STAILQ_INSERT_TAIL(&rulepats_list, rulepat, link);
368
369 error = regcomp(&rulepat->re, pat, REG_EXTENDED|REG_NOSUB);
370 if (error != 0) {
371 logconfx("regcomp(\"%s\"): %s", pat, regerrbuf(error));
372 return (-1);
373 }
374
375 rulepat->pat = pat;
376 rulepat->no = rulepatno++;
377 rulepat->check_next = -1;
378 rulepat->st_list = NULL;
379
380 #ifdef WITH_LIMITS
381 limits_list = &rulepat->limits;
382 STAILQ_INIT(limits_list);
383 limitno = 0;
384 #endif
385
386 #ifdef WITH_THRESHOLDS
387 thresholds_list = &rulepat->thresholds;
388 STAILQ_INIT(thresholds_list);
389 thresholdno = 0;
390 #endif
391
392 currulepat = rulepat;
393 conf_event_no = rulepat->no;
394 conf_event_arg = rulepat->pat;
395
396 return (0);
397 }
398
399 /*
400 * Parse the "check_next_rulepat" parameter.
401 */
402 static int
parse_check_next_rulepat(void * arg)403 parse_check_next_rulepat(void *arg)
404 {
405 currulepat->check_next = *(int *)arg;
406 return (0);
407 }
408
409 /*
410 * Parse the "value_units" parameter.
411 */
412 static int
parse_value_units(void * arg)413 parse_value_units(void *arg)
414 {
415 unsigned int i;
416
417 if (value_units >= 0) {
418 logconfx("cannot re-define this parameter");
419 return (-1);
420 }
421 if (got_arg_value) {
422 logconfx("this parameter must be used before other parameters "
423 "and sections, that accept the IPA_CONF_TYPE_VALUE data "
424 "type");
425 return (-1);
426 }
427 for (i = 0; i < VALUE_UNITS_TBL_SIZE; ++i)
428 if (strcmp(*(char **)arg, value_units_tbl[i].str) == 0) {
429 value_units = value_units_tbl[i].val;
430 return (0);
431 }
432 logconfx("wrong value");
433 return (-1);
434 }
435
436 #ifdef WITH_LIMITS
437 /*
438 * Parse the "limit" section.
439 */
440 static int
parse_limit(void * arg)441 parse_limit(void *arg)
442 {
443 const char *name;
444 struct limit *limit;
445
446 name = *(char **)arg;
447 if (conf_validate_name(name) < 0)
448 return (-1);
449 STAILQ_FOREACH(limit, limits_list, link)
450 if (strcmp(limit->name, name) == 0) {
451 logconfx("this section is duplicated");
452 return (-1);
453 }
454 limit = alloc_limit();
455 if (limit == NULL) {
456 logconfx("alloc_limit failed");
457 return (-1);
458 }
459 limit->name = mem_strdup(name, m_anon);
460 if (limit->name == NULL) {
461 logconfx("mem_strdup failed");
462 return (-1);
463 }
464
465 STAILQ_INSERT_TAIL(limits_list, limit, link);
466 limit->no = limitno++;
467
468 limit->free_mask = LIMIT_FREE_NAME;
469
470 curlimit = limit;
471 conf_event_no = limit->no;
472 conf_event_arg = limit->name;
473
474 return (0);
475 }
476
477 /*
478 * Parse the "dynamic_limits" parameter.
479 */
480 static int
parse_dynamic_limits(void * arg)481 parse_dynamic_limits(void *arg)
482 {
483 dynamic_limits = (signed char)*(int *)arg;
484 return (0);
485 }
486 #endif /* WITH_LIMITS */
487
488 #ifdef WITH_THRESHOLDS
489 /*
490 * Parse the "threshold" section.
491 */
492 static int
parse_threshold(void * arg)493 parse_threshold(void *arg)
494 {
495 const char *name;
496 struct threshold *threshold;
497
498 name = *(char **)arg;
499 if (conf_validate_name(name) < 0)
500 return (-1);
501 STAILQ_FOREACH(threshold, thresholds_list, link)
502 if (strcmp(threshold->name, name) == 0) {
503 logconfx("this section is duplicated");
504 return (-1);
505 }
506 threshold = alloc_threshold();
507 if (threshold == NULL) {
508 logconfx("alloc_threshold failed");
509 return (-1);
510 }
511 threshold->name = mem_strdup(name, m_anon);
512 if (threshold->name == NULL) {
513 logconfx("mem_strdup failed");
514 return (-1);
515 }
516
517 STAILQ_INSERT_TAIL(thresholds_list, threshold, link);
518 threshold->no = thresholdno++;
519
520 threshold->free_mask = THRESHOLD_FREE_NAME;
521
522 curthreshold = threshold;
523 conf_event_no = threshold->no;
524 conf_event_arg = threshold->name;
525
526 return (0);
527 }
528
529 /*
530 * Parse the "dynamic_thresholds" parameter.
531 */
532 static int
parse_dynamic_thresholds(void * arg)533 parse_dynamic_thresholds(void *arg)
534 {
535 dynamic_thresholds = (signed char)*(int *)arg;
536 return (0);
537 }
538 #endif /* WITH_THRESHOLDS */
539
540 /*
541 * Check security of configuration file: absolute path, regular file.
542 */
543 static int
check_conf_file(const char * fname,int ignore_non_regular)544 check_conf_file(const char *fname, int ignore_non_regular)
545 {
546 struct stat statbuf;
547
548 if (stat(fname, &statbuf) < 0) {
549 logconfe("stat(%s): %s", fname, strerror(errno));
550 return (-1);
551 }
552 if (!S_ISREG(statbuf.st_mode)) {
553 if (ignore_non_regular)
554 return (0);
555 logconfe("configuration file \"%s\" should be a regular file",
556 fname);
557 return (-1);
558 }
559 return (0);
560 }
561
562 /*
563 * Parse the "st_mod" parameter.
564 */
565 static int
parse_st_mod(void * arg)566 parse_st_mod(void *arg)
567 {
568 const struct st_mod *st_mod2;
569 struct ipa_st_mod *ipa_st_mod;
570 struct st_mod *st_mod;
571 char *mod_name, *sym;
572
573 st_mod = mem_malloc(sizeof(*st_mod), m_anon);
574 if (st_mod == NULL) {
575 logconfx("mem_malloc failed");
576 return (-1);
577 }
578 st_mod->mod_file = *(char **)arg;
579 st_mod->mod_handle = dl_open(st_mod->mod_file);
580 if (st_mod->mod_handle == NULL) {
581 logconfx("dl_open(%s): %s", st_mod->mod_file, dl_error());
582 return (-1);
583 }
584 mod_name = get_mod_name(st_mod->mod_file);
585 if (mod_name == NULL)
586 return (-1);
587 if (mem_asprintf(m_anon, &sym, "%s_st_mod", mod_name) < 0) {
588 logconfx("mem_asprintf failed");
589 return (-1);
590 }
591 st_mod->ipa_st_mod =
592 (struct ipa_st_mod *)dl_lookup_sym(st_mod->mod_handle, sym);
593 if (st_mod->ipa_st_mod == NULL) {
594 logconfx("given module is not an IPA statistics module "
595 "or unknown symbol naming scheme is used");
596 return (-1);
597 }
598 mem_free(sym, m_anon);
599 mem_free(mod_name, m_anon);
600 ipa_st_mod = st_mod->ipa_st_mod;
601
602 /* Check ipa_st_mod API version. */
603 if (ipa_st_mod->api_ver != IPA_ST_MOD_API_VERSION) {
604 logconfx("module %s uses ipa_st_mod API version %u, my "
605 "ipa_st_mod API version is %u", st_mod->mod_file,
606 ipa_st_mod->api_ver, IPA_DB_MOD_API_VERSION);
607 return (-1);
608 }
609
610 /* Check if module is thread-safe or vice versa. */
611 #ifdef WITH_PTHREAD
612 if (!(ipa_st_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE)) {
613 logconfx("module %s must be thread-safe", st_mod->mod_file);
614 return (-1);
615 }
616 #else
617 if (ipa_st_mod->mod_flags & IPA_MOD_FLAG_PTHREAD_SAFE) {
618 logconfx("module %s must not be thread-safe", st_mod->mod_file);
619 return (-1);
620 }
621 #endif /* WITH_PTHREAD */
622
623 if (strcmp(ipa_st_mod->st_name, "null") == 0) {
624 logconfx("module's statistics name is \"null\", "
625 "this is a name of the built-in statistics");
626 return (-1);
627 }
628
629 st_mod2 = st_mod_by_name(ipa_st_mod->st_name);
630 if (st_mod2 != NULL) {
631 logconfx("duplicated statistics name \"%s\" in %s and %s"
632 "modules", ipa_st_mod->st_name, st_mod->mod_file,
633 st_mod2->mod_file);
634 return (-1);
635 }
636 if (ipa_st_mod->conf_prefix != NULL) {
637 st_mod2 = st_mod_by_prefix(ipa_st_mod->conf_prefix);
638 if (st_mod2 != NULL) {
639 logconfx("duplicated configuration prefix \"%s\" in "
640 "%s and %s modules", ipa_st_mod->conf_prefix,
641 st_mod->mod_file, st_mod2->mod_file);
642 return (-1);
643 }
644 }
645
646 ipa_st_mod->suppfunc = &suppfunc;
647 ipa_st_mod->memfunc = &memfunc;
648
649 if (init_conf_tbls(st_mod->mod_file, 1,
650 ipa_st_mod->conf_sect_tbl, &st_mod->conf_sect_hash,
651 ipa_st_mod->conf_param_tbl, &st_mod->conf_param_hash) < 0)
652 return (-1);
653
654 if (ipa_st_mod->conf_init() < 0) {
655 logconfx("module %s: conf_init failed", st_mod->mod_file);
656 return (-1);
657 }
658
659 SLIST_INSERT_HEAD(&st_mod_list, st_mod, link);
660 return (0);
661 }
662
663 /*
664 * Parse the "st_list" parameter.
665 */
666 static int
parse_st_list(void * arg)667 parse_st_list(void *arg)
668 {
669 const struct st_list **list_ptr;
670 const struct st_mod *st_mod;
671 const char *st_name;
672 struct st_elem *st;
673 struct st_set *set;
674 struct st_list *list;
675 char *ptr;
676
677 switch (section) {
678 #ifdef WITH_RULES
679 case IPA_CONF_SECT_RULE:
680 list_ptr = &currule->st_list;
681 break;
682 #endif
683 case IPA_CONF_SECT_RULEPAT:
684 list_ptr = &currulepat->st_list;
685 break;
686 #ifdef WITH_LIMITS
687 case IPA_CONF_SECT_LIMIT:
688 list_ptr = &curlimit->st_list;
689 break;
690 #endif
691 #ifdef WITH_THRESHOLDS
692 case IPA_CONF_SECT_THRESHOLD:
693 list_ptr = &curthreshold->st_list;
694 break;
695 #endif
696 default: /* IPA_CONF_SECT_GLOBAL */
697 list_ptr = &global_st_list;
698 }
699
700 if (*list_ptr != NULL) {
701 logconfx("cannot re-define this parameter");
702 return (-1);
703 }
704
705 set = NULL;
706 list = NULL;
707
708 for (ptr = *(char **)arg; ptr != NULL;) {
709 /* Get the name of the next statistics system. */
710 st_name = ptr;
711 ptr = strchr(ptr, ' ');
712 if (ptr != NULL)
713 *ptr++ = '\0';
714
715 /* Handle "null" statistics system. */
716 if (strcmp(st_name, "null") == 0) {
717 if (list != NULL || ptr != NULL) {
718 logconfx("built-in statistics system \"null\" "
719 "cannot be used together with another "
720 "statistics systems");
721 return (-1);
722 }
723 *list_ptr = &st_list_null;
724 return (0);
725 }
726
727 st_mod = st_mod_by_name(st_name);
728 if (st_mod == NULL) {
729 logconfx("cannot find module with \"%s\" statistics "
730 "system name", st_name);
731 return (-1);
732 }
733
734 if (set != NULL) {
735 /* We already have set for current st_list parameter. */
736 STAILQ_FOREACH(st, list, link)
737 if (strcmp(st_name,
738 st->ipa_st_mod->st_name) == 0) {
739 logconfx("duplicated statistics "
740 "system \"%s\"", st_name);
741 return (-1);
742 }
743 } else {
744 /* Create new set for st_list parameter. */
745 set = mem_malloc(sizeof(*set), m_anon);
746 if (set == NULL) {
747 logconfx("mem_malloc failed");
748 return (-1);
749 }
750 list = &set->list;
751 STAILQ_INIT(list);
752 }
753
754 /* Add new st element to st_list. */
755 st = mem_malloc(sizeof(*st), m_anon);
756 if (st == NULL) {
757 logconfx("mem_malloc failed");
758 return (-1);
759 }
760 st->ipa_st_mod = st_mod->ipa_st_mod;
761 st->mod_file = st_mod->mod_file;
762
763 STAILQ_INSERT_TAIL(list, st, link);
764 }
765
766 /* New st_list --> add it to st_sets. */
767 *list_ptr = list;
768 SLIST_INSERT_HEAD(&st_sets, set, link);
769 return (0);
770 }
771
772 /*
773 * Parse the "posix_re_pattern" parameter.
774 */
775 static int
parse_posix_re_pattern(void * arg)776 parse_posix_re_pattern(void *arg)
777 {
778 posix_re_pattern = (signed char)*(int *)arg;
779 return (0);
780 }
781
782 /*
783 * Parse the "debug_st_null" parameter.
784 */
785 static int
parse_debug_st_null(void * arg)786 parse_debug_st_null(void *arg)
787 {
788 uint32_t level;
789
790 level = *(uint32_t *)arg;
791 if (level > 1) {
792 logconfx("too big debug level, max level is 1");
793 return (-1);
794 }
795 debug_st_null = (signed char)level;
796 return (0);
797 }
798
799 /*
800 * Check file path: non empty string and should start with '/'.
801 */
802 static int
check_file_path(const char * fname,const char * what)803 check_file_path(const char *fname, const char *what)
804 {
805 if (*fname == '\0') {
806 logconfx("argument should be a non-empty string");
807 return (-1);
808 }
809 if (*fname != '/') {
810 logconfx("%s should be given with absolute path", what);
811 return (-1);
812 }
813 return (0);
814 }
815
816 static int
check_include_depth(void)817 check_include_depth(void)
818 {
819 if (include_depth > INCLUDE_DEPTH_MAX) {
820 logconfx("too big (> %u) depth of included files",
821 INCLUDE_DEPTH_MAX);
822 return (-1);
823 }
824 return (0);
825 }
826
827 /*
828 * Parse the "include" parameter.
829 */
830 static int
parse_include(void * arg)831 parse_include(void *arg)
832 {
833 struct parser_pb *pb;
834 char *fname;
835
836 /* Save offset of current configuration file and close it. */
837 fname = *(char **)arg;
838 pb = parser_top_pb();
839 pb->foff = ftell(pb->fp);
840 if (pb->foff < 0) {
841 logconf("ftell(%s)", pb->fname);
842 return (-1);
843 }
844 if (fclose(pb->fp) != 0) {
845 logconf("fclose(%s)", pb->fname);
846 return (-1);
847 }
848
849 /* Validate an argument. */
850 if (check_file_path(fname, "file") < 0)
851 return (-1);
852
853 ++include_depth;
854 if (check_include_depth() < 0)
855 return (-1);
856
857 /* Check security of configuration file. */
858 if (check_conf_file(fname, 0) < 0)
859 return (-1);
860
861 /* Open included configuration file and put it to stack. */
862 pb = parser_new_pb(0);
863 if (pb == NULL)
864 return (-1);
865 pb->fp = fopen(fname, "r");
866 if (pb->fp == NULL) {
867 logconf("fopen(%s, \"r\")", fname);
868 return (-1);
869 }
870 pb->fname = fname;
871 return (parser_push_pb(pb));
872 }
873
874 /*
875 * Parse the "include_files" parameter.
876 */
877 static int
parse_include_files(void * arg)878 parse_include_files(void *arg)
879 {
880 struct stat statbuf;
881 regex_t re;
882 const struct dirent *dp;
883 struct parser_pb *pb, *tpb;
884 char *dir, *pat, *fname;
885 DIR *dirp;
886
887 /* Validate an argument. */
888 dir = *(char **)arg;
889 if (check_file_path(dir, "directory") < 0)
890 return (-1);
891
892 pat = strrchr(dir, '/');
893 *pat++ = '\0';
894 if (posix_re_pattern > 0) {
895 int error;
896
897 error = regcomp(&re, pat, REG_EXTENDED|REG_NOSUB);
898 if (error != 0) {
899 logconfx("cannot compile regular expression: "
900 "regcomp(\"%s\"): %s", pat, regerrbuf(error));
901 return (-1);
902 }
903 }
904
905 /* Check security of the given directory. */
906 if (lstat(dir, &statbuf) < 0) {
907 logconf("lstat(%s)", dir);
908 return (-1);
909 }
910 if (!S_ISDIR(statbuf.st_mode)) {
911 logconfx("given pathname is not a directory");
912 return (-1);
913 }
914
915 /* Read directory and push pb to stack. */
916 dirp = opendir(dir);
917 if (dirp == NULL) {
918 logconf("opendir(%s)", dir);
919 return (-1);
920 }
921 pb = NULL;
922 tpb = parser_top_pb();
923 for (;;) {
924 errno = 0;
925 dp = readdir(dirp);
926 if (dp == NULL) {
927 if (errno != 0) {
928 logconf("readdir(%s)", dir);
929 return (-1);
930 }
931 break;
932 }
933 /* Ignore "." amd ".." directories. */
934 if (dp->d_name[0] == '.')
935 switch (dp->d_name[1]) {
936 case '\0':
937 continue;
938 case '.':
939 if (dp->d_name[2] == '\0')
940 continue;
941 }
942 if (posix_re_pattern != 1) {
943 if (fnmatch(pat, dp->d_name, FNM_PERIOD) == FNM_NOMATCH)
944 continue;
945 } else {
946 if (regexec_simple(&re, dp->d_name) != 0)
947 continue;
948 }
949 if (mem_asprintf(m_parser, &fname, "%s/%s", dir,
950 dp->d_name) < 0) {
951 logconfx("mem_asprintf failed");
952 return (-1);
953 }
954 switch (check_conf_file(fname, 1)) {
955 case -1:
956 return (-1);
957 case 0:
958 mem_free(fname, m_parser);
959 continue;
960 }
961 pb = parser_new_pb(0);
962 if (pb == NULL)
963 return (-1);
964 pb->fname = fname;
965 pb->foff = 0;
966 if (parser_push_pb(pb) < 0)
967 return (-1);
968 }
969 if (posix_re_pattern > 0)
970 regfree(&re);
971 if (closedir(dirp) < 0) {
972 logconf("closedir(%s)", dir);
973 return (-1);
974 }
975 mem_free(dir, m_parser);
976 if (pb != NULL) {
977 tpb->foff = ftell(tpb->fp);
978 if (tpb->foff < 0) {
979 logconf("ftell(%s)", tpb->fname);
980 return (-1);
981 }
982 if (fclose(tpb->fp) != 0) {
983 logconf("fclose(%s)", tpb->fname);
984 return (-1);
985 }
986 ++include_depth;
987 if (check_include_depth() < 0)
988 return (-1);
989 pb->fp = fopen(pb->fname, "r");
990 if (pb->fp == NULL) {
991 logconf("fopen(%s, \"r\")", pb->fname);
992 return (-1);
993 }
994 }
995 return (0);
996 }
997
998 /*
999 * Log information about chain of included files.
1000 */
1001 static void
log_include_history(void)1002 log_include_history(void)
1003 {
1004 const struct parser_pb *pb;
1005 const char *fname;
1006
1007 pb = parser_top_pb();
1008 if (pb != NULL) {
1009 fname = pb->fname;
1010 while ((pb = parser_top_pb()) != NULL) {
1011 if (fname != pb->fname) {
1012 fname = pb->fname;
1013 logconfe("included from %s:%u",
1014 fname, pb->lineno);
1015 }
1016 (void)parser_pop_pb();
1017 }
1018 }
1019 }
1020
1021 static void
set_global_params(void)1022 set_global_params(void)
1023 {
1024 global_section_set = 1;
1025 if (global_st_list == NULL)
1026 global_st_list = &st_list_null;
1027 if (debug_st_null < 0)
1028 debug_st_null = 0;
1029 if (dynamic_rules < 0)
1030 dynamic_rules = 0;
1031 #ifdef WITH_LIMITS
1032 if (dynamic_limits < 0)
1033 dynamic_limits = 0;
1034 #endif
1035 #ifdef WITH_THRESHOLDS
1036 if (dynamic_thresholds < 0)
1037 dynamic_thresholds = 0;
1038 #endif
1039 if (value_units < 0)
1040 value_units = 0;
1041 }
1042
1043 static const unsigned int sect_root[] = { IPA_CONF_SECT_ROOT, 0 };
1044 static const unsigned int sect_for_st_list[] = { IPA_CONF_SECT_GLOBAL,
1045 IPA_CONF_SECT_RULE,
1046 #ifdef WITH_LIMITS
1047 IPA_CONF_SECT_LIMIT,
1048 #endif
1049 #ifdef WITH_THRESHOLDS
1050 IPA_CONF_SECT_THRESHOLD,
1051 #endif
1052 IPA_CONF_SECT_RULEPAT, 0
1053 };
1054 static const unsigned int sect_rulepat[] = { IPA_CONF_SECT_RULEPAT, 0 };
1055 #ifdef WITH_ANY_LIMITS
1056 static const unsigned int sect_for_any_limit[] = { IPA_CONF_SECT_RULE,
1057 IPA_CONF_SECT_RULEPAT, 0
1058 };
1059 #endif
1060
1061 /*
1062 * Sections in ipastat.conf.
1063 */
1064 static ipa_conf_sect conf_sect_tbl[] = {
1065 { "global", IPA_CONF_SECT_GLOBAL, 0, NULL, NULL,
1066 IPA_CONF_TYPE_MISC, sect_root, parse_global
1067 },
1068 { "rule", IPA_CONF_SECT_RULE, 1, NULL, NULL,
1069 IPA_CONF_TYPE_MISC, sect_root, parse_rule
1070 },
1071 { "rulepat", IPA_CONF_SECT_RULEPAT, 1, NULL, NULL,
1072 IPA_CONF_TYPE_STRING, sect_root, parse_rulepat
1073 },
1074 #ifdef WITH_LIMITS
1075 { "limit", IPA_CONF_SECT_LIMIT, 1, NULL, NULL,
1076 IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_limit
1077 },
1078 #endif
1079 #ifdef WITH_THRESHOLDS
1080 { "threshold", IPA_CONF_SECT_THRESHOLD, 1, NULL, NULL,
1081 IPA_CONF_TYPE_MISC, sect_for_any_limit, parse_threshold
1082 },
1083 #endif
1084 { NULL, 0, 0, NULL, NULL,
1085 IPA_CONF_TYPE_MISC, NULL, NULL
1086 }
1087 };
1088
1089 #define PAT_LIST "^[^ \"]+( [^ \"]+)*$"
1090
1091 /*
1092 * Parameters in ipastat.conf.
1093 */
1094 static ipa_conf_param conf_param_tbl[] = {
1095 { "st_mod", 1, NULL, NULL, IPA_CONF_TYPE_STRING,
1096 sect_root, parse_st_mod
1097 },
1098 { "include", 1, NULL, NULL, IPA_CONF_TYPE_STRING,
1099 NULL, parse_include
1100 },
1101 { "include_files", 1, NULL, NULL, IPA_CONF_TYPE_STRING,
1102 NULL, parse_include_files
1103 },
1104 { "posix_re_pattern", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN,
1105 sect_root, parse_posix_re_pattern
1106 },
1107 { "st_list", -1, PAT_LIST, ®_list, IPA_CONF_TYPE_MISC,
1108 sect_for_st_list, parse_st_list
1109 },
1110 { "check_next_rulepat", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN,
1111 sect_rulepat, parse_check_next_rulepat
1112 },
1113 { "debug_st_null", 1, NULL, NULL, IPA_CONF_TYPE_UINT32,
1114 sect_root, parse_debug_st_null
1115 },
1116 { "dynamic_rules", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN,
1117 sect_root, parse_dynamic_rules
1118 },
1119 { "value_units", 1, NULL, NULL, IPA_CONF_TYPE_MISC,
1120 sect_root, parse_value_units
1121 },
1122 #ifdef WITH_LIMITS
1123 { "dynamic_limits", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN,
1124 sect_root, parse_dynamic_limits
1125 },
1126 #endif
1127 #ifdef WITH_THRESHOLDS
1128 { "dynamic_thresholds", 1, NULL, NULL, IPA_CONF_TYPE_BOOLEAN,
1129 sect_root, parse_dynamic_thresholds
1130 },
1131 #endif
1132 { NULL, 0, NULL, NULL, IPA_CONF_TYPE_MISC,
1133 NULL, NULL
1134 }
1135 };
1136
1137 /* Module which own current custom section. */
1138 static const struct st_mod *cur_st_mod;
1139
1140 /* Hash tables for ipastat.conf configuration. */
1141 static struct conf_sect_hash *conf_sect_hash;
1142 static struct conf_param_hash *conf_param_hash;
1143
1144 /* Pointers to configuration tables. */
1145 static const ipa_conf_sect *conf_sect_tbl_ptr;
1146 static const ipa_conf_param *conf_param_tbl_ptr;
1147 static const struct conf_sect_hash *conf_sect_hash_ptr;
1148 static const struct conf_param_hash *conf_param_hash_ptr;
1149
1150 /* Set if in own ipastat.conf section. */
1151 static char in_own_sect;
1152
1153 /* Stack of nested configuration sections. */
1154 struct sect_stack {
1155 SLIST_ENTRY(sect_stack) link;
1156 unsigned int section;
1157 };
1158
1159 SLIST_HEAD(EMPTY, sect_stack) sect_stack_list =
1160 SLIST_HEAD_INITIALIZER(sect_stack_list);
1161
1162 static int
init_config_data(void)1163 init_config_data(void)
1164 {
1165 if ((m_result = mem_type_new_local(MTYPE_NAME(result),
1166 "Memory for query results", 0)) == NULL ||
1167 (m_parser = mem_type_new_local(MTYPE_NAME(parser),
1168 "Memory of parser", MEMTYPE_FLAGS)) == NULL)
1169 return (-1);
1170
1171 value_units = -1;
1172 got_arg_value = 0;
1173
1174 global_st_list = NULL;
1175 debug_st_null = -1;
1176 SLIST_INIT(&st_mod_list);
1177
1178 conf_sect_he_mzone = mzone_init(MZONE_NAME(conf_sect_he),
1179 "Config sections hash entries", 0, sizeof(struct conf_sect_he),
1180 CONF_SECT_HE_NSIZE, CONF_SECT_HE_NALLOC);
1181 if (conf_sect_he_mzone == NULL)
1182 return (-1);
1183
1184 conf_param_he_mzone = mzone_init(MZONE_NAME(conf_param_he),
1185 "Config parameters hash entries", 0, sizeof(struct conf_param_he),
1186 CONF_PARAM_HE_NSIZE, CONF_PARAM_HE_NALLOC);
1187 if (conf_param_he_mzone == NULL)
1188 return (-1);
1189
1190 rules_hash_init();
1191
1192 rule_mzone = mzone_init(MZONE_NAME(rule), "Rules", 0,
1193 sizeof(struct rule), RULE_NSIZE, RULE_NALLOC);
1194 if (rule_mzone == NULL)
1195 return (-1);
1196 dynamic_rules = -1;
1197
1198 rulepat_mzone = mzone_init(MZONE_NAME(rulepat), "Rules patterns", 0,
1199 sizeof(struct rulepat), RULEPAT_NSIZE, RULEPAT_NALLOC);
1200 if (rulepat_mzone == NULL)
1201 return (-1);
1202
1203 #ifdef WITH_LIMITS
1204 limit_mzone = mzone_init(MZONE_NAME(limit), "Limits", 0,
1205 sizeof(struct limit), LIMIT_NSIZE, LIMIT_NALLOC);
1206 if (limit_mzone == NULL)
1207 return (-1);
1208 dynamic_limits = -1;
1209 #endif
1210
1211 #ifdef WITH_THRESHOLDS
1212 threshold_mzone = mzone_init(MZONE_NAME(threshold), "Thresholds", 0,
1213 sizeof(struct threshold), THRESHOLD_NSIZE, THRESHOLD_NALLOC);
1214 if (threshold_mzone == NULL)
1215 return (-1);
1216 dynamic_thresholds = -1;
1217 #endif
1218
1219 posix_re_pattern = -1;
1220
1221 if (build_conf_re() < 0)
1222 return (-1);
1223 if (init_conf_tbls((char *)NULL, 1, conf_sect_tbl, &conf_sect_hash,
1224 conf_param_tbl, &conf_param_hash) < 0)
1225 return (-1);
1226
1227 return (0);
1228 }
1229
1230 /*
1231 * Deinitialize not needed configuration data structures.
1232 */
1233 static void
deinit_config_data(void)1234 deinit_config_data(void)
1235 {
1236 deinit_conf_tbls(0, conf_sect_tbl, conf_sect_hash,
1237 conf_param_tbl, conf_param_hash);
1238
1239 mzone_deinit(conf_sect_he_mzone);
1240 mzone_deinit(conf_param_he_mzone);
1241
1242 if (mzone_is_empty(rulepat_mzone)) {
1243 mzone_deinit(rulepat_mzone);
1244 rulepat_mzone = NULL;
1245 }
1246 }
1247
1248 static int
token_section_begin(void)1249 token_section_begin(void)
1250 {
1251 const unsigned int *id;
1252 const char *prefix;
1253 const ipa_conf_sect *conf_sect;
1254 const struct get_arg *get_arg;
1255 struct sect_stack *sect_stack;
1256 struct st_mod *st_mod;
1257 char *ptr;
1258 int nargs;
1259
1260 /* Put previous section on top of sections stack. */
1261 sect_stack = mem_malloc(sizeof(*sect_stack), m_anon);
1262 if (sect_stack == NULL) {
1263 logconfe("token_section_begin: mem_malloc failed");
1264 return (-1);
1265 }
1266 sect_stack->section = section;
1267 SLIST_INSERT_HEAD(§_stack_list, sect_stack, link);
1268
1269 /* Get name of current section: "section" or "prefix:section". */
1270 cursect = parser_token;
1271 if (in_own_sect && (ptr = strchr(cursect, ':')) != NULL) {
1272 /*
1273 * Previous section is not a custom section and
1274 * new custom section appears.
1275 */
1276 *ptr = '\0';
1277 prefix = cursect;
1278 cursect = ptr + 1;
1279 st_mod = st_mod_by_prefix(prefix);
1280 if (st_mod != NULL) {
1281 conf_sect_hash_ptr = st_mod->conf_sect_hash;
1282 conf_param_hash_ptr = st_mod->conf_param_hash;
1283 conf_sect_tbl_ptr = st_mod->ipa_st_mod->conf_sect_tbl;
1284 conf_param_tbl_ptr = st_mod->ipa_st_mod->conf_param_tbl;
1285 curmodfile = st_mod->mod_file;
1286 cur_st_mod = st_mod;
1287 } else {
1288 logconfx("cannot find module with \"%s\" "
1289 "configuration prefix", prefix);
1290 return (-1);
1291 }
1292 in_own_sect = 0;
1293 }
1294
1295 /* Find section. */
1296 conf_sect = find_conf_sect(conf_sect_tbl_ptr, conf_sect_hash_ptr,
1297 cursect);
1298 if (conf_sect == NULL) {
1299 logconfx("unknown section");
1300 return (-1);
1301 }
1302
1303 /* Validate type of argument. */
1304 if (conf_sect->arg_type > IPA_CONF_TYPE_MISC) {
1305 logconfx("internal error: unknown type %u of function's "
1306 "argument", conf_sect->arg_type);
1307 return (-1);
1308 }
1309
1310 /* Check whether current section is used in correct place. */
1311 id = conf_sect->sect_where;
1312 if (id != NULL)
1313 for (;; ++id) {
1314 if (*id == section)
1315 break;
1316 if (*id == 0) {
1317 logconfx("this section is not expected here");
1318 return (-1);
1319 }
1320 }
1321
1322 /* Check number of section's arguments. */
1323 nargs = conf_sect->arg_nargs;
1324 if (nargs >= 0) {
1325 if (parser_nargs != nargs) {
1326 logconfx("wrong number of arguments (has %d, "
1327 "should have %d)", parser_nargs, nargs);
1328 return (-1);
1329 }
1330 } else {
1331 nargs = -nargs;
1332 if (parser_nargs < nargs) {
1333 logconfx("this section should have at "
1334 "least %d argument%s", nargs,
1335 plural_form((unsigned int)nargs));
1336 return (-1);
1337 }
1338 }
1339
1340 /* Validate arguments if needed. */
1341 if (conf_sect->arg_regexp != NULL)
1342 if (regexec_simple(conf_sect->arg_regexp, parser_args) != 0) {
1343 logconfx("wrong format of an argument");
1344 return (-1);
1345 }
1346
1347 section = conf_sect->sect_id;
1348 if (!in_own_sect) {
1349 /* Register configuration event in module for its section. */
1350 if (st_mod_conf_event(cur_st_mod,
1351 IPA_CONF_EVENT_CUSTOM_SECT_BEGIN, section,
1352 (void *)NULL) < 0)
1353 return (-1);
1354 }
1355
1356 /* Parse it. */
1357 conf_event_no = 0;
1358 conf_event_arg = NULL;
1359 get_arg = &get_arg_tbl[conf_sect->arg_type];
1360 if (get_arg->reg != NULL)
1361 if (regexec_simple(get_arg->reg, parser_args) != 0) {
1362 logconfx("wrong format of an argument");
1363 return (-1);
1364 }
1365 if (get_arg->parse(get_arg->argp) < 0)
1366 return (-1);
1367 if (conf_sect->arg_parse != NULL)
1368 if (conf_sect->arg_parse(get_arg->argp) < 0)
1369 return (-1);
1370
1371 if (in_own_sect) {
1372 /*
1373 * Register configuration event in all modules for
1374 * ipastat.conf's section.
1375 */
1376 if (mod_conf_event(conf_event_begin[section], conf_event_no,
1377 conf_event_arg) < 0)
1378 return (-1);
1379 }
1380
1381 return (0);
1382 }
1383
1384 static int
token_parameter(void)1385 token_parameter(void)
1386 {
1387 const unsigned int *id;
1388 const char *prefix;
1389 const ipa_conf_param *conf_param;
1390 const struct get_arg *get_arg;
1391 const struct st_mod *st_mod;
1392 char *ptr;
1393 int nargs;
1394
1395 /* Get name of current parameter: "parameter" or "prefix:parameter". */
1396 curparam = parser_token;
1397 if (in_own_sect && (ptr = strchr(curparam, ':')) != NULL) {
1398 /*
1399 * Current section is not a custom section
1400 * and custom parameter appears.
1401 */
1402 *ptr = '\0';
1403 prefix = curparam;
1404 curparam = ptr + 1;
1405 st_mod = st_mod_by_prefix(prefix);
1406 if (st_mod != NULL) {
1407 conf_param_hash_ptr = st_mod->conf_param_hash;
1408 conf_param_tbl_ptr = st_mod->ipa_st_mod->conf_param_tbl;
1409 curmodfile = st_mod->mod_file;
1410 } else {
1411 logconfx("cannot find module with \"%s\" "
1412 "configuration prefix", prefix);
1413 return (-1);
1414 }
1415 }
1416
1417 /* Find parameter. */
1418 conf_param = find_conf_param(conf_param_tbl_ptr, conf_param_hash_ptr,
1419 curparam);
1420 if (conf_param == NULL) {
1421 logconfx("unknown parameter");
1422 return (-1);
1423 }
1424
1425 /* Validate type of argument. */
1426 if (conf_param->arg_type > IPA_CONF_TYPE_MISC) {
1427 logconfx("internal error: unknown type %u of an argument",
1428 conf_param->arg_type);
1429 return (-1);
1430 }
1431
1432 /* Check whether parameter is used in correct place. */
1433 id = conf_param->param_where;
1434 if (id != NULL)
1435 for (;; ++id) {
1436 if (*id == section)
1437 break;
1438 if (*id == 0) {
1439 logconfx("this parameter is not expected here");
1440 return (-1);
1441 }
1442 }
1443
1444 /* Check number of parameter's arguments. */
1445 nargs = conf_param->arg_nargs;
1446 if (nargs >= 0) {
1447 if (parser_nargs != nargs) {
1448 logconfx("wrong number of arguments (has %d, "
1449 "should have %d)", parser_nargs, nargs);
1450 return (-1);
1451 }
1452 } else {
1453 nargs = -nargs;
1454 if (parser_nargs < nargs) {
1455 logconfx("this parameter should have at "
1456 "least %d argument%s", nargs,
1457 plural_form((unsigned int)nargs));
1458 return (-1);
1459 }
1460 }
1461
1462 /* Validate arguments if needed. */
1463 if (conf_param->arg_regexp != NULL)
1464 if (regexec_simple(conf_param->arg_regexp, parser_args) != 0) {
1465 logconfx("wrong format of an argument");
1466 return (-1);
1467 }
1468
1469 /* Parse it. */
1470 get_arg = &get_arg_tbl[conf_param->arg_type];
1471 if (get_arg->reg != NULL)
1472 if (regexec_simple(get_arg->reg, parser_args) != 0) {
1473 logconfx("wrong format of an argument");
1474 return (-1);
1475 }
1476 if (get_arg->parse(get_arg->argp) < 0)
1477 return (-1);
1478 if (conf_param->arg_parse != NULL)
1479 if (conf_param->arg_parse(get_arg->argp) < 0)
1480 return (-1);
1481
1482 curparam = NULL;
1483 if (curmodfile != NULL && in_own_sect) {
1484 /* Restore ipastat.conf parameters tables. */
1485 curmodfile = NULL;
1486 conf_param_hash_ptr = conf_param_hash;
1487 conf_param_tbl_ptr = conf_param_tbl;
1488 }
1489
1490 return (0);
1491 }
1492
1493 static int
token_section_end(void)1494 token_section_end(void)
1495 {
1496 struct sect_stack *sect_stack;
1497
1498 if (!in_own_sect) {
1499 /* Register configuration event in module for its section. */
1500 if (st_mod_conf_event(cur_st_mod,
1501 IPA_CONF_EVENT_CUSTOM_SECT_END, section, (void *)NULL) < 0)
1502 return (-1);
1503 } else {
1504 /*
1505 * Register configuration event in all modules for
1506 * ipastat.conf's section.
1507 */
1508 if (mod_conf_event(conf_event_begin[section] + 1,
1509 conf_event_no, conf_event_arg) < 0)
1510 return (-1);
1511 #ifdef WITH_ANY_LIMITS
1512 switch (section) {
1513 # ifdef WITH_LIMITS
1514 case IPA_CONF_SECT_LIMIT:
1515 (void)parser_local_sym_del("limit");
1516 break;
1517 # endif
1518 # ifdef WITH_THRESHOLDS
1519 case IPA_CONF_SECT_THRESHOLD:
1520 (void)parser_local_sym_del("threshold");
1521 break;
1522 # endif
1523 }
1524 #endif /* WITH_ANY_LIMITS */
1525 }
1526
1527 sect_stack = SLIST_FIRST(§_stack_list);
1528 SLIST_REMOVE_HEAD(§_stack_list, link);
1529 section = sect_stack->section;
1530 mem_free(sect_stack, m_anon);
1531 if (!in_own_sect && section < IPA_CONF_SECT_CUSTOM_OFFSET) {
1532 /*
1533 * We were in custom section and previous section
1534 * is not a custom section. Restore ipastat.conf tables.
1535 */
1536 curmodfile = NULL;
1537 conf_sect_tbl_ptr = conf_sect_tbl;
1538 conf_param_tbl_ptr = conf_param_tbl;
1539 conf_sect_hash_ptr = conf_sect_hash;
1540 conf_param_hash_ptr = conf_param_hash;
1541 in_own_sect = 1;
1542 }
1543
1544 return (0);
1545 }
1546
1547 static int
deinit_config_mods(void)1548 deinit_config_mods(void)
1549 {
1550 const struct ipa_st_mod *ipa_st_mod;
1551 const struct st_mod *st_mod;
1552
1553 SLIST_FOREACH(st_mod, &st_mod_list, link) {
1554 ipa_st_mod = st_mod->ipa_st_mod;
1555 if (mimic_real_config)
1556 if (ipa_st_mod->conf_mimic_real() < 0) {
1557 logconfe("module %s: conf_mimic_real failed",
1558 st_mod->mod_file);
1559 return (-1);
1560 }
1561 deinit_conf_tbls(1,
1562 ipa_st_mod->conf_sect_tbl, st_mod->conf_sect_hash,
1563 ipa_st_mod->conf_param_tbl, st_mod->conf_param_hash);
1564 if (ipa_st_mod->conf_deinit() < 0) {
1565 logconfe("module %s: conf_deinit failed",
1566 st_mod->mod_file);
1567 return (-1);
1568 }
1569 }
1570 return (0);
1571 }
1572
1573 int
configure(PARSING_MODE mode)1574 configure(PARSING_MODE mode)
1575 {
1576 struct parser_pb *pb;
1577
1578 use_log = mode != TEST_PARSING ? 1 : 0;
1579
1580 /* Set wrappers for log functions during configuration. */
1581 xvlogmsgx = vlogconfx_priority;
1582 mvlogmsgx = vlogconfe;
1583 parser_vlogmsgx = parser_vlogmsgx_wrapper;
1584
1585 if (init_config_data() < 0)
1586 goto failed;
1587
1588 section = IPA_CONF_SECT_ROOT;
1589 memfunc.m_parser = m_parser;
1590
1591 if (check_conf_file(ipastat_conf_file, 0) < 0)
1592 goto failed;
1593
1594 /* Initialize parser and first pb. */
1595 if (parser_init() < 0)
1596 goto failed;
1597 pb = parser_new_pb(0);
1598 if (pb == NULL)
1599 return (-1);
1600 pb->fname = ipastat_conf_file;
1601 pb->fp = fopen(ipastat_conf_file, "r");
1602 if (pb->fp == NULL) {
1603 logconfe("fopen(%s, \"r\"): %s", ipastat_conf_file,
1604 strerror(errno));
1605 goto failed;
1606 }
1607 if (parser_push_pb(pb) < 0)
1608 goto failed;
1609 include_depth = 1;
1610
1611 /* Needed for log messages. */
1612 curparam = cursect = curmodfile = NULL;
1613
1614 /* Set ipastat.conf configuration tables. */
1615 conf_sect_tbl_ptr = conf_sect_tbl;
1616 conf_param_tbl_ptr = conf_param_tbl;
1617 conf_sect_hash_ptr = conf_sect_hash;
1618 conf_param_hash_ptr = conf_param_hash;
1619 in_own_sect = 1;
1620
1621 for (;;) {
1622 switch (parser_read_string()) {
1623 case 1:
1624 /* Successfully read one logical line. */
1625 break;
1626 case 0:
1627 /* EOF of current configuration file. */
1628 --include_depth;
1629 pb = parser_top_pb();
1630 if (fclose(pb->fp) != 0) {
1631 logconfe("fclose(%s): %s", pb->fname,
1632 strerror(errno));
1633 goto failed;
1634 }
1635 if (pb->fname != ipastat_conf_file)
1636 mem_free(pb->fname, m_parser);
1637 pb = parser_pop_pb();
1638 if (pb == NULL)
1639 goto end_of_parsing;
1640 /* Initialize previous file for parsing. */
1641 pb->fp = fopen(pb->fname, "r");
1642 if (pb->fp == NULL) {
1643 logconfe("fopen(%s, \"r\"): %s", pb->fname,
1644 strerror(errno));
1645 goto failed;
1646 }
1647 if (fseek(pb->fp, pb->foff, SEEK_SET) < 0) {
1648 logconfe("fseek(%s, %ld, SEEK_SET): %s",
1649 pb->fname, pb->foff, strerror(errno));
1650 goto failed;
1651 }
1652 continue;
1653 default: /* -1 */
1654 goto failed;
1655 }
1656 switch (parser_token_id) {
1657 case TOKEN_ID_SECTION_BEGIN:
1658 if (token_section_begin() < 0)
1659 goto failed;
1660 break;
1661 case TOKEN_ID_SECTION_END:
1662 if (token_section_end() < 0)
1663 goto failed;
1664 break;
1665 case TOKEN_ID_PARAMETER:
1666 if (token_parameter() < 0)
1667 goto failed;
1668 break;
1669 }
1670 }
1671
1672 end_of_parsing:
1673 if (deinit_config_mods() < 0)
1674 goto failed;
1675
1676 if (parser_deinit() < 0)
1677 goto failed;
1678
1679 deinit_config_data();
1680
1681 if (mode != TEST_PARSING)
1682 mimic_real_config = 1;
1683
1684 if (mimic_real_config) {
1685 set_global_params();
1686 rulepats_inherit();
1687 if (rules_inherit() < 0)
1688 goto failed;
1689 }
1690
1691 /* Set wrappers for log functions after configuration. */
1692 mvlogmsgx = mvlogmsgx_wrapper;
1693 xvlogmsgx = vlogmsgx;
1694 return (0);
1695
1696 failed:
1697 log_include_history();
1698 logconfe("configuration file parsing failed!");
1699 return (-1);
1700 }
1701
1702 /*
1703 * Unload all modules and free memory used by structures which
1704 * describe loaded modules. Note that any pointer should not
1705 * references any data in unloaded modules' memory. So, the best
1706 * place when to call this function: after memfunc_deinit_1() and
1707 * before memfunc_deinit_2().
1708 */
1709 int
unload_all_mods(void)1710 unload_all_mods(void)
1711 {
1712 struct st_mod *st_mod, *st_mod_next;
1713 int rv;
1714
1715 rv = 0;
1716 SLIST_FOREACH_SAFE(st_mod, &st_mod_list, link, st_mod_next) {
1717 if (dl_close(st_mod->mod_handle) < 0) {
1718 logmsgx(IPA_LOG_ERR, "module %s: dl_close failed: %s",
1719 st_mod->mod_file, dl_error());
1720 rv = -1;
1721 }
1722 mem_free(st_mod->mod_file, m_parser);
1723 mem_free(st_mod, m_anon);
1724 }
1725 return (rv);
1726 }
1727
1728 static void
mod_conf_show(unsigned int sect_id,unsigned int no)1729 mod_conf_show(unsigned int sect_id, unsigned int no)
1730 {
1731 const struct st_mod *st_mod;
1732
1733 need_nl = 0;
1734 SLIST_FOREACH(st_mod, &st_mod_list, link)
1735 st_mod->ipa_st_mod->conf_show(sect_id, no);
1736 if (sect_id == IPA_CONF_SECT_ROOT)
1737 print_nl_cond();
1738 }
1739
1740 static void
show_st_list(const struct st_list * list)1741 show_st_list(const struct st_list *list)
1742 {
1743 if (list != NULL) {
1744 print_param_name("st_list");
1745 if (list != &st_list_null) {
1746 const struct st_elem *st;
1747
1748 for (st = STAILQ_FIRST(list);;) {
1749 printf("%s", st->ipa_st_mod->st_name);
1750 st = STAILQ_NEXT(st, link);
1751 if (st == NULL)
1752 break;
1753 printf(" ");
1754 }
1755 } else
1756 printf("null");
1757 print_param_end();
1758 }
1759 }
1760
1761 #ifdef WITH_LIMITS
1762 static void
show_limits(const struct limits_list * list)1763 show_limits(const struct limits_list *list)
1764 {
1765 const struct limit *limit;
1766
1767 STAILQ_FOREACH(limit, list, link) {
1768 print_sect_name("limit");
1769 printf("%s ", limit->name);
1770 print_sect_begin();
1771 show_st_list(limit->st_list);
1772 mod_conf_show(IPA_CONF_SECT_LIMIT, limit->no);
1773 print_sect_end();
1774 }
1775 }
1776 #endif
1777
1778 #ifdef WITH_THRESHOLDS
1779 static void
show_thresholds(const struct thresholds_list * list)1780 show_thresholds(const struct thresholds_list *list)
1781 {
1782 const struct threshold *threshold;
1783
1784 STAILQ_FOREACH(threshold, list, link) {
1785 print_sect_name("threshold");
1786 printf("%s ", threshold->name);
1787 print_sect_begin();
1788 show_st_list(threshold->st_list);
1789 mod_conf_show(IPA_CONF_SECT_THRESHOLD, threshold->no);
1790 print_sect_end();
1791 }
1792 }
1793 #endif
1794
1795 /*
1796 * Main function for outputting configuration.
1797 */
1798 void
show_config(void)1799 show_config(void)
1800 {
1801 const struct rulepat *rulepat;
1802 const struct st_mod *st_mod;
1803 const struct rule *rule;
1804 unsigned int i;
1805
1806 printf(
1807 "/*\n\
1808 * This output is not identical to the original content of the\n\
1809 * configuration file(s), this is just how ipastat(8) and IPA modules\n\
1810 * see their configurations. Any \"include\" or \"include_files\"\n\
1811 * parameters are not printed and all macro variables are expanded.\n\
1812 */\n\n");
1813
1814 if (mimic_real_config)
1815 printf("/* Mimic real configuration regime (" IPASTAT_NAME "-"
1816 PACKAGE_VERSION "). */\n\n");
1817
1818 SLIST_FOREACH(st_mod, &st_mod_list, link) {
1819 print_param_name0("st_mod");
1820 print_string(st_mod->mod_file);
1821 print_param_end();
1822 need_nl = 1;
1823 }
1824 print_nl_cond();
1825
1826 if (posix_re_pattern >= 0) {
1827 print_param_name("posix_re_pattern");
1828 print_boolean(posix_re_pattern);
1829 print_param_end();
1830 need_nl = 1;
1831 }
1832 print_nl_cond();
1833
1834 if (debug_st_null >= 0) {
1835 print_param_name("debug_st_null");
1836 printf("%d", debug_st_null);
1837 print_param_end();
1838 need_nl = 1;
1839 }
1840 print_nl_cond();
1841
1842 if (dynamic_rules >= 0) {
1843 print_param_name("dynamic_rules");
1844 print_boolean(dynamic_rules);
1845 print_param_end();
1846 need_nl = 1;
1847 }
1848 #ifdef WITH_LIMITS
1849 if (dynamic_limits >= 0) {
1850 print_param_name("dynamic_limits");
1851 print_boolean(dynamic_limits);
1852 print_param_end();
1853 need_nl = 1;
1854 }
1855 #endif
1856 #ifdef WITH_THRESHOLDS
1857 if (dynamic_thresholds >= 0) {
1858 print_param_name("dynamic_thresholds");
1859 print_boolean(dynamic_thresholds);
1860 print_param_end();
1861 need_nl = 1;
1862 }
1863 #endif
1864 print_nl_cond();
1865
1866 if (value_units >= 0) {
1867 print_param_name("value_units");
1868 for (i = 0; i < VALUE_UNITS_TBL_SIZE; ++i)
1869 if (value_units == value_units_tbl[i].val) {
1870 printf("%s", value_units_tbl[i].str);
1871 print_param_end();
1872 print_nl();
1873 break;
1874 }
1875 }
1876
1877 mod_conf_show(IPA_CONF_SECT_ROOT, 0);
1878
1879 if (global_section_set) {
1880 print_sect_name("global");
1881 print_sect_begin();
1882 show_st_list(global_st_list);
1883 mod_conf_show(IPA_CONF_SECT_GLOBAL, 0);
1884 print_sect_end();
1885 print_nl();
1886 }
1887
1888 STAILQ_FOREACH(rulepat, &rulepats_list, link) {
1889 print_sect_name("rulepat");
1890 print_string(rulepat->pat);
1891 printf(" ");
1892 print_sect_begin();
1893 if (rulepat->check_next >= 0) {
1894 print_param_name("check_next_rulepat");
1895 print_boolean(rulepat->check_next);
1896 print_param_end();
1897 }
1898 show_st_list(rulepat->st_list);
1899 mod_conf_show(IPA_CONF_SECT_RULE, rulepat->no);
1900 #ifdef WITH_LIMITS
1901 show_limits(&rulepat->limits);
1902 #endif
1903 #ifdef WITH_THRESHOLDS
1904 show_thresholds(&rulepat->thresholds);
1905 #endif
1906 print_sect_end();
1907 print_nl();
1908 }
1909
1910 STAILQ_FOREACH(rule, &rules_list, list) {
1911 print_sect_name("rule");
1912 printf("%s ", rule->name);
1913 print_sect_begin();
1914 show_st_list(rule->st_list);
1915 mod_conf_show(IPA_CONF_SECT_RULE, rule->no);
1916 #ifdef WITH_LIMITS
1917 show_limits(&rule->limits);
1918 #endif
1919 #ifdef WITH_THRESHOLDS
1920 show_thresholds(&rule->thresholds);
1921 #endif
1922 print_sect_end();
1923 print_nl();
1924 }
1925 }
1926