Commit d1f43bb1 authored by Alessandro Rubini's avatar Alessandro Rubini

config: new config parser, to simplify further additions

This is a rewrite of the configuration parser, more based on data
structures se we'll soon be able to read more than one configuration
file (to add special cases while developing) and protocol extensions
will be able to extend the table of allowed options.

Both the configuration file format and the layout of the code calling
lib/conf.c::pp_config_file() is unchanged in this commit. This means
that even if you enable config diagnostics on the command line it
won't have effect, because the config file is parsed before the
command line. This is fixed by a later commit.

The only visible change in this commit is that "port" is allowed as a
synonim for "link", to be deprecated soon (but it will not be removed
from the parsing of the config file). Thus, pp_instance->link_name is
renamed to port_name.

The compiled size of this commit is the same as the previous one.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent f44d4a0a
......@@ -14,6 +14,7 @@ static char *thing_name[] = {
[pp_dt_servo] = "diag-servo",
[pp_dt_bmc] = "diag-bmc",
[pp_dt_ext] = "diag-extension",
[pp_dt_config] = "diag-config",
};
......
......@@ -113,7 +113,7 @@ struct pp_net_path {
* (see lib/conf.c)
*/
struct pp_instance_cfg {
char link_name[16];
char port_name[16];
char iface_name[16];
int proto; /* 0: raw, 1: udp */
int role; /* 0: auto, 1: master, 2: slave */
......
/*
* Copyright (C) 2013 CERN (www.cern.ch)
* Author: Aurelio Colosimo
* Authors: Aurelio Colosimo, Alessandro Rubini
*
* Released according to the GNU LGPL, version 2.1 or any later version.
*/
#include <ppsi/ppsi.h>
/* This file is build in hosted environments, so following headers are Ok */
/* This file is built in hosted environments, so following headers are Ok */
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
/* Each pre-defined keyword is handled in the keywords list, so that the
* same code is used to parse every token in the conf file */
/* config lines are global or ppi-specific. Keep track of globals and ppi */
static struct pp_globals *current_ppg;
static struct pp_instance *current_ppi;
struct keyword_t {
int id;
char *key;
int (*handle_key)(struct pp_globals *ppg, char *val);
};
enum {KW_INV = -1, KW_LINK, KW_IFACE, KW_PROTO, KW_RAW, KW_UDP, KW_ROLE,
KW_AUTO, KW_MASTER, KW_SLAVE, KW_EXT, KW_NONE, KW_WHITERAB,
KW_CLOCK_CLASS, KW_CLOCK_ACCURACY,
KW_NUMBER};
static int handle_link(struct pp_globals *ppg, char *val);
static int handle_iface(struct pp_globals *ppg, char *val);
static int handle_proto(struct pp_globals *ppg, char *val);
static int handle_role(struct pp_globals *ppg, char *val);
static int handle_ext(struct pp_globals *ppg, char *val);
static int handle_clock_class(struct pp_globals *ppg, char *val);
static int handle_clock_accuracy(struct pp_globals *ppg, char *val);
static struct keyword_t keywords[KW_NUMBER] = {
{KW_LINK, "link", handle_link},
{KW_IFACE, "iface", handle_iface},
{KW_PROTO, "proto", handle_proto}, {KW_RAW, "raw"}, {KW_UDP, "udp"},
{KW_ROLE, "role", handle_role}, {KW_AUTO, "auto"}, {KW_MASTER, "master"},
{KW_SLAVE, "slave"},
{KW_EXT, "extension", handle_ext}, {KW_NONE, "none"},
{KW_WHITERAB, "whiterabbit"},
{KW_CLOCK_CLASS, "clock-class", handle_clock_class},
{KW_CLOCK_ACCURACY, "clock-accuracy", handle_clock_accuracy},
};
static int detect_keyword(char *kw)
{
int i;
for (i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++)
if (!strcmp(keywords[i].key, kw))
return i;
return KW_INV;
}
/* Handlers for "key" keywords: return a bool value, 1 in case of success,
* 0 otherwise */
static int handle_link(struct pp_globals *ppg, char *val)
/* A "port" (or "link", for compatibility) line creates a ppi instance */
static int f_port(int lineno, int iarg, char *sarg)
{
/* ppg->nlinks is initialized to -1 and used as index for current link,
* so increase it before using it */
ppg->nlinks++;
if (ppg->nlinks >= ppg->max_links) {
pp_printf("ppsi: Too many links in config file\n");
exit(1);
if (current_ppg->nlinks >= current_ppg->max_links) {
pp_printf("config line %i: out of available ports\n",
lineno);
return -1;
}
/* FIXME: strncpy (it is missing in bare archs by now) */
strcpy(ppg->pp_instances[ppg->nlinks].cfg.link_name, val);
return 1;
}
current_ppi = current_ppg->pp_instances + current_ppg->nlinks;
static int handle_iface(struct pp_globals *ppg, char *val)
{
/* FIXME: strncpy (it is missing in bare archs by now) */
strcpy(ppg->pp_instances[ppg->nlinks].iface_name, val);
return 1;
strcpy(current_ppi->cfg.port_name, sarg);
current_ppg->nlinks++;
return 0;
}
static int handle_clock_class(struct pp_globals *ppg, char *val)
static int f_if(int lineno, int iarg, char *sarg)
{
GOPTS(ppg)->clock_quality.clockClass = atoi(val);
return 1;
if (!current_ppi) {
pp_printf("config line %i: no port for this config\n", lineno);
return -1;
}
strcpy(current_ppi->cfg.iface_name, sarg);
return 0;
}
static int handle_clock_accuracy(struct pp_globals *ppg, char *val)
/* The following ones are so similar. Bah... set a pointer somewhere? */
static int f_proto(int lineno, int iarg, char *sarg)
{
GOPTS(ppg)->clock_quality.clockAccuracy = atoi(val);
return 1;
if (!current_ppi) {
pp_printf("config line %i: no port for this config\n", lineno);
return -1;
}
current_ppi->cfg.proto = iarg;
return 0;
}
static int handle_proto(struct pp_globals *ppg, char *val)
static int f_role(int lineno, int iarg, char *sarg)
{
int v_id;
v_id = detect_keyword(val);
switch(v_id) {
case KW_RAW:
case KW_UDP:
ppg->pp_instances[ppg->nlinks].cfg.proto
= v_id - KW_RAW;
break;
default:
return 0;
if (!current_ppi) {
pp_printf("config line %i: no port for this config\n", lineno);
return -1;
}
return 1;
current_ppi->cfg.role = iarg;
return 0;
}
static int handle_role(struct pp_globals *ppg, char *val)
static int f_ext(int lineno, int iarg, char *sarg)
{
int v_id;
v_id = detect_keyword(val);
switch(v_id) {
case KW_AUTO:
case KW_MASTER:
case KW_SLAVE:
ppg->pp_instances[ppg->nlinks].cfg.role
= v_id - KW_AUTO;
break;
default:
return 0;
if (!current_ppi) {
pp_printf("config line %i: no port for this config\n", lineno);
return -1;
}
return 1;
current_ppi->cfg.ext = iarg;
return 0;
}
static int handle_ext(struct pp_globals *ppg, char *val)
/* The following two are identical as well. I really need a pointer... */
static int f_class(int lineno, int iarg, char *sarg)
{
int v_id;
v_id = detect_keyword(val);
switch(v_id) {
case KW_NONE:
case KW_WHITERAB:
ppg->pp_instances[ppg->nlinks].cfg.ext
= v_id - KW_NONE;
break;
default:
return 0;
if (current_ppi) {
pp_printf("config line %i: global config under \"port\"\n",
lineno);
return -1;
}
return 1;
GOPTS(current_ppg)->clock_quality.clockClass = iarg;
return 0;
}
/* Called with the pair key-val detected by pp_parse_conf state machine */
static int handle_key_val(struct pp_globals *ppg, char *key, char *val)
static int f_accuracy(int lineno, int iarg, char *sarg)
{
int k_id;
k_id = detect_keyword(key);
switch(k_id) {
case KW_LINK:
case KW_IFACE:
case KW_PROTO:
case KW_ROLE:
case KW_EXT:
case KW_CLOCK_CLASS:
case KW_CLOCK_ACCURACY:
return keywords[k_id].handle_key(ppg, val);
default:
return 0;
if (current_ppi) {
pp_printf("config line %i: global config under \"port\"\n",
lineno);
return -1;
}
GOPTS(current_ppg)->clock_quality.clockAccuracy = iarg;
return 0;
}
/* Parse configuration (e.g. coming from /etc/ppsi.conf file) */
static int pp_parse_conf(struct pp_globals *ppg, char *conf, int len)
{
int st = 0;
char c;
char key[16], val[16];
int toklen = 0;
int ln = 1; /* Line counter */
int newln;
int i;
/* ppg->nlinks is initialized to -1 and increased at first link found,
* so that we can use it as array index */
ppg->nlinks = -1;
typedef int (*cfg_handler)(int lineno, int iarg, char *sarg); /* /me lazy... */
/*
* The parser is using a table, built up by the following structures
*/
for (i = 0; i < len; i++) {
c = conf[i];
struct argname {
char *name;
int value;
};
enum argtype {
ARG_NONE,
ARG_INT,
ARG_STR,
ARG_NAMES,
};
struct argline {
cfg_handler f;
char *keyword; /* Each line starts with a keyword */
enum argtype t;
struct argname *args;
};
/* Increase line counter and check if latest line is invalid */
newln = 0;
if ((c == '\n') || (c == '\0')) {
ln++;
newln = 1;
if (st == 2)
goto ret_invalid_line;
}
/* These are the tables for the parser */
static struct argname arg_proto[] = {
{"raw", PPSI_PROTO_RAW},
{"udp", PPSI_PROTO_UDP},
{},
};
static struct argname arg_role[] = {
{"auto", PPSI_ROLE_AUTO},
{"master",PPSI_ROLE_MASTER},
{"slave", PPSI_ROLE_SLAVE},
{},
};
static struct argname arg_ext[] = {
{"none", PPSI_EXT_NONE},
{"whiterabbit", PPSI_EXT_WR},
{},
};
switch (st) {
case 0: /* initial status */
if ((c == ' ') || (c == '\t') || (c == '\r') || (newln))
continue;
if (c == '#')
st = 1;
else {
key[0] = c;
toklen = 1;
st = 2;
}
break;
static struct argline arglines[] = {
{ f_port, "port", ARG_STR},
{ f_port, "link", ARG_STR}, /* old name for "port" */
{ f_if, "iface", ARG_STR},
{ f_proto, "proto", ARG_NAMES, arg_proto},
{ f_role, "role", ARG_NAMES, arg_role},
{ f_ext, "extension", ARG_NAMES, arg_ext},
{ f_class, "clock-class", ARG_INT},
{ f_accuracy, "clock-accuracy", ARG_INT},
{}
};
case 1: /* ignoring commented line, those starting with '#' */
if (newln)
st = 0;
else
continue;
break;
/* local implementation of isblank() for bare-metal users */
static int blank(int c)
{
return c == ' ' || c == '\t' || c == '\n';
}
case 2: /* detecting key */
static char *first_word(char *line, char **rest)
{
char *ret;
int l = strlen(line) -1;
/* remove trailing blanks */
while (l >= 0 && blank(line[l]))
line [l--] = '\0';
/* skip leading blanks to find first word */
while (*line && blank(*line))
line++;
ret = line;
/* find next blank and thim there*/
while (*line && !blank(*line))
line++;
if (*line) {
*line = '\0';
line++;
}
*rest = line;
return ret;
}
if (toklen == sizeof(key))
goto ret_invalid_line;
static int pp_parse_line(char *line, int lineno)
{
struct argline *l;
struct argname *n;
char *word;
int i;
if ((c != ' ') && (c != '\t'))
key[toklen++] = c;
else {
st = 3;
key[toklen] = '\0';
toklen = 0;
}
pp_diag(NULL, config, 2, "parsing line %i: \"%s\"\n", lineno, line);
word = first_word(line, &line);
/* now line points to the next word, with no leading blanks */
if (word[0] == '#')
return 0;
if (!*word) {
/* empty or blank-only */
current_ppi = NULL;
return 0;
}
for (l = arglines; l->f; l++)
if (!strcmp(word, l->keyword))
break;
if (!l->f) {
pp_diag(NULL, config, 1, "line %i: no such keyword \"%s\"\n",
lineno, word);
return -1;
}
case 3: /* detecting val */
switch(l->t) {
if (toklen == sizeof(val))
goto ret_invalid_line;
case ARG_NONE:
break;
if ((c != ' ') && (c != '\t') && (c != '\r') && (!newln))
val[toklen++] = c;
else if (newln) {
val[toklen] = '\0';
if (!handle_key_val(ppg, key, val))
goto ret_invalid_line;
st = 0;
toklen = 0;
}
case ARG_INT:
if (sscanf(line, "%i", &i) != 1) {
pp_diag(NULL, config, 1, "line %i: \"%s\": not int\n",
lineno, word);
return -1;
}
if (l->f(lineno, i, NULL))
return -1;
break;
case ARG_STR:
while (*line && *line == ' ' && *line == '\t')
line++;
if (l->f(lineno, 0, line))
return -1;
break;
case ARG_NAMES:
for (n = l->args; n->name; n++)
if (!strcmp(line, n->name))
break;
if (!n->name) {
pp_diag(NULL, config, 1, "line %i: wrong arg \"%s\""
" for \"%s\"\n", lineno, line, word);
return -1;
}
if (l->f(lineno, n->value, NULL))
return -1;
break;
}
/* finally, increase nlinks so that it is equal to the configured links
* count */
ppg->nlinks++;
return 0;
ret_invalid_line:
ln--;
return -ln;
}
/* Parse a whole string by splitting line (shoud I fgets instead?) */
static int pp_parse_conf(struct pp_globals *ppg, char *conf, int len)
{
current_ppg = ppg;
int errcount = 0;
char *line, *rest, term;
int lineno = 0;
line = conf;
do {
lineno++;
for (rest = line; *rest && *rest != '\n'; rest++)
;
term = *rest;
*rest = '\0';
if (*line)
errcount += pp_parse_line(line, lineno) < 0;
line = rest + 1;
} while (term); /* if terminator was already 0, we are done */
return errcount ? -1 : 0;
}
/* Open about a file, warn if not found */
static int pp_open_conf_file(char **argv, char *name)
......@@ -291,6 +302,8 @@ int pp_config_file(struct pp_globals *ppg, int *argcp, char **argv,
struct stat conf_fs;
char *conf_buf;
current_ppg = ppg;
/* First, look for fname on the command line (which is parsed later) */
for (i = 1; i < *argcp - 1; i++) {
if (strcmp(argv[i], "-f"))
......@@ -331,13 +344,10 @@ int pp_config_file(struct pp_globals *ppg, int *argcp, char **argv,
close(conf_fd);
}
/* We need this trailing \n for the parser */
conf_buf[conf_len + 1] = '\n';
/* We need this trailing \0 for the parser */
conf_buf[conf_len + 1] = '\0';
if ((i = pp_parse_conf(ppg, conf_buf, conf_len)) < 0) {
pp_printf("%s: configuration: error at line %d\n", argv[0], -1);
exit(1);
}
i = pp_parse_conf(ppg, conf_buf, conf_len);
free(conf_buf);
return 0;
return i;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment