Reuse command line parsing framework with monitor interface

This commit is contained in:
Jeremy Lakeman 2012-09-25 13:31:34 +09:30
parent 222cb80ce7
commit ac3864ff20
15 changed files with 538 additions and 665 deletions

View File

@ -12,6 +12,7 @@ SERVALD_SRC_FILES = \
serval-dna/overlay_mdp.c \ serval-dna/overlay_mdp.c \
serval-dna/batman.c \ serval-dna/batman.c \
serval-dna/ciphers.c \ serval-dna/ciphers.c \
serval-dna/cli.c \
serval-dna/client.c \ serval-dna/client.c \
serval-dna/commandline.c \ serval-dna/commandline.c \
serval-dna/conf.c \ serval-dna/conf.c \
@ -53,6 +54,7 @@ SERVALD_SRC_FILES = \
serval-dna/fdqueue.c \ serval-dna/fdqueue.c \
serval-dna/monitor.c \ serval-dna/monitor.c \
serval-dna/monitor-cli.c \ serval-dna/monitor-cli.c \
serval-dna/monitor-client.c \
serval-dna/codecs.c \ serval-dna/codecs.c \
serval-dna/audiodevices.c \ serval-dna/audiodevices.c \
serval-dna/audio_msm_g1.c \ serval-dna/audio_msm_g1.c \

View File

@ -5,6 +5,7 @@ SRCS= \
audio_reflector.c \ audio_reflector.c \
batman.c \ batman.c \
ciphers.c \ ciphers.c \
cli.c \
client.c \ client.c \
codecs.c \ codecs.c \
commandline.c \ commandline.c \
@ -24,6 +25,7 @@ SRCS= \
mdp_client.c \ mdp_client.c \
mkdir.c \ mkdir.c \
monitor.c \ monitor.c \
monitor-client.c \
monitor-cli.c \ monitor-cli.c \
net.c \ net.c \
overlay.c \ overlay.c \

164
cli.c Normal file
View File

@ -0,0 +1,164 @@
#include <stdio.h>
#include <strings.h>
#include "cli.h"
#include "log.h"
#include "serval.h"
#include "rhizome.h"
int cli_usage(struct command_line_option *options) {
printf("Usage:\n");
int i,j;
for(i=0;options[i].function;i++) {
for(j=0;options[i].words[j];j++)
printf(" %s",options[i].words[j]);
printf("\n %s\n",options[i].description);
}
return 0;
}
int cli_execute(const char *argv0, int argc, const char *const *args, struct command_line_option *options, void *context){
int ambiguous=0;
int cli_call=-1;
int i;
for(i=0;options[i].function;i++)
{
int j;
const char *word = NULL;
int optional = 0;
int mandatory = 0;
for (j = 0; (word = options[i].words[j]); ++j) {
int wordlen = strlen(word);
if (optional < 0) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" not allowed after \"...\"", i, j, word);
break;
}
else if (!( (wordlen > 2 && word[0] == '<' && word[wordlen-1] == '>')
|| (wordlen > 4 && word[0] == '[' && word[1] == '<' && word[wordlen-2] == '>' && word[wordlen-1] == ']')
|| (wordlen > 0)
)) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" is malformed", i, j, word);
break;
} else if (word[0] == '<') {
++mandatory;
if (optional) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" should be optional", i, j, word);
break;
}
} else if (word[0] == '[') {
++optional;
} else if (wordlen == 3 && word[0] == '.' && word[1] == '.' && word[2] == '.') {
optional = -1;
} else {
++mandatory;
if (j < argc && strcasecmp(word, args[j])) // literal words don't match
break;
}
}
if (!word && argc >= mandatory && (optional < 0 || argc <= mandatory + optional)) {
/* A match! We got through the command definition with no internal errors and all literal
args matched and we have a proper number of args. If we have multiple matches, then note
that the call is ambiguous. */
if (cli_call>=0) ambiguous++;
if (ambiguous==1) {
WHY("Ambiguous command line call:");
WHY_argv(" ", argc, args);
WHY("Matches the following known command line calls:");
WHY_argv(" ", argc, options[cli_call].words);
}
if (ambiguous)
WHY_argv(" ", argc, options[i].words);
cli_call=i;
}
}
/* Don't process ambiguous calls */
if (ambiguous) return -1;
/* Complain if we found no matching calls */
if (cli_call<0) {
if (argc) {
WHY("Unknown command line call:");
WHY_argv(" ", argc, args);
}
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);
}
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 arglen = strlen(argname);
int i;
const char *word;
for(i = 0; (word = o->words[i]); ++i) {
int wordlen = strlen(word);
/* No need to check that the "<...>" and "[<...>]" are all intact in the command_line_option,
because that was already checked in parseCommandLine(). */
if (i < argc
&&( (wordlen == arglen + 2 && word[0] == '<' && !strncasecmp(&word[1], argname, arglen))
|| (wordlen == arglen + 4 && word[0] == '[' && !strncasecmp(&word[2], argname, arglen)))
) {
const char *value = argv[i];
if (validator && !(*validator)(value))
return WHYF("Invalid argument %d '%s': \"%s\"", i + 1, argname, value);
*dst = value;
return 0;
}
}
/* No matching valid argument was found, so return default value. It might seem that this should
never happen, but it can because more than one version of a command line option may exist, one
with a given argument and another without, and allowing a default value means we can have a
single function handle both in a fairly simple manner. */
*dst = defaultvalue;
return 1;
}
int cli_lookup_did(const char *text)
{
return text[0] == '\0' || strcmp(text, "*") == 0 || str_is_did(text);
}
int cli_absolute_path(const char *arg)
{
return arg[0] == '/' && arg[1] != '\0';
}
int cli_optional_sid(const char *arg)
{
return !arg[0] || str_is_subscriber_id(arg);
}
int cli_optional_bundle_key(const char *arg)
{
return !arg[0] || rhizome_str_is_bundle_key(arg);
}
int cli_manifestid(const char *arg)
{
return rhizome_str_is_manifest_id(arg);
}
int cli_fileid(const char *arg)
{
return rhizome_str_is_file_hash(arg);
}
int cli_optional_bundle_crypt_key(const char *arg)
{
return !arg[0] || rhizome_str_is_bundle_crypt_key(arg);
}
int cli_uint(const char *arg)
{
register const char *s = arg;
while (isdigit(*s++))
;
return s != arg && *s == '\0';
}
int cli_optional_did(const char *text)
{
return text[0] == '\0' || str_is_did(text);
}

