mirror of
https://github.com/servalproject/serval-dna.git
synced 2025-01-29 15:43:56 +00:00
Improve monitor client interface and vomp for asterisk channel driver
This commit is contained in:
parent
736a8f43c0
commit
f059546eb0
@ -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
22
log.c
@ -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
3
log.h
@ -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))
|
||||
|
||||
|
294
monitor-client.c
294
monitor-client.c
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
88
monitor.c
88
monitor.c
@ -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;
|
||||
}
|
||||
|
1
serval.h
1
serval.h
@ -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
167
vomp.c
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user