From fa7719fbe8e498876813213ff10e29a67545352b Mon Sep 17 00:00:00 2001 From: Jeremy Lakeman Date: Thu, 27 Sep 2012 16:27:37 +0930 Subject: [PATCH] Write simple vomp console client --- Android.mk | 1 + Makefile.in | 1 + commandline.c | 2 + constants.h | 5 +- directory_service.c | 7 +- monitor-client.c | 9 +- monitor.c | 52 ++++---- overlay_mdp.c | 8 +- serval.h | 6 + str.c | 20 +++ str.h | 1 + vomp.c | 108 ++++++++++++---- vomp_console.c | 291 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 451 insertions(+), 60 deletions(-) create mode 100644 vomp_console.c diff --git a/Android.mk b/Android.mk index 90bed54e..5dcc4dfd 100644 --- a/Android.mk +++ b/Android.mk @@ -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 \ diff --git a/Makefile.in b/Makefile.in index 8c7d96d6..02ce366c 100755 --- a/Makefile.in +++ b/Makefile.in @@ -62,6 +62,7 @@ SRCS= \ strbuf_helpers.c \ strlcpy.c \ vomp.c \ + vomp_console.c \ xprintf.c MONITORCLIENTSRCS=conf.c \ diff --git a/commandline.c b/commandline.c index 396394f1..a9a2806c 100644 --- a/commandline.c +++ b/commandline.c @@ -1652,6 +1652,8 @@ struct command_line_option command_line_options[]={ "Set specified configuration variable."}, {app_config_get,{"config","get","[]",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","",NULL},CLIFLAG_STANDALONE, "Compute the Rhizome hash of a file"}, {app_rhizome_add_file,{"rhizome","add","file","","","","[]","[]",NULL},CLIFLAG_STANDALONE, diff --git a/constants.h b/constants.h index e14dfc33..7a713076 100644 --- a/constants.h +++ b/constants.h @@ -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 diff --git a/directory_service.c b/directory_service.c index c49543e7..02353c8e 100644 --- a/directory_service.c +++ b/directory_service.c @@ -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; diff --git a/monitor-client.c b/monitor-client.c index e1e9a86c..eef7a177 100644 --- a/monitor-client.c +++ b/monitor-client.c @@ -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;ibufferBytes;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->argcargv[res->argc]=p+1; diff --git a/monitor.c b/monitor.c index 0b77df5b..3822beb8 100644 --- a/monitor.c +++ b/monitor.c @@ -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;jflags|=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=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","","...",NULL},0,""}, {monitor_set,{"monitor","",NULL},0,""}, {monitor_clear,{"ignore","",NULL},0,""}, {monitor_lookup_match,{"lookup","match","","","","",NULL},0,""}, @@ -491,28 +516,9 @@ struct command_line_option monitor_options[]={ {monitor_call_audio,{"audio","","","[]",NULL},0,""}, {monitor_call_hangup, {"hangup","",NULL},0,""}, {monitor_call_dtmf, {"dtmf","","",NULL},0,""}, + {NULL}, }; -static int parse_argv(char *cmdline, char delim, char **argv, int max_argv){ - int argc=0; - - if (*cmdline && argcsun_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 */ diff --git a/serval.h b/serval.h index 5a29cc78..cfdd8c53 100644 --- a/serval.h +++ b/serval.h @@ -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); diff --git a/str.c b/str.c index 496760ab..e23b441e 100644 --- a/str.c +++ b/str.c @@ -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 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 (;ofsin.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;iremote_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_TIMEOUTlocal.state==VOMP_STATE_CALLENDED + &&call->remote.state==VOMP_STATE_CALLENDED){ vomp_call_destroy(call); return; } diff --git a/vomp_console.c b/vomp_console.c new file mode 100644 index 00000000..ab224df1 --- /dev/null +++ b/vomp_console.c @@ -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 +#include +#include +#include +#include +#include +#include + +#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=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","","[]","[]",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 (;iline_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; +}