Refactor source and build process to split out test commands

This commit is contained in:
Jeremy Lakeman 2014-12-01 12:48:46 +10:30
parent 44ed0ba575
commit 7bc253e844
13 changed files with 523 additions and 530 deletions

View File

@ -18,6 +18,17 @@ OBJSDIR_TOOLS = objs
OBJSDIRS = $(OBJSDIR_SERVALD) $(OBJSDIR_LIB) $(OBJSDIR_TOOLS)
ALL_SOURCES = \
$(SERVAL_CLIENT_SOURCES) \
$(MDP_CLIENT_SOURCES) \
$(SERVAL_DAEMON_SOURCES) \
$(NACL_SOURCES) \
$(TEST_SOURCES) \
$(SERVAL_LIB_SOURCES) \
$(MONITOR_CLIENT_SRCS) \
$(SIMULATOR_SOURCES) \
$(SQLITE3_SOURCES)
SERVAL_DAEMON_OBJS = \
$(addprefix $(OBJSDIR_SERVALD)/, $(SERVAL_CLIENT_SOURCES:.c=.o)) \
$(addprefix $(OBJSDIR_SERVALD)/, $(MDP_CLIENT_SOURCES:.c=.o)) \
@ -26,6 +37,10 @@ SERVALD_OBJS = \
$(addprefix $(OBJSDIR_SERVALD)/, $(notdir $(SQLITE3_SOURCES:.c=.o))) \
$(addprefix $(OBJSDIR_SERVALD)/, $(NACL_SOURCES:.c=.o)) \
$(SERVAL_DAEMON_OBJS)
TEST_OBJS = \
$(addprefix $(OBJSDIR_SERVALD)/, $(TEST_SOURCES:.c=.o)) \
$(addprefix $(OBJSDIR_SERVALD)/, $(NACL_SOURCES:.c=.o)) \
$(addprefix $(OBJSDIR_SERVALD)/, $(SERVAL_CLIENT_SOURCES:.c=.o))
LIB_SERVAL_OBJS = \
$(addprefix $(OBJSDIR_LIB)/, $(SERVAL_CLIENT_SOURCES:.c=.o)) \
$(addprefix $(OBJSDIR_LIB)/, $(SERVAL_LIB_SOURCES:.c=.o)) \
@ -67,7 +82,7 @@ DEFS= @DEFS@
all: servald libserval.so libmonitorclient.so libmonitorclient.a test
test: tfw_createfile directory_service fakeradio config_test simulator
test: tfw_createfile directory_service fakeradio simulator serval-tests
covzero: | is_built_with_coverage
@echo REMOVE all .gcda files
@ -117,7 +132,7 @@ config.status: configure
configure: configure.in
$(warning configure may be out of date, please run autoreconf -f -i)
$(OBJSDIR_TOOLS)/version.o: $(SERVALD_OBJS) version_servald.c version_string.sh $(wildcard VERSION.txt) COPYRIGHT.txt
$(OBJSDIR_TOOLS)/version.o: $(ALL_SOURCES) $(HDRS) version_servald.c version_string.sh $(wildcard VERSION.txt) COPYRIGHT.txt
@echo CC version_servald.c
@mkdir -p $(dir $@)
@$(RM) $(@:.o=.gcno) $(@:.o=.gcda)
@ -159,14 +174,15 @@ $(SERVALD_OBJS): Makefile
$(LIB_SERVAL_OBJS): $(HDRS) Makefile
$(MONITOR_CLIENT_OBJS): $(HDRS) Makefile
$(SIMULATOR_OBJS): $(HDRS) Makefile
$(OBJSDIR_TOOLS)/directory_service.o $(OBJSDIR_TOOLS)/tfw_createfile.o $(OBJSDIR_TOOLS)/fakeradio.o \
$(OBJSDIR_TOOLS)/config_test.o $(OBJSDIR_TOOLS)/conf_om.o $(OBJSDIR_TOOLS)/conf_schema.o $(OBJSDIR_TOOLS)/conf_parse.o libserval.a \
: $(HDRS) Makefile
servald: $(SERVALD_OBJS) $(OBJSDIR_TOOLS)/version.o
@echo LINK $@
@$(CC) -Wall -o $@ $(SERVALD_OBJS) $(OBJSDIR_TOOLS)/version.o $(LDFLAGS)
serval-tests: $(TEST_OBJS) $(OBJSDIR_TOOLS)/version.o
@echo LINK $@
@$(CC) -Wall -o $@ $(TEST_OBJS) $(OBJSDIR_TOOLS)/version.o $(LDFLAGS)
libserval.a: $(LIB_SERVAL_OBJS) $(OBJSDIR_TOOLS)/version.o
@echo AR $@
@$(AR) -cr $@ $(LIB_SERVAL_OBJS) $(OBJSDIR_TOOLS)/version.o
@ -187,10 +203,6 @@ simulator: $(SIMULATOR_OBJS) libserval.a
@echo LINK $@
@$(CC) -Wall -o $@ $(SIMULATOR_OBJS) libserval.a $(LDFLAGS)
config_test: $(OBJSDIR_TOOLS)/config_test.o $(OBJSDIR_TOOLS)/conf_om.o $(OBJSDIR_TOOLS)/conf_schema.o $(OBJSDIR_TOOLS)/conf_parse.o libserval.a
@echo LINK $@
@$(CC) -Wall -o $@ $(OBJSDIR_TOOLS)/config_test.o $(OBJSDIR_TOOLS)/conf_om.o $(OBJSDIR_TOOLS)/conf_schema.o $(OBJSDIR_TOOLS)/conf_parse.o libserval.a $(LDFLAGS)
copyright:
@if [ -x "$(COPYRIGHT_TOOL)" ]; then \
echo GENERATE COPYRIGHT.txt; \

