mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-18 02:39:44 +00:00
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:
commit
8425882ffc
39
Makefile.in
39
Makefile.in
@ -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
17
cli.c
@ -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
16
cli.h
@ -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
|
||||
|
191
commandline.c
191
commandline.c
@ -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
661
conf.c
@ -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
591
conf.h
@ -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
554
conf_om.c
Normal 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
415
conf_parse.c
Normal 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
596
conf_schema.c
Normal 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
327
conf_schema.h
Normal 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
165
config_test.c
Normal 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;
|
||||
}
|
@ -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
|
||||
|
131
dataformats.c
131
dataformats.c
@ -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;
|
||||
|
@ -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));
|
||||
}
|
||||
|
33
dna_helper.c
33
dna_helper.c
@ -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;
|
||||
|
@ -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
57
instance.c
Normal 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
40
log.c
@ -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
2
log.h
@ -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
2
main.c
@ -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
81
mem.c
Normal 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
65
mem.h
Normal 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
|
@ -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;
|
||||
|
@ -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
|
||||
|
25
monitor.c
25
monitor.c
@ -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
60
net.c
@ -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
2
net.h
@ -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
|
||||
|
@ -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
64
os.h
Normal 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
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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[]={
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
32
packetformats.c
Normal 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;
|
||||
}
|
20
rhizome.c
20
rhizome.c
@ -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
|
||||
|
16
rhizome.h
16
rhizome.h
@ -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
|
||||
|
@ -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 */
|
||||
|
124
rhizome_direct.c
124
rhizome_direct.c
@ -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);
|
||||
|
@ -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);
|
||||
|
98
serval.h
98
serval.h
@ -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);
|
||||
|
||||
|
5
server.c
5
server.c
@ -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)
|
||||
|
@ -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 \
|
||||
|
@ -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
290
str.c
@ -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
107
str.h
@ -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);
|
||||
|
@ -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))
|
||||
|
@ -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>
|
||||
|
216
tests/config
216
tests/config
@ -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 "$@"
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
11
tests/server
11
tests/server
@ -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"
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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",
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user