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, &reg_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(&sect_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(&sect_stack_list);
1528 	SLIST_REMOVE_HEAD(&sect_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