30
cli.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef __SERVALD_CLI_H
#define __SERVALD_CLI_H
typedef struct command_line_option {
int (*function)(int argc, const char *const *argv, 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 */
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_lookup_did(const char *text);
int cli_absolute_path(const char *arg);
int cli_optional_sid(const char *arg);
int cli_optional_bundle_key(const char *arg);
int cli_manifestid(const char *arg);
int cli_fileid(const char *arg);
int cli_optional_bundle_crypt_key(const char *arg);
int cli_uint(const char *arg);
int cli_optional_did(const char *text);
#endif

View File

@ -38,17 +38,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "rhizome.h" #include "rhizome.h"
#include "strbuf.h" #include "strbuf.h"
#include "mdp_client.h" #include "mdp_client.h"
#include "cli.h"
int cli_usage() { extern struct command_line_option command_line_options[];
int commandline_usage(int argc, const char *const *argv, struct command_line_option *o, void *context){
printf("Serval Mesh version <version>.\n"); printf("Serval Mesh version <version>.\n");
printf("Usage:\n"); return cli_usage(command_line_options);
int i,j;
for(i=0;command_line_options[i].function;i++) {
for(j=0;command_line_options[i].words[j];j++)
printf(" %s",command_line_options[i].words[j]);
printf("\n %s\n",command_line_options[i].description);
}
return 0;
} }
/* Data structures for accumulating output of a single JNI call. /* Data structures for accumulating output of a single JNI call.
@ -185,77 +181,11 @@ JNIEXPORT jint JNICALL Java_org_servalproject_servald_ServalD_rawCommand(JNIEnv
*/ */
int parseCommandLine(const char *argv0, int argc, const char *const *args) int parseCommandLine(const char *argv0, int argc, const char *const *args)
{ {
int ambiguous=0;
int cli_call=-1;
fd_clearstats(); fd_clearstats();
IN(); IN();
int i;
for(i=0;command_line_options[i].function;i++)
{
int j;
const char *word = NULL;
int optional = 0;
int mandatory = 0;
for (j = 0; (word = command_line_options[i].words[j]); ++j) {
int wordlen = strlen(word);
if (optional < 0) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" not allowed after \"...\"", i, j, word);
break;
}
else if (!( (wordlen > 2 && word[0] == '<' && word[wordlen-1] == '>')
|| (wordlen > 4 && word[0] == '[' && word[1] == '<' && word[wordlen-2] == '>' && word[wordlen-1] == ']')
|| (wordlen > 0)
)) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" is malformed", i, j, word);
break;
} else if (word[0] == '<') {
++mandatory;
if (optional) {
WHYF("Internal error: command_line_options[%d].word[%d]=\"%s\" should be optional", i, j, word);
break;
}
} else if (word[0] == '[') {
++optional;
} else if (wordlen == 3 && word[0] == '.' && word[1] == '.' && word[2] == '.') {
optional = -1;
} else {
++mandatory;
if (j < argc && strcasecmp(word, args[j])) // literal words don't match
break;
}
}
if (!word && argc >= mandatory && (optional < 0 || argc <= mandatory + optional)) {
/* A match! We got through the command definition with no internal errors and all literal
args matched and we have a proper number of args. If we have multiple matches, then note
that the call is ambiguous. */
if (cli_call>=0) ambiguous++;
if (ambiguous==1) {
WHY("Ambiguous command line call:");
WHY_argv(" ", argc, args);
WHY("Matches the following known command line calls:");
WHY_argv(" ", argc, command_line_options[cli_call].words);
}
if (ambiguous)
WHY_argv(" ", argc, command_line_options[i].words);
cli_call=i;
}
}
/* Don't process ambiguous calls */
if (ambiguous) return -1;
/* Complain if we found no matching calls */
if (cli_call<0) {
if (argc) {
WHY("Unknown command line call:");
WHY_argv(" ", argc, args);
}
INFO("Use \"help\" command to see a list of valid commands");
return -1;
}
/* Otherwise, make call */
confSetDebugFlags(); confSetDebugFlags();
int result=command_line_options[cli_call].function(argc, args, &command_line_options[cli_call]);
int result = cli_execute(argv0, argc, args, command_line_options, NULL);
/* clean up after ourselves */ /* clean up after ourselves */
overlay_mdp_client_done(); overlay_mdp_client_done();
OUT(); OUT();
@ -265,34 +195,6 @@ int parseCommandLine(const char *argv0, int argc, const char *const *args)
return result; return result;
} }
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 arglen = strlen(argname);
int i;
const char *word;
for(i = 0; (word = o->words[i]); ++i) {
int wordlen = strlen(word);
/* No need to check that the "<...>" and "[<...>]" are all intact in the command_line_option,
because that was already checked in parseCommandLine(). */
if (i < argc
&&( (wordlen == arglen + 2 && word[0] == '<' && !strncasecmp(&word[1], argname, arglen))
|| (wordlen == arglen + 4 && word[0] == '[' && !strncasecmp(&word[2], argname, arglen)))
) {
const char *value = argv[i];
if (validator && !(*validator)(value))
return WHYF("Invalid argument %d '%s': \"%s\"", i + 1, argname, value);
*dst = value;
return 0;
}
}
/* No matching valid argument was found, so return default value. It might seem that this should
never happen, but it can because more than one version of a command line option may exist, one
with a given argument and another without, and allowing a default value means we can have a
single function handle both in a fairly simple manner. */
*dst = defaultvalue;
return 1;
}
/* Write a single character to output. If in a JNI call, then this appends the character to the /* Write a single character to output. If in a JNI call, then this appends the character to the
current output field. Returns the character written cast to an unsigned char then to int, or EOF current output field. Returns the character written cast to an unsigned char then to int, or EOF
on error. on error.
@ -392,7 +294,7 @@ int cli_delim(const char *opt)
return 0; return 0;
} }
int app_echo(int argc, const char *const *argv, struct command_line_option *o) int app_echo(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int i; int i;
@ -405,12 +307,7 @@ int app_echo(int argc, const char *const *argv, struct command_line_option *o)
return 0; return 0;
} }
int cli_lookup_did(const char *text) int app_dna_lookup(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return text[0] == '\0' || strcmp(text, "*") == 0 || str_is_did(text);
}
int app_dna_lookup(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int i; int i;
@ -555,12 +452,7 @@ int app_dna_lookup(int argc, const char *const *argv, struct command_line_option
return 0; return 0;
} }
int cli_absolute_path(const char *arg) int app_server_start(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return arg[0] == '/' && arg[1] != '\0';
}
int app_server_start(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* Process optional arguments */ /* Process optional arguments */
@ -711,7 +603,7 @@ int app_server_start(int argc, const char *const *argv, struct command_line_opti
return ret; return ret;
} }
int app_server_stop(int argc, const char *const *argv, struct command_line_option *o) int app_server_stop(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int pid, tries, running; int pid, tries, running;
@ -778,7 +670,7 @@ int app_server_stop(int argc, const char *const *argv, struct command_line_optio
return 0; return 0;
} }
int app_server_status(int argc, const char *const *argv, struct command_line_option *o) int app_server_status(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
int pid; int pid;
@ -810,7 +702,7 @@ int app_server_status(int argc, const char *const *argv, struct command_line_opt
return pid > 0 ? 0 : 1; return pid > 0 ? 0 : 1;
} }
int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *o) int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid, *count; const char *sid, *count;
@ -952,7 +844,7 @@ int app_mdp_ping(int argc, const char *const *argv, struct command_line_option *
return ret; return ret;
} }
int app_config_set(int argc, const char *const *argv, struct command_line_option *o) int app_config_set(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var, *val; const char *var, *val;
@ -964,7 +856,7 @@ int app_config_set(int argc, const char *const *argv, struct command_line_option
return confValueSet(var, val) == -1 ? -1 : confWrite(); return confValueSet(var, val) == -1 ? -1 : confWrite();
} }
int app_config_del(int argc, const char *const *argv, struct command_line_option *o) 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); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var; const char *var;
@ -975,7 +867,7 @@ int app_config_del(int argc, const char *const *argv, struct command_line_option
return confValueSet(var, NULL) == -1 ? -1 : confWrite(); return confValueSet(var, NULL) == -1 ? -1 : confWrite();
} }
int app_config_get(int argc, const char *const *argv, struct command_line_option *o) int app_config_get(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *var; const char *var;
@ -1006,7 +898,7 @@ int app_config_get(int argc, const char *const *argv, struct command_line_option
return 0; return 0;
} }
int app_rhizome_hash_file(int argc, const char *const *argv, struct command_line_option *o) int app_rhizome_hash_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* compute hash of file. We do this without a manifest, so it will necessarily /* compute hash of file. We do this without a manifest, so it will necessarily
@ -1021,17 +913,7 @@ int app_rhizome_hash_file(int argc, const char *const *argv, struct command_line
return 0; return 0;
} }
int cli_optional_sid(const char *arg) int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return !arg[0] || str_is_subscriber_id(arg);
}
int cli_optional_bundle_key(const char *arg)
{
return !arg[0] || rhizome_str_is_bundle_key(arg);
}
int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *filepath, *manifestpath, *authorSidHex, *pin, *bskhex; const char *filepath, *manifestpath, *authorSidHex, *pin, *bskhex;
@ -1215,7 +1097,7 @@ int app_rhizome_add_file(int argc, const char *const *argv, struct command_line_
return ret; return ret;
} }
int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_line_option *o) int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *filepath, *manifestpath; const char *filepath, *manifestpath;
@ -1286,12 +1168,7 @@ int app_rhizome_import_bundle(int argc, const char *const *argv, struct command_
return status; return status;
} }
int cli_manifestid(const char *arg) int app_rhizome_extract_manifest(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return rhizome_str_is_manifest_id(arg);
}
int app_rhizome_extract_manifest(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *manifestid, *manifestpath; const char *manifestid, *manifestpath;
@ -1327,17 +1204,7 @@ int app_rhizome_extract_manifest(int argc, const char *const *argv, struct comma
return ret; return ret;
} }
int cli_fileid(const char *arg) int app_rhizome_extract_file(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return rhizome_str_is_file_hash(arg);
}
int cli_optional_bundle_crypt_key(const char *arg)
{
return !arg[0] || rhizome_str_is_bundle_crypt_key(arg);
}
int app_rhizome_extract_file(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *fileid, *filepath, *keyhex; const char *fileid, *filepath, *keyhex;
@ -1367,15 +1234,7 @@ int app_rhizome_extract_file(int argc, const char *const *argv, struct command_l
return ret; return ret;
} }
int cli_uint(const char *arg) int app_rhizome_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
register const char *s = arg;
while (isdigit(*s++))
;
return s != arg && *s == '\0';
}
int app_rhizome_list(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin, *service, *sender_sid, *recipient_sid, *offset, *limit; const char *pin, *service, *sender_sid, *recipient_sid, *offset, *limit;
@ -1395,7 +1254,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)); 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) int app_keyring_create(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin; const char *pin;
@ -1405,7 +1264,7 @@ int app_keyring_create(int argc, const char *const *argv, struct command_line_op
return 0; return 0;
} }
int app_keyring_list(int argc, const char *const *argv, struct command_line_option *o) int app_keyring_list(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin; const char *pin;
@ -1432,7 +1291,7 @@ int app_keyring_list(int argc, const char *const *argv, struct command_line_opti
return 0; return 0;
} }
int app_keyring_add(int argc, const char *const *argv, struct command_line_option *o) int app_keyring_add(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *pin; const char *pin;
@ -1477,12 +1336,7 @@ int app_keyring_add(int argc, const char *const *argv, struct command_line_optio
return 0; return 0;
} }
int cli_optional_did(const char *text) int app_keyring_set_did(int argc, const char *const *argv, struct command_line_option *o, void *context)
{
return text[0] == '\0' || str_is_did(text);
}
int app_keyring_set_did(int argc, const char *const *argv, struct command_line_option *o)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid, *did, *pin, *name; const char *sid, *did, *pin, *name;
@ -1511,7 +1365,7 @@ int app_keyring_set_did(int argc, const char *const *argv, struct command_line_o
return 0; return 0;
} }
int app_id_self(int argc, const char *const *argv, struct command_line_option *o) int app_id_self(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
/* List my own identities */ /* List my own identities */
@ -1558,7 +1412,7 @@ int app_id_self(int argc, const char *const *argv, struct command_line_option *o
return 0; return 0;
} }
int app_test_rfs(int argc, const char *const *argv, struct command_line_option *o) int app_test_rfs(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
printf("Testing that RFS coder works properly.\n"); printf("Testing that RFS coder works properly.\n");
@ -1574,7 +1428,7 @@ int app_test_rfs(int argc, const char *const *argv, struct command_line_option *
return 0; return 0;
} }
int app_crypt_test(int argc, const char *const *argv, struct command_line_option *o) int app_crypt_test(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
unsigned char nonce[crypto_box_curve25519xsalsa20poly1305_NONCEBYTES]; unsigned char nonce[crypto_box_curve25519xsalsa20poly1305_NONCEBYTES];
@ -1601,7 +1455,7 @@ int app_crypt_test(int argc, const char *const *argv, struct command_line_option
return 0; return 0;
} }
int app_node_info(int argc, const char *const *argv, struct command_line_option *o) int app_node_info(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv); if (debug & DEBUG_VERBOSE) DEBUG_argv("command", argc, argv);
const char *sid; const char *sid;
@ -1764,10 +1618,10 @@ int app_node_info(int argc, const char *const *argv, struct command_line_option
Keep this list alphabetically sorted for user convenience. Keep this list alphabetically sorted for user convenience.
*/ */
command_line_option command_line_options[]={ struct command_line_option command_line_options[]={
{app_dna_lookup,{"dna","lookup","<did>","[<timeout>]",NULL},0, {app_dna_lookup,{"dna","lookup","<did>","[<timeout>]",NULL},0,
"Lookup the SIP/MDP address of the supplied telephone number (DID)."}, "Lookup the SIP/MDP address of the supplied telephone number (DID)."},
{cli_usage,{"help",NULL},0, {commandline_usage,{"help",NULL},0,
"Display command usage."}, "Display command usage."},
{app_echo,{"echo","...",NULL},CLIFLAG_STANDALONE, {app_echo,{"echo","...",NULL},CLIFLAG_STANDALONE,
"Output the supplied string."}, "Output the supplied string."},
@ -1827,8 +1681,8 @@ command_line_option command_line_options[]={
"Return information about SID, and optionally ask for DID resolution via network"}, "Return information about SID, and optionally ask for DID resolution via network"},
{app_test_rfs,{"test","rfs",NULL},0, {app_test_rfs,{"test","rfs",NULL},0,
"Test RFS field calculation"}, "Test RFS field calculation"},
{app_monitor_cli,{"monitor","[<sid>]",NULL},0, {app_monitor_cli,{"monitor",NULL},0,
"Interactive servald monitor interface. Specify SID to auto-dial that peer and insert dummy audio data"}, "Interactive servald monitor interface."},
{app_crypt_test,{"crypt","test",NULL},0, {app_crypt_test,{"crypt","test",NULL},0,
"Run cryptography speed test"}, "Run cryptography speed test"},
#ifdef HAVE_VOIPTEST #ifdef HAVE_VOIPTEST

View File

@ -25,282 +25,68 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <fcntl.h> #include <fcntl.h>
#include "serval.h" #include "serval.h"
#include "cli.h"
#include "monitor-client.h"
static char cmd[1024]; int remote_print(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
static int cmdLen=0;
static int cmdOfs=0;
static int dataBytesExpected=0;
static unsigned char data[65536];
static int dataBytes=0;
#define STATE_CMD 1
#define STATE_DATA 2
static int state=STATE_CMD;
static int fd;
static int processChar(int c);
static int autoAnswerP=1;
static int pipeAudio=1;
static int reflectAudio=0;
static int syntheticAudio=0;
static int showReceived=1;
static int interactiveP=1;
static int recordCodec=VOMP_CODEC_PCM;
static int recordCodecTimespan=20;
static int callSessionToken=0;
static int fast_audio=0;
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o)
{
const char *sid=NULL;
cli_arg(argc, argv, o, "sid", &sid, NULL, "");
struct sockaddr_un addr;
if (!strcasecmp(sid,"reflect")) {
pipeAudio=1; reflectAudio=1;
sid="";
}
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
addr.sun_path[0]=0;
snprintf(&addr.sun_path[1],100,"%s",
confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME));
int len = 1+strlen(&addr.sun_path[1]) + sizeof(addr.sun_family);
char *p=(char *)&addr;
printf("last char='%c' %02x\n",p[len-1],p[len-1]);
if (connect(fd, (struct sockaddr*)&addr, len) == -1) {
perror("connect");
exit(-1);
}
if (pipeAudio) {
if (reflectAudio)
audev=audio_reflector_detect();
else
detectAudioDevice();
char *name=audev?audev->name:NULL;
if (!name) {
WHY("Could not detect any audio device. Will not pipe audio.");
pipeAudio=0;
}
}
struct pollfd fds[128];
int fdcount=0;
fds[fdcount].fd=fd;
fds[fdcount].events=POLLIN;
fdcount++;
if (interactiveP) {
fds[fdcount].fd=STDIN_FILENO;
fds[fdcount].events=POLLIN;
fdcount++;
}
write_str(fd, "monitor vomp\n");
write_str(fd, "monitor rhizome\n");
if (sid!=NULL&&sid[0]) {
char msg[1024];
snprintf(msg,1024,"call %s 5551 5552\n",argv[1]);
write_str(fd, msg);
}
char line[1024];
/* Allow for up to one second of audio read from the microphone
to be buffered. This is probably more than we will ever need.
The primary purpose of the buffer is in fact to handle the fact
that we are unlikely to ever read exaclty the number of samples
we need, so we need to keep any left over ones from the previous
read. */
int audioRecordBufferBytes=0;
int audioRecordBufferSize=8000*2;
unsigned char audioRecordBuffer[8000*2];
int base_fd_count=fdcount;
while(1) {
fdcount=base_fd_count;
if (audev&&audev->poll_fds) fdcount+=audev->poll_fds(&fds[fdcount],128-fdcount);
poll(fds,fdcount,1000);
set_nonblock(fd);
if (interactiveP)
set_nonblock(STDIN_FILENO);
int bytes;
int i; int i;
line[0]=0; printf("%s",cmd);
bytes=read(fd,line,1024); for (i=0;i<argc;i++){
if (bytes>0) printf(" %s",argv[i]);
for(i=0;i<bytes;i++) processChar(line[i]);
if (interactiveP) {
bytes=read(STDIN_FILENO,line,1024);
if (bytes>0) {
line[bytes]=0;
printf("< %s",line);
write(fd,line,bytes);
}
}
if (audev&&audev->read)
{
WHY("about to read");
int bytesRead=audev->read(&audioRecordBuffer[audioRecordBufferBytes],
audioRecordBufferSize-audioRecordBufferBytes);
WHY("read");
if (bytesRead>0) audioRecordBufferBytes+=bytesRead;
/* 8KHz 16 bit samples = 16000 bytes per second.
Thus one 1ms of audio = 16 bytes. */
int audioRecordBufferOffset=0;
while ((audioRecordBufferBytes-audioRecordBufferOffset)
>recordCodecTimespan*16) {
/* encode and deliver audio block to servald via monitor interface */
encodeAndDispatchRecordedAudio(fd,callSessionToken,recordCodec,
&audioRecordBuffer[audioRecordBufferOffset],
recordCodecTimespan*16);
WHY("sample block sent");
/* skip over the samples we have already processed */
audioRecordBufferOffset+=recordCodecTimespan*16;
}
/* copy the remaining buffered bytes down and correct buffer length */
if (audioRecordBufferOffset<0) audioRecordBufferOffset=0;
if (audioRecordBufferOffset>audioRecordBufferBytes)
audioRecordBufferOffset=audioRecordBufferBytes;
bcopy(&audioRecordBuffer[audioRecordBufferOffset],
&audioRecordBuffer[0],
audioRecordBufferBytes-audioRecordBufferOffset);
audioRecordBufferBytes-=audioRecordBufferOffset;
}
set_block(fd);
if (interactiveP)
set_block(STDIN_FILENO);
}
return 0;
}
int counter=0;
int callState=0;
int processLine(char *cmd,unsigned char *data,int dataLen)
{
int l_id,r_id,l_state,r_state,codec;
time_ms_t start_time, end_time;
if (showReceived) {
printf("> %s\n",cmd);
if (data) {
int i,j;
for(i=0;i<dataLen;i+=16) {
printf(" %04x :",i);
for(j=0;j<16;j++)
if (i+j<dataLen) printf(" %02x",data[i+j]); else printf(" ");
printf(" ");
for(j=0;j<16;j++)
if (i+j<dataLen) {
if (data[i+j]>=0x20&&data[i+j]<0x7e)
printf("%c",data[i+j]); else printf(".");
} }
printf("\n"); printf("\n");
if (dataLen){
dump(NULL,data,dataLen);
} }
return 1;
} }
}
if (sscanf(cmd,"AUDIOPACKET:%x:%x:%d:%d:%d:%lld:%lld", struct monitor_command_handler monitor_handlers[]={
&l_id,&r_id,&l_state,&r_state, &codec, &start_time, &end_time)==7) {.command="", .handler=remote_print},
};
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o, void *context)
{ {
if (pipeAudio&&audev&&fast_audio) { struct pollfd fds[2];
bufferAudioForPlayback(codec, start_time, end_time, data, dataLen); struct monitor_state *state;
int monitor_client_fd = monitor_client_open(&state);
set_nonblock(STDIN_FILENO);
set_nonblock(monitor_client_fd);
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = monitor_client_fd;
fds[1].events = POLLIN;
while(1){
int r = poll(fds, 2, 100);
if (r>0){
if (fds[0].revents & POLLIN){
char buff[256];
int bytes = read(STDIN_FILENO, buff, sizeof(buff));
set_block(monitor_client_fd);
write(monitor_client_fd, buff, bytes);
set_nonblock(monitor_client_fd);
}
if (fds[1].revents & POLLIN){
if (monitor_client_read(monitor_client_fd, state, monitor_handlers,
sizeof(monitor_handlers)/sizeof(struct monitor_command_handler))<0){
break;
} }
} }
char msg[1024];
if (sscanf(cmd,"CALLSTATUS:%x:%x:%d:%d:%d", if (fds[0].revents & (POLLHUP | POLLERR))
&l_id,&r_id,&l_state,&r_state,&fast_audio)==5) break;
{
if (l_state<5&&l_id&&pipeAudio) {
// Take control of audio for this call, and let the java side know
snprintf(msg,1024,"FASTAUDIO:%x:1\n",l_id);
write_str(fd, msg);
}
if (l_state==4&&autoAnswerP) {
// We are ringing, so pickup
sprintf(msg,"pickup %x\n",l_id);
write_str(fd, msg);
}
if (l_state==5) {
if (fast_audio) {
startAudio();
}
callSessionToken=l_id;
} else {
stopAudio();
callSessionToken=0;
}
callState=l_state;
}
if (sscanf(cmd,"KEEPALIVE:%x",&l_id)==1) {
if (callState==5&&syntheticAudio) {
/* Send synthetic audio packet */
char buffer[1024];
sprintf(buffer,"*320:AUDIO:%x:8\n"
"%08d pasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456"
"qwertyuiopasdfghjklzxcvbnm123456",l_id,counter++);
write_str(fd, buffer);
printf("< *320:AUDIO:%x:8\\n<320 bytes>\n",l_id);
} }
} }
cmd[0]=0;
cmdLen=0; monitor_client_close(monitor_client_fd, state);
dataBytes=0; monitor_client_fd=-1;
dataBytesExpected=0;
state=STATE_CMD;
return 0; return 0;
} }
int processChar(int c)
{
switch(state) {
case STATE_CMD:
if (c!='\n') {
if (cmdLen<1000) {
cmd[cmdLen++]=c;
}
} else {
if (!cmdLen) return 0;
cmd[cmdLen]=0;
if (sscanf(cmd,"*%d:%n",&dataBytesExpected,&cmdOfs)==1) {
if (dataBytesExpected<0) dataBytesExpected=0;
if (dataBytesExpected>65535) dataBytesExpected=65535;
state=STATE_DATA;
} else {
processLine(cmd,NULL,0);
cmdLen=0;
}
}
break;
case STATE_DATA:
if (dataBytes<dataBytesExpected)
data[dataBytes++]=c;
if (dataBytes>=dataBytesExpected) {
processLine(&cmd[cmdOfs],data,dataBytes);
cmdLen=0;
}
}
return 0;
}

