Write simple vomp console client

This commit is contained in:
Jeremy Lakeman 2012-09-27 16:27:37 +09:30
parent 66e0711d6b
commit fa7719fbe8
13 changed files with 451 additions and 60 deletions

View File

@ -48,6 +48,7 @@ SERVALD_SRC_FILES = \
serval-dna/str.c \
serval-dna/keyring.c \
serval-dna/vomp.c \
serval-dna/vomp_console.c \
serval-dna/lsif.c \
serval-dna/dna_helper.c \
serval-dna/sighandlers.c \

View File

@ -62,6 +62,7 @@ SRCS= \
strbuf_helpers.c \
strlcpy.c \
vomp.c \
vomp_console.c \
xprintf.c
MONITORCLIENTSRCS=conf.c \

View File

@ -1652,6 +1652,8 @@ struct command_line_option command_line_options[]={
"Set specified configuration variable."},
{app_config_get,{"config","get","[<variable>]",NULL},CLIFLAG_STANDALONE,
"Get specified configuration variable."},
{app_vomp_console,{"console",NULL},0,
"Test phone call life-cycle from the console"},
{app_rhizome_hash_file,{"rhizome","hash","file","<filepath>",NULL},CLIFLAG_STANDALONE,
"Compute the Rhizome hash of a file"},
{app_rhizome_add_file,{"rhizome","add","file","<author_sid>","<pin>","<filepath>","[<manifestpath>]","[<bsk>]",NULL},CLIFLAG_STANDALONE,

View File

@ -297,9 +297,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define VOMP_CODEC_DTMF 0x80
#define VOMP_CODEC_ENGAGED 0x81
#define VOMP_CODEC_ONHOLD 0x82
#define VOMP_CODEC_CALLERID 0x83
#define VOMP_CODEC_CODECSISUPPORT 0xfe
#define VOMP_CODEC_CHANGEYOURCODECTO 0xff
#define CODEC_FLAGS_LENGTH 32
/* in milliseconds of inactivity */
// 20 seconds to start ringing

View File

@ -123,7 +123,12 @@ static void resolve_request(){
static char line_buff[1024];
static int line_pos=0;
set_nonblock(STDIN_FILENO);
int bytes = read(STDIN_FILENO, line_buff + line_pos, sizeof(line_buff) - line_pos);
set_block(STDIN_FILENO);
int i = line_pos;
int processed=0;
line_pos+=bytes;
@ -156,8 +161,6 @@ int main(int argc, char **argv){
if (overlay_mdp_bind(srcsid,MDP_PORT_DIRECTORY))
return WHY("Could not bind to MDP socket");
set_nonblock(STDIN_FILENO);
fds[0].fd = STDIN_FILENO;
fds[0].events = POLLIN;
fds[1].fd = mdp_client_socket;

View File

@ -163,6 +163,9 @@ int monitor_client_read(int fd, struct monitor_state *res, struct monitor_comman
/* Read any available bytes */
int oldOffset = res->bufferBytes;
if (oldOffset+1>=MONITOR_CLIENT_BUFFER_SIZE)
return WHY("Buffer full without finding command");
if (res->bufferBytes==0)
res->cmd = (char *)res->buffer;
@ -182,14 +185,12 @@ int monitor_client_read(int fd, struct monitor_state *res, struct monitor_comman
WHY_perror("read");
return -1;
}
res->bufferBytes+=bytesRead;
again:
// wait until we have the whole command line
if (res->state == STATE_INIT){
int i;
for(i=oldOffset;i<res->bufferBytes;i++){
if (res->buffer[i]=='\n'){
// skip any leading \n's
@ -205,6 +206,8 @@ again:
res->cmd++;
for (; isdigit(*res->cmd); ++res->cmd)
res->dataBytes = res->dataBytes * 10 + *res->cmd - '0';
if (res->dataBytes<0 || res->dataBytes > MONITOR_CLIENT_BUFFER_SIZE)
return WHYF("Invalid data length %d", res->dataBytes);
if (*res->cmd==':')
res->cmd++;
}
@ -213,7 +216,7 @@ again:
{
char *p=res->cmd;
res->argc=0;
while (*p){
while (*p && res->argc<MAX_ARGS){
if (*p==':'){
*p=0;
res->argv[res->argc]=p+1;

View File

@ -39,7 +39,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define MONITOR_DATA_SIZE MAX_AUDIO_BYTES
struct monitor_context {
struct sched_ent alarm;
// monitor interest bitmask
int flags;
// what types of audio can we write to this client?
// (packed bits)
unsigned char supported_codecs[CODEC_FLAGS_LENGTH];
char line[MONITOR_LINE_LENGTH];
int line_length;
#define MONITOR_STATE_COMMAND 1
@ -335,7 +340,7 @@ static void monitor_new_client(int s) {
c->alarm.poll.events=POLLIN;
c->line_length = 0;
c->state = MONITOR_STATE_COMMAND;
write_str(s,"\nMONITOR:You are talking to servald\n");
write_str(s,"\nINFO:You are talking to servald\n");
INFOF("Got %d clients", monitor_socket_count);
watch(&c->alarm);
@ -346,11 +351,30 @@ static void monitor_new_client(int s) {
return;
}
void monitor_get_all_supported_codecs(unsigned char *codecs){
int i, j;
bzero(codecs,CODEC_FLAGS_LENGTH);
for(i=monitor_socket_count -1;i>=0;i--) {
if (monitor_sockets[i].flags & MONITOR_VOMP){
for (j=0;j<CODEC_FLAGS_LENGTH;j++)
codecs[j]|=monitor_sockets[i].supported_codecs[j];
}
}
}
static int monitor_set(int argc, const char *const *argv, struct command_line_option *o, void *context){
struct monitor_context *c=context;
if (strcase_startswith((char *)argv[1],"vomp",NULL))
if (strcase_startswith((char *)argv[1],"vomp",NULL)){
c->flags|=MONITOR_VOMP;
else if (strcase_startswith((char *)argv[1],"rhizome", NULL))
// store the list of supported codecs against the monitor connection,
// since we need to forget about them when the client disappears.
int i;
for (i=2;i<argc;i++){
int codec = atoi(argv[i]);
if (codec>=0 && codec <=255)
set_codec_flag(codec, c->supported_codecs);
}
}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;
@ -482,6 +506,7 @@ static int monitor_call_dtmf(int argc, const char *const *argv, struct command_l
}
struct command_line_option monitor_options[]={
{monitor_set,{"monitor","vomp","<codec>","...",NULL},0,""},
{monitor_set,{"monitor","<type>",NULL},0,""},
{monitor_clear,{"ignore","<type>",NULL},0,""},
{monitor_lookup_match,{"lookup","match","<sid>","<port>","<ext>","<name>",NULL},0,""},
@ -491,28 +516,9 @@ struct command_line_option monitor_options[]={
{monitor_call_audio,{"audio","<token>","<type>","[<offset>]",NULL},0,""},
{monitor_call_hangup, {"hangup","<token>",NULL},0,""},
{monitor_call_dtmf, {"dtmf","<token>","<digits>",NULL},0,""},
{NULL},
};
static int parse_argv(char *cmdline, char delim, char **argv, int max_argv){
int argc=0;
if (*cmdline && argc<max_argv){
argv[argc++]=cmdline;
}
// 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)
{
char *argv[16]={NULL,};

View File

@ -212,14 +212,12 @@ int overlay_mdp_process_bind_request(int sock, struct subscriber *subscriber, in
}
/* See if binding already exists */
int found=-1; // XXX: this is never set, why is it here?
int free=-1;
for(i=0;i<MDP_MAX_BINDINGS;i++) {
/* Look for duplicate bindings */
if (mdp_bindings[i].port == port && mdp_bindings[i].subscriber == subscriber) {
if (found != -1 &&
mdp_bindings[found].name_len==recvaddrlen &&
!memcmp(mdp_bindings[found].socket_name,recvaddr->sun_path,recvaddrlen)) {
if (mdp_bindings[i].name_len==recvaddrlen &&
!memcmp(mdp_bindings[i].socket_name,recvaddr->sun_path,recvaddrlen)) {
// this client already owns this port binding?
INFO("Identical binding exists");
return 0;
@ -657,6 +655,8 @@ int overlay_mdp_dispatch(overlay_mdp_frame *mdp,int userGeneratedFrameP,
/* Prepare the overlay frame for dispatch */
struct overlay_frame *frame = calloc(1,sizeof(struct overlay_frame));
if (!frame)
FATAL("Couldn't allocate frame buffer");
if (is_sid_any(mdp->out.src.sid)){
/* set source to ourselves */

View File

@ -788,6 +788,10 @@ void _serval_debug_free(void *p, struct __sourceloc where);
struct vomp_call_state;
void set_codec_flag(int codec, unsigned char *flags);
int is_codec_set(int codec, unsigned char *flags);
struct vomp_call_state *vomp_find_call_by_session(int session_token);
int vomp_mdp_received(overlay_mdp_frame *mdp);
int vomp_tick_interval();
@ -799,6 +803,7 @@ int vomp_pickup(struct vomp_call_state *call);
int vomp_hangup(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);
void monitor_get_all_supported_codecs(unsigned char *codecs);
int cli_putchar(char c);
int cli_puts(const char *str);
@ -825,6 +830,7 @@ struct command_line_option;
int app_pa_phone(int argc, const char *const *argv, struct command_line_option *o, void *context);
#endif
int app_monitor_cli(int argc, const char *const *argv, struct command_line_option *o, void *context);
int app_vomp_console(int argc, const char *const *argv, struct command_line_option *o, void *context);
int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax);

20
str.c
View File

@ -42,3 +42,23 @@ int strcase_startswith(char *str, const char *substring, char **afterp)
return 1;
}
int parse_argv(char *cmdline, char delim, char **argv, int max_argv){
int argc=0;
if (*cmdline && argc<max_argv){
argv[argc++]=cmdline;
}
// TODO quoted argument handling?
while(*cmdline){
if (*cmdline==delim){
*cmdline=0;
if (cmdline[1] && argc<max_argv)
argv[argc++]=cmdline+1;
}
cmdline++;
}
return argc;
}

1
str.h
View File

@ -37,5 +37,6 @@ int str_startswith(char *str, const char *substring, char **afterp);
*/
int strcase_startswith(char *str, const char *substring, char **afterp);
int parse_argv(char *cmdline, char delim, char **argv, int max_argv);
#endif

108
vomp.c
View File

@ -49,7 +49,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// this might be a replay attack
< NOCALL + codecs
// Ok, we have a network path, lets try to establish the call
$ CODECS [token] [their supported codec list]
> RINGOUT
$ CODECS [token] [their supported codec list]
// (Note that if both parties are trying to dial each other,
// the call should jump straight to INCALL)
// inform client about the call request
@ -145,7 +147,7 @@ struct vomp_call_state {
time_ms_t audio_clock;
// last local & remote status we sent to all interested parties
int last_sent_status;
unsigned char remote_codec_list[256];
unsigned char remote_codec_flags[CODEC_FLAGS_LENGTH];
// track when we first heard audio, so we can calculate timing from the current sequence number
int first_remote_audio_sequence;
@ -168,8 +170,18 @@ static void vomp_process_tick(struct sched_ent *alarm);
static const char *vomp_describe_codec(int c);
strbuf strbuf_append_vomp_supported_codecs(strbuf sb, const unsigned char supported_codecs[256]);
/* which codecs we support (set by registered listener) */
unsigned char vomp_local_codec_list[256];
void set_codec_flag(int codec, unsigned char *flags){
if (codec<0 || codec>255)
return;
flags[codec >> 3] |= 1<<(codec & 7);
}
int is_codec_set(int codec, unsigned char *flags){
if (codec<0 || codec>255)
return 0;
return flags[codec >> 3] & (1<<(codec & 7));
}
struct vomp_call_state *vomp_find_call_by_session(int session_token)
{
@ -338,19 +350,22 @@ int vomp_send_status_remote(struct vomp_call_state *call)
prepare_vomp_header(call, &mdp);
if (call->local.state < VOMP_STATE_RINGINGOUT && call->remote.state < VOMP_STATE_RINGINGOUT) {
/* Include src and dst phone numbers */
int didLen;
unsigned char codecs[CODEC_FLAGS_LENGTH];
/* Include the list of supported codecs */
monitor_get_all_supported_codecs(codecs);
int i;
for (i = 0; i < 256; ++i)
if (vomp_local_codec_list[i]) {
if (is_codec_set(i,codecs)) {
mdp.out.payload[(*len)++]=i;
if (debug & DEBUG_VOMP)
DEBUGF("I support the %s codec", vomp_describe_codec(i));
}
mdp.out.payload[(*len)++]=0;
/* Include src and dst phone numbers */
if (call->initiated_call){
DEBUGF("Sending phone numbers %s, %s",call->local.did,call->remote.did);
didLen = snprintf((char *)(mdp.out.payload + *len), sizeof(mdp.out.payload) - *len, "%s", call->local.did);
@ -363,9 +378,10 @@ int vomp_send_status_remote(struct vomp_call_state *call)
DEBUGF("mdp frame with codec list is %d bytes", mdp.out.payload_length);
}
call->local.sequence++;
overlay_mdp_dispatch(&mdp,0,NULL,0);
call->local.sequence++;
return 0;
}
@ -406,13 +422,13 @@ int vomp_received_audio(struct vomp_call_state *call, int audio_codec, const uns
offset+=codec_block_size;
call->audio_clock += codec_duration;
call->local.sequence++;
// send the payload more than once to add resilience to dropped packets
// TODO remove once network links have built in retries
mdp.out.send_copies=VOMP_MAX_RECENT_SAMPLES;
overlay_mdp_dispatch(&mdp,0,NULL,0);
call->local.sequence++;
}
return 0;
@ -462,6 +478,24 @@ int vomp_update_local_state(struct vomp_call_state *call, int new_state){
if (call->local.state>=new_state)
return 0;
if (new_state > VOMP_STATE_CALLPREP && new_state <= VOMP_STATE_INCALL && call->local.state<=VOMP_STATE_CALLPREP){
// tell clients about the remote codec list
int i;
unsigned char our_codecs[CODEC_FLAGS_LENGTH];
char msg[256];
monitor_get_all_supported_codecs(our_codecs);
strbuf b = strbuf_local(msg, sizeof msg);
strbuf_sprintf(b, "\nCODECS:%06x", call->local.session);
for (i = 0; i < 256; ++i){
if (is_codec_set(i,call->remote_codec_flags) && is_codec_set(i,our_codecs)) {
strbuf_sprintf(b, ":%d", i);
}
}
strbuf_putc(b, '\n');
monitor_tell_clients(strbuf_str(b), strbuf_len(b), MONITOR_VOMP);
}
switch(new_state){
case VOMP_STATE_CALLPREP:
// tell client our session id.
@ -601,15 +635,13 @@ int vomp_ringing(struct vomp_call_state *call){
int vomp_call_destroy(struct vomp_call_state *call)
{
if (debug & DEBUG_VOMP)
DEBUGF("Destroying call %s <--> %s", call->local.did,call->remote.did);
/* tell everyone the call has died */
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
vomp_update(call);
DEBUGF("Destroying call %06x:%06x [%s,%s]", call->local.session, call->remote.session, call->local.did,call->remote.did);
/* now release the call structure */
int i = (call - vomp_call_states);
unschedule(&call->alarm);
call->local.session=0;
call->remote.session=0;
vomp_call_count--;
if (i!=vomp_call_count){
@ -693,7 +725,8 @@ int vomp_extract_remote_codec_list(struct vomp_call_state *call,overlay_mdp_fram
dump("codec list mdp frame", (unsigned char *)&mdp->in.payload[0],mdp->in.payload_length);
for (;ofs<mdp->in.payload_length && mdp->in.payload[ofs];ofs++){
call->remote_codec_list[mdp->in.payload[ofs]]=1;
int codec = mdp->in.payload[ofs];
set_codec_flag(codec, call->remote_codec_flags);
}
if (!call->initiated_call){
ofs++;
@ -764,16 +797,34 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
{
/* No registered listener, so we cannot answer the call, so just reject
it. */
if (debug & DEBUG_VOMP)
DEBUGF("Rejecting call due to lack of a listener: states=%d,%d", recvr_state, sender_state);
WHY("Rejecting call, no listening clients");
recvr_state=VOMP_STATE_CALLENDED;
/* now let the state machine progress to destroy the call */
}
if (recvr_state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){
unsigned char supported_codecs[CODEC_FLAGS_LENGTH];
int i, found=0;
// the other party should have given us their list of supported codecs
vomp_extract_remote_codec_list(call,mdp);
// make sure we have at least one codec in common
monitor_get_all_supported_codecs(supported_codecs);
// look for a matching bit
for (i=0;i<CODEC_FLAGS_LENGTH;i++){
if (supported_codecs[i] & call->remote_codec_flags[i]){
found=1;
break;
}
}
// nope, we can't speak the same language.
if (!found){
WHY("Rejecting call, no matching codecs found");
recvr_state=VOMP_STATE_CALLENDED;
}
}
if (sender_state==VOMP_STATE_CALLENDED){
@ -889,10 +940,6 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
/* send an update to the call status if required */
vomp_update(call);
if (sender_state==VOMP_STATE_CALLENDED
&&recvr_state==VOMP_STATE_CALLENDED)
return vomp_call_destroy(call);
}
return 0;
break;
@ -920,7 +967,6 @@ static const char *vomp_describe_codec(int c)
case VOMP_CODEC_DTMF: return "DTMF";
case VOMP_CODEC_ENGAGED: return "Engaged-tone";
case VOMP_CODEC_ONHOLD: return "On-Hold";
case VOMP_CODEC_CALLERID: return "CallerID";
}
return "unknown";
}
@ -941,7 +987,6 @@ int vomp_sample_size(int c)
case VOMP_CODEC_DTMF: return 1;
case VOMP_CODEC_ENGAGED: return 0;
case VOMP_CODEC_ONHOLD: return 0;
case VOMP_CODEC_CALLERID: return 32;
}
return -1;
}
@ -961,7 +1006,6 @@ int vomp_codec_timespan(int c)
case VOMP_CODEC_DTMF: return 80;
case VOMP_CODEC_ENGAGED: return 20;
case VOMP_CODEC_ONHOLD: return 20;
case VOMP_CODEC_CALLERID: return 0;
}
return -1;
}
@ -997,7 +1041,7 @@ static void vomp_process_tick(struct sched_ent *alarm)
time_ms_t now = gettime_ms();
struct vomp_call_state *call = (struct vomp_call_state *)alarm;
/* See if any calls need to be expired.
Allow VOMP_CALL_DIAL_TIMEOUT ms for the other party to ring / request ringing
Allow VOMP_CALL_RING_TIMEOUT ms for the ringing party to answer
@ -1007,6 +1051,20 @@ static void vomp_process_tick(struct sched_ent *alarm)
if ((call->remote.state < VOMP_STATE_RINGINGOUT && call->create_time + VOMP_CALL_DIAL_TIMEOUT < now) ||
(call->local.state < VOMP_STATE_INCALL && call->create_time + VOMP_CALL_RING_TIMEOUT < now) ||
(call->last_activity+VOMP_CALL_NETWORK_TIMEOUT<now) ){
/* tell any local clients that call has died */
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
vomp_update_remote_state(call, VOMP_STATE_CALLENDED);
vomp_update(call);
}
/*
If we are calling ourselves, mdp packets are processed as soon as they are sent.
So we can't risk moving call entries around at that time as that will change pointers that are still on the stack.
So instead we wait for the next vomp tick to destroy the structure
*/
if (call->local.state==VOMP_STATE_CALLENDED
&&call->remote.state==VOMP_STATE_CALLENDED){
vomp_call_destroy(call);
return;
}

291
vomp_console.c Normal file
View File

@ -0,0 +1,291 @@
/*
Copyright (C) 2012 Serval Project
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 <sys/socket.h>
#include <sys/un.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <poll.h>
#include <fcntl.h>
#include "serval.h"
#include "cli.h"
#include "monitor-client.h"
#include "str.h"
#include "constants.h"
int call_token=-1;
int monitor_client_fd=-1;
static void send_hangup(int session_id){
monitor_client_writeline(monitor_client_fd, "hangup %06x\n",session_id);
}
static void send_ringing(int session_id){
monitor_client_writeline(monitor_client_fd, "ringing %06x\n",session_id);
}
static void send_pickup(int session_id){
monitor_client_writeline(monitor_client_fd, "pickup %06x\n",session_id);
}
static void send_call(const char *sid, const char *caller_id, const char *remote_ext){
monitor_client_writeline(monitor_client_fd, "call %s %s %s\n", sid, caller_id, remote_ext);
}
static void send_audio(int session_id, unsigned char *buffer, int len, int codec){
monitor_client_writeline_and_data(monitor_client_fd, buffer, len, "audio %06x %d\n", session_id, codec);
}
static int remote_call(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token != -1){
send_hangup(token);
printf("Rejected incoming call, already busy\n");
fflush(stdout);
return 1;
}
call_token = token;
printf("Incoming call\n");
fflush(stdout);
send_ringing(token);
return 1;
}
static int remote_ringing(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == token){
printf("They're ringing\n");
fflush(stdout);
}else
send_hangup(token);
return 1;
}
static int remote_pickup(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == token){
printf("They've picked up\n");
fflush(stdout);
}else
send_hangup(token);
return 1;
}
static int remote_dialing(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == -1){
call_token=token;
printf("Dialling\n");
fflush(stdout);
}else
send_hangup(token);
return 1;
}
static int remote_hangup(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == token){
printf("Hangup\n");
fflush(stdout);
call_token=-1;
}
return 1;
}
static int remote_audio(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == token){
printf("Incoming audio\n");
fflush(stdout);
}else
send_hangup(token);
return 1;
}
static int remote_codecs(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int token = strtol(argv[0], NULL, 16);
if (call_token == token){
printf("Codec list ...\n");
fflush(stdout);
}else
send_hangup(token);
return 1;
}
static int remote_print(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
int i;
printf("%s",cmd);
for (i=0;i<argc;i++){
printf(" %s",argv[i]);
}
printf("\n");
if (dataLen){
dump(NULL,data,dataLen);
}
fflush(stdout);
return 1;
}
static int remote_noop(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context){
return 1;
}
struct monitor_command_handler console_handlers[]={
{.command="CALLFROM", .handler=remote_call},
{.command="RINGING", .handler=remote_ringing},
{.command="ANSWERED", .handler=remote_pickup},
{.command="CALLTO", .handler=remote_dialing},
{.command="HANGUP", .handler=remote_hangup},
{.command="AUDIOPACKET", .handler=remote_audio},
{.command="CODECS", .handler=remote_codecs},
{.command="INFO", .handler=remote_print},
{.command="CALLSTATUS", .handler=remote_noop},
{.command="KEEPALIVE", .handler=remote_noop},
{.command="MONITORSTATUS", .handler=remote_noop},
};
static int console_dial(int argc, const char *const *argv, struct command_line_option *o, void *context){
if (call_token!=-1){
printf("Already in a call\n");
return 0;
}
const char *sid=argv[1];
const char *local=argc>=3?argv[2]:"55500000";
const char *remote=argc>=4?argv[3]:"55500000";
send_call(sid, local, remote);
return 0;
}
static int console_answer(int argc, const char *const *argv, struct command_line_option *o, void *context){
if (call_token==-1){
printf("No call to answer\n");
fflush(stdout);
}else
send_pickup(call_token);
return 0;
}
static int console_hangup(int argc, const char *const *argv, struct command_line_option *o, void *context){
if (call_token==-1){
printf("No call to hangup\n");
fflush(stdout);
}else
send_hangup(call_token);
return 0;
}
static int console_usage(int argc, const char *const *argv, struct command_line_option *o, void *context);
struct command_line_option console_commands[]={
{console_dial,{"call","<sid>","[<local_number>]","[<remote_extension>]",NULL},0,"Start dialling a given person"},
{console_answer,{"answer",NULL},0,"Answer an incoming phone call"},
{console_hangup,{"hangup",NULL},0,"Hangup the line"},
{console_usage,{"help",NULL},0,"This usage message"},
{NULL},
};
static int console_usage(int argc, const char *const *argv, struct command_line_option *o, void *context){
cli_usage(console_commands);
fflush(stdout);
return 0;
}
static void console_command(char *line){
char *argv[16];
int argc = parse_argv(line, ' ', argv, 16);
if (cli_execute(NULL, argc, (const char *const*)argv, console_commands, NULL)){
printf("Unknown command, try help\n");
fflush(stdout);
}
}
struct line_state{
int fd;
char line_buff[1024];
int line_pos;
};
static void read_lines(struct line_state *state, void (*process_line)(char *line)){
set_nonblock(STDIN_FILENO);
int bytes = read(state->fd, state->line_buff + state->line_pos, sizeof(state->line_buff) - state->line_pos);
set_block(STDIN_FILENO);
int i = state->line_pos;
int processed=0;
state->line_pos+=bytes;
char *line_start=state->line_buff;
for (;i<state->line_pos;i++){
if (state->line_buff[i]=='\n'){
state->line_buff[i]=0;
if (*line_start)
process_line(line_start);
processed=i+1;
line_start = state->line_buff + processed;
}
}
if (processed){
// squash unprocessed data back to the start of the buffer
state->line_pos -= processed;
bcopy(state->line_buff, line_start, state->line_pos);
}
}
int app_vomp_console(int argc, const char *const *argv, struct command_line_option *o, void *context){
struct pollfd fds[2];
struct line_state stdin_state;
struct monitor_state *state;
monitor_client_fd = monitor_client_open(&state);
monitor_client_writeline(monitor_client_fd, "monitor vomp %d %d %d\n",
VOMP_CODEC_8ULAW,VOMP_CODEC_8ALAW,VOMP_CODEC_PCM);
bzero(&stdin_state, sizeof(struct line_state));
stdin_state.fd = 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, 10000);
if (r>0){
if (fds[0].revents & POLLIN)
read_lines(&stdin_state, console_command);
if (fds[1].revents & POLLIN){
if (monitor_client_read(monitor_client_fd, state, console_handlers,
sizeof(console_handlers)/sizeof(struct monitor_command_handler))<0){
break;
}
}
if (fds[0].revents & (POLLHUP | POLLERR))
break;
}
}
monitor_client_close(monitor_client_fd, state);
monitor_client_fd=-1;
return 0;
}