Improve new config prototype code

Add optional validator functions for STRUCT and ARRAY parsing.  Tighten up
logic for parsing STRUCTs and ARRAYs and the result flags.  Improve
documentation in block comments.  Add CFUNSUPPORTED result flag.
This commit is contained in:
Andrew Bettison 2012-11-27 16:47:30 +10:30
parent cf3c069f83
commit 479ec5d0df
3 changed files with 291 additions and 139 deletions

143
config.h
View File

@ -23,7 +23,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
/* This file defines the internal API to the configuration file. See "config_schema.h" for the
* definition of the configuration schema, which is used to generate these API components.
*
* Each STRUCT(foo) schema declaration produces the following data declaration:
* Each STRUCT(foo, ...) schema declaration produces the following data declaration:
*
* struct config_foo {
* TYPE NAME;
@ -75,7 +75,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* struct config_STRUCTNAME value;
*
* Each STRUCT(foo) and ARRAY_*(SIZE, foo, ...) schema declaration produces the following API
* Each STRUCT(foo, ...) and ARRAY_*(SIZE, foo, ...) schema declaration produces the following API
* functions:
*
* - int dfl_config_foo(struct config_foo *dest);
@ -86,34 +86,83 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* - int opt_config_foo(struct config_foo *dest, const struct cf_om_node *node);
*
* A C function which parses the given COM (configuration object model) and assigns the parsed
* result into the given C structure. See below for the return value.
* result into the given C structure. See below for the return value. For arrays, this
* function is used to parse each individual array element, and the parsed result is only
* appended to the array if it returns CFOK.
*
* If a STRUCT(foo, validator) or ARRAY(foo, ..., validator) schema declaration is given a validator
* function, then the function must have the following signature:
*
* - int validator(struct config_foo *dest, int orig_result);
*
* A C function which validates the contents of the given C structure (struct or array) as
* defined in the schema. This function is invoked by the opt_config_foo() parser function
* just before it returns, so all the parse functions have already been called and the result
* is assembled. The validator function is passed a pointer to the (non-const) structure,
* which it may modify if desired, and the original CFxxx flags result code (not CFERROR) that
* would be returned by the opt_config_foo() parser function. It returns a new CFxxx flags
* result (which may simply be the same as was passed).
*
* In the case arrays, validator() is passed a *dest containing elements that were successfully
* parsed from the COM, omitting any that did not parse successfully (in which case the
* relevant CFxxx result flags will be set) and arbitrarily omitting others that did not fit
* (in which case the CFOVERFLOW flag is set). It is up to validator() to decide whether to
* return some, all or none of these elements (ie, alter dest->ac and/or dest->av), and whether
* to set or clear the CFARRAYOVERFLOW bit, or set other bits (like CFINVALID for example). If
* there is no validator function, then opt_config_foo() will return an empty array (dest->ac
* == 0) in the case of CFARRAYOVERFLOW.
*
* All parse functions assign the result of their parsing into the struct given in their 'dest'
* argument, and return a bitmask of the following flags:
*
* - CFERROR (all bits set, == -1) if an unrecoverable error occurrs (eg, malloc() fails); the
* result in *dest is undefined and may be malformed or inconsistent;
* - CFERROR (all bits set, == -1) if an unrecoverable error occurs (eg, malloc() fails). The
* result in *dest is undefined and may be malformed or inconsistent.
*
* - CFEMPTY if no items were encountered in the COM (ie, no array elements or no struct elements);
* the memory at *dest is unchanged;
* - CFEMPTY if no items were parsed from the COM. In the case of a struct, this means that no
* child nodes were found for any elements; if any child nodes were present but failed parsing
* then CFEMPTY is not set but other flags will be set. In the case of arrays, CFEMPTY means
* that the returned array has zero length for _any_ reason (overflow, element parsing failures,
* or no elements present in the COM).
*
* - CFARRAYOVERFLOW if the size of any array was exceeded; a valid result is produced and the
* overflowed array(s) are fully populated, arbitrarily omitting some elements that were found in
* the COM;
* - CFUNSUPPORTED if the config item (array or struct) is not supported. This flag is not
* produced by the normal opt_config_foo() parse functions, but a validation function could
* set it as a way of indicating, for example, that a given option is not yet implemented or
* has been deprecated. In that case, the validation function should also log a message to that
* effect. The CFUNSUPPORTED flag is mainly used in its CFSUB(CFUNSUPPORTED) form (see below)
* to indicate that the COM contains elements that are not defined in the STRUCT.
*
* - CFSTRINGFOVERFLOW if the size of any string element was exceeded, a valid result is produced
* but the overflowed string elements are unchanged -- those parts of *dest are not overwritten;
* - CFARRAYOVERFLOW if the size of any array was exceeded. The result in *dest may be empty (in
* which case CFEMPTY is also set), or may contain elements parsed successfully from the COM (ie,
* returned CFOK), omitting any that did not parse successfully (in which case other bits will be
* set) and arbitrarily omitting others that did not fit (it is not defined which will be
* omitted). Normal array parsing without a validator function will return an empty result in
* the case of overflow, but a validator function may change this behaviour.
*
* - CFINCOMPLETE if any MANDATORY element was missing; a valid result is produced but the missing
* mandatory element(s) are unchanged -- those parts of *dest are not overwritten;
* - CFSTRINGFOVERFLOW if the size of any string element was exceeded. The result in *dest may be
* unchanged or may contain a truncated string, depending on the parser that detected and
* reported the string overflow.
*
* - CFINCOMPLETE if any MANDATORY element is missing (no node in the COM) or empty (as indicated
* by the CFEMPTY bit in its parse result). The result in *dest is valid but the missing
* mandatory element(s) are unchanged (in the case of a struct) or zero-length (in the case of an
* array).
*
* - CFINVALID if any invalid configuration value was encountered, ie, any parse function returned
* CFINVALID in its return flags; a valid result is produced but the invalid elements are
* unchanged -- those parts of *dest are not overwritten;
* CFINVALID in its return flags. The result in *dest is valid and the elements that failed
* to parse are unchanged.
*
* - CFSUB(CFxxx) if the STRUCT parser function encountered the error CFxxx when parsing a struct
* element, ie, a parse function returned CFxxx; a valid result is produced but some parts of
* *dest will not be overwritten;
* - CFSUB(CFxxx) if any element of a STRUCT or ARRAY produced a CFxxx result when being parsed, ie
* any element's parse function returned CFxxx. In the case of a STRUCT, the failed elements are
* usually left with their prior (default) values, but this depends on the parse functions'
* behaviours. In the case of an ARRAY, failed elements are omitted from the array
*
* The difference between CFSUB(CFxxx) and CFxxx needs explaining. To illustrate, CFSUB(CFINVALID)
* is different from CFINVALID because an element of a struct or array may have failed to parse, yet
* the whole struct or array itself may still be valid (in the case of a struct, the element's prior
* value may be retained, and in the case of an array, the failed element is simply omitted from the
* result). A validator function may wish to reflect any CFSUB() bit as a CFINVALID result, but the
* normal behaviour of opt_config_foo() is to not return CFINVALID unless the validator function
* sets it.
*
* The special value CFOK is zero (no bits set); in this case a valid result is produced and all of
* *dest is overwritten (except unused array elements).
@ -140,7 +189,7 @@ struct pattern_list {
#define PATTERN_LIST_EMPTY ((struct pattern_list){.patc = 0})
// Generate value structs, struct config_SECTION.
#define STRUCT(__name) \
#define STRUCT(__name, __validator...) \
struct config_##__name {
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
__type __element;
@ -162,10 +211,10 @@ struct pattern_list {
__decl; \
} av[(__size)]; \
};
#define ARRAY_ATOM(__name, __size, __type, __parser, __comment) __ARRAY(__name, __size, __type value)
#define ARRAY_STRING(__name, __size, __strsize, __parser, __comment) __ARRAY(__name, __size, char value[(__strsize) + 1])
#define ARRAY_NODE(__name, __size, __type, __parser, __comment) __ARRAY(__name, __size, __type value)
#define ARRAY_STRUCT(__name, __size, __structname, __comment) __ARRAY(__name, __size, struct config_##__structname value)
#define ARRAY_ATOM(__name, __size, __type, __eltparser, __validator...) __ARRAY(__name, __size, __type value)
#define ARRAY_STRING(__name, __size, __strsize, __eltparser, __validator...) __ARRAY(__name, __size, char value[(__strsize) + 1])
#define ARRAY_NODE(__name, __size, __type, __eltparser, __validator...) __ARRAY(__name, __size, __type value)
#define ARRAY_STRUCT(__name, __size, __structname, __validator...) __ARRAY(__name, __size, struct config_##__structname value)
#include "config_schema.h"
#undef STRUCT
#undef NODE
@ -189,6 +238,7 @@ struct pattern_list {
#define CFSTRINGOVERFLOW (1<<2)
#define CFINCOMPLETE (1<<3)
#define CFINVALID (1<<4)
#define CFUNSUPPORTED (1<<5)
#define CF__SUB_SHIFT 16
#define CFSUB(f) ((f) << CF__SUB_SHIFT)
#define CF__SUBFLAGS CFSUB(~0)
@ -198,7 +248,7 @@ strbuf strbuf_cf_flags(strbuf, int);
// Generate default functions, dfl_config_SECTION().
#define STRUCT(__name) \
#define STRUCT(__name, __validator...) \
int dfl_config_##__name(struct config_##__name *s) {
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
s->__element = (__default);
@ -218,10 +268,10 @@ strbuf strbuf_cf_flags(strbuf, int);
a->ac = 0; \
return CFOK; \
}
#define ARRAY_ATOM(__name, __size, __type, __parser, __comment) __ARRAY(__name)
#define ARRAY_STRING(__name, __size, __strsize, __parser, __comment) __ARRAY(__name)
#define ARRAY_NODE(__name, __size, __type, __parser, __comment) __ARRAY(__name)
#define ARRAY_STRUCT(__name, __size, __structname, __comment) __ARRAY(__name)
#define ARRAY_ATOM(__name, __size, __type, __eltparser, __validator...) __ARRAY(__name)
#define ARRAY_STRING(__name, __size, __strsize, __eltparser, __validator...) __ARRAY(__name)
#define ARRAY_NODE(__name, __size, __type, __eltparser, __validator...) __ARRAY(__name)
#define ARRAY_STRUCT(__name, __size, __structname, __validator...) __ARRAY(__name)
#include "config_schema.h"
#undef STRUCT
#undef NODE
@ -251,8 +301,12 @@ struct cf_om_node {
};
// Generate parser function prototypes.
#define STRUCT(__name) \
int opt_config_##__name(struct config_##__name *, const struct cf_om_node *);
#define __VALIDATOR(__name, __validator...) \
typedef int __validator_func__config_##__name##__t(struct config_##__name *, int); \
__validator_func__config_##__name##__t __dummy__validator_func__config_##__name, ##__validator;
#define STRUCT(__name, __validator...) \
int opt_config_##__name(struct config_##__name *, const struct cf_om_node *); \
__VALIDATOR(__name, ##__validator)
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
int __parser(__type *, const struct cf_om_node *);
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
@ -264,19 +318,20 @@ struct cf_om_node {
#define NODE_STRUCT(__name, __element, __parser, __flags) \
int __parser(struct config_##__name *, const struct cf_om_node *);
#define END_STRUCT
#define __ARRAY(__name) \
int opt_config_##__name(struct config_##__name *, const struct cf_om_node *);
#define ARRAY_ATOM(__name, __size, __type, __parser, __comment) \
__ARRAY(__name) \
int __parser(__type *, const struct cf_om_node *);
#define ARRAY_STRING(__name, __size, __strsize, __parser, __comment) \
__ARRAY(__name) \
int __parser(char *, size_t, const char *);
#define ARRAY_NODE(__name, __size, __type, __parser, __comment) \
__ARRAY(__name) \
int __parser(__type *, const struct cf_om_node *);
#define ARRAY_STRUCT(__name, __size, __structname, __comment) \
__ARRAY(__name) \
#define __ARRAY(__name, __validator...) \
int opt_config_##__name(struct config_##__name *, const struct cf_om_node *); \
__VALIDATOR(__name, ##__validator)
#define ARRAY_ATOM(__name, __size, __type, __eltparser, __validator...) \
__ARRAY(__name, ##__validator) \
int __eltparser(__type *, const struct cf_om_node *);
#define ARRAY_STRING(__name, __size, __strsize, __eltparser, __validator...) \
__ARRAY(__name, ##__validator) \
int __eltparser(char *, size_t, const char *);
#define ARRAY_NODE(__name, __size, __type, __eltparser, __validator...) \
__ARRAY(__name, ##__validator) \
int __eltparser(__type *, const struct cf_om_node *);
#define ARRAY_STRUCT(__name, __size, __structname, __validator...) \
__ARRAY(__name, ##__validator) \
int opt_config_##__structname(struct config_##__structname *, const struct cf_om_node *);
#include "config_schema.h"
#undef STRUCT

View File

@ -42,7 +42,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* STRING(80, element2, "boo!", opt_str_nonempty, MANDATORY, "A non-empty string")
* END_STRUCT
*
* ARRAY_STRUCT(joy, 16, happy, "An array of integer-string pairs")
* ARRAY_STRUCT(joy, 16, happy,, "An array of integer-string pairs")
*
* STRUCT(love)
* SUB_STRUCT(happy, thing,)
@ -79,18 +79,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* A schema definition is composed from the following STRUCT and ARRAY definitions:
*
* STRUCT(name)
* STRUCT(name[, validatorfunc])
* ATOM(type, element, default, parsefunc, flags, comment)
* NODE(type, element, default, parsefunc, flags, comment)
* STRING(size, element, default, parsefunc, flags, comment)
* STRING(strsize, element, default, parsefunc, flags, comment)
* SUB_STRUCT(structname, element, flags)
* NODE_STRUCT(structname, element, parsefunc, flags)
* END_STRUCT
*
* ARRAY_ATOM(name, size, type, parsefunc, comment)
* ARRAY_STRING(name, size, strsize, parsefunc, comment)
* ARRAY_NODE(name, size, type, parsefunc, comment)
* ARRAY_STRUCT(name, size, structname, comment)
* ARRAY_ATOM(name, size, type, parsefunc[, validatorfunc])
* ARRAY_STRING(name, size, strsize, parsefunc[, validatorfunc])
* ARRAY_NODE(name, size, type, parsefunc[, validatorfunc])
* ARRAY_STRUCT(name, size, structname[, validatorfunc])
*
* The meanings of the parameters are:
*
@ -98,6 +98,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* A label used to qualify this struct/array's API from the API components of other structs and
* arrays. This label does not appear anywhere in the config file itself; it is purely for
* internal code-related purposes.
* 'strsize'
* For STRING elements and ARRAY_STRING arrays of strings, gives the maximum length of the
* string. The struct is declared with an array of char[strsize+1] to leave room for a
* terminating nul.
* 'size'
* For all ARRAYs, gives the maximum size of the array.
* 'type'
* Only used for ATOM, NODE, ARRAY_ATOM and ARRAY_NODE declarations. Gives the C type of the
* element. For STRING and ARRAY_STRING, this is implicitly (const char *).
@ -114,9 +120,18 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
* Only used for ATOM and NODE struct elements. Gives the default value for the element if
* absent from the config file.
* 'parsefunc'
* The function used to parse the value from the config file. The ATOM, STRING, ARRAY_ATOM and
* ARRAY_STRING parse functions take a string argument (const char *). The NODE, NODE_STRUCT
* and ARRAY_NODE parse functions take a pointer to a COM node (const struct cf_om_node).
* The function used to parse a text value from the config file. The ATOM, STRING, ARRAY_ATOM
* and ARRAY_STRING parse functions take a string argument (const char *). The NODE,
* NODE_STRUCT and ARRAY_NODE parse functions take a pointer to a COM node (const struct
* cf_om_node). For arrays, the parsefunc is used to parse each element's value, not the
* entire array. Returns a CFxxx result code as documented in "config.h".
* 'validatorfunc'
* A function that is called after the struct/array is fully parsed and populated. This
* function can perform validation checks on the whole struct/array that cannot be performed by
* the parse functions of each element in isolation, and can even alter the contents of the
* struct/array, eg, sort an array or fill in default values in structs that depend on other
* elements. Takes as its second argument the CFxxx code produced by the parser, and returns
* an updated CFxxx result code (which could be the same) as documented in "config.h".
* 'flags'
* A space-separated list of flags. At present only the MANDATORY flag is supported, which
* will cause parsing to fail if the given STRUCT element is not set in the config file. In
@ -140,15 +155,15 @@ STRUCT(server)
STRING(256, chdir, "/", opt_absolute_path,, "Absolute path of chdir(2) for server process")
END_STRUCT
ARRAY_STRING(argv, 8, 128, opt_str, "Array of arguments to pass to command")
ARRAY_STRING(argv, 8, 128, opt_str, vld_argv)
STRUCT(dnahelper)
STRING(256, executable, "", opt_absolute_path,, "Absolute path of dna helper executable")
STRUCT(executable)
STRING(256, executable, "", opt_absolute_path, MANDATORY, "Absolute path of dna helper executable")
SUB_STRUCT(argv, argv,)
END_STRUCT
STRUCT(dna)
SUB_STRUCT(dnahelper, helper,)
SUB_STRUCT(executable, helper,)
END_STRUCT
STRUCT(rhizomepeer)
@ -157,7 +172,7 @@ STRING(256, host, "", opt_str_nonempty, MANDATORY, "Host n
ATOM(uint16_t, port, RHIZOME_HTTP_PORT, opt_port,, "Port number")
END_STRUCT
ARRAY_NODE(peerlist, 10, struct config_rhizomepeer, opt_rhizome_peer, "Rhizome peers")
ARRAY_NODE(peerlist, 10, struct config_rhizomepeer, opt_rhizome_peer)
STRUCT(rhizomedirect)
SUB_STRUCT(peerlist, peer,)
@ -181,7 +196,7 @@ ATOM(uint16_t, port, RHIZOME_HTTP_PORT, opt_port,, "Port numb
ATOM(uint64_t, speed, 1000000, opt_uint64_scaled,, "Speed in bits per second")
END_STRUCT
ARRAY_STRUCT(interface_list, 10, network_interface, "Network interfaces")
ARRAY_STRUCT(interface_list, 10, network_interface)
STRUCT(main)
NODE_STRUCT(interface_list, interfaces, opt_interface_list, MANDATORY)

View File

@ -4,16 +4,22 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <assert.h>
#include "str.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
#define NELS(a) (sizeof (a) / sizeof *(a))
#define DEBUGF(F,...) fprintf(stderr, "DEBUG: " F "\n", ##__VA_ARGS__)
#define WARNF(F,...) fprintf(stderr, "WARN: " F "\n", ##__VA_ARGS__)
#define WHYF(F,...) fprintf(stderr, "ERROR: " F "\n", ##__VA_ARGS__)
#define WHYF_perror(F,...) fprintf(stderr, "ERROR: " F ": %s [errno=%d]\n", ##__VA_ARGS__, strerror(errno), errno)
#define _DEBUGF(F,...) fprintf(stderr, "DEBUG: " F "\n", ##__VA_ARGS__)
#define _WARNF(F,...) fprintf(stderr, "WARN: " F "\n", ##__VA_ARGS__)
#define _WHYF(F,...) fprintf(stderr, "ERROR: " F "\n", ##__VA_ARGS__)
#define _WHYF_perror(F,...) fprintf(stderr, "ERROR: " F ": %s [errno=%d]\n", ##__VA_ARGS__, strerror(errno), errno)
#define DEBUGF(F,...) _DEBUGF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
#define WARNF(F,...) _WARNF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
#define WHYF(F,...) _WHYF("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
#define WHYF_perror(F,...) _WHYF_perror("%s:%u " F, __FILE__, __LINE__, ##__VA_ARGS__)
#define alloca_str(s) ((s) ? alloca_str_toprint(s) : "NULL")
#include "config.h"
@ -215,9 +221,26 @@ int get_child(const struct cf_om_node *parent, const char *key)
return -1;
}
void warn_node(const char *file, unsigned line, const struct cf_om_node *node, const char *fmt, ...)
{
strbuf b = strbuf_alloca(1024);
if (node) {
if (node->source && node->line_number)
strbuf_sprintf(b, "%s:%u: ", node->source, node->line_number);
strbuf_puts(b, "configuration option \"");
strbuf_puts(b, node->fullkey);
strbuf_puts(b, "\": ");
}
va_list ap;
va_start(ap, fmt);
strbuf_vsprintf(b, fmt, ap);
va_end(ap);
_WARNF("%s:%u %s", file, line, strbuf_str(b));
}
void missing_node(const struct cf_om_node *parent, const char *key)
{
WARNF("missing configuration option `%s%s%s`",
WARNF("missing configuration option %s%s%s",
parent->fullkey ? parent->fullkey : "", parent->fullkey ? "." : "", key
);
}
@ -233,10 +256,7 @@ strbuf strbuf_cf_flags(strbuf sb, int flags)
{ CFARRAYOVERFLOW, "CFARRAYOVERFLOW" },
{ CFINCOMPLETE, "CFINCOMPLETE" },
{ CFINVALID, "CFINVALID" },
{ CFSUB(CFSTRINGOVERFLOW), "CFSUB(CFSTRINGOVERFLOW)" },
{ CFSUB(CFARRAYOVERFLOW), "CFSUB(CFARRAYOVERFLOW)" },
{ CFSUB(CFINCOMPLETE), "CFSUB(CFINCOMPLETE)" },
{ CFSUB(CFINVALID), "CFSUB(CFINVALID)" },
{ CFUNSUPPORTED, "CFUNSUPPORTED" },
};
int i;
for (i = 0; i < NELS(flagdefs); ++i) {
@ -247,6 +267,16 @@ strbuf strbuf_cf_flags(strbuf sb, int flags)
flags &= ~flagdefs[i].flag;
}
}
for (i = 0; i < NELS(flagdefs); ++i) {
if (flags & CFSUB(flagdefs[i].flag)) {
if (strbuf_len(sb) != n)
strbuf_putc(sb, ' ');
strbuf_puts(sb, "CFSUB(");
strbuf_puts(sb, flagdefs[i].name);
strbuf_putc(sb, ')');
flags &= ~CFSUB(flagdefs[i].flag);
}
}
if (flags) {
if (strbuf_len(sb) != n)
strbuf_putc(sb, ' ');
@ -271,14 +301,18 @@ void warn_ignoring_node(const struct cf_om_node *node, int reason)
why = "array overflow";
else if (reason & CFSTRINGOVERFLOW)
why = "string overflow";
else if (reason & CFUNSUPPORTED)
adj = "unsupported";
else if (reason & CFSUB(CFINVALID))
adj = "invalid in sub-struct";
adj = "contains invalid element";
else if (reason & CFSUB(CFINCOMPLETE))
why = "incomplete sub-struct";
why = "contains incomplete element";
else if (reason & CFSUB(CFARRAYOVERFLOW))
why = "array overflow in sub-struct";
why = "contains array overflow";
else if (reason & CFSUB(CFSTRINGOVERFLOW))
why = "string overflow in sub-struct";
why = "contains string overflow";
else if (reason & CFSUB(CFUNSUPPORTED))
why = "contains unsupported element";
else if (reason & CFEMPTY)
why = "empty";
else if (reason)
@ -287,9 +321,7 @@ void warn_ignoring_node(const struct cf_om_node *node, int reason)
adj = "valid";
why = "no reason";
}
WARNF("%s:%u: ignoring configuration option %s with%s%s value %s%s%s",
node->source, node->line_number,
alloca_str(node->fullkey),
warn_node(__FILE__, __LINE__, node, "ignoring%s%s value %s%s%s",
adj ? " " : "", adj ? adj : "",
alloca_str(node->text),
why ? " -- " : "", why ? why : ""
@ -298,16 +330,9 @@ void warn_ignoring_node(const struct cf_om_node *node, int reason)
void ignore_node(const struct cf_om_node *node, const char *msg)
{
if (node->source && node->line_number)
WARNF("%s:%u: ignoring configuration option %s%s%s",
node->source, node->line_number, alloca_str(node->fullkey),
msg && msg[0] ? " -- " : "", msg ? msg : ""
);
else
WARNF("ignoring configuration option %s%s%s",
alloca_str(node->fullkey),
msg && msg[0] ? " -- " : "", msg ? msg : ""
);
warn_node(__FILE__, __LINE__, node, "ignoring%s%s",
msg && msg[0] ? " -- " : "", msg ? msg : ""
);
}
void ignore_tree(const struct cf_om_node *node, const char *msg);
@ -572,6 +597,11 @@ int opt_uint64_scaled(uint64_t *intp, const char *text)
return CFOK;
}
int vld_argv(struct config_argv *array, int result)
{
return result;
}
int opt_port(unsigned short *portp, const char *text)
{
unsigned short port = 0;
@ -652,6 +682,36 @@ int opt_pattern_list(struct pattern_list *listp, const char *text)
}
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
* single text string of the form:
*
* ( "+" | "-" ) [ interfacename ] [ "=" type ] [ ":" port [ ":" speed ] ]
*
* where:
*
* "+" means include the interface
* "-" means exclude the interface
*
* The original implementation applied include/exclude matching in the order that the list was
* given, but the new implementation applies all exclusions before apply inclusions. This should
* not be a problem, as there were no known uses that depended on testing an inclusion before an
* exclusion.
*
* An empty 'interfacename' matches all interfaces. So a "+" by itself includes all interfaces,
* and a '-' by itself excludes all interfaces. These two rules are applied after all other
* interface inclusions/exclusions are tested, otherwise "-" would overrule all other interfaces.
*
* The optional 'type' tells DNA how to handle the interface in terms of bandwidth:distance
* relationship for calculating tick times etc.
*
* The optional 'port' is the port number to bind all interfaces, instead of the default.
*
* The optional 'speed' is the nominal bits/second bandwidth of the interface, instead of the
* default. It is expressed as a positive integer with an optional scaling suffix, eg, "150k",
* "1K", "900M".
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int opt_network_interface(struct config_network_interface *nifp, const char *text)
{
//DEBUGF("%s text=%s", __FUNCTION__, alloca_str(text));
@ -730,10 +790,18 @@ int opt_network_interface(struct config_network_interface *nifp, const char *tex
return CFOK;
}
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
* comma-separated list of interface rules (see opt_network_interface() for the format of each
* rule), then parses the regular config array-of-struct style interface option settings so that
* both forms are supported.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node)
{
int result = CFOK;
int ret;
int result = opt_config_interface_list(listp, node);
if (result == CFERROR)
return CFERROR;
if (node->text) {
const char *p;
const char *arg = NULL;
@ -743,12 +811,12 @@ int opt_interface_list(struct config_interface_list *listp, const struct cf_om_n
if (arg) {
int len = p - arg;
if (len > 80) {
result = CFSTRINGOVERFLOW;
goto invalid;
result |= CFSTRINGOVERFLOW;
goto bye;
}
char buf[len + 1];
strncpy(buf, arg, len)[len] = '\0';
ret = opt_network_interface(&listp->av[n].value, buf);
int ret = opt_network_interface(&listp->av[n].value, buf);
switch (ret) {
case CFERROR: return CFERROR;
case CFOK:
@ -757,8 +825,9 @@ int opt_interface_list(struct config_interface_list *listp, const struct cf_om_n
++n;
break;
default:
result = ret;
goto invalid;
warn_node(__FILE__, __LINE__, node, "ignoring invalid interface rule %s", alloca_str(buf)); \
result |= CFSUB(ret);
break;
}
arg = NULL;
}
@ -768,21 +837,17 @@ int opt_interface_list(struct config_interface_list *listp, const struct cf_om_n
arg = p;
}
if (*p) {
result = CFARRAYOVERFLOW;
goto invalid;
result |= CFARRAYOVERFLOW;
goto bye;
}
assert(n <= NELS(listp->av));
listp->ac = n;
}
invalid:
ret = opt_config_interface_list(listp, node);
switch (ret) {
case CFERROR: return CFERROR;
default:
if (result < ret)
result = ret;
break;
}
bye:
if (listp->ac == 0)
result |= CFEMPTY;
else
result &= ~CFEMPTY;
return result;
}
@ -803,8 +868,9 @@ void list_omit_element(const struct cf_om_node *node);
#define USES_CHILDREN |__CHILDREN
// Generate parsing functions, opt_config_SECTION()
#define STRUCT(__name) \
int opt_config_##__name(struct config_##__name *s, const struct cf_om_node *node) { \
#define STRUCT(__name, __validator...) \
int opt_config_##__name(struct config_##__name *strct, const struct cf_om_node *node) { \
int (*validator)(struct config_##__name *, int) = (NULL, ##__validator); \
int result = CFEMPTY; \
char used[node->nodc]; \
memset(used, 0, node->nodc * sizeof used[0]);
@ -816,10 +882,11 @@ void list_omit_element(const struct cf_om_node *node);
if (child) { \
used[i] |= (__flags); \
ret = (__parseexpr); \
if (ret == CFERROR) \
return CFERROR; \
} \
if (ret == CFERROR) \
return CFERROR; \
result |= ret & CF__SUBFLAGS; \
ret &= CF__FLAGS; \
if (!(ret & CFEMPTY)) \
result &= ~CFEMPTY; \
else if ((__flags) & __MANDATORY) { \
@ -834,63 +901,78 @@ void list_omit_element(const struct cf_om_node *node);
} \
}
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
__ITEM(__element, 0 __flags, __parser(&s->__element, child))
__ITEM(__element, 0 __flags, __parser(&strct->__element, child))
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(&s->__element, child->text) : CFEMPTY)
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(&strct->__element, child->text) : CFEMPTY)
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(s->__element, (__size) + 1, child->text) : CFEMPTY)
__ITEM(__element, ((0 __flags)|__TEXT)&~__CHILDREN, child->text ? __parser(strct->__element, (__size) + 1, child->text) : CFEMPTY)
#define SUB_STRUCT(__name, __element, __flags) \
__ITEM(__element, (0 __flags)|__CHILDREN, opt_config_##__name(&s->__element, child))
__ITEM(__element, (0 __flags)|__CHILDREN, opt_config_##__name(&strct->__element, child))
#define NODE_STRUCT(__name, __element, __parser, __flags) \
__ITEM(__element, (0 __flags)|__TEXT|__CHILDREN, __parser(&s->__element, child))
__ITEM(__element, (0 __flags)|__TEXT|__CHILDREN, __parser(&strct->__element, child))
#define END_STRUCT \
{ \
int i; \
for (i = 0; i < node->nodc; ++i) { \
if (node->nodv[i]->text && !(used[i] & __TEXT)) \
if (node->nodv[i]->text && !(used[i] & __TEXT)) { \
unsupported_node(node->nodv[i]); \
if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) \
result |= CFSUB(CFUNSUPPORTED); \
} \
if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) { \
unsupported_children(node->nodv[i]); \
result |= CFSUB(CFUNSUPPORTED); \
} \
} \
} \
if (validator) \
result = (*validator)(strct, result); \
return result; \
}
#define __ARRAY(__name, __parseexpr) \
int opt_config_##__name(struct config_##__name *s, const struct cf_om_node *node) { \
#define __ARRAY(__name, __parseexpr, __validator...) \
int opt_config_##__name(struct config_##__name *array, const struct cf_om_node *node) { \
int (*validator)(struct config_##__name *, int) = (NULL, ##__validator); \
int result = CFOK; \
int i; \
for (i = 0; i < node->nodc && s->ac < NELS(s->av); ++i) { \
int i, n; \
for (n = 0, i = 0; i < node->nodc && n < NELS(array->av); ++i) { \
const struct cf_om_node *child = node->nodv[i]; \
int ret = (__parseexpr); \
switch (ret) { \
case CFERROR: return CFERROR; \
case CFOK: \
strncpy(s->av[s->ac].label, child->key, sizeof s->av[s->ac].label - 1)\
[sizeof s->av[s->ac].label - 1] = '\0'; \
++s->ac; \
break; \
default: \
if (ret == CFERROR) \
return CFERROR; \
result |= ret & CF__SUBFLAGS; \
ret &= CF__FLAGS; \
if (ret == CFOK) { \
strncpy(array->av[n].label, child->key, sizeof array->av[n].label - 1)\
[sizeof array->av[n].label - 1] = '\0'; \
++n; \
} else { \
list_omit_element(child); \
break; \
result |= CFSUB(ret); \
} \
} \
for (; i < node->nodc; ++i) { \
if (i < node->nodc) { \
assert(n == NELS(array->av)); \
result |= CFARRAYOVERFLOW; \
list_overflow(node->nodv[i]); \
for (; i < node->nodc; ++i) \
list_overflow(node->nodv[i]); \
} \
if (s->ac == 0) \
array->ac = n; \
if (validator) \
result = (*validator)(array, result); \
else if (result & CFARRAYOVERFLOW) \
array->ac = 0; \
if (array->ac == 0) \
result |= CFEMPTY; \
return result; \
}
#define ARRAY_ATOM(__name, __size, __type, __parser, __comment) \
__ARRAY(__name, child->text ? __parser(&s->av[s->ac].value, child->text) : CFEMPTY)
#define ARRAY_STRING(__name, __size, __strsize, __parser, __comment) \
__ARRAY(__name, child->text ? __parser(s->av[s->ac].value, (__strsize) + 1, child->text) : CFEMPTY)
#define ARRAY_NODE(__name, __size, __type, __parser, __comment) \
__ARRAY(__name, __parser(&s->av[s->ac].value, child))
#define ARRAY_STRUCT(__name, __size, __structname, __comment) \
__ARRAY(__name, opt_config_##__structname(&s->av[s->ac].value, child))
#define ARRAY_ATOM(__name, __size, __type, __eltparser, __validator...) \
__ARRAY(__name, child->text ? __eltparser(&array->av[n].value, child->text) : CFEMPTY, ##__validator)
#define ARRAY_STRING(__name, __size, __strsize, __eltparser, __validator...) \
__ARRAY(__name, child->text ? __eltparser(array->av[n].value, sizeof array->av[n].value, child->text) : CFEMPTY, ##__validator)
#define ARRAY_NODE(__name, __size, __type, __eltparser, __validator...) \
__ARRAY(__name, __eltparser(&array->av[n].value, child), ##__validator)
#define ARRAY_STRUCT(__name, __size, __structname, __validator...) \
__ARRAY(__name, opt_config_##__structname(&array->av[n].value, child), ##__validator)
#include "config_schema.h"