Merge branch 'newconfig' into 'master'

Conflicts:
	commandline.c
	conf.h
	dataformats.c
	log.h
	overlay_address.c
	overlay_interface.c
	packetformats.c
	rhizome.c
	serval.h
	tests/directory_service
	vomp_console.c
This commit is contained in:
Andrew Bettison 2012-12-07 14:09:55 +10:30
commit 8425882ffc
59 changed files with 4345 additions and 1560 deletions

View File

@ -2,41 +2,52 @@ NACL_BASE= nacl/src
include $(NACL_BASE)/nacl.mk
SERVAL_BASE=
include sourcefiles.mk
SRCS= $(NACL_SOURCES) $(SERVAL_SOURCES)
include headerfiles.mk
HAVE_ALSA= @HAVE_ALSA@
ifeq (HAVE_ALSA,1)
SRCS+= audio_alsa.c
SERVAL_SOURCES+= $(SERVAL_BASE)/audio_alsa.c
endif
HAVE_VOIPTEST= @HAVE_VOIPTEST@
ifeq ($(HAVE_VOIPTEST), 1)
SERVAL_SOURCES+= $(SERVAL_BASE)/pa_phone.c
VOIPTEST_CFLAGS=-DHAVE_VOIPTEST=1
endif
SRCS= $(NACL_SOURCES) $(SERVAL_SOURCES)
MONITORCLIENTSRCS=conf.c \
conf_om.c \
conf_parse.c \
conf_schema.c \
log.c \
mkdir.c \
os.c \
mem.c \
monitor-client.c \
instance.c \
net.c \
str.c \
strbuf.c \
strbuf_helpers.c
MDPCLIENTSRCS=conf.c \
conf_om.c \
conf_parse.c \
conf_schema.c \
dataformats.c \
mkdir.c \
os.c \
mem.c \
log.c \
mdp_client.c \
instance.c \
net.c \
str.c \
strbuf.c \
strbuf_helpers.c
HAVE_VOIPTEST= @HAVE_VOIPTEST@
ifeq ($(HAVE_VOIPTEST), 1)
SRCS+= pa_phone.c
VOIPTEST_CFLAGS=-DHAVE_VOIPTEST=1
endif
OBJS= $(SRCS:.c=.o)
SERVAL_OBJS= $(SERVAL_SOURCES:.c=.o)
MONITORCLIENTOBJS= $(MONITORCLIENTSRCS:.c=.o)
MDPCLIENTOBJS= $(MDPCLIENTSRCS:.c=.o)
@ -60,10 +71,14 @@ sqlite-amalgamation-3070900/sqlite3.o: sqlite-amalgamation-3070900/sqlite3.c
@echo CC $<
@$(CC) $(CFLAGS) $(DEFS) -c $< -o sqlite-amalgamation-3070900/sqlite3.o
%.o: %.c $(HDRS)
%.o: %.c
@echo CC $<
@$(CC) $(CFLAGS) $(DEFS) -c $< -o $@
$(SERVAL_OBJS): $(HDRS)
$(MONITORCLIENTOBJS): $(HDRS)
$(MDPCLIENTOBJS): $(HDRS)
servald: $(OBJS)
@echo LINK $@
@$(CC) $(CFLAGS) -Wall -o $@ $(OBJS) $(LDFLAGS)

17
cli.c
View File

