mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-04-07 19:14:17 +00:00
Improve new config prototype code
Make config LISTs more flexible. Start implementing existing config options: directory.service (SID parsing), interfaces (better list logic).
This commit is contained in:
parent
ef03eb1a3e
commit
70531fccb6
144
config.h
144
config.h
@ -1,139 +1,35 @@
|
||||
#include <netinet/in.h>
|
||||
#include <stdint.h>
|
||||
#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
|
||||
|
||||
|
@ -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
|
||||
|
441
config_test.c
441
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user