View File

@ -250,10 +250,7 @@ int parseCommandLine(struct cli_context *context, const char *argv0, int argc, c
break;
}
/* clean up after ourselves */
rhizome_close_db();
free_subscribers();
assert(keyring==NULL);
cli_cleanup();
OUT();
@ -555,278 +552,3 @@ static int app_log(const struct cli_parsed *parsed, struct cli_context *UNUSED(c
return 0;
}
static void lookup_send_request(int mdp_sockfd, const sid_t *srcsid, int srcport, const sid_t *dstsid, const char *did)
{
overlay_mdp_frame mdp;
bzero(&mdp,sizeof(mdp));
/* set source address to the local address and port */
mdp.out.src.port = srcport;
mdp.out.src.sid = *srcsid;
/* Send to destination address and DNA lookup port */
if (dstsid) {
/* Send an encrypted unicast packet */
mdp.packetTypeAndFlags=MDP_TX;
mdp.out.dst.sid = *dstsid;
}else{
/* Send a broadcast packet, flooding across the local mesh network */
mdp.packetTypeAndFlags=MDP_TX|MDP_NOCRYPT;
mdp.out.dst.sid = SID_BROADCAST;
}
mdp.out.dst.port=MDP_PORT_DNALOOKUP;
/* put DID into packet */
bcopy(did,&mdp.out.payload[0],strlen(did)+1);
mdp.out.payload_length=strlen(did)+1;
overlay_mdp_send(mdp_sockfd, &mdp, 0, 0);
/* Also send an encrypted unicast request to a configured directory service */
if (!dstsid){
if (!is_sid_t_any(config.directory.service)) {
mdp.out.dst.sid = config.directory.service;
mdp.packetTypeAndFlags=MDP_TX;
overlay_mdp_send(mdp_sockfd, &mdp,0,0);
}
}
}
DEFINE_CMD(app_dna_lookup, 0,
"Lookup the subscribers (SID) with the supplied telephone number (DID).",
"dna","lookup","<did>","[<timeout>]");
static int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
{
int mdp_sockfd;
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
/* Create the instance directory if it does not yet exist */
if (create_serval_instance_dir() == -1)
return -1;
int uri_count=0;
#define MAXREPLIES 256
#define MAXURILEN 256
char uris[MAXREPLIES][MAXURILEN];
const char *did, *delay;
if (cli_arg(parsed, "did", &did, cli_lookup_did, "*") == -1)
return -1;
if (cli_arg(parsed, "timeout", &delay, NULL, "3000") == -1)
return -1;
int idelay=atoi(delay);
int one_reply=0;
// Ugly hack, if timeout is negative, stop after first reply
if (idelay<0){
one_reply=1;
idelay=-idelay;
}
if ((mdp_sockfd = overlay_mdp_client_socket()) < 0)
return WHY("Cannot create MDP socket");
/* Bind to MDP socket and await confirmation */
sid_t srcsid;
mdp_port_t port=32768+(random()&32767);
if (overlay_mdp_getmyaddr(mdp_sockfd, 0, &srcsid)) {
overlay_mdp_client_close(mdp_sockfd);
return WHY("Could not get local address");
}
if (overlay_mdp_bind(mdp_sockfd, &srcsid, port)) {
overlay_mdp_client_close(mdp_sockfd);
return WHY("Could not bind to MDP socket");
}
/* use MDP to send the lookup request to MDP_PORT_DNALOOKUP, and wait for
replies. */
/* Now repeatedly send resolution request and collect results until we reach
timeout. */
time_ms_t timeout = gettime_ms() + idelay;
time_ms_t last_tx = 0;
time_ms_t now;
int interval=125;
const char *names[]={
"uri",
"did",
"name"
};
cli_columns(context, 3, names);
size_t rowcount = 0;
while (timeout > (now = gettime_ms())){
if ((last_tx+interval)<now){
lookup_send_request(mdp_sockfd, &srcsid, port, NULL, did);
last_tx=now;
interval+=interval>>1;
}
time_ms_t short_timeout=125;
while(short_timeout>0) {
if (overlay_mdp_client_poll(mdp_sockfd, short_timeout)){
overlay_mdp_frame rx;
int ttl;
if (overlay_mdp_recv(mdp_sockfd, &rx, port, &ttl)==0){
if (rx.packetTypeAndFlags==MDP_ERROR){
WHYF(" Error message: %s", rx.error.message);
} else if ((rx.packetTypeAndFlags&MDP_TYPE_MASK)==MDP_TX) {
/* Extract DID, Name, URI from response. */
if (strlen((char *)rx.out.payload)<512) {
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if ( !parseDnaReply((char *)rx.out.payload, rx.out.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s", alloca_toprint(160, (const char *)rx.out.payload, rx.out.payload_length));
} else {
/* Have we seen this response before? */
int i;
for(i=0;i<uri_count;i++)
if (!strcmp(uri,uris[i])) break;
if (i==uri_count) {
/* Not previously seen, so report it */
cli_put_string(context, uri, ":");
cli_put_string(context, did, ":");
cli_put_string(context, name, "\n");
rowcount++;
if (one_reply){
timeout=now;
short_timeout=0;
}
/* Remember that we have seen it */
if (uri_count<MAXREPLIES&&strlen(uri)<MAXURILEN) {
strcpy(uris[uri_count++],uri);
}
}
}
}
}
else WHYF("packettype=0x%x",rx.packetTypeAndFlags);
}
}
short_timeout=125-(gettime_ms()-now);
}
}
overlay_mdp_client_close(mdp_sockfd);
cli_row_count(context, rowcount);
return 0;
}
DEFINE_CMD(app_reverse_lookup, 0,
"Lookup the phone number (DID) and name of a given subscriber (SID)",
"reverse", "lookup", "<sid>", "[<timeout>]");
static int app_reverse_lookup(const struct cli_parsed *parsed, struct cli_context *context)
{
int mdp_sockfd;
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
const char *sidhex, *delay;
if (cli_arg(parsed, "sid", &sidhex, str_is_subscriber_id, "") == -1)
return -1;
if (cli_arg(parsed, "timeout", &delay, NULL, "3000") == -1)
return -1;
mdp_port_t port=32768+(random()&0xffff);
sid_t srcsid;
sid_t dstsid;
if (str_to_sid_t(&dstsid, sidhex) == -1)
return WHY("str_to_sid_t() failed");
if ((mdp_sockfd = overlay_mdp_client_socket()) < 0)
return WHY("Cannot create MDP socket");
if (overlay_mdp_getmyaddr(mdp_sockfd, 0, &srcsid)){
overlay_mdp_client_close(mdp_sockfd);
return WHY("Unable to get my address");
}
if (overlay_mdp_bind(mdp_sockfd, &srcsid, port)){
overlay_mdp_client_close(mdp_sockfd);
return WHY("Unable to bind port");
}
time_ms_t now = gettime_ms();
time_ms_t timeout = now + atoi(delay);
time_ms_t next_send = now;
overlay_mdp_frame mdp_reply;
while (now < timeout){
now=gettime_ms();
if (now >= next_send){
/* Send a unicast packet to this node, asking for any did */
lookup_send_request(mdp_sockfd, &srcsid, port, &dstsid, "");
next_send+=125;
continue;
}
time_ms_t poll_timeout = (next_send>timeout?timeout:next_send) - now;
if (overlay_mdp_client_poll(mdp_sockfd, poll_timeout)<=0)
continue;
int ttl=-1;
if (overlay_mdp_recv(mdp_sockfd, &mdp_reply, port, &ttl))
continue;
if ((mdp_reply.packetTypeAndFlags&MDP_TYPE_MASK)==MDP_ERROR){
// TODO log error?
continue;
}
if (mdp_reply.packetTypeAndFlags!=MDP_TX) {
WHYF("MDP returned an unexpected message (type=0x%x)",
mdp_reply.packetTypeAndFlags);
if (mdp_reply.packetTypeAndFlags==MDP_ERROR)
WHYF("MDP message is return/error: %d:%s",
mdp_reply.error.error,mdp_reply.error.message);
continue;
}
// we might receive a late response from an ealier request on the same socket, ignore it
if (cmp_sid_t(&mdp_reply.out.src.sid, &dstsid) != 0) {
WHYF("Unexpected result from SID %s", alloca_tohex_sid_t(mdp_reply.out.src.sid));
continue;
}
{
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if ( !parseDnaReply((char *)mdp_reply.out.payload, mdp_reply.out.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s",
alloca_toprint(160, (const char *)mdp_reply.out.payload, mdp_reply.out.payload_length));
continue;
}
/* Got a good DNA reply, copy it into place and stop polling */
cli_field_name(context, "sid", ":");
cli_put_string(context, sidhex, "\n");
cli_field_name(context, "did", ":");
cli_put_string(context, did, "\n");
cli_field_name(context, "name", ":");
cli_put_string(context, name, "\n");
overlay_mdp_client_close(mdp_sockfd);
return 0;
}
}
overlay_mdp_client_close(mdp_sockfd);
return 1;
}

View File

@ -67,4 +67,6 @@ void cli_put_string(struct cli_context *context, const char *value, const char *
void cli_put_hexvalue(struct cli_context *context, const unsigned char *value, int length, const char *delim);
int cli_write(struct cli_context *context, const unsigned char *buf, size_t len);
void cli_cleanup();
#endif

4
conf.c
View File

@ -51,7 +51,7 @@ static int reload(const char *path, int *resultp)
if (config.debug.config)
DEBUGF(" file path=%s", alloca_str_toprint(path));
struct file_meta meta;
if (get_file_meta(conffile_path(), &meta) == -1)
if (get_file_meta(path, &meta) == -1)
return -1;
if (config.debug.config) {
DEBUGF(" file meta=%s", alloca_file_meta(&meta));
@ -60,7 +60,7 @@ static int reload(const char *path, int *resultp)
if (cmp_file_meta(&meta, &conffile_meta) == 0)
return 0;
if (conffile_meta.mtime.tv_sec != -1)
INFOF("config file %s -- detected new version", conffile_path());
INFOF("config file %s -- detected new version", path);
char *buf = NULL;
if (meta.mtime.tv_sec == -1) {
WARNF("config file %s does not exist -- using all defaults", path);

View File

@ -1,114 +0,0 @@
/*
Serval DNA configuration stand-alone configuration check utility
Copyright 2013 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 <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/stat.h>
#include "str.h"
#define __SERVAL_LOG_INLINE
#include "log.h"
#include "conf.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_om_parse(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_om_free_node(&root);
free(buf);
DEBUGF("result = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), result)));
DEBUGF("config.log.file.path = %s", alloca_str_toprint(config.log.file.path));
DEBUGF("config.log.file.show_pid = %d", config.log.file.show_pid);
DEBUGF("config.log.file.show_time = %d", config.log.file.show_time);
DEBUGF("config.server.chdir = %s", alloca_str_toprint(config.server.chdir));
DEBUGF("config.debug.verbose = %d", config.debug.verbose);
DEBUGF("config.directory.service = %s", alloca_tohex_sid_t(config.directory.service));
DEBUGF("config.rhizome.api.addfile.allow_host = %s", inet_ntoa(config.rhizome.api.addfile.allow_host));
unsigned 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.%u", config.interfaces.av[j].key);
DEBUGF(" .exclude = %d", config.interfaces.av[j].value.exclude);
DEBUGF(" .match = [");
unsigned 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(" .drop_broadcasts = %d", (int) config.interfaces.av[j].value.drop_broadcasts);
DEBUGF(" .drop_unicasts = %d", (int) config.interfaces.av[j].value.drop_unicasts);
DEBUGF(" .drop_packets = %u", (unsigned) config.interfaces.av[j].value.drop_packets);
}
for (j = 0; j < config.hosts.ac; ++j) {
char sidhex[SID_STRLEN + 1];
tohex(sidhex, SID_STRLEN, config.hosts.av[j].key.binary);
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);
}
void cf_on_config_change()
{
}

View File

@ -60,14 +60,7 @@ struct radio_state {
#define STATE_PLUSPLUSPLUS 3
#define STATE_COMMAND 4
int64_t gettime_ms()
{
struct timeval nowtv;
// If gettimeofday() fails or returns an invalid value, all else is lost!
if (gettimeofday(&nowtv, NULL) == -1)
perror("gettimeofday");
return nowtv.tv_sec * 1000LL + nowtv.tv_usec / 1000;
}
void cf_on_config_change(){}
void log_time(){
struct timeval tv;

115
log.c
View File

@ -17,6 +17,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <libgen.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
@ -29,7 +30,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <sys/stat.h>
#include <fcntl.h>
#include <stdint.h>
#include <libgen.h>
#include <dirent.h>
#include <assert.h>
@ -401,14 +401,6 @@ static void _log_iterator_vprintf_nl(_log_iterator *it, int level, struct __sour
}
}
static void _log_iterator_printf_nl(_log_iterator *it, int level, struct __sourceloc whence, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
_log_iterator_vprintf_nl(it, level, whence, fmt, ap);
va_end(ap);
}
static void _logs_vprintf_nl(int level, struct __sourceloc whence, const char *fmt, va_list ap)
{
_log_iterator it;
@ -688,108 +680,3 @@ void logConfigChanged()
logFlush();
}
int logBacktrace(int level, struct __sourceloc whence)
{
#ifndef NO_BACKTRACE
_log_iterator it;
_log_iterator_start(&it);
_rotate_log_file(&it);
char execpath[MAXPATHLEN];
if (get_self_executable_path(execpath, sizeof execpath) == -1)
return WHY("cannot log backtrace: own executable path unknown");
char tempfile[MAXPATHLEN];
if (!FORMF_SERVAL_TMP_PATH(tempfile, "servalgdb.XXXXXX"))
return -1;
int tmpfd = mkstemp(tempfile);
if (tmpfd == -1)
return WHYF_perror("mkstemp(%s)", alloca_str_toprint(tempfile));
if (write_str(tmpfd, "backtrace\n") == -1) {
close(tmpfd);
unlink(tempfile);
return -1;
}
if (close(tmpfd) == -1) {
WHY_perror("close");
unlink(tempfile);
return -1;
}
char pidstr[12];
snprintf(pidstr, sizeof pidstr, "%jd", (intmax_t)getpid());
int stdout_fds[2];
if (pipe(stdout_fds) == -1)
return WHY_perror("pipe");
pid_t child_pid;
switch (child_pid = fork()) {
case -1: // error
WHY_perror("fork");
close(stdout_fds[0]);
close(stdout_fds[1]);
return WHY("cannot log backtrace: fork failed");
case 0: // child
if (dup2(stdout_fds[1], 1) == -1 || dup2(stdout_fds[1], 2) == -1) {
perror("dup2");
_exit(-1);
}
close(0);
if (open("/dev/null", O_RDONLY) != 0) {
perror("open(\"/dev/null\")");
_exit(-2);
}
close(stdout_fds[0]);
// Need the (void*) cast on Solaris because it defines NULL as 0L and gcc doesn't accept it as a
// sentinal
execlp("gdb", "gdb", "-n", "-batch", "-x", tempfile, execpath, pidstr, (void*)NULL);
perror("execlp(\"gdb\")");
do { _exit(-3); } while (1);
break;
}
// parent
close(stdout_fds[1]);
_log_iterator_printf_nl(&it, level, whence, "GDB BACKTRACE");
char buf[1024];
char *const bufe = buf + sizeof buf;
char *linep = buf;
char *readp = buf;
ssize_t nr;
while ((nr = read(stdout_fds[0], readp, bufe - readp)) > 0) {
char *p = readp;
readp = readp + nr;
for (; p < readp; ++p)
if (*p == '\n' || *p == '\0') {
*p = '\0';
_log_iterator_printf_nl(&it, level, __NOWHERE__, "GDB %s", linep);
linep = p + 1;
}
if (readp >= bufe && linep == buf) {
// Line does not fit into buffer.
char t = bufe[-1];
bufe[-1] = '\0';
_log_iterator_printf_nl(&it, level, __NOWHERE__, "GDB %s", buf);
buf[0] = t;
readp = buf + 1;
} else if (readp + 120 >= bufe && linep != buf) {
// Buffer low on space.
if (linep < readp)
memmove(buf, linep, readp - linep);
readp -= linep - buf;
linep = buf;
}
// Invariant: readp < bufe
}
if (nr == -1)
WHY_perror("read");
if (readp > linep) {
*readp = '\0';
_log_iterator_printf_nl(&it, level, __NOWHERE__, "GDB %s", linep);
}
close(stdout_fds[0]);
int status = 0;
if (waitpid(child_pid, &status, 0) == -1)
WHY_perror("waitpid");
strbuf b = strbuf_local(buf, sizeof buf);
strbuf_append_exit_status(b, status);
_log_iterator_printf_nl(&it, level, __NOWHERE__, "gdb %s", buf);
unlink(tempfile);
#endif
return 0;
}

View File

@ -17,9 +17,17 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <libgen.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/param.h>
#include "log.h"
#include "strbuf.h"
#include "strbuf_helpers.h"
#include "instance.h"
#include "str.h"
#include "net.h"
int logDump(int level, struct __sourceloc whence, char *name, const unsigned char *addr, size_t len)
{
@ -104,3 +112,106 @@ int string_to_log_level(const char *text)
if (strcasecmp(text, "silent") == 0) return LOG_LEVEL_SILENT;
return LOG_LEVEL_INVALID;
}
int logBacktrace(int level, struct __sourceloc whence)
{
#ifndef NO_BACKTRACE
char execpath[MAXPATHLEN];
if (get_self_executable_path(execpath, sizeof execpath) == -1)
return WHY("cannot log backtrace: own executable path unknown");
char tempfile[MAXPATHLEN];
if (!FORMF_SERVAL_TMP_PATH(tempfile, "servalgdb.XXXXXX"))
return -1;
int tmpfd = mkstemp(tempfile);
if (tmpfd == -1)
return WHYF_perror("mkstemp(%s)", alloca_str_toprint(tempfile));
if (write_str(tmpfd, "backtrace\n") == -1) {
close(tmpfd);
unlink(tempfile);
return -1;
}
if (close(tmpfd) == -1) {
WHY_perror("close");
unlink(tempfile);
return -1;
}
char pidstr[12];
snprintf(pidstr, sizeof pidstr, "%jd", (intmax_t)getpid());
int stdout_fds[2];
if (pipe(stdout_fds) == -1)
return WHY_perror("pipe");
pid_t child_pid;
switch (child_pid = fork()) {
case -1: // error
WHY_perror("fork");
close(stdout_fds[0]);
close(stdout_fds[1]);
return WHY("cannot log backtrace: fork failed");
case 0: // child
if (dup2(stdout_fds[1], 1) == -1 || dup2(stdout_fds[1], 2) == -1) {
perror("dup2");
_exit(-1);
}
close(0);
if (open("/dev/null", O_RDONLY) != 0) {
perror("open(\"/dev/null\")");
_exit(-2);
}
close(stdout_fds[0]);
// Need the (void*) cast on Solaris because it defines NULL as 0L and gcc doesn't accept it as a
// sentinal
execlp("gdb", "gdb", "-n", "-batch", "-x", tempfile, execpath, pidstr, (void*)NULL);
perror("execlp(\"gdb\")");
do { _exit(-3); } while (1);
break;
}
// parent
close(stdout_fds[1]);
logMessage(level, whence, "GDB BACKTRACE");
char buf[1024];
char *const bufe = buf + sizeof buf;
char *linep = buf;
char *readp = buf;
ssize_t nr;
while ((nr = read(stdout_fds[0], readp, bufe - readp)) > 0) {
char *p = readp;
readp = readp + nr;
for (; p < readp; ++p)
if (*p == '\n' || *p == '\0') {
*p = '\0';
logMessage(level, __NOWHERE__, "GDB %s", linep);
linep = p + 1;
}
if (readp >= bufe && linep == buf) {
// Line does not fit into buffer.
char t = bufe[-1];
bufe[-1] = '\0';
logMessage(level, __NOWHERE__, "GDB %s", buf);
buf[0] = t;
readp = buf + 1;
} else if (readp + 120 >= bufe && linep != buf) {
// Buffer low on space.
if (linep < readp)
memmove(buf, linep, readp - linep);
readp -= linep - buf;
linep = buf;
}
// Invariant: readp < bufe
}
if (nr == -1)
WHY_perror("read");
if (readp > linep) {
*readp = '\0';
logMessage(level, __NOWHERE__, "GDB %s", linep);
}
close(stdout_fds[0]);
int status = 0;
if (waitpid(child_pid, &status, 0) == -1)
WHY_perror("waitpid");
strbuf b = strbuf_local(buf, sizeof buf);
strbuf_append_exit_status(b, status);
logMessage(level, __NOWHERE__, "gdb %s", buf);
unlink(tempfile);
#endif
return 0;
}

View File

@ -27,6 +27,10 @@
#include "conf.h"
#include "commandline.h"
#include "sighandlers.h"
#include "instance.h"
#include "serval.h"
#include "overlay_buffer.h"
DEFINE_CMD(app_mdp_ping, 0,
"Attempts to ping specified node via Mesh Datagram Protocol (MDP).",
@ -530,3 +534,278 @@ static int app_network_scan(const struct cli_parsed *parsed, struct cli_context
cli_put_string(context, mdp.error.message, "\n");
return mdp.error.error;
}
static void lookup_send_request(int mdp_sockfd, const sid_t *srcsid, int srcport, const sid_t *dstsid, const char *did)
{
overlay_mdp_frame mdp;
bzero(&mdp,sizeof(mdp));
/* set source address to the local address and port */
mdp.out.src.port = srcport;
mdp.out.src.sid = *srcsid;
/* Send to destination address and DNA lookup port */
if (dstsid) {
/* Send an encrypted unicast packet */
mdp.packetTypeAndFlags=MDP_TX;
mdp.out.dst.sid = *dstsid;
}else{
/* Send a broadcast packet, flooding across the local mesh network */
mdp.packetTypeAndFlags=MDP_TX|MDP_NOCRYPT;
mdp.out.dst.sid = SID_BROADCAST;
}
mdp.out.dst.port=MDP_PORT_DNALOOKUP;
/* put DID into packet */
bcopy(did,&mdp.out.payload[0],strlen(did)+1);
mdp.out.payload_length=strlen(did)+1;
overlay_mdp_send(mdp_sockfd, &mdp, 0, 0);
/* Also send an encrypted unicast request to a configured directory service */
if (!dstsid){
if (!is_sid_t_any(config.directory.service)) {
mdp.out.dst.sid = config.directory.service;
mdp.packetTypeAndFlags=MDP_TX;
overlay_mdp_send(mdp_sockfd, &mdp,0,0);
}
}
}
DEFINE_CMD(app_dna_lookup, 0,
"Lookup the subscribers (SID) with the supplied telephone number (DID).",
"dna","lookup","<did>","[<timeout>]");
static int app_dna_lookup(const struct cli_parsed *parsed, struct cli_context *context)
{
int mdp_sockfd;
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
/* Create the instance directory if it does not yet exist */
if (create_serval_instance_dir() == -1)
return -1;
int uri_count=0;
#define MAXREPLIES 256
#define MAXURILEN 256
char uris[MAXREPLIES][MAXURILEN];
const char *did, *delay;
if (cli_arg(parsed, "did", &did, cli_lookup_did, "*") == -1)
return -1;
if (cli_arg(parsed, "timeout", &delay, NULL, "3000") == -1)
return -1;
int idelay=atoi(delay);
int one_reply=0;
// Ugly hack, if timeout is negative, stop after first reply
if (idelay<0){
one_reply=1;
idelay=-idelay;
}
if ((mdp_sockfd = overlay_mdp_client_socket()) < 0)
return WHY("Cannot create MDP socket");
/* Bind to MDP socket and await confirmation */
sid_t srcsid;
mdp_port_t port=32768+(random()&32767);
if (overlay_mdp_getmyaddr(mdp_sockfd, 0, &srcsid)) {
overlay_mdp_client_close(mdp_sockfd);
return WHY("Could not get local address");
}
if (overlay_mdp_bind(mdp_sockfd, &srcsid, port)) {
overlay_mdp_client_close(mdp_sockfd);
return WHY("Could not bind to MDP socket");
}
/* use MDP to send the lookup request to MDP_PORT_DNALOOKUP, and wait for
replies. */
/* Now repeatedly send resolution request and collect results until we reach
timeout. */
time_ms_t timeout = gettime_ms() + idelay;
time_ms_t last_tx = 0;
time_ms_t now;
int interval=125;
const char *names[]={
"uri",
"did",
"name"
};
cli_columns(context, 3, names);
size_t rowcount = 0;
while (timeout > (now = gettime_ms())){
if ((last_tx+interval)<now){
lookup_send_request(mdp_sockfd, &srcsid, port, NULL, did);
last_tx=now;
interval+=interval>>1;
}
time_ms_t short_timeout=125;
while(short_timeout>0) {
if (overlay_mdp_client_poll(mdp_sockfd, short_timeout)){
overlay_mdp_frame rx;
int ttl;
if (overlay_mdp_recv(mdp_sockfd, &rx, port, &ttl)==0){
if (rx.packetTypeAndFlags==MDP_ERROR){
WHYF(" Error message: %s", rx.error.message);
} else if ((rx.packetTypeAndFlags&MDP_TYPE_MASK)==MDP_TX) {
/* Extract DID, Name, URI from response. */
if (strlen((char *)rx.out.payload)<512) {
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if ( !parseDnaReply((char *)rx.out.payload, rx.out.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s", alloca_toprint(160, (const char *)rx.out.payload, rx.out.payload_length));
} else {
/* Have we seen this response before? */
int i;
for(i=0;i<uri_count;i++)
if (!strcmp(uri,uris[i])) break;
if (i==uri_count) {
/* Not previously seen, so report it */
cli_put_string(context, uri, ":");
cli_put_string(context, did, ":");
cli_put_string(context, name, "\n");
rowcount++;
if (one_reply){
timeout=now;
short_timeout=0;
}
/* Remember that we have seen it */
if (uri_count<MAXREPLIES&&strlen(uri)<MAXURILEN) {
strcpy(uris[uri_count++],uri);
}
}
}
}
}
else WHYF("packettype=0x%x",rx.packetTypeAndFlags);
}
}
short_timeout=125-(gettime_ms()-now);
}
}
overlay_mdp_client_close(mdp_sockfd);
cli_row_count(context, rowcount);
return 0;
}
DEFINE_CMD(app_reverse_lookup, 0,
"Lookup the phone number (DID) and name of a given subscriber (SID)",
"reverse", "lookup", "<sid>", "[<timeout>]");
static int app_reverse_lookup(const struct cli_parsed *parsed, struct cli_context *context)
{
int mdp_sockfd;
if (config.debug.verbose)
DEBUG_cli_parsed(parsed);
const char *sidhex, *delay;
if (cli_arg(parsed, "sid", &sidhex, str_is_subscriber_id, "") == -1)
return -1;
if (cli_arg(parsed, "timeout", &delay, NULL, "3000") == -1)
return -1;
mdp_port_t port=32768+(random()&0xffff);
sid_t srcsid;
sid_t dstsid;
if (str_to_sid_t(&dstsid, sidhex) == -1)
return WHY("str_to_sid_t() failed");
if ((mdp_sockfd = overlay_mdp_client_socket()) < 0)
return WHY("Cannot create MDP socket");
if (overlay_mdp_getmyaddr(mdp_sockfd, 0, &srcsid)){
overlay_mdp_client_close(mdp_sockfd);
return WHY("Unable to get my address");
}
if (overlay_mdp_bind(mdp_sockfd, &srcsid, port)){
overlay_mdp_client_close(mdp_sockfd);
return WHY("Unable to bind port");
}
time_ms_t now = gettime_ms();
time_ms_t timeout = now + atoi(delay);
time_ms_t next_send = now;
overlay_mdp_frame mdp_reply;
while (now < timeout){
now=gettime_ms();
if (now >= next_send){
/* Send a unicast packet to this node, asking for any did */
lookup_send_request(mdp_sockfd, &srcsid, port, &dstsid, "");
next_send+=125;
continue;
}
time_ms_t poll_timeout = (next_send>timeout?timeout:next_send) - now;
if (overlay_mdp_client_poll(mdp_sockfd, poll_timeout)<=0)
continue;
int ttl=-1;
if (overlay_mdp_recv(mdp_sockfd, &mdp_reply, port, &ttl))
continue;
if ((mdp_reply.packetTypeAndFlags&MDP_TYPE_MASK)==MDP_ERROR){
// TODO log error?
continue;
}
if (mdp_reply.packetTypeAndFlags!=MDP_TX) {
WHYF("MDP returned an unexpected message (type=0x%x)",
mdp_reply.packetTypeAndFlags);
if (mdp_reply.packetTypeAndFlags==MDP_ERROR)
WHYF("MDP message is return/error: %d:%s",
mdp_reply.error.error,mdp_reply.error.message);
continue;
}
// we might receive a late response from an ealier request on the same socket, ignore it
if (cmp_sid_t(&mdp_reply.out.src.sid, &dstsid) != 0) {
WHYF("Unexpected result from SID %s", alloca_tohex_sid_t(mdp_reply.out.src.sid));
continue;
}
{
char sidhex[SID_STRLEN + 1];
char did[DID_MAXSIZE + 1];
char name[64];
char uri[512];
if ( !parseDnaReply((char *)mdp_reply.out.payload, mdp_reply.out.payload_length, sidhex, did, name, uri, NULL)
|| !str_is_subscriber_id(sidhex)
|| !str_is_did(did)
|| !str_is_uri(uri)
) {
WHYF("Received malformed DNA reply: %s",
alloca_toprint(160, (const char *)mdp_reply.out.payload, mdp_reply.out.payload_length));
continue;
}
/* Got a good DNA reply, copy it into place and stop polling */
cli_field_name(context, "sid", ":");
cli_put_string(context, sidhex, "\n");
cli_field_name(context, "did", ":");
cli_put_string(context, did, "\n");
cli_field_name(context, "name", ":");
cli_put_string(context, name, "\n");
overlay_mdp_client_close(mdp_sockfd);
return 0;
}
}
overlay_mdp_client_close(mdp_sockfd);
return 1;
}

View File

@ -23,7 +23,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "constants.h"
#include "os.h" // for time_ms_t
#include "socket.h"
#include "overlay_buffer.h" // for struct overlay_buffer
// not reachable
#define REACHABLE_NONE 0
@ -48,6 +47,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define BROADCAST_LEN 8
struct packet_rule;
struct overlay_buffer;
// This structure supports both our own routing protocol which can store calculation details in *node
// or IP4 addresses reachable via any other kind of normal layer3 routing protocol, eg olsr

View File

@ -55,6 +55,13 @@ static const char *_server_pidfile_path(struct __sourceloc __whence);
#define server_pidfile_path() (_server_pidfile_path(__WHENCE__))
void server_shutdown_check(struct sched_ent *alarm);
void cli_cleanup(){
/* clean up after ourselves */
rhizome_close_db();
free_subscribers();
assert(keyring==NULL);
}
/** Return the PID of the currently running server process, return 0 if there is none.
*/
int server_pid()

View File

@ -50,7 +50,6 @@ SERVAL_DAEMON_SOURCES = \
rhizome_cli.c \
keyring_cli.c \
network_cli.c \
test_cli.c \
crypto.c \
directory_client.c \
dna_helper.c \
@ -102,7 +101,13 @@ SERVAL_DAEMON_SOURCES = \
fec-3.0.1/ccsds_tables.c \
fec-3.0.1/decode_rs_8.c \
fec-3.0.1/encode_rs_8.c \
fec-3.0.1/init_rs_char.c \
fec-3.0.1/init_rs_char.c
TEST_SOURCES = \
commandline.c \
main.c \
test_cli.c \
log_stderr.c \
context1.c
MDP_CLIENT_SOURCES = \

View File

@ -17,12 +17,24 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/stat.h>
#include "serval_types.h"
#include "dataformats.h"
#include "os.h"
#include "cli.h"
#include "conf.h"
#include "commandline.h"
#include "mem.h"
void cli_cleanup(){}
void cf_on_config_change(){}
DEFINE_CMD(app_byteorder_test, 0,
"Run byte order handling test",
@ -260,3 +272,80 @@ static int app_mem_test(const struct cli_parsed *UNUSED(parsed), struct cli_cont
return 0;
}
DEFINE_CMD(app_config_test, 0,
"Load a test config file and log various fields",
"config","test","<file>");
static int app_config_test(const struct cli_parsed *UNUSED(parsed), struct cli_context *UNUSED(context))
{
const char *filename;
if (cli_arg(parsed, "file", &filename, NULL, NULL)==-1)
return -1;
int fd = open(filename, O_RDONLY);
if (fd == -1)
return WHY_perror("open");
struct stat st;
fstat(fd, &st);
char *buf = emalloc(st.st_size);
if (!buf)
return -1;
if (read(fd, buf, st.st_size) != st.st_size)
return WHY_perror("read");
struct cf_om_node *root = NULL;
int ret = cf_om_parse(filename, 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_om_free_node(&root);
free(buf);
DEBUGF("result = %s", strbuf_str(strbuf_cf_flags(strbuf_alloca(128), result)));
DEBUGF("config.log.file.path = %s", alloca_str_toprint(config.log.file.path));
DEBUGF("config.log.file.show_pid = %d", config.log.file.show_pid);
DEBUGF("config.log.file.show_time = %d", config.log.file.show_time);
DEBUGF("config.server.chdir = %s", alloca_str_toprint(config.server.chdir));
DEBUGF("config.debug.verbose = %d", config.debug.verbose);
DEBUGF("config.directory.service = %s", alloca_tohex_sid_t(config.directory.service));
DEBUGF("config.rhizome.api.addfile.allow_host = %s", inet_ntoa(config.rhizome.api.addfile.allow_host));
unsigned 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.%u", config.interfaces.av[j].key);
DEBUGF(" .exclude = %d", config.interfaces.av[j].value.exclude);
DEBUGF(" .match = [");
unsigned 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(" .drop_broadcasts = %d", (int) config.interfaces.av[j].value.drop_broadcasts);
DEBUGF(" .drop_unicasts = %d", (int) config.interfaces.av[j].value.drop_unicasts);
DEBUGF(" .drop_packets = %u", (unsigned) config.interfaces.av[j].value.drop_packets);
}
for (j = 0; j < config.hosts.ac; ++j) {
char sidhex[SID_STRLEN + 1];
tohex(sidhex, SID_STRLEN, config.hosts.av[j].key.binary);
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);
}
return 0;
}