View File

@ -65,7 +65,6 @@ struct monitor_state {
int bufferBytes; int bufferBytes;
}; };
// FIX ME, COPY-PASTA from monitor.c
int monitor_socket_name(struct sockaddr_un *name){ int monitor_socket_name(struct sockaddr_un *name){
int len; int len;
#ifdef linux #ifdef linux

View File

@ -32,5 +32,6 @@ int monitor_client_writeline(int fd,char *fmt, ...);
int monitor_client_writeline_and_data(int fd,unsigned char *data,int bytes,char *fmt,...); int monitor_client_writeline_and_data(int fd,unsigned char *data,int bytes,char *fmt,...);
int monitor_client_read(int fd, struct monitor_state *res, struct monitor_command_handler *handlers, int handler_count); int monitor_client_read(int fd, struct monitor_state *res, struct monitor_command_handler *handlers, int handler_count);
int monitor_client_close(int fd, struct monitor_state *res); int monitor_client_close(int fd, struct monitor_state *res);
int monitor_socket_name(struct sockaddr_un *name);
#endif #endif

373
monitor.c
View File

@ -23,9 +23,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
data structures (except for a binary extent for an audio sample block). data structures (except for a binary extent for an audio sample block).
*/ */
#include <sys/stat.h>
#include "serval.h" #include "serval.h"
#include "rhizome.h" #include "rhizome.h"
#include <sys/stat.h> #include "cli.h"
#include "str.h"
#include "overlay_address.h"
#include "monitor-client.h"
#if defined(LOCAL_PEERCRED) && !defined(SO_PEERCRED) #if defined(LOCAL_PEERCRED) && !defined(SO_PEERCRED)
#define SO_PEERCRED LOCAL_PEERCRED #define SO_PEERCRED LOCAL_PEERCRED
@ -44,8 +48,6 @@ struct monitor_context {
unsigned char buffer[MONITOR_DATA_SIZE]; unsigned char buffer[MONITOR_DATA_SIZE];
int data_expected; int data_expected;
int data_offset; int data_offset;
int sample_codec;
int sample_call_session_token;
}; };
#define MAX_MONITOR_SOCKETS 8 #define MAX_MONITOR_SOCKETS 8
@ -60,28 +62,6 @@ struct sched_ent named_socket;
struct profile_total named_stats; struct profile_total named_stats;
struct profile_total client_stats; struct profile_total client_stats;
int monitor_socket_name(struct sockaddr_un *name){
int len;
#ifdef linux
/* Use abstract namespace as Android has no writable FS which supports sockets.
Abstract namespace is just plain better, anyway, as no dead files end up
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,
confValueGet("monitor.socket",DEFAULT_MONITOR_SOCKET_NAME));
/* 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));
/* Includes trailing nul */
len = 1+strlen(name->sun_path) + sizeof(name->sun_family);
#endif
return len;
}
int monitor_setup_sockets() int monitor_setup_sockets()
{ {
struct sockaddr_un name; struct sockaddr_un name;
@ -136,6 +116,13 @@ int monitor_setup_sockets()
return -1; return -1;
} }
int monitor_write_error(struct monitor_context *c, const char *error){
char msg[256];
snprintf(msg, sizeof(msg), "\nERROR:%s\n", error);
write_str(c->alarm.poll.fd, msg);
return -1;
}
void monitor_poll(struct sched_ent *alarm) void monitor_poll(struct sched_ent *alarm)
{ {
int s; int s;
@ -165,7 +152,7 @@ void monitor_poll(struct sched_ent *alarm)
} }
} }
void monitor_client_close(struct monitor_context *c){ static void monitor_close(struct monitor_context *c){
struct monitor_context *last; struct monitor_context *last;
INFO("Tearing down monitor client"); INFO("Tearing down monitor client");
@ -197,11 +184,10 @@ void monitor_client_poll(struct sched_ent *alarm)
bytes = 1; bytes = 1;
while(bytes == 1) { while(bytes == 1) {
if (c->line_length >= MONITOR_LINE_LENGTH) { if (c->line_length >= MONITOR_LINE_LENGTH) {
/* line too long */ c->line_length=0;
c->line[MONITOR_LINE_LENGTH-1] = 0; monitor_write_error(c,"Command too long");
monitor_process_command(c); monitor_close(c);
bytes = -1; return;
break;
} }
bytes = read(c->alarm.poll.fd, &c->line[c->line_length], 1); bytes = read(c->alarm.poll.fd, &c->line[c->line_length], 1);
if (bytes < 1) { if (bytes < 1) {
@ -216,22 +202,42 @@ void monitor_client_poll(struct sched_ent *alarm)
default: default:
WHY_perror("read"); WHY_perror("read");
/* all other errors; close socket */ /* all other errors; close socket */
monitor_client_close(c); monitor_close(c);
return; return;
} }
} }
if (bytes > 0 && (c->line[c->line_length] != '\r')) {
// silently skip all \r characters
if (c->line[c->line_length] == '\r')
continue;
// parse data length as soon as we see the : delimiter,
// so we can read the rest of the line into the start of the buffer
if (c->data_expected==0 && c->line[0]=='*' && c->line[c->line_length]==':'){
c->line[c->line_length]=0;
c->data_expected=atoi(c->line +1);
c->line_length=0;
continue;
}
if (c->line[c->line_length] == '\n') {
/* got whole command line, start reading data if required */
c->line[c->line_length]=0;
c->state=MONITOR_STATE_DATA;
c->data_offset=0;
break;
}
c->line_length += bytes; c->line_length += bytes;
if (c->line[c->line_length-1] == '\n') { }
/* got command */
c->line[c->line_length-1] = 0; /* trim new line for easier parsing */ if (c->state!=MONITOR_STATE_DATA)
monitor_process_command(c);
break;
}
}
}
break; break;
// else fall through
case MONITOR_STATE_DATA: case MONITOR_STATE_DATA:
if (c->data_expected - c->data_offset >0){
bytes = read(c->alarm.poll.fd, bytes = read(c->alarm.poll.fd,
&c->buffer[c->data_offset], &c->buffer[c->data_offset],
c->data_expected - c->data_offset); c->data_expected - c->data_offset);
@ -244,26 +250,32 @@ void monitor_client_poll(struct sched_ent *alarm)
/* all other errors; close socket */ /* all other errors; close socket */
WHYF("Tearing down monitor client due to errno=%d", WHYF("Tearing down monitor client due to errno=%d",
errno); errno);
monitor_client_close(c); monitor_close(c);
return; return;
} }
} else { }
c->data_offset += bytes; c->data_offset += bytes;
if (c->data_offset >= c->data_expected)
{
/* we have the binary data we were expecting. */
monitor_process_data(c);
c->state = MONITOR_STATE_COMMAND;
}
} }
if (c->data_offset < c->data_expected)
break; break;
/* we have the next command and all of the binary data we were expecting. Now we can process it */
monitor_process_command(c);
// fall through
default: default:
// reset parsing state
c->state = MONITOR_STATE_COMMAND; c->state = MONITOR_STATE_COMMAND;
WHY("fixed monitor connection state"); c->data_expected = 0;
c->data_offset = 0;
c->line_length = 0;
} }
} }
if (alarm->poll.revents & (POLLHUP | POLLERR)) { if (alarm->poll.revents & (POLLHUP | POLLERR)) {
monitor_client_close(c); monitor_close(c);
} }
return; return;
} }
@ -334,118 +346,131 @@ static void monitor_new_client(int s) {
return; return;
} }
int monitor_send_lookup_response(const char *sid, const int port, const char *ext, const char *name){ static int monitor_set(int argc, const char *const *argv, struct command_line_option *o, void *context){
struct sockaddr_mdp addr={ struct monitor_context *c=context;
.port = port if (strcase_startswith((char *)argv[1],"vomp",NULL))
}; c->flags|=MONITOR_VOMP;
else if (strcase_startswith((char *)argv[1],"rhizome", NULL))
c->flags|=MONITOR_RHIZOME;
else if (strcase_startswith((char *)argv[1],"peers", NULL))
c->flags|=MONITOR_PEERS;
else if (strcase_startswith((char *)argv[1],"dnahelper", NULL))
c->flags|=MONITOR_DNAHELPER;
else
return monitor_write_error(c,"Unknown monitor type");
if (stowSid((unsigned char *)&addr.sid, 0, sid)==-1) char msg[1024];
return WHYF("Invalid SID %s", sid); snprintf(msg,sizeof(msg),"\nMONITORSTATUS:%d\n",c->flags);
write_str(c->alarm.poll.fd,msg);
int cn=0, in=0, kp=0;
if (!keyring_next_identity(keyring, &cn, &in, &kp))
WHY("No local identity, cannot send DNA LOOKUP reply");
else{
char uri[256];
snprintf(uri, sizeof(uri), "sid://%s/external/%s", alloca_tohex_sid(keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key), ext);
DEBUGF("Sending response to %s for %s", sid, uri);
overlay_mdp_dnalookup_reply(&addr, keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key, uri, ext, name);
}
return 0; return 0;
} }
int monitor_process_command(struct monitor_context *c) static int monitor_clear(int argc, const char *const *argv, struct command_line_option *o, void *context){
{ struct monitor_context *c=context;
int callSessionToken,sampleType,bytes; if (strcase_startswith((char *)argv[1],"vomp",NULL))
char sid[MONITOR_LINE_LENGTH],localDid[MONITOR_LINE_LENGTH]; c->flags&=~MONITOR_VOMP;
char remoteDid[MONITOR_LINE_LENGTH],digits[MONITOR_LINE_LENGTH]; else if (strcase_startswith((char *)argv[1],"rhizome", NULL))
int port; c->flags&=~MONITOR_RHIZOME;
else if (strcase_startswith((char *)argv[1],"peers", NULL))
char *cmd = c->line; c->flags&=~MONITOR_PEERS;
IN(); else if (strcase_startswith((char *)argv[1],"dnahelper", NULL))
c->flags&=~MONITOR_DNAHELPER;
remoteDid[0]='\0'; else
c->line_length=0; return monitor_write_error(c,"Unknown monitor type");
if (strlen(cmd)>MONITOR_LINE_LENGTH) {
write_str(c->alarm.poll.fd,"\nERROR:Command too long\n");
RETURN(-1);
}
char msg[1024]; char msg[1024];
snprintf(msg,sizeof(msg),"\nMONITORSTATUS:%d\n",c->flags);
write_str(c->alarm.poll.fd,msg);
if (cmd[0]=='*') { return 0;
/* command with content */ }
int ofs=0;
if (sscanf(cmd,"*%d:%n",&bytes,&ofs)==1) {
/* work out rest of command */
cmd=&cmd[ofs];
c->state=MONITOR_STATE_DATA;
c->data_expected=bytes;
c->data_offset=0;
c->sample_codec=-1;
if (sscanf(cmd,"AUDIO %x %d", static int monitor_lookup_match(int argc, const char *const *argv, struct command_line_option *o, void *context){
&callSessionToken,&sampleType)==2) struct monitor_context *c=context;
{ const char *sid=argv[2];
/* Start getting sample */ const char *ext=argv[4];
c->sample_call_session_token=callSessionToken; const char *name=argv[5];
c->sample_codec=sampleType;
RETURN(0); if (!my_subscriber)
return monitor_write_error(c,"I don't know who I am");
struct sockaddr_mdp addr={
.port = atoi(argv[3]),
};
if (stowSid((unsigned char *)&addr.sid, 0, sid)==-1)
return monitor_write_error(c,"Invalid SID");
char uri[256];
snprintf(uri, sizeof(uri), "sid://%s/external/%s", alloca_tohex_sid(my_subscriber->sid), ext);
DEBUGF("Sending response to %s for %s", sid, uri);
overlay_mdp_dnalookup_reply(&addr, my_subscriber->sid, uri, ext, name);
return 0;
} }
static int monitor_call(int argc, const char *const *argv, struct command_line_option *o, void *context){
struct monitor_context *c=context;
unsigned char sid[SID_SIZE];
if (stowSid(sid, 0, argv[1]) == -1)
return monitor_write_error(c,"invalid SID, so cannot place call");
if (!my_subscriber)
return monitor_write_error(c,"I don't know who I am");
vomp_dial(my_subscriber->sid, sid, argv[2], argv[3]);
return 0;
} }
}
else if (strcase_startswith(cmd,"monitor vomp",NULL)) static int monitor_call_ring(int argc, const char *const *argv, struct command_line_option *o, void *context){
// TODO add supported codec list argument struct monitor_context *c=context;
c->flags|=MONITOR_VOMP; struct vomp_call_state *call=vomp_find_call_by_session(strtol(argv[1],NULL,16));
else if (strcase_startswith(cmd,"ignore vomp",NULL)) if (!call)
c->flags&=~MONITOR_VOMP; return monitor_write_error(c,"Invalid call token");
else if (strcase_startswith(cmd,"monitor rhizome", NULL))
c->flags|=MONITOR_RHIZOME;
else if (strcase_startswith(cmd,"ignore rhizome", NULL))
c->flags&=~MONITOR_RHIZOME;
else if (strcase_startswith(cmd,"monitor peers", NULL))
c->flags|=MONITOR_PEERS;
else if (strcase_startswith(cmd,"ignore peers", NULL))
c->flags&=~MONITOR_PEERS;
else if (strcase_startswith(cmd,"monitor dnahelper", NULL))
c->flags|=MONITOR_DNAHELPER;
else if (strcase_startswith(cmd,"ignore dnahelper", NULL))
c->flags&=~MONITOR_DNAHELPER;
else if (sscanf(cmd,"lookup match %s %d %s %s",sid,&port,localDid,remoteDid)>=3) {
monitor_send_lookup_response(sid,port,localDid,remoteDid);
}else if (sscanf(cmd,"call %s %s %s",sid,localDid,remoteDid)==3) {
// pack the binary representation of the sid into the same buffer.
if (stowSid((unsigned char*)sid, 0, sid) == -1)
write_str(c->alarm.poll.fd,"\nERROR:invalid SID, so cannot place call\n");
else {
int cn=0, in=0, kp=0;
if (!keyring_next_identity(keyring, &cn, &in, &kp))
write_str(c->alarm.poll.fd,"\nERROR:no local identity, so cannot place call\n");
else {
vomp_dial(keyring->contexts[cn]->identities[in]->keypairs[kp]->public_key, (unsigned char *)sid, localDid, remoteDid);
}
}
} else if (sscanf(cmd,"ringing %x",&callSessionToken)==1) {
struct vomp_call_state *call=vomp_find_call_by_session(callSessionToken);
vomp_ringing(call); vomp_ringing(call);
} else if (sscanf(cmd,"pickup %x",&callSessionToken)==1) { return 0;
struct vomp_call_state *call=vomp_find_call_by_session(callSessionToken);
vomp_pickup(call);
} }
else if (sscanf(cmd,"hangup %x",&callSessionToken)==1) {
struct vomp_call_state *call=vomp_find_call_by_session(callSessionToken); static int monitor_call_pickup(int argc, const char *const *argv, 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)
return monitor_write_error(c,"Invalid call token");
vomp_pickup(call);
return 0;
}
static int monitor_call_audio(int argc, const char *const *argv, 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)
return monitor_write_error(c,"Invalid call token");
vomp_received_audio(call, atoi(argv[2]), c->buffer, c->data_expected);
return 0;
}
static int monitor_call_hangup(int argc, const char *const *argv, 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)
return monitor_write_error(c,"Invalid call token");
vomp_hangup(call); vomp_hangup(call);
} else if (sscanf(cmd,"dtmf %x %s",&callSessionToken,digits)==2) { return 0;
struct vomp_call_state *call=vomp_find_call_by_session(callSessionToken); }
if (call){
static int monitor_call_dtmf(int argc, const char *const *argv, 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)
return monitor_write_error(c,"Invalid call token");
const char *digits = argv[2];
int i; int i;
for(i=0;i<strlen(digits);i++) { for(i=0;i<strlen(digits);i++) {
int digit=vomp_parse_dtmf_digit(digits[i]); int digit=vomp_parse_dtmf_digit(digits[i]);
if (digit<0) { if (digit<0)
snprintf(msg,1024,"\nERROR: invalid DTMF digit 0x%02x\n",digit); monitor_write_error(c,"Invalid DTMF digit");
write_str(c->alarm.poll.fd,msg); else{
}
/* 80ms standard tone duration, so that it is a multiple /* 80ms standard tone duration, so that it is a multiple
of the majority of codec time units (70ms is the nominal of the majority of codec time units (70ms is the nominal
DTMF tone length for most systems). */ DTMF tone length for most systems). */
@ -453,29 +478,49 @@ int monitor_process_command(struct monitor_context *c)
vomp_received_audio(call, VOMP_CODEC_DTMF, &code, 1); vomp_received_audio(call, VOMP_CODEC_DTMF, &code, 1);
} }
} }
return 0;
} }
snprintf(msg,1024,"\nMONITORSTATUS:%d\n",c->flags); struct command_line_option monitor_options[]={
write_str(c->alarm.poll.fd,msg); {monitor_set,{"monitor","<type>",NULL},0,""},
{monitor_clear,{"ignore","<type>",NULL},0,""},
{monitor_lookup_match,{"lookup","match","<sid>","<port>","<ext>","<name>",NULL},0,""},
{monitor_call, {"call","<sid>","<local_did>","<remote_did>",NULL},0,""},
{monitor_call_ring, {"ringing","<token>",NULL},0,""},
{monitor_call_pickup, {"pickup","<token>",NULL},0,""},
{monitor_call_audio,{"audio","<token>","<type>","[<offset>]",NULL},0,""},
{monitor_call_hangup, {"hangup","<token>",NULL},0,""},
{monitor_call_dtmf, {"dtmf","<token>","<digits>",NULL},0,""},
};
RETURN(0); static int parse_argv(char *cmdline, char delim, char **argv, int max_argv){
int argc=0;
if (*cmdline && argc<max_argv){
argv[argc++]=cmdline;
} }
int monitor_process_data(struct monitor_context *c) // TODO quoted argument handling?
while(*cmdline){
if (*cmdline==delim){
*cmdline=0;
if (cmdline[1] && argc<max_argv)
argv[argc++]=cmdline+1;
}
cmdline++;
}
return argc;
}
int monitor_process_command(struct monitor_context *c)
{ {
IN(); char *argv[16]={NULL,};
/* Called when we have received an entire data sample */ int argc = parse_argv(c->line, ' ', argv, 16);
c->state=MONITOR_STATE_COMMAND;
struct vomp_call_state *call=vomp_find_call_by_session(c->sample_call_session_token); if (cli_execute(NULL, argc, (const char *const*)argv, monitor_options, c))
if (!call) { return monitor_write_error(c, "Invalid command");
write_str(c->alarm.poll.fd,"\nERROR:No such call\n"); return 0;
RETURN(-1);
}
vomp_received_audio(call, c->sample_codec, &c->buffer[0], vomp_sample_size(c->sample_codec));
RETURN(0);
} }
int monitor_announce_bundle(rhizome_manifest *m) int monitor_announce_bundle(rhizome_manifest *m)
@ -501,7 +546,7 @@ int monitor_announce_bundle(rhizome_manifest *m)
|| set_block(monitor_sockets[i].alarm.poll.fd) == -1 || set_block(monitor_sockets[i].alarm.poll.fd) == -1
) { ) {
INFO("Tearing down monitor client"); INFO("Tearing down monitor client");
monitor_client_close(&monitor_sockets[i]); monitor_close(&monitor_sockets[i]);
} }
} }
} }
@ -540,7 +585,7 @@ int monitor_tell_clients(char *msg, int msglen, int mask)
|| set_block(monitor_sockets[i].alarm.poll.fd) == -1 || set_block(monitor_sockets[i].alarm.poll.fd) == -1
) { ) {
INFOF("Tearing down monitor client #%d", i); INFOF("Tearing down monitor client #%d", i);
monitor_client_close(&monitor_sockets[i]); monitor_close(&monitor_sockets[i]);
} }
} }
} }

