diff --git a/config.h b/config.h index 0a8aa713..ace8bb77 100644 --- a/config.h +++ b/config.h @@ -1,139 +1,35 @@ -#include +#include +#include "constants.h" typedef unsigned long debugflags_t; -struct config_node { - const char *source; // = parse_config() 'source' arg - unsigned int line_number; - const char *fullkey; // malloc() - const char *key; // points inside fullkey, do not free() - const char *text; // malloc() - size_t nodc; - struct config_node *nodv[10]; // malloc() +#define RHIZOME_HTTP_PORT 4110 + +typedef struct binarysid { unsigned char binary[SID_SIZE]; } sid_t; + +struct pattern_list { + unsigned patc; + char patv[16][41]; }; -void *emalloc(size_t len); -char *strn_emalloc(const char *str, size_t len); -char *str_emalloc(const char *str); +#define PATTERN_LIST_EMPTY ((struct pattern_list){.patc = 0}) -struct config_node *parse_config(const char *source, const char *buf, size_t len); -int get_child(const struct config_node *parent, const char *key); -void unsupported_node(const struct config_node *node); -void unsupported_tree(const struct config_node *node); +#define SID_NONE ((sid_t){0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}) +#define SID_BROADCAST ((sid_t){0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}) // Generate value structs, struct config_SECTION. #define SECTION(__sect) struct config_##__sect { -#define ITEM(__name, __type, __default, __parser, __comment) __type __name; -#define SUB(__name, __sect) struct config_##__sect __name; -#define LIST(__name, __type, __parser, __comment) struct { unsigned listc; struct { const char *label; __type value; } *listv; } __name; +#define ATOM(__type, __name, __default, __parser, __flags, __comment) __type __name; +#define STRING(__size, __name, __default, __parser, __flags, __comment) char __name[__size + 1]; +#define SUB(__sect, __name, __flags) struct config_##__sect __name; +#define SUBP(__sect, __name, __parser, __flags) struct config_##__sect __name; #define SECTION_END }; +#define LIST(__sect, __type, __size, __parser, __comment) struct config_##__sect { unsigned listc; struct { char label[41]; __type value; } listv[(__size)]; }; #include "config_schema.h" #undef SECTION -#undef ITEM +#undef ATOM +#undef STRING #undef SUB -#undef LIST +#undef SUBP #undef SECTION_END - -// Generate parser function prototypes. -#define SECTION(__sect) int opt_config_##__sect(struct config_##__sect *s, const struct config_node *node); -#define ITEM(__name, __type, __default, __parser, __comment) int __parser(__type *, const struct config_node *); -#define SUB(__name, __sect) -#define LIST(__name, __type, __parser, __comment) int __parser(__type *, const struct config_node *); -#define SECTION_END -#include "config_schema.h" -#undef SECTION -#undef ITEM -#undef SUB #undef LIST -#undef SECTION_END - -int opt_boolean(int *booleanp, const struct config_node *node); -int opt_absolute_path(const char **pathp, const struct config_node *node); -int opt_debugflags(debugflags_t *flagsp, const struct config_node *node); -int opt_rhizome_peer(struct config_rhizomepeer *, const struct config_node *node); -int opt_host(const char **hostp, const struct config_node *node); -int opt_port(unsigned short *portp, const struct config_node *node); - -// Generate default functions, dfl_config_SECTION() -#define SECTION(__sect) \ - void dfl_config_##__sect(struct config_##__sect *s) { -#define ITEM(__name, __type, __default, __parser, __comment) s->__name = __default; -#define SUB(__name, __sect) dfl_config_##__sect(&s->__name); -#define LIST(__name, __type, __parser, __comment) s->__name.listc = 0; s->__name.listv = NULL; -#define SECTION_END } -#include "config_schema.h" -#undef SECTION -#undef ITEM -#undef SUB -#undef LIST -#undef SECTION_END - -// Generate parsing functions, opt_config_SECTION() -#define SECTION(__sect) \ - int opt_config_##__sect(struct config_##__sect *s, const struct config_node *node) { \ - if (node->text) unsupported_node(node); \ - int i; \ - char used[node->nodc]; \ - for (i = 0; i < node->nodc; ++i) \ - used[i] = 0; -#define ITEM(__name, __type, __default, __parser, __comment) \ - if ((i = get_child(node, #__name)) != -1) { \ - used[i] = 1; \ - __parser(&s->__name, node->nodv[i]); \ - } -#define SUB(__name, __sect) \ - if ((i = get_child(node, #__name)) != -1) { \ - used[i] = 1; \ - opt_config_##__sect(&s->__name, node->nodv[i]); \ - } -#define LIST(__name, __type, __parser, __comment) \ - if ((i = get_child(node, #__name)) != -1) { \ - used[i] = 1; \ - const struct config_node *child = node->nodv[i]; \ - if (child->text) \ - unsupported_node(child); \ - char *labels[child->nodc]; \ - __type values[child->nodc]; \ - int n = 0; \ - int j; \ - for (j = 0; j < child->nodc; ++j) { \ - const struct config_node *elt = child->nodv[j]; \ - switch (__parser(&values[n], elt)) { \ - case -1: \ - return -1; \ - case 0: \ - if (!(labels[n++] = str_emalloc(elt->key))) { \ - while (n) \ - free(labels[--n]); \ - return -1; \ - } \ - break; \ - } \ - } \ - if (n) { \ - if (s->__name.listv = emalloc(n * sizeof(s->__name.listv[0]))) { \ - for (j = 0; j < n; ++j) { \ - s->__name.listv[j].label = labels[j]; \ - s->__name.listv[j].value = values[j]; \ - } \ - s->__name.listc = n; \ - } else { \ - while (n) \ - free(labels[--n]); \ - return -1; \ - } \ - } \ - } -#define SECTION_END \ - for (i = 0; i < node->nodc; ++i) \ - if (!used[i]) \ - unsupported_tree(node->nodv[i]); \ - return 0; \ - } -#include "config_schema.h" -#undef SECTION -#undef ITEM -#undef SUB -#undef LIST -#undef SECTION_END - diff --git a/config_schema.h b/config_schema.h index 233e86e7..2d216b41 100644 --- a/config_schema.h +++ b/config_schema.h @@ -1,27 +1,44 @@ SECTION(log) -ITEM( file, const char *, NULL, opt_absolute_path, "Absolute path of log file") -ITEM( show_pid, int, 1, opt_boolean, "If true, all log lines contain PID of logging process") -ITEM( show_time, int, 1, opt_boolean, "If true, all log lines contain time stamp") +STRING(256, file, "", opt_absolute_path,, "Absolute path of log file") +ATOM(int, show_pid, 1, opt_boolean,, "If true, all log lines contain PID of logging process") +ATOM(int, show_time, 1, opt_boolean,, "If true, all log lines contain time stamp") SECTION_END SECTION(rhizomepeer) -ITEM( protocol, const char *, NULL, opt_protocol, "Protocol name") -ITEM( host, const char *, NULL, opt_host, "Host name or IP address") -ITEM( port, unsigned short, 4110, opt_port, "Port number") +STRING(25, protocol, "http", opt_protocol,, "Protocol name") +STRING(256, host, "", opt_str_nonempty, MANDATORY, "Host name or IP address") +ATOM(uint16_t, port, RHIZOME_HTTP_PORT, opt_port,, "Port number") SECTION_END +LIST(peerlist, struct config_rhizomepeer, 10, opt_rhizome_peer, "List of rhizome peers") + SECTION(rhizomedirect) -LIST( peer, struct config_rhizomepeer, opt_rhizome_peer, "List of rhizome peers") +SUB(peerlist, peer,) SECTION_END SECTION(rhizome) -ITEM( path, const char *, NULL, opt_absolute_path, "Absolute path of rhizome directory") -ITEM( enabled, int, 1, opt_boolean, "If true, Rhizome HTTP server is started") -SUB( direct, rhizomedirect) +STRING(256, path, "", opt_absolute_path,, "Absolute path of rhizome directory") +ATOM(int, enabled, 1, opt_boolean,, "If true, Rhizome HTTP server is started") +SUB(rhizomedirect, direct,) SECTION_END -SECTION(main) -SUB( log, log) -ITEM( debug, debugflags_t, 0, opt_debugflags, "Debug flags") -SUB( rhizome, rhizome) +SECTION(directory) +ATOM(sid_t, service, SID_NONE, opt_sid,, "Subscriber ID of Serval Directory Service") +SECTION_END + +SECTION(network_interface) +ATOM(struct pattern_list,match, PATTERN_LIST_EMPTY, opt_pattern_list, MANDATORY, "Names that match network interface") +ATOM(short, type, OVERLAY_INTERFACE_WIFI, opt_interface_type,, "Type of network interface") +ATOM(uint16_t, port, RHIZOME_HTTP_PORT, opt_port,, "Port number for network interface") +ATOM(uint64_t, speed, 1000000, opt_uint64_scaled,, "Speed in bits per second") +SECTION_END + +LIST(interface_list, struct config_network_interface, 10, opt_config_network_interface, "List of network interfaces") + +SECTION(main) +SUBP(interface_list, interfaces, opt_interface_list, MANDATORY) +SUB(log, log,) +ATOM(debugflags_t, debug, 0, opt_debugflags,, "Debug flags") +SUB(rhizome, rhizome,) +SUB(directory, directory,) SECTION_END diff --git a/config_test.c b/config_test.c index 5d4ddd3b..18820fbf 100644 --- a/config_test.c +++ b/config_test.c @@ -8,16 +8,25 @@ #include "str.h" #include "strbuf_helpers.h" -#include "config.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 alloca_str(s) ((s) ? alloca_str_toprint(s) : "NULL") -struct config_main config; -struct config_main default_config; +#include "config.h" + +struct config_node { + const char *source; // = parse_config() 'source' arg + unsigned int line_number; + const char *fullkey; // malloc() + const char *key; // points inside fullkey, do not free() + const char *text; // malloc() + size_t nodc; + struct config_node *nodv[10]; // malloc() +}; const char *find_keyend(const char *const key, const char *const fullkeyend) { @@ -73,7 +82,7 @@ int make_child(struct config_node **const parentp, const char *const fullkey, co c = strncmp(key, child->key, keylen); if (c == 0 && child->key[keylen]) c = -1; - //DEBUGF(" m=%d n=%d i=%d child->key=%s c=%d", m, n, i, alloca_str_toprint(child->key), c); + //DEBUGF(" m=%d n=%d i=%d child->key=%s c=%d", m, n, i, alloca_str(child->key), c); if (c == 0) { //DEBUGF(" found i=%d", i); return i; @@ -90,7 +99,7 @@ int make_child(struct config_node **const parentp, const char *const fullkey, co child = emalloc(sizeof *child); if (child == NULL) return -1; - memset(child, sizeof *child, 0); + memset(child, 0, sizeof *child); ++(*parentp)->nodc; if ((*parentp)->nodc > NELS((*parentp)->nodv)) *parentp = realloc(*parentp, sizeof(**parentp) + sizeof((*parentp)->nodv[0]) * ((*parentp)->nodc - NELS((*parentp)->nodv))); @@ -127,7 +136,7 @@ struct config_node *parse_config(const char *source, const char *buf, size_t len struct config_node *root = emalloc(sizeof(struct config_node)); if (root == NULL) return NULL; - memset(root, sizeof *root, 0); + memset(root, 0, sizeof *root); const char *end = buf + len; const char *line = buf; const char *nextline; @@ -177,8 +186,7 @@ struct config_node *parse_config(const char *source, const char *buf, size_t len ); continue; } - for (++p; p < lend && isspace(*p); ++p) - ; + ++p; if (!(node->text = strn_emalloc(p, lend - p))) break; // out of memory node->source = source; @@ -198,9 +206,9 @@ void dump_config_node(const struct config_node *node, int indent) DEBUGF("%*s%s:%u fullkey=%s key=%s text=%s", indent * 3, "", node->source ? node->source : "NULL", node->line_number, - node->fullkey ? alloca_str_toprint(node->fullkey) : "NULL", - node->key ? alloca_str_toprint(node->key) : "NULL", - node->text ? alloca_str_toprint(node->text) : "NULL" + alloca_str(node->fullkey), + alloca_str(node->key), + alloca_str(node->text) ); int i; for (i = 0; i < node->nodc; ++i) @@ -217,22 +225,33 @@ int get_child(const struct config_node *parent, const char *key) return -1; } +void missing_node(const struct config_node *parent, const char *key) +{ + WARNF("missing configuration option `%s.%s`", parent->fullkey, key); +} + void invalid_text(const struct config_node *node, const char *reason) { WARNF("%s:%u: ignoring configuration option %s with invalid value %s%s%s", node->source, node->line_number, - alloca_str_toprint(node->fullkey), - alloca_str_toprint(node->text), + alloca_str(node->fullkey), + alloca_str(node->text), reason && reason[0] ? " -- " : "", reason ? reason : "" ); } void ignore_node(const struct config_node *node, const char *msg) { - WARNF("%s:%u: ignoring configuration option %s%s%s", - node->source, node->line_number, alloca_str_toprint(node->fullkey), - msg && msg[0] ? " -- " : "", msg ? 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 : "" + ); } void ignore_tree(const struct config_node *node, const char *msg); @@ -256,6 +275,16 @@ void unsupported_node(const struct config_node *node) ignore_node(node, "not supported"); } +void list_overflow(const struct config_node *node) +{ + ignore_children(node, "list overflow"); +} + +void list_omit_element(const struct config_node *node) +{ + ignore_node(node, "omitted from list"); +} + void spurious_children(const struct config_node *parent) { ignore_children(parent, "spurious"); @@ -271,39 +300,84 @@ void unsupported_tree(const struct config_node *node) ignore_tree(node, "not supported"); } +/* Return values for parsing functions */ +#define CFERROR (-1) +#define CFOK 0 +#define CFOVERFLOW 1 +#define CFMISSING 2 +#define CFINVALID 3 + +// Generate parser function prototypes. +#define SECTION(__sect) \ + int opt_config_##__sect(struct config_##__sect *, const struct config_node *); +#define ATOM(__type, __name, __default, __parser, __flags, __comment) \ + int __parser(__type *, const struct config_node *); +#define STRING(__size, __name, __default, __parser, __flags, __comment) \ + int __parser(char *, size_t, const struct config_node *); +#define SUB(__sect, __name, __flags) \ + int opt_config_##__sect(struct config_##__sect *, const struct config_node *node); +#define SUBP(__sect, __name, __parser, __flags) \ + int __parser(struct config_##__sect *, const struct config_node *node); +#define SECTION_END +#define LIST(__sect, __type, __size, __parser, __comment) \ + int opt_config_##__sect(struct config_##__sect *, const struct config_node *); \ + int __parser(__type *, const struct config_node *); +#include "config_schema.h" +#undef SECTION +#undef ATOM +#undef STRING +#undef SUB +#undef SUBP +#undef SECTION_END +#undef LIST + +int opt_boolean(int *booleanp, const struct config_node *node); +int opt_absolute_path(char *str, size_t len, const struct config_node *node); +int opt_debugflags(debugflags_t *flagsp, const struct config_node *node); +int opt_rhizome_peer(struct config_rhizomepeer *, const struct config_node *node); +int opt_str_nonempty(char *str, size_t len, const struct config_node *node); +int opt_port(unsigned short *portp, const struct config_node *node); +int opt_uint64_scaled(uint64_t *intp, const struct config_node *node); +int opt_sid(sid_t *sidp, const struct config_node *node); +int opt_interface_type(short *typep, const struct config_node *node); +int opt_pattern_list(struct pattern_list *listp, const struct config_node *node); +int opt_interface_list(struct config_interface_list *listp, const struct config_node *node); + int opt_boolean(int *booleanp, const struct config_node *node) { unsupported_children(node); if (!node->text) - return 1; + return CFMISSING; if (!strcasecmp(node->text, "true") || !strcasecmp(node->text, "yes") || !strcasecmp(node->text, "on") || !strcasecmp(node->text, "1")) { *booleanp = 1; - return 0; + return CFOK; } else if (!strcasecmp(node->text, "false") || !strcasecmp(node->text, "no") || !strcasecmp(node->text, "off") || !strcasecmp(node->text, "0")) { *booleanp = 0; - return 0; + return CFOK; } invalid_text(node, "expecting true|yes|on|1|false|no|off|0"); - return 1; + return CFINVALID; } -int opt_absolute_path(const char **pathp, const struct config_node *node) +int opt_absolute_path(char *str, size_t len, const struct config_node *node) { //DEBUGF("%s", __FUNCTION__); //dump_config_node(node, 1); unsupported_children(node); if (!node->text) - return 1; + return CFMISSING; if (node->text[0] != '/') { invalid_text(node, "must start with '/'"); - return 1; + return CFINVALID; } - if (*pathp) - free((char *) *pathp); // TODO: this should be unnecessary - if ((*pathp = str_emalloc(node->text)) == NULL) - return -1; - return 0; + if (strlen(node->text) >= len) { + invalid_text(node, "string overflow"); + return CFOVERFLOW; + } + strncpy(str, node->text, len); + assert(str[len - 1] == '\0'); + return CFOK; } debugflags_t debugFlagMask(const char *flagname) @@ -377,17 +451,35 @@ int opt_debugflags(debugflags_t *flagsp, const struct config_node *node) *flagsp = 0; *flagsp &= ~clearmask; *flagsp |= setmask; - return 0; + return CFOK; } -int opt_protocol(const char **protocolp, const struct config_node *node) +int opt_protocol(char *str, size_t len, const struct config_node *node) { + //DEBUGF("%s", __FUNCTION__); + //dump_config_node(node, 1); + unsupported_children(node); + if (!node->text) + return CFMISSING; + if (!str_is_uri_scheme(node->text)) { + invalid_text(node, "contains invalid character"); + return CFINVALID; + } + if (strlen(node->text) >= len) { + invalid_text(node, "string overflow"); + return CFOVERFLOW; + } + strncpy(str, node->text, len); + assert(str[len - 1] == '\0'); + return CFOK; } int opt_rhizome_peer(struct config_rhizomepeer *rpeer, const struct config_node *node) { - if (!node->text) + if (!node->text) { + dfl_config_rhizomepeer(rpeer); return opt_config_rhizomepeer(rpeer, node); + } spurious_children(node); const char *protocol; size_t protolen; @@ -397,10 +489,8 @@ int opt_rhizome_peer(struct config_rhizomepeer *rpeer, const struct config_node if (!( str_uri_scheme(node->text, &protocol, &protolen) && str_uri_hierarchical(node->text, &hier, NULL) && str_uri_hierarchical_authority(hier, &auth, NULL)) - ) { - invalid_text(node, "malformed URL"); - return -1; - } + ) + goto invalid; } else { auth = node->text; protocol = "http"; @@ -408,36 +498,62 @@ int opt_rhizome_peer(struct config_rhizomepeer *rpeer, const struct config_node } const char *host; size_t hostlen; - unsigned short port = 0; + unsigned short port = 4110; if (!str_uri_authority_hostname(auth, &host, &hostlen)) - return -1; + goto invalid; str_uri_authority_port(auth, &port); - if (!(rpeer->protocol = strn_emalloc(protocol, protolen))) - return -1; - if (!(rpeer->host = str_emalloc(host))) { - free((char *) rpeer->protocol); - return -1; + if (protolen >= sizeof rpeer->protocol) { + invalid_text(node, "protocol string overflow"); + return CFOVERFLOW; } + if (hostlen >= sizeof rpeer->host) { + invalid_text(node, "hostname string overflow"); + return CFOVERFLOW; + } + strncpy(rpeer->protocol, protocol, protolen)[protolen] = '\0'; + strncpy(rpeer->host, host, hostlen)[hostlen] = '\0'; rpeer->port = port; - return 0; + return CFOK; +invalid: + invalid_text(node, "malformed URL"); + return CFINVALID; } -int opt_host(const char **hostp, const struct config_node *node) +int opt_str_nonempty(char *str, size_t len, const struct config_node *node) { //DEBUGF("%s", __FUNCTION__); //dump_config_node(node, 1); unsupported_children(node); if (!node->text) - return 1; + return CFMISSING; if (!node->text[0]) { - invalid_text(node, "empty host name"); - return 1; + invalid_text(node, "empty string"); + return CFINVALID; } - if (*hostp) - free((char *) *hostp); // TODO: this should be unnecessary - if ((*hostp = str_emalloc(node->text)) == NULL) - return -1; - return 0; + if (strlen(node->text) >= len) { + invalid_text(node, "string overflow"); + return CFOVERFLOW; + } + strncpy(str, node->text, len); + assert(str[len - 1] == '\0'); + return CFOK; +} + +int opt_uint64_scaled(uint64_t *intp, const struct config_node *node) +{ + //DEBUGF("%s", __FUNCTION__); + //dump_config_node(node, 1); + unsupported_children(node); + if (!node->text) + return CFMISSING; + uint64_t result; + const char *end; + if (!str_to_uint64_scaled(node->text, 10, &result, &end)) { + invalid_text(node, "invalid scaled unsigned integer"); + return CFINVALID; + } + *intp = result; + return CFOK; } int opt_port(unsigned short *portp, const struct config_node *node) @@ -446,7 +562,7 @@ int opt_port(unsigned short *portp, const struct config_node *node) //dump_config_node(node, 1); unsupported_children(node); if (!node->text) - return 1; + return CFMISSING; unsigned short port = 0; const char *p; for (p = node->text; isdigit(*p); ++p) { @@ -455,14 +571,217 @@ int opt_port(unsigned short *portp, const struct config_node *node) if (port / 10 != oport) break; } - if (*p) { + if (*p || port == 0) { invalid_text(node, "invalid port number"); - return 1; + return CFINVALID; } *portp = port; - return 1; + return CFOK; } +int opt_sid(sid_t *sidp, const struct config_node *node) +{ + //DEBUGF("%s", __FUNCTION__); + //dump_config_node(node, 1); + unsupported_children(node); + if (!node->text) + return CFMISSING; + sid_t sid; + if (!str_is_subscriber_id(node->text)) { + invalid_text(node, "invalid subscriber ID"); + return CFINVALID; + } + size_t n = fromhex(sidp->binary, node->text, SID_SIZE); + assert(n == SID_SIZE); + return CFOK; +} + +int opt_interface_type(short *typep, const struct config_node *node) +{ + //DEBUGF("%s", __FUNCTION__); + //dump_config_node(node, 1); + unsupported_children(node); + if (!node->text) + return CFMISSING; + if (strcasecmp(node->text, "ethernet") == 0) { + *typep = OVERLAY_INTERFACE_ETHERNET; + return CFOK; + } + if (strcasecmp(node->text, "wifi") == 0) { + *typep = OVERLAY_INTERFACE_WIFI; + return CFOK; + } + if (strcasecmp(node->text, "catear") == 0) { + *typep = OVERLAY_INTERFACE_PACKETRADIO; + return CFOK; + } + if (strcasecmp(node->text, "other") == 0) { + *typep = OVERLAY_INTERFACE_UNKNOWN; + return CFOK; + } + invalid_text(node, "invalid network interface type"); + return CFINVALID; +} + +int opt_pattern_list(struct pattern_list *listp, const struct config_node *node) +{ + //DEBUGF("%s", __FUNCTION__); + //dump_config_node(node, 1); + unsupported_children(node); + if (!node->text) + return CFMISSING; + struct pattern_list list; + memset(&list, 0, sizeof list); + const char *word = NULL; + const char *p; + for (p = node->text; ; ++p) { + if (!*p || isspace(*p) || *p == ',') { + if (word) { + size_t len = p - word; + if (list.patc >= NELS(list.patv) || len >= sizeof(list.patv[list.patc])) { + invalid_text(node, "string overflow"); + return CFOVERFLOW; + } + strncpy(list.patv[list.patc++], word, len)[len] = '\0'; + word = NULL; + } + if (!*p) + break; + } else if (!word) + word = p; + } + assert(word == NULL); + *listp = list; + return CFOK; +} + +int opt_interface_list(struct config_interface_list *listp, const struct config_node *node) +{ + if (!node->text) { + dfl_config_interface_list(listp); + return opt_config_interface_list(listp, node); + } + spurious_children(node); + invalid_text(node, "NOT IMPLEMENTED"); + return CFINVALID; +} + +void missing_node(const struct config_node *parent, const char *key); +void unsupported_node(const struct config_node *node); +void unsupported_tree(const struct config_node *node); +void list_overflow(const struct config_node *node); +void list_omit_element(const struct config_node *node); + +// Schema item flags. +#define __MANDATORY 1 + +// Schema flag symbols, to be used in the '__flags' macro arguments. +#define MANDATORY |__MANDATORY + +// Generate default functions, dfl_config_SECTION() +#define SECTION(__sect) \ + int dfl_config_##__sect(struct config_##__sect *s) { +#define ATOM(__type, __name, __default, __parser, __flags, __comment) \ + s->__name = (__default); +#define STRING(__size, __name, __default, __parser, __flags, __comment) \ + strncpy(s->__name, (__default), (__size))[(__size)] = '\0'; +#define SUB(__sect, __name, __flags) \ + dfl_config_##__sect(&s->__name); +#define SUBP(__sect, __name, __parser, __flags) \ + dfl_config_##__sect(&s->__name); +#define SECTION_END \ + return 0; \ + } +#define LIST(__sect, __type, __size, __parser, __comment) \ + int dfl_config_##__sect(struct config_##__sect *s) { \ + s->listc = 0; \ + return 0; \ + } +#include "config_schema.h" +#undef SECTION +#undef ATOM +#undef STRING +#undef SUB +#undef SUBP +#undef SECTION_END +#undef LIST + +// Generate parsing functions, opt_config_SECTION() +#define SECTION(__sect) \ + int opt_config_##__sect(struct config_##__sect *s, const struct config_node *node) { \ + if (node->text) unsupported_node(node); \ + int result = CFOK; \ + char used[node->nodc]; \ + memset(used, 0, node->nodc); +#define __ITEM(__name, __flags, __parseexpr) \ + { \ + int i = get_child(node, #__name); \ + if (i != -1) { \ + used[i] = 1; \ + const struct config_node *child = node->nodv[i]; \ + int ret = (__parseexpr); \ + switch (ret) { \ + case CFERROR: return CFERROR; \ + case CFMISSING: i = -1; break; \ + default: if (result < ret) result = ret; break; \ + } \ + } \ + if (i == -1 && ((0 __flags) & __MANDATORY)) { \ + missing_node(node, #__name); \ + if (result < CFMISSING) result = CFMISSING; \ + } \ + } +#define ATOM(__type, __name, __default, __parser, __flags, __comment) \ + __ITEM(__name, __flags, __parser(&s->__name, child)) +#define STRING(__size, __name, __default, __parser, __flags, __comment) \ + __ITEM(__name, __flags, __parser(s->__name, (__size) + 1, child)) +#define SUB(__sect, __name, __flags) \ + __ITEM(__name, __flags, opt_config_##__sect(&s->__name, child)) +#define SUBP(__sect, __name, __parser, __flags) \ + __ITEM(__name, __flags, __parser(&s->__name, child)) +#define SECTION_END \ + { \ + int i; \ + for (i = 0; i < node->nodc; ++i) \ + if (!used[i]) \ + unsupported_tree(node->nodv[i]); \ + } \ + return result; \ + } +#define LIST(__sect, __type, __size, __parser, __comment) \ + int opt_config_##__sect(struct config_##__sect *s, const struct config_node *node) { \ + if (node->text) unsupported_node(node); \ + int result = CFOK; \ + int i; \ + for (i = 0; i < node->nodc && s->listc < NELS(s->listv); ++i) { \ + const struct config_node *elt = node->nodv[i]; \ + int ret = __parser(&s->listv[s->listc].value, elt); \ + switch (ret) { \ + case CFERROR: return CFERROR; \ + case CFOK: \ + strncpy(s->listv[s->listc].label, elt->key, sizeof s->listv[s->listc].label - 1)\ + [sizeof s->listv[s->listc].label - 1] = '\0'; \ + ++s->listc; \ + break; \ + default: \ + list_omit_element(elt); \ + break; \ + } \ + } \ + for (; i < node->nodc; ++i) { \ + if (result < CFOVERFLOW) result = CFOVERFLOW; \ + list_overflow(node->nodv[i]); \ + } \ + return result; \ + } +#include "config_schema.h" +#undef SECTION +#undef ATOM +#undef STRING +#undef SUB +#undef SUBP +#undef SECTION_END +#undef LIST int main(int argc, char **argv) { int i; @@ -487,19 +806,21 @@ int main(int argc, char **argv) close(fd); //dump_config_node(root, 0); struct config_main config; + memset(&config, 0, sizeof config); dfl_config_main(&config); opt_config_main(&config, root); free_config_node(root); free(buf); - DEBUGF("config.log.file = %s", config.log.file ? alloca_str_toprint(config.log.file) : "NULL"); + DEBUGF("config.log.file = %s", alloca_str(config.log.file)); DEBUGF("config.log.show_pid = %d", config.log.show_pid); DEBUGF("config.log.show_time = %d", config.log.show_time); DEBUGF("config.debug = %llx", (unsigned long long) config.debug); + DEBUGF("config.directory.service = %s", alloca_tohex(config.directory.service.binary, SID_SIZE)); int j; for (j = 0; j < config.rhizome.direct.peer.listc; ++j) { DEBUGF("config.rhizome.direct.peer.%s", config.rhizome.direct.peer.listv[j].label); - DEBUGF(" .protocol = %s", alloca_str_toprint(config.rhizome.direct.peer.listv[j].value.protocol)); - DEBUGF(" .host = %s", alloca_str_toprint(config.rhizome.direct.peer.listv[j].value.host)); + DEBUGF(" .protocol = %s", alloca_str(config.rhizome.direct.peer.listv[j].value.protocol)); + DEBUGF(" .host = %s", alloca_str(config.rhizome.direct.peer.listv[j].value.host)); DEBUGF(" .port = %u", config.rhizome.direct.peer.listv[j].value.port); } }