1 /*-
2  * Copyright (c) 2003 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: confcommon.c,v 1.2.2.1 2011/11/15 18:12:29 simon Exp $";
32 #endif /* !lint */
33 
34 #include <sys/types.h>
35 
36 #include <ctype.h>
37 #include <errno.h>
38 #include <limits.h>
39 #include <locale.h>
40 #include <regex.h>
41 #include <stdarg.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45 #include <strings.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 
56 #if (SIZEOF_LONG * CHAR_BIT) < 32
57 # error "'long' is less than 32 bits: too small"
58 #endif
59 
60 #if (SIZEOF_LONG_LONG * CHAR_BIT) < 64
61 # error "'long long' is less than 64 bits: too small"
62 #endif
63 
64 /* Number of spaces for indentation of outputted configuration. */
65 #ifndef CONF_INDENT
66 # define CONF_INDENT	4
67 #endif
68 
69 /*
70  * All strto_xxx() functions get strings with positive decimal
71  * integers, so it is not necessary to check '-' in the first
72  * character of a string inside strto_xxx() functions (remember,
73  * that strtoul() and strtoull() work with negative values).
74  */
75 
76 #define LOG_BUF_SIZE	1024
77 
78 const char *const conf_event_msg[] = {
79 	"GLOBAL_BEGIN",		/*  0 */
80 	"GLOBAL_END",		/*  1 */
81 	"RULE_BEGIN",		/*  2 */
82 	"RULE_END",		/*  3 */
83 	"LIMIT_BEGIN",		/*  4 */
84 	"LIMIT_END",		/*  5 */
85 	"THRESHOLD_BEGIN",	/*  6 */
86 	"THRESHOLD_END",	/*  7 */
87 	"AUTORULE_BEGIN",	/*  8 */
88 	"AUTORULE_END",		/*  9 */
89 	"RULEPAT_BEGIN",	/* 10 */
90 	"RULEPAT_END",		/* 11 */
91 	"CUSTOM_SECT_BEGIN",	/* 12 */
92 	"CUSTOM_SECT_END"	/* 13 */
93 };
94 
95 int		value_units;	/* value_units parameter. */
96 char		got_arg_value;	/* Set if get_arg_value() was called. */
97 char		need_nl = 0;	/* Need a new line. */
98 static char	was_space = 0;	/* There was already space in output. */
99 
100 const char	*curmodfile;	/* File name of the current module. */
101 const char	*curparam;	/* Current parameter name. */
102 const char	*cursect;	/* Current section name. */
103 
104 static unsigned int conf_indent = 0; /* Current output indentation. */
105 
106 void		*conf_sect_he_mzone;
107 void		*conf_param_he_mzone;
108 
109 regex_t		re_time;
110 regex_t		re_bytes;
111 
112 /* Return values from get_arg_*(). */
113 static char	*arg_string;
114 static char	*arg_misc;
115 static int	arg_int;
116 static int32_t	arg_int32;
117 static uint32_t	arg_uint32;
118 static int64_t	arg_int64;
119 static uint64_t	arg_uint64[2];
120 
121 extern ipa_mem_type *m_anon;
122 
123 /*
124  * Just call regexec(3), ignoring result of match.
125  */
126 int
regexec_simple(const regex_t * re,const char * str)127 regexec_simple(const regex_t *re, const char *str)
128 {
129 	return (regexec(re, str, 0, (regmatch_t *)NULL, 0));
130 }
131 
132 /*
133  * Form an error message in buffer according to code using
134  * regerror(3) and return pointer to it.
135  */
136 const char *
regerrbuf(int code)137 regerrbuf(int code)
138 {
139 	static char buf[128];
140 
141 	regerror(code, (regex_t *)NULL, buf, sizeof(buf));
142 	return (buf);
143 }
144 
145 /*
146  * Print indentation spaces.
147  */
148 static void
print_indent(void)149 print_indent(void)
150 {
151 	unsigned int i;
152 
153 	for (i = conf_indent; i > 0; --i)
154 		printf(" ");
155 }
156 
157 /*
158  * This function is called by modules to output their log messages
159  * about errors occurred during parsing configuration file.
160  * If code is not equal to zero, then output error string as well.
161  */
162 void
mod_logconferr(const char * mod_name,int code,const char * format,va_list ap)163 mod_logconferr(const char *mod_name, int code, const char *format, va_list ap)
164 {
165 	char buf[LOG_BUF_SIZE];
166 	const struct parser_pb *pb;
167 	const char *err_pre, *err_msg;
168 	char *msg;
169 	int rv, msg_alloced;
170 
171 	msg_alloced = 0;
172 	rv = vsnprintf(buf, sizeof(buf), format, ap);
173 	if (rv < 0)
174 		msg = "(mod_logconferr: vsnprintf failed)";
175 	else if (rv < sizeof(buf))
176 		msg = buf;
177 	else if (rv >= sizeof(buf)) {
178 		msg = mem_malloc(++rv, m_anon);
179 		if (msg == NULL)
180 			msg = "(mod_logconferr: mem_malloc failed)";
181 		else if (vsnprintf(msg, rv, format, ap) < 0) {
182 			mem_free(msg, m_anon);
183 			msg = "(mod_logconferr: vsnprintf failed)";
184 		} else
185 			msg_alloced = 1;
186 	}
187 
188 	if (code == 0)
189 		err_pre = err_msg = "";
190 	else {
191 		err_pre = ": ";
192 		err_msg = strerror(code);
193 	}
194 
195 	pb = parser_top_pb();
196 	if (pb != NULL) {
197 		const char *token_type, *token_name, *token_tail;
198 
199 		if (curparam != NULL) {
200 			token_type = " parameter \"";
201 			token_name = curparam;
202 			token_tail = "\":";
203 		} else if (cursect != NULL) {
204 			token_type = " section \"";
205 			token_name = cursect;
206 			token_tail = "\":";
207 		} else
208 			token_type = token_name = token_tail = "\0";
209 		logconfe("%s:%u%s: MOD %s:%s%s%s %s%s%s",
210 		    pb->fname, pb->lineno,
211 		    pb->fp != NULL ? "" : "(in variable's value)",
212 		    mod_name, token_type, token_name, token_tail,
213 		    msg, err_pre, err_msg);
214 	} else
215 		logconfe("MOD %s: %s%s%s", mod_name, msg, err_pre, err_msg);
216 
217 	if (msg_alloced)
218 		mem_free(msg, m_anon);
219 }
220 
221 /*
222  * Output an error message for some line in the configuration file.
223  * If code is not zero, then output error string as well.
224  * This function is called from places where pb is defined.
225  */
226 static void
vlogconf(int code,const char * format,va_list ap)227 vlogconf(int code, const char *format, va_list ap)
228 {
229 	char buf[LOG_BUF_SIZE];
230 	const struct parser_pb *pb;
231 	const char *token_type, *token_name, *token_tail;
232 	const char *err_pre, *err_msg, *in_var_msg;
233 	char *msg;
234 	int rv, msg_alloced;
235 
236 	if (curparam != NULL) {
237 		token_type = " parameter \"";
238 		token_name = curparam;
239 		token_tail = "\":";
240 	} else if (cursect != NULL) {
241 		token_type = " section \"";
242 		token_name = cursect;
243 		token_tail = "\":";
244 	} else
245 		token_type = token_name = token_tail = "\0";
246 
247 	msg_alloced = 0;
248 	rv = vsnprintf(buf, sizeof(buf), format, ap);
249 	if (rv < 0)
250 		msg = "(vlogconf: vsnprintf failed)";
251 	else if (rv < sizeof(buf))
252 		msg = buf;
253 	else if (rv >= sizeof(buf)) {
254 		msg = mem_malloc(++rv, m_anon);
255 		if (msg == NULL)
256 			msg = "(vlogconf: mem_malloc failed)";
257 		else if (vsnprintf(msg, rv, format, ap) < 0) {
258 			mem_free(msg, m_anon);
259 			msg = "(vlogconf: vsnprintf failed)";
260 		} else
261 			msg_alloced = 1;
262 	}
263 
264 	if (code == 0)
265 		err_pre = err_msg = "";
266 	else {
267 		err_pre = ": ";
268 		err_msg = strerror(code);
269 	}
270 
271 	pb = parser_top_pb();
272 	in_var_msg = pb->fp != NULL ? "" : "(in variable's value)";
273 	if (curmodfile == NULL)
274 		logconfe("%s:%u%s:%s%s%s %s%s%s", pb->fname, pb->lineno,
275 		    in_var_msg, token_type, token_name, token_tail,
276 		    msg, err_pre, err_msg);
277 	else
278 		logconfe("%s:%u%s: module %s:%s%s%s %s%s%s", pb->fname,
279 		    pb->lineno, in_var_msg, curmodfile, token_type,
280 		    token_name, token_tail, msg, err_pre, err_msg);
281 
282 	if (msg_alloced)
283 		mem_free(msg, m_anon);
284 }
285 
286 /*
287  * Log an error message during configuration,
288  * use errno as error code.
289  */
290 void
logconf(const char * format,...)291 logconf(const char *format, ...)
292 {
293 	va_list ap;
294 	int errno_save;
295 
296 	errno_save = errno;
297 	va_start(ap, format);
298 	vlogconf(errno_save, format, ap);
299 	va_end(ap);
300 }
301 
302 /*
303  * Log an error message during configuration,
304  * do not use errno.
305  */
306 void
logconfx(const char * format,...)307 logconfx(const char *format, ...)
308 {
309 	va_list ap;
310 
311 	va_start(ap, format);
312 	vlogconf(0, format, ap);
313 	va_end(ap);
314 }
315 
316 /*
317  * Convert string to uint32_t.
318  */
319 int
strto_uint32(uint32_t * result,const char * nptr,char ** endptr_ret)320 strto_uint32(uint32_t *result, const char *nptr, char **endptr_ret)
321 {
322 	char *endptr;
323 	unsigned long val;
324 
325 	errno = 0;
326 	val = strtoul(nptr, &endptr, 10);
327 	if (errno != 0) {
328 		logconf("strtoul");
329 		return (-1);
330 	}
331 #if (SIZEOF_LONG * CHAR_BIT) > 32
332 	if (val > UINT32_MAX) {
333 		logconfx("too big value for 'uint32_t' type");
334 		return (-1);
335 	}
336 #endif
337 	if (nptr == endptr) {
338 		logconfx("wrong number");
339 		return (-1);
340 	}
341 	*result = (uint32_t)val;
342 	if (endptr_ret != NULL)
343 		*endptr_ret = endptr;
344 	return (0);
345 }
346 
347 #ifndef HAVE_STRTOULL
348 /*
349  * On the system (on which I tried to build IPA) that does not
350  * have strtoull(), ULLONG_MAX also was not defined.
351  */
352 # ifndef ULLONG_MAX
353 #  define ULLONG_MAX	UINT64_MAX
354 # endif
355 
356 /*
357  * Since this function always is called for base 10, third argument
358  * is not used, and since first argument always points to a string
359  * with all digits checks and implementation can be simplified
360  */
361 /* ARGSUSED2 */
362 static unsigned long long
strtoull(const char * nptr,char ** endptr,int base ATTR_UNUSED)363 strtoull(const char *nptr, char **endptr, int base ATTR_UNUSED)
364 {
365 	unsigned long long x;
366 	const char *ptr;
367 	char c;
368 
369 	x = 0;
370 	for (ptr = nptr; *ptr != '\0'; ++ptr) {
371 		c = *ptr - '0';
372 		if (x > (ULLONG_MAX / 10) ||
373 		    (x == (ULLONG_MAX / 10) && c > (ULLONG_MAX % 10))) {
374 			errno = ERANGE;
375 			return (ULLONG_MAX);
376 		}
377 		x *= 10;
378 		x += c;
379 	}
380 	if (endptr != NULL)
381 		*endptr = (char *)ptr;
382 	return (x);
383 }
384 #endif /* !HAVE_STRTOULL */
385 
386 /*
387  * Convert string to uint64_t.
388  */
389 int
strto_uint64(uint64_t * result,const char * nptr,char ** endptr_ret)390 strto_uint64(uint64_t *result, const char *nptr, char **endptr_ret)
391 {
392 	unsigned long long val;
393 	char *endptr;
394 
395 	errno = 0;
396 	val = strtoull(nptr, &endptr, 10);
397 	if (errno != 0) {
398 		logconf("strtoull");
399 		return (-1);
400 	}
401 #if (SIZEOF_LONG_LONG * CHAR_BIT) > 64
402 	if (val > UINT64_MAX) {
403 		logconfx("too big value for 'uint64_t' type");
404 		return (-1);
405 	}
406 #endif
407 	if (nptr == endptr) {
408 		logconfx("wrong number");
409 		return (-1);
410 	}
411 	*result = (uint64_t)val;
412 	if (endptr_ret != NULL)
413 		*endptr_ret = endptr;
414 	return (0);
415 }
416 
417 /*
418  * Convert string to 'unsigned int'.
419  */
420 int
strto_u_int(unsigned int * result,const char * nptr,char ** endptr)421 strto_u_int(unsigned int *result, const char *nptr, char **endptr)
422 {
423 	unsigned long val;
424 
425 	errno = 0;
426 	val = strtoul(nptr, endptr, 10);
427 	if (errno != 0) {
428 		logconf("strtoul");
429 		return (-1);
430 	}
431 #if ULONG_MAX > UINT_MAX
432 	if (val > UINT_MAX) {
433 		logconfx("too big value for 'unsigned int' type");
434 		return (-1);
435 	}
436 #endif
437 	*result = (unsigned int)val;
438 	return (0);
439 }
440 
441 /*
442  * Check whether the given string is an unsigned integer.
443  */
444 static int
check_unsigned_integer(const char * ptr,const char * what)445 check_unsigned_integer(const char *ptr, const char *what)
446 {
447 	for (; *ptr != '\0'; ++ptr)
448 		if (!isdigit((unsigned char)*ptr)) {
449 			logconfx("argument should be %s integer", what);
450 			return (-1);
451 		}
452 	return (0);
453 }
454 
455 /*
456  * Convert string to uint32_t, allow a user to specify
457  * a `+' sign before number.
458  */
459 static int
get_arg_uint32(void * res)460 get_arg_uint32(void *res)
461 {
462 	char *ptr;
463 
464 	ptr = parser_args;
465 	if (*ptr == '+')
466 		++ptr;
467 	if (check_unsigned_integer(ptr, "a positive") < 0)
468 		return (-1);
469 	return (strto_uint32((uint32_t *)res, ptr, (char **)NULL));
470 }
471 
472 /*
473  * Convert string to int32_t, allow a user to specify
474  * `-' and `+' signs before number.
475  */
476 static int
get_arg_int32(void * res)477 get_arg_int32(void *res)
478 {
479 	char *ptr;
480 	uint32_t val;
481 	char sign;
482 
483 	ptr = parser_args;
484 	switch (*ptr) {
485 	case '-':
486 		sign = 1;
487 		++ptr;
488 		break;
489 	case '+':
490 		++ptr;
491 		/* FALLTHROUGH */
492 	default:
493 		sign = 0;
494 	}
495 
496 	if (check_unsigned_integer(ptr, "an") < 0)
497 		return (-1);
498 	if (strto_uint32(&val, ptr, (char **)NULL) < 0)
499 		return (-1);
500 
501 	if (sign) {
502 		/* This can be wrong on system where |INT32_MIN| > INT32_MAX */
503 		if (val > INT32_MAX) {
504 			logconfx("too little value for 'int32_t' type");
505 			return (-1);
506 		}
507 		*(int32_t *)res = -(int32_t)val;
508 	} else {
509 		if (val > INT32_MAX) {
510 			logconfx("too big value for 'int32_t' type");
511 			return (-1);
512 		}
513 		*(int32_t *)res = (int32_t)val;
514 	}
515 
516 	return (0);
517 }
518 
519 /*
520  * Convert string to int64_t, allow a user to specify
521  * `-' and `+' signs before number.
522  */
523 static int
get_arg_int64(void * res)524 get_arg_int64(void *res)
525 {
526 	uint64_t val;
527 	char *ptr;
528 	char sign;
529 
530 	ptr = parser_args;
531 	switch (*ptr) {
532 	case '-':
533 		sign = 1;
534 		++ptr;
535 		break;
536 	case '+':
537 		++ptr;
538 		/* FALLTHROUGH */
539 	default:
540 		sign = 0;
541 	}
542 
543 	if (check_unsigned_integer(ptr, "an") < 0)
544 		return (-1);
545 	if (strto_uint64(&val, ptr, (char **)NULL) < 0)
546 		return (-1);
547 
548 	if (sign) {
549 		/* This can be wrong on system where |INT64_MIN| > INT64_MAX */
550 		if (val > INT64_MAX) {
551 			logconfx("too little value for 'int64_t' type");
552 			return (-1);
553 		}
554 		*(int64_t *)res = -(int64_t)val;
555 	} else {
556 		if (val > INT64_MAX) {
557 			logconfx("too big value for 'int64_t' type");
558 			return (-1);
559 		}
560 		*(int64_t *)res = (int64_t)val;
561 	}
562 
563 	return (0);
564 }
565 
566 /*
567  * Convert string to uint64_t, allow a user to specify
568  * a `+' sign before number.
569  */
570 int
get_arg_uint64(void * res)571 get_arg_uint64(void *res)
572 {
573 	char *ptr;
574 
575 	ptr = parser_args;
576 	if (*ptr == '+')
577 		++ptr;
578 	if (check_unsigned_integer(ptr, "a positive") < 0)
579 		return (-1);
580 	return (strto_uint64((uint64_t *)res, ptr, (char **)NULL));
581 }
582 
583 int
get_arg_time(void * res)584 get_arg_time(void *res)
585 {
586 	uint64_t value, result;
587 	char *ptr, *endptr;
588 	char level, error, overflow;
589 
590 	result = 0;
591 	ptr = parser_args;
592 	level = error = overflow = 0;
593 
594 	for (;;) {
595 		if (strto_uint64(&value, ptr, &endptr) < 0)
596 			return (-1);
597 		ptr = endptr;
598 		switch (*ptr) {
599 		case 'h':
600 			if (level > 0)
601 				error = 1;
602 			else if (value > UINT64_MAX / SECONDS_IN_HOUR)
603 				overflow = 1;
604 			else {
605 				level = 1;
606 				value *= SECONDS_IN_HOUR;
607 			}
608 			break;
609 		case 'm':
610 			if (level > 1)
611 				error = 1;
612 			else if (value > UINT64_MAX / SECONDS_IN_MINUTE)
613 				overflow = 1;
614 			else {
615 				level = 2;
616 				value *= SECONDS_IN_MINUTE;
617 			}
618 			break;
619 		default: /* 's' */
620 			if (level > 2)
621 				error = 1;
622 			else
623 				level = 3;
624 		}
625 		if (error) {
626 			logconfx("wrong time format");
627 			return (-1);
628 		}
629 		if (overflow || result > UINT64_MAX - value) {
630 			logconfx("too big value for 'uint64_t' type");
631 			return (-1);
632 		}
633 		result += value;
634 
635 		if (*++ptr == '\0') {
636 			/* EOL */
637 			break;
638 		}
639 		if (*ptr == ' ')
640 			++ptr;
641 	}
642 	*(uint64_t *)res = result;
643 	return (0);
644 }
645 
646 /*
647  * Parse a value for the boolean variable,
648  * all comparisons are case insensitive.
649  */
650 static int
get_arg_boolean(void * res)651 get_arg_boolean(void *res)
652 {
653 	const char *ptr;
654 
655 	ptr = parser_args;
656 	if (strcasecmp(ptr, "yes") == 0)
657 		*(int *)res = 1;
658 	else if (strcasecmp(ptr, "no") == 0)
659 		*(int *)res = 0;
660 	else {
661 		logconfx("argument should be \"yes\" or \"no\"");
662 		return (-1);
663 	}
664 	return (0);
665 }
666 
667 static int
get_arg_string(void * res)668 get_arg_string(void *res)
669 {
670 	char *ptr;
671 
672 	if (!parser_arg_is_str()) {
673 		logconfx("argument should be a string");
674 		return (-1);
675 	}
676 	ptr = parser_strdup(parser_args, m_parser);
677 	if (ptr == NULL)
678 		return (-1);
679 	*(char **)res = ptr;
680 	return (0);
681 }
682 
683 int
get_arg_bytes(void * res)684 get_arg_bytes(void *res)
685 {
686 	uint64_t value, result;
687 	char *ptr, *endptr;
688 	char level, error, overflow;
689 
690 	result = 0;
691 	ptr = parser_args;
692 	level = error = overflow = 0;
693 
694 	for (;;) {
695 		if (strto_uint64(&value, ptr, &endptr) < 0)
696 			return (-1);
697 		ptr = endptr;
698 		switch (*ptr) {
699 		case 'T':
700 			if (level > 0)
701 				error = 1;
702 			else if (value > UINT64_MAX / TBYTE)
703 				overflow = 1;
704 			else {
705 				level = 1;
706 				value *= TBYTE;
707 			}
708 			break;
709 		case 'G':
710 			if (level > 1)
711 				error = 1;
712 			else if (value > UINT64_MAX / GBYTE)
713 				overflow = 1;
714 			else {
715 				level = 2;
716 				value *= GBYTE;
717 			}
718 			break;
719 		case 'M':
720 			if (level > 2)
721 				error = 1;
722 			else if (value > UINT64_MAX / MBYTE)
723 				overflow = 1;
724 			else {
725 				level = 3;
726 				value *= MBYTE;
727 			}
728 			break;
729 		case 'K':
730 			if (level > 3)
731 				error = 1;
732 			else if (value > UINT64_MAX / KBYTE)
733 				overflow = 1;
734 			else {
735 				level = 4;
736 				value *= KBYTE;
737 			}
738 			break;
739 		default: /* 'B' */
740 			if (level > 4)
741 				error = 1;
742 			else
743 				level = 5;
744 		}
745 		if (error) {
746 			logconfx("wrong format of an argument");
747 			return (-1);
748 		}
749 		if (overflow || result > UINT64_MAX - value) {
750 			logconfx("too big value for 'uint64_t' type");
751 			return (-1);
752 		}
753 		result += value;
754 
755 		if (*++ptr == '\0') {
756 			/* EOL */
757 			break;
758 		}
759 		if (*ptr == ' ')
760 			++ptr;
761 	}
762 	*(uint64_t *)res = result;
763 	return (0);
764 }
765 
766 int
get_arg_value(void * res)767 get_arg_value(void *res)
768 {
769 	uint64_t type, val;
770 	char *ptr;
771 
772 	ptr = parser_args;
773 	if (regexec_simple(&re_bytes, ptr) == 0) {
774 		if (get_arg_bytes(&val) < 0)
775 			return (-1);
776 		type = IPA_CONF_TYPE_BYTES;
777 	} else if (regexec_simple(&re_time, ptr) == 0) {
778 		if (get_arg_time(&val) < 0)
779 			return (-1);
780 		type = IPA_CONF_TYPE_TIME;
781 	} else {
782 		if (get_arg_uint64(&val) < 0)
783 			return (-1);
784 		type = IPA_CONF_TYPE_UINT64;
785 	}
786 	if (value_units > 0 && type != value_units) {
787 		logconfx("type of value is not the same as allowed "
788 		    "by the \"value_units\" parameter");
789 		return (-1);
790 	}
791 	*(uint64_t *)res = type;
792 	*((uint64_t *)res + 1) = val;
793 	got_arg_value = 1;
794 	return (0);
795 }
796 
797 int
get_arg_per_cent(void * res)798 get_arg_per_cent(void *res)
799 {
800 	char *ptr;
801 	uint32_t pc;
802 
803 	ptr = parser_args + parser_args_len - 1;
804 	if (*ptr != '%') {
805 		logconfx("wrong format of an argument");
806 		return (-1);
807 	}
808 	*ptr = '\0';
809 	if (get_arg_uint32(&pc) < 0)
810 		return (-1);
811 	if (pc > 100) {
812 		logconfx("per cent value should be <= 100%%");
813 		return (-1);
814 	}
815 	*(int *)res = pc;
816 	return (0);
817 }
818 
819 /*
820  * Simply point *res to read string from configuration file.
821  */
822 static int
get_arg_misc(void * res)823 get_arg_misc(void *res)
824 {
825 	*(char **)res = parser_args;
826 	return (0);
827 }
828 
829 static unsigned int
conf_hash_value(const char * name)830 conf_hash_value(const char *name)
831 {
832 	unsigned int hash_value;
833 
834 	for (hash_value = 0; *name != '\0'; ++name)
835 		hash_value += *name;
836 	return (hash_value);
837 }
838 
839 #define sect_hash_value(x) conf_hash_value(x)
840 #define param_hash_value(x) conf_hash_value(x)
841 
842 #define sect_hash_bucket(x) ((x) & (CONF_SECT_BUCKETS - 1))
843 #define param_hash_bucket(x) ((x) & (CONF_PARAM_BUCKETS - 1))
844 
845 static int
init_conf_sect_tbl(const char * mod_file,int build_re,ipa_conf_sect * tbl,struct conf_sect_hash ** hash_ptr)846 init_conf_sect_tbl(const char *mod_file, int build_re, ipa_conf_sect *tbl,
847     struct conf_sect_hash **hash_ptr)
848 {
849 	const char *errbuf;
850 	const ipa_conf_sect *sect;
851 	struct conf_sect_hash *hash;
852 	struct conf_sect_he *he;
853 	unsigned int i;
854 	int error;
855 
856 	/* Allocate hash buckets. */
857 	hash = mem_malloc(CONF_SECT_BUCKETS * sizeof(*hash), m_anon);
858 	if (hash == NULL) {
859 		logconfe("init_conf_sect_tbls: mem_malloc failed");
860 		return (-1);
861 	}
862 	*hash_ptr = hash;
863 
864 	/* Initialize head of each hash bucket. */
865 	for (i = 0; i < CONF_SECT_BUCKETS; ++i)
866 		SLIST_INIT(&hash[i]);
867 
868 	/* Build regular expressions and hash entries. */
869 	for (sect = tbl, i = 0; sect->sect_name != NULL; ++i, ++sect) {
870 		if (sect->arg_pattern != NULL && build_re) {
871 			error = regcomp(sect->arg_regexp, sect->arg_pattern,
872 			    REG_EXTENDED|REG_NOSUB);
873 			if (error != 0) {
874 				errbuf = regerrbuf(error);
875 				if (mod_file == NULL)
876 					logconfe("init_conf_sect_tbls: regcomp"
877 					    "(\"%s\"): %s", sect->arg_pattern,
878 					    errbuf);
879 				else
880 					logconfe("init_conf_sect_tbls for "
881 					    "module %s: regcomp(\"%s\"): %s",
882 					    mod_file, sect->arg_pattern,
883 					    errbuf);
884 				return (-1);
885 			}
886 		}
887 		he = mzone_alloc(conf_sect_he_mzone);
888 		if (he == NULL) {
889 			logconfe("init_conf_sect_tbls: mzone_malloc failed");
890 			return (-1);
891 		}
892 		he->sect_name = sect->sect_name;
893 		he->idx = i;
894 		he->hash_value = sect_hash_value(he->sect_name);
895 		SLIST_INSERT_HEAD(
896 		    &hash[sect_hash_bucket(he->hash_value)], he, link);
897 	}
898 	return (0);
899 }
900 
901 static int
init_conf_param_tbl(const char * mod_file,int build_re,ipa_conf_param * tbl,struct conf_param_hash ** hash_ptr)902 init_conf_param_tbl(const char *mod_file, int build_re, ipa_conf_param *tbl,
903     struct conf_param_hash **hash_ptr)
904 {
905 	const char *errbuf;
906 	const ipa_conf_param *param;
907 	struct conf_param_hash *hash;
908 	struct conf_param_he *he;
909 	unsigned int i;
910 	int error;
911 
912 	/* Allocate hash buckets. */
913 	hash = mem_malloc(CONF_PARAM_BUCKETS * sizeof(*hash), m_anon);
914 	if (hash == NULL) {
915 		logconfe("init_conf_param_tbls: mem_malloc failed");
916 		return (-1);
917 	}
918 	*hash_ptr = hash;
919 
920 	/* Initialize head of each hash bucket. */
921 	for (i = 0; i < CONF_PARAM_BUCKETS; ++i)
922 		SLIST_INIT(&hash[i]);
923 
924 	/* Build regular expressions and hash entries. */
925 	for (param = tbl, i = 0; param->param_name != NULL; ++i, ++param) {
926 		if (param->arg_pattern != NULL && build_re) {
927 			error = regcomp(param->arg_regexp, param->arg_pattern,
928 			    REG_EXTENDED|REG_NOSUB);
929 			if (error != 0) {
930 				errbuf = regerrbuf(error);
931 				if (mod_file == NULL)
932 					logconfe("init_conf_param_tbls: regcomp"
933 					    "(\"%s\"): %s", param->arg_pattern,
934 					    errbuf);
935 				else
936 					logconfe("init_conf_param_tbls for "
937 					    "module %s: regcomp(\"%s\"): %s",
938 					    mod_file, param->arg_pattern,
939 					    errbuf);
940 				return (-1);
941 			}
942 		}
943 		he = mzone_alloc(conf_param_he_mzone);
944 		if (he == NULL) {
945 			logconfe("init_conf_param_tbls: mzone_alloc failed");
946 			return (-1);
947 		}
948 		he->param_name = param->param_name;
949 		he->idx = i;
950 		he->hash_value = param_hash_value(he->param_name);
951 		SLIST_INSERT_HEAD(
952 		    &hash[param_hash_bucket(he->hash_value)], he, link);
953 	}
954 	return (0);
955 }
956 
957 /*
958  * Compile regular expressions and initialize hash tables for
959  * configuration sections and parameters.
960  */
961 int
init_conf_tbls(const char * mod_file,int build_re,ipa_conf_sect * sect_tbl,struct conf_sect_hash ** sect_hash_ptr,ipa_conf_param * param_tbl,struct conf_param_hash ** param_hash_ptr)962 init_conf_tbls(const char *mod_file, int build_re,
963     ipa_conf_sect *sect_tbl, struct conf_sect_hash **sect_hash_ptr,
964     ipa_conf_param *param_tbl, struct conf_param_hash **param_hash_ptr)
965 {
966 	if (sect_tbl == NULL)
967 		*sect_hash_ptr = NULL;
968 	else if (init_conf_sect_tbl(mod_file, build_re, sect_tbl,
969 	    sect_hash_ptr) < 0)
970 		return (-1);
971 
972 	if (param_tbl == NULL)
973 		*param_hash_ptr = NULL;
974 	else if (init_conf_param_tbl(mod_file, build_re, param_tbl,
975 	    param_hash_ptr) < 0)
976 		return (-1);
977 
978 	return (0);
979 }
980 
981 const ipa_conf_sect *
find_conf_sect(const ipa_conf_sect * tbl,const struct conf_sect_hash * hash,const char * sect_name)982 find_conf_sect(const ipa_conf_sect *tbl, const struct conf_sect_hash *hash,
983     const char *sect_name)
984 {
985 	if (hash != NULL) {
986 		const struct conf_sect_hash *bucket;
987 		const struct conf_sect_he *he;
988 		unsigned int hash_value;
989 
990 		hash_value = sect_hash_value(sect_name);
991 		bucket = &hash[sect_hash_bucket(hash_value)];
992 
993 		SLIST_FOREACH(he, bucket, link)
994 			if (he->hash_value == hash_value &&
995 			    strcmp(he->sect_name, sect_name) == 0)
996 				return (&tbl[he->idx]);
997 	}
998 	return (NULL);
999 }
1000 
1001 const ipa_conf_param *
find_conf_param(const ipa_conf_param * tbl,const struct conf_param_hash * hash,const char * param_name)1002 find_conf_param(const ipa_conf_param *tbl, const struct conf_param_hash *hash,
1003     const char *param_name)
1004 {
1005 	if (hash != NULL) {
1006 		const struct conf_param_hash *bucket;
1007 		const struct conf_param_he *he;
1008 		unsigned int hash_value;
1009 
1010 		hash_value = param_hash_value(param_name);
1011 		bucket = &hash[param_hash_bucket(hash_value)];
1012 
1013 		SLIST_FOREACH(he, bucket, link)
1014 			if (he->hash_value == hash_value &&
1015 			    strcmp(he->param_name, param_name) == 0)
1016 				return (&tbl[he->idx]);
1017 	}
1018 	return (NULL);
1019 }
1020 
1021 /*
1022  * Release memory hold by compiled regular expressions, if
1023  * free_regexp is non-zero, for configuration sections and
1024  * parameters, hash entries are freed all at once with one
1025  * mzone_deinit() call.
1026  */
1027 void
deinit_conf_tbls(int free_re,ipa_conf_sect * sect_tbl,struct conf_sect_hash * sect_hash,ipa_conf_param * param_tbl,struct conf_param_hash * param_hash)1028 deinit_conf_tbls(int free_re,
1029     ipa_conf_sect *sect_tbl, struct conf_sect_hash *sect_hash,
1030     ipa_conf_param *param_tbl, struct conf_param_hash *param_hash)
1031 {
1032 	/* Release memory hold by hash buckets. */
1033 	mem_free(sect_hash, m_anon);
1034 	mem_free(param_hash, m_anon);
1035 
1036 	if (!free_re)
1037 		return;
1038 
1039 	/* Release memory hold by regular expressions. */
1040 	if (sect_tbl != NULL) {
1041 		const ipa_conf_sect *sect;
1042 
1043 		for (sect = sect_tbl; sect->sect_name != NULL; ++sect)
1044 			if (sect->arg_pattern != NULL)
1045 				regfree(sect->arg_regexp);
1046 	}
1047 	if (param_tbl != NULL) {
1048 		const ipa_conf_param *param;
1049 
1050 		for (param = param_tbl; param->param_name != NULL; ++param)
1051 			if (param->arg_pattern != NULL)
1052 				regfree(param->arg_regexp);
1053 	}
1054 }
1055 
1056 const char *
plural_form(unsigned int n)1057 plural_form(unsigned int n)
1058 {
1059 	return (n != 1 ? "s" : "");
1060 }
1061 
1062 /*
1063  * All print_*() functions are trivial, just take care about
1064  * need_nl and was_space variables.
1065  */
1066 
1067 struct time_conv {
1068 	uint64_t	div;
1069 	uint64_t	val;
1070 	char		ch;
1071 };
1072 
1073 static struct time_conv time_conv_tbl[] = {
1074 	{ SECONDS_IN_HOUR,	0, 'h' },
1075 	{ SECONDS_IN_MINUTE,	0, 'm' },
1076 	{ 1,			0, 's' }
1077 };
1078 
1079 #define TIME_CONV_TBL_SIZE (sizeof(time_conv_tbl) / sizeof(time_conv_tbl[0]))
1080 
1081 void
print_time(const uint64_t * ptr)1082 print_time(const uint64_t *ptr)
1083 {
1084 	uint64_t a;
1085 	int i, i1, i2;
1086 
1087 	a = *ptr;
1088 	i1 = i2 = -1;
1089 	for (i = 0; i < TIME_CONV_TBL_SIZE; ++i) {
1090 		if ((time_conv_tbl[i].val = a / time_conv_tbl[i].div) != 0) {
1091 			if (i1 < 0)
1092 				i1 = i;
1093 			i2 = i;
1094 		}
1095 		a %= time_conv_tbl[i].div;
1096 	}
1097 	if (i1 >= 0) {
1098 		for (i = i1;; ++i) {
1099 			printf("%"PRIu64"%c", time_conv_tbl[i].val,
1100 			    time_conv_tbl[i].ch);
1101 			if (i == i2)
1102 				break;
1103 			printf(" ");
1104 		}
1105 	} else
1106 		printf("0s");
1107 }
1108 
1109 struct byte_conv {
1110 	uint64_t	div;
1111 	unsigned int	val;
1112 	char		ch;
1113 };
1114 
1115 static struct byte_conv byte_conv_tbl[] = {
1116 	{ TBYTE,	0, 'T' },
1117 	{ GBYTE,	0, 'G' },
1118 	{ MBYTE,	0, 'M' },
1119 	{ KBYTE,	0, 'K' },
1120 	{ 1,		0, 'B' }
1121 };
1122 
1123 #define BYTE_CONV_TBL_SIZE (sizeof(byte_conv_tbl) / sizeof(byte_conv_tbl[0]))
1124 
1125 void
print_bytes(const uint64_t * ptr)1126 print_bytes(const uint64_t *ptr)
1127 {
1128 	uint64_t a;
1129 	int i, i1, i2;
1130 
1131 	a = *ptr;
1132 	i1 = i2 = -1;
1133 	for (i = 0; i < BYTE_CONV_TBL_SIZE; ++i) {
1134 		if ((byte_conv_tbl[i].val = a / byte_conv_tbl[i].div) != 0) {
1135 			if (i1 < 0)
1136 				i1 = i;
1137 			i2 = i;
1138 		}
1139 		a %= byte_conv_tbl[i].div;
1140 	}
1141 	if (i1 >= 0) {
1142 		for (i = i1;; ++i) {
1143 			printf("%u%c", byte_conv_tbl[i].val,
1144 			    byte_conv_tbl[i].ch);
1145 			if (i == i2)
1146 				break;
1147 			printf(" ");
1148 		}
1149 	} else
1150 		printf("0B");
1151 }
1152 
1153 void
print_value(const uint64_t * value,unsigned int value_type)1154 print_value(const uint64_t *value, unsigned int value_type)
1155 {
1156 	switch (value_type) {
1157 	case IPA_CONF_TYPE_UINT64:
1158 		printf("%"PRIu64, *value);
1159 		break;
1160 	case IPA_CONF_TYPE_BYTES:
1161 		print_bytes(value);
1162 		break;
1163 	case IPA_CONF_TYPE_TIME:
1164 		print_time(value);
1165 		break;
1166 	}
1167 }
1168 
1169 void
print_boolean(int boolean)1170 print_boolean(int boolean)
1171 {
1172 	was_space = 0;
1173 	if (boolean)
1174 		printf("yes");
1175 	else
1176 		printf("no");
1177 }
1178 
1179 void
print_string(const char * str)1180 print_string(const char *str)
1181 {
1182 	was_space = 0;
1183 	printf("\"");
1184 	for (; *str != '\0'; ++str)
1185 		switch (*str) {
1186 		case '\t':
1187 			printf("\\t");
1188 			break;
1189 		case '\n':
1190 			printf("\\n");
1191 			break;
1192 		case '\"':
1193 		case '\\':
1194 			printf("\\");
1195 			/* FALLTHROUGH */
1196 		default:
1197 			printf("%c", *str);
1198 		}
1199 	printf("\"");
1200 }
1201 
1202 void
mod_print_param_name(const char * prefix,const char * param_name)1203 mod_print_param_name(const char *prefix, const char *param_name)
1204 {
1205 	print_indent();
1206 	if (prefix != NULL)
1207 		printf("%s:", prefix);
1208 	printf("%s = ", param_name);
1209 	was_space = 1;
1210 }
1211 
1212 void
mod_print_param_end(void)1213 mod_print_param_end(void)
1214 {
1215 	print_param_end();
1216 	need_nl = 1;
1217 }
1218 
1219 void
mod_print_args(const char * format,va_list ap)1220 mod_print_args(const char *format, va_list ap)
1221 {
1222 	was_space = 0;
1223 	vprintf(format, ap);
1224 }
1225 
1226 void
print_nl(void)1227 print_nl(void)
1228 {
1229 	printf("\n");
1230 }
1231 
1232 void
print_nl_cond(void)1233 print_nl_cond(void)
1234 {
1235 	if (need_nl) {
1236 		need_nl = 0;
1237 		print_nl();
1238 	}
1239 }
1240 
1241 void
mod_print_sect_name(const char * prefix,const char * sect_name)1242 mod_print_sect_name(const char *prefix, const char *sect_name)
1243 {
1244 	print_indent();
1245 	if (prefix != NULL)
1246 		printf("%s:", prefix);
1247 	printf("%s ", sect_name);
1248 	was_space = 1;
1249 }
1250 
1251 void
mod_print_sect_end(void)1252 mod_print_sect_end(void)
1253 {
1254 	print_sect_end();
1255 	need_nl = 1;
1256 }
1257 
1258 void
print_space(void)1259 print_space(void)
1260 {
1261 	if (!was_space) {
1262 		was_space = 1;
1263 		printf(" ");
1264 	}
1265 }
1266 
1267 void
print_param_name(const char * name)1268 print_param_name(const char *name)
1269 {
1270 	print_indent();
1271 	printf("%s = ", name);
1272 }
1273 
1274 void
print_param_name0(const char * name)1275 print_param_name0(const char *name)
1276 {
1277 	print_indent();
1278 	printf("%s ", name);
1279 }
1280 
1281 void
print_param_end(void)1282 print_param_end(void)
1283 {
1284 	printf(";\n");
1285 }
1286 
1287 void
print_sect_name(const char * name)1288 print_sect_name(const char *name)
1289 {
1290 	print_indent();
1291 	printf("%s ", name);
1292 }
1293 
1294 void
print_sect_begin(void)1295 print_sect_begin(void)
1296 {
1297 	printf("{\n");
1298 	conf_indent += CONF_INDENT;
1299 }
1300 
1301 void
print_sect_end(void)1302 print_sect_end(void)
1303 {
1304 	conf_indent -= CONF_INDENT;
1305 	print_indent();
1306 	printf("}\n");
1307 }
1308 
1309 struct conf_re {
1310 	const char	*pat;	/* Regular expression pattern. */
1311 	regex_t		*re;	/* Compiled regular expression. */
1312 };
1313 
1314 static struct conf_re conf_re_tbl[] = {
1315 	{ PAT_BYTES,	&re_bytes	},
1316 	{ PAT_TIME,	&re_time	}
1317 };
1318 
1319 #define CONF_RE_TBL_SIZE (sizeof(conf_re_tbl) / sizeof(conf_re_tbl[0]))
1320 
1321 /*
1322  * Build regular expressions for parsing some IPA_CONF_TYPE_xxx.
1323  */
1324 int
build_conf_re(void)1325 build_conf_re(void)
1326 {
1327 	static char called = 0;
1328 
1329 	const struct conf_re *cre;
1330 	unsigned int i;
1331 	int error;
1332 
1333 	if (called)
1334 		return (0);
1335 	called = 1;
1336 
1337 	for (i = 0, cre = conf_re_tbl; i < CONF_RE_TBL_SIZE; ++cre, ++i) {
1338 		error = regcomp(cre->re, cre->pat, REG_EXTENDED|REG_NOSUB);
1339 		if (error != 0) {
1340 			logconfe("build_conf_re: regcomp(\"%s\"): %s",
1341 			    cre->pat, regerrbuf(error));
1342 			return (-1);
1343 		}
1344 	}
1345 
1346 	return (0);
1347 }
1348 
1349 void *
dl_lookup_sym(dl_handle handle,const char * sym)1350 dl_lookup_sym(dl_handle handle, const char *sym)
1351 {
1352 	void *ptr;
1353 #ifdef SYM_PREFIX
1354 	char *psym;
1355 #endif
1356 
1357 #ifdef SYM_PREFIX
1358 	if (mem_asprintf(m_anon, &psym, SYM_PREFIX"%s", sym) < 0) {
1359 		logconfx("dl_lookup_sym: mem_asprintf failed");
1360 		return (NULL);
1361 	}
1362 	ptr = dl_sym(handle, psym);
1363 	mem_free(psym, m_anon);
1364 	if (ptr != NULL)
1365 		return (ptr);
1366 #endif
1367 
1368 	ptr = dl_sym(handle, sym);
1369 	if (ptr == NULL)
1370 		logconfx("dl_lookup_sym: dl_sym(\"%s\"): %s", sym, dl_error());
1371 
1372 	return (ptr);
1373 }
1374 
1375 /*
1376  * Any symbol in any name must be letter, digit or punctuation (except
1377  * double quote and both slashes) from the ASCII character set.
1378  */
1379 int
validate_name(const char * s)1380 validate_name(const char *s)
1381 {
1382 	unsigned char c;
1383 
1384 	if (*s == '\0')
1385 		return (-1);
1386 	do {
1387 		c = (unsigned char)*s;
1388 		if (!isascii(c) || !(isalnum(c) || ispunct(c)) ||
1389 		    c == '\"' || c == '/' || c == '\\')
1390 			return (-1);
1391 	} while (*++s != '\0');
1392 	return (0);
1393 }
1394 
1395 /*
1396  * A wrapper for validate_name() with logging during configuration parsing.
1397  */
1398 int
conf_validate_name(const char * s)1399 conf_validate_name(const char *s)
1400 {
1401 	if (validate_name(s) < 0) {
1402 		logconfx("illegal symbol in name");
1403 		return (-1);
1404 	}
1405 	return (0);
1406 }
1407 
1408 /*
1409  * Get module's name from the file name "[/path/]foobar[-x.y.z][.so]".
1410  * Return "foobar" part from the given path name.
1411  */
1412 char *
get_mod_name(char * file_name)1413 get_mod_name(char *file_name)
1414 {
1415 	char *ptr, *ptr2, *name;
1416 
1417 	ptr = strrchr(file_name, '/');
1418 	if (ptr != NULL)
1419 		++ptr;			/* foobar[-x.y.z].[so] */
1420 	else
1421 		ptr = file_name;
1422 	name = mem_strdup(ptr, m_anon);
1423 	if (name == NULL) {
1424 		logconfx("get_mod_name: mem_strdup failed");
1425 		return (NULL);
1426 	}
1427 	ptr2 = strchr(name, '.');
1428 	if (ptr2 != NULL)
1429 		*ptr2 = '\0';		/* foobar[-x] */
1430 	ptr = strrchr(name, '-');
1431 	if (ptr != NULL) {
1432 		for (ptr2 = ptr; *ptr2 != '\0'; ++ptr2)
1433 			if (!isdigit((unsigned char)*ptr2))
1434 				break;
1435 		if (*ptr2 == '\0')
1436 			*ptr = '\0';	/* foobar */
1437 	}
1438 	return (name);
1439 }
1440 
1441 /* get_arg_tbl[] must be ordered by IPA_CONF_TYPE_xxx values. */
1442 const struct get_arg get_arg_tbl[] = {
1443 	{ &arg_int32,	get_arg_int32,	  NULL		}, /* TYPE_INT32    */
1444 	{ &arg_uint32,	get_arg_uint32,	  NULL		}, /* TYPE_UINT32   */
1445 	{ &arg_int64,	get_arg_int64,	  NULL		}, /* TYPE_INT64    */
1446 	{ arg_uint64,	get_arg_uint64,	  NULL		}, /* TYPE_UINT64   */
1447 	{ &arg_string,	get_arg_string,	  NULL		}, /* TYPE_STRING   */
1448 	{ arg_uint64,	get_arg_bytes,	  &re_bytes	}, /* TYPE_BYTES    */
1449 	{ arg_uint64,	get_arg_time,	  &re_time	}, /* TYPE_TIME     */
1450 	{ arg_uint64,	get_arg_value,	  NULL		}, /* TYPE_VALUE    */
1451 	{ &arg_int,	get_arg_per_cent, NULL		}, /* TYPE_PER_CENT */
1452 	{ &arg_int,	get_arg_boolean,  NULL		}, /* TYPE_BOOLEAN  */
1453 	{ &arg_misc,	get_arg_misc,	  NULL		}  /* TYPE_MISC     */
1454 };
1455