diff --git a/config.h b/config.h index ec8436e0..5cce977a 100644 --- a/config.h +++ b/config.h @@ -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 diff --git a/config_schema.h b/config_schema.h index cfc51509..b20b9c2b 100644 --- a/config_schema.h +++ b/config_schema.h @@ -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) diff --git a/config_test.c b/config_test.c index 51aabfe5..faed0c02 100644 --- a/config_test.c +++ b/config_test.c @@ -4,16 +4,22 @@ #include #include #include +#include #include #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 + */ 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 + */ 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"