Improve monitor client interface and vomp for asterisk channel driver

This commit is contained in:
Jeremy Lakeman 2012-08-08 10:56:05 +09:30
parent 736a8f43c0
commit f059546eb0
8 changed files with 421 additions and 201 deletions

View File

@ -346,13 +346,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#define VOMP_CODEC_CODECSISUPPORT 0xfe
#define VOMP_CODEC_CHANGEYOURCODECTO 0xff
#define VOMP_STATE_NOCALL 1
#define VOMP_STATE_CALLPREP 2
#define VOMP_STATE_RINGINGOUT 3
#define VOMP_STATE_RINGINGIN 4
#define VOMP_STATE_INCALL 5
#define VOMP_STATE_CALLENDED 6
/* in milliseconds of inactivity */
#define VOMP_CALL_TIMEOUT 120000
#define VOMP_CALL_STATUS_INTERVAL 1000

22
log.c
View File

@ -175,8 +175,7 @@ static int _log_prepare(int level, const char *file, unsigned int line, const ch
return 1;
}
static void _log_finish(int level)
{
static void _log_internal(int level, struct strbuf *buf){
#ifdef ANDROID
int alevel = ANDROID_LOG_UNKNOWN;
switch (level) {
@ -186,17 +185,28 @@ static void _log_finish(int level)
case LOG_LEVEL_WARN: alevel = ANDROID_LOG_WARN; break;
case LOG_LEVEL_DEBUG: alevel = ANDROID_LOG_DEBUG; break;
}
__android_log_print(alevel, "servald", "%s", strbuf_str(&logbuf));
strbuf_reset(&logbuf);
__android_log_print(alevel, "servald", "%s", strbuf_str(buf));
strbuf_reset(buf);
#else
FILE *logf = open_logging();
if (logf) {
fprintf(logf, "%s\n%s", strbuf_str(&logbuf), strbuf_overrun(&logbuf) ? "LOG OVERRUN\n" : "");
strbuf_reset(&logbuf);
fprintf(logf, "%s\n%s", strbuf_str(buf), strbuf_overrun(buf) ? "LOG OVERRUN\n" : "");
strbuf_reset(buf);
}
#endif
}
void (*_log_implementation)(int level, struct strbuf *buf)=_log_internal;
static void _log_finish(int level){
if(_log_implementation)
_log_implementation(level, &logbuf);
}
void set_log_implementation(void (*log_function)(int level, struct strbuf *buf)){
_log_implementation=log_function;
}
void logArgv(int level, const char *file, unsigned int line, const char *function, const char *label, int argc, const char *const *argv)
{
if (_log_prepare(level, file, line, function)) {

3
log.h
View File

@ -61,6 +61,8 @@ extern unsigned int debug;
#define LOG_LEVEL_ERROR (3)
#define LOG_LEVEL_FATAL (4)
struct strbuf;
void set_logging(FILE *f);
FILE *open_logging();
void close_logging();
@ -74,6 +76,7 @@ char *toprint(char *dstStr, ssize_t dstBufSiz, const char *srcBuf, size_t srcByt
size_t toprint_strlen(const char *srcBuf, size_t srcBytes);
ssize_t get_self_executable_path(char *buf, size_t len);
int log_backtrace(const char *file, unsigned int line, const char *function);
void set_log_implementation(void (*log_function)(int level, struct strbuf *buf));
#define alloca_toprint(dstlen,buf,len) toprint((char *)alloca((dstlen) == -1 ? toprint_strlen((buf),(len)) + 1 : (dstlen)), (dstlen), (buf), (len))

View File

@ -16,99 +16,261 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "monitor-client.h"
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#ifdef WIN32
#include "win32/win32.h"
#endif
#include <unistd.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <sys/un.h>
#include <fcntl.h>
#include "constants.h"
#include "conf.h"
#include "log.h"
#include "str.h"
#include "monitor-client.h"
#include <ctype.h>
#define STATE_INIT 0
#define STATE_DATA 1
#define STATE_READY 2
#define MONITOR_CLIENT_BUFFER_SIZE 8192
#define MAX_ARGS 32
struct monitor_state {
char *cmd;
int argc;
char *argv[MAX_ARGS];
unsigned char *data;
int dataBytes;
int cmdBytes;
int state;
unsigned char buffer[MONITOR_CLIENT_BUFFER_SIZE];
int bufferBytes;
};
// FIX ME, COPY-PASTA from monitor.c
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;
}
/* Open monitor interface abstract domain named socket */
int monitor_client_open()
int monitor_client_open(struct monitor_state **res)
{
int fd;
struct sockaddr_un addr;
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(-1);
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
/* XXX - On non-linux systems, we need to use a regular named socket */
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]);
int len = monitor_socket_name(&addr);
INFOF("Attempting to connect to %s", &addr.sun_path[1]);
if (connect(fd, (struct sockaddr*)&addr, len) == -1) {
perror("connect");
exit(-1);
return -1;
}
*res = (struct monitor_state*)malloc(sizeof(struct monitor_state));
return fd;
}
int monitor_client_writeline(int fd,char *msg)
{
return write(fd,msg,strlen(msg));
int monitor_client_close(int fd, struct monitor_state *res){
free(res);
close(fd);
return 0;
}
int monitor_client_writeline_and_data(int fd,char *msg,unsigned char *data,int bytes)
int monitor_client_writeline(int fd,char *fmt, ...)
{
int maxlen=strlen(msg)+20+bytes;
char msg[512];
int n;
va_list ap;
if (fd<0)
return -1;
va_start(ap, fmt);
n=vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
return write(fd,msg,n);
}
int monitor_client_writeline_and_data(int fd,unsigned char *data,int bytes,char *fmt,...)
{
int maxlen=512+bytes;
char out[maxlen];
snprintf(out,maxlen,"*%d:%s\n",bytes,msg);
int len=strlen(msg);
bcopy(&data[0],&msg[len],bytes);
len+=bytes;
return write(fd,msg,len);
va_list ap;
int n;
if (fd<0)
return -1;
n=snprintf(out,maxlen-bytes,"*%d:",bytes);
va_start(ap, fmt);
n+=vsnprintf(out+n, maxlen-bytes-n, fmt, ap);
va_end(ap);
bcopy(data,out+n,bytes);
n+=bytes;
return write(fd,out,n);
}
static unsigned char buffer[MONITOR_CLIENT_BUFFER_SIZE];
static int buffer_bytes=0;
int monitor_client_readline(int fd, monitor_result **res)
int monitor_client_read(int fd, struct monitor_state *res, struct monitor_command_handler *handlers, int handler_count)
{
monitor_result *r=*res;
/* Read any available bytes */
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, NULL) | O_NONBLOCK);
int bytesRead=read(fd,&buffer[buffer_bytes],
MONITOR_CLIENT_BUFFER_SIZE-buffer_bytes);
if (bytesRead>0) buffer_bytes+=bytesRead;
/* Now see if we have a full line of results to return */
int i;
for(i=0;i<buffer_bytes;i++)
if (buffer[i]=='\n') {
/* Found an end of line marker.
Now check if there is a data section to extract. */
int dataBytes=0;
int lineStart=0;
if (sscanf("*%d:%n",(char *)buffer,&dataBytes,&lineStart)==1)
{
if ((dataBytes+i)>buffer_bytes)
{
/* We don't yet have enough bytes to return */
return -1;
}
/* Copy data section */
r->dataBytes=dataBytes;
bcopy(&buffer[i],&r->data[0],dataBytes);
/* copy line from after the *len: part, and without the
new line. Then null-terminate it */
bcopy(&buffer[lineStart],&r->line[0],i-lineStart);
r->line[i-lineStart]=0;
/* remember to discard the data section from the buffer */
i+=dataBytes;
} else {
/* no data section */
r->dataBytes=0;
}
/* shuffle buffer down */
bcopy(&buffer[i],&buffer[0],buffer_bytes-i);
buffer_bytes-=i;
return 0;
int oldOffset = res->bufferBytes;
if (res->bufferBytes==0)
res->cmd = (char *)res->buffer;
int bytesRead=read(fd, res->buffer + oldOffset, MONITOR_CLIENT_BUFFER_SIZE - oldOffset);
if (bytesRead<1){
switch(errno) {
case EINTR:
case ENOTRECOVERABLE:
/* transient errors */
WHY_perror("read");
case EAGAIN:
return 0;
}
/* no end of line, so need to read more */
return -1;
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
if ((char*)(res->buffer+i) == res->cmd){
res->cmd++;
continue;
}
res->buffer[i]=0;
res->dataBytes = 0;
res->cmdBytes = i + 1;
if (*res->cmd=='*'){
res->cmd++;
for (; isdigit(*res->cmd); ++res->cmd)
res->dataBytes = res->dataBytes * 10 + *res->cmd - '0';
if (*res->cmd==':')
res->cmd++;
}
// find all arguments, initialise argc / argv && null terminate strings
{
char *p=res->cmd;
res->argc=0;
while (*p){
if (*p==':'){
*p=0;
res->argv[res->argc]=p+1;
res->argc++;
}
p++;
}
}
if (res->dataBytes){
res->data=(unsigned char *)&res->buffer[i+1];
res->state = STATE_DATA;
}else{
res->data=NULL;
res->state = STATE_READY;
}
break;
}
}
}
// make sure all the data has arrived
if (res->state == STATE_DATA){
if (res->bufferBytes >= res->dataBytes + res->cmdBytes){
res->state = STATE_READY;
}
}
// ok, now we can try to process the command
if (res->state == STATE_READY){
int handled=0;
int i;
// call all handlers that match (yes there might be more than one)
for (i=0;i<handler_count;i++){
/* since we know res->cmd is terminated with a '\n',
and there shouldn't be a '\n' in h->command,
this shouldn't run past the end of the buffer */
if (handlers[i].handler && (!handlers[i].command || strcase_startswith(res->cmd,handlers[i].command, NULL))){
if (handlers[i].handler(res->cmd, res->argc, res->argv, res->data, res->dataBytes, handlers[i].context)>0)
handled=1;
}
}
if (!handled){
INFOF("Event \"%s\" was not handled", res->cmd);
}
// shuffle any unprocessed bytes
int remaining = res->bufferBytes - (res->dataBytes + res->cmdBytes);
if (remaining>0){
bcopy(res->buffer+res->dataBytes + res->cmdBytes,res->buffer,remaining);
}
res->bufferBytes=remaining;
res->cmdBytes=0;
res->dataBytes=0;
res->state = STATE_INIT;
res->cmd = (char *)res->buffer;
oldOffset = 0;
goto again;
}
return 0;
}

