config: introduce a new API for reading configuration settings.

This patch introduces generic code for adding and parsing new options.
The public 'get' methods return option values directly.  Although the
API is easy to use, it does not provide error checking in case the
option does not exist or if there is a type mismatch.

Therefore the code performs a BIST to ensure that the options are
properly populated.  In addition, the code terminates the program in
case of missing options or type mismatches.  This heavy handed
approach is meant to catch errors during development and should never
trigger during normal usage.

As a first element, we include an option for specifying the UDP TTL.

Users are required to call 'config_init', and so this patch add that into
all three programs, ptp4l, phc2sys and pmc.

Signed-off-by: Richard Cochran <richardcochran@gmail.com>
master
Richard Cochran 2015-08-12 18:34:04 +02:00
parent 8f5344eed9
commit e7dbc068f6
6 changed files with 272 additions and 14 deletions

241
config.c
View File

@ -34,6 +34,116 @@ enum config_section {
UNKNOWN_SECTION,
};
enum config_type {
CFG_TYPE_INT,
CFG_TYPE_DOUBLE,
};
typedef union {
int i;
double d;
} any_t;
#define CONFIG_LABEL_SIZE 32
#define CFG_ITEM_STATIC (1 << 0) /* statically allocated, not to be freed */
#define CFG_ITEM_LOCKED (1 << 1) /* command line value, may not be changed */
#define CFG_ITEM_PORT (1 << 2) /* item may appear in port sections */
struct config_item {
char label[CONFIG_LABEL_SIZE];
enum config_type type;
unsigned int flags;
any_t val;
any_t min;
any_t max;
};
#define N_CONFIG_ITEMS (sizeof(config_tab) / sizeof(config_tab[0]))
#define CONFIG_ITEM_INT(_label, _port, _default, _min, _max) { \
.label = _label, \
.type = CFG_TYPE_INT, \
.flags = _port ? CFG_ITEM_PORT : 0, \
.val.i = _default, \
.min.i = _min, \
.max.i = _max, \
}
#define GLOB_ITEM_INT(label, _default, min, max) \
CONFIG_ITEM_INT(label, 0, _default, min, max)
#define PORT_ITEM_INT(label, _default, min, max) \
CONFIG_ITEM_INT(label, 1, _default, min, max)
struct config_item config_tab[] = {
PORT_ITEM_INT("udp_ttl", 1, 1, 255),
};
static struct config_item *config_section_item(struct config *cfg,
const char *section,
const char *name)
{
char buf[CONFIG_LABEL_SIZE + MAX_IFNAME_SIZE];
snprintf(buf, sizeof(buf), "%s.%s", section, name);
return hash_lookup(cfg->htab, buf);
}
static struct config_item *config_global_item(struct config *cfg,
const char *name)
{
return config_section_item(cfg, "global", name);
}
static struct config_item *config_find_item(struct config *cfg,
const char *section,
const char *name)
{
struct config_item *ci;
if (section) {
ci = config_section_item(cfg, section, name);
if (ci) {
return ci;
}
}
return config_global_item(cfg, name);
}
static struct config_item *config_item_alloc(struct config *cfg,
const char *section,
const char *name,
enum config_type type)
{
struct config_item *ci;
char buf[CONFIG_LABEL_SIZE + MAX_IFNAME_SIZE];
ci = calloc(1, sizeof(*ci));
if (!ci) {
fprintf(stderr, "low memory\n");
return NULL;
}
strncpy(ci->label, name, CONFIG_LABEL_SIZE - 1);
ci->type = type;
snprintf(buf, sizeof(buf), "%s.%s", section, ci->label);
if (hash_insert(cfg->htab, buf, ci)) {
fprintf(stderr, "low memory or duplicate item %s\n", name);
free(ci);
return NULL;
}
return ci;
}
static void config_item_free(void *ptr)
{
struct config_item *ci = ptr;
if (ci->flags & CFG_ITEM_STATIC)
return;
free(ci);
}
static enum parser_result parse_section_line(char *s, enum config_section *section)
{
if (!strcasecmp(s, "[global]")) {
@ -52,6 +162,65 @@ static enum parser_result parse_section_line(char *s, enum config_section *secti
return PARSED_OK;
}
static enum parser_result parse_item(struct config *cfg,
const char *section,
const char *option,
const char *value)
{
struct config_item *cgi, *dst;
enum parser_result r = BAD_VALUE;
double df;
int val;
/* If there is no default value, then the option is bogus. */
cgi = config_global_item(cfg, option);
if (!cgi) {
return NOT_PARSED;
}
switch (cgi->type) {
case CFG_TYPE_INT:
r = get_ranged_int(value, &val, cgi->min.i, cgi->max.i);
break;
case CFG_TYPE_DOUBLE:
r = get_ranged_double(value, &df, cgi->min.d, cgi->max.d);
break;
}
if (r != PARSED_OK) {
return r;
}
if (section) {
if (!(cgi->flags & CFG_ITEM_PORT)) {
return NOT_PARSED;
}
/* Create or update this port specific item. */
dst = config_section_item(cfg, section, option);
if (!dst) {
dst = config_item_alloc(cfg, section, option, cgi->type);
if (!dst) {
return NOT_PARSED;
}
}
} else if (cgi->flags & CFG_ITEM_LOCKED) {
/* This global option was set on the command line. */
return PARSED_OK;
} else {
/* Update the global default value. */
dst = cgi;
}
switch (dst->type) {
case CFG_TYPE_INT:
dst->val.i = val;
break;
case CFG_TYPE_DOUBLE:
dst->val.d = df;
break;
}
return PARSED_OK;
}
static enum parser_result parse_pod_setting(const char *option,
const char *value,
struct port_defaults *pod)
@ -173,7 +342,8 @@ static enum parser_result parse_pod_setting(const char *option,
return PARSED_OK;
}
static enum parser_result parse_port_setting(const char *option,
static enum parser_result parse_port_setting(struct config *cfg,
const char *option,
const char *value,
struct interface *iface)
{
@ -237,7 +407,7 @@ static enum parser_result parse_port_setting(const char *option,
iface->boundary_clock_jbod = val;
} else
return NOT_PARSED;
return parse_item(cfg, iface->name, option, value);
return PARSED_OK;
}
@ -614,7 +784,7 @@ static enum parser_result parse_global_setting(const char *option,
cfg->dds.boundary_clock_jbod = val;
} else
return NOT_PARSED;
return parse_item(cfg, NULL, option, value);
return PARSED_OK;
}
@ -727,7 +897,7 @@ int config_read(char *name, struct config *cfg)
if (current_section == GLOBAL_SECTION)
parser_res = parse_global_setting(option, value, cfg);
else
parser_res = parse_port_setting(option, value, current_port);
parser_res = parse_port_setting(cfg, option, value, current_port);
switch (parser_res) {
case PARSED_OK:
@ -806,11 +976,40 @@ void config_init_interface(struct interface *iface, struct config *cfg)
int config_init(struct config *cfg)
{
char buf[CONFIG_LABEL_SIZE + 8];
struct config_item *ci;
int i;
cfg->htab = hash_create();
if (!cfg->htab) {
return -1;
}
/* Populate the hash table with global defaults. */
for (i = 0; i < N_CONFIG_ITEMS; i++) {
ci = &config_tab[i];
ci->flags |= CFG_ITEM_STATIC;
snprintf(buf, sizeof(buf), "global.%s", ci->label);
if (hash_insert(cfg->htab, buf, ci)) {
fprintf(stderr, "duplicate item %s\n", ci->label);
goto fail;
}
}
/* Perform a Built In Self Test.*/
for (i = 0; i < N_CONFIG_ITEMS; i++) {
ci = &config_tab[i];
ci = config_global_item(cfg, ci->label);
if (ci != &config_tab[i]) {
fprintf(stderr, "config BIST failed at %s\n",
config_tab[i].label);
goto fail;
}
}
return 0;
fail:
hash_destroy(cfg->htab, NULL);
return -1;
}
void config_destroy(struct config *cfg)
@ -821,5 +1020,37 @@ void config_destroy(struct config *cfg)
STAILQ_REMOVE_HEAD(&cfg->interfaces, list);
free(iface);
}
hash_destroy(cfg->htab, free);
hash_destroy(cfg->htab, config_item_free);
}
double config_get_double(struct config *cfg, const char *section,
const char *option)
{
struct config_item *ci = config_find_item(cfg, section, option);
if (!ci || ci->type != CFG_TYPE_DOUBLE) {
pr_err("bug: config option %s missing or invalid!", option);
exit(-1);
}
pr_debug("config item %s.%s is %f", section, option, ci->val.d);
return ci->val.d;
}
int config_get_int(struct config *cfg, const char *section, const char *option)
{
struct config_item *ci = config_find_item(cfg, section, option);
if (!ci) {
pr_err("bug: config option %s missing!", option);
exit(-1);
}
switch (ci->type) {
case CFG_TYPE_DOUBLE:
pr_err("bug: config option %s type mismatch!", option);
exit(-1);
case CFG_TYPE_INT:
break;
}
pr_debug("config item %s.%s is %d", section, option, ci->val.i);
return ci->val.i;
}

View File

@ -105,4 +105,14 @@ struct interface *config_create_interface(char *name, struct config *cfg);
void config_init_interface(struct interface *iface, struct config *cfg);
void config_destroy(struct config *cfg);
/* New, hash table based methods: */
int config_init(struct config *cfg);
double config_get_double(struct config *cfg, const char *section,
const char *option);
int config_get_int(struct config *cfg, const char *section,
const char *option);
#endif

View File

@ -46,13 +46,13 @@ all: $(PRG)
ptp4l: $(OBJ)
pmc: msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o transport.o udp.o \
udp6.o uds.o util.o version.o
phc2sys: clockadj.o clockcheck.o linreg.o msg.o ntpshm.o nullf.o phc.o \
phc2sys.o pi.o pmc_common.o print.o raw.o servo.o sk.o stats.o sysoff.o tlv.o \
pmc: config.o hash.o msg.o pmc.o pmc_common.o print.o raw.o sk.o tlv.o \
transport.o udp.o udp6.o uds.o util.o version.o
phc2sys: clockadj.o clockcheck.o config.o hash.o linreg.o msg.o ntpshm.o \
nullf.o phc.o phc2sys.o pi.o pmc_common.o print.o raw.o servo.o sk.o stats.o \
sysoff.o tlv.o transport.o udp.o udp6.o uds.o util.o version.o
hwstamp_ctl: hwstamp_ctl.o version.o
phc_ctl: phc_ctl.o phc.o sk.o util.o clockadj.o sysoff.o print.o version.o

View File

@ -1236,6 +1236,10 @@ int main(int argc, char *argv[])
handle_term_signals();
if (config_init(&phc2sys_config)) {
return -1;
}
configured_pi_kp = KP;
configured_pi_ki = KI;

13
pmc.c
View File

@ -739,11 +739,16 @@ int main(int argc, char *argv[])
enum transport_type transport_type = TRANS_UDP_IPV4;
UInteger8 boundary_hops = 1, domain_number = 0, transport_specific = 0;
struct ptp_message *msg;
struct config *cfg = &pmc_config;
#define N_FD 2
struct pollfd pollfd[N_FD];
handle_term_signals();
if (config_init(&pmc_config)) {
return -1;
}
/* Process the command line arguments. */
progname = strrchr(argv[0], '/');
progname = progname ? 1+progname : argv[0];
@ -774,6 +779,7 @@ int main(int argc, char *argv[])
if (strlen(optarg) > MAX_IFNAME_SIZE) {
fprintf(stderr, "path %s too long, max is %d\n",
optarg, MAX_IFNAME_SIZE);
config_destroy(cfg);
return -1;
}
strncpy(uds_path, optarg, MAX_IFNAME_SIZE);
@ -784,18 +790,19 @@ int main(int argc, char *argv[])
break;
case 'v':
version_show(stdout);
config_destroy(cfg);
return 0;
case 'z':
zero_datalen = 1;
break;
case 'h':
usage(progname);
config_destroy(cfg);
return 0;
case '?':
usage(progname);
return -1;
default:
usage(progname);
config_destroy(cfg);
return -1;
}
}
@ -821,6 +828,7 @@ int main(int argc, char *argv[])
domain_number, transport_specific, zero_datalen);
if (!pmc) {
fprintf(stderr, "failed to create pmc\n");
config_destroy(cfg);
return -1;
}
@ -896,5 +904,6 @@ int main(int argc, char *argv[])
pmc_destroy(pmc);
msg_cleanup();
config_destroy(cfg);
return ret;
}

View File

@ -182,6 +182,10 @@ int main(int argc, char *argv[])
if (handle_term_signals())
return -1;
if (config_init(&cfg_settings)) {
return -1;
}
/* Set fault timeouts to a default value */
for (i = 0; i < FT_CNT; i++) {
cfg_settings.pod.flt_interval_pertype[i].type = FTMO_LOG2_SECONDS;