/*-
* 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 <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <regex.h>
#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 */
};
syntax highlighted by Code2HTML, v. 0.9.1