View File

@ -16,34 +16,16 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#include <string.h>
#include <signal.h>
#include <sys/types.h>
struct monitor_state;
#ifdef WIN32
#include "win32/win32.h"
#endif
#include <unistd.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <sys/un.h>
#include <fcntl.h>
struct monitor_command_handler{
char *command;
void *context;
int (*handler)(char *cmd, int argc, char **argv, unsigned char *data, int dataLen, void *context);
};
#include "constants.h"
#include "conf.h"
#include "log.h"
#define MONITOR_CLIENT_BUFFER_SIZE 8192
typedef struct monitor_result {
char line[MONITOR_CLIENT_BUFFER_SIZE];
unsigned char data[MONITOR_CLIENT_BUFFER_SIZE];
int dataBytes;
} monitor_result;
int monitor_client_open(struct monitor_state **res);
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_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);

View File

@ -60,6 +60,28 @@ struct sched_ent named_socket;
struct profile_total named_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()
{
struct sockaddr_un name;
@ -74,23 +96,9 @@ int monitor_setup_sockets()
goto error;
}
#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));
len = monitor_socket_name(&name);
#ifndef linux
unlink(name.sun_path);
/* Includes trailing nul */
len = 1+strlen(name.sun_path) + sizeof(name.sun_family);
#endif
if(bind(sock, (struct sockaddr *)&name, len)==-1) {
@ -176,7 +184,7 @@ void monitor_client_close(struct monitor_context *c){
void monitor_client_poll(struct sched_ent *alarm)
{
/* Read from any open connections */
/* Read available data from a monitor socket */
struct monitor_context *c=(struct monitor_context *)alarm;
errno=0;
int bytes;
@ -288,9 +296,12 @@ static void monitor_new_client(int s) {
#endif
if (otheruid != getuid()) {
WHYF("monitor.socket client has wrong uid (%d versus %d)", otheruid,getuid());
write_str(s, "\nCLOSE:Incorrect UID\n");
goto error;
int allowed_id = confValueGetInt64("monitor.uid",-1);
if (otheruid != allowed_id){
WHYF("monitor.socket client has wrong uid (%d versus %d)", otheruid,getuid());
write_str(s, "\nCLOSE:Incorrect UID\n");
goto error;
}
}
if (monitor_socket_count >= MAX_MONITOR_SOCKETS
||monitor_socket_count < 0) {
@ -333,7 +344,6 @@ int monitor_process_command(struct monitor_context *c)
}
char msg[1024];
int flag;
if (cmd[0]=='*') {
/* command with content */
@ -346,7 +356,7 @@ int monitor_process_command(struct monitor_context *c)
c->data_offset=0;
c->sample_codec=-1;
if (sscanf(cmd,"AUDIO:%x:%d",
if (sscanf(cmd,"AUDIO %x %d",
&callSessionToken,&sampleType)==2)
{
/* Start getting sample */
@ -356,18 +366,18 @@ int monitor_process_command(struct monitor_context *c)
}
}
}
else if (!strcasecmp(cmd,"monitor vomp"))
else if (strcase_startswith(cmd,"monitor vomp",NULL))
// TODO add supported codec list argument
c->flags|=MONITOR_VOMP;
else if (!strcasecmp(cmd,"ignore vomp"))
else if (strcase_startswith(cmd,"ignore vomp",NULL))
c->flags&=~MONITOR_VOMP;
else if (!strcasecmp(cmd,"monitor rhizome"))
else if (strcase_startswith(cmd,"monitor rhizome", NULL))
c->flags|=MONITOR_RHIZOME;
else if (!strcasecmp(cmd,"ignore rhizome"))
else if (strcase_startswith(cmd,"ignore rhizome", NULL))
c->flags&=~MONITOR_RHIZOME;
else if (!strcasecmp(cmd,"monitor peers"))
else if (strcase_startswith(cmd,"monitor peers", NULL))
c->flags|=MONITOR_PEERS;
else if (!strcasecmp(cmd,"ignore peers"))
else if (strcase_startswith(cmd,"ignore peers", NULL))
c->flags&=~MONITOR_PEERS;
else if (sscanf(cmd,"call %s %s %s",sid,localDid,remoteDid)==3) {
DEBUG("here");
@ -490,18 +500,12 @@ int monitor_announce_bundle(rhizome_manifest *m)
int monitor_announce_peer(const unsigned char *sid)
{
char msg[1024];
int n = snprintf(msg, sizeof msg, "\nNEWPEER:%s\n", alloca_tohex_sid(sid));
monitor_tell_clients(msg, n, MONITOR_PEERS);
return 0;
return monitor_tell_formatted(MONITOR_PEERS, "\nNEWPEER:%s\n", alloca_tohex_sid(sid));
}
int monitor_announce_unreachable_peer(const unsigned char *sid)
{
char msg[1024];
int n = snprintf(msg, sizeof msg, "\nOLDPEER:%s\n", alloca_tohex_sid(sid));
monitor_tell_clients(msg, n, MONITOR_PEERS);
return 0;
return monitor_tell_formatted(MONITOR_PEERS, "\nOLDPEER:%s\n", alloca_tohex_sid(sid));
}
// test if any monitor clients are interested in a particular type of event
@ -532,3 +536,15 @@ int monitor_tell_clients(char *msg, int msglen, int mask)
}
RETURN(0);
}
int monitor_tell_formatted(int mask, char *fmt, ...){
char msg[1024];
int n;
va_list ap;
va_start(ap, fmt);
n=vsnprintf(msg, sizeof(msg), fmt, ap);
va_end(ap);
monitor_tell_clients(msg, n, mask);
return 0;
}

View File

@ -1092,6 +1092,7 @@ int monitor_get_fds(struct pollfd *fds,int *fdcount,int fdmax);
int monitor_announce_peer(const unsigned char *sid);
int monitor_announce_unreachable_peer(const unsigned char *sid);
int monitor_tell_clients(char *msg, int msglen, int mask);
int monitor_tell_formatted(int mask, char *fmt, ...);
int monitor_client_interested(int mask);
extern int monitor_socket_count;

167
vomp.c
View File

@ -34,26 +34,40 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# incoming command from monitor client
$ outgoing monitor status
<> vomp packet with state change sent across the network
Monitor Init
# MONITOR VOMP
# MONITOR VOMP [supported codec list]
Dialing
# CALL [sid] [myDid] [TheirDid] [token]
// client requests an outgoing call
# CALL [sid] [myDid] [TheirDid]
> CALLPREP + codecs
$ CALLTO [token]
// let the client know what token we are going to use for the remainder of the call
$ CALLTO [token] [mySid] [myDid] [TheirSid] [TheirDid]
// allocate a session number and tell them our codecs,
// but we don't need to do anything else yet,
// this might be a replay attack
< NOCALL + codecs
// Ok, we have a network path, lets try to establish the call
> RINGOUT
$ CALLFROM [token] [sid] [myDid] [TheirDid]
// (Note that if both parties are trying to dial each other,
// the call should jump straight to INCALL)
// inform client about the call request
$ CALLFROM [token] [mySid] [myDid] [TheirSid] [TheirDid]
// Note that we may need to wait for other external processes
// before a phone is actually ringing
# RING [token]
< RINGIN (they start ringing)
< RINGIN
// All good, there's a phone out there ringing, you can indicate that to the user
$ RINGING [token]
Answering
# PICKUP [token]
< INCALL
// The client can now start sending audio
> INCALL
$ INCALL [token]
// The client can now start sending audio
$ INCALL [token]
Tell any clients that the call hasn't timed out yet
@ -68,7 +82,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
$ HANGUP [token]
*/
// ideally these id's should only be used on the network, with monitor events to inform clients of state changes
#define VOMP_STATE_NOCALL 1
#define VOMP_STATE_CALLPREP 2
#define VOMP_STATE_RINGINGOUT 3
#define VOMP_STATE_RINGINGIN 4
#define VOMP_STATE_INCALL 5
#define VOMP_STATE_CALLENDED 6
struct vomp_call_state {
struct sched_ent alarm;
@ -147,9 +167,7 @@ int vomp_generate_session_id()
struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
unsigned char *local_sid,
unsigned int remote_session,
unsigned int local_session,
int remote_state,
int local_state)
unsigned int local_session)
{
int i;
if (!local_session)
@ -164,8 +182,8 @@ struct vomp_call_state *vomp_create_call(unsigned char *remote_sid,
bcopy(remote_sid,call->remote.sid,SID_SIZE);
call->local.session=local_session;
call->remote.session=remote_session;
call->local.state=local_state;
call->remote.state=remote_state;
call->local.state=VOMP_STATE_NOCALL;
call->remote.state=VOMP_STATE_NOCALL;
call->last_sent_status=-1;
call->create_time=gettime_ms();
call->last_activity=call->create_time;
@ -245,7 +263,7 @@ struct vomp_call_state *vomp_find_or_create_call(unsigned char *remote_sid,
/* Only create a call record if either party is in CALLPREP state */
if (sender_state==VOMP_STATE_CALLPREP || recvr_state==VOMP_STATE_CALLPREP)
return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session, VOMP_STATE_NOCALL, VOMP_STATE_NOCALL);
return vomp_create_call(remote_sid, local_sid, sender_session, recvr_session);
return NULL;
}
@ -448,10 +466,9 @@ int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned i
Put newline at start of these so that receiving data in command
mode doesn't confuse the parser. */
int msglen = snprintf(msg, 1024,
"\n*%d:AUDIOPACKET:%06x:%06x:%d:%d:%d:%d:%d\n",
"\n*%d:AUDIOPACKET:%x:%d:%d:%d\n",
sample_bytes,
call->local.session,call->remote.session,
call->local.state,call->remote.state,
call->local.session,
audio_codec, start_time, end_time);
bcopy(audio, &msg[msglen], sample_bytes);
@ -461,6 +478,54 @@ int monitor_send_audio(struct vomp_call_state *call, int audio_codec, unsigned i
return 0;
}
// update local state and notify interested clients with the correct message
int vomp_update_local_state(struct vomp_call_state *call, int new_state){
if (call->local.state>=new_state)
return 0;
switch(new_state){
case VOMP_STATE_CALLPREP:
// tell client our session id.
monitor_tell_formatted(MONITOR_VOMP, "\nCALLTO:%x:%s:%s:%s:%s\n",
call->local.session,
alloca_tohex_sid(call->local.sid), call->local.did,
alloca_tohex_sid(call->remote.sid), call->remote.did);
break;
case VOMP_STATE_CALLENDED:
monitor_tell_formatted(MONITOR_VOMP, "\nHANGUP:%x\n", call->local.session);
break;
}
call->local.state=new_state;
return 0;
}
// update remote state and notify interested clients with the correct message
int vomp_update_remote_state(struct vomp_call_state *call, int new_state){
if (call->remote.state>=new_state)
return 0;
switch(new_state){
case VOMP_STATE_RINGINGOUT:
monitor_tell_formatted(MONITOR_VOMP, "\nCALLFROM:%x:%s:%s:%s:%s\n",
call->local.session,
alloca_tohex_sid(call->local.sid), call->local.did,
alloca_tohex_sid(call->remote.sid), call->remote.did);
break;
case VOMP_STATE_RINGINGIN:
monitor_tell_formatted(MONITOR_VOMP, "\nRINGING:%x\n", call->local.session);
break;
case VOMP_STATE_INCALL:
if (call->remote.state==VOMP_STATE_RINGINGIN){
monitor_tell_formatted(MONITOR_VOMP, "\nANSWERED:%x\n", call->local.session);
}
break;
}
call->remote.state=new_state;
return 0;
}
// send call state updates if required.
int vomp_update(struct vomp_call_state *call)
{
@ -568,7 +633,7 @@ int vomp_ringing(struct vomp_call_state *call){
if (debug & DEBUG_VOMP)
DEBUGF("RING RING!");
if (call->local.state<VOMP_STATE_RINGINGIN && call->remote.state==VOMP_STATE_RINGINGOUT){
call->local.state=VOMP_STATE_RINGINGIN;
vomp_update_local_state(call, VOMP_STATE_RINGINGIN);
vomp_update(call);
}
}
@ -584,8 +649,7 @@ int vomp_call_destroy(struct vomp_call_state *call)
DEBUGF("Destroying call %s <--> %s", call->local.did,call->remote.did);
/* tell everyone the call has died */
call->local.state=VOMP_STATE_CALLENDED; call->remote.state=VOMP_STATE_CALLENDED;
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
vomp_update(call);
/* now release the call structure */
@ -621,11 +685,8 @@ int vomp_dial(unsigned char *local_sid, unsigned char *remote_sid, char *local_d
remote_sid,
local_sid,
0,
0,
VOMP_STATE_NOCALL,
VOMP_STATE_CALLPREP
);
0);
vomp_update_local_state(call, VOMP_STATE_CALLPREP);
// remember that we initiated this call, not the other party
call->initiated_call = 1;
@ -643,7 +704,7 @@ int vomp_pickup(struct vomp_call_state *call)
DEBUG("Picking up");
if (call->local.state!=VOMP_STATE_RINGINGIN)
return WHY("Call is not ringing");
call->local.state=VOMP_STATE_INCALL;
vomp_update_local_state(call, VOMP_STATE_INCALL);
call->create_time=gettime_ms();
/* state machine does job of starting audio stream, just tell everyone about
the changed state. */
@ -658,7 +719,7 @@ int vomp_hangup(struct vomp_call_state *call)
if (debug & DEBUG_VOMP)
DEBUG("Hanging up");
if (call->local.state==VOMP_STATE_INCALL) vomp_call_stop_audio(call);
call->local.state=VOMP_STATE_CALLENDED;
vomp_update_local_state(call, VOMP_STATE_CALLENDED);
vomp_update(call);
}
return 0;
@ -947,6 +1008,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
if (!recvr_session && (debug & DEBUG_VOMP))
DEBUG("recvr_session==0, created call");
recvr_state = call->local.state;
// TODO ignore state changes if sequence is stale?
// TODO ignore state changes that seem to go backwards?
@ -954,17 +1016,17 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
if ((!vomp_interested_usock_count)
&&(!monitor_socket_count)
&&(!monitor_client_interested(MONITOR_VOMP)))
{
{
/* 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", call->local.state,sender_state);
if (debug & DEBUG_VOMP)
DEBUGF("Rejecting call due to lack of a listener: states=%d,%d", recvr_state, sender_state);
call->local.state=VOMP_STATE_CALLENDED;
recvr_state=VOMP_STATE_CALLENDED;
/* now let the state machine progress to destroy the call */
}
if (call->local.state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){
if (recvr_state < VOMP_STATE_RINGINGOUT && sender_state < VOMP_STATE_RINGINGOUT){
// the other party should have given us their list of supported codecs
vomp_extract_remote_codec_list(call,mdp);
}
@ -974,7 +1036,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
so we must also move to CALLENDED no matter what state we were in */
if (call->audio_started) vomp_call_stop_audio(call);
call->local.state=VOMP_STATE_CALLENDED;
recvr_state=VOMP_STATE_CALLENDED;
}
/* Consider states: our actual state, sender state, what the sender thinks
@ -982,7 +1044,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
breaks down to what we think our state is, and what they think their
state is. That leaves us with just 6X6=36 cases.
*/
int combined_state=call->local.state<<3 | sender_state;
int combined_state=recvr_state<<3 | sender_state;
switch(combined_state) {
case (VOMP_STATE_NOCALL<<3)|VOMP_STATE_CALLPREP:
@ -1000,7 +1062,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
if (call->initiated_call)
// hey, quit it, we were trying to call you.
call->local.state=VOMP_STATE_CALLENDED;
recvr_state=VOMP_STATE_CALLENDED;
else{
// Don't automatically transition to RINGIN, wait for a client to tell us when.
}
@ -1016,9 +1078,9 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
*/
if (call->initiated_call){
// TODO fail the call if we can't agree on codec's
call->local.state=VOMP_STATE_RINGINGOUT;
recvr_state=VOMP_STATE_RINGINGOUT;
}else{
call->local.state=VOMP_STATE_CALLENDED;
recvr_state=VOMP_STATE_CALLENDED;
}
break;
@ -1035,7 +1097,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_RINGINGOUT:
/* Woah, we're trying to dial each other?? That must have been well timed.
Jump to INCALL and start audio */
call->local.state=VOMP_STATE_INCALL;
recvr_state=VOMP_STATE_INCALL;
// reset create time when call is established
call->create_time=gettime_ms();
break;
@ -1047,7 +1109,7 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
case (VOMP_STATE_RINGINGOUT<<3)|VOMP_STATE_INCALL:
/* They have answered, we can jump to incall as well */
call->local.state=VOMP_STATE_INCALL;
recvr_state=VOMP_STATE_INCALL;
// reset create time when call is established
call->create_time=gettime_ms();
// Fall through
@ -1073,11 +1135,14 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
Any state not explicitly listed above is considered invalid and possibly stale,
the packet will be completely ignored.
*/
WHYF("Ignoring invalid call state %d.%d",sender_state,recvr_state);
return 0;
}
call->remote.sequence=sender_seq;
call->remote.state=sender_state;
vomp_update_remote_state(call, sender_state);
vomp_update_local_state(call, recvr_state);
call->last_activity=gettime_ms();
// TODO if we hear a stale echo of our state should we force another outgoing packet now?
@ -1086,8 +1151,8 @@ int vomp_mdp_received(overlay_mdp_frame *mdp)
/* send an update to the call status if required */
vomp_update(call);
if (call->remote.state==VOMP_STATE_CALLENDED
&&call->local.state==VOMP_STATE_CALLENDED)
if (sender_state==VOMP_STATE_CALLENDED
&&recvr_state==VOMP_STATE_CALLENDED)
return vomp_call_destroy(call);
}
return 0;
@ -1512,22 +1577,10 @@ static void vomp_process_tick(struct sched_ent *alarm)
slots getting full of cruft. */
vomp_call_destroy(call);
return;
} else if (call->last_activity+VOMP_CALL_TIMEOUT<now)
switch(call->local.state)
{
case VOMP_STATE_INCALL:
/* Timeout while call in progress, so end call.
Keep call structure hanging around for a bit so that we can
synchonrise with the far end if possible. */
call->local.state=VOMP_STATE_CALLENDED;
vomp_call_stop_audio(call);
call->last_activity=now;
break;
default:
/* Call timed out while not actually in progress, so just immmediately
tear the call down */
vomp_call_destroy(call);
return;
} else if (call->last_activity+VOMP_CALL_TIMEOUT<now){
/* Call timed out, so just immmediately tear the call down */
vomp_call_destroy(call);
return;
}
/* update everyone if the state has changed */