@ -5,7 +5,7 @@
#include "serval.h"
#include "rhizome.h"
int cli_usage(struct command_line_option *options) {
int cli_usage(const struct command_line_option *options) {
printf("Usage:\n");
int i,j;
for(i=0;options[i].function;i++) {
@ -16,7 +16,8 @@ int cli_usage(struct command_line_option *options) {
return 0;
}
int cli_execute(const char *argv0, int argc, const char *const *args, struct command_line_option *options, void *context){
int cli_parse(const int argc, const char *const *args, const struct command_line_option *options)
{
int ambiguous=0;
int cli_call=-1;
int i;
@ -82,12 +83,16 @@ int cli_execute(const char *argv0, int argc, const char *const *args, struct com
INFO("Use \"help\" command to see a list of valid commands");
return -1;
}
/* Otherwise, make call */
return options[cli_call].function(argc, args, &options[cli_call], context);
return cli_call;
}
int cli_arg(int argc, const char *const *argv, struct command_line_option *o, char *argname, const char **dst, int (*validator)(const char *arg), char *defaultvalue)
int cli_invoke(const struct command_line_option *option, const int argc, const char *const *args, void *context)
{
return option->function(argc, args, option, context);
}
int cli_arg(int argc, const char *const *argv, const struct command_line_option *o, char *argname, const char **dst, int (*validator)(const char *arg), char *defaultvalue)
{
int arglen = strlen(argname);
int i;

16
cli.h
View File

@ -2,18 +2,20 @@
#define __SERVALD_CLI_H
typedef struct command_line_option {
int (*function)(int argc, const char *const *argv, struct command_line_option *o, void *context);
int (*function)(int argc, const char *const *argv, const struct command_line_option *o, void *context);
const char *words[32]; // 32 words should be plenty!
unsigned long long flags;
#define CLIFLAG_NONOVERLAY (1<<0) /* Uses a legacy IPv4 DNA call instead of overlay mnetwork */
#define CLIFLAG_STANDALONE (1<<1) /* Cannot be issued to a running instance */
#define CLIFLAG_NONOVERLAY (1<<0) /* Uses a legacy IPv4 DNA call instead of overlay mnetwork */
#define CLIFLAG_STANDALONE (1<<1) /* Cannot be issued to a running instance */
#define CLIFLAG_PERMISSIVE_CONFIG (1<<2) /* No error on bad configuration file */
const char *description; // describe this invocation
} command_line_option;
int cli_usage(command_line_option *options);
int cli_execute(const char *argv0, int argc, const char *const *args, command_line_option *options, void *context);
int cli_arg(int argc, const char *const *argv, command_line_option *o, char *argname, const char **dst, int (*validator)(const char *arg), char *defaultvalue);
int cli_usage(const command_line_option *options);
int cli_parse(const int argc, const char *const *args, const struct command_line_option *options);
int cli_invoke(const struct command_line_option *option, const int argc, const char *const *args, void *context);
int cli_arg(int argc, const char *const *argv, const command_line_option *o, char *argname, const char **dst, int (*validator)(const char *arg), char *defaultvalue);
int cli_lookup_did(const char *text);
int cli_absolute_path(const char *arg);
@ -27,4 +29,4 @@ int cli_optional_did(const char *text);
#endif
#endif

View File

@ -35,6 +35,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <jni.h>
#endif
#include "serval.h"
#include "conf.h"
#include "rhizome.h"
#include "strbuf.h"
#include "str.h"
@ -44,7 +45,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
extern struct command_line_option command_line_options[];
int commandline_usage(int argc, const char *const *argv, struct command_line_option *o, void *context){
int commandline_usage(int argc, const char *const *argv, const struct command_line_option *o, void *context){
printf("Serval Mesh version <version>.\n");
return cli_usage(command_line_options);
}
@ -189,9 +190,19 @@ int parseCommandLine(const char *argv0, int argc, const char *const *args)
{
fd_clearstats();
IN();
confSetDebugFlags();
int result = cli_execute(argv0, argc, args, command_line_options, NULL);
int result = cli_parse(argc, args, command_line_options);
if (result != -1) {
const struct command_line_option *option = &command_line_options[result];
if (option->flags & CLIFLAG_PERMISSIVE_CONFIG)
cf_reload_permissive();
else
cf_reload();
result = cli_invoke(option, argc, args, NULL);
} else {
cf_reload();
}
/* clean up after ourselves */
overlay_mdp_client_done();
rhizome_close_db();
@ -322,7 +333,7 @@ void cli_flush()
fflush(stdout);
}
int app_echo(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_echo(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int i = 1;
@ -377,19 +388,15 @@ void lookup_send_request(unsigned char *srcsid, int srcport, unsigned char *dsts
/* Also send an encrypted unicast request to a configured directory service */
if (!dstsid){
const char *directory_service = confValueGet("directory.service", NULL);
if (directory_service){
if (stowSid(mdp.out.dst.sid, 0, directory_service)==-1){
WHYF("Invalid directory server SID %s", directory_service);
}else{
mdp.packetTypeAndFlags=MDP_TX;
overlay_mdp_send(&mdp,0,0);
}
if (!is_sid_any(config.directory.service.binary)) {
memcpy(mdp.out.dst.sid, config.directory.service.binary, SID_SIZE);
mdp.packetTypeAndFlags=MDP_TX;
overlay_mdp_send(&mdp,0,0);
}
}
}
int app_dna_lookup(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_dna_lookup(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
@ -506,7 +513,7 @@ int app_dna_lookup(int argc, const char *const *argv, struct command_line_option
return 0;
}
int app_server_start(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_server_start(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* Process optional arguments */
@ -571,10 +578,8 @@ int app_server_start(int argc, const char *const *argv, struct command_line_opti
return -1;
/* Now that we know our instance path, we can ask for the default set of
network interfaces that we will take interest in. */
const char *interfaces = confValueGet("interfaces", "");
if (!interfaces[0])
WHY("No network interfaces configured (empty 'interfaces' config setting)");
overlay_interface_args(interfaces);
if (config.interfaces.ac == 0)
WARN("No network interfaces configured (empty 'interfaces' config option)");
if (pid == -1)
pid = server_pid();
if (pid < 0)
@ -599,7 +604,7 @@ int app_server_start(int argc, const char *const *argv, struct command_line_opti
return server(NULL);
const char *dir = getenv("SERVALD_SERVER_CHDIR");
if (!dir)
dir = confValueGet("server.chdir", "/");
dir = config.server.chdir;
switch (cpid = fork()) {
case -1:
/* Main process. Fork failed. There is no child process. */
@ -684,7 +689,7 @@ int app_server_start(int argc, const char *const *argv, struct command_line_opti
return ret;
}
int app_server_stop(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_server_stop(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int pid, tries, running;
@ -745,7 +750,7 @@ int app_server_stop(int argc, const char *const *argv, struct command_line_optio
return 0;
}
int app_server_status(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_server_status(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int pid;
@ -772,7 +777,7 @@ int app_server_status(int argc, const char *const *argv, struct command_line_opt
return pid > 0 ? 0 : 1;
}
int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_mdp_ping(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid, *count;
@ -919,13 +924,29 @@ int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *
return ret;
}
int app_config_set(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_config_schema(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var, *val;
if ( cli_arg(argc, argv, o, "variable", &var, is_configvarname, NULL)
|| cli_arg(argc, argv, o, "value", &val, NULL, ""))
if (create_serval_instance_dir() == -1)
return -1;
struct cf_om_node *root = NULL;
if (cf_sch_config_main(&root) == -1)
return -1;
struct cf_om_iterator it;
for (cf_om_iter_start(&it, root); it.node; cf_om_iter_next(&it))
if (it.node->text || it.node->nodc == 0) {
cli_puts(it.node->fullkey);
cli_delim("=");
if (it.node->text)
cli_puts(it.node->text);
cli_delim("\n");
}
return 0;
}
int app_config_set(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
if (create_serval_instance_dir() == -1)
return -1;
// <kludge>
@ -941,26 +962,42 @@ int app_config_set(int argc, const char *const *argv, struct command_line_option
// Bingo, the old version of servald.conf is what remains. This kludge intervenes in step 4, by
// reading the new servald.conf into the memory buffer before applying the "rhizome.enable" set
// value and overwriting.
confReloadIfChanged();
if (cf_om_reload() == -1)
return -1;
// </kludge>
return confValueSet(var, val) == -1 ? -1 : confWrite();
const char *var[argc - 1];
const char *val[argc - 1];
int nvar = 0;
int i;
for (i = 1; i < argc; ++i) {
int iv;
if (strcmp(argv[i], "set") == 0) {
if (i + 2 > argc)
return WHYF("malformed command at argv[%d]: 'set' not followed by two arguments", i);
var[nvar] = argv[iv = ++i];
val[nvar] = argv[++i];
} else if (strcmp(argv[i], "del") == 0) {
if (i + 1 > argc)
return WHYF("malformed command at argv[%d]: 'del' not followed by one argument", i);
var[nvar] = argv[iv = ++i];
val[nvar] = NULL;
} else
return WHYF("malformed command at argv[%d]: unsupported action '%s'", i, argv[i]);
if (!is_configvarname(var[nvar]))
return WHYF("malformed command at argv[%d]: '%s' is not a valid config option name", iv, var[nvar]);
++nvar;
}
for (i = 0; i < nvar; ++i)
if (cf_om_set(&cf_om_root, var[i], val[i]) == -1)
return -1;
if (cf_om_save() == -1)
return -1;
if (cf_reload() == -1) // logs an error if the new config is bad
return 2;
return 0;
}
int app_config_del(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var;
if (cli_arg(argc, argv, o, "variable", &var, is_configvarname, NULL))
return -1;
if (create_serval_instance_dir() == -1)
return -1;
// <kludge> See app_config_set()
confReloadIfChanged();
// </kludge>
return confValueSet(var, NULL) == -1 ? -1 : confWrite();
}
int app_config_get(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_config_get(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var;
@ -968,8 +1005,10 @@ int app_config_get(int argc, const char *const *argv, struct command_line_option
return -1;
if (create_serval_instance_dir() == -1)
return -1;
if (cf_om_reload() == -1)
return -1;
if (var) {
const char *value = confValueGet(var, NULL);
const char *value = cf_om_get(cf_om_root, var);
if (value) {
cli_puts(var);
cli_delim("=");
@ -977,21 +1016,20 @@ int app_config_get(int argc, const char *const *argv, struct command_line_option
cli_delim("\n");
}
} else {
int n = confVarCount();
if (n == -1)
return -1;
unsigned int i;
for (i = 0; i != n; ++i) {
cli_puts(confVar(i));
cli_delim("=");
cli_puts(confValue(i));
cli_delim("\n");
struct cf_om_iterator it;
for (cf_om_iter_start(&it, cf_om_root); it.node; cf_om_iter_next(&it)) {
if (it.node->text) {
cli_puts(it.node->fullkey);
cli_delim("=");
cli_puts(it.node->text);
cli_delim("\n");
}
}
}
return 0;
}
int app_rhizome_hash_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_hash_file(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* compute hash of file. We do this without a manifest, so it will necessarily
@ -1006,7 +1044,7 @@ int app_rhizome_hash_file(int argc, const char *const *argv, struct command_line
return 0;
}
int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_add_file(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *filepath, *manifestpath, *authorSidHex, *pin, *bskhex;
@ -1246,7 +1284,7 @@ int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_
return ret;
}
int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_import_bundle(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *filepath, *manifestpath;
@ -1258,7 +1296,7 @@ int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_
return status;
}
int app_rhizome_extract_manifest(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_extract_manifest(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pins, *manifestid, *manifestpath;
@ -1302,7 +1340,7 @@ int app_rhizome_extract_manifest(int argc, const char *const *argv, struct comma
return ret;
}
int app_rhizome_extract_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_extract_file(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *fileid, *filepath, *keyhex;
@ -1332,7 +1370,7 @@ int app_rhizome_extract_file(int argc, const char *const *argv, struct command_l
return ret;
}
int app_rhizome_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_rhizome_list(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pins, *service, *sender_sid, *recipient_sid, *offset, *limit;
@ -1352,7 +1390,7 @@ int app_rhizome_list(int argc, const char *const *argv, struct command_line_opti
return rhizome_list_manifests(service, sender_sid, recipient_sid, atoi(offset), atoi(limit));
}
int app_keyring_create(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_keyring_create(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin;
@ -1362,7 +1400,7 @@ int app_keyring_create(int argc, const char *const *argv, struct command_line_op
return 0;
}
int app_keyring_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_keyring_list(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pins;
@ -1389,7 +1427,7 @@ int app_keyring_list(int argc, const char *const *argv, struct command_line_opti
return 0;
}
int app_keyring_add(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_keyring_add(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin;
@ -1434,7 +1472,7 @@ int app_keyring_add(int argc, const char *const *argv, struct command_line_optio
return 0;
}
int app_keyring_set_did(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_keyring_set_did(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid, *did, *pin, *name;
@ -1463,7 +1501,7 @@ int app_keyring_set_did(int argc, const char *const *argv, struct command_line_o
return 0;
}
int app_id_self(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_id_self(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* List my own identities */
@ -1510,7 +1548,8 @@ int app_id_self(int argc, const char *const *argv, struct command_line_option *o
return 0;
}
int app_count_peers(int argc, const char *const *argv, struct command_line_option *o, void *context){
int app_count_peers(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
overlay_mdp_frame a;
bzero(&a, sizeof(overlay_mdp_frame));
a.packetTypeAndFlags=MDP_GETADDRS;
@ -1526,7 +1565,7 @@ int app_count_peers(int argc, const char *const *argv, struct command_line_optio
return 0;
}
int app_crypt_test(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_crypt_test(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
unsigned char nonce[crypto_box_curve25519xsalsa20poly1305_NONCEBYTES];
@ -1689,7 +1728,7 @@ int app_crypt_test(int argc, const char *const *argv, struct command_line_option
return 0;
}
int app_node_info(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_node_info(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid;
@ -1736,7 +1775,7 @@ int app_node_info(int argc, const char *const *argv, struct command_line_option
return 0;
}
int app_route_print(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_route_print(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
overlay_mdp_frame mdp;
bzero(&mdp,sizeof(mdp));
@ -1778,7 +1817,7 @@ int app_route_print(int argc, const char *const *argv, struct command_line_optio
return 0;
}
int app_reverse_lookup(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_reverse_lookup(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
const char *sid, *delay;
@ -1874,7 +1913,7 @@ int app_reverse_lookup(int argc, const char *const *argv, struct command_line_op
return 0;
}
int app_network_scan(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_network_scan(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
overlay_mdp_frame mdp;
bzero(&mdp,sizeof(mdp));
@ -1945,11 +1984,13 @@ struct command_line_option command_line_options[]={
"Display information about any running Serval Mesh node."},
{app_mdp_ping,{"mdp","ping","<SID|broadcast>","[<count>]",NULL},CLIFLAG_STANDALONE,
"Attempts to ping specified node via Mesh Datagram Protocol (MDP)."},
{app_config_set,{"config","set","<variable>","<value>",NULL},CLIFLAG_STANDALONE,
"Set specified configuration variable."},
{app_config_del,{"config","del","<variable>",NULL},CLIFLAG_STANDALONE,
"Set specified configuration variable."},
{app_config_get,{"config","get","[<variable>]",NULL},CLIFLAG_STANDALONE,
{app_config_schema,{"config","schema",NULL},CLIFLAG_STANDALONE|CLIFLAG_PERMISSIVE_CONFIG,
"Dump configuration schema."},
{app_config_set,{"config","set","<variable>","<value>","...",NULL},CLIFLAG_STANDALONE|CLIFLAG_PERMISSIVE_CONFIG,
"Set and del specified configuration variables."},
{app_config_set,{"config","del","<variable>","...",NULL},CLIFLAG_STANDALONE|CLIFLAG_PERMISSIVE_CONFIG,
"Del and set specified configuration variables."},
{app_config_get,{"config","get","[<variable>]",NULL},CLIFLAG_STANDALONE|CLIFLAG_PERMISSIVE_CONFIG,
"Get specified configuration variable."},
{app_vomp_console,{"console",NULL},0,
"Test phone call life-cycle from the console"},

661
conf.c
View File

@ -1,6 +1,6 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010-2012 Paul Gardner-Stephen
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -23,208 +23,107 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "conf.h"
#include "log.h"
#include "str.h"
/* This predicate function defines the constraints on configuration option names.
Valid:
foo
foo.bar
foo.bar.chow
_word
word1
word_1
Invalid:
foo.
.foo
1foo
foo.bar.
12
1.2.3
foo bar
@author Andrew Bettison <andrew@servalproject.com>
*/
int is_configvarname(const char *arg)
{
if (arg[0] == '\0')
return 0;
if (!(isalnum(arg[0]) || arg[0] == '_'))
return 0;
const char *s = arg + 1;
for (; *s; ++s)
if (!(isalnum(*s) || *s == '_' || (*s == '.' && s[-1] != '.')))
return 0;
return s[-1] != '.';
}
#include "mem.h"
#define CONFFILE_NAME "serval.conf"
#define MAX_CONFIG_VARS (100)
#define CONFIG_BUFFER_ALLOCSIZE (1024)
static int busy = 0;
static time_t config_timestamp = 0;
static char *config_buffer = NULL;
static char *config_buffer_top = NULL;
static char *config_buffer_end = NULL;
static unsigned int confc = 0;
static char *confvar[MAX_CONFIG_VARS];
static char *confvalue[MAX_CONFIG_VARS];
struct file_meta {
time_t mtime;
off_t size;
};
#define BUSY_BODY(_type, _return_if_busy, _return) \
do { if (busy) { return (_return_if_busy); } else { busy = 1; _type ret = (_return); busy = 0; return ret; } } while (0)
struct cf_om_node *cf_om_root = NULL;
static struct file_meta conffile_meta = { .mtime = -1, .size = -1 };
static char *grow_config_buffer(size_t needed)
int cf_limbo = 1;
struct config_main config;
static struct file_meta config_meta = { .mtime = -1, .size = -1 };
static const char *conffile_path()
{
size_t cursize = config_buffer_end - config_buffer;
size_t used = config_buffer_top - config_buffer;
size_t newsize = used + needed;
if (newsize > cursize) {
// Round up to nearest multiple of CONFIG_BUFFER_ALLOCSIZE.
newsize = newsize + CONFIG_BUFFER_ALLOCSIZE - ((newsize - 1) % CONFIG_BUFFER_ALLOCSIZE + 1);
char *newbuf = realloc(config_buffer, newsize);
if (newbuf == NULL) {
WHYF_perror("realloc(%llu)", newsize);
return NULL;
}
ssize_t dif = newbuf - config_buffer;
unsigned int i;
for (i = 0; i != confc; ++i) {
confvar[i] += dif;
confvalue[i] += dif;
}
config_buffer_end = newbuf + newsize;
config_buffer_top = newbuf + used;
config_buffer = newbuf;
}
char *ret = config_buffer_top;
config_buffer_top += needed;
return ret;
static char path[1024] = "";
if (!path[0] && !FORM_SERVAL_INSTANCE_PATH(path, CONFFILE_NAME))
abort();
return path;
}
static int _read_config()
static int get_meta(const char *path, struct file_meta *metap)
{
char conffile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(conffile, CONFFILE_NAME))
return -1;
size_t size = 0;
confc = 0;
int exists = 0;
FILE *f = fopen(conffile, "r");
if (f == NULL) {
struct stat st;
if (stat(path, &st) == -1) {
if (errno != ENOENT)
return WHYF_perror("fopen(%s)", conffile);
INFOF("non-existent config file %s", conffile);
config_timestamp = 0;
return WHYF_perror("stat(%s)", path);
metap->size = 0;
metap->mtime = -1;
} else {
exists = 1;
struct stat st;
if (fstat(fileno(f), &st) == -1) {
WHYF_perror("fstat(%s)", conffile);
fclose(f);
return -1;
metap->size = st.st_size;
metap->mtime = st.st_mtime;
}
return 0;
}
static int load()
{
const char *path = conffile_path();
struct file_meta meta;
if (get_meta(path, &meta) == -1)
return CFERROR;
char *buf = NULL;
if (meta.mtime == -1)
INFOF("config file %s does not exist", path);
else if (meta.size > CONFIG_FILE_MAX_SIZE) {
WHYF("config file %s is too big (%ld bytes exceeds limit %ld)", path, meta.size, CONFIG_FILE_MAX_SIZE);
return CFERROR;
} else if (meta.size <= 0) {
INFOF("config file %s is zero size", path);
} else {
FILE *f = fopen(path, "r");
if (f == NULL) {
WHYF_perror("fopen(%s)", path);
return CFERROR;
}
if (fseeko(f, (off_t) 0, SEEK_END) == -1) {
WHYF_perror("fseeko(%s, 0, SEEK_END)", conffile);
if ((buf = emalloc(meta.size)) == NULL) {
fclose(f);
return -1;
return CFERROR;
}
off_t tell = ftello(f);
if (tell == -1) {
WHYF_perror("ftello(%s)", conffile);
fclose(f);
return -1;
}
size = tell;
if (fseeko(f, (off_t) 0, SEEK_SET) == -1) {
WHYF_perror("fseeko(%s, 0, SEEK_SET)", conffile);
fclose(f);
return -1;
}
if (grow_config_buffer(size) == NULL) {
fclose(f);
return -1;
}
config_timestamp = 0;
if (fread(config_buffer, size, 1, f) != 1) {
if (fread(buf, meta.size, 1, f) != 1) {
if (ferror(f))
WHYF_perror("fread(%s, %llu)", conffile, (unsigned long long) size);
WHYF_perror("fread(%s, %llu)", path, (unsigned long long) meta.size);
else
WHYF("fread(%s, %llu) hit EOF", conffile, (unsigned long long) size);
free(config_buffer);
config_buffer = NULL;
WHYF("fread(%s, %llu) hit EOF", path, (unsigned long long) meta.size);
free(buf);
fclose(f);
return CFERROR;
}
if (fclose(f) == EOF) {
free(buf);
return WHYF_perror("fclose(%s)", path);
}
INFOF("config file %s successfully read", path);
}
struct cf_om_node *new_root = NULL;
int result = cf_om_parse(path, buf, meta.size, &new_root);
free(buf);
if (result != CFERROR) {
cf_om_free_node(&cf_om_root);
cf_om_root = new_root;
conffile_meta = meta;
}
return result;
}
static int has_changed(const struct file_meta *metap)
{
const char *path = conffile_path();
struct file_meta meta;
if (get_meta(path, &meta) == -1)
return -1;
}
config_timestamp = st.st_mtime;
if (fclose(f) == EOF)
return WHYF_perror("fclose(%s)", conffile);
INFOF("successfully read %s", conffile);
}
config_buffer_top = config_buffer + size;
char *c = config_buffer;
char *e = config_buffer_top;
unsigned int linenum;
char *problem = NULL;
char *extra = "";
for (linenum = 1; !problem && c < e; ++linenum) {
if (*c == '#') {
// skip comment lines
while (c < e && *c != '\n')
++c;
} else if (*c == '\n') {
// skip empty lines
++c;
} else if (c < e - 1 && *c == '\r' && c[1] == '\n') {
// skip empty lines
c += 2;
} else if (confc < MAX_CONFIG_VARS) {
char *var = confvar[confc] = c;
while (c < e && *c != '=' && *c != '\r' && *c != '\n')
++c;
if (c < e && *c == '=') {
*c++ = '\0';
if (is_configvarname(var)) {
confvalue[confc] = c;
while (c < e && *c != '\r' && *c != '\n')
++c;
if (c < e && *c == '\n') {
*c++ = '\0';
++confc;
} else if (c < e - 1 && *c == '\r' && c[1] == '\n') {
*c++ = '\0';
*c++ = '\0';
++confc;
} else {
problem = "missing end-of-line";
}
} else {
problem = "invalid variable name: ";
extra = var;
}
} else {
problem = "missing '='";
}
} else {
problem = "too many variables";
}
}
if (problem)
return WHYF("Error in %s at line %u: %s%s", conffile, linenum, problem, extra);
return exists;
return metap->size != meta.size || metap->mtime != meta.mtime;
}
static int read_config()
int cf_om_load()
{
BUSY_BODY(int, -1, _read_config());
}
/* Return true if any conf...() function is in progress, ie, a configuration fetch function is being
* re-entered. This function allows other modules to avoid this re-entry, typically the logging
* system that uses confValueGet() to set itself up, which in turn can log messages.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int confBusy()
{
return busy;
return load() == CFERROR ? -1 : 0;
}
/* Check if the config file has changed since we last read it, and if so, invalidate the buffer so
@ -236,330 +135,120 @@ int confBusy()
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int _confReloadIfChanged()
int cf_om_reload()
{
char conffile[1024];
if (!FORM_SERVAL_INSTANCE_PATH(conffile, CONFFILE_NAME))
return -1;
struct stat st;
if (stat(conffile, &st) == -1) {
if (errno != ENOENT)
return WHYF_perror("stat(%s)", conffile);
st.st_mtime = 0;
}
if (config_timestamp != st.st_mtime) {
INFOF("detected new version of %s", conffile);
_read_config();
return 1;
}
return 0;
}
int confReloadIfChanged()
{
BUSY_BODY(int, -1, _confReloadIfChanged());
if (!has_changed(&conffile_meta))
return CFOK;
if (conffile_meta.mtime != -1)
INFOF("config file %s -- detected new version", conffile_path());
return cf_om_load();
}
/* Return the number of config options.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int _confVarCount()
int cf_om_save()
{
if (!config_buffer && _read_config() == -1)
return -1;
return confc;
}
int confVarCount()
{
BUSY_BODY(int, -1, _confVarCount());
}
/* Return the string name of the config option with the given index, which must be in the range
* 0..confVarCount(). The returned pointer is only valid until the next call to confVar() or any
* other configuration query function.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static const char *_confVar(unsigned int index)
{
if (!config_buffer && _read_config() == -1)
return NULL;
if (index >= confc) {
WHYF("Config index=%u too big, confc=%u", index, confc);
return NULL;
}
return confvar[index];
}
const char *confVar(unsigned int index)
{
BUSY_BODY(const char *, NULL, _confVar(index));
}
/* Return the string value of the config option with the given index, which must be in the range
* 0..confVarCount(). The returned pointer is only valid until the next call to confVar() or any
* other configuration query function.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static const char *_confValue(unsigned int index)
{
if (!config_buffer && _read_config() == -1)
return NULL;
if (index >= confc) {
WHYF("Config index=%u too big, confc=%u", index, confc);
return NULL;
}
return confvalue[index];
}
const char *confValue(unsigned int index)
{
BUSY_BODY(const char *, NULL, _confValue(index));
}
/* Return the string value of the config option with the given name. The returned pointer is only
* valid until the next call to confVarCount(), confVar(), confValue() or any other configuration
* query function. If the named config option is not defined, then returns the given default value.
* If the named config option cannot be determined for any other reason, then logs the reason and
* returns the given default value.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static const char *_confValueGet(const char *var, const char *defaultValue)
{
if (var == NULL) {
WHYF("NULL var name, returning default value: %s", defaultValue ? defaultValue : "NULL");
return defaultValue;
}
if (!config_buffer && _read_config() == -1) {
if (defaultValue)
WARNF("Config option %s: using default value: %s", var, defaultValue);
return defaultValue;
}
unsigned int i;
for (i = 0; i != confc; ++i)
if (strcasecmp(confvar[i], var) == 0)
return confvalue[i];
return defaultValue;
}
const char *confValueGet(const char *var, const char *defaultValue)
{
BUSY_BODY(const char *, defaultValue, _confValueGet(var, defaultValue));
}
static int _confValueGetBoolean(const char *var, int defaultValue)
{
const char *value = _confValueGet(var, NULL);
if (!value)
return defaultValue;
int flag = confParseBoolean(value, var);
if (flag >= 0)
return flag;
WARNF("Config option %s: using default value %s", var, defaultValue ? "true" : "false");
return defaultValue;
}
int confValueGetBoolean(const char *var, int defaultValue)
{
BUSY_BODY(int, defaultValue, _confValueGetBoolean(var, defaultValue));
}
static int64_t _confValueGetInt64(const char *var, int64_t defaultValue)
{
const char *start = _confValueGet(var, NULL);
if (!start)
return defaultValue;
const char *end = start;
long long value = strtoll(start, (char **)&end, 10);
if (*start && !*end && end != start)
return value;
WARNF("Config option %s: '%s' is not an integer, using default value %lld", var, start, (long long) defaultValue);
return defaultValue;
}
int64_t confValueGetInt64(const char *var, int64_t defaultValue)
{
BUSY_BODY(int64_t, defaultValue, _confValueGetInt64(var, defaultValue));
}
static int64_t _confValueGetInt64Range(const char *var, int64_t defaultValue, int64_t rangemin, int64_t rangemax)
{
int64_t value = _confValueGetInt64(var, defaultValue);
if (value >= rangemin || value <= rangemax)
return value;
WARNF("Config option %s: configured value %lld out of range [%lld,%lld], using default value %lld",
var, (long long) value, (long long) rangemin, (long long) rangemax, (long long) defaultValue);
return defaultValue;
}
int64_t confValueGetInt64Range(const char *var, int64_t defaultValue, int64_t rangemin, int64_t rangemax)
{
BUSY_BODY(int64_t, defaultValue, _confValueGetInt64Range(var, defaultValue, rangemin, rangemax));
}
void confSetDebugFlags()
{
if (config_buffer || read_config() != -1) {
debugflags_t setmask = 0;
debugflags_t clearmask = 0;
int setall = 0;
int clearall = 0;
unsigned int i;
for (i = 0; i != confc; ++i) {
char *var = confvar[i];
if (strncasecmp(var, "debug.", 6) == 0) {
debugflags_t mask = debugFlagMask(var + 6);
if (mask == 0)
WARNF("Unsupported debug option '%s'", var);
else {
int flag = confParseBoolean(confvalue[i], var);
if (flag != -1) {
if (mask == DEBUG_ALL) {
if (flag) {
//DEBUGF("Set all debug flags");
setall = 1;
} else {
//DEBUGF("Clear all debug flags");
clearall = 1;
}
} else {
if (flag) {
//DEBUGF("Set %s", var);
setmask |= mask;
} else {
//DEBUGF("Clear %s", var);
clearmask |= mask;
}
}
}
}
}
}
if (setall)
debug = DEBUG_ALL;
else if (clearall)
debug = 0;
debug &= ~clearmask;
debug |= setmask;
}
}
int confParseBoolean(const char *text, const char *option_name)
{
if (!strcasecmp(text, "on") || !strcasecmp(text, "yes") || !strcasecmp(text, "true") || !strcmp(text, "1"))
return 1;
if (!strcasecmp(text, "off") || !strcasecmp(text, "no") || !strcasecmp(text, "false") || !strcmp(text, "0"))
return 0;
WARNF("Config option %s: invalid boolean value '%s'", option_name, text);
return -1;
}
int confValueSet(const char *var, const char *value)
{
INFOF("var=%s defaultValue=%s",
var ? alloca_str_toprint(var) : "NULL",
value ? alloca_str_toprint(value) : "NULL"
);
if (!config_buffer && read_config() == -1)
return -1;
if (!is_configvarname(var))
return WHYF("Cannot %s %s: invalid variable name", value ? "set" : "delete", var);
if (value == NULL) {
unsigned int i;
for (i = 0; i < confc; ++i) {
if (strcasecmp(var, confvar[i]) == 0) {
--confc;
for (; i < confc; ++i) {
confvar[i] = confvar[i + 1];
confvalue[i] = confvalue[i + 1];
}
return 0;
}
}
} else {
size_t valuelen = strlen(value);
unsigned int i;
for (i = 0; i != confc; ++i) {
if (strcasecmp(var, confvar[i]) == 0) {
char *valueptr = confvalue[i];
if (valuelen > strlen(valueptr)) {
if ((valueptr = grow_config_buffer(valuelen + 1)) == NULL)
return -1;
}
strcpy(confvar[i], var);
confvalue[i] = strcpy(valueptr, value);
return 0;
}
}
if (confc >= MAX_CONFIG_VARS)
return WHYF("Cannot set %s: too many variables", var);
size_t varlen = strlen(var);
char *buf = grow_config_buffer(varlen + 1 + valuelen + 1);
if (buf == NULL)
return -1;
confvar[confc] = strcpy(buf, var);
confvalue[confc] = strcpy(buf + varlen + 1, value);
++confc;
}
INFOF("config set %s = %s", var, value ? alloca_str_toprint(value) : "NULL");
return 0;
}
int confWrite()
{
if (config_buffer) {
char conffile[1024];
if (cf_om_root) {
const char *path = conffile_path();
char tempfile[1024];
FILE *outf = NULL;
if (!FORM_SERVAL_INSTANCE_PATH(conffile, "serval.conf"))
return -1;
if (!FORM_SERVAL_INSTANCE_PATH(tempfile, "serval.conf.temp"))
return -1;
if ((outf = fopen(tempfile, "w")) == NULL)
return WHYF_perror("fopen(%s, \"w\")", tempfile);
unsigned int i;
for (i = 0; i != confc; ++i)
fprintf(outf, "%s=%s\n", confvar[i], confvalue[i]);
struct cf_om_iterator it;
for (cf_om_iter_start(&it, cf_om_root); it.node; cf_om_iter_next(&it))
if (it.node->text)
fprintf(outf, "%s=%s\n", it.node->fullkey, it.node->text);
if (fclose(outf) == EOF)
return WHYF_perror("fclose(%s)", tempfile);
if (rename(tempfile, conffile)) {
WHYF_perror("rename(%s, %s)", tempfile, conffile);
if (rename(tempfile, path)) {
WHYF_perror("rename(%s, %s)", tempfile, path);
unlink(tempfile);
return -1;
}
struct stat st;
if (stat(conffile, &st) == -1)
return WHYF_perror("stat(%s)", conffile);
config_timestamp = st.st_mtime;
INFOF("successfully wrote %s", conffile);
struct file_meta meta;
if (get_meta(path, &meta) == -1)
return -1;
INFOF("successfully wrote %s", path);
conffile_meta = meta;
}
return 0;
}
static char *thisinstancepath = NULL;
const char *serval_instancepath()
int cf_init()
{
if (thisinstancepath)
return thisinstancepath;
const char *instancepath = getenv("SERVALINSTANCE_PATH");
if (!instancepath)
instancepath = DEFAULT_INSTANCE_PATH;
return instancepath;
}
void serval_setinstancepath(const char *instancepath)
{
if (thisinstancepath == NULL)
free(thisinstancepath);
thisinstancepath = strdup(instancepath);
}
int form_serval_instance_path(char *buf, size_t bufsiz, const char *path)
{
if (snprintf(buf, bufsiz, "%s/%s", serval_instancepath(), path) < bufsiz)
return 1;
WHYF("Cannot form pathname \"%s/%s\" -- buffer too small (%lu bytes)", serval_instancepath(), path, (unsigned long)bufsiz);
cf_limbo = 1;
if (cf_dfl_config_main(&config) == CFERROR)
return -1;
debug = config.debug;
return 0;
}
int create_serval_instance_dir() {
return mkdirs(serval_instancepath(), 0700);
static int load_and_parse(int permissive)
{
int result = CFOK;
if (cf_limbo)
result = cf_dfl_config_main(&config);
if (result == CFOK) {
result = load();
if (result == CFOK || result == CFEMPTY) {
result = CFOK;
struct config_main new_config;
memset(&new_config, 0, sizeof new_config);
result = cf_dfl_config_main(&new_config);
if (result == CFOK) {
result = cf_om_root ? cf_opt_config_main(&new_config, cf_om_root) : CFEMPTY;
if (result == CFOK || result == CFEMPTY) {
result = CFOK;
config = new_config;
config_meta = conffile_meta;
cf_limbo = 0;
} else if (result != CFERROR) {
result &= ~CFEMPTY;
config = new_config;
cf_limbo = 0;
WARN("limping along with incomplete configuration");
}
}
}
}
debug = config.debug;
if (result == CFOK)
return 0;
cf_limbo = 0; // let log messages out
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, result);
if (!permissive)
return WHYF("config file %s not loaded -- %s", conffile_path(), strbuf_str(b));
WARNF("config file %s loaded despite problems -- %s", conffile_path(), strbuf_str(b));
return 0;
}
static int reload_and_parse(int permissive)
{
if (!cf_limbo && cf_om_root) {
if (!has_changed(&config_meta))
return 0;
INFOF("config file %s reloading", conffile_path());
}
return load_and_parse(permissive);
}
int cf_load()
{
return load_and_parse(0);
}
int cf_load_permissive()
{
return load_and_parse(1);
}
int cf_reload()
{
return reload_and_parse(0);
}
int cf_reload_permissive()
{
return reload_and_parse(1);
}

591
conf.h
View File

@ -1,5 +1,6 @@
/*
Copyright (C) 2010-2012 Paul Gardner-Stephen, Serval Project.
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@ -16,50 +17,550 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
/*
DEFAULT_INSTANCE_PATH can be set via the configure option
--enable-instance-path=<path>
*/
#ifdef INSTANCE_PATH
#define DEFAULT_INSTANCE_PATH INSTANCE_PATH
#else
#ifdef ANDROID
#define DEFAULT_INSTANCE_PATH "/data/data/org.servalproject/var/serval-node"
#else
#define DEFAULT_INSTANCE_PATH "/var/serval-node"
#endif
#endif
/* Handy statement for forming a path to an instance file in a char buffer whose declaration
* is in scope (so that sizeof(buf) will work). Evaluates to true if the pathname fitted into
* the provided buffer, false (0) otherwise (after logging an error).
/* This file defines the internal API to the configuration file. See "conf_schema.h" for the
* definition of the configuration schema, which is used to generate these API components.
*
* Each STRUCT(NAME, ...) schema declaration generates the following data declaration:
*
* struct config_NAME {
* ...
* };
*
* A C struct definition containing exactly one element per schema declaration inside the
* STRUCT..END_STRUCT block, in the defined order. The TYPE and NAME of each element depends
* on the schema declaration that produces it:
*
* ATOM(TYPE, bar, ...)
* NODE(TYPE, bar, ...)
*
* TYPE bar;
*
* STRING(SIZE, bar, ...)
*
* char bar[SIZE+1];
*
* SUB_STRUCT(NAME, bar, ...)
* NODE_STRUCT(NAME, bar, ...)
*
* struct config_NAME bar;
*
* Each ARRAY(NAME, ...) ... END_ARRAY(SIZE) schema declaration produces the following data
* declaration:
*
* struct config_NAME {
* unsigned ac;
* struct config_NAME__element {
* KEY-DECLARATION;
* VALUE-DECLARATION;
* } av[SIZE];
* };
*
* A C struct definition containing a count 'ac' of the number of array elements 0..SIZE-1,
* and 'av' an array of element values, each one consisting of a key and a value:
*
* KEY_ATOM(TYPE, ...)
*
* TYPE key;
*
* KEY_STRING(SIZE, ...)
*
* char key[SIZE+1];
*
* VALUE_ATOM(NAME, SIZE, LABELLEN, TYPE, ...)
* VALUE_NODE(NAME, SIZE, LABELLEN, TYPE, ...)
*
* TYPE value;
*
* VALUE_STRING(STRINGSIZE, ...)
*
* char value[STRINGSIZE+1];
*
* VALUE_SUB_STRUCT(STRUCTNAME)
* VALUE_NODE_STRUCT(STRUCTNAME, ...)
*
* struct config_STRUCTNAME value;
*
* Each STRUCT(NAME, ...) and ARRAY(NAME, ...) schema declaration generates the following API
* functions:
*
* - int cf_dfl_config_NAME(struct config_NAME *dest);
*
* A C function which sets the entire contents of the given C structure to its default values
* as defined in the schema. This will only return CFOK or CFERROR; see below.
*
* - int cf_opt_config_NAME(struct config_NAME *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. 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(NAME, VALIDATOR) or ARRAY(NAME, FLAGS, VALIDATOR) schema declaration is given a
* validator function, then the function must have the following signature:
*
* - int VALIDATOR(struct config_NAME *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 cf_opt_config_NAME() 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 cf_opt_config_NAME() 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 cf_opt_config_NAME() 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 occurs (eg, malloc() fails). The
* result in *dest is undefined and may be malformed or inconsistent.
*
* - 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).
*
* - CFUNSUPPORTED if the config item (array or struct) is not supported. This flag is not
* produced by the normal cf_opt_config_NAME() parse functions, but a validation function could set
* it to indicate 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. This may indicate a typo in the name
* of a config option, resulting in the intended option not being set.
*
* - CFDUPLICATE if a duplicate array entry was found. The result may be an empty array (in which
* case CFEMPTY is also set), or an array that omits the duplicate element. It is not defined
* which of the two conflicting elements will get omitted. Normal array parsing without a
* validator function will return an empty array in the case of duplicate, but a validator
* function may change this behaviour.
*
* - 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 the relevant
* CFSUB() bits will be set) and arbitrarily omitting others that did not fit. It is not defined
* which elements get omitted from an overflowed array. Normal array parsing without a validator
* function will return an empty array in the case of overflow, but a validator function may
* change this behaviour.
*
* - 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. The result in *dest is valid and the elements that failed
* to parse are unchanged.
*
* - 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 cf_opt_config_NAME() 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).
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
#define FORM_SERVAL_INSTANCE_PATH(buf, path) (form_serval_instance_path(buf, sizeof(buf), (path)))
int confBusy();
int confReloadIfChanged();
const char *confValueGet(const char *var, const char *defaultValue);
int confValueGetBoolean(const char *var, int defaultValue);
int64_t confValueGetInt64(const char *var, int64_t defaultValue);
int64_t confValueGetInt64Range(const char *var, int64_t defaultValue, int64_t rangemin, int64_t rangemax);
void confSetDebugFlags();
int confParseBoolean(const char *text, const char *option_name);
int confValueSet(const char *var, const char *value);
int confWrite();
int confVarCount();
const char *confVar(unsigned int index);
const char *confValue(unsigned int index);
int form_serval_instance_path(char *buf, size_t bufsiz, const char *path);
const char *trimbuildpath(const char *s);
int mkdirs(const char *path, mode_t mode);
int mkdirsn(const char *path, size_t len, mode_t mode);
const char *serval_instancepath();
void serval_setinstancepath(const char *instancepath);
#ifndef __SERVALDNA_CONFIG_H
#define __SERVALDNA_CONFIG_H
#include <stdint.h>
#include <arpa/inet.h>
#include "constants.h"
#include "strbuf.h"
#include "serval.h"
#include "rhizome.h"
#define CONFIG_FILE_MAX_SIZE (32 * 1024)
#define INTERFACE_NAME_STRLEN 40
/* Return bit flags for config schema default cf_dfl_xxx() and parsing cf_opt_xxx() functions. */
#define CFERROR (~0) // all set
#define CFOK 0
#define CFEMPTY (1<<0)
#define CFDUPLICATE (1<<1)
#define CFARRAYOVERFLOW (1<<2)
#define CFSTRINGOVERFLOW (1<<3)
#define CFINCOMPLETE (1<<4)
#define CFINCOMPATIBLE (1<<5)
#define CFINVALID (1<<6)
#define CFUNSUPPORTED (1<<7)
#define CF__SUB_SHIFT 16
#define CFSUB(f) ((f) << CF__SUB_SHIFT)
#define CF__SUBFLAGS CFSUB(~0)
#define CF__FLAGS (~0 & ~CF__SUBFLAGS)
strbuf strbuf_cf_flags(strbuf, int);
strbuf strbuf_cf_flag_reason(strbuf sb, int flags);
/* The Configuration Object Model (COM). The config file is parsed into a tree of these structures
* first, then those structures are passed as arguments to the schema parsing functions.
*/
struct cf_om_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 cf_om_node *nodv[10]; // malloc()
};
int is_configvarname(const char *);
int cf_om_parse(const char *source, const char *buf, size_t len, struct cf_om_node **rootp);
int cf_om_get_child(const struct cf_om_node *parent, const char *key, const char *keyend);
const char *cf_om_get(const struct cf_om_node *root, const char *fullkey);
int cf_om_set(struct cf_om_node **nodep, const char *fullkey, const char *text);
int cf_om_add_child(struct cf_om_node **const parentp, const char *const key);
void cf_om_free_node(struct cf_om_node **nodep);
void cf_om_dump_node(const struct cf_om_node *node, int indent);
struct cf_om_iterator {
const struct cf_om_node *node;
unsigned sp;
struct {
const struct cf_om_node *node;
unsigned index;
} stack[20];
};
void cf_om_iter_start(struct cf_om_iterator *, const struct cf_om_node *);
int cf_om_iter_next(struct cf_om_iterator *);
struct cf_om_node *cf_om_root;
int cf_om_load();
int cf_om_reload();
int cf_om_save();
/* Diagnostic functions for use in config schema parsing functions, cf_opt_xxx(). */
void _cf_warn_nodev(struct __sourceloc __whence, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap);
void _cf_warn_childrenv(struct __sourceloc __whence, const struct cf_om_node *parent, const char *fmt, va_list ap);
void _cf_warn_node(struct __sourceloc __whence, const struct cf_om_node *node, const char *key, const char *fmt, ...);
void _cf_warn_children(struct __sourceloc __whence, const struct cf_om_node *node, const char *fmt, ...);
void _cf_warn_duplicate_node(struct __sourceloc __whence, const struct cf_om_node *parent, const char *key);
void _cf_warn_missing_node(struct __sourceloc __whence, const struct cf_om_node *parent, const char *key);
void _cf_warn_node_value(struct __sourceloc __whence, const struct cf_om_node *node, int reason);
void _cf_warn_no_array(struct __sourceloc __whence, const struct cf_om_node *node, int reason);
void _cf_warn_unsupported_node(struct __sourceloc __whence, const struct cf_om_node *node);
void _cf_warn_unsupported_children(struct __sourceloc __whence, const struct cf_om_node *parent);
void _cf_warn_list_overflow(struct __sourceloc __whence, const struct cf_om_node *node);
void _cf_warn_incompatible(struct __sourceloc __whence, const struct cf_om_node *node, const struct cf_om_node *orig);
void _cf_warn_incompatible_children(struct __sourceloc __whence, const struct cf_om_node *parent);
void _cf_warn_array_key(struct __sourceloc __whence, const struct cf_om_node *node, int reason);
void _cf_warn_array_value(struct __sourceloc __whence, const struct cf_om_node *node, int reason);
#define cf_warn_nodev(node, key, fmt, ap) _cf_warn_nodev(__WHENCE__, node, key, fmt, ap)
#define cf_warn_childrenv(parent, fmt, ap) _cf_warn_childrenv(__WHENCE__, parent, fmt, ap)
#define cf_warn_node(node, key, fmt, ...) _cf_warn_node(__WHENCE__, node, key, fmt, ##__VA_ARGS__)
#define cf_warn_children(node, fmt, ...) _cf_warn_children(__WHENCE__, node, fmt, ##__VA_ARGS__)
#define cf_warn_duplicate_node(parent, key) _cf_warn_duplicate_node(__WHENCE__, parent, key)
#define cf_warn_missing_node(parent, key) _cf_warn_missing_node(__WHENCE__, parent, key)
#define cf_warn_node_value(node, reason) _cf_warn_node_value(__WHENCE__, node, reason)
#define cf_warn_no_array(node, reason) _cf_warn_no_array(__WHENCE__, node, reason)
#define cf_warn_unsupported_node(node) _cf_warn_unsupported_node(__WHENCE__, node)
#define cf_warn_unsupported_children(parent) _cf_warn_unsupported_children(__WHENCE__, parent)
#define cf_warn_list_overflow(node) _cf_warn_list_overflow(__WHENCE__, node)
#define cf_warn_incompatible(node, orig) _cf_warn_incompatible(__WHENCE__, node, orig)
#define cf_warn_incompatible_children(parent) _cf_warn_incompatible_children(__WHENCE__, parent)
#define cf_warn_array_key(node, reason) _cf_warn_array_key(__WHENCE__, node, reason)
#define cf_warn_array_value(node, reason) _cf_warn_array_value(__WHENCE__, node, reason)
struct pattern_list {
unsigned patc;
char patv[16][INTERFACE_NAME_STRLEN + 1];
};
#define PATTERN_LIST_EMPTY ((struct pattern_list){.patc = 0})
// Generate config struct definitions, struct config_NAME.
#define STRUCT(__name, __validator...) \
struct config_##__name {
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
__type __element;
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
__type __element;
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
char __element[__size + 1];
#define SUB_STRUCT(__name, __element, __flags) \
struct config_##__name __element;
#define NODE_STRUCT(__name, __element, __parser, __flags) \
struct config_##__name __element;
#define END_STRUCT \
};
#define ARRAY(__name, __flags, __validator...) \
struct config_##__name { \
unsigned ac; \
struct config_##__name##__element {
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
__type key;
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
char key[(__strsize) + 1];
#define VALUE_ATOM(__type, __eltparser) \
__type value;
#define VALUE_STRING(__strsize, __eltparser) \
char value[(__strsize) + 1];
#define VALUE_NODE(__type, __eltparser) \
__type value;
#define VALUE_SUB_STRUCT(__structname) \
struct config_##__structname value;
#define VALUE_NODE_STRUCT(__structname, __eltparser) \
struct config_##__structname value;
#define END_ARRAY(__size) \
} av[(__size)]; \
};
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config function prototypes, cf_dfl_config_NAME(), cf_sch_config_NAME().
#define STRUCT(__name, __validator...) \
int cf_dfl_config_##__name(struct config_##__name *s); \
int cf_sch_config_##__name(struct cf_om_node **parentp);
#define NODE(__type, __element, __default, __parser, __flags, __comment)
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
#define STRING(__size, __element, __default, __parser, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags)
#define NODE_STRUCT(__name, __element, __parser, __flags)
#define END_STRUCT
#define ARRAY(__name, __flags, __validator...) \
int cf_dfl_config_##__name(struct config_##__name *a); \
int cf_sch_config_##__name(struct cf_om_node **parentp);
#define KEY_ATOM(__type, __eltparser, __cmpfunc...)
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...)
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config parser function prototypes.
#define __VALIDATOR(__name, __validator...) \
typedef int __validator_func__config_##__name##__t(const struct cf_om_node *, struct config_##__name *, int); \
__validator_func__config_##__name##__t __dummy__validator_func__config_##__name, ##__validator;
#define STRUCT(__name, __validator...) \
int cf_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) \
int __parser(__type *, const char *);
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
int __parser(char *, size_t, const char *);
#define SUB_STRUCT(__name, __element, __flags) \
int cf_opt_config_##__name(struct config_##__name *, const 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, __flags, __validator...) \
int cf_opt_config_##__name(struct config_##__name *, const struct cf_om_node *); \
__VALIDATOR(__name, ##__validator)
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
int __eltparser(__type *, const char *);
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
int __eltparser(char *, size_t, const char *);
#define VALUE_ATOM(__type, __eltparser) \
int __eltparser(__type *, const char *);
#define VALUE_STRING(__strsize, __eltparser) \
int __eltparser(char *, size_t, const char *);
#define VALUE_NODE(__type, __eltparser) \
int __eltparser(__type *, const struct cf_om_node *);
#define VALUE_SUB_STRUCT(__structname) \
int cf_opt_config_##__structname(struct config_##__structname *, const struct cf_om_node *);
#define VALUE_NODE_STRUCT(__structname, __eltparser) \
int __eltparser(struct config_##__structname *, const struct cf_om_node *);
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef __VALIDATOR
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config array key comparison function prototypes.
#define STRUCT(__name, __validator...)
#define NODE(__type, __element, __default, __parser, __flags, __comment)
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
#define STRING(__size, __element, __default, __parser, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags)
#define NODE_STRUCT(__name, __element, __parser, __flags)
#define END_STRUCT
#define ARRAY(__name, __flags, __validator...) \
typedef int __compare_func__config_##__name##__t
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
(const __type *, const __type *);
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
(const char *, const char *);
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#define ARRAY(__name, __flags, __validator...) \
__compare_func__config_##__name##__t __dummy__compare_func__config_##__name
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
,##__cmpfunc;
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
,##__cmpfunc;
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config array search-by-key function prototypes.
#define STRUCT(__name, __validator...)
#define NODE(__type, __element, __default, __parser, __flags, __comment)
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
#define STRING(__size, __element, __default, __parser, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags)
#define NODE_STRUCT(__name, __element, __parser, __flags)
#define END_STRUCT
#define ARRAY(__name, __flags, __validator...) \
int config_##__name##__get(const struct config_##__name *,
#define KEY_ATOM(__type, __eltparser, __cmpfunc...) \
const __type *);
#define KEY_STRING(__strsize, __eltparser, __cmpfunc...) \
const char *);
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
int cf_opt_boolean(int *booleanp, const char *text);
int cf_opt_absolute_path(char *str, size_t len, const char *text);
int cf_opt_debugflags(debugflags_t *flagsp, const struct cf_om_node *node);
int cf_opt_rhizome_peer(struct config_rhizome_peer *, const struct cf_om_node *node);
int cf_opt_rhizome_peer_from_uri(struct config_rhizome_peer *, const char *uri);
int cf_opt_str(char *str, size_t len, const char *text);
int cf_opt_str_nonempty(char *str, size_t len, const char *text);
int cf_opt_int(int *intp, const char *text);
int cf_opt_uint16(uint16_t *intp, const char *text);
int cf_opt_uint16_nonzero(uint16_t *intp, const char *text);
int cf_opt_int32_nonneg(int32_t *intp, const char *text);
int cf_opt_uint32_nonzero(uint32_t *intp, const char *text);
int cf_opt_uint64_scaled(uint64_t *intp, const char *text);
int cf_opt_protocol(char *str, size_t len, const char *text);
int cf_opt_in_addr(struct in_addr *addrp, const char *text);
int cf_opt_sid(sid_t *sidp, const char *text);
int cf_opt_rhizome_bk(rhizome_bk_t *bkp, const char *text);
int cf_opt_interface_type(short *typep, const char *text);
int cf_opt_pattern_list(struct pattern_list *listp, const char *text);
int cf_opt_network_interface(struct config_network_interface *nifp, const struct cf_om_node *node);
int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node);
extern int cf_limbo;
extern struct config_main config;
int cf_init();
int cf_load();
int cf_load_permissive();
int cf_reload();
int cf_reload_permissive();
#endif //__SERVALDNA_CONFIG_H

554
conf_om.c Normal file
View File

@ -0,0 +1,554 @@
/*
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdarg.h>
#include <assert.h>
#include "mem.h"
#include "str.h"
#include "strbuf.h"
#include "log.h"
#include "conf.h"
static const char *cf_find_keyend(const char *const key, const char *const fullkeyend)
{
const char *s = key;
if (s < fullkeyend && (isalpha(*s) || *s == '_'))
++s;
while (s < fullkeyend && (isalnum(*s) || *s == '_'))
++s;
if (s == key || (s < fullkeyend && *s != '.'))
return NULL;
return s;
}
/* This predicate function defines the constraints on configuration option names.
* Valid:
* foo
* foo.bar
* foo.bar.chow
* _word
* word1
* word_1
* Invalid:
* foo.
* .foo
* 1foo
* foo.bar.
* 12
* 1.2.3
* foo bar
* @author Andrew Bettison <andrew@servalproject.com>
*/
int is_configvarname(const char *text)
{
const char *const textend = text + strlen(text);
const char *key = text;
const char *keyend = NULL;
while (key <= textend && (keyend = cf_find_keyend(key, textend)) != NULL)
key = keyend + 1;
return keyend != NULL;
}
static int cf_om_make_child(struct cf_om_node **const parentp, const char *const fullkey, const char *const key, const char *const keyend)
{
// Allocate parent node if it is not present.
if (!*parentp && (*parentp = emalloc_zero(sizeof **parentp)) == NULL)
return -1;
size_t keylen = keyend - key;
int i = 0;
struct cf_om_node *child;
if ((*parentp)->nodc) {
// Binary search for matching child.
int m = 0;
int n = (*parentp)->nodc - 1;
int c;
do {
i = (m + n) / 2;
child = (*parentp)->nodv[i];
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);
if (c == 0) {
//DEBUGF(" found i=%d", i);
return i;
}
if (c > 0)
m = ++i;
else
n = i - 1;
} while (m <= n);
}
// At this point, i is the index where a new child should be inserted.
assert(i >= 0);
assert(i <= (*parentp)->nodc);
if ((child = emalloc_zero(sizeof *child)) == NULL)
return -1;
++(*parentp)->nodc;
if ((*parentp)->nodc > NELS((*parentp)->nodv))
*parentp = realloc(*parentp, sizeof(**parentp) + sizeof((*parentp)->nodv[0]) * ((*parentp)->nodc - NELS((*parentp)->nodv)));
int j;
for (j = (*parentp)->nodc - 1; j > i; --j)
(*parentp)->nodv[j] = (*parentp)->nodv[j-1];
(*parentp)->nodv[i] = child;
if (!(child->fullkey = strn_edup(fullkey, keyend - fullkey))) {
free(child);
return -1;
}
child->key = child->fullkey + (key - fullkey);
//DEBUGF(" insert i=%d", i);
return i;
}
int cf_om_add_child(struct cf_om_node **const parentp, const char *const key)
{
size_t parent_fullkey_len = (parentp && *parentp && (*parentp)->fullkey) ? strlen((*parentp)->fullkey) : 0;
size_t fullkey_len = parent_fullkey_len + 1 + strlen(key);
char fullkey[fullkey_len + 1];
char *pkey = fullkey;
if (parent_fullkey_len) {
strcpy(fullkey, (*parentp)->fullkey);
pkey = fullkey + parent_fullkey_len;
*pkey++ = '.';
}
strcpy(pkey, key);
return cf_om_make_child(parentp, fullkey, pkey, fullkey + fullkey_len);
}
int cf_om_get_child(const struct cf_om_node *parent, const char *key, const char *keyend)
{
if (keyend == NULL)
keyend = key + strlen(key);
// TODO: use binary search, since child nodes are already sorted by key
int i;
for (i = 0; i < parent->nodc; ++i)
if (memcmp(parent->nodv[i]->key, key, keyend - key) == 0 && parent->nodv[i]->key[keyend - key] == '\0')
return i;
return -1;
}
int cf_om_parse(const char *source, const char *buf, size_t len, struct cf_om_node **rootp)
{
const char *end = buf + len;
const char *line = buf;
const char *nextline;
unsigned lineno = 1;
int result = CFEMPTY;
for (lineno = 1; line < end; line = nextline, ++lineno) {
const char *lend = line;
while (lend < end && *lend != '\n')
++lend;
nextline = lend + 1;
if (lend > line && lend[-1] == '\r')
--lend;
//DEBUGF("lineno=%u %s", lineno, alloca_toprint(-1, line, lend - line));
if (line[0] == '#')
continue; // skip comment lines
const char *p;
for (p = line; p < lend && isspace(*p); ++p)
;
if (p == lend)
continue; // skip empty and blank lines
for (p = line; p < lend && *p != '='; ++p)
;
if (p == line || p == lend) {
WARNF("%s:%u: malformed configuration line", source, lineno);
result |= CFINVALID;
continue;
}
struct cf_om_node **nodep = rootp;
const char *fullkey = line;
const char *fullkeyend = p;
const char *key = fullkey;
const char *keyend = NULL;
int nodi = -1;
while (key <= fullkeyend && (keyend = cf_find_keyend(key, fullkeyend)) && (nodi = cf_om_make_child(nodep, fullkey, key, keyend)) != -1) {
key = keyend + 1;
nodep = &(*nodep)->nodv[nodi];
}
if (keyend == NULL) {
WARNF("%s:%u: malformed configuration option %s",
source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey)
);
result |= CFINVALID;
continue;
}
if (nodi == -1)
return CFERROR; // out of memory
struct cf_om_node *node = *nodep;
if (node->text) {
WARNF("%s:%u: duplicate configuration option %s (original is at %s:%u)",
source, lineno, alloca_toprint(-1, fullkey, fullkeyend - fullkey),
node->source, node->line_number
);
result |= CFDUPLICATE;
continue;
}
++p;
if (!(node->text = strn_edup(p, lend - p)))
return CFERROR; // out of memory
node->source = source;
node->line_number = lineno;
result &= ~CFEMPTY;
}
return result;
}
void cf_om_free_node(struct cf_om_node **nodep)
{
if (*nodep) {
while ((*nodep)->nodc)
cf_om_free_node(&(*nodep)->nodv[--(*nodep)->nodc]);
if ((*nodep)->fullkey) {
free((char *)(*nodep)->fullkey);
(*nodep)->fullkey = (*nodep)->key = NULL;
}
if ((*nodep)->text) {
free((char *)(*nodep)->text);
(*nodep)->text = NULL;
}
free(*nodep);
*nodep = NULL;
}
}
void cf_om_dump_node(const struct cf_om_node *node, int indent)
{
if (node == NULL)
DEBUGF("%*sNULL", indent * 3, "");
else {
DEBUGF("%*s%s:%u fullkey=%s key=%s text=%s", indent * 3, "",
node->source ? node->source : "NULL",
node->line_number,
alloca_str_toprint(node->fullkey),
alloca_str_toprint(node->key),
alloca_str_toprint(node->text)
);
int i;
for (i = 0; i < node->nodc; ++i)
cf_om_dump_node(node->nodv[i], indent + 1);
}
}
const char *cf_om_get(const struct cf_om_node *node, const char *fullkey)
{
if (node == NULL)
return NULL;
const char *fullkeyend = fullkey + strlen(fullkey);
const char *key = fullkey;
const char *keyend = NULL;
int nodi = -1;
while (key <= fullkeyend && (keyend = cf_find_keyend(key, fullkeyend)) && (nodi = cf_om_get_child(node, key, keyend)) != -1) {
key = keyend + 1;
node = node->nodv[nodi];
}
if (keyend == NULL) {
WARNF("malformed configuration option %s", alloca_toprint(-1, fullkey, fullkeyend - fullkey));
return NULL;
}
if (nodi == -1)
return NULL;
return node->text;
}
int cf_om_set(struct cf_om_node **nodep, const char *fullkey, const char *text)
{
const char *fullkeyend = fullkey + strlen(fullkey);
const char *key = fullkey;
const char *keyend = NULL;
int nodi = -1;
while (key <= fullkeyend && (keyend = cf_find_keyend(key, fullkeyend)) && (nodi = cf_om_make_child(nodep, fullkey, key, keyend)) != -1) {
key = keyend + 1;
nodep = &(*nodep)->nodv[nodi];
}
if (keyend == NULL) {
WARNF("malformed configuration option %s", alloca_toprint(-1, fullkey, fullkeyend - fullkey));
return CFINVALID;
}
if (nodi == -1)
return CFERROR; // out of memory
struct cf_om_node *node = *nodep;
free((char *)node->text);
if (text == NULL)
node->text = NULL;
else if (!(node->text = str_edup(text)))
return CFERROR; // out of memory
return CFOK;
}
void cf_om_iter_start(struct cf_om_iterator *it, const struct cf_om_node *root)
{
it->sp = 0;
it->stack[0].node = it->node = root;
it->stack[0].index = 0;
}
#if 0
static void cf_om_iter_dump(struct cf_om_iterator *it)
{
strbuf b = strbuf_alloca(1024);
strbuf_sprintf(b, "node=%p sp=%d", it->node, it->sp);
int i;
for (i = 0; i <= it->sp; ++i)
strbuf_sprintf(b, " %p[%d]", it->stack[i].node, it->stack[i].index);
DEBUG(strbuf_str(b));
}
#endif
int cf_om_iter_next(struct cf_om_iterator *it)
{
//cf_om_iter_dump(it);
if (!it->node)
return 0;
while (1) {
const struct cf_om_node *parent = it->stack[it->sp].node;
int i = it->stack[it->sp].index++;
if (i < parent->nodc) {
it->node = parent->nodv[i];
if (it->sp >= NELS(it->stack))
return -1;
++it->sp;
it->stack[it->sp].node = it->node;
it->stack[it->sp].index = 0;
return 0;
} else if (it->sp) {
--it->sp;
} else {
it->node = NULL;
return 0;
}
}
}
void _cf_warn_nodev(struct __sourceloc __whence, const struct cf_om_node *node, const char *key, const char *fmt, va_list ap)
{
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 \"");
if (node->fullkey && node->fullkey[0])
strbuf_puts(b, node->fullkey);
if (key && key[0]) {
if (node->fullkey && node->fullkey[0])
strbuf_putc(b, '.');
strbuf_puts(b, key);
}
strbuf_puts(b, "\" ");
}
strbuf_vsprintf(b, fmt, ap);
WARN(strbuf_str(b));
}
void _cf_warn_childrenv(struct __sourceloc __whence, const struct cf_om_node *parent, const char *fmt, va_list ap)
{
int i;
for (i = 0; i < parent->nodc; ++i) {
_cf_warn_nodev(__whence, parent->nodv[i], NULL, fmt, ap);
_cf_warn_childrenv(__whence, parent->nodv[i], fmt, ap);
}
}
void _cf_warn_node(struct __sourceloc __whence, const struct cf_om_node *node, const char *key, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_cf_warn_nodev(__whence, node, key, fmt, ap);
va_end(ap);
}
void _cf_warn_children(struct __sourceloc __whence, const struct cf_om_node *node, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_cf_warn_childrenv(__whence, node, fmt, ap);
va_end(ap);
}
void _cf_warn_duplicate_node(struct __sourceloc __whence, const struct cf_om_node *parent, const char *key)
{
_cf_warn_node(__whence, parent, key, "is duplicate");
}
void _cf_warn_missing_node(struct __sourceloc __whence, const struct cf_om_node *parent, const char *key)
{
_cf_warn_node(__whence, parent, key, "is missing");
}
void _cf_warn_incompatible(struct __sourceloc __whence, const struct cf_om_node *node, const struct cf_om_node *orig)
{
assert(node != orig);
strbuf b = strbuf_alloca(180);
if (orig) {
strbuf_sprintf(b, "\"%s\"", orig->fullkey);
if (orig->source && orig->line_number)
strbuf_sprintf(b, " at %s:%u", orig->source, orig->line_number);
} else {
strbuf_puts(b, "other option(s)");
}
_cf_warn_node(__whence, node, NULL, "is incompatible with %s", strbuf_str(b));
}
void _cf_warn_incompatible_children(struct __sourceloc __whence, const struct cf_om_node *parent)
{
struct cf_om_iterator it;
for (cf_om_iter_start(&it, parent); it.node; cf_om_iter_next(&it))
if (it.node != parent && it.node->text)
_cf_warn_incompatible(__whence, parent, it.node);
}
void _cf_warn_unsupported_node(struct __sourceloc __whence, const struct cf_om_node *node)
{
_cf_warn_node(__whence, node, NULL, "not supported");
}
void _cf_warn_unsupported_children(struct __sourceloc __whence, const struct cf_om_node *parent)
{
int i;
for (i = 0; i < parent->nodc; ++i) {
if (parent->nodv[i]->text)
_cf_warn_unsupported_node(__whence, parent->nodv[i]);
_cf_warn_unsupported_children(__whence, parent->nodv[i]);
}
}
strbuf strbuf_cf_flags(strbuf sb, int flags)
{
if (flags == CFERROR)
return strbuf_puts(sb, "CFERROR");
size_t n = strbuf_len(sb);
static struct { int flag; const char *name; } flagdefs[] = {
{ CFEMPTY, "CFEMPTY" },
{ CFDUPLICATE, "CFDUPLICATE" },
{ CFSTRINGOVERFLOW, "CFSTRINGOVERFLOW" },
{ CFARRAYOVERFLOW, "CFARRAYOVERFLOW" },
{ CFINCOMPLETE, "CFINCOMPLETE" },
{ CFINCOMPATIBLE, "CFINCOMPATIBLE" },
{ CFINVALID, "CFINVALID" },
{ CFUNSUPPORTED, "CFUNSUPPORTED" },
};
int i;
for (i = 0; i < NELS(flagdefs); ++i) {
if (flags & flagdefs[i].flag) {
if (strbuf_len(sb) != n)
strbuf_putc(sb, ' ');
strbuf_puts(sb, flagdefs[i].name);
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, ' ');
strbuf_sprintf(sb, "%#x", flags);
}
if (strbuf_len(sb) == n)
strbuf_puts(sb, "CFOK");
return sb;
}
strbuf strbuf_cf_flag_reason(strbuf sb, int flags)
{
if (flags == CFERROR)
return strbuf_puts(sb, "unrecoverable error");
size_t n = strbuf_len(sb);
static struct { int flag; const char *reason; } flagdefs[] = {
{ CFEMPTY, "empty" },
{ CFDUPLICATE, "duplicate element" },
{ CFSTRINGOVERFLOW, "string overflow" },
{ CFARRAYOVERFLOW, "array overflow" },
{ CFINCOMPLETE, "incomplete" },
{ CFINCOMPATIBLE, "incompatible" },
{ CFINVALID, "invalid" },
{ CFUNSUPPORTED, "not supported" },
{ CFSUB(CFEMPTY), "contains empty element" },
{ CFSUB(CFDUPLICATE), "contains element with duplicate" },
{ CFSUB(CFSTRINGOVERFLOW), "contains string overflow" },
{ CFSUB(CFARRAYOVERFLOW), "contains array overflow" },
{ CFSUB(CFINCOMPLETE), "contains incomplete element" },
{ CFSUB(CFINCOMPATIBLE), "contains incompatible element" },
{ CFSUB(CFINVALID), "contains invalid element" },
{ CFSUB(CFUNSUPPORTED), "contains unsupported element" },
};
int i;
for (i = 0; i < NELS(flagdefs); ++i) {
if (flags & flagdefs[i].flag) {
if (strbuf_len(sb) != n)
strbuf_puts(sb, ", ");
strbuf_puts(sb, flagdefs[i].reason);
flags &= ~flagdefs[i].flag;
}
}
if (strbuf_len(sb) == n)
strbuf_puts(sb, "no reason");
return sb;
}
void _cf_warn_node_value(struct __sourceloc __whence, const struct cf_om_node *node, int reason)
{
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, reason);
_cf_warn_node(__whence, node, NULL, "value %s %s", alloca_str_toprint(node->text), strbuf_str(b));
}
void _cf_warn_no_array(struct __sourceloc __whence, const struct cf_om_node *node, int reason)
{
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, reason);
_cf_warn_node(__whence, node, NULL, "array discarded -- %s", strbuf_str(b));
}
void _cf_warn_array_key(struct __sourceloc __whence, const struct cf_om_node *node, int reason)
{
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, reason);
_cf_warn_node(__whence, node, NULL, "array key %s -- %s", alloca_str_toprint(node->key), strbuf_str(b));
}
void _cf_warn_array_value(struct __sourceloc __whence, const struct cf_om_node *node, int reason)
{
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, reason);
if (node->text)
_cf_warn_node(__whence, node, NULL, "array value %s -- %s", alloca_str_toprint(node->text), strbuf_str(b));
else
_cf_warn_node(__whence, node, NULL, "array element -- %s", strbuf_str(b));
}
void _cf_warn_list_overflow(struct __sourceloc __whence, const struct cf_om_node *node)
{
_cf_warn_node(__whence, node, NULL, "list overflow");
_cf_warn_children(__whence, node, "list overflow");
}

415
conf_parse.c Normal file
View File

@ -0,0 +1,415 @@
/*
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <assert.h>
#include "log.h"
#include "conf.h"
// Generate config set-default function definitions, cf_dfl_config_NAME().
#define STRUCT(__name, __validator...) \
int cf_dfl_config_##__name(struct config_##__name *s) {
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
s->__element = (__default);
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
s->__element = (__default);
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
strncpy(s->__element, (__default), (__size))[(__size)] = '\0';
#define SUB_STRUCT(__name, __element, __flags) \
cf_dfl_config_##__name(&s->__element);
#define NODE_STRUCT(__name, __element, __parser, __flags) \
cf_dfl_config_##__name(&s->__element);
#define END_STRUCT \
return CFOK; \
}
#define ARRAY(__name, __flags, __validator...) \
int cf_dfl_config_##__name(struct config_##__name *a) { \
a->ac = 0; \
return CFOK; \
}
#define KEY_ATOM(__type, __keyparser, __cmpfunc...)
#define KEY_STRING(__strsize, __keyparser, __cmpfunc...)
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate array element comparison functions.
#define STRUCT(__name, __validator...)
#define NODE(__type, __element, __default, __parser, __flags, __comment)
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
#define STRING(__size, __element, __default, __parser, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags)
#define NODE_STRUCT(__name, __element, __parser, __flags)
#define END_STRUCT
#define ARRAY(__name, __flags, __validator...) \
static int __cmp_config_##__name(const struct config_##__name##__element *a, const struct config_##__name##__element *b) { \
__compare_func__config_##__name##__t *cmp = (NULL
#define KEY_ATOM(__type, __keyparser, __cmpfunc...) \
,##__cmpfunc); \
return cmp ? (*cmp)(&a->key, &b->key) : memcmp(&a->key, &b->key, sizeof a->key);
#define KEY_STRING(__strsize, __keyparser, __cmpfunc...) \
,##__cmpfunc); \
return cmp ? (*cmp)(a->key, b->key) : strcmp(a->key, b->key);
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size) \
}
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Schema item flags.
#define __MANDATORY (1<<0)
#define __TEXT (1<<1)
#define __CHILDREN (1<<2)
#define __SORTED (1<<3)
#define __NO_DUPLICATES (1<<4)
// Schema flag symbols, to be used in the '__flags' macro arguments.
#define MANDATORY |__MANDATORY
#define USES_TEXT |__TEXT
#define USES_CHILDREN |__CHILDREN
#define SORTED |__SORTED
#define NO_DUPLICATES |__NO_DUPLICATES
// Generate parsing functions, cf_opt_config_SECTION()
#define STRUCT(__name, __validator...) \
int cf_opt_config_##__name(struct config_##__name *strct, const struct cf_om_node *node) { \
int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \
int result = CFEMPTY; \
char used[node->nodc]; \
memset(used, 0, node->nodc * sizeof used[0]);
#define __ITEM(__element, __flags, __parseexpr) \
{ \
int i = cf_om_get_child(node, #__element, NULL); \
const struct cf_om_node *child = (i != -1) ? node->nodv[i] : NULL; \
int ret = CFEMPTY; \
if (child) { \
used[i] |= (__flags); \
ret = (__parseexpr); \
if (ret == CFERROR) \
return CFERROR; \
} \
result |= ret & CF__SUBFLAGS; \
ret &= CF__FLAGS; \
if (!(ret & CFEMPTY)) \
result &= ~CFEMPTY; \
else if ((__flags) & __MANDATORY) { \
cf_warn_missing_node(node, #__element); \
result |= CFINCOMPLETE; \
} \
if (ret & ~CFEMPTY) { \
assert(child != NULL); \
if (child->text) \
cf_warn_node_value(child, ret); \
result |= CFSUB(ret); \
} \
}
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
__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(&strct->__element, child->text) : CFEMPTY)
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
__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, cf_opt_config_##__name(&strct->__element, child))
#define NODE_STRUCT(__name, __element, __parser, __flags) \
__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)) { \
cf_warn_unsupported_node(node->nodv[i]); \
result |= CFSUB(CFUNSUPPORTED); \
} \
if (node->nodv[i]->nodc && !(used[i] & __CHILDREN)) { \
cf_warn_unsupported_children(node->nodv[i]); \
result |= CFSUB(CFUNSUPPORTED); \
} \
} \
} \
if (validator) \
result = (*validator)(node, strct, result); \
return result; \
}
#define ARRAY(__name, __flags, __validator...) \
int cf_opt_config_##__name(struct config_##__name *array, const struct cf_om_node *node) { \
int flags = (0 __flags); \
int (*eltcmp)(const struct config_##__name##__element *, const struct config_##__name##__element *) = __cmp_config_##__name; \
int (*validator)(const struct cf_om_node *, struct config_##__name *, int) = (NULL, ##__validator); \
int result = CFOK; \
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 = CFEMPTY;
#define __ARRAY_KEY(__parseexpr, __cmpfunc...) \
ret = (__parseexpr); \
if (ret == CFERROR) \
return CFERROR; \
result |= ret & CF__SUBFLAGS; \
ret &= CF__FLAGS; \
result |= CFSUB(ret); \
if (ret == CFOK && (flags & __NO_DUPLICATES)) { \
int j; \
for (j = 0; j < n; ++j) { \
if ((*eltcmp)(&array->av[j], &array->av[n]) == 0) { \
cf_warn_duplicate_node(child, NULL); \
ret |= CFDUPLICATE; \
} \
} \
} \
if (ret != CFOK) \
cf_warn_array_key(child, ret);
#define __ARRAY_VALUE(__parseexpr) \
if (ret == CFOK) { \
ret = (__parseexpr); \
if (ret == CFERROR) \
return CFERROR; \
result |= ret & CF__SUBFLAGS; \
ret &= CF__FLAGS; \
result |= CFSUB(ret); \
if (ret == CFOK) \
++n; \
else \
cf_warn_array_value(child, ret); \
}
#define END_ARRAY(__size) \
} \
if (i < node->nodc) { \
assert(n == NELS(array->av)); \
result |= CFARRAYOVERFLOW; \
for (; i < node->nodc; ++i) \
cf_warn_list_overflow(node->nodv[i]); \
} \
array->ac = n; \
if (flags & __SORTED) \
qsort(array->av, array->ac, sizeof array->av[0], (int (*)(const void *, const void *)) eltcmp); \
if (validator) \
result = (*validator)(node, array, result); \
if (result & ~CFEMPTY) { \
cf_warn_no_array(node, result); \
array->ac = 0; \
} \
if (array->ac == 0) \
result |= CFEMPTY; \
return result; \
}
#define KEY_ATOM(__type, __keyparser, __cmpfunc...) \
__ARRAY_KEY(__keyparser(&array->av[n].key, child->key), ##__cmpfunc)
#define KEY_STRING(__strsize, __keyparser, __cmpfunc...) \
__ARRAY_KEY(__keyparser(array->av[n].key, sizeof array->av[n].key, child->key), ##__cmpfunc)
#define VALUE_ATOM(__type, __eltparser) \
__ARRAY_VALUE(child->text ? __eltparser(&array->av[n].value, child->text) : CFEMPTY)
#define VALUE_STRING(__strsize, __eltparser) \
__ARRAY_VALUE(child->text ? __eltparser(array->av[n].value, sizeof array->av[n].value, child->text) : CFEMPTY)
#define VALUE_NODE(__type, __eltparser) \
__ARRAY_VALUE(__eltparser(&array->av[n].value, child))
#define VALUE_SUB_STRUCT(__structname) \
__ARRAY_VALUE(cf_opt_config_##__structname(&array->av[n].value, child))
#define VALUE_NODE_STRUCT(__structname, __eltparser) \
__ARRAY_VALUE(__eltparser(&array->av[n].value, child))
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config array search-by-key functions.
#define STRUCT(__name, __validator...)
#define NODE(__type, __element, __default, __parser, __flags, __comment)
#define ATOM(__type, __element, __default, __parser, __flags, __comment)
#define STRING(__size, __element, __default, __parser, __flags, __comment)
#define SUB_STRUCT(__name, __element, __flags)
#define NODE_STRUCT(__name, __element, __parser, __flags)
#define END_STRUCT
#define ARRAY(__name, __flags, __validator...) \
int config_##__name##__get(const struct config_##__name *array,
#define KEY_ATOM(__type, __keyparser, __cmpfunc...) \
const __type *key) { \
int (*cmp)(const __type *, const __type *) = (NULL, ##__cmpfunc); \
int i; \
for (i = 0; i < array->ac; ++i) \
if ((cmp ? (*cmp)(key, &array->av[i].key) : memcmp(key, &array->av[i].key, sizeof *key)) == 0) \
return i; \
return -1; \
}
#define KEY_STRING(__strsize, __keyparser, __cmpfunc...) \
const char *key) { \
int (*cmp)(const char *, const char *) = (NULL, ##__cmpfunc); \
int i; \
for (i = 0; i < array->ac; ++i) \
if ((cmp ? (*cmp)(key, array->av[i].key) : strcmp(key, array->av[i].key)) == 0) \
return i; \
return -1; \
}
#define VALUE_ATOM(__type, __eltparser)
#define VALUE_STRING(__strsize, __eltparser)
#define VALUE_NODE(__type, __eltparser)
#define VALUE_SUB_STRUCT(__structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser)
#define END_ARRAY(__size)
#include "conf_schema.h"
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY
// Generate config schema dump functions, cf_sch_config_NAME().
#define STRUCT(__name, __validator...) \
int cf_sch_config_##__name(struct cf_om_node **rootp) { \
int i; \
struct cf_om_node **childp;
#define __ADD_CHILD(nodep, __elementstr) \
if ((i = cf_om_add_child(nodep, __elementstr)) == -1) \
return -1; \
childp = &(*nodep)->nodv[i];
#define __ATOM(nodep, __text) \
if (((*nodep)->text = str_edup(__text)) == NULL) \
return -1;
#define __STRUCT(nodep, __structname) \
if (cf_sch_config_##__structname(nodep) == -1) \
return -1;
#define NODE(__type, __element, __default, __parser, __flags, __comment) \
__ADD_CHILD(rootp, #__element) \
__ATOM(childp, "(" #__parser ")") \
__ADD_CHILD(childp, "(" #__parser ")") \
__ATOM(childp, "(" #__parser ")")
#define ATOM(__type, __element, __default, __parser, __flags, __comment) \
__ADD_CHILD(rootp, #__element) \
__ATOM(childp, "(" #__parser ")")
#define STRING(__size, __element, __default, __parser, __flags, __comment) \
__ADD_CHILD(rootp, #__element) \
__ATOM(childp, "(" #__parser ")")
#define SUB_STRUCT(__structname, __element, __flags) \
__ADD_CHILD(rootp, #__element) \
__STRUCT(childp, __structname)
#define NODE_STRUCT(__structname, __element, __parser, __flags) \
__ADD_CHILD(rootp, #__element) \
__ATOM(childp, "(" #__parser ")") \
__STRUCT(childp, __structname)
#define END_STRUCT \
return 0; \
}
#define ARRAY(__name, __flags, __validator...) \
int cf_sch_config_##__name(struct cf_om_node **rootp) { \
int i; \
struct cf_om_node **childp;
#define KEY_ATOM(__type, __keyparser, __cmpfunc...) \
__ADD_CHILD(rootp, "[" #__keyparser "]")
#define KEY_STRING(__strsize, __keyparser, __cmpfunc...) \
__ADD_CHILD(rootp, "[" #__keyparser "]")
#define VALUE_ATOM(__type, __eltparser) \
__ATOM(childp, "(" #__eltparser ")")
#define VALUE_STRING(__strsize, __eltparser) \
__ATOM(childp, "(" #__eltparser ")")
#define VALUE_NODE(__type, __eltparser) \
__ATOM(childp, "(" #__eltparser ")") \
__ADD_CHILD(childp, "(" #__eltparser ")") \
__ATOM(childp, "(" #__eltparser ")")
#define VALUE_SUB_STRUCT(__structname) \
__STRUCT(childp, __structname)
#define VALUE_NODE_STRUCT(__structname, __eltparser) \
__ATOM(childp, "(" #__eltparser ")") \
__STRUCT(childp, __structname)
#define END_ARRAY(__size) \
return 0; \
}
#include "conf_schema.h"
#undef __ADD_CHILD
#undef __ATOM
#undef __STRUCT
#undef STRUCT
#undef NODE
#undef ATOM
#undef STRING
#undef SUB_STRUCT
#undef NODE_STRUCT
#undef END_STRUCT
#undef ARRAY
#undef KEY_ATOM
#undef KEY_STRING
#undef VALUE_ATOM
#undef VALUE_STRING
#undef VALUE_NODE
#undef VALUE_SUB_STRUCT
#undef VALUE_NODE_STRUCT
#undef END_ARRAY

596
conf_schema.c Normal file
View File

@ -0,0 +1,596 @@
/*
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <stdarg.h>
#include <assert.h>
#include <arpa/inet.h>
#include "log.h"
#include "str.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
#include "conf.h"
int cf_opt_boolean(int *booleanp, const char *text)
{
if (!strcasecmp(text, "true") || !strcasecmp(text, "yes") || !strcasecmp(text, "on") || !strcasecmp(text, "1")) {
*booleanp = 1;
return CFOK;
}
else if (!strcasecmp(text, "false") || !strcasecmp(text, "no") || !strcasecmp(text, "off") || !strcasecmp(text, "0")) {
*booleanp = 0;
return CFOK;
}
return CFINVALID;
}
int cf_opt_absolute_path(char *str, size_t len, const char *text)
{
if (text[0] != '/')
return CFINVALID;
if (strlen(text) >= len)
return CFSTRINGOVERFLOW;
strncpy(str, text, len);
assert(str[len - 1] == '\0');
return CFOK;
}
int cf_opt_debugflags(debugflags_t *flagsp, const struct cf_om_node *node)
{
//DEBUGF("%s", __FUNCTION__);
//cf_dump_node(node, 1);
debugflags_t setmask = 0;
debugflags_t clearmask = 0;
int setall = 0;
int clearall = 0;
int result = CFEMPTY;
int i;
for (i = 0; i < node->nodc; ++i) {
const struct cf_om_node *child = node->nodv[i];
cf_warn_unsupported_children(child);
debugflags_t mask = debugFlagMask(child->key);
int flag = -1;
if (!mask)
cf_warn_unsupported_node(child);
else if (child->text) {
int ret = cf_opt_boolean(&flag, child->text);
switch (ret) {
case CFERROR: return CFERROR;
case CFOK:
result &= ~CFEMPTY;
if (mask == ~0) {
if (flag)
setall = 1;
else
clearall = 1;
} else {
if (flag)
setmask |= mask;
else
clearmask |= mask;
}
break;
default:
cf_warn_node_value(child, ret);
result |= ret;
break;
}
}
}
if (setall)
*flagsp = ~0;
else if (clearall)
*flagsp = 0;
*flagsp &= ~clearmask;
*flagsp |= setmask;
return result;
}
int cf_opt_protocol(char *str, size_t len, const char *text)
{
if (!str_is_uri_scheme(text))
return CFINVALID;
if (strlen(text) >= len)
return CFSTRINGOVERFLOW;
strncpy(str, text, len);
assert(str[len - 1] == '\0');
return CFOK;
}
int cf_opt_rhizome_peer(struct config_rhizome_peer *rpeer, const struct cf_om_node *node)
{
if (!node->text)
return cf_opt_config_rhizome_peer(rpeer, node);
if (node->nodc) {
cf_warn_incompatible_children(node);
return CFINCOMPATIBLE;
}
return cf_opt_rhizome_peer_from_uri(rpeer, node->text);
}
int cf_opt_rhizome_peer_from_uri(struct config_rhizome_peer *rpeer, const char *text)
{
const char *protocol;
size_t protolen;
const char *auth;
if (str_is_uri(text)) {
const char *hier;
if (!( str_uri_scheme(text, &protocol, &protolen)
&& str_uri_hierarchical(text, &hier, NULL)
&& str_uri_hierarchical_authority(hier, &auth, NULL))
)
return CFINVALID;
} else {
auth = text;
protocol = "http";
protolen = strlen(protocol);
}
const char *host;
size_t hostlen;
unsigned short port = RHIZOME_HTTP_PORT;
if (!str_uri_authority_hostname(auth, &host, &hostlen))
return CFINVALID;
str_uri_authority_port(auth, &port);
if (protolen >= sizeof rpeer->protocol)
return CFSTRINGOVERFLOW;
if (hostlen >= sizeof rpeer->host)
return CFSTRINGOVERFLOW;
strncpy(rpeer->protocol, protocol, protolen)[protolen] = '\0';
strncpy(rpeer->host, host, hostlen)[hostlen] = '\0';
rpeer->port = port;
return CFOK;
}
int cf_opt_str(char *str, size_t len, const char *text)
{
if (strlen(text) >= len)
return CFSTRINGOVERFLOW;
strncpy(str, text, len);
assert(str[len - 1] == '\0');
return CFOK;
}
int cf_opt_str_nonempty(char *str, size_t len, const char *text)
{
if (!text[0])
return CFINVALID;
return cf_opt_str(str, len, text);
}
int cf_opt_int(int *intp, const char *text)
{
const char *end = text;
long value = strtol(text, (char**)&end, 10);
if (end == text || *end)
return CFINVALID;
*intp = value;
return CFOK;
}
int cf_opt_uint(unsigned int *uintp, const char *text)
{
const char *end = text;
unsigned long value = strtoul(text, (char**)&end, 10);
if (end == text || *end)
return CFINVALID;
*uintp = value;
return CFOK;
}
int cf_opt_int32_nonneg(int32_t *intp, const char *text)
{
const char *end = text;
long value = strtol(text, (char**)&end, 10);
if (end == text || *end || value < 0 || value > 0x7fffffffL)
return CFINVALID;
*intp = value;
return CFOK;
}
int cf_opt_uint32_nonzero(uint32_t *intp, const char *text)
{
const char *end = text;
unsigned long value = strtoul(text, (char**)&end, 10);
if (end == text || *end || value < 1 || value > 0xffffffffL)
return CFINVALID;
*intp = value;
return CFOK;
}
int cf_opt_uint64_scaled(uint64_t *intp, const char *text)
{
uint64_t result;
const char *end;
if (!str_to_uint64_scaled(text, 10, &result, &end) || *end)
return CFINVALID;
*intp = result;
return CFOK;
}
int cf_opt_ushort_nonzero(unsigned short *ushortp, const char *text)
{
uint32_t ui;
if (cf_opt_uint32_nonzero(&ui, text) != CFOK || ui > 0xffff)
return CFINVALID;
*ushortp = ui;
return CFOK;
}
int cmp_short(const short *a, const short *b)
{
return *a < *b ? -1 : *a > *b ? 1 : 0;
}
int cmp_ushort(const unsigned short *a, const unsigned short *b)
{
return *a < *b ? -1 : *a > *b ? 1 : 0;
}
int cmp_sid(const sid_t *a, const sid_t *b)
{
return memcmp(a->binary, b->binary, sizeof a->binary);
}
int vld_argv(const struct cf_om_node *parent, struct config_argv *array, int result)
{
unsigned short last_key = 0;
int i;
if (array->ac) {
unsigned short last_key = array->av[0].key;
for (i = 1; i < array->ac; ++i) {
unsigned short key = array->av[i].key;
if (last_key > key) {
cf_warn_node(parent, NULL, "array is not sorted");
return CFERROR;
}
last_key = key;
}
}
for (i = 0; i < array->ac; ++i) {
unsigned short key = array->av[i].key;
assert(key >= 1);
assert(key >= last_key);
if (last_key == key) {
char labelkey[12];
sprintf(labelkey, "%u", last_key);
cf_warn_duplicate_node(parent, labelkey);
result |= CFDUPLICATE;
}
while (++last_key < key && last_key <= sizeof(array->av)) {
char labelkey[12];
sprintf(labelkey, "%u", last_key);
cf_warn_missing_node(parent, labelkey);
result |= CFINCOMPLETE;
}
last_key = key;
}
return result;
}
int cf_opt_in_addr(struct in_addr *addrp, const char *text)
{
struct in_addr addr;
if (!inet_aton(text, &addr))
return CFINVALID;
*addrp = addr;
return CFOK;
}
int cf_opt_uint16(uint16_t *intp, const char *text)
{
uint16_t ui = 0;
const char *p;
for (p = text; isdigit(*p); ++p) {
uint16_t oui = ui;
ui = ui * 10 + *p - '0';
if (ui / 10 != oui)
break;
}
if (*p)
return CFINVALID;
*intp = ui;
return CFOK;
}
int cf_opt_uint16_nonzero(uint16_t *intp, const char *text)
{
uint16_t ui;
if (cf_opt_uint16(&ui, text) != CFOK || ui == 0)
return CFINVALID;
*intp = ui;
return CFOK;
}
int cf_opt_sid(sid_t *sidp, const char *text)
{
if (!str_is_subscriber_id(text))
return CFINVALID;
size_t n = fromhex(sidp->binary, text, SID_SIZE);
assert(n == SID_SIZE);
return CFOK;
}
int cf_opt_rhizome_bk(rhizome_bk_t *bkp, const char *text)
{
if (!rhizome_str_is_bundle_key(text))
return CFINVALID;
size_t n = fromhex(bkp->binary, text, RHIZOME_BUNDLE_KEY_BYTES);
assert(n == RHIZOME_BUNDLE_KEY_BYTES);
return CFOK;
}
int cf_opt_interface_type(short *typep, const char *text)
{
if (strcasecmp(text, "ethernet") == 0) {
*typep = OVERLAY_INTERFACE_ETHERNET;
return CFOK;
}
if (strcasecmp(text, "wifi") == 0) {
*typep = OVERLAY_INTERFACE_WIFI;
return CFOK;
}
if (strcasecmp(text, "catear") == 0) {
*typep = OVERLAY_INTERFACE_PACKETRADIO;
return CFOK;
}
if (strcasecmp(text, "other") == 0) {
*typep = OVERLAY_INTERFACE_UNKNOWN;
return CFOK;
}
return CFINVALID;
}
int cf_opt_pattern_list(struct pattern_list *listp, const char *text)
{
struct pattern_list list;
memset(&list, 0, sizeof list);
const char *word = NULL;
const char *p;
for (p = 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]))
return CFARRAYOVERFLOW;
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;
}
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
* single text string of the form:
*
* ( "+" | "-" ) [ interfacename ] [ "=" type ] [ ":" port [ ":" speed ] ]
*
* where:
*
* "+" means include the interface
* "-" means exclude the interface
*
* The original implementation applied include/exclude matching in the order that the list was
* given, but the new implementation applies all exclusions before apply inclusions. This should
* not be a problem, as there were no known uses that depended on testing an inclusion before an
* exclusion.
*
* An empty 'interfacename' matches all interfaces. So a "+" by itself includes all interfaces,
* and a '-' by itself excludes all interfaces. These two rules are applied after all other
* interface inclusions/exclusions are tested, otherwise "-" would overrule all other interfaces.
*
* The optional 'type' tells DNA how to handle the interface in terms of bandwidth:distance
* relationship for calculating tick times etc.
*
* The optional 'port' is the port number to bind all interfaces, instead of the default.
*
* The optional 'speed' is the nominal bits/second bandwidth of the interface, instead of the
* default. It is expressed as a positive integer with an optional scaling suffix, eg, "150k",
* "1K", "900M".
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
static int cf_opt_network_interface_legacy(struct config_network_interface *nifp, const char *text)
{
//DEBUGF("%s text=%s", __FUNCTION__, alloca_str_toprint(text));
struct config_network_interface nif;
(&nif);
cf_dfl_config_network_interface(&nif);
if (text[0] != '+' && text[0] != '-')
return CFINVALID; // "Sign must be + or -"
nif.exclude = (text[0] == '-');
const char *const endtext = text + strlen(text);
const char *name = text + 1;
const char *p = strpbrk(name, "=:");
if (!p)
p = endtext;
size_t len = p - name;
if (name[0] == '>') {
if (len - 1 >= sizeof(nif.dummy))
return CFSTRINGOVERFLOW;
strncpy(nif.dummy, &name[1], len - 1)[len - 1] = '\0';
nif.match.patc = 0;
} else {
int star = (strchr(name, '*') != NULL) ? 1 : 0;
if (len + star >= sizeof(nif.match.patv[0]))
return CFSTRINGOVERFLOW;
strncpy(nif.match.patv[0], name, len)[len + star] = '\0';
if (star)
nif.match.patv[0][len] = '*';
nif.match.patc = 1;
}
if (*p == '=') {
const char *const type = p + 1;
p = strchr(type, ':');
if (!p)
p = endtext;
len = p - type;
if (len) {
char buf[len + 1];
strncpy(buf, type, len)[len] = '\0';
int result = cf_opt_interface_type(&nif.type, buf);
switch (result) {
case CFERROR: return CFERROR;
case CFOK: break;
default: return result; // "Invalid interface type"
}
}
}
if (*p == ':') {
const char *const port = p + 1;
p = strchr(port, ':');
if (!p)
p = endtext;
len = p - port;
if (len) {
char buf[len + 1];
strncpy(buf, port, len)[len] = '\0';
int result = cf_opt_uint16_nonzero(&nif.port, buf);
switch (result) {
case CFERROR: return CFERROR;
case CFOK: break;
default: return result; // "Invalid interface port number"
}
}
}
if (*p == ':') {
const char *const speed = p + 1;
p = endtext;
len = p - speed;
if (len) {
char buf[len + 1];
strncpy(buf, speed, len)[len] = '\0';
int result = cf_opt_uint64_scaled(&nif.speed, buf);
switch (result) {
case CFERROR: return CFERROR;
case CFOK: break;
default: return result; // "Invalid interface speed"
}
if (nif.speed < 1)
return CFINVALID; // "Interfaces must be capable of at least 1 bit per second"
}
}
if (*p)
return CFINVALID; // "Extra junk at end of interface specification"
*nifp = nif;
return CFOK;
}
int cf_opt_network_interface(struct config_network_interface *nifp, const struct cf_om_node *node)
{
if (!node->text)
return cf_opt_config_network_interface(nifp, node);
if (node->nodc) {
cf_warn_incompatible_children(node);
return CFINCOMPATIBLE;
}
return cf_opt_network_interface_legacy(nifp, node->text);
}
int vld_network_interface(const struct cf_om_node *parent, struct config_network_interface *nifp, int result)
{
if (nifp->match.patc != 0 && nifp->dummy[0]) {
int nodei_match = cf_om_get_child(parent, "match", NULL);
int nodei_dummy = cf_om_get_child(parent, "dummy", NULL);
assert(nodei_match != -1);
assert(nodei_dummy != -1);
cf_warn_incompatible(parent->nodv[nodei_match], parent->nodv[nodei_dummy]);
return result | CFSUB(CFINCOMPATIBLE);
}
if (nifp->match.patc == 0 && !nifp->dummy[0]) {
DEBUGF("dummy=%s", alloca_str_toprint(nifp->dummy));
cf_warn_missing_node(parent, "match");
return result | CFINCOMPLETE;
}
return result;
}
/* Config parse function. Implements the original form of the 'interfaces' config option. Parses a
* comma-separated list of interface rules (see cf_opt_network_interface_legacy() for the format of
* each rule), then parses the regular config array-of-struct style interface option settings so
* that both forms are supported.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int cf_opt_interface_list(struct config_interface_list *listp, const struct cf_om_node *node)
{
if (!node->text)
return cf_opt_config_interface_list(listp, node);
if (node->nodc) {
cf_warn_incompatible_children(node);
return CFINCOMPATIBLE;
}
const char *p;
const char *arg = NULL;
unsigned n = listp->ac;
int result = CFOK;
for (p = node->text; n < NELS(listp->av); ++p) {
if (*p == '\0' || *p == ',' || isspace(*p)) {
if (arg) {
int len = p - arg;
if (len > 80) {
result |= CFSTRINGOVERFLOW;
goto bye;
}
char buf[len + 1];
strncpy(buf, arg, len)[len] = '\0';
int ret = cf_opt_network_interface_legacy(&listp->av[n].value, buf);
switch (ret) {
case CFERROR: return CFERROR;
case CFOK:
listp->av[n].key = n;
++n;
break;
default: {
strbuf b = strbuf_alloca(180);
strbuf_cf_flag_reason(b, ret);
cf_warn_node(node, NULL, "invalid interface rule %s -- %s", alloca_str_toprint(buf), strbuf_str(b)); \
result |= CFSUB(ret);
break;
}
}
arg = NULL;
}
if (!*p)
break;
} else if (!arg)
arg = p;
}
if (*p) {
result |= CFARRAYOVERFLOW;
goto bye;
}
assert(n <= NELS(listp->av));
listp->ac = n;
bye:
if (listp->ac == 0)
result |= CFEMPTY;
return result;
}

327
conf_schema.h Normal file
View File

@ -0,0 +1,327 @@
/*
Serval DNA configuration
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* This file contains definitions for the schema of the Serval DNA configuration file. See comments
* in "config.h" for a description of the internal configuration API.
*
* A configuration schema is set of nested structures and arrays. By convention, the top level, or
* root of the schema is called "main", but every structure and array has its own complete API which
* can be used by itself. So if there were two independent configuration files, both could be
* defined in this file, each with a conventional name for its own root element.
*
* A configuration file consists of lines of the form:
*
* FULLKEY "=" VALUE "\n"
*
* where FULLKEY has the form KEY [ "." KEY [ "." KEY [ ... ] ] ] and VALUE is any string not
* containing newline. Lines ending with "\r\n" have the "\r" stripped from the end of VALUE.
* Otherwise VALUE is preserved exactly, with all leading and trailing spaces intact.
*
* To describe a configuration file that looks like this:
*
* some.thing.element1=integer
* some.thing.element2=string
* some.list.foo.element1=integer
* some.list.foo.element2=string
* some.list.bar.element1=integer
* some.list.bar.element2=string
* another_thing=http://my.host.com:1234/path/to/nowhere
*
* the following schema would do:
*
* STRUCT(happy)
* ATOM(int32_t, element1, 0, cf_opt_int32_nonnegative,, "An integer >= 0")
* STRING(80, element2, "boo!", cf_opt_str_nonempty, MANDATORY, "A non-empty string")
* END_STRUCT
*
* ARRAY(joy,)
* KEY_STRING(3, happy, cf_opt_str)
* VALUE_SUB_STRUCT(happy)
* END_ARRAY(16)
*
* STRUCT(love)
* SUB_STRUCT(happy, thing,)
* SUB_STRUCT(joy, list,)
* END_STRUCT
*
* STRUCT(main)
* SUB_STRUCT(love, some,)
* STRING(128, another_thing, "", cf_opt_uri,, "URL; protocol://hostname[:port][/path]")
* END_STRUCT
*
* which would produce an API based on the following definitions (see "config.h" for more
* information):
*
* struct config_happy {
* int32_t element1;
* char element2[81];
* };
* struct config_joy {
* unsigned ac;
* struct config_joy__element {
* char key[4];
* struct config_happy value;
* } av[16];
* };
* struct config_love {
* struct config_happy thing;
* struct config_joy list;
* };
* struct config_main {
* struct config_love some;
* char another_thing[129];
* };
*
* A schema definition is composed from the following STRUCT and ARRAY definitions:
*
* STRUCT(name[, validatorfunc])
* element-declaration
* element-declaration
* ...
* END_STRUCT
*
* where each element-declaration is one of:
*
* ATOM(type, element, default, parsefunc, flags, comment)
* NODE(type, element, default, parsefunc, flags, comment)
* STRING(strlen, element, default, parsefunc, flags, comment)
* SUB_STRUCT(structname, element, flags)
* NODE_STRUCT(structname, element, parsefunc, flags)
*
* ARRAY(name, flags[, validatorfunc])
* key-declaration
* value-declaration
* END_ARRAY(size)
*
* where key-declaration is one of:
*
* KEY_ATOM(type, parsefunc[, comparefunc])
* KEY_STRING(strlen, parsefunc[, comparefunc])
*
* and value-declaration is one of:
*
* VALUE_ATOM(type, parsefunc)
* VALUE_STRING(strlen, parsefunc)
* VALUE_NODE(type, parsefunc)
* VALUE_SUB_STRUCT(structname)
* VALUE_NODE_STRUCT(structname, parsefunc)
*
* The meanings of the parameters are:
*
* 'name'
* 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.
* 'strlen'
* For STRING, LABEL_STRING and VALUE_STRING, gives the maximum length of the string. The
* string is declared as an array of char[strlen+1] to leave room for a terminating nul.
* 'size'
* For all ARRAYs, gives the maximum size of the array.
* 'type'
* Used for ATOM, NODE, LABEL_ATOM, VALUE_ATOM and VALUE_NODE declarations. Gives the C type
* of the element. For STRING, KEY_STRING and VALUE_STRING this is implicitly a char[].
* 'structname'
* Only used for SUB_STRUCT, NODE_STRUCT, VALUE_SUB_STRUCT and VALUE_NODE_STRUCT declarations.
* Identifies a sub- structure by 'name' to nest in the enclosing struct or array.
* 'element'
* The name of the struct element and the key in the configuration file. This name does appear
* in the config file and also in the API, so that an option mamed "some.thing.element1" in the
* file is referred to as some.thing.element1 in the C code. Arrays are more complicated:
* "some.list.foo.element1" in the config file is referred to as some.list.av[n].value.element1
* in the C code, and some.list.ac gives the size of the some.list.av array.
* 'default'
* 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 a VALUE from the config file for a STRUCT element, or a KEY or
* VALUE for an array element. Parse functions for ATOM, STRING, KEY_ATOM, KEY_STRING,
* VALUE_ATOM and VALUE_STRING all take a string argument (const char *) which is a
* nul-terminated text. The parse functions for NODE, NODE_STRUCT, VALUE_NODE and
* VALUE_NODE_STRUCT take a pointer to a COM node (const struct cf_om_node *), and are
* responsible for parsing the node's text and all of its descendents (children).
* 'comparefunc'
* A function used to sort an array after all elements have been parsed, and before being
* validated (see below).
* '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
* the case of struct elements that are arrays, the config file must set at least one element
* of the array, or parsing fails.
* 'comment'
* A human-readable string describing the value of the configuration option. Must be
* informative enough to help users diagnose parse errors. Eg, "An integer" is not enough;
* better would be "Integer >= 0, number of seconds since Unix epoch".
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
STRUCT(log)
STRING(256, file, "", cf_opt_absolute_path,, "Absolute path of log file")
ATOM(int, show_pid, 1, cf_opt_boolean,, "If true, all log lines contain PID of logging process")
ATOM(int, show_time, 1, cf_opt_boolean,, "If true, all log lines contain time stamp")
END_STRUCT
STRUCT(server)
STRING(256, chdir, "/", cf_opt_absolute_path,, "Absolute path of chdir(2) for server process")
STRING(256, dummy_interface_dir, "", cf_opt_str_nonempty,, "Path of directory containing dummy interface files, either absolute or relative to instance directory")
ATOM(int, respawn_on_crash, 0, cf_opt_boolean,, "If true, server will exec(2) itself on fatal signals, eg SEGV")
END_STRUCT
STRUCT(monitor)
STRING(256, socket, DEFAULT_MONITOR_SOCKET_NAME, cf_opt_str_nonempty,, "Name of socket for monitor interface")
ATOM(int, uid, -1, cf_opt_int,, "Allowed UID for monitor socket client")
END_STRUCT
STRUCT(mdp_iftype)
ATOM(uint32_t, tick_ms, 0, cf_opt_uint32_nonzero,, "Tick interval for this interface type")
END_STRUCT
ARRAY(mdp_iftypelist, NO_DUPLICATES)
KEY_ATOM(short, cf_opt_interface_type, cmp_short)
VALUE_SUB_STRUCT(mdp_iftype)
END_ARRAY(5)
STRUCT(mdp)
STRING(256, socket, DEFAULT_MDP_SOCKET_NAME, cf_opt_str_nonempty,, "Name of socket for MDP client interface")
SUB_STRUCT(mdp_iftypelist, iftype,)
END_STRUCT
STRUCT(olsr)
ATOM(int, enable, 1, cf_opt_boolean,, "If true, OLSR is used for mesh routing")
ATOM(uint16_t, remote_port,4130, cf_opt_uint16_nonzero,, "Remote port number")
ATOM(uint16_t, local_port, 4131, cf_opt_uint16_nonzero,, "Local port number")
END_STRUCT
ARRAY(argv, SORTED NO_DUPLICATES, vld_argv)
KEY_ATOM(unsigned short, cf_opt_ushort_nonzero, cmp_ushort)
VALUE_STRING(128, cf_opt_str)
END_ARRAY(16)
STRUCT(executable)
STRING(256, executable, "", cf_opt_absolute_path, MANDATORY, "Absolute path of dna helper executable")
SUB_STRUCT(argv, argv,)
END_STRUCT
STRUCT(dna)
SUB_STRUCT(executable, helper,)
END_STRUCT
STRUCT(rhizome_peer)
STRING(25, protocol, "http", cf_opt_protocol,, "Protocol name")
STRING(256, host, "", cf_opt_str_nonempty, MANDATORY, "Host name or IP address")
ATOM(uint16_t, port, RHIZOME_HTTP_PORT, cf_opt_uint16_nonzero,, "Port number")
END_STRUCT
ARRAY(peerlist,)
KEY_STRING(15, cf_opt_str)
VALUE_NODE_STRUCT(rhizome_peer, cf_opt_rhizome_peer)
END_ARRAY(10)
STRUCT(rhizome_direct)
SUB_STRUCT(peerlist, peer,)
END_STRUCT
STRUCT(rhizome_api_addfile)
STRING(64, uri_path, "", cf_opt_absolute_path,, "URI path for HTTP add-file request")
ATOM(struct in_addr, allow_host, (struct in_addr){htonl(INADDR_LOOPBACK)}, cf_opt_in_addr,, "IP address of host allowed to make HTTP add-file request")
STRING(256, manifest_template_file, "", cf_opt_str_nonempty,, "Path of manifest template file, either absolute or relative to instance directory")
ATOM(sid_t, default_author, SID_ANY, cf_opt_sid,, "Author of add-file bundle if sender not given")
ATOM(rhizome_bk_t, bundle_secret_key, RHIZOME_BK_NONE, cf_opt_rhizome_bk,, "Secret key of add-file bundle to try if sender not given")
END_STRUCT
STRUCT(rhizome_api)
SUB_STRUCT(rhizome_api_addfile, addfile,)
END_STRUCT
STRUCT(rhizome_http)
ATOM(int, enable, 1, cf_opt_boolean,, "If true, Rhizome HTTP server is started")
END_STRUCT
STRUCT(rhizome_mdp)
ATOM(int, enable, 1, cf_opt_boolean,, "If true, Rhizome MDP server is started")
END_STRUCT
STRUCT(rhizome_advertise)
ATOM(int, enable, 1, cf_opt_boolean,, "If true, Rhizome advertisements are sent")
END_STRUCT
STRUCT(rhizome)
ATOM(int, enable, 1, cf_opt_boolean,, "If true, server opens Rhizome database when starting")
STRING(256, datastore_path, "", cf_opt_absolute_path,, "Path of rhizome storage directory, absolute or relative to instance directory")
ATOM(uint64_t, database_size, 1000000, cf_opt_uint64_scaled,, "Size of database in bytes")
ATOM(uint32_t, fetch_delay_ms, 50, cf_opt_uint32_nonzero,, "Delay from receiving first bundle advert to initiating fetch")
SUB_STRUCT(rhizome_direct, direct,)
SUB_STRUCT(rhizome_api, api,)
SUB_STRUCT(rhizome_http, http,)
SUB_STRUCT(rhizome_mdp, mdp,)
SUB_STRUCT(rhizome_advertise, advertise,)
END_STRUCT
STRUCT(directory)
ATOM(sid_t, service, SID_ANY, cf_opt_sid,, "Subscriber ID of Serval Directory Service")
END_STRUCT
STRUCT(host)
STRING(INTERFACE_NAME_STRLEN, interface, "", cf_opt_str_nonempty, MANDATORY, "Interface name")
ATOM(struct in_addr, address, (struct in_addr){htonl(INADDR_NONE)}, cf_opt_in_addr, MANDATORY, "Host IP address")
ATOM(uint16_t, port, PORT_DNA, cf_opt_uint16_nonzero,, "Port number")
END_STRUCT
ARRAY(host_list, NO_DUPLICATES)
KEY_ATOM(sid_t, cf_opt_sid, cmp_sid)
VALUE_SUB_STRUCT(host)
END_ARRAY(32)
STRUCT(network_interface, vld_network_interface)
ATOM(int, exclude, 0, cf_opt_boolean,, "If true, do not use matching interfaces")
ATOM(struct pattern_list, match, PATTERN_LIST_EMPTY, cf_opt_pattern_list,, "Names that match network interface")
STRING(256, dummy, "", cf_opt_str_nonempty,, "Path of dummy file, absolute or relative to server.dummy_interface_dir")
ATOM(short, type, OVERLAY_INTERFACE_WIFI, cf_opt_interface_type,, "Type of network interface")
ATOM(uint16_t, port, RHIZOME_HTTP_PORT, cf_opt_uint16_nonzero,, "Port number for network interface")
ATOM(uint64_t, speed, 1000000, cf_opt_uint64_scaled,, "Speed in bits per second")
ATOM(int, mdp_tick_ms, -1, cf_opt_int32_nonneg,, "Override MDP tick interval for this interface")
ATOM(int, default_route, 0, cf_opt_boolean,, "If true, use this interface as a default route")
END_STRUCT
ARRAY(interface_list, SORTED NO_DUPLICATES)
KEY_ATOM(unsigned, cf_opt_uint)
VALUE_NODE_STRUCT(network_interface, cf_opt_network_interface)
END_ARRAY(10)
// The top level.
STRUCT(main)
NODE_STRUCT(interface_list, interfaces, cf_opt_interface_list,)
SUB_STRUCT(log, log,)
SUB_STRUCT(server, server,)
SUB_STRUCT(monitor, monitor,)
SUB_STRUCT(mdp, mdp,)
SUB_STRUCT(dna, dna,)
NODE(debugflags_t, debug, 0, cf_opt_debugflags, USES_CHILDREN, "Debug flags")
SUB_STRUCT(rhizome, rhizome,)
SUB_STRUCT(directory, directory,)
SUB_STRUCT(olsr, olsr,)
SUB_STRUCT(host_list, hosts,)
END_STRUCT

165
config_test.c Normal file
View File

@ -0,0 +1,165 @@
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include "str.h"
#include "log.h"
#include "config.h"
int main(int argc, char **argv)
{
int i;
for (i = 1; i < argc; ++i) {
int fd = open(argv[i], O_RDONLY);
if (fd == -1) {
perror("open");
exit(1);
}
struct stat st;
fstat(fd, &st);
char *buf = malloc(st.st_size);
if (!buf) {
perror("malloc");
exit(1);
}
if (read(fd, buf, st.st_size) != st.st_size) {
perror("read");
exit(1);
}
struct cf_om_node *root = NULL;
int ret = cf_parse_to_om(argv[i], buf, st.st_size, &root);
close(fd);
DEBUGF("ret = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), ret)));
//cf_dump_node(root, 0);
struct config_main config;
memset(&config, 0, sizeof config);
cf_dfl_config_main(&config);
int result = root ? cf_opt_config_main(&config, root) : CFEMPTY;
cf_free_node(&root);
free(buf);
DEBUGF("result = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), result)));
DEBUGF("config.log.file = %s", alloca_str_toprint(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.server.chdir = %s", alloca_str_toprint(config.server.chdir));
DEBUGF("config.debug = %llx", (unsigned long long) config.debug);
DEBUGF("config.directory.service = %s", alloca_tohex(config.directory.service.binary, SID_SIZE));
DEBUGF("config.rhizome.api.addfile.allow_host = %s", inet_ntoa(config.rhizome.api.addfile.allow_host));
int j;
for (j = 0; j < config.mdp.iftype.ac; ++j) {
DEBUGF("config.mdp.iftype.%u", config.mdp.iftype.av[j].key);
DEBUGF(" .tick_ms = %u", config.mdp.iftype.av[j].value.tick_ms);
}
for (j = 0; j < config.dna.helper.argv.ac; ++j) {
DEBUGF("config.dna.helper.argv.%u=%s", config.dna.helper.argv.av[j].key, config.dna.helper.argv.av[j].value);
}
for (j = 0; j < config.rhizome.direct.peer.ac; ++j) {
DEBUGF("config.rhizome.direct.peer.%s", config.rhizome.direct.peer.av[j].key);
DEBUGF(" .protocol = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.protocol));
DEBUGF(" .host = %s", alloca_str_toprint(config.rhizome.direct.peer.av[j].value.host));
DEBUGF(" .port = %u", config.rhizome.direct.peer.av[j].value.port);
}
for (j = 0; j < config.interfaces.ac; ++j) {
DEBUGF("config.interfaces.%s", config.interfaces.av[j].key);
DEBUGF(" .exclude = %d", config.interfaces.av[j].value.exclude);
DEBUGF(" .match = [");
int k;
for (k = 0; k < config.interfaces.av[j].value.match.patc; ++k)
DEBUGF(" %s", alloca_str_toprint(config.interfaces.av[j].value.match.patv[k]));
DEBUGF(" ]");
DEBUGF(" .type = %d", config.interfaces.av[j].value.type);
DEBUGF(" .port = %u", config.interfaces.av[j].value.port);
DEBUGF(" .speed = %llu", (unsigned long long) config.interfaces.av[j].value.speed);
}
for (j = 0; j < config.hosts.ac; ++j) {
char sidhex[SID_STRLEN + 1];
tohex(sidhex, config.hosts.av[j].key.binary, SID_SIZE);
DEBUGF("config.hosts.%s", sidhex);
DEBUGF(" .interface = %s", alloca_str_toprint(config.hosts.av[j].value.interface));
DEBUGF(" .address = %s", inet_ntoa(config.hosts.av[j].value.address));
DEBUGF(" .port = %u", config.hosts.av[j].value.port);
}
}
exit(0);
}
const struct __sourceloc __whence = __NOWHERE__;
static const char *_trimbuildpath(const char *path)
{
/* Remove common path prefix */
int lastsep = 0;
int i;
for (i = 0; __FILE__[i] && path[i]; ++i) {
if (i && path[i - 1] == '/')
lastsep = i;
if (__FILE__[i] != path[i])
break;
}
return &path[lastsep];
}
void logMessage(int level, struct __sourceloc whence, const char *fmt, ...)
{
const char *levelstr = "UNKWN:";
switch (level) {
case LOG_LEVEL_FATAL: levelstr = "FATAL:"; break;
case LOG_LEVEL_ERROR: levelstr = "ERROR:"; break;
case LOG_LEVEL_INFO: levelstr = "INFO:"; break;
case LOG_LEVEL_WARN: levelstr = "WARN:"; break;
case LOG_LEVEL_DEBUG: levelstr = "DEBUG:"; break;
}
fprintf(stderr, "%s ", levelstr);
if (whence.file) {
fprintf(stderr, "%s", _trimbuildpath(whence.file));
if (whence.line)
fprintf(stderr, ":%u", whence.line);
if (whence.function)
fprintf(stderr, ":%s()", whence.function);
fputc(' ', stderr);
} else if (whence.function) {
fprintf(stderr, "%s() ", whence.function);
}
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
fputc('\n', stderr);
}
debugflags_t debugFlagMask(const char *flagname)
{
if (!strcasecmp(flagname,"all")) return ~0;
else if (!strcasecmp(flagname,"interfaces")) return 1 << 0;
else if (!strcasecmp(flagname,"rx")) return 1 << 1;
else if (!strcasecmp(flagname,"tx")) return 1 << 2;
else if (!strcasecmp(flagname,"verbose")) return 1 << 3;
else if (!strcasecmp(flagname,"verbio")) return 1 << 4;
else if (!strcasecmp(flagname,"peers")) return 1 << 5;
else if (!strcasecmp(flagname,"dnaresponses")) return 1 << 6;
else if (!strcasecmp(flagname,"dnahelper")) return 1 << 7;
else if (!strcasecmp(flagname,"vomp")) return 1 << 8;
else if (!strcasecmp(flagname,"packetformats")) return 1 << 9;
else if (!strcasecmp(flagname,"packetconstruction")) return 1 << 10;
else if (!strcasecmp(flagname,"gateway")) return 1 << 11;
else if (!strcasecmp(flagname,"keyring")) return 1 << 12;
else if (!strcasecmp(flagname,"sockio")) return 1 << 13;
else if (!strcasecmp(flagname,"frames")) return 1 << 14;
else if (!strcasecmp(flagname,"abbreviations")) return 1 << 15;
else if (!strcasecmp(flagname,"routing")) return 1 << 16;
else if (!strcasecmp(flagname,"security")) return 1 << 17;
else if (!strcasecmp(flagname,"rhizome")) return 1 << 18;
else if (!strcasecmp(flagname,"rhizometx")) return 1 << 19;
else if (!strcasecmp(flagname,"rhizomerx")) return 1 << 20;
else if (!strcasecmp(flagname,"rhizomeads")) return 1 << 21;
else if (!strcasecmp(flagname,"monitorroutes")) return 1 << 22;
else if (!strcasecmp(flagname,"queues")) return 1 << 23;
else if (!strcasecmp(flagname,"broadcasts")) return 1 << 24;
else if (!strcasecmp(flagname,"manifests")) return 1 << 25;
else if (!strcasecmp(flagname,"mdprequests")) return 1 << 26;
else if (!strcasecmp(flagname,"timing")) return 1 << 27;
return 0;
}

View File

@ -19,6 +19,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef __SERVALD_CONSTANTS_H
#define __SERVALD_CONSTANTS_H
#define NELS(a) (sizeof (a) / sizeof *(a))
/* Packet format:
16 bit - Magic value 0x4110

View File

@ -106,137 +106,6 @@ int strn_is_did(const char *did, size_t *lenp)
return 1;
}
int extractDid(unsigned char *packet,int *ofs,char *did)
{
int d=0;
int highP=1;
int nybl;
nybl=0;
while(nybl!=0xf&&(d<64))
{
if (highP) nybl=packet[*ofs]>>4; else nybl=packet[*ofs]&0xf;
if (nybl<0xa) did[d++]='0'+nybl;
else
switch(nybl) {
case 0xa: did[d++]='*'; break;
case 0xb: did[d++]='#'; break;
case 0xc: did[d++]='+'; break;
}
if (highP) highP=0; else { (*ofs)++; highP=1; }
}
if (d>63) return WHY("DID too long");
did[d]=0;
return 0;
}
int stowDid(unsigned char *packet,int *ofs,char *did)
{
int highP=1;
int nybl;
int d=0;
int len=0;
if (debug&DEBUG_PACKETFORMATS) printf("Packing DID \"%s\"\n",did);
while(did[d]&&(d<DID_MAXSIZE))
{
switch(did[d])
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
nybl=did[d]-'0'; break;
case '*': nybl=0xa; break;
case '#': nybl=0xb; break;
case '+': nybl=0xc; break;
default:
WHY("Illegal digits in DID number");
return -1;
}
if (highP) { packet[*ofs]=nybl<<4; highP=0; }
else {
packet[(*ofs)++]|=nybl; highP=1;
len++;
}
d++;
}
if (d>=DID_MAXSIZE)
{
WHY("DID number too long");
return -1;
}
/* Append end of number code, filling the whole byte for fast and easy comparison */
if (highP) packet[(*ofs)++]=0xff;
else packet[(*ofs)++]|=0x0f;
len++;
/* Fill remainder of field with randomness to protect any encryption */
for(;len<SID_SIZE;len++) packet[(*ofs)++]=random()&0xff;
return 0;
}
int extractSid(const unsigned char *packet, int *ofs, char *sid)
{
(void) tohex(sid, packet + *ofs, SID_SIZE);
*ofs += SID_SIZE;
return 0;
}
int stowSid(unsigned char *packet, int ofs, const char *sid)
{
if (debug & DEBUG_PACKETFORMATS)
printf("stowing SID \"%s\"\n", sid);
if (strcasecmp(sid,"broadcast") == 0)
memset(packet + ofs, 0xff, SID_SIZE);
else if (fromhex(packet + ofs, sid, SID_SIZE) != SID_SIZE || sid[SID_STRLEN] != '\0')
return WHY("invalid SID");
return 0;
}
int is_uri_char_scheme(char c)
{
return isalpha(c) || isdigit(c) || c == '+' || c == '-' || c == '.';
}
int is_uri_char_unreserved(char c)
{
return isalpha(c) || isdigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
}
int is_uri_char_reserved(char c)
{
switch (c) {
case ':': case '/': case '?': case '#': case '[': case ']': case '@':
case '!': case '$': case '&': case '\'': case '(': case ')':
case '*': case '+': case ',': case ';': case '=':
return 1;
}
return 0;
}
/* Return true if the string resembles a URI.
Based on RFC-3986 generic syntax, assuming nothing about the hierarchical part.
@author Andrew Bettison <andrew@servalproject.com>
*/
int str_is_uri(const char *uri)
{
const char *p = uri;
// Scheme is ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
if (!isalpha(*p++))
return 0;
while (is_uri_char_scheme(*p))
++p;
// Scheme is followed by colon ":".
if (*p++ != ':')
return 0;
// Hierarchical part must contain only valid characters.
const char *q = p;
while (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p))
++p;
return p != q && *p == '\0';
}
void write_uint64(unsigned char *o,uint64_t v)
{
int i;

View File

@ -15,6 +15,7 @@
#include "serval.h"
#include "str.h"
#include "overlay_address.h"
#include "conf.h"
struct subscriber *directory_service;
@ -77,18 +78,9 @@ static void directory_send_keyring(struct subscriber *directory_service){
static int load_directory_config(){
if (!directory_service){
const char *sid_hex = confValueGet("directory.service", NULL);
if (!sid_hex)
return 0;
unsigned char sid[SID_SIZE];
if (stowSid(sid, 0, sid_hex)==-1)
return WHYF("Invalid directory server SID %s", sid_hex);
directory_service = find_subscriber(sid, SID_SIZE, 1);
directory_service = find_subscriber(config.directory.service.binary, SID_SIZE, 1);
if (!directory_service)
return WHYF("Failed to create subscriber record");
// used by tests
INFOF("ADD DIRECTORY SERVICE %s", alloca_tohex_sid(directory_service->sid));
}

View File

@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <sys/stat.h>
#include <signal.h>
#include "serval.h"
#include "conf.h"
#include "str.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
@ -148,9 +149,7 @@ dna_helper_close_pipes()
int
dna_helper_start()
{
const char *command = confValueGet("dna.helper.executable", NULL);
const char *arg = confValueGet("dna.helper.argv.1", NULL);
if (!command || !command[0]) {
if (!config.dna.helper.executable[0]) {
/* Check if we have a helper configured. If not, then set
dna_helper_pid to magic value of 0 so that we don't waste time
in future looking up the dna helper configuration value. */
@ -182,7 +181,14 @@ dna_helper_start()
close(stdout_fds[1]);
return -1;
}
// Construct argv[] for execv() and log messages.
const char *argv[config.dna.helper.argv.ac + 2];
argv[0] = config.dna.helper.executable;
int i;
for (i = 0; i < config.dna.helper.argv.ac; ++i)
argv[i + 1] = config.dna.helper.argv.av[i].value;
argv[i + 1] = NULL;
strbuf argv_sb = strbuf_append_argv(strbuf_alloca(1024), config.dna.helper.argv.ac + 1, argv);
switch (dna_helper_pid = fork()) {
case 0:
/* Child, should exec() to become helper after installing file descriptors. */
@ -197,11 +203,14 @@ dna_helper_start()
fflush(stderr);
_exit(-1);
}
/* XXX: Need the cast on Solaris because it defins NULL as 0L and gcc doesn't
* see it as a sentinal */
execl(command, command, arg, (void *)NULL);
LOGF_perror(LOG_LEVEL_FATAL, "execl(%s, %s, %s, NULL)", command, command, arg ? arg : "NULL");
fflush(stderr);
{
execv(config.dna.helper.executable, (char **)argv);
LOGF_perror(LOG_LEVEL_FATAL, "execl(%s, [%s])",
alloca_str_toprint(config.dna.helper.executable),
strbuf_str(argv_sb)
);
fflush(stderr);
}
do { _exit(-1); } while (1);
break;
case -1:
@ -223,13 +232,13 @@ dna_helper_start()
dna_helper_stdin = stdin_fds[1];
dna_helper_stdout = stdout_fds[0];
dna_helper_stderr = stderr_fds[0];
INFOF("STARTED DNA HELPER pid=%u stdin=%d stdout=%d stderr=%d executable=%s arg=%s",
INFOF("STARTED DNA HELPER pid=%u stdin=%d stdout=%d stderr=%d executable=%s argv=[%s]",
dna_helper_pid,
dna_helper_stdin,
dna_helper_stdout,
dna_helper_stderr,
command,
arg ? arg : "NULL"
alloca_str_toprint(config.dna.helper.executable),
strbuf_str(argv_sb)
);
sched_requests.function = monitor_requests;
sched_requests.context = NULL;

View File

@ -5,10 +5,14 @@ HDRS= fifo.h \
overlay_packet.h \
rhizome.h \
serval.h \
str.h \
mem.h \
os.h \
strbuf.h \
strbuf_helpers.h \
sha2.h \
conf.h \
conf_schema.h \
crypto.h \
log.h \
net.h \

57
instance.c Normal file
View File

@ -0,0 +1,57 @@
/*
Serval DNA instance directory path
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include "serval.h"
#include "os.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
static char *thisinstancepath = NULL;
const char *serval_instancepath()
{
if (thisinstancepath)
return thisinstancepath;
const char *instancepath = getenv("SERVALINSTANCE_PATH");
if (!instancepath)
instancepath = DEFAULT_INSTANCE_PATH;
return instancepath;
}
void serval_setinstancepath(const char *instancepath)
{
if (thisinstancepath == NULL)
free(thisinstancepath);
thisinstancepath = strdup(instancepath);
}
int form_serval_instance_path(char *buf, size_t bufsiz, const char *path)
{
strbuf b = strbuf_local(buf, bufsiz);
strbuf_path_join(b, serval_instancepath(), path, NULL);
if (!strbuf_overrun(b))
return 1;
WHYF("Cannot form pathname \"%s/%s\" -- buffer too small (%lu bytes)", serval_instancepath(), path, (unsigned long)bufsiz);
return 0;
}
int create_serval_instance_dir() {
return mkdirs(serval_instancepath(), 0700);
}

40
log.c
View File

@ -46,8 +46,6 @@ const struct __sourceloc __whence = __NOWHERE__;
debugflags_t debug = 0;
static FILE *logfile = NULL;
static int flag_show_pid = -1;
static int flag_show_time = -1;
/* The logbuf is used to accumulate log messages before the log file is open and ready for
writing.
@ -78,11 +76,11 @@ static FILE *_open_logging()
if (!logfile) {
const char *logpath = getenv("SERVALD_LOG_FILE");
if (!logpath) {
if (confBusy())
if (cf_limbo)
return NULL;
logpath = confValueGet("log.file", NULL);
logpath = config.log.file;
}
if (!logpath) {
if (!logpath || !logpath[0]) {
logfile = stderr; //fopen("/tmp/foo", "a");
INFO("No logfile configured -- logging to stderr");
} else if ((logfile = fopen(logpath, "a"))) {
@ -102,20 +100,6 @@ FILE *open_logging()
return _open_logging();
}
static int show_pid()
{
if (flag_show_pid < 0 && !confBusy())
flag_show_pid = confValueGetBoolean("log.show_pid", 0);
return flag_show_pid;
}
static int show_time()
{
if (flag_show_time < 0 && !confBusy())
flag_show_time = confValueGetBoolean("log.show_time", 0);
return flag_show_time;
}
void close_logging()
{
if (logfile) {
@ -144,10 +128,8 @@ static int _log_prepare(int level, struct __sourceloc whence)
return 0;
struct timeval tv;
tv.tv_sec = 0;
int showtime = show_time();
if (showtime)
if (config.log.show_time)
gettimeofday(&tv, NULL);
int showpid = show_pid();
_open_logging(); // Put initial INFO message at start of log file
// No calls outside log.c from this point on.
if (strbuf_is_empty(&logbuf))
@ -165,9 +147,9 @@ static int _log_prepare(int level, struct __sourceloc whence)
}
strbuf_sprintf(&logbuf, "%-6.6s ", levelstr);
#endif
if (showpid)
if (config.log.show_pid)
strbuf_sprintf(&logbuf, "[%5u] ", getpid());
if (showtime) {
if (config.log.show_time) {
if (tv.tv_sec == 0) {
strbuf_puts(&logbuf, "NOTIME______ ");
} else {
@ -235,15 +217,7 @@ void logArgv(int level, struct __sourceloc whence, const char *label, int argc,
strbuf_puts(&logbuf, label);
strbuf_putc(&logbuf, ' ');
}
int i;
for (i = 0; i < argc; ++i) {
if (i)
strbuf_putc(&logbuf, ' ');
if (argv[i])
strbuf_toprint_quoted(&logbuf, "\"\"", argv[i]);
else
strbuf_puts(&logbuf, "NULL");
}
strbuf_append_argv(&logbuf, argc, argv);
_log_finish(level);
}
}

2
log.h
View File

@ -20,8 +20,10 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define __SERVALD_LOG_H
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <errno.h>
typedef unsigned int debugflags_t;

2
main.c
View File

@ -18,6 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "conf.h"
int main(int argc, char **argv)
{
@ -31,6 +32,7 @@ int main(int argc, char **argv)
srandomdev();
server_save_argv(argc, (const char*const*)argv);
cf_init();
int status = parseCommandLine(argv[0], argc - 1, (const char*const*)&argv[1]);
#if defined WIN32
WSACleanup();

81
mem.c Normal file
View File

@ -0,0 +1,81 @@
/*
Serval DNA memory management
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <string.h>
#include "mem.h"
void *_emalloc(struct __sourceloc __whence, size_t bytes)
{
char *new = malloc(bytes);
if (!new) {
WHYF_perror("malloc(%lu)", (long)bytes);
return NULL;
}
return new;
}
void *_emalloc_zero(struct __sourceloc __whence, size_t bytes)
{
char *new = _emalloc(__whence, bytes);
if (new)
memset(new, 0, bytes);
return new;
}
char *_strn_edup(struct __sourceloc __whence, const char *str, size_t len)
{
char *new = _emalloc(__whence, len + 1);
if (new) {
strncpy(new, str, len);
new[len] = '\0';
}
return new;
}
char *_str_edup(struct __sourceloc __whence, const char *str)
{
return _strn_edup(__whence, str, strlen(str));
}
#undef malloc
#undef calloc
#undef free
#undef realloc
#define SDM_GUARD_AFTER 16384
void *_serval_debug_malloc(unsigned int bytes, struct __sourceloc __whence)
{
void *r=malloc(bytes+SDM_GUARD_AFTER);
DEBUGF("malloc(%d) -> %p", bytes, r);
return r;
}
void *_serval_debug_calloc(unsigned int bytes, unsigned int count, struct __sourceloc __whence)
{
void *r=calloc((bytes*count)+SDM_GUARD_AFTER,1);
DEBUGF("calloc(%d,%d) -> %p", bytes, count, r);
return r;
}
void _serval_debug_free(void *p, struct __sourceloc __whence)
{
free(p);
DEBUGF("free(%p)", p);
}

65
mem.h Normal file
View File

@ -0,0 +1,65 @@
/*
Serval DNA memory management
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVALDNA__MEM_H
#define __SERVALDNA__MEM_H
#include <sys/types.h>
#include "log.h"
// #define MALLOC_PARANOIA
#ifdef MALLOC_PARANOIA
#define malloc(X) _serval_debug_malloc(X,__WHENCE__)
#define calloc(X,Y) _serval_debug_calloc(X,Y,__WHENCE__)
#define free(X) _serval_debug_free(X,__WHENCE__)
void *_serval_debug_malloc(unsigned int bytes, struct __sourceloc whence);
void *_serval_debug_calloc(unsigned int bytes, unsigned int count, struct __sourceloc whence);
void _serval_debug_free(void *p, struct __sourceloc whence);
#endif
/* Equivalent to malloc(3), but logs an error before returning NULL.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void *_emalloc(struct __sourceloc, size_t bytes);
/* Equivalent to malloc(3) followed by memset(3) to zerofill, but logs an error
* before returning NULL.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
void *_emalloc_zero(struct __sourceloc, size_t bytes);
/* Equivalent to strdup(3)/strndup(3), but logs an error before returning NULL.
*
* Why aren't these in str.h? Because str.c must not depend on log.h/log.c! str.c is used in link
* contexts where log.c is not present.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
char *_str_edup(struct __sourceloc, const char *str);
char *_strn_edup(struct __sourceloc, const char *str, size_t len);
#define emalloc(bytes) _emalloc(__HERE__, (bytes))
#define emalloc_zero(bytes) _emalloc_zero(__HERE__, (bytes))
#define str_edup(str) _str_edup(__HERE__, (str))
#define strn_edup(str, len) _strn_edup(__HERE__, (str), (len))
#endif // __SERVALDNA__MEM_H

View File

@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <fcntl.h>
#include "serval.h"
#include "conf.h"
#include "cli.h"
#include "monitor-client.h"
@ -45,7 +46,7 @@ struct monitor_command_handler monitor_handlers[]={
{.command="", .handler=remote_print},
};
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o, void *context)
int app_monitor_cli(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
struct pollfd fds[2];
struct monitor_state *state;

View File

@ -73,14 +73,14 @@ int monitor_socket_name(struct sockaddr_un *name){
hanging around. */
name->sun_path[0] = '\0';
/* XXX: 104 comes from OSX sys/un.h - no #define (note Linux has UNIX_PATH_MAX and it's 108(!)) */
snprintf(&name->sun_path[1],104-2,"%s",
confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME));
snprintf(&name->sun_path[1],104-2,"%s", config.monitor.socket);
/* Doesn't include trailing nul */
len = 1+strlen(&name->sun_path[1]) + sizeof(name->sun_family);
#else
snprintf(name->sun_path,104-1,"%s/%s",
serval_instancepath(),
confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME));
config.monitor.socket
);
/* Includes trailing nul */
len = 1+strlen(name->sun_path) + sizeof(name->sun_family);
#endif

View File

@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <sys/stat.h>
#include "serval.h"
#include "conf.h"
#include "rhizome.h"
#include "cli.h"
#include "str.h"
@ -339,8 +340,7 @@ static void monitor_new_client(int s) {
#endif
if (otheruid != getuid()) {
int allowed_id = confValueGetInt64("monitor.uid",-1);
if (otheruid != allowed_id){
if (otheruid != config.monitor.uid){
WHYF("monitor.socket client has wrong uid (%d versus %d)", otheruid,getuid());
write_str(s, "\nCLOSE:Incorrect UID\n");
goto error;
@ -382,7 +382,7 @@ void monitor_get_all_supported_codecs(unsigned char *codecs){
}
}
static int monitor_set(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_set(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
if (strcase_startswith((char *)argv[1],"vomp",NULL)){
c->flags|=MONITOR_VOMP;
@ -410,7 +410,7 @@ static int monitor_set(int argc, const char *const *argv, struct command_line_op
return 0;
}
static int monitor_clear(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_clear(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
if (strcase_startswith((char *)argv[1],"vomp",NULL))
c->flags&=~MONITOR_VOMP;
@ -430,7 +430,7 @@ static int monitor_clear(int argc, const char *const *argv, struct command_line_
return 0;
}
static int monitor_lookup_match(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_lookup_match(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
const char *sid=argv[2];
const char *ext=argv[4];
@ -453,7 +453,7 @@ static int monitor_lookup_match(int argc, const char *const *argv, struct comman
return 0;
}
static int monitor_call(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
unsigned char sid[SID_SIZE];
if (stowSid(sid, 0, argv[1]) == -1)
@ -466,7 +466,7 @@ static int monitor_call(int argc, const char *const *argv, struct command_line_o
return 0;
}
static int monitor_call_ring(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call_ring(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
if (!call)
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%s\n", argv[1]);
@ -475,7 +475,7 @@ static int monitor_call_ring(int argc, const char *const *argv, struct command_l
return 0;
}
static int monitor_call_pickup(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call_pickup(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
if (!call)
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%s\n", argv[1]);
@ -484,7 +484,7 @@ static int monitor_call_pickup(int argc, const char *const *argv, struct command
return 0;
}
static int monitor_call_audio(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call_audio(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
@ -501,7 +501,7 @@ static int monitor_call_audio(int argc, const char *const *argv, struct command_
return 0;
}
static int monitor_call_hangup(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call_hangup(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
if (!call)
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%s\n", argv[1]);
@ -510,7 +510,7 @@ static int monitor_call_hangup(int argc, const char *const *argv, struct command
return 0;
}
static int monitor_call_dtmf(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int monitor_call_dtmf(int argc, const char *const *argv, const struct command_line_option *o, void *context){
struct monitor_context *c=context;
struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
if (!call)
@ -552,7 +552,8 @@ int monitor_process_command(struct monitor_context *c)
char *argv[16]={NULL,};
int argc = parse_argv(c->line, ' ', argv, 16);
if (cli_execute(NULL, argc, (const char *const*)argv, monitor_options, c))
int res = cli_parse(argc, (const char *const*)argv, monitor_options);
if (res == -1 || cli_invoke(&monitor_options[res], argc, (const char *const*)argv, c))
return monitor_write_error(c, "Invalid command");
return 0;
}

60
net.c
View File

@ -173,63 +173,3 @@ ssize_t recvwithttl(int sock,unsigned char *buffer, size_t bufferlen,int *ttl,
return len;
}
int urandombytes(unsigned char *x, unsigned long long xlen)
{
static int urandomfd = -1;
int tries = 0;
if (urandomfd == -1) {
for (tries = 0; tries < 4; ++tries) {
urandomfd = open("/dev/urandom",O_RDONLY);
if (urandomfd != -1) break;
sleep(1);
}
if (urandomfd == -1) {
WHY_perror("open(/dev/urandom)");
return -1;
}
}
tries = 0;
while (xlen > 0) {
int i = (xlen < 1048576) ? xlen : 1048576;
i = read(urandomfd, x, i);
if (i == -1) {
if (++tries > 4) {
WHY_perror("read(/dev/urandom)");
return -1;
}
sleep(1);
} else {
tries = 0;
x += i;
xlen -= i;
}
}
return 0;
}
time_ms_t gettime_ms()
{
struct timeval nowtv;
// If gettimeofday() fails or returns an invalid value, all else is lost!
if (gettimeofday(&nowtv, NULL) == -1)
FATAL_perror("gettimeofday");
if (nowtv.tv_sec < 0 || nowtv.tv_usec < 0 || nowtv.tv_usec >= 1000000)
FATALF("gettimeofday returned tv_sec=%ld tv_usec=%ld", nowtv.tv_sec, nowtv.tv_usec);
return nowtv.tv_sec * 1000LL + nowtv.tv_usec / 1000;
}
// Returns sleep time remaining.
time_ms_t sleep_ms(time_ms_t milliseconds)
{
if (milliseconds <= 0)
return 0;
struct timespec delay;
struct timespec remain;
delay.tv_sec = milliseconds / 1000;
delay.tv_nsec = (milliseconds % 1000) * 1000000;
if (nanosleep(&delay, &remain) == -1 && errno != EINTR)
FATALF_perror("nanosleep(tv_sec=%ld, tv_nsec=%ld)", delay.tv_sec, delay.tv_nsec);
return remain.tv_sec * 1000 + remain.tv_nsec / 1000000;
}

2
net.h
View File

@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define __SERVALD_NET_H
#include <sys/types.h> // for size_t, ssize_t
#include <sys/socket.h> // for struct sockaddr, socklen_t
#include "log.h" // for __WHENCE__ and struct __sourceloc
#define set_nonblock(fd) (_set_nonblock(fd, __WHENCE__))
@ -39,5 +40,6 @@ ssize_t _write_nonblock(int fd, const void *buf, size_t len, struct __sourceloc
ssize_t _write_all_nonblock(int fd, const void *buf, size_t len, struct __sourceloc __whence);
ssize_t _write_str(int fd, const char *str, struct __sourceloc __whence);
ssize_t _write_str_nonblock(int fd, const char *str, struct __sourceloc __whence);
ssize_t recvwithttl(int sock, unsigned char *buffer, size_t bufferlen, int *ttl, struct sockaddr *recvaddr, socklen_t *recvaddrlen);
#endif // __SERVALD_NET_H

View File

@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <sys/types.h>
#include <alloca.h>
#include <dirent.h>
#include <time.h>
#include "serval.h"
int mkdirs(const char *path, mode_t mode)
@ -62,3 +63,61 @@ int mkdirsn(const char *path, size_t len, mode_t mode)
return WHYF("cannot mkdir %s", pathfrag);
}
int urandombytes(unsigned char *buf, unsigned long long len)
{
static int urandomfd = -1;
int tries = 0;
if (urandomfd == -1) {
for (tries = 0; tries < 4; ++tries) {
urandomfd = open("/dev/urandom",O_RDONLY);
if (urandomfd != -1) break;
sleep(1);
}
if (urandomfd == -1) {
WHY_perror("open(/dev/urandom)");
return -1;
}
}
tries = 0;
while (len > 0) {
int i = (len < 1048576) ? len : 1048576;
i = read(urandomfd, buf, i);
if (i == -1) {
if (++tries > 4) {
WHY_perror("read(/dev/urandom)");
return -1;
}
sleep(1);
} else {
tries = 0;
buf += i;
len -= i;
}
}
return 0;
}
time_ms_t gettime_ms()
{
struct timeval nowtv;
// If gettimeofday() fails or returns an invalid value, all else is lost!
if (gettimeofday(&nowtv, NULL) == -1)
FATAL_perror("gettimeofday");
if (nowtv.tv_sec < 0 || nowtv.tv_usec < 0 || nowtv.tv_usec >= 1000000)
FATALF("gettimeofday returned tv_sec=%ld tv_usec=%ld", nowtv.tv_sec, nowtv.tv_usec);
return nowtv.tv_sec * 1000LL + nowtv.tv_usec / 1000;
}
// Returns sleep time remaining.
time_ms_t sleep_ms(time_ms_t milliseconds)
{
if (milliseconds <= 0)
return 0;
struct timespec delay;
struct timespec remain;
delay.tv_sec = milliseconds / 1000;
delay.tv_nsec = (milliseconds % 1000) * 1000000;
if (nanosleep(&delay, &remain) == -1 && errno != EINTR)
FATALF_perror("nanosleep(tv_sec=%ld, tv_nsec=%ld)", delay.tv_sec, delay.tv_nsec);
return remain.tv_sec * 1000 + remain.tv_nsec / 1000000;
}

64
os.h Normal file
View File

@ -0,0 +1,64 @@
/*
Serval DNA native Operating System interface
Copyright (C) 2012 Serval Project Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#ifndef __SERVALDNA_OS_H
#define __SERVALDNA_OS_H
/* All wall clock times in the Serval daemon are represented in milliseconds
* since the Unix epoch. The gettime_ms() function uses gettimeofday(2) to
* return this value when called. The time_ms_t typedef should be used
* wherever this time value is handled or stored.
*
* This type could perfectly well be unsigned, but is defined as signed to
* avoid the need to cast or define a special signed timedelta_ms_t type at **
* (1):
*
* static time_ms_t then = 0;
* time_ms_t now = gettime_ms();
* time_ms_t ago = now - then; // ** (1)
* if (then && ago < 0) {
* ... time going backwards ...
* } else {
* ... time has advanced ...
* then = now;
* }
*/
typedef long long time_ms_t;
time_ms_t gettime_ms();
time_ms_t sleep_ms(time_ms_t milliseconds);
/* bzero(3) is deprecated in favour of memset(3). */
#define bzero(addr,len) memset((addr), 0, (len))
/* OpenWRT libc doesn't have bcopy, but has memmove */
#define bcopy(A,B,C) memmove(B,A,C)
int mkdirs(const char *path, mode_t mode);
int mkdirsn(const char *path, size_t len, mode_t mode);
void srandomdev();
int urandombytes(unsigned char *buf, unsigned long long len);
#endif //__SERVALDNA_OS_H

View File

@ -26,6 +26,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "conf.h"
#include "str.h"
#include "overlay_address.h"
#include "overlay_buffer.h"
@ -252,40 +253,23 @@ int reachable_unicast(struct subscriber *subscriber, overlay_interface *interfac
return 0;
}
// load a unicast address from configuration
int load_subscriber_address(struct subscriber *subscriber){
// load a unicast address from configuration, replace with database??
int load_subscriber_address(struct subscriber *subscriber)
{
if (subscriber_is_reachable(subscriber)&REACHABLE)
return 0;
char buff[80];
const char *sid_hex = alloca_tohex_sid(subscriber->sid);
overlay_interface *interface=NULL;
snprintf(buff, sizeof(buff), "%s.address", sid_hex);
const char *address = confValueGet(buff, NULL);
// no address configuration? just return.
if (!address)
int i = config_host_list__get(&config.hosts, (const sid_t*)subscriber->sid);
// No unicast configuration? just return.
if (i == -1)
return 1;
snprintf(buff, sizeof(buff), "%s.interface", sid_hex);
const char *interface_name = confValueGet(buff, NULL);
if (interface_name){
interface = overlay_interface_find_name(interface_name);
// explicity defined interface isn't up? just return.
if (!interface)
return 1;
}
const struct config_host *hostc = &config.hosts.av[i].value;
overlay_interface *interface = overlay_interface_find_name(hostc->interface);
if (!interface)
return -1;
struct sockaddr_in addr;
addr.sin_family=AF_INET;
if (!inet_aton(address, &addr.sin_addr)){
return WHYF("%s doesn't look like an IP address", address);
}
snprintf(buff, sizeof(buff), "%s.port", sid_hex);
addr.sin_port = htons(confValueGetInt64Range(buff, PORT_DNA, 1, 65535));
addr.sin_family = AF_INET;
addr.sin_addr = hostc->address;
addr.sin_port = hostc->port;
return overlay_send_probe(subscriber, addr, interface);
}

View File

@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "mem.h"
#include "serval.h"
#include "overlay_buffer.h"
@ -404,29 +405,3 @@ int ob_dump(struct overlay_buffer *b,char *desc)
dump(NULL, b->bytes, b->sizeLimit>b->position?b->sizeLimit:b->position);
return 0;
}
#undef malloc
#undef calloc
#undef free
#undef realloc
#define SDM_GUARD_AFTER 16384
void *_serval_debug_malloc(unsigned int bytes, struct __sourceloc __whence)
{
void *r=malloc(bytes+SDM_GUARD_AFTER);
DEBUGF("malloc(%d) -> %p", bytes, r);
return r;
}
void *_serval_debug_calloc(unsigned int bytes, unsigned int count, struct __sourceloc __whence)
{
void *r=calloc((bytes*count)+SDM_GUARD_AFTER,1);
DEBUGF("calloc(%d,%d) -> %p", bytes, count, r);
return r;
}
void _serval_debug_free(void *p, struct __sourceloc __whence)
{
free(p);
DEBUGF("free(%p)", p);
}

View File

@ -23,8 +23,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <arpa/inet.h>
#include <assert.h>
#include <time.h>
#include <fnmatch.h>
#include "serval.h"
#include "conf.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
#include "overlay_buffer.h"
#include "overlay_packet.h"
#include "str.h"
@ -38,17 +41,6 @@ int overlay_interface_count=0;
overlay_interface overlay_interfaces[OVERLAY_MAX_INTERFACES];
int overlay_last_interface_number=-1;
struct interface_rules {
char *namespec;
unsigned long long speed_in_bits;
int port;
char type;
char excludeP;
struct interface_rules *next;
};
struct interface_rules *interface_filter=NULL;
struct profile_total interface_poll_stats;
struct profile_total dummy_poll_stats;
@ -58,133 +50,9 @@ struct profile_total sock_any_stats;
static void overlay_interface_poll(struct sched_ent *alarm);
static void logServalPacket(int level, struct __sourceloc __whence, const char *message, const unsigned char *packet, size_t len);
static long long parse_quantity(char *q);
#define DEBUG_packet_visualise(M,P,N) logServalPacket(LOG_LEVEL_DEBUG, __WHENCE__, (M), (P), (N))
static int overlay_interface_type(char *s)
{
if (!strcasecmp(s,"ethernet")) return OVERLAY_INTERFACE_ETHERNET;
if (!strcasecmp(s,"wifi")) return OVERLAY_INTERFACE_WIFI;
if (!strcasecmp(s,"other")) return OVERLAY_INTERFACE_UNKNOWN;
if (!strcasecmp(s,"catear")) return OVERLAY_INTERFACE_PACKETRADIO;
return WHY("Invalid interface type -- consider using 'wifi','ethernet' or 'other'");
}
static long long
parse_quantity(char *q)
{
if (strlen(q) >= 80)
return WHY("quantity string >=80 characters");
long long result;
if (str_to_ll_scaled(q, 10, &result, NULL))
return result;
return WHYF("Illegal quantity: %s", alloca_str_toprint(q));
}
int overlay_interface_arg(char *arg)
{
/* Parse an interface argument, of the form:
<+|->[interfacename][=type]
+interface tells DNA to sit on that interface
-interface tells DNA to not sit on that interface
+/- without an interface tells DNA to sit on all interfaces.
The first match rules, so -en0+ tells DNA to use all interfaces, excepting en0
The optional =type specifier tells DNA how to handle the interface in terms of
bandwidth:distance relationship for calculating tick times etc.
The special type =custom allows full specification:
XXX - Settle the custom specification now that we have changed the interface
management.
*/
char sign[80]="+";
char interface_name[80]="";
char speed[80]="1m";
char typestring[80]="wifi";
int port=PORT_DNA;
int type=OVERLAY_INTERFACE_UNKNOWN;
int n=0;
/* Too long */
if (strlen(arg)>79) return WHY("interface specification was >79 characters");
struct interface_rules *r=calloc(sizeof(struct interface_rules),1);
if (!r) return WHY("calloc(struct interface rules),1) failed");
if (sscanf(arg,"%[+-]%n%[^=:,]%n=%[^:]%n:%d%n:%[^:]%n",
sign,&n,interface_name,&n,typestring,&n,&port,&n,speed,&n)>=1)
{
if (n<strlen(arg)) { free(r); return WHY("Extra junk at end of interface specification"); }
if (strlen(sign)>1) { free(r); return WHY("Sign must be + or -"); }
switch(sign[0])
{
case '+': break;
case '-': r->excludeP=1; break;
default:
free(r);
return WHY("Invalid interface list item: Must begin with + or -");
}
long long speed_in_bits=parse_quantity(speed);
if (speed_in_bits<=1) {
free(r);
return WHY("Interfaces must be capable of at least 1 bit per second");
}
if (n<strlen(arg)) return WHY("Extra stuff at end of interface specification");
type=overlay_interface_type(typestring);
if (type<0) { free(r); return WHY("Invalid interface type in specification"); }
/* Okay, register the interface preference */
r->namespec=strdup(interface_name);
r->speed_in_bits=speed_in_bits;
r->port=port;
r->type=type;
r->next=interface_filter;
interface_filter=r;
return 0;
}
else { free(r); return WHY("Bad interface specification"); }
}
int overlay_interface_args(const char *arg)
{
/* Parse series of comma-separated interface definitions from a single argument
*/
int i=0;
char interface[80];
int len=0;
for(i=0;arg[i];i++)
{
if (arg[i]==','||arg[i]=='\n') {
interface[len]=0;
if (overlay_interface_arg(interface)) return WHY("Could not add interface");
len=0;
} else {
if (len<79) {
interface[len++]=arg[i];
interface[len]=0;
} else
return WHY("Interface definition is too long (each must be <80 characters)");
}
}
if (len) if (overlay_interface_arg(interface)) return WHY("Could not add final interface");
return 0;
}
static void
overlay_interface_close(overlay_interface *interface){
if (interface->fileP){
@ -287,11 +155,8 @@ overlay_interface * overlay_interface_find_name(const char *name){
for (i=0;i<OVERLAY_MAX_INTERFACES;i++){
if (overlay_interfaces[i].state!=INTERFACE_STATE_UP)
continue;
if (strcasecmp((*name=='>'?name+1:name),
(*overlay_interfaces[i].name=='>'?overlay_interfaces[i].name+1:overlay_interfaces[i].name)
)==0){
if (strcasecmp(name, overlay_interfaces[i].name) == 0)
return &overlay_interfaces[i];
}
}
return NULL;
}
@ -352,7 +217,8 @@ overlay_interface_read_any(struct sched_ent *alarm){
// bind a socket to INADDR_ANY:port
// for now, we don't have a graceful close for this interface but it should go away when the process dies
static int overlay_interface_init_any(int port){
static int overlay_interface_init_any(int port)
{
struct sockaddr_in addr;
if (sock_any.poll.fd>0){
@ -440,9 +306,8 @@ overlay_interface_init_socket(int interface_index)
}
static int
overlay_interface_init(char *name, struct in_addr src_addr, struct in_addr netmask,
struct in_addr broadcast,
int speed_in_bits, int port, int type)
overlay_interface_init(const char *name, struct in_addr src_addr, struct in_addr netmask, struct in_addr broadcast,
const struct config_network_interface *ifconfig)
{
/* Too many interfaces */
if (overlay_interface_count>=OVERLAY_MAX_INTERFACES) return WHY("Too many interfaces -- Increase OVERLAY_MAX_INTERFACES");
@ -455,59 +320,56 @@ overlay_interface_init(char *name, struct in_addr src_addr, struct in_addr netma
This will ultimately get tuned by the bandwidth and other properties of the interface */
interface->mtu=1200;
interface->state=INTERFACE_STATE_DOWN;
interface->bits_per_second=speed_in_bits;
interface->port=port;
interface->type=type;
interface->bits_per_second = ifconfig->speed;
interface->port= ifconfig->port;
interface->type= ifconfig->type;
interface->default_route = ifconfig->default_route;
interface->last_tick_ms= -1; // not ticked yet
interface->alarm.poll.fd=0;
// how often do we announce ourselves on this interface?
switch (type) {
case OVERLAY_INTERFACE_PACKETRADIO:
interface->tick_ms = confValueGetInt64Range("mdp.packetradio.tick_ms", 15000LL, 1LL, 3600000LL);
break;
case OVERLAY_INTERFACE_ETHERNET:
interface->tick_ms = confValueGetInt64Range("mdp.ethernet.tick_ms", 500LL, 1LL, 3600000LL);
break;
case OVERLAY_INTERFACE_WIFI:
interface->tick_ms = confValueGetInt64Range("mdp.wifi.tick_ms", 500LL, 1LL, 3600000LL);
break;
case OVERLAY_INTERFACE_UNKNOWN:
interface->tick_ms = confValueGetInt64Range("mdp.unknown.tick_ms", 500LL, 1LL, 3600000LL);
break;
default:
return WHYF("Unsupported interface type %d", type);
}
// allow for a per interface override of tick interval
{
char option_name[64];
snprintf(option_name, sizeof(option_name), "mdp.%s.tick_ms", (*name=='>'?name+1:name));
interface->tick_ms = confValueGetInt64Range(option_name, interface->tick_ms, 1LL, 3600000LL);
snprintf(option_name, sizeof(option_name), "interface.%s.default_route", (*name=='>'?name+1:name));
interface->default_route=confValueGetBoolean(option_name,0);
// How often do we announce ourselves on this interface?
int32_t tick_ms = ifconfig->mdp_tick_ms;
if (tick_ms < 0) {
int i = config_mdp_iftypelist__get(&config.mdp.iftype, &ifconfig->type);
if (i != -1)
tick_ms = config.mdp.iftype.av[i].value.tick_ms;
}
// disable announcements and other broadcasts if tick_ms=0.
if (interface->tick_ms>0)
if (tick_ms < 0) {
switch (ifconfig->type) {
case OVERLAY_INTERFACE_PACKETRADIO:
tick_ms = 15000;
break;
case OVERLAY_INTERFACE_ETHERNET:
tick_ms = 500;
break;
case OVERLAY_INTERFACE_WIFI:
tick_ms = 500;
break;
case OVERLAY_INTERFACE_UNKNOWN:
tick_ms = 500;
break;
default:
return WHYF("Unsupported interface type %d", ifconfig->type);
}
}
assert(tick_ms >= 0);
interface->tick_ms = tick_ms;
// disable announcements and other broadcasts if tick_ms=0.
if (interface->tick_ms > 0)
interface->send_broadcasts=1;
else{
interface->send_broadcasts=0;
INFOF("Interface %s is running tickless", name);
}
if (name[0]=='>') {
interface->fileP=1;
if (ifconfig->dummy[0]) {
interface->fileP = 1;
char dummyfile[1024];
if (name[1]=='/') {
/* Absolute path */
snprintf(dummyfile, sizeof(dummyfile), "%s", &name[1]);
} else {
const char *interface_folder = confValueGet("interface.folder", serval_instancepath());
snprintf(dummyfile, sizeof(dummyfile), "%s/%s", interface_folder, &name[1]);
}
strbuf d = strbuf_local(dummyfile, sizeof dummyfile);
strbuf_path_join(d, serval_instancepath(), config.server.dummy_interface_dir, ifconfig->dummy, NULL);
if (strbuf_overrun(d))
return WHYF("dummy interface file name overrun: %s", alloca_str_toprint(strbuf_str(d)));
if ((interface->alarm.poll.fd = open(dummyfile,O_APPEND|O_RDWR)) < 1) {
return WHYF("could not open dummy interface file %s for append", dummyfile);
}
@ -786,41 +648,50 @@ overlay_broadcast_ensemble(int interface_number,
}
}
/* Register the interface, or update the existing interface registration */
/* Register the real interface, or update the existing interface registration. */
int
overlay_interface_register(char *name,
struct in_addr addr,
struct in_addr mask) {
struct interface_rules *r, *me;
int i;
struct in_addr mask)
{
struct in_addr broadcast = {.s_addr = addr.s_addr | ~mask.s_addr};
if (debug & DEBUG_OVERLAYINTERFACES) {
// note, inet_ntop doesn't seem to behave on android
DEBUGF("%s address: %s", name, inet_ntoa(addr));
DEBUGF("%s broadcast address: %s", name, inet_ntoa(broadcast));
}
/* See if the interface is listed in the filter */
me = NULL;
for (r = interface_filter; r && !me; r = r->next)
if (r->namespec[0] == '\0' || strcasecmp(name, r->namespec) == 0)
me = r;
if (me == NULL || me->excludeP) {
// Find the matching non-dummy interface rule.
const struct config_network_interface *ifconfig = NULL;
int i;
for (i = 0; i < config.interfaces.ac; ++i, ifconfig = NULL) {
ifconfig = &config.interfaces.av[i].value;
if (!ifconfig->dummy[0]) {
int j;
for (j = 0; j < ifconfig->match.patc; ++j)
if (fnmatch(ifconfig->match.patv[j], name, 0) == 0)
break;
}
}
if (ifconfig == NULL) {
if (debug & DEBUG_OVERLAYINTERFACES)
DEBUGF("Interface %s is not interesting.",name);
DEBUGF("Interface %s does not match any rule", name);
return 0;
}
if (ifconfig->exclude) {
if (debug & DEBUG_OVERLAYINTERFACES)
DEBUGF("Interface %s is explicitly excluded", name);
return 0;
}
int found_interface= -1;
/* Search in the exist list of interfaces */
int found_interface= -1;
for(i = 0; i < overlay_interface_count; i++){
int broadcast_match = 0;
int name_match =0;
if (overlay_interfaces[i].broadcast_address.sin_addr.s_addr
== broadcast.s_addr)
if (overlay_interfaces[i].broadcast_address.sin_addr.s_addr == broadcast.s_addr)
broadcast_match = 1;
name_match = !strcasecmp(overlay_interfaces[i].name, name);
@ -855,7 +726,7 @@ overlay_interface_register(char *name,
}
/* New interface, so register it */
if (overlay_interface_init(name, addr, mask, broadcast, me->speed_in_bits, me->port, me->type))
if (overlay_interface_init(name, addr, mask, broadcast, ifconfig))
return WHYF("Could not initialise newly seen interface %s", name);
else
if (debug & DEBUG_OVERLAYINTERFACES) DEBUGF("Registered interface %s", name);
@ -863,66 +734,61 @@ overlay_interface_register(char *name,
return 0;
}
void overlay_interface_discover(struct sched_ent *alarm){
int i;
struct interface_rules *r;
struct in_addr dummyaddr;
int detect_real_interfaces = 0;
void overlay_interface_discover(struct sched_ent *alarm)
{
/* Mark all UP interfaces as DETECTING, so we can tell which interfaces are new, and which are dead */
int i;
for (i = 0; i < overlay_interface_count; i++)
if (overlay_interfaces[i].state==INTERFACE_STATE_UP)
overlay_interfaces[i].state=INTERFACE_STATE_DETECTING;
/* Check through for any virtual dummy interfaces */
for (r = interface_filter; r != NULL; r = r->next) {
if (r->namespec[0] != '>'){
/* Register new dummy interfaces */
int detect_real_interfaces = 0;
const struct config_network_interface *ifconfig = NULL;
for (i = 0; i < config.interfaces.ac; ++i, ifconfig = NULL) {
ifconfig = &config.interfaces.av[i].value;
if (!ifconfig->dummy[0]) {
detect_real_interfaces = 1;
continue;
}
for (i = 0; i < overlay_interface_count; i++)
if (!strcasecmp(overlay_interfaces[i].name,r->namespec)){
if (strcasecmp(overlay_interfaces[i].name, ifconfig->dummy) == 0) {
if (overlay_interfaces[i].state==INTERFACE_STATE_DETECTING)
overlay_interfaces[i].state=INTERFACE_STATE_UP;
break;
}
if (i >= overlay_interface_count){
/* New interface, so register it */
overlay_interface_init(r->namespec,dummyaddr,dummyaddr,dummyaddr,1000000,PORT_DNA,OVERLAY_INTERFACE_WIFI);
if (i >= overlay_interface_count) {
// New dummy interface, so register it.
struct in_addr dummyaddr = (struct in_addr){htonl(INADDR_NONE)};
overlay_interface_init(ifconfig->dummy, dummyaddr, dummyaddr, dummyaddr, ifconfig);
}
}
/* Look for real interfaces */
if (detect_real_interfaces){
// Register new real interfaces
if (detect_real_interfaces) {
int no_route = 1;
#ifdef HAVE_IFADDRS_H
if (no_route != 0)
no_route = doifaddrs();
#endif
#ifdef SIOCGIFCONF
if (no_route != 0)
no_route = lsif();
#endif
#ifdef linux
if (no_route != 0)
no_route = scrapeProcNetRoute();
#endif
if (no_route != 0) {
FATAL("Unable to get any interface information");
}
}
// detect if any interfaces have gone away and need to be closed
// Close any interfaces that have gone away.
for(i = 0; i < overlay_interface_count; i++)
if (overlay_interfaces[i].state==INTERFACE_STATE_DETECTING)
overlay_interface_close(&overlay_interfaces[i]);
alarm->alarm = gettime_ms()+5000;
alarm->deadline = alarm->alarm + 10000;
schedule(alarm);

View File

@ -32,13 +32,12 @@
*/
#include "serval.h"
#include "conf.h"
#include "overlay_packet.h"
#include "overlay_buffer.h"
#include "overlay_address.h"
#define PACKET_FORMAT_NUMBER 123
static int local_port =4131;
static int remote_port =4130;
static void olsr_read(struct sched_ent *alarm);
@ -60,17 +59,14 @@ int olsr_init_socket(void){
if (read_watch.poll.fd>=0)
return 0;
if (!confValueGetBoolean("olsr.enabled",0))
if (!config.olsr.enable)
return 0;
local_port = confValueGetInt64Range("olsr.local.port", local_port, 1LL, 0xFFFFLL);
remote_port = confValueGetInt64Range("olsr.remote.port", remote_port, 1LL, 0xFFFFLL);
INFOF("Initialising olsr broadcast forwarding via ports %d-%d", local_port, remote_port);
INFOF("Initialising olsr broadcast forwarding via ports %d-%d", config.olsr.local_port, config.olsr.remote_port);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = htonl(INADDR_LOOPBACK),
.sin_port = htons(local_port),
.sin_port = htons(config.olsr.local_port),
};
fd = socket(AF_INET,SOCK_DGRAM,0);
@ -204,7 +200,7 @@ static void olsr_read(struct sched_ent *alarm){
return;
// drop packets from other port numbers
if (ntohs(addr.sin_port)!=remote_port){
if (ntohs(addr.sin_port)!= config.olsr.remote_port){
WHYF("Dropping unexpected packet from port %d", ntohs(addr.sin_port));
return;
}
@ -228,7 +224,7 @@ static int send_packet(unsigned char *header, int header_len, unsigned char *pay
struct sockaddr_in addr={
.sin_family=AF_INET,
.sin_addr.s_addr=htonl(INADDR_LOOPBACK),
.sin_port=htons(remote_port),
.sin_port=htons(config.olsr.remote_port),
};
struct iovec iov[]={

View File

@ -116,6 +116,7 @@ overlay_queue_remove(overlay_txqueue *queue, struct overlay_frame *frame){
return next;
}
#if 0 // unused
static int
overlay_queue_dump(overlay_txqueue *q)
{
@ -148,6 +149,7 @@ overlay_queue_dump(overlay_txqueue *q)
DEBUG(strbuf_str(b));
return 0;
}
#endif
int overlay_payload_enqueue(struct overlay_frame *p)
{
@ -545,4 +547,4 @@ overlay_tick_interface(int i, time_ms_t now) {
/* Stuff more payloads from queues and send it */
overlay_fill_send_packet(&packet, now);
RETURN(0);
}
}

View File

@ -58,7 +58,8 @@ static PaCtx *pa_phone_setup(void);
/* Declarations */
int
app_pa_phone(int argc, const char *const *argv, struct command_line_option *o) {
app_pa_phone(int argc, const char *const *argv, const struct command_line_option *o)
{
PaCtx *ctx;
if ((ctx = pa_phone_setup()) == NULL)

32
packetformats.c Normal file
View File

@ -0,0 +1,32 @@
/*
Serval Distributed Numbering Architecture (DNA)
Copyright (C) 2010 Paul Gardner-Stephen
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "str.h"
int stowSid(unsigned char *packet, int ofs, const char *sid)
{
if (debug & DEBUG_PACKETFORMATS)
printf("stowing SID \"%s\"\n", sid);
if (strcasecmp(sid,"broadcast") == 0)
memset(packet + ofs, 0xff, SID_SIZE);
else if (fromhex(packet + ofs, sid, SID_SIZE) != SID_SIZE || sid[SID_STRLEN] != '\0')
return WHY("invalid SID");
return 0;
}

View File

@ -18,26 +18,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "conf.h"
#include "str.h"
#include "rhizome.h"
#include <stdlib.h>
int is_rhizome_enabled()
{
return confValueGetBoolean("rhizome.enable", 1);
return config.rhizome.enable;
}
int is_rhizome_http_enabled()
{
return confValueGetBoolean("rhizome.enable", 1)
&& confValueGetBoolean("rhizome.http.enable", 1)
return config.rhizome.enable
&& config.rhizome.http.enable
&& rhizome_db;
}
int is_rhizome_mdp_enabled()
{
return confValueGetBoolean("rhizome.enable", 1)
&& confValueGetBoolean("rhizome.mdp.enable", 1)
return config.rhizome.enable
&& config.rhizome.mdp.enable
&& rhizome_db;
}
@ -48,16 +49,15 @@ int is_rhizome_mdp_server_running()
int is_rhizome_advertise_enabled()
{
return confValueGetBoolean("rhizome.enable", 1)
&& confValueGetBoolean("rhizome.advertise.enable", 1)
return config.rhizome.enable
&& config.rhizome.advertise.enable
&& rhizome_db
&& ( is_rhizome_http_server_running()
||is_rhizome_mdp_server_running());
&& (is_rhizome_http_server_running() || is_rhizome_mdp_server_running());
}
int rhizome_fetch_delay_ms()
{
return confValueGetInt64Range("rhizome.fetch_delay_ms", 50, 1, 3600000);
return config.rhizome.fetch_delay_ms;
}
/* Import a bundle from a pair of files, one containing the manifest and the optional other

View File

@ -17,8 +17,12 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __SERVALDNA__RHIZOME_H
#define __SERVALDNA__RHIZOME_H
#include <sqlite3.h>
#include "sha2.h"
#include "str.h"
#include "strbuf.h"
#include "nacl.h"
#include <sys/stat.h>
@ -45,6 +49,16 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define RHIZOME_HTTP_PORT 4110
#define RHIZOME_HTTP_PORT_MAX 4150
typedef struct rhizome_bk_binary {
unsigned char binary[RHIZOME_BUNDLE_KEY_BYTES];
} rhizome_bk_t;
#define RHIZOME_BK_NONE ((rhizome_bk_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}})
__RHIZOME_INLINE int rhizome_is_bk_none(const rhizome_bk_t *bk) {
return is_all_matching(bk->binary, sizeof bk->binary, 0);
}
extern time_ms_t rhizome_voice_timeout;
#define RHIZOME_PRIORITY_HIGHEST RHIZOME_PRIORITY_SERVAL_CORE
@ -577,3 +591,5 @@ struct http_response_parts {
};
int unpack_http_response(char *response, struct http_response_parts *parts);
#endif //__SERVALDNA__RHIZOME_H

View File

@ -21,32 +21,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <stdlib.h>
#include <time.h>
#include "serval.h"
#include "conf.h"
#include "rhizome.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
#include "str.h"
long long rhizome_space=0;
static const char *rhizome_thisdatastore_path = NULL;
static char rhizome_thisdatastore_path[256];
const char *rhizome_datastore_path()
{
if (!rhizome_thisdatastore_path)
if (!rhizome_thisdatastore_path[0])
rhizome_set_datastore_path(NULL);
return rhizome_thisdatastore_path;
}
int rhizome_set_datastore_path(const char *path)
{
if (!path)
path = confValueGet("rhizome.datastore_path", NULL);
if (path) {
rhizome_thisdatastore_path = strdup(path);
if (path[0] != '/')
WARNF("Dangerous rhizome.datastore_path setting: '%s' -- should be absolute", rhizome_thisdatastore_path);
} else {
rhizome_thisdatastore_path = serval_instancepath();
WARNF("Rhizome datastore path not configured -- using instance path '%s'", rhizome_thisdatastore_path);
}
strbuf b = strbuf_local(rhizome_thisdatastore_path, sizeof rhizome_thisdatastore_path);
strbuf_path_join(b, serval_instancepath(), config.rhizome.datastore_path, path, NULL);
INFOF("Rhizome datastore path = %s", alloca_str_toprint(rhizome_thisdatastore_path));
return 0;
}
@ -197,11 +191,8 @@ int rhizome_opendb()
int loglevel = (debug & DEBUG_RHIZOME) ? LOG_LEVEL_DEBUG : LOG_LEVEL_SILENT;
/* Read Rhizome configuration */
double rhizome_kb = atof(confValueGet("rhizome_kb", "1024"));
rhizome_space = 1024LL * rhizome_kb;
if (debug&DEBUG_RHIZOME) {
DEBUGF("serval.conf:rhizome_kb=%.f", rhizome_kb);
DEBUGF("Rhizome will use %lldB of storage for its database.", rhizome_space);
DEBUGF("Rhizome will use %lluB of storage for its database.", (unsigned long long) config.rhizome.database_size);
}
/* Create tables as required */
sqlite_exec_void_loglevel(loglevel, "PRAGMA auto_vacuum=2;");
@ -586,7 +577,7 @@ long long rhizome_database_used_bytes()
int rhizome_make_space(int group_priority, long long bytes)
{
/* Asked for impossibly large amount */
if (bytes>=(rhizome_space-65536))
if (bytes>=(config.rhizome.database_size-65536))
return WHYF("bytes=%lld is too large", bytes);
long long db_used = rhizome_database_used_bytes();
@ -594,7 +585,7 @@ int rhizome_make_space(int group_priority, long long bytes)
return -1;
/* If there is already enough space now, then do nothing more */
if (db_used<=(rhizome_space-bytes-65536))
if (db_used<=(config.rhizome.database_size-bytes-65536))
return 0;
/* Okay, not enough space, so free up some. */
@ -602,7 +593,7 @@ int rhizome_make_space(int group_priority, long long bytes)
sqlite3_stmt *statement = sqlite_prepare(&retry, "select id,length from files where highestpriority < %d order by descending length", group_priority);
if (!statement)
return -1;
while (bytes > (rhizome_space - 65536 - rhizome_database_used_bytes())
while (bytes > (config.rhizome.database_size - 65536 - rhizome_database_used_bytes())
&& sqlite_step_retry(&retry, statement) == SQLITE_ROW
) {
/* Make sure we can drop this blob, and if so drop it, and recalculate number of bytes required */

View File

@ -108,6 +108,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "serval.h"
#include "conf.h"
#include "rhizome.h"
#include "str.h"
#include <assert.h>
@ -480,7 +481,39 @@ rhizome_manifest *rhizome_direct_get_manifest(unsigned char *bid_prefix,int pref
}
int app_rhizome_direct_sync(int argc, const char *const *argv, struct command_line_option *o, void *context)
static int rhizome_sync_with_peers(int mode, int peer_count, const struct config_rhizome_peer *const *peers)
{
/* Get iterator capable of 64KB buffering.
In future we should parse the sync URL and base the buffer size on the
transport and allowable traffic volumes. */
rhizome_direct_transport_state_http *state = emalloc_zero(sizeof(rhizome_direct_transport_state_http));
/* XXX This code runs each sync in series, when we can probably do them in
parallel. But we can't really do them in parallel until we make the
synchronisation process fully asynchronous, which probably won't happen
for a while yet.
Also, we don't currently parse the URI protocol field fully. */
int peer_number;
for (peer_number = 0; peer_number < peer_count; ++peer_number) {
const struct config_rhizome_peer *peer = peers[peer_number];
if (strcasecmp(peer->protocol, "http") != 0)
return WHYF("Unsupported Rhizome Direct protocol %s", alloca_str_toprint(peer->protocol));
strbuf h = strbuf_local(state->host, sizeof state->host);
strbuf_puts(h, peer->host);
if (strbuf_overrun(h))
return WHYF("Rhizome Direct host name too long: %s", alloca_str_toprint(peer->host));
state->port = peer->port;
DEBUGF("Rhizome direct peer is %s://%s:%d", peer->protocol, state->host, state->port);
rhizome_direct_sync_request *s = rhizome_direct_new_sync_request(rhizome_direct_http_dispatch, 65536, 0, mode, state);
rhizome_direct_start_sync_request(s);
if (rd_sync_handle_count > 0)
while (fd_poll() && rd_sync_handle_count > 0)
;
}
return 0;
}
int app_rhizome_direct_sync(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
/* Attempt to connect with a remote Rhizome Direct instance,
and negotiate which BARs to synchronise. */
@ -488,83 +521,30 @@ int app_rhizome_direct_sync(int argc, const char *const *argv, struct command_li
int mode=3; /* two-way sync */
if (!strcasecmp(modeName,"push")) mode=1; /* push only */
if (!strcasecmp(modeName,"pull")) mode=2; /* pull only */
DEBUGF("sync direction = %d",mode);
/* Get iterator capable of 64KB buffering.
In future we should parse the sync URL and base the buffer size on the
transport and allowable traffic volumes. */
rhizome_direct_transport_state_http
*state=calloc(sizeof(rhizome_direct_transport_state_http),1);
const char *sync_url=NULL;
int peer_count=confValueGetInt64("rhizome.direct.peer.count",0);
int peer_number=0;
char peer_var[128];
if (argv[3]) {
peer_count=1;
sync_url=argv[3];
}
if (peer_count<1) {
DEBUG("No rhizome direct peers were configured or supplied.");
}
/* XXX This code runs each sync in series, when we can probably do them in
parallel. But we can't really do them in parallel until we make the
synchronisation process fully asynchronous, which probably won't happen
for a while yet.
Also, we don't currently parse the URI protocol field fully. */
next_peer:
if (!sync_url) {
snprintf(peer_var,128,"rhizome.direct.peer.%d",peer_number);
sync_url=confValueGet(peer_var, NULL);
}
if (strlen(sync_url)>1020)
{
DEBUG("rhizome direct URI too long");
return -1;
struct config_rhizome_peer peer;
const struct config_rhizome_peer *peers[1] = { &peer };
int result = cf_opt_rhizome_peer_from_uri(&peer, argv[3]);
if (result == CFOK)
return rhizome_sync_with_peers(mode, 1, peers);
else {
strbuf b = strbuf_alloca(128);
strbuf_cf_flag_reason(b, result);
return WHYF("Invalid peer URI %s -- %s", alloca_str_toprint(argv[3]), strbuf_str(b));
}
char protocol[1024];
state->port=RHIZOME_HTTP_PORT;
int ok=0;
if (sscanf(sync_url,"%[^:]://%[^:]:%d",protocol,state->host,&state->port)>=2)
ok=1;
if (!ok) sprintf(protocol,"http");
if ((!ok)&&(sscanf(sync_url,"%[^:]:%d",state->host,&state->port)>=1))
ok=2;
if (!ok)
{
DEBUG("could not parse rhizome direct URI");
return -1;
}
DEBUGF("Rhizome direct peer is %s://%s:%d (parse route %d)",
protocol,state->host,state->port,ok);
if (strcasecmp(protocol,"http")) {
DEBUG("Unsupport Rhizome Direct synchronisation protocol."
" Only HTTP is supported at present.");
} else if (config.rhizome.direct.peer.ac == 0) {
DEBUG("No rhizome direct peers were configured or supplied");
return -1;
} else {
const struct config_rhizome_peer *peers[config.rhizome.direct.peer.ac];
int i;
for (i = 0; i < config.rhizome.direct.peer.ac; ++i)
peers[i] = &config.rhizome.direct.peer.av[i].value;
return rhizome_sync_with_peers(mode, config.rhizome.direct.peer.ac, peers);
}
rhizome_direct_sync_request
*s = rhizome_direct_new_sync_request(rhizome_direct_http_dispatch,
65536,0,mode,state);
rhizome_direct_start_sync_request(s);
if (rd_sync_handle_count>0)
while(fd_poll()&&(rd_sync_handle_count>0)) continue;
sync_url=NULL;
peer_number++;
if (peer_number<peer_count) goto next_peer;
return 0;
}
rhizome_direct_bundle_cursor *rhizome_direct_bundle_iterator(int buffer_size)
{
rhizome_direct_bundle_cursor *r=calloc(sizeof(rhizome_direct_bundle_cursor),1);

View File

@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "serval.h"
#include "rhizome.h"
#include "conf.h"
#include "str.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
@ -43,8 +44,6 @@ int rhizome_direct_clear_temporary_files(rhizome_http_request *r)
int rhizome_direct_form_received(rhizome_http_request *r)
{
const char *submitBareFileURI=confValueGet("rhizome.api.addfile.uri", NULL);
/* Process completed form based on the set of fields seen */
if (!strcmp(r->path,"/rhizome/import")) {
switch(r->fields_seen) {
@ -175,17 +174,16 @@ int rhizome_direct_form_received(rhizome_http_request *r)
why we leave it disabled by default, but it will be sufficient for testing
possible uses, including integration with OpenDataKit.
*/
else if (submitBareFileURI&&(!strcmp(r->path,submitBareFileURI))) {
if (strcmp(inet_ntoa(r->requestor.sin_addr),
confValueGet("rhizome.api.addfile.allowedaddress","127.0.0.1")))
{
DEBUGF("rhizome.api.addfile request received from %s, but is only allowed from %s",
inet_ntoa(r->requestor.sin_addr),
confValueGet("rhizome.api.addfile.allowedaddress", "127.0.0.1"));
else if (config.rhizome.api.addfile.uri_path[0] && strcmp(r->path, config.rhizome.api.addfile.uri_path) == 0) {
if (r->requestor.sin_addr.s_addr != config.rhizome.api.addfile.allow_host.s_addr) {
DEBUGF("rhizome.api.addfile request received from %s, but is only allowed from %s",
inet_ntoa(r->requestor.sin_addr),
inet_ntoa(config.rhizome.api.addfile.allow_host)
);
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,404,"Not available from here.");
}
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,404,"Not available from here.");
}
switch(r->fields_seen) {
case RD_MIME_STATE_DATAHEADERS:
@ -201,14 +199,13 @@ int rhizome_direct_form_received(rhizome_http_request *r)
char filepath[1024];
snprintf(filepath,1024,"rhizomedirect.%d.data",r->alarm.poll.fd);
const char *manifestTemplate
=confValueGet("rhizome.api.addfile.manifesttemplate", NULL);
if (manifestTemplate&&access(manifestTemplate, R_OK) != 0)
{
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.manifesttemplate points to a file I could not read.");
}
char manifestTemplate[1024];
strbuf b = strbuf_local(manifestTemplate, sizeof manifestTemplate);
strbuf_path_join(b, serval_instancepath(), config.rhizome.api.addfile.manifest_template_file, NULL);
if (manifestTemplate[0] && access(manifestTemplate, R_OK) != 0) {
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,500,"rhizome.api.addfile.manifesttemplate points to a file I could not read.");
}
rhizome_manifest *m = rhizome_new_manifest();
if (!m)
@ -218,7 +215,7 @@ int rhizome_direct_form_received(rhizome_http_request *r)
return WHY("Manifest struct could not be allocated -- not added to rhizome");
}
if (manifestTemplate)
if (manifestTemplate[0])
if (rhizome_read_manifest_file(m, manifestTemplate, 0) == -1) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
@ -252,11 +249,10 @@ int rhizome_direct_form_received(rhizome_http_request *r)
}
const char *senderhex = rhizome_manifest_get(m, "sender", NULL, 0);
if (!senderhex)
senderhex = confValueGet("rhizome.api.addfile.author", NULL);
if (senderhex)
fromhexstr(m->author, senderhex, SID_SIZE);
const char *bskhex = confValueGet("rhizome.api.addfile.bundlesecretkey", NULL);
else if (!is_sid_any(config.rhizome.api.addfile.default_author.binary))
memcpy(m->author, config.rhizome.api.addfile.default_author.binary, sizeof m->author); // TODO replace with sid_t struct assignment
/* Bind an ID to the manifest, and also bind the file. Then finalise the
manifest. But if the manifest already contains an ID, don't override it. */
@ -266,13 +262,11 @@ int rhizome_direct_form_received(rhizome_http_request *r)
m = NULL;
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,500,"Could not bind manifest to an ID");
}
} else if (bskhex) {
}
} else if (!rhizome_is_bk_none(&config.rhizome.api.addfile.bundle_secret_key)) {
/* Allow user to specify a bundle secret key so that the same bundle can
be updated, rather than creating a new bundle each time. */
unsigned char bsk[RHIZOME_BUNDLE_KEY_BYTES];
fromhexstr(bsk,bskhex,RHIZOME_BUNDLE_KEY_BYTES);
memcpy(m->cryptoSignSecret, bsk, RHIZOME_BUNDLE_KEY_BYTES);
memcpy(m->cryptoSignSecret, config.rhizome.api.addfile.bundle_secret_key.binary, RHIZOME_BUNDLE_KEY_BYTES);
if (rhizome_verify_bundle_privatekey(m,m->cryptoSignSecret,m->cryptoSignPublic) == -1) {
rhizome_manifest_free(m);
m = NULL;
@ -295,7 +289,7 @@ int rhizome_direct_form_received(rhizome_http_request *r)
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
return rhizome_server_simple_http_response(r,500,"Could not bind manifest to file");
}
}
if (rhizome_manifest_finalise(m)) {
rhizome_manifest_free(m);
rhizome_direct_clear_temporary_files(r);
@ -576,8 +570,7 @@ struct http_request_parts {
int rhizome_direct_parse_http_request(rhizome_http_request *r)
{
const char *submitBareFileURI=confValueGet("rhizome.api.addfile.uri", NULL);
DEBUGF("uri=%s", submitBareFileURI ? alloca_str_toprint(submitBareFileURI) : "NULL");
DEBUGF("uri=%s", alloca_str_toprint(config.rhizome.api.addfile.uri_path));
/* Switching to writing, so update the call-back */
r->alarm.poll.events=POLLOUT;
@ -630,7 +623,7 @@ int rhizome_direct_parse_http_request(rhizome_http_request *r)
} else if (strcmp(verb, "POST") == 0
&& ( strcmp(path, "/rhizome/import") == 0
|| strcmp(path, "/rhizome/enquiry") == 0
|| (submitBareFileURI && strcmp(path, submitBareFileURI) == 0)
|| (config.rhizome.api.addfile.uri_path[0] && strcmp(path, config.rhizome.api.addfile.uri_path) == 0)
)
) {
const char *cl_str=str_str(headers,"Content-Length: ",headerlen);

View File

@ -21,8 +21,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef __SERVALD_SERVALD_H
#define __SERVALD_SERVALD_H
// #define MALLOC_PARANOIA
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
@ -112,42 +110,16 @@ struct in_addr {
#include <sys/stat.h>
#include "constants.h"
#include "mem.h"
#include "xprintf.h"
#include "log.h"
#include "net.h"
#include "conf.h"
/* All wall clock times in the Serval daemon are represented in milliseconds
* since the Unix epoch. The gettime_ms() function uses gettimeofday(2) to
* return this value when called. The time_ms_t typedef should be used
* wherever this time value is handled or stored.
*
* This type could perfectly well be unsigned, but is defined as signed to
* avoid the need to cast or define a special signed timedelta_ms_t type at **
* (1):
*
* static time_ms_t then = 0;
* time_ms_t now = gettime_ms();
* time_ms_t ago = now - then; // ** (1)
* if (then && ago < 0) {
* ... time going backwards ...
* } else {
* ... time has advanced ...
* then = now;
* }
*/
typedef long long time_ms_t;
/* bzero(3) is deprecated in favour of memset(3). */
#define bzero(addr,len) memset((addr), 0, (len))
#include "os.h"
/* UDP Port numbers for various Serval services.
The overlay mesh works over DNA */
#define PORT_DNA 4110
/* OpenWRT libc doesn't have bcopy, but has memmove */
#define bcopy(A,B,C) memmove(B,A,C)
#define BATCH 1
#define NONBATCH 0
@ -168,7 +140,29 @@ typedef long long time_ms_t;
/* Limit packet payloads to minimise packet loss of big packets in mesh networks */
#define MAX_DATA_BYTES 256
double simulatedBER;
/*
* INSTANCE_PATH can be set via the ./configure option --enable-instance-path=<path>
*/
#ifdef INSTANCE_PATH
#define DEFAULT_INSTANCE_PATH INSTANCE_PATH
#else
#ifdef ANDROID
#define DEFAULT_INSTANCE_PATH "/data/data/org.servalproject/var/serval-node"
#else
#define DEFAULT_INSTANCE_PATH "/var/serval-node"
#endif
#endif
/* Handy statement for forming a path to an instance file in a char buffer whose declaration
* is in scope (so that sizeof(buf) will work). Evaluates to true if the pathname fitted into
* the provided buffer, false (0) otherwise (after logging an error).
*/
#define FORM_SERVAL_INSTANCE_PATH(buf, path) (form_serval_instance_path(buf, sizeof(buf), (path)))
const char *serval_instancepath();
int create_serval_instance_dir();
int form_serval_instance_path(char *buf, size_t bufsiz, const char *path);
void serval_setinstancepath(const char *instancepath);
extern int serverMode;
extern int servalShutdown;
@ -345,7 +339,7 @@ typedef struct overlay_interface {
struct sched_ent alarm;
char name[256];
int recv_offset;
int fileP;
int fileP; // dummyP
int bits_per_second;
int port;
int type;
@ -360,7 +354,7 @@ typedef struct overlay_interface {
For ~10K ISM915MHz (nominal range ~3000m) it will probably be about 15000ms.
These figures will be refined over time, and we will allow people to set them per-interface.
*/
int tick_ms; /* milliseconds per tick */
unsigned tick_ms; /* milliseconds per tick */
int send_broadcasts;
/* The time of the last tick on this interface in milli seconds */
time_ms_t last_tick_ms;
@ -402,7 +396,12 @@ extern overlay_interface overlay_interfaces[OVERLAY_MAX_INTERFACES];
extern int overlay_last_interface_number; // used to remember where a packet came from
extern unsigned int overlay_sequence_number;
ssize_t recvwithttl(int sock, unsigned char *buffer, size_t bufferlen, int *ttl, struct sockaddr *recvaddr, socklen_t *recvaddrlen);
typedef struct sid_binary {
unsigned char binary[SID_SIZE];
} sid_t;
#define SID_ANY ((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}})
// is the SID entirely 0xFF?
#define is_sid_broadcast(SID) is_all_matching(SID, SID_SIZE, 0xFF)
@ -414,12 +413,8 @@ int str_is_subscriber_id(const char *sid);
int strn_is_subscriber_id(const char *sid, size_t *lenp);
int str_is_did(const char *did);
int strn_is_did(const char *did, size_t *lenp);
int str_is_uri(const char *uri);
int stowSid(unsigned char *packet, int ofs, const char *sid);
void srandomdev();
time_ms_t gettime_ms();
time_ms_t sleep_ms(time_ms_t milliseconds);
int server_pid();
void server_save_argv(int argc, const char *const *argv);
int server(char *backing_file);
@ -437,7 +432,6 @@ int packetOkOverlay(struct overlay_interface *interface,unsigned char *packet, s
int overlay_frame_process(struct overlay_interface *interface, struct overlay_frame *f);
int overlay_frame_resolve_addresses(struct overlay_frame *f);
#define alloca_tohex(buf,len) tohex((char *)alloca((len)*2+1), (buf), (len))
#define alloca_tohex_sid(sid) alloca_tohex((sid), SID_SIZE)
#define alloca_tohex_sas(sas) alloca_tohex((sas), SAS_SIZE)
@ -518,9 +512,6 @@ int rhizome_opendb();
int parseCommandLine(const char *argv0, int argc, const char *const *argv);
int form_serval_instance_path(char * buf, size_t bufsiz, const char *path);
int create_serval_instance_dir();
int overlay_mdp_get_fds(struct pollfd *fds,int *fdcount,int fdmax);
int overlay_mdp_reply_error(int sock,
struct sockaddr_un *recvaddr,int recvaddrlen,
@ -597,19 +588,6 @@ int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
int overlay_mdp_encode_ports(struct overlay_buffer *plaintext, int dst_port, int src_port);
int overlay_mdp_dnalookup_reply(const sockaddr_mdp *dstaddr, const unsigned char *resolved_sid, const char *uri, const char *did, const char *name);
int urandombytes(unsigned char *x,unsigned long long xlen);
#ifdef MALLOC_PARANOIA
#define malloc(X) _serval_debug_malloc(X,__WHENCE__)
#define calloc(X,Y) _serval_debug_calloc(X,Y,__WHENCE__)
#define free(X) _serval_debug_free(X,__WHENCE__)
void *_serval_debug_malloc(unsigned int bytes, struct __sourceloc whence);
void *_serval_debug_calloc(unsigned int bytes, unsigned int count, struct __sourceloc whence);
void _serval_debug_free(void *p, struct __sourceloc whence);
#endif
struct vomp_call_state;
void set_codec_flag(int codec, unsigned char *flags);
@ -631,8 +609,6 @@ int cli_puts(const char *str);
int cli_printf(const char *fmt, ...);
int cli_delim(const char *opt);
int is_configvarname(const char *arg);
int overlay_mdp_getmyaddr(int index,unsigned char *sid);
int overlay_mdp_bind(unsigned char *localaddr,int port);
int overlay_route_node_info(overlay_mdp_nodeinfo *node_info);
@ -649,12 +625,12 @@ int directory_registration();
int directory_service_init();
struct command_line_option;
int app_rhizome_direct_sync(int argc, const char *const *argv, struct command_line_option *o, void *context);
int app_rhizome_direct_sync(int argc, const char *const *argv, const struct command_line_option *o, void *context);
#ifdef HAVE_VOIPTEST
int app_pa_phone(int argc, const char *const *argv, struct command_line_option *o, void *context);
int app_pa_phone(int argc, const char *const *argv, const struct command_line_option *o, void *context);
#endif
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o, void *context);
int app_vomp_console(int argc, const char *const *argv, struct command_line_option *o, void *context);
int app_monitor_cli(int argc, const char *const *argv, const struct command_line_option *o, void *context);
int app_vomp_console(int argc, const char *const *argv, const struct command_line_option *o, void *context);
int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax);

View File

@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <sys/stat.h>
#include "serval.h"
#include "conf.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
@ -36,7 +37,6 @@ char *exec_args[EXEC_NARGS + 1];
int exec_argc = 0;
int serverMode=0;
int serverRespawnOnCrash = 0;
int servalShutdown = 0;
static int server_getpid = 0;
@ -95,7 +95,6 @@ int server(char *backing_file)
sleep_ms(atoi(delay));
serverMode = 1;
serverRespawnOnCrash = confValueGetBoolean("server.respawn_on_crash", 0);
/* Catch crash signals so that we can log a backtrace before expiring. */
struct sigaction sig;
@ -362,7 +361,7 @@ void crash_handler(int signal)
WHYF("Caught %s", buf);
dump_stack();
BACKTRACE;
if (serverRespawnOnCrash) {
if (config.server.respawn_on_crash) {
int i;
for(i=0;i<overlay_interface_count;i++)
if (overlay_interfaces[i].alarm.poll.fd>-1)

View File

@ -3,6 +3,9 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \
$(SERVAL_BASE)cli.c \
$(SERVAL_BASE)commandline.c \
$(SERVAL_BASE)conf.c \
$(SERVAL_BASE)conf_om.c \
$(SERVAL_BASE)conf_parse.c \
$(SERVAL_BASE)conf_schema.c \
$(SERVAL_BASE)crypto.c \
$(SERVAL_BASE)dataformats.c \
$(SERVAL_BASE)directory_client.c \
@ -15,7 +18,9 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \
$(SERVAL_BASE)lsif.c \
$(SERVAL_BASE)main.c \
$(SERVAL_BASE)mdp_client.c \
$(SERVAL_BASE)mkdir.c \
$(SERVAL_BASE)os.c \
$(SERVAL_BASE)mem.c \
$(SERVAL_BASE)instance.c \
$(SERVAL_BASE)monitor.c \
$(SERVAL_BASE)monitor-client.c \
$(SERVAL_BASE)monitor-cli.c \
@ -32,6 +37,7 @@ SERVAL_SOURCES = $(SERVAL_BASE)audiodevices.c \
$(SERVAL_BASE)overlay_packetformats.c \
$(SERVAL_BASE)overlay_payload.c \
$(SERVAL_BASE)overlay_route.c \
$(SERVAL_BASE)packetformats.c \
$(SERVAL_BASE)performance_timing.c \
$(SERVAL_BASE)randombytes.c \
$(SERVAL_BASE)rhizome.c \

View File

@ -55,7 +55,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <unistd.h>
#endif
#include "serval.h"
#include "os.h"
#include <fcntl.h>
#include <stdlib.h>
#include <time.h>

290
str.c
View File

@ -25,8 +25,10 @@
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <limits.h>
char hexdigit[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
const char hexdigit[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char *tohex(char *dstHex, const unsigned char *srcBinary, size_t bytes)
{
@ -161,24 +163,50 @@ char *str_str(char *haystack, const char *needle, int haystack_len)
return NULL;
}
int str_to_ll_scaled(const char *str, int base, long long *result, const char **afterp)
uint64_t scale_factor(const char *str, const char **afterp)
{
uint64_t factor = 1;
switch (str[0]) {
case 'k': ++str; factor = 1000LL; break;
case 'K': ++str; factor = 1024LL; break;
case 'm': ++str; factor = 1000LL * 1000LL; break;
case 'M': ++str; factor = 1024LL * 1024LL; break;
case 'g': ++str; factor = 1000LL * 1000LL * 1000LL; break;
case 'G': ++str; factor = 1024LL * 1024LL * 1024LL; break;
}
if (afterp)
*afterp = str;
else if (*str)
factor = 0;
return factor;
}
int str_to_int64_scaled(const char *str, int base, int64_t *result, const char **afterp)
{
if (!(isdigit(*str) || *str == '-' || *str == '+'))
return 0;
char *end;
long long value = strtoll(str, &end, base);
const char *end = str;
long long value = strtoll(str, (char**)&end, base);
if (end == str)
return 0;
switch (*end) {
case '\0': break;
case 'k': value *= 1000LL; ++end; break;
case 'K': value *= 1024LL; ++end; break;
case 'm': value *= 1000LL * 1000LL; ++end; break;
case 'M': value *= 1024LL * 1024LL; ++end; break;
case 'g': value *= 1000LL * 1000LL * 1000LL; ++end; break;
case 'G': value *= 1024LL * 1024LL * 1024LL; ++end; break;
default: return 0;
}
value *= scale_factor(end, &end);
if (afterp)
*afterp = end;
else if (*end)
return 0;
*result = value;
return 1;
}
int str_to_uint64_scaled(const char *str, int base, uint64_t *result, const char **afterp)
{
if (!isdigit(*str))
return 0;
const char *end = str;
unsigned long long value = strtoull(str, (char**)&end, base);
if (end == str)
return 0;
value *= scale_factor(end, &end);
if (afterp)
*afterp = end;
else if (*end)
@ -208,25 +236,26 @@ size_t toprint_len(const char *srcBuf, size_t srcBytes, const char quotes[2])
return strbuf_count(strbuf_toprint_quoted_len(strbuf_local(NULL, 0), quotes, srcBuf, srcBytes));
}
/* Format a null-terminated string as a printable representation, eg: "Abc\x0b\n", for display
in log messages.
/* Format a null-terminated string as a printable representation, eg: `Abc\x0b\n`, for display
in log messages. If the given string pointer is NULL, return the string "NULL" without quotes.
@author Andrew Bettison <andrew@servalproject.com>
*/
char *toprint_str(char *dstStr, ssize_t dstBufSiz, const char *srcStr, const char quotes[2])
{
strbuf b = strbuf_local(dstStr, dstBufSiz);
strbuf_toprint_quoted(b, quotes, srcStr);
if (srcStr)
strbuf_toprint_quoted(b, quotes, srcStr);
else
strbuf_puts(b, "NULL");
return dstStr;
}
/* Compute the length of the string produced by toprint_str(). If dstStrLen == -1 then returns the
exact number of characters in the printable representation (excluding the terminating nul),
otherwise returns dstStrLen.
/* Compute the length of the string produced by toprint_str(), excluding the terminating nul.
@author Andrew Bettison <andrew@servalproject.com>
*/
size_t toprint_str_len(const char *srcStr, const char quotes[2])
{
return strbuf_count(strbuf_toprint_quoted(strbuf_local(NULL, 0), quotes, srcStr));
return srcStr ? strbuf_count(strbuf_toprint_quoted(strbuf_local(NULL, 0), quotes, srcStr)) : 4;
}
size_t str_fromprint(unsigned char *dst, const char *src)
@ -262,3 +291,222 @@ size_t str_fromprint(unsigned char *dst, const char *src)
}
return dst - odst;
}
/* Return true if the string resembles a URI.
* Based on RFC-3986 generic syntax, assuming nothing about the hierarchical part.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_is_uri(const char *uri)
{
const char *p;
size_t len;
if (!str_uri_scheme(uri, &p, &len))
return 0;
const char *const q = (p += len + 1);
for (; *p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '?' && *p != '#'; ++p)
;
if (p == q)
return 0;
if (*p == '?')
for (++p; *p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '?' && *p != '#'; ++p)
;
if (*p == '#')
for (++p; *p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '?' && *p != '#'; ++p)
;
return !*p;
}
int str_uri_scheme(const char *uri, const char **partp, size_t *lenp)
{
const char *p = uri;
// Scheme is ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
if (!isalpha(*p++))
return 0;
while (is_uri_char_scheme(*p))
++p;
// Scheme is followed by colon ":".
if (*p != ':')
return 0;
if (partp)
*partp = uri;
if (lenp)
*lenp = p - uri;
return 1;
}
int str_uri_hierarchical(const char *uri, const char **partp, size_t *lenp)
{
const char *p = uri;
while (*p && *p != ':')
++p;
if (*p != ':')
return 0;
const char *const q = ++p;
while (*p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '?' && *p != '#')
++p;
if (p == q)
return 0;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_query(const char *uri, const char **partp, size_t *lenp)
{
const char *p = uri;
while (*p && *p != '?')
++p;
if (*p != '?')
return 0;
const char *const q = ++p;
while (*p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '#')
++p;
if (p == q || (*p && *p != '#'))
return 0;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_fragment(const char *uri, const char **partp, size_t *lenp)
{
const char *p = uri;
while (*p && *p != '#')
++p;
if (*p != '#')
return 0;
const char *const q = ++p;
while (*p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)))
++p;
if (p == q || *p)
return 0;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_hierarchical_authority(const char *hier, const char **partp, size_t *lenp)
{
if (hier[0] != '/' || hier[1] != '/')
return 0;
const char *const q = hier + 2;
const char *p = q;
while (*p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '/' && *p != '?' && *p != '#')
++p;
if (p == q || (*p && *p != '/' && *p != '?' && *p != '#'))
return 0;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_hierarchical_path(const char *hier, const char **partp, size_t *lenp)
{
if (hier[0] != '/' || hier[1] != '/')
return 0;
const char *p = hier + 2;
while (*p && *p != '/' && *p != '?' && *p != '#')
++p;
if (!*p)
return 0;
const char *const q = ++p;
while (*p && (is_uri_char_unreserved(*p) || is_uri_char_reserved(*p)) && *p != '/' && *p != '?' && *p != '#')
++p;
if (p == q || (*p && *p != '/' && *p != '?' && *p != '#'))
return 0;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_authority_username(const char *auth, const char **partp, size_t *lenp)
{
const char *p;
for (p = auth; *p && *p != '@' && *p != '/' && *p != '?' && *p != '#'; ++p)
;
if (*p != '@')
return 0;
for (p = auth; *p && *p != ':' && *p != '@'; ++p)
;
if (*p != ':')
return 0;
if (partp)
*partp = auth;
if (lenp)
*lenp = p - auth;
return 1;
}
int str_uri_authority_password(const char *auth, const char **partp, size_t *lenp)
{
const char *p;
for (p = auth; *p && *p != '@' && *p != '/' && *p != '?' && *p != '#'; ++p)
;
if (*p != '@')
return 0;
for (p = auth; *p && *p != ':' && *p != '@'; ++p)
;
if (*p != ':')
return 0;
const char *const q = ++p;
for (; *p && *p != '@'; ++p)
;
assert(*p == '@');
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_authority_hostname(const char *auth, const char **partp, size_t *lenp)
{
const char *p;
const char *q = auth;
for (p = auth; *p && *p != '/' && *p != '?' && *p != '#'; ++p)
if (*p == '@')
q = p + 1;
const char *r = p;
while (r > q && isdigit(*--r))
;
if (r < p - 1 && *r == ':')
p = r;
if (partp)
*partp = q;
if (lenp)
*lenp = p - q;
return 1;
}
int str_uri_authority_port(const char *auth, unsigned short *portp)
{
const char *p;
const char *q = auth;
for (p = auth; *p && *p != '/' && *p != '?' && *p != '#'; ++p)
if (*p == '@')
q = p + 1;
const char *r = p;
while (r > q && isdigit(*--r))
;
if (r < p - 1 && *r == ':') {
for (++r; *r == '0'; ++r)
;
int n;
if (p - r <= 5 && (n = atoi(r)) <= USHRT_MAX) {
*portp = n;
return 1;
}
}
return 0;
}

107
str.h
View File

@ -21,6 +21,7 @@
#define __STR_H__
#include <string.h>
#include <stdint.h>
#include <sys/types.h>
#include <ctype.h>
#include <alloca.h>
@ -59,13 +60,15 @@ __STR_INLINE int is_xstring(const char *text, int len)
return *text == '\0';
}
extern char hexdigit[16];
extern const char hexdigit[16];
char *tohex(char *dstHex, const unsigned char *srcBinary, size_t bytes);
size_t fromhex(unsigned char *dstBinary, const char *srcHex, size_t nbinary);
int fromhexstr(unsigned char *dstBinary, const char *srcHex, size_t nbinary);
int is_all_matching(const unsigned char *ptr, size_t len, unsigned char value);
char *str_toupper_inplace(char *s);
#define alloca_tohex(buf,len) tohex((char *)alloca((len)*2+1), (buf), (len))
__STR_INLINE int hexvalue(char c)
{
if (c >= '0' && c <= '9') return c - '0';
@ -137,7 +140,107 @@ char *str_str(char *haystack, const char *needle, int haystack_len);
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_to_ll_scaled(const char *str, int base, long long *result, const char **afterp);
int str_to_int64_scaled(const char *str, int base, int64_t *result, const char **afterp);
int str_to_uint64_scaled(const char *str, int base, uint64_t *result, const char **afterp);
uint64_t scale_factor(const char *str, const char **afterp);
/* Return true if the string resembles a nul-terminated URI.
* Based on RFC-3986 generic syntax, assuming nothing about the hierarchical part.
*
* uri := scheme ":" hierarchical [ "?" query ] [ "#" fragment ]
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_is_uri(const char *uri);
__STR_INLINE int is_uri_char_scheme(char c)
{
return isalpha(c) || isdigit(c) || c == '+' || c == '-' || c == '.';
}
__STR_INLINE int is_uri_char_unreserved(char c)
{
return isalpha(c) || isdigit(c) || c == '-' || c == '.' || c == '_' || c == '~';
}
__STR_INLINE int is_uri_char_reserved(char c)
{
switch (c) {
case ':': case '/': case '?': case '#': case '[': case ']': case '@':
case '!': case '$': case '&': case '\'': case '(': case ')':
case '*': case '+': case ',': case ';': case '=':
return 1;
}
return 0;
}
/* Return true if the string resembles a URI scheme without the terminating colon.
* Based on RFC-3986 generic syntax.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
__STR_INLINE int str_is_uri_scheme(const char *scheme)
{
if (!isalpha(*scheme++))
return 0;
while (is_uri_char_scheme(*scheme))
++scheme;
return *scheme == '\0';
}
/* Pick apart a URI into its basic parts.
*
* uri := scheme ":" hierarchical [ "?" query ] [ "#" fragment ]
*
* Based on RFC-3986 generic syntax, assuming nothing about the hierarchical
* part. If the respective part is found, sets (*partp) to point to the start
* of the part within the supplied 'uri' string, sets (*lenp) to the length of
* the part substring and returns 1. Otherwise returns 0. These functions
* do not reliably validate that the string in 'uri' is a valid URI; that must
* be done by calling str_is_uri().
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_uri_scheme(const char *uri, const char **partp, size_t *lenp);
int str_uri_hierarchical(const char *uri, const char **partp, size_t *lenp);
int str_uri_query(const char *uri, const char **partp, size_t *lenp);
int str_uri_fragment(const char *uri, const char **partp, size_t *lenp);
/* Pick apart a URI hierarchical part into its basic parts.
*
* hierarchical := "//" authority [ "/" path ]
*
* If the respective part is found, sets (*partp) to point to the start of the
* part within the supplied 'uri' string, sets (*lenp) to the length of the
* part substring and returns 1. Otherwise returns 0.
*
* These functions may be called directly on the part returned by
* str_uri_hierarchical(), even though it is not nul-terminated, because they
* treat "?" and "#" as equally valid terminators.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_uri_hierarchical_authority(const char *hier, const char **partp, size_t *lenp);
int str_uri_hierarchical_path(const char *hier, const char **partp, size_t *lenp);
/* Pick apart a URI authority into its basic parts.
*
* authority := [ username ":" password "@" ] hostname [ ":" port ]
*
* If the respective part is found, sets (*partp) to point to the start of the
* part within the supplied 'uri' string, sets (*lenp) to the length of the
* part substring and returns 1. Otherwise returns 0.
*
* These functions may be called directly on the part returned by
* str_uri_hierarchical_authority(), even though it is not nul-terminated,
* because they treat "/", "?" and "#" as equally valid terminators.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
int str_uri_authority_username(const char *auth, const char **partp, size_t *lenp);
int str_uri_authority_password(const char *auth, const char **partp, size_t *lenp);
int str_uri_authority_hostname(const char *auth, const char **partp, size_t *lenp);
int str_uri_authority_port(const char *auth, unsigned short *portp);
int parse_argv(char *cmdline, char delim, char **argv, int max_argv);

View File

@ -21,6 +21,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <poll.h>
#include <ctype.h>
#include <string.h>
#include <stdarg.h>
#include <sys/wait.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
@ -112,6 +113,22 @@ strbuf strbuf_toprint_quoted(strbuf sb, const char quotes[2], const char *str)
return _overrun_quote(sb, quotes ? quotes[1] : '\0', "...");
}
strbuf strbuf_path_join(strbuf sb, ...)
{
va_list ap;
va_start(ap, sb);
const char *segment;
while ((segment = va_arg(ap, const char*))) {
if (segment[0] == '/')
strbuf_reset(sb);
else if (strbuf_len(sb) && *strbuf_substr(sb, -1) != '/')
strbuf_putc(sb, '/');
strbuf_puts(sb, segment);
}
va_end(ap);
return sb;
}
strbuf strbuf_append_poll_events(strbuf sb, short events)
{
static struct { short flags; const char *name; } symbols[] = {
@ -186,6 +203,20 @@ strbuf strbuf_append_shell_quotemeta(strbuf sb, const char *word)
return sb;
}
strbuf strbuf_append_argv(strbuf sb, int argc, const char *const *argv)
{
int i;
for (i = 0; i < argc; ++i) {
if (i)
strbuf_putc(sb, ' ');
if (argv[i])
strbuf_toprint_quoted(sb, "\"\"", argv[i]);
else
strbuf_puts(sb, "NULL");
}
return sb;
}
strbuf strbuf_append_exit_status(strbuf sb, int status)
{
if (WIFEXITED(status))

View File

@ -46,6 +46,14 @@ strbuf strbuf_toprint_quoted_len(strbuf sb, const char quotes[2], const char *bu
*/
strbuf strbuf_toprint_quoted(strbuf sb, const char quotes[2], const char *str);
/* Join Unix file path segments together with separator characters '/' to form
* a complete path. Any segment that starts with '/' is taken as the start of
* an absolute path, and all prior segments are discarded.
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_path_join(strbuf sb, ...);
/* Append a symbolic representation of the poll(2) event flags.
* @author Andrew Bettison <andrew@servalproject.com>
*/
@ -74,6 +82,13 @@ strbuf strbuf_append_shell_quote(strbuf sb, const char *word);
*/
strbuf strbuf_append_shell_quotemeta(strbuf sb, const char *word);
/* Append an array of nul-terminated strings as a space-separated sequence of
* quoted strings. Any NULL entry in argv[] is printed as unquoted "NULL".
*
* @author Andrew Bettison <andrew@servalproject.com>
*/
strbuf strbuf_append_argv(strbuf sb, int argc, const char *const *argv);
/* Append a textual description of a process exit status as produced by wait(2)
* and waitpid(2).
* @author Andrew Bettison <andrew@servalproject.com>

View File

@ -25,6 +25,33 @@ setup() {
setup_servald
}
assert_stderr_log() {
local -a warn_patterns=()
local -a error_patterns=()
while [ $# -gt 0 ]; do
case "$1" in
--warn-pattern=*) warn_patterns+=("${1#*=}"); shift;;
--error-pattern=*) error_patterns+=("${1#*=}"); shift;;
*) error "unsupported option: $1"; return;;
esac
done
assertStderrGrep \
--matches=${#error_patterns[*]} \
--message="stderr of ($executed) contains exactly ${#error_patterns[*]} error message(s)" \
'^ERROR:'
local pattern
for pattern in "${warn_patterns[@]}"; do
assertStderrGrep \
--message="stderr of ($executed) contains a warning message matching \"$pattern\"" \
"^WARN:.*$pattern"
done
for pattern in "${error_patterns[@]}"; do
assertStderrGrep \
--message="stderr of ($executed) contains an error message matching \"$pattern\"" \
"^ERROR:.*$pattern"
done
}
doc_GetCreateInstanceDir="Get creates instance directory"
setup_GetCreateInstanceDir() {
setup
@ -41,96 +68,102 @@ setup_SetCreateInstanceDir() {
assert ! [ -d "$SERVALINSTANCE_PATH" ]
}
test_SetCreateInstanceDir() {
executeOk_servald config set foo bar
executeOk_servald config set debug.verbose 0
assert [ -d "$SERVALINSTANCE_PATH" ]
}
doc_GetNull="Get an unset config item"
test_GetNull() {
executeOk_servald config get foo
executeOk_servald config get debug.verbose
assertStdoutLineCount '==' 0
}
doc_SetGet="Set and get a single config item"
test_SetGet() {
executeOk_servald config set foo bar
executeOk_servald config get foo
executeOk_servald config set debug.verbose yes
executeOk_servald config get debug.verbose
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo=bar$'
assertStdoutGrep --stdout --stderr --matches=1 '^debug\.verbose=yes$'
}
doc_GetAll="Get all config items"
test_GetAll() {
executeOk_servald config set foo bar
executeOk_servald config set hello world
executeOk_servald config \
set debug.verbose true \
set log.show_pid true \
set server.chdir /tmp/nothing \
set rhizome.enable no
executeOk_servald config get
assertStdoutLineCount '==' 4
assertStdoutGrep --stdout --matches=1 '^debug\.verbose=true$'
assertStdoutGrep --stdout --matches=1 '^log\.show_pid=true$'
assertStdoutGrep --stdout --matches=1 '^server\.chdir=/tmp/nothing$'
assertStdoutGrep --stdout --matches=1 '^rhizome\.enable=no$'
}
doc_SetDelMixed="Set and del config items in single command"
test_SetDelMixed() {
executeOk_servald config \
set debug.verbose true \
set log.show_pid true \
set server.chdir /tmp/nothing \
set rhizome.enable no
executeOk_servald config \
set debug.verbose false \
del log.show_pid \
set log.show_time 1 \
del server.chdir \
del rhizome.enable
executeOk_servald config get
assertStdoutLineCount '==' 2
assertStdoutGrep --stdout --matches=1 '^foo=bar$'
assertStdoutGrep --stdout --matches=1 '^hello=world$'
assertStdoutGrep --stdout --matches=1 '^debug\.verbose=false$'
assertStdoutGrep --stdout --matches=1 '^log\.show_time=1$'
}
doc_SetTwice="Set a single config item twice"
test_SetTwice() {
executeOk_servald config set foo bar
executeOk_servald config get foo
executeOk_servald config set debug.verbose yes
executeOk_servald config get debug.verbose
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo=bar$'
executeOk_servald config set foo wah
executeOk_servald config get foo
assertStdoutGrep --stdout --stderr --matches=1 '^debug\.verbose=yes$'
executeOk_servald config set debug.verbose false
executeOk_servald config get debug.verbose
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo=wah$'
assertStdoutGrep --stdout --stderr --matches=1 '^debug\.verbose=false$'
}
doc_DelNull="Delete an unset config item"
test_DelNull() {
executeOk_servald config del foo
executeOk_servald config del debug.verbose
assertStdoutLineCount '==' 0
}
doc_Del="Delete single config item"
test_Del() {
executeOk_servald config set foo bar
executeOk_servald config set hello world
executeOk_servald config set debug.verbose yes set log.show_pid true
executeOk_servald config get
assertStdoutLineCount '==' 2
executeOk_servald config del foo
executeOk_servald config del debug.verbose
executeOk_servald config get
assertStdoutLineCount '==' 1
executeOk_servald config get foo
executeOk_servald config del log.show_pid
assertStdoutLineCount '==' 0
}
doc_CaseInsensitive="Config item names are case insensitive"
test_CaseInsensitive() {
executeOk_servald config set foo bar
executeOk_servald config get foo
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo=bar$'
executeOk_servald config get Foo
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^Foo=bar$'
executeOk_servald config set FOO wah
executeOk_servald config get foo
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo=wah$'
doc_CaseSensitive="Config item names are case sensitive"
test_CaseSensitive() {
execute $servald config set Debug.verbose yes
assertExitStatus --stderr '!=' 0
}
doc_DotsInNames="Config item names can have internal dots"
test_DotsInNames() {
executeOk_servald config set foo.bar yes
executeOk_servald config get foo.bar
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo\.bar=yes$'
execute $servald config set foo. yes
assertExitStatus '!=' 0
execute $servald config set .foo yes
assertExitStatus '!=' 0
execute $servald config set foo..bar yes
assertExitStatus '!=' 0
executeOk_servald config set foo.x.bar yes
executeOk_servald config get foo.x.bar
assertStdoutLineCount '==' 1
assertStdoutGrep --stdout --stderr --matches=1 '^foo\.x\.bar=yes$'
doc_OptionNames="Config item names must be well formed"
test_OptionNames() {
execute $servald config set debug. yes
assertExitStatus --stderr '!=' 0
execute $servald config set .verbose yes
assertExitStatus --stderr '!=' 0
execute $servald config del debug..verbose
assertExitStatus --stderr '!=' 0
}
doc_DebugFlags="Debug config options affect verbosity"
@ -161,4 +194,89 @@ test_DebugFlagAll() {
assertStderrGrep --matches=3 '\<echo:argv\['
}
doc_InterfacesLegacyIncludeAll="Legacy 'interfaces' config option include all"
test_InterfacesLegacyIncludeAll() {
executeOk_servald config set interfaces '+'
}
doc_InterfacesLegacyExcludeAll="Legacy 'interfaces' config option exclude all"
test_InterfacesLegacyExcludeAll() {
executeOk_servald config set interfaces '-'
}
doc_InterfacesLegacyMixed="Legacy 'interfaces' config option mixed list"
test_InterfacesLegacyMixed() {
executeOk_servald config set interfaces '+eth,-wifi,+'
}
doc_InterfacesLegacyMixedDetail="Legacy 'interfaces' config option mixed list with details"
test_InterfacesLegacyMixedDetail() {
executeOk_servald config set interfaces '+eth=ethernet:4111:9M, +wifi=wifi:4112:900K, -'
}
doc_InterfacesLegacyInvalidType="Legacy 'interfaces' config option invalid type"
test_InterfacesLegacyInvalidType() {
execute --stderr --core-backtrace --exit-status=2 --executable=$servald \
config set interfaces '+eth=foo:4111:9M'
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*not loaded.*invalid'
}
doc_InterfacesLegacyInvalidPort="Legacy 'interfaces' config option invalid port"
test_InterfacesLegacyInvalidPort() {
execute --stderr --core-backtrace --exit-status=2 --executable=$servald \
config set interfaces '+eth=ethernet:-1000:9M'
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*not loaded.*invalid'
}
doc_InterfacesLegacyInvalidSpeed="Legacy 'interfaces' config option invalid speed"
test_InterfacesLegacyInvalidSpeed() {
execute --stderr --core-backtrace --exit-status=2 --executable=$servald \
config set interfaces '+eth=ethernet:4111:9MB'
assert_stderr_log \
--warn-pattern='"interfaces".*invalid' \
--error-pattern='config file.*not loaded.*invalid'
}
doc_InterfacesLegacyIncompatible="Legacy 'interfaces' config option incompatible with modern form"
test_InterfacesLegacyIncompatible() {
executeOk_servald config set interfaces '+'
execute --stderr --core-backtrace --executable=$servald \
config set interfaces.10.match 'eth'
tfw_cat $SERVALINSTANCE_PATH/serval.conf
assertExitStatus '==' 2
assert_stderr_log \
--warn-pattern='"interfaces.*incompatible' \
--error-pattern='config file.*not loaded.*incompatible'
tfw_cat --stderr
}
doc_InterfacesModernMatch="Modern 'interfaces.N.match' config option"
test_InterfacesModernMatch() {
executeOk_servald config set interfaces.0.match 'eth*, wifi*'
}
doc_InterfacesModernDummy="Modern 'interfaces.N.dummy' config option"
test_InterfacesModernDummy() {
executeOk_servald config set interfaces.0.dummy dummyname
}
teardown_InterfacesModernDummy() {
tfw_cat $SERVALINSTANCE_PATH/serval.conf
teardown
}
doc_InterfacesModernIncompatible="Config options 'interfaces.match' and 'interfaces.dummy' are incompatible"
test_InterfacesModernIncompatible() {
executeOk_servald config set interfaces.0.match eth
execute --stderr --core-backtrace --executable=$servald \
config set interfaces.0.dummy dummy
assertExitStatus '==' 2
assert_stderr_log \
--warn-pattern='"interfaces\.0\..*incompatible' \
--error-pattern='config file.*not loaded.*incompatible'
}
runTests "$@"

View File

@ -21,9 +21,10 @@ source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
configure_servald_server() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.mdprequests Yes
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set debug.mdprequests Yes
}
setup() {
@ -37,8 +38,9 @@ setup() {
DIDD=$DIDC1
NAMED=$NAMED1
set_instance +A
executeOk_servald config set dna.helper.executable "$servald_build_root/directory_service"
executeOk_servald config set debug.dnahelper on
executeOk_servald config \
set dna.helper.executable "$servald_build_root/directory_service" \
set debug.dnahelper on
foreach_instance +B +C +D executeOk_servald config set directory.service $SIDA
start_servald_instances +A +B +C +D
}
@ -84,12 +86,13 @@ test_publish() {
}
start_routing_instance() {
executeOk_servald config set interface.folder "$SERVALD_VAR"
executeOk_servald config set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.mdprequests Yes
executeOk_servald config \
set server.dummy_interface_dir "$SERVALD_VAR" \
set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name" \
set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name" \
set log.show_pid on \
set log.show_time on \
set debug.mdprequests Yes
start_servald_server
}
@ -101,23 +104,27 @@ setup_routing() {
>$SERVALD_VAR/dummyB
>$SERVALD_VAR/dummyC
set_instance +A
executeOk_servald config set interfaces "+>dummyB,+>dummyC"
executeOk_servald config set mdp.dummyB.tick_ms 0
executeOk_servald config set mdp.dummyC.tick_ms 0
executeOk_servald config set dna.helper.executable "$servald_build_root/directory_service"
executeOk_servald config set debug.dnahelper on
executeOk_servald config \
set interfaces.0.dummy dummyB \
set interfaces.0.mdp_tick_ms 0 \
set interfaces.1.dummy dummyC \
set interfaces.1.mdp_tick_ms 0 \
set dna.helper.executable "$servald_build_root/directory_service" \
set debug.dnahelper on
set_instance +B
executeOk_servald config set interfaces "+>dummyB"
executeOk_servald config set mdp.dummyB.tick_ms 0
executeOk_servald config set directory.service $SIDA
executeOk_servald config set $SIDA.interface "dummyB"
executeOk_servald config set $SIDA.address 127.0.0.1
executeOk_servald config \
set interfaces.0.dummy dummyB \
set interfaces.0.mdp_tick_ms 0 \
set directory.service $SIDA \
set hosts.$SIDA.address 127.0.0.1 \
set hosts.$SIDA.interface "dummyB"
set_instance +C
executeOk_servald config set interfaces "+>dummyC"
executeOk_servald config set mdp.dummyC.tick_ms 0
executeOk_servald config set directory.service $SIDA
executeOk_servald config set $SIDA.interface "dummyC"
executeOk_servald config set $SIDA.address 127.0.0.1
executeOk_servald config \
set interfaces.1.dummy dummyC \
set interfaces.1.mdp_tick_ms 0 \
set directory.service $SIDA \
set hosts.$SIDA.address 127.0.0.1 \
set hosts.$SIDA.interface "dummyC"
foreach_instance +A +B +C start_routing_instance
}

View File

@ -42,11 +42,13 @@ teardown() {
# Called by start_servald_instances immediately before starting the server
# process in each instance.
configure_servald_server() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.dnahelper on
executeOk_servald config set dna.helper.executable "$dnahelper"
executeOk_servald config set dna.helper.argv.1 "Hello, World!"
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set debug.dnahelper on \
set dna.helper.executable "$dnahelper" \
set dna.helper.argv.1 "Hello," \
set dna.helper.argv.2 "World!"
}
setup_dnahelper() {
@ -232,8 +234,8 @@ test_ExecError() {
executeOk_servald dna lookup 12345
}
doc_ExecArg1="DNA helper configured argument"
setup_ExecArg1() {
doc_ExecArgs="DNA helper configured argument"
setup_ExecArgs() {
setup_servald
assert_no_servald_processes
dnahelper="$TFWTMP/dnahelper"
@ -246,7 +248,7 @@ do
line="${line#*|}"
did="${line%%|*}"
line="${line#*|}"
echo "$token|uri:dumb|$did|$1|"
echo "$token|uri:dumb|$did|$*|"
echo DONE
done
EOF
@ -254,7 +256,7 @@ EOF
start_servald_instances +A
assert_all_instance_peers_complete +A
}
test_ExecArg1() {
test_ExecArgs() {
executeOk_servald dna lookup 12345
assertStdoutIs -e "uri:dumb:12345:Hello, World!\n"
}

View File

@ -58,17 +58,17 @@ teardown() {
}
set_server_vars() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set mdp.wifi.tick_ms 100
executeOk_servald config set mdp.selfannounce.ticks_per_full_address 1
executeOk_servald config set debug.interfaces Yes
executeOk_servald config set debug.packetformats No
executeOk_servald config set debug.routing No
executeOk_servald config set debug.tx No
executeOk_servald config set debug.rx No
executeOk_servald config set debug.mdprequests Yes
executeOk_servald config set debug.keyring Yes
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set mdp.iftype.wifi.tick_ms 100 \
set debug.interfaces Yes \
set debug.packetformats No \
set debug.routing No \
set debug.tx No \
set debug.rx No \
set debug.mdprequests Yes \
set debug.keyring Yes
}
doc_LookupWildcard="Lookup by wildcard"
@ -139,9 +139,10 @@ EOF
foreach_instance +A +B +C +D create_single_identity
configure_servald_server() {
set_server_vars
executeOk_servald config set debug.dnahelper on
executeOk_servald config set dna.helper.executable "$dnahelper"
executeOk_servald config set dna.helper.argv.1 "$instance_name"
executeOk_servald config \
set debug.dnahelper on \
set dna.helper.executable "$dnahelper" \
set dna.helper.argv.1 "$instance_name"
}
start_servald_instances +A +B +C +D
wait_until --sleep=0.25 instances_reach_each_other +A +B +C +D

View File

@ -36,14 +36,14 @@ teardown() {
# Called by start_servald_instances for each instance.
configure_servald_server() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.rhizome on
executeOk_servald config set debug.rhizometx on
executeOk_servald config set debug.rhizomerx on
executeOk_servald config set server.respawn_on_signal off
executeOk_servald config set mdp.wifi.tick_ms 500
executeOk_servald config set mdp.selfannounce.ticks_per_full_address 1
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set debug.rhizome on \
set debug.rhizometx on \
set debug.rhizomerx on \
set server.respawn_on_crash off \
set mdp.iftype.wifi.tick_ms 500
}
setup_curl_7() {
@ -340,15 +340,16 @@ setup_HttpAddLocal() {
setup_curl_7
setup_common
set_instance +A
executeOk_servald config set rhizome.api.addfile.uri "/rhizome/secretaddfile"
executeOk_servald config set rhizome.api.addfile.author $SIDA
executeOk_servald config \
set rhizome.api.addfile.uri_path "/rhizome/secretaddfile" \
set rhizome.api.addfile.default_author $SIDA
start_servald_instances +A
wait_until rhizome_http_server_started +A
get_rhizome_server_port PORTA +A
}
test_HttpAddLocal() {
echo 'File file1' >file1
executeOk curl --silent --form 'data=@file1' "http://$addr_localhost:$PORTA/rhizome/secretaddfile" --output file1.manifest
executeOk curl --silent --form 'data=@file1' "http://${addr_localhost}:$PORTA/rhizome/secretaddfile" --output file1.manifest
assert_manifest_complete file1.manifest
executeOk_servald rhizome list ''
assert_rhizome_list --fromhere=1 file1
@ -366,12 +367,12 @@ setup_sync() {
wait_until rhizome_http_server_started +A
get_rhizome_server_port PORTA +A
set_instance +B
executeOk_servald config set log.show_time on
executeOk_servald config set debug.rhizome on
executeOk_servald config set debug.rhizometx on
executeOk_servald config set debug.rhizomerx on
executeOk_servald config set rhizome.direct.peer.count "1"
executeOk_servald config set rhizome.direct.peer.0 "http://${addr_localhost}:${PORTA}"
executeOk_servald config \
set log.show_time on \
set debug.rhizome on \
set debug.rhizometx on \
set debug.rhizomerx on \
set rhizome.direct.peer.0 "http://${addr_localhost}:${PORTA}"
rhizome_add_file file2
BID2=$BID
VERSION2=$VERSION

View File

@ -36,14 +36,14 @@ teardown() {
# Called by start_servald_instances for each instance.
configure_servald_server() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.rhizome on
executeOk_servald config set debug.rhizometx on
executeOk_servald config set debug.rhizomerx on
executeOk_servald config set server.respawn_on_signal off
executeOk_servald config set mdp.wifi.tick_ms 500
executeOk_servald config set mdp.selfannounce.ticks_per_full_address 1
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set debug.rhizome on \
set debug.rhizometx on \
set debug.rhizomerx on \
set server.respawn_on_crash off \
set mdp.iftype.wifi.tick_ms 500
}
doc_FileTransferStress="Stress - five nodes each sharing 16 bundles"

View File

@ -21,9 +21,10 @@ source "${0%/*}/../testframework.sh"
source "${0%/*}/../testdefs.sh"
configure_servald_server() {
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.mdprequests Yes
executeOk_servald config \
set log.show_pid on \
set log.show_time on \
set debug.mdprequests Yes
}
add_interface() {
@ -34,12 +35,13 @@ add_interface() {
}
start_routing_instance() {
executeOk_servald config set interface.folder "$SERVALD_VAR"
executeOk_servald config set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config set log.show_pid on
executeOk_servald config set log.show_time on
executeOk_servald config set debug.mdprequests Yes
executeOk_servald config \
set server.dummy_interface_dir "$SERVALD_VAR" \
set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name" \
set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name" \
set log.show_pid on \
set log.show_time on \
set debug.mdprequests Yes
start_servald_server
}

View File

@ -28,9 +28,10 @@ setup() {
setup_interfaces() {
>$TFWTMP/dummy
executeOk_servald config set interfaces "+>$TFWTMP/dummy"
executeOk_servald config set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name"
executeOk_servald config \
set interfaces "+>$TFWTMP/dummy" \
set monitor.socket "org.servalproject.servald.monitor.socket.$TFWUNIQUE.$instance_name" \
set mdp.socket "org.servalproject.servald.mdp.socket.$TFWUNIQUE.$instance_name"
}
teardown() {
@ -61,14 +62,14 @@ test_StartLogfile() {
tfw_cat log
}
doc_StartNoInterfaces="Starting server with no configured interfaces gives error"
doc_StartNoInterfaces="Starting server with no configured interfaces gives warning"
setup_StartNoInterfaces() {
setup
}
test_StartNoInterfaces() {
start_servald_server
sleep 0.1
assertGrep --message="log contains 'no interfaces' error message" "$instance_servald_log" '^ERROR:.*interfaces'
assertGrep --message="log contains 'no interfaces' warning" "$instance_servald_log" '^WARN:.*interfaces'
tfw_cat "$instance_servald_log"
}

View File

@ -49,13 +49,13 @@ static inline char stripe(int i)
int main(int argc, char **argv)
{
argv0 = argv[0];
long long size = 0;
uint64_t size = 0;
const char *label = "";
int i;
for (i = 1; i < argc; ++i) {
const char *arg = argv[i];
if (str_startswith(arg, "--size=", &arg)) {
if (!str_to_ll_scaled(arg, 10, &size, NULL) || size < 0)
if (!str_to_uint64_scaled(arg, 10, &size, NULL) || size < 0)
fatal("illegal --size= argument: %s", arg);
}
else if (str_startswith(arg, "--label=", &arg))
@ -63,7 +63,7 @@ int main(int argc, char **argv)
else
fatal("unrecognised argument: %s", arg);
}
long long offset = 0;
uint64_t offset = 0;
char buf[127];
for (i = 0; i != sizeof buf; ++i)
buf[i] = stripe(i);

View File

@ -25,6 +25,7 @@
#include <fcntl.h>
#include "serval.h"
#include "conf.h"
#include "cli.h"
#include "monitor-client.h"
#include "str.h"
@ -185,7 +186,7 @@ struct monitor_command_handler console_handlers[]={
{.command="MONITORSTATUS", .handler=remote_noop},
};
static int console_dial(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int console_dial(int argc, const char *const *argv, const struct command_line_option *o, void *context){
if (call_token!=-1){
printf("Already in a call\n");
return 0;
@ -197,7 +198,7 @@ static int console_dial(int argc, const char *const *argv, struct command_line_o
return 0;
}
static int console_answer(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int console_answer(int argc, const char *const *argv, const struct command_line_option *o, void *context){
if (call_token==-1){
printf("No active call to answer\n");
fflush(stdout);
@ -206,7 +207,7 @@ static int console_answer(int argc, const char *const *argv, struct command_line
return 0;
}
static int console_hangup(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int console_hangup(int argc, const char *const *argv, const struct command_line_option *o, void *context){
if (call_token==-1){
printf("No call to hangup\n");
fflush(stdout);
@ -215,7 +216,7 @@ static int console_hangup(int argc, const char *const *argv, struct command_line
return 0;
}
static int console_audio(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int console_audio(int argc, const char *const *argv, const struct command_line_option *o, void *context){
if (call_token==-1){
printf("No active call\n");
fflush(stdout);
@ -238,7 +239,7 @@ static int console_audio(int argc, const char *const *argv, struct command_line_
return 0;
}
static int console_usage(int argc, const char *const *argv, struct command_line_option *o, void *context);
static int console_usage(int argc, const char *const *argv, const struct command_line_option *o, void *context);
struct command_line_option console_commands[]={
{console_answer,{"answer",NULL},0,"Answer an incoming phone call"},
@ -249,7 +250,7 @@ struct command_line_option console_commands[]={
{NULL},
};
static int console_usage(int argc, const char *const *argv, struct command_line_option *o, void *context){
static int console_usage(int argc, const char *const *argv, const struct command_line_option *o, void *context){
cli_usage(console_commands);
fflush(stdout);
return 0;
@ -259,9 +260,12 @@ static void console_command(char *line){
char *argv[16];
int argc = parse_argv(line, ' ', argv, 16);
if (cli_execute(NULL, argc, (const char *const*)argv, console_commands, NULL)){
int ret = cli_parse(argc, (const char *const*)argv, console_commands);
if (ret == -1) {
printf("Unknown command, try help\n");
fflush(stdout);
} else {
cli_invoke(&console_commands[ret], argc, (const char *const*)argv, NULL);
}
}
@ -302,7 +306,8 @@ static void monitor_read(struct sched_ent *alarm){
}
}
int app_vomp_console(int argc, const char *const *argv, struct command_line_option *o, void *context){
int app_vomp_console(int argc, const char *const *argv, const struct command_line_option *o, void *context)
{
static struct profile_total stdin_profile={
.name="read_lines",
};