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