View File

@ -172,8 +172,6 @@ int rhizome_str_is_file_hash(const char *text);
#define alloca_tohex_bid(bid) alloca_tohex((bid), RHIZOME_MANIFEST_ID_BYTES) #define alloca_tohex_bid(bid) alloca_tohex((bid), RHIZOME_MANIFEST_ID_BYTES)
int http_header_complete(const char *buf, size_t len, size_t tail); int http_header_complete(const char *buf, size_t len, size_t tail);
int str_startswith(char *str, const char *substring, char **afterp);
int strcase_startswith(char *str, const char *substring, char **afterp);
typedef struct sqlite_retry_state { typedef struct sqlite_retry_state {
unsigned int limit; // do not retry once elapsed >= limit unsigned int limit; // do not retry once elapsed >= limit

View File

@ -17,9 +17,10 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#include <stdlib.h>
#include "serval.h" #include "serval.h"
#include "rhizome.h" #include "rhizome.h"
#include <stdlib.h> #include "str.h"
int rhizome_manifest_verify(rhizome_manifest *m) int rhizome_manifest_verify(rhizome_manifest *m)
{ {

View File

@ -18,11 +18,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/ */
#define __RHIZOME_INLINE #define __RHIZOME_INLINE
#include <stdlib.h>
#include <time.h>
#include "serval.h" #include "serval.h"
#include "rhizome.h" #include "rhizome.h"
#include "strbuf.h" #include "strbuf.h"
#include <stdlib.h> #include "str.h"
#include <time.h>
long long rhizome_space=0; long long rhizome_space=0;
static const char *rhizome_thisdatastore_path = NULL; static const char *rhizome_thisdatastore_path = NULL;

View File

@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <time.h> #include <time.h>
#include "serval.h" #include "serval.h"
#include "rhizome.h" #include "rhizome.h"
#include "str.h"
extern int sigPipeFlag; extern int sigPipeFlag;
extern int sigIoFlag; extern int sigIoFlag;

View File

@ -831,24 +831,12 @@ int vomp_tick_interval();
int vomp_sample_size(int c); int vomp_sample_size(int c);
int vomp_codec_timespan(int c); int vomp_codec_timespan(int c);
int vomp_parse_dtmf_digit(char c); int vomp_parse_dtmf_digit(char c);
int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, char *local_did, char *remote_did); int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, const char *local_did, const char *remote_did);
int vomp_pickup(struct vomp_call_state *call); int vomp_pickup(struct vomp_call_state *call);
int vomp_hangup(struct vomp_call_state *call); int vomp_hangup(struct vomp_call_state *call);
int vomp_ringing(struct vomp_call_state *call); int vomp_ringing(struct vomp_call_state *call);
int vomp_received_audio(struct vomp_call_state *call, int audio_codec, const unsigned char *audio, int audio_length); int vomp_received_audio(struct vomp_call_state *call, int audio_codec, const unsigned char *audio, int audio_length);
typedef struct command_line_option {
int (*function)(int argc, const char *const *argv, struct command_line_option *o);
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 */
const char *description; // describe this invocation
} command_line_option;
extern command_line_option command_line_options[];
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_putchar(char c); int cli_putchar(char c);
int cli_puts(const char *str); int cli_puts(const char *str);
int cli_printf(const char *fmt, ...); int cli_printf(const char *fmt, ...);
@ -869,10 +857,11 @@ overlay_interface * overlay_interface_find_name(const char *name);
int directory_registration(); int directory_registration();
int directory_service_init(); int directory_service_init();
struct command_line_option;
#ifdef HAVE_VOIPTEST #ifdef HAVE_VOIPTEST
int app_pa_phone(int argc, const char *const *argv, struct command_line_option *o); int app_pa_phone(int argc, const char *const *argv, struct command_line_option *o, void *context);
#endif #endif
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o); int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o, void *context);
int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax); int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax);

2
vomp.c
View File

@ -658,7 +658,7 @@ int vomp_call_destroy(struct vomp_call_state *call)
return 0; return 0;
} }
int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, char *local_did, char *remote_did) int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, const char *local_did, const char *remote_did)
{ {
/* TODO use local_did and remote_did start putting the call together. /* TODO use local_did and remote_did start putting the call together.
These need to be passed to the node being called to provide caller id, These need to be passed to the node being called to provide caller id,