/*- * Copyright (c) 2003 Andrey Simonenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "config.h" #ifndef lint static const char rcsid[] ATTR_UNUSED = "@(#)$Id: confcommon.c,v 1.1.4.4 2007/05/11 16:29:59 simon Exp $"; #endif /* !lint */ #include #include #include #include #include #include #include #include #include #include #include #include "ipa_mod.h" #include "dlapi.h" #include "confcommon.h" #include "memfunc.h" #include "parser.h" /* * All strto_xxx() functions get strings with positive decimal * integers, so it is not necessary to check '-' in the first * character of a string inside strto_xxx() functions (remember, * that strtoul() and strtoull() work with negative values). */ extern void *m_anon; #define LOG_BUF_SIZE 1024 const char *const conf_event_msg[] = { "GLOBAL_BEGIN", /* 0 */ "GLOBAL_END", /* 1 */ "RULE_BEGIN", /* 2 */ "RULE_END", /* 3 */ "LIMIT_BEGIN", /* 4 */ "LIMIT_END", /* 5 */ "THRESHOLD_BEGIN", /* 6 */ "THRESHOLD_END", /* 7 */ "AUTORULE_BEGIN", /* 8 */ "AUTORULE_END", /* 9 */ "RULEPAT_BEGIN", /* 10 */ "RULEPAT_END", /* 11 */ "CUSTOM_SECT_BEGIN", /* 12 */ "CUSTOM_SECT_END" /* 13 */ }; const char *curmodfile; /* File name of the current module (in sense of configuration prefix). */ const char *curparam; /* Current parameter name. */ const char *cursect; /* Current section name. */ /* For reporting errors from regcomp(3) function. */ int re_errcode; char re_errbuf[RE_ERRBUF_SIZ]; int need_nl = 0; /* Need new line. */ int was_space = 0; /* There was already space, flag for print_space(). */ int conf_indent; /* Current output indent. */ int mod_indent; /* Additional module's output indent. */ void *conf_sect_hentry_mzone; void *conf_param_hentry_mzone; regex_t reg_time; regex_t reg_bytes; static char *arg_string; static char *arg_misc; static int arg_int; static int32_t arg_int32; static uint32_t arg_uint32; static int64_t arg_int64; static uint64_t arg_uint64[2]; extern void conferrx(const char *, ...) ATTR_FORMAT(printf, 1, 2); /* * Form an error message in re_errbuf according to * re_errcode with the help from the regerror() function. */ void re_form_errbuf(void) { regerror(re_errcode, (regex_t *)NULL, re_errbuf, sizeof re_errbuf); } /* * This function is called by modules to output their log messages * about errors occurred during parsing configuration file. If code * isn't zero, then output strerror(code) as well. */ void mod_logconferr(const char *mod_name, int code, const char *format, va_list ap) { int rv, allocated = 0; char buf[LOG_BUF_SIZE]; char *ptr; const char *what, *token, *tail; const char *in_var_msg; if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0) ptr = "(mod_logconferr: vsnprintf error)"; else if (rv >= sizeof buf) { if ( (ptr = mem_malloc(++rv, m_anon)) == NULL) ptr = "(mod_logconferr: mem_malloc failed)"; else { if (vsnprintf(ptr, rv, format, ap) < 0) { mem_free(ptr, m_anon); ptr = "(mod_logconferr: vsnprintf error again)"; } else allocated = 1; } } else ptr = buf; if (parser_curpbuf != NULL) { if (curparam != NULL) { what = " parameter \""; token = curparam; tail = "\":"; } else if (cursect != NULL) { what = " section \""; token = cursect; tail = "\":"; } else what = token = tail = "\0"; in_var_msg = parser_curpbuf->fp != NULL ? "" : "(in variable's value)"; if (code == 0) conferrx("%s:%u%s: MOD %s:%s%s%s %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, mod_name, what, token, tail, ptr); else conferrx("%s:%u%s: MOD %s:%s%s%s %s: %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, mod_name, what, token, tail, ptr, strerror(code)); } else { if (code == 0) conferrx("MOD %s: %s", mod_name, ptr); else conferrx("MOD %s: %s: %s", mod_name, ptr, strerror(code)); } if (allocated) mem_free(ptr, m_anon); } /* * Output an error message for some line in the configuration file. * If code isn't zero, then output strerror(code) as well. * This function is called from places where parser_curpbuf is defined. */ static void vlogconferr(int code, const char *format, va_list ap) { int rv, allocated = 0; char buf[LOG_BUF_SIZE]; char *ptr; const char *what, *token, *tail; const char *in_var_msg = parser_curpbuf->fp != NULL ? "" : "(in variable's value)"; if (curparam != NULL) { token = curparam; what = " parameter \""; tail = "\":"; } else if (cursect != NULL) { token = cursect; what = " section \""; tail = "\":"; } else what = token = tail = "\0"; if ( (rv = vsnprintf(buf, sizeof buf, format, ap)) < 0) ptr = "(vlogconferr: vsnprintf error)"; else if (rv >= sizeof buf) { if ( (ptr = mem_malloc(++rv, m_anon)) == NULL) ptr = "(vlogconferr: mem_malloc failed)"; else { if (vsnprintf(ptr, rv, format, ap) < 0) { mem_free(ptr, m_anon); ptr = "(vlogconferr: vsnprintf error again)"; } else allocated = 1; } } else ptr = buf; if (code == 0) { if (curmodfile == NULL) conferrx("%s:%u%s:%s%s%s %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, what, token, tail, ptr); else conferrx("%s:%u%s: module %s:%s%s%s %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, curmodfile, what, token, tail, ptr); } else { if (curmodfile == NULL) conferrx("%s:%u%s:%s%s%s %s: %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, what, token, tail, ptr, strerror(code)); else conferrx("%s:%u%s: module %s:%s%s%s %s: %s", parser_curpbuf->fname, parser_curpbuf->lineno, in_var_msg, curmodfile, what, token, tail, ptr, strerror(code)); } if (allocated) mem_free(ptr, m_anon); } /* * Log an error message during configuration, * use errno as error code. */ void logconferr(const char *format, ...) { int errno_save = errno; va_list va; va_start(va, format); vlogconferr(errno_save, format, va); va_end(va); } /* * Log an error message during configuration, * don't use errno. */ void logconferrx(const char *format, ...) { va_list va; va_start(va, format); vlogconferr(0, format, va); va_end(va); } /* * Convert string to uint32_t. * Assume that sizeof(u_long) >= sizeof(uint32_t). */ int strto_uint32(uint32_t *result, const char *nptr, char **endptr_ret) { char *endptr; u_long val_ul; errno = 0; val_ul = strtoul(nptr, &endptr, 10); if (errno != 0) { logconferr("strtoul"); return -1; } if (val_ul > UINT32_MAX) { logconferrx("too big value for uint32_t type"); return -1; } if (nptr == endptr) { logconferrx("wrong number"); return -1; } *result = (uint32_t)val_ul; if (endptr_ret != NULL) *endptr_ret = endptr; return 0; } /* * Convert string to uint64_t. * Assume that sizeof(unsigned long long) >= sizeof(uint64_t). */ static int strto_uint64(uint64_t *result, const char *nptr, char **endptr_ret) { char *endptr; unsigned long long val_ull; errno = 0; val_ull = strtoull(nptr, &endptr, 10); if (errno != 0) { logconferr("strtoull"); return -1; } if (val_ull > UINT64_MAX) { logconferrx("too big value for uint64_t type"); return -1; } if (nptr == endptr) { logconferrx("wrong number"); return -1; } *result = (uint64_t)val_ull; if (endptr_ret != NULL) *endptr_ret = endptr; return 0; } /* * Convert string to u_int. * Assumed that sizeof(u_long) >= sizeof(u_int). */ int strto_u_int(u_int *result, const char *nptr, char **endptr) { u_long val_ul; errno = 0; val_ul = strtoul(nptr, endptr, 10); if (errno != 0) { logconferr("strtoul"); return -1; } if (val_ul > UINT_MAX) { logconferrx("too big value for u_int type"); return -1; } *result = (u_int)val_ul; return 0; } static int check_unsigned_integer(const char *ptr, const char *what) { for (; *ptr != '\0'; ++ptr) if (isdigit(*ptr) == 0) { logconferrx("argument should be %s integer", what); return -1; } return 0; } /* * Convert string to uint32_t, allow user to specify * a `+' sign before number. */ static int get_arg_uint32(void *res) { char *ptr = parser_args; if (*ptr == '+') ++ptr; if (check_unsigned_integer(ptr, "a positive") < 0) return -1; return strto_uint32((uint32_t *)res, ptr, (char **)NULL); } /* * Convert string to int32_t, allow user to specify * `-' and `+' signs before number. */ static int get_arg_int32(void *res) { char *ptr = parser_args; int sign; uint32_t val; switch (*ptr) { case '-': sign = -1; ++ptr; break; case '+': ++ptr; /* FALLTHROUGH */ default: sign = 1; } if (check_unsigned_integer(ptr, "an") < 0) return -1; if (strto_uint32(&val, ptr, (char **)NULL) < 0) return -1; if (sign < 0) { /* This can be wrong on systems where |INT32_MIN| > INT32_MAX */ if (val > INT32_MAX) { logconferrx("too little value for int32_t type"); return -1; } *(int32_t *)res = -(int32_t)val; } else { if (val > INT32_MAX) { logconferrx("too big value for int32_t type"); return -1; } *(int32_t *)res = (int32_t)val; } return 0; } /* * Convert string to int64_t, allow user to specify * `-' and `+' signs before number. */ static int get_arg_int64(void *res) { char *ptr = parser_args; int sign; uint64_t val; switch (*ptr) { case '-': sign = -1; ++ptr; break; case '+': ++ptr; /* FALLTHROUGH */ default: sign = 1; } if (check_unsigned_integer(ptr, "an") < 0) return -1; if (strto_uint64(&val, ptr, (char **)NULL) < 0) return -1; if (sign < 0) { /* This can be wrong on systems where |INT64_MIN| > INT64_MAX */ if (val > INT64_MAX) { logconferrx("too little value for int64_t type"); return -1; } *(int64_t *)res = -(int64_t)val; } else { if (val > INT64_MAX) { logconferrx("too big value for int64_t type"); return -1; } *(int64_t *)res = (int64_t)val; } return 0; } /* * Convert string to uint64_t, allow user to specify * a `+' sign before number. */ int get_arg_uint64(void *res) { char *ptr = parser_args; if (*ptr == '+') ++ptr; if (check_unsigned_integer(ptr, "a positive") < 0) return -1; return strto_uint64((uint64_t *)res, ptr, (char **)NULL); } int get_arg_time(void *res) { char *ptr = parser_args, *endptr; int level = 0, error = 0, overflow = 0; uint64_t value, result = UINT64_C(0); for (;;) { if (strto_uint64(&value, ptr, &endptr) < 0) return -1; ptr = endptr; switch (*ptr) { case 'h': if (level > 0) error = 1; else { if (value > UINT64_MAX / SECONDS_IN_HOUR) overflow = 1; else { level = 1; value *= SECONDS_IN_HOUR; } } break; case 'm': if (level > 1) error = 1; else { if (value > UINT64_MAX / SECONDS_IN_MINUTE) overflow = 1; else { level = 2; value *= SECONDS_IN_MINUTE; } } break; default: /* 's' */ if (level > 2) error = 1; else level = 3; } if (error) { logconferrx("wrong time format"); return -1; } if (overflow || result > UINT64_MAX - value) { logconferrx("too big value for uint64_t type"); return -1; } result += value; if (*++ptr == '\0') break; /* EOL */ if (*ptr == ' ') ++ptr; } *(uint64_t *)res = result; return 0; } /* * Parse a value for the boolean variable, * all comparisons are case insensitive. */ static int get_arg_boolean(void *res) { if (strcasecmp(parser_args, "yes") == 0) *(int *)res = 1; else if (strcasecmp(parser_args, "no") == 0) *(int *)res = 0; else { logconferrx("argument should be \"yes\" or \"no\""); return -1; } return 0; } static int get_arg_string(void *res) { char *ptr; if (!parser_arg_is_string()) { logconferrx("argument should be a string"); return -1; } if ( (ptr = parser_string_strdup(parser_args, m_parser)) == NULL) return -1; *(char **)res = ptr; return 0; } int get_arg_bytes(void *res) { char *ptr = parser_args, *endptr; int level = 0, error = 0, overflow = 0; uint64_t value, result = UINT64_C(0); for (;;) { if (strto_uint64(&value, ptr, &endptr) < 0) return -1; ptr = endptr; switch (*ptr) { case 'T': if (level > 0) error = 1; else { if (value > UINT64_MAX / TBYTE) overflow = 1; else { level = 1; value *= TBYTE; } } break; case 'G': if (level > 1) error = 1; else { if (value > UINT64_MAX / GBYTE) overflow = 1; else { level = 2; value *= GBYTE; } } break; case 'M': if (level > 2) error = 1; else { if (value > UINT64_MAX / MBYTE) overflow = 1; else { level = 3; value *= MBYTE; } } break; case 'K': if (level > 3) error = 1; else { if (value > UINT64_MAX / KBYTE) overflow = 1; else { level = 4; value *= KBYTE; } } break; default: /* 'B' */ if (level > 4) error = 1; else level = 5; } if (error) { logconferrx("wrong format of an argument"); return -1; } if (overflow || result > UINT64_MAX - value) { logconferrx("too big value for uint64_t type"); return -1; } result += value; if (*++ptr == '\0') break; /* EOL */ if (*ptr == ' ') ++ptr; } *(uint64_t *)res = result; return 0; } static int get_arg_value(void *res) { char *ptr = parser_args; uint64_t type, val; if (regexec(®_bytes, ptr, 0, (regmatch_t *)NULL, 0) == 0) { if (get_arg_bytes(&val) < 0) return -1; type = IPA_CONF_TYPE_BYTES; } else if (regexec(®_time, ptr, 0, (regmatch_t *)NULL, 0) == 0) { if (get_arg_time(&val) < 0) return -1; type = IPA_CONF_TYPE_TIME; } else { if (get_arg_uint64(&val) < 0) return -1; type = IPA_CONF_TYPE_UINT64; } *(uint64_t *)res = type; *((uint64_t *)res + 1) = val; return 0; } int get_arg_per_cent(void *res) { char *ptr; uint32_t per_cent; ptr = parser_args + parser_args_len - 1; if (*ptr != '%') { logconferrx("wrong format of an argument"); return -1; } *ptr = '\0'; if (get_arg_uint32(&per_cent) < 0) return -1; if (per_cent > 100) { logconferrx("per cent value should be <= 100%%"); return -1; } *(int *)res = per_cent; return 0; } /* * Simply point *res to read string from configuration file. */ static int get_arg_misc(void *res) { *(char **)res = parser_args; return 0; } static u_int get_conf_hash_value(const char *name) { u_int hash_value; for (hash_value = 0; *name != '\0'; ++name) hash_value += *name; return hash_value; } #define get_conf_sect_hash_value(x) get_conf_hash_value(x) #define get_conf_param_hash_value(x) get_conf_hash_value(x) #define get_conf_sect_bucket(x) ((x) & (CONF_SECT_BUCKETS - 1)) #define get_conf_param_bucket(x) ((x) & (CONF_PARAM_BUCKETS - 1)) /* * Compile regular expressions and init hash tables for * configuration sections and parameters. */ int init_conf_tbls(const char *mod_file, int build_regexp, ipa_conf_sect *sect_tbl, struct conf_sect_hash **sect_hash_ret, ipa_conf_param *param_tbl, struct conf_param_hash **param_hash_ret) { u_int i; const ipa_conf_sect *sect; const ipa_conf_param *param; struct conf_sect_hash *sect_hash; struct conf_param_hash *param_hash; struct conf_sect_hentry *sect_hentry; struct conf_param_hentry *param_hentry; /* Allocate hash buckets. */ if ( (sect_hash = mem_malloc(CONF_SECT_BUCKETS * sizeof *sect_hash, m_anon)) == NULL) { conferrx("init_conf_tbls: mem_malloc failed"); return -1; } /* Init head of each hash bucket. */ for (i = 0; i < CONF_SECT_BUCKETS; ++i) SLIST_INIT(§_hash[i]); *sect_hash_ret = sect_hash; /* Build regular expressions and hash entries. */ for (sect = sect_tbl, i = 0; sect->sect_name != NULL; ++i, ++sect) { if (sect->arg_pattern != NULL && build_regexp) if ( (re_errcode = regcomp(sect->arg_regexp, sect->arg_pattern, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); if (mod_file == NULL) conferrx("init_conf_tbls: regcomp(\"%s\"): %s", sect->arg_pattern, re_errbuf); else conferrx("init_conf_tbls for module %s: regcomp(\"%s\"): %s", mod_file, sect->arg_pattern, re_errbuf); return -1; } if ( (sect_hentry = mzone_alloc(conf_sect_hentry_mzone)) == NULL) { conferrx("init_conf_tbls: mzone_malloc failed"); return -1; } sect_hentry->sect_name = sect->sect_name; sect_hentry->idx = i; sect_hentry->hash_value = get_conf_sect_hash_value(sect_hentry->sect_name); SLIST_INSERT_HEAD(§_hash[get_conf_sect_bucket(sect_hentry->hash_value)], sect_hentry, link); } /* Allocate hash buckets. */ if ( (param_hash = mem_malloc(CONF_PARAM_BUCKETS * sizeof *param_hash, m_anon)) == NULL) { conferrx("init_conf_tbls: mem_malloc failed"); return -1; } /* Init head of each hash bucket. */ for (i = 0; i < CONF_PARAM_BUCKETS; ++i) SLIST_INIT(¶m_hash[i]); *param_hash_ret = param_hash; /* Build regular expressions and hash entries. */ for (param = param_tbl, i = 0; param->param_name != NULL; ++i, ++param) { if (param->arg_pattern != NULL && build_regexp) if ( (re_errcode = regcomp(param->arg_regexp, param->arg_pattern, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); if (mod_file == NULL) conferrx("init_conf_tbls: regcomp(\"%s\"): %s", param->arg_pattern, re_errbuf); else conferrx("init_conf_tbls for module %s: regcomp(\"%s\"): %s", mod_file, param->arg_pattern, re_errbuf); return -1; } if ( (param_hentry = mzone_alloc(conf_param_hentry_mzone)) == NULL) { conferrx("init_conf_tbls: mzone_malloc failed"); return -1; } param_hentry->param_name = param->param_name; param_hentry->idx = i; param_hentry->hash_value = get_conf_param_hash_value(param_hentry->param_name); SLIST_INSERT_HEAD(¶m_hash[get_conf_param_bucket(param_hentry->hash_value)], param_hentry, link); } return 0; } const ipa_conf_sect * find_conf_sect(const ipa_conf_sect *conf_sect_tbl, const struct conf_sect_hash *sect_hash, const char *sect_name) { u_int hash_value; const struct conf_sect_hash *hash_bucket; const struct conf_sect_hentry *conf_sect_hentry; hash_value = get_conf_sect_hash_value(sect_name); hash_bucket = §_hash[get_conf_sect_bucket(hash_value)]; SLIST_FOREACH(conf_sect_hentry, hash_bucket, link) if (conf_sect_hentry->hash_value == hash_value && strcmp(conf_sect_hentry->sect_name, sect_name) == 0) return &conf_sect_tbl[conf_sect_hentry->idx]; return NULL; } const ipa_conf_param * find_conf_param(const ipa_conf_param *conf_param_tbl, const struct conf_param_hash *param_hash, const char *param_name) { u_int hash_value; const struct conf_param_hash *hash_bucket; const struct conf_param_hentry *conf_param_hentry; hash_value = get_conf_param_hash_value(param_name); hash_bucket = ¶m_hash[get_conf_param_bucket(hash_value)]; SLIST_FOREACH(conf_param_hentry, hash_bucket, link) if (conf_param_hentry->hash_value == hash_value && strcmp(conf_param_hentry->param_name, param_name) == 0) return &conf_param_tbl[conf_param_hentry->idx]; return NULL; } /* * Release memory hold by compiled regular expressions, * for configuration sections and parameters, hash entries * are deleted all at once with one mzone_deinit() call. */ void deinit_conf_tbls(int free_regexp, ipa_conf_sect *sect_tbl, struct conf_sect_hash *sect_hash, ipa_conf_param *param_tbl, struct conf_param_hash *param_hash) { const ipa_conf_sect *sect; const ipa_conf_param *param; if (free_regexp) { /* Release memory hold by regular expressions. */ for (sect = sect_tbl; sect->sect_name != NULL; ++sect) if (sect->arg_pattern != NULL) regfree(sect->arg_regexp); for (param = param_tbl; param->param_name != NULL; ++param) if (param->arg_pattern != NULL) regfree(param->arg_regexp); } /* Release memory hold by hash buckets. */ mem_free(sect_hash, m_anon); mem_free(param_hash, m_anon); } /* * All print_* functions are trivial, just take care about * need_nl and was_space variables. */ struct time_conv { uint64_t div; char ch; }; static const struct time_conv time_conv_tbl[] = { { SECONDS_IN_HOUR, 'h' }, { SECONDS_IN_MINUTE, 'm' }, { 1, 's' }, { 0, '\0' } }; void print_time(const uint64_t *ptr) { int need_space = 0; uint64_t t, a = *ptr; const struct time_conv *time_conv; was_space = 0; if (a == UINT64_C(0)) { printf("0s"); return; } for (time_conv = time_conv_tbl; time_conv->div > UINT64_C(0); ++time_conv) { t = a / time_conv->div; if (t != UINT64_C(0)) { if (need_space) printf(" "); printf("%"PRIu64"%c", t, time_conv->ch); need_space = 1; a -= t * time_conv->div; } } } struct byte_conv { uint64_t div; char ch; }; static const struct byte_conv byte_conv_tbl[] = { { TBYTE, 'T' }, { GBYTE, 'G' }, { MBYTE, 'M' }, { KBYTE, 'K' }, { 1, 'B' }, { 0, '\0' } }; void print_bytes(const uint64_t *bytes) { int need_space = 0; uint64_t t, a = *bytes; const struct byte_conv *byte_conv; was_space = 0; if (a == UINT64_C(0)) { printf("0B"); return; } for (byte_conv = byte_conv_tbl; byte_conv->div > UINT64_C(0); ++byte_conv) { t = a / byte_conv->div; if (t != UINT64_C(0)) { if (need_space) printf(" "); printf("%"PRIu64"%c", t, byte_conv->ch); need_space = 1; a -= t * byte_conv->div; } } } void print_value(const uint64_t *value, u_int value_type) { switch (value_type) { case IPA_CONF_TYPE_BYTES: print_bytes(value); break; case IPA_CONF_TYPE_TIME: print_time(value); break; default: /* IPA_CONF_TYPE_UINT64 */ printf("%"PRIu64, *value); } } void print_boolean(int boolean) { was_space = 0; if (boolean) printf("yes"); else printf("no"); } void print_string(const char *string) { const char *ptr; was_space = 0; printf("\""); for (ptr = string; *ptr != '\0'; ++ptr) switch (*ptr) { case '\t': printf("\\t"); break; case '\n': printf("\\n"); break; case '\"': case '\\': printf("\\"); /* FALLTHROUGH */ default: printf("%c", *ptr); } printf("\""); } void print_param_name(const char *name, const char *param) { printf("%*s%*s", conf_indent, "", mod_indent, ""); if (name == NULL) printf("%s = ", param); else printf("%s:%s = ", name, param); was_space = 1; } void print_args(const char *format, va_list ap) { was_space = 0; vprintf(format, ap); } /* * Print the end of a configuration string. */ void print_eol(void) { printf(";\n"); } /* * Print new line if needed. */ void print_nl(void) { if (need_nl) { printf("\n"); need_nl = 0; } } void print_param_end(void) { print_eol(); need_nl = 1; } void print_sect_name(const char *name, const char *sect) { printf("%*s%*s", conf_indent, "", mod_indent, ""); if (name == NULL) printf("%s ", sect); else printf("%s:%s ", name, sect); was_space = 1; } void print_sect_begin(void) { printf("{\n"); } void print_sect_end(void) { printf("%*s%*s}\n", conf_indent, "", mod_indent, ""); need_nl = 1; } void set_indent(int indent) { mod_indent = indent * CONF_INDENT; } void print_space(void) { if (!was_space) { printf(" "); was_space = 1; } } struct conf_regexp { const char *pat; regex_t *reg; }; static struct conf_regexp conf_regexp_tbl[] = { { PAT_BYTES, ®_bytes }, { PAT_TIME, ®_time }, { NULL, NULL } }; /* * Build regular expressions for parsing some IPA_CONF_TYPE_xxx. */ int build_conf_regexp(void) { static int called = 0; struct conf_regexp *conf_regexp; if (called) return 0; for (conf_regexp = conf_regexp_tbl; conf_regexp->pat != NULL; ++conf_regexp) if ( (re_errcode = regcomp(conf_regexp->reg, conf_regexp->pat, REG_EXTENDED|REG_NOSUB)) != 0) { re_form_errbuf(); conferrx("build_conf_regexp: regcomp(\"%s\"): %s", conf_regexp->pat, re_errbuf); return -1; } called = 1; return 0; } void * dl_lookup_sym(dl_handle handle, const char *sym) { void *ptr; #ifdef SYM_PREFIX char *psym; #endif #ifdef SYM_PREFIX if (mem_asprintf(m_anon, &psym, SYM_PREFIX"%s", sym) < 0) { logconferrx("dl_lookup_sym: mem_asprintf failed"); return NULL; } ptr = dl_sym(handle, psym); mem_free(psym, m_anon); if (ptr != NULL) return NULL; #endif if ( (ptr = dl_sym(handle, sym)) == NULL) { logconferrx("dl_lookup_sym: dl_sym(\"%s\"): %s", sym, dl_error()); return NULL; } return ptr; } /* get_arg_tbl[] must be ordered by IPA_CONF_TYPE_xxx values. */ const struct get_arg_tbl get_arg_tbl[] = { { &arg_int32, get_arg_int32, NULL }, /* IPA_CONF_TYPE_INT32 */ { &arg_uint32, get_arg_uint32, NULL }, /* IPA_CONF_TYPE_UINT32 */ { &arg_int64, get_arg_int64, NULL, }, /* IPA_CONF_TYPE_INT64 */ { arg_uint64, get_arg_uint64, NULL }, /* IPA_CONF_TYPE_UINT64 */ { &arg_string, get_arg_string, NULL }, /* IPA_CONF_TYPE_STRING */ { arg_uint64, get_arg_bytes, ®_bytes }, /* IPA_CONF_TYPE_BYTES */ { arg_uint64, get_arg_time, ®_time }, /* IPA_CONF_TYPE_TIME */ { arg_uint64, get_arg_value, NULL }, /* IPA_CONF_TYPE_VALUE */ { &arg_int, get_arg_per_cent, NULL }, /* IPA_CONF_TYPE_PER_CENT */ { &arg_int, get_arg_boolean, NULL }, /* IPA_CONF_TYPE_BOOLEAN */ { &arg_misc, get_arg_misc, NULL } /* IPA_CONF_TYPE_MISC */ };