wanpipe/ssmg/sangoma_mgd.trunk/sangoma_mgd.c

5010 lines
133 KiB
C

/*********************************************************************************
* sangoma_mgd.c -- Sangoma Media Gateway Daemon for Sangoma/Wanpipe Cards
*
* Copyright 05-08, Nenad Corbic <ncorbic@sangoma.com>
* Anthony Minessale II <anthmct@yahoo.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*
* =============================================
* v1.27 Nenad Corbic <ncorbic@sangoma.com>
* Jan 24 2007
* Fixed a memory leak on incoming calls
* Removed the use of server listener which
* was not used
*
* v1.26 Nenad Corbic <ncorbic@sangoma.com>
* Jan 18 2007
* Fixed hangup after invalid Answer or Ack Session
* Can cause double use of setup id - now fixed
* Update on autoacm on accept check for acked.
*
* v1.25 Nenad Corbic <ncorbic@sangoma.com>
* Dec 31 2007
* Removed UDP Resync it can cause skb_over errors.
* Moved RDNIS message to higher debug level
*
* v1.24 Nenad Corbic <ncorbic@sangoma.com>
* Nov 30 2007
* Bug fix on return code on ALL ckt busy
*
* v1.23 Nenad Corbic <ncorbic@sangoma.com>
* Bug fix on socket open. Check for retun code >= 0
*
* v1.22 Nenad Corbic <ncorbic@sangoma.com>
* Nov 27 2007
* Updated DTMF Tx function
* Fixed - dtmf tx without voice
* Fxied - dtmf clipping.
*
* v1.21 Nenad Corbic <ncorbic@sangoma.com>
* Nov 25 2007
* Major unit testing of each state
* Numerous bug fixes for non autoacm mode.
* Changed "Channel-Name" to tg/cic
* Added compile option WANPIPE_CHAN_NAME to change Asterisk channel
* name of chan_woomera.so. So one can use Dial(SS7/g1/${EXTE})
* instead of WOOMERA (for example)
*
* v1.20 Nenad Corbic <ncorbic@sangoma.com>
* Added option for Auto ACM response mode.
*
* v1.19 Nenad Corbic <ncorbic@sangoma.com>
* Configurable DTMF
* Bug fix in release codes (all ckt busy)
*
* v1.18 Nenad Corbic <ncorbic@sangoma.com>
* Added new rel cause support based on
* digits instead of strings.
*
* v1.17 Nenad Corbic <ncorbic@sangoma.com>
* Added session support
*
* v1.16 Nenad Corbic <ncorbic@sangoma.com>
* Added hwec disable on loop ccr
*
* v1.15 Nenad Corbic <ncorbic@sangoma.com>
* Updated DTMF Locking
* Added delay between digits
*
* v1.14 Nenad Corbic <ncorbic@sangoma.com>
* Updated DTMF Library
* Fixed DTMF synchronization
*
* v1.13 Nenad Corbic <ncorbic@sangoma.com>
* Woomera OPAL Dialect
* Added Congestion control
* Added SCTP
* Added priority ISUP queue
* Fixed presentation
*
* v1.12 Nenad Corbic <ncorbic@sangoma.com>
* Fixed CCR
* Removed socket shutdown on end call.
* Let Media thread shutodwn sockets.
*
* v1.11 Nenad Corbic <ncorbic@sangoma.com>
* Fixed Remote asterisk/woomera connection
* Increased socket timeouts
*
* v1.10 Nenad Corbic <ncorbic@sangoma.com>
* Added Woomera OPAL dialect.
* Start montor thread in priority
*
* v1.9 Nenad Corbic <ncorbic@sangoma.com>
* Added Loop mode for ccr
* Added remote debug enable
* Fixed syslog logging.
*
* v1.8 Nenad Corbic <ncorbic@sangoma.com>
* Added a ccr loop mode for each channel.
* Boost can set any channel in loop mode
*
* v1.7 Nenad Corbic <ncorbic@sangoma.com>
* Pass trunk group number to incoming call
* chan woomera will use it to append to context
* name. Added presentation feature.
*
* v1.6 Nenad Corbic <ncorbic@sangoma.com>
* Use only ALAW and MLAW not SLIN.
* This reduces the load quite a bit.
* Send out ALAW/ULAW format on HELLO message.
* RxTx Gain is done now in chan_woomera.
*
* v1.5 Nenad Corbic <ncorbic@sangoma.com>
* Bug fix in START_NACK_ACK handling.
* When we receive START_NACK we must alwasy pull tank before
* we send out NACK_ACK this way we will not try to send NACK
* ourself.
*********************************************************************************/
#include "sangoma_mgd.h"
#include "q931_cause.h"
#define USE_SYSLOG 1
#define CODEC_LAW_DEFAULT 1
#ifdef CODEC_LAW_DEFAULT
static uint32_t codec_sample=8;
#else
static uint32_t codec_sample=16;
#endif
static char ps_progname[]="sangoma_mgd";
static struct woomera_interface woomera_dead_dev;
#if 0
#define DOTRACE
#endif
#define SMG_VERSION "v1.27"
/* enable early media */
#if 1
#define WOOMERA_EARLY_MEDIA 1
#endif
#define SMG_DTMF_ON 60
#define SMG_DTMF_OFF 10
#define SMG_DTMF_RATE 8000
#if 0
#define MEDIA_SOCK_SHUTDOWN 1
#endif
#ifdef DOTRACE
static int tc = 0;
#endif
#if 0
#warning "NENAD: HPTDM API"
#define WP_HPTDM_API 1
#else
#undef WP_HPTDM_API
#endif
#ifdef WP_HPTDM_API
hp_tdm_api_span_t *hptdmspan[WOOMERA_MAX_SPAN];
#endif
#undef SMG_CALLING_NAME
const char WELCOME_TEXT[] =
"================================================================================\n"
"Sangoma Media Gateway Daemon v1.27 \n"
"TDM Signal Media Gateway for Sangoma/Wanpipe Cards\n"
"Copyright 2005, 2006, 2007 \n"
"Nenad Corbic <ncorbic@sangoma.com>, Anthony Minessale II <anthmct@yahoo.com>\n"
"This program is free software, distributed under the terms of\n"
"the GNU General Public License\n"
"================================================================================\n"
"";
static int master_reset=0;
static int coredump=1;
static int autoacm=1;
static int launch_media_tdm_thread(struct woomera_interface *woomera);
static int launch_woomera_thread(struct woomera_interface *woomera);
static struct woomera_interface *alloc_woomera(void);
q931_cause_to_str_array_t q931_cause_to_str_array[255];
#if 0
static uint32_t string_to_release(char *code)
{
if (code) {
if (!strcasecmp(code, "CHANUNAVAIL")) {
return SIGBOOST_RELEASE_CAUSE_NOANSWER;
}
if (!strcasecmp(code, "INVALID")) {
return SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST;
}
if (!strcasecmp(code, "ERROR")) {
return SIGBOOST_RELEASE_CAUSE_UNDEFINED;
}
if (!strcasecmp(code, "CONGESTION")) {
//return SIGBOOST_RELEASE_CAUSE_BUSY;
return SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST;
}
if (!strcasecmp(code, "BUSY")) {
return SIGBOOST_RELEASE_CAUSE_BUSY;
}
if (!strcasecmp(code, "NOANSWER")) {
return SIGBOOST_RELEASE_CAUSE_NOANSWER;
}
if (!strcasecmp(code, "ANSWER")) {
return SIGBOOST_RELEASE_CAUSE_NORMAL;
}
if (!strcasecmp(code, "CANCEL")) {
return SIGBOOST_RELEASE_CAUSE_BUSY;
}
if (!strcasecmp(code, "UNKNOWN")) {
return SIGBOOST_RELEASE_CAUSE_UNDEFINED;
}
}
return SIGBOOST_RELEASE_CAUSE_NORMAL;
}
static char * release_to_string(uint32_t rel_cause)
{
switch (rel_cause) {
case SIGBOOST_RELEASE_CAUSE_UNDEFINED:
return "UNKNOWN";
case SIGBOOST_RELEASE_CAUSE_NORMAL:
return "NORMAL";
case SIGBOOST_RELEASE_CAUSE_BUSY:
return "BUSY";
case SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST:
return "CHANUNAVAIL";
case SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET:
return "CANCEL";
case SIGBOOST_RELEASE_CAUSE_NOANSWER:
return "NOANSWER";
case SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT:
return "TIMEOUT";
case SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY:
return "CONGESTION";
case SIGBOOST_CALL_SETUP_NACK_CALLED_NUM_TOO_BIG:
return "ERROR";
case SIGBOOST_CALL_SETUP_NACK_CALLING_NUM_TOO_BIG:
return "ERROR";
case SIGBOOST_CALL_SETUP_NACK_CALLED_NUM_TOO_SMALL:
return "ERROR";
case SIGBOOST_CALL_SETUP_NACK_CALLING_NUM_TOO_SMALL:
return "ERROR";
}
return "NORMAL";
}
#endif
void __log_printf(int level, FILE *fp, char *file, const char *func, int line, char *fmt, ...)
{
char *data;
int ret = 0;
va_list ap;
if (socket < 0) {
return;
}
if (level && level > server.debug) {
return;
}
va_start(ap, fmt);
#ifdef SOLARIS
data = (char *) malloc(2048);
vsnprintf(data, 2048, fmt, ap);
#else
ret = vasprintf(&data, fmt, ap);
#endif
va_end(ap);
if (ret == -1) {
fprintf(stderr, "Memory Error\n");
} else {
char date[80] = "";
struct tm now;
time_t epoch;
if (time(&epoch) && localtime_r(&epoch, &now)) {
strftime(date, sizeof(date), "%Y-%m-%d %T", &now);
}
#ifdef USE_SYSLOG
syslog(LOG_DEBUG | LOG_LOCAL2, data);
#else
fprintf(fp, "[%d] %s %s:%d %s() %s", getpid(), date, file, line, func, data);
#endif
free(data);
}
#ifndef USE_SYSLOG
fflush(fp);
#endif
}
static int isup_exec_command(int span, int chan, int id, int cmd, int cause)
{
call_signal_event_t oevent;
int retry=5;
call_signal_event_init(&oevent, cmd, chan, span);
oevent.release_cause = cause;
if (id >= 0) {
oevent.call_setup_id = id;
}
isup_exec_cmd_retry:
if (call_signal_connection_write(&server.mcon, &oevent) <= 0){
--retry;
if (retry <= 0) {
log_printf(0, server.log,
"Critical System Error: Failed to tx on ISUP socket: %s\n",
strerror(errno));
return -1;
} else {
log_printf(0, server.log,
"System Warning: Failed to tx on ISUP socket: %s :retry %i\n",
strerror(errno),retry);
}
goto isup_exec_cmd_retry;
}
return 0;
}
static int socket_printf(int socket, char *fmt, ...)
{
char *data;
int ret = 0;
va_list ap;
if (socket < 0) {
return -1;
}
va_start(ap, fmt);
#ifdef SOLARIS
data = (char *) malloc(2048);
vsnprintf(data, 2048, fmt, ap);
#else
ret = vasprintf(&data, fmt, ap);
#endif
va_end(ap);
if (ret == -1) {
fprintf(stderr, "Memory Error\n");
log_printf(0, server.log, "Crtical ERROR: Memory Error!\n");
} else {
int err;
int len = strlen(data);
err=send(socket, data, strlen(data), 0);
if (err != strlen(data)) {
log_printf(2, server.log, "ERROR: Failed to send data to woomera socket(%i): err=%i len=%d %s\n",
socket,err,len,strerror(errno));
ret = err;
} else {
ret = 0;
}
free(data);
}
return ret;
}
static int woomera_next_pair(struct woomera_config *cfg, char **var, char **val)
{
int ret = 0;
char *p, *end;
*var = *val = NULL;
for(;;) {
cfg->lineno++;
if (!fgets(cfg->buf, sizeof(cfg->buf), cfg->file)) {
ret = 0;
break;
}
*var = cfg->buf;
if (**var == '[' && (end = strchr(*var, ']'))) {
*end = '\0';
(*var)++;
strncpy(cfg->category, *var, sizeof(cfg->category) - 1);
continue;
}
if (**var == '#' || **var == '\n' || **var == '\r') {
continue;
}
if ((end = strchr(*var, '#'))) {
*end = '\0';
end--;
} else if ((end = strchr(*var, '\n'))) {
if (*end - 1 == '\r') {
end--;
}
*end = '\0';
}
p = *var;
while ((*p == ' ' || *p == '\t') && p != end) {
*p = '\0';
p++;
}
*var = p;
if (!(*val = strchr(*var, '='))) {
ret = -1;
log_printf(0, server.log, "Invalid syntax on %s: line %d\n", cfg->path, cfg->lineno);
continue;
} else {
p = *val - 1;
*(*val) = '\0';
(*val)++;
if (*(*val) == '>') {
*(*val) = '\0';
(*val)++;
}
while ((*p == ' ' || *p == '\t') && p != *var) {
*p = '\0';
p--;
}
p = *val;
while ((*p == ' ' || *p == '\t') && p != end) {
*p = '\0';
p++;
}
*val = p;
ret = 1;
break;
}
}
return ret;
}
#if 0
static void woomera_set_span_chan(struct woomera_interface *woomera, int span, int chan)
{
pthread_mutex_lock(&woomera->vlock);
woomera->span = span;
woomera->chan = chan;
pthread_mutex_unlock(&woomera->vlock);
}
#endif
static struct woomera_event *new_woomera_event_printf(struct woomera_event *ebuf, char *fmt, ...)
{
struct woomera_event *event = NULL;
int ret = 0;
va_list ap;
if (ebuf) {
event = ebuf;
} else if (!(event = new_woomera_event())) {
log_printf(0, server.log, "Memory Error queuing event!\n");
return NULL;
} else {
return NULL;
}
va_start(ap, fmt);
#ifdef SOLARIS
event->data = (char *) malloc(2048);
vsnprintf(event->data, 2048, fmt, ap);
#else
ret = vasprintf(&event->data, fmt, ap);
#endif
va_end(ap);
if (ret == -1) {
log_printf(0, server.log, "Memory Error queuing event!\n");
destroy_woomera_event(&event, EVENT_FREE_DATA);
return NULL;
}
return event;
}
static struct woomera_event *woomera_clone_event(struct woomera_event *event)
{
struct woomera_event *clone;
if (!(clone = new_woomera_event())) {
return NULL;
}
memcpy(clone, event, sizeof(*event));
clone->next = NULL;
clone->data = strdup(event->data);
return clone;
}
static void enqueue_event(struct woomera_interface *woomera,
struct woomera_event *event,
event_args free_data)
{
struct woomera_event *ptr, *clone = NULL;
assert(woomera != NULL);
assert(event != NULL);
if (!(clone = woomera_clone_event(event))) {
log_printf(0, server.log, "Error Cloning Event\n");
return;
}
pthread_mutex_lock(&woomera->queue_lock);
for (ptr = woomera->event_queue; ptr && ptr->next ; ptr = ptr->next);
if (ptr) {
ptr->next = clone;
} else {
woomera->event_queue = clone;
}
pthread_mutex_unlock(&woomera->queue_lock);
woomera_set_flag(woomera, WFLAG_EVENT);
if (free_data && event->data) {
/* The event has been duplicated, the original data
* should be freed */
free(event->data);
event->data=NULL;
}
}
static char *dequeue_event(struct woomera_interface *woomera)
{
struct woomera_event *event;
char *data = NULL;
if (!woomera) {
return NULL;
}
pthread_mutex_lock(&woomera->queue_lock);
if (woomera->event_queue) {
event = woomera->event_queue;
woomera->event_queue = event->next;
data = event->data;
pthread_mutex_unlock(&woomera->queue_lock);
destroy_woomera_event(&event, EVENT_KEEP_DATA);
return data;
}
pthread_mutex_unlock(&woomera->queue_lock);
return data;
}
static int enqueue_event_on_listeners(struct woomera_event *event)
{
struct woomera_listener *ptr;
int x = 0;
assert(event != NULL);
pthread_mutex_lock(&server.listen_lock);
for (ptr = server.listeners ; ptr ; ptr = ptr->next) {
enqueue_event(ptr->woomera, event, EVENT_KEEP_DATA);
x++;
}
pthread_mutex_unlock(&server.listen_lock);
return x;
}
static void del_listener(struct woomera_interface *woomera)
{
struct woomera_listener *ptr, *last = NULL;
pthread_mutex_lock(&server.listen_lock);
for (ptr = server.listeners ; ptr ; ptr = ptr->next) {
if (ptr->woomera == woomera) {
if (last) {
last->next = ptr->next;
} else {
server.listeners = ptr->next;
}
free(ptr);
break;
}
last = ptr;
}
pthread_mutex_unlock(&server.listen_lock);
}
static void add_listener(struct woomera_interface *woomera)
{
struct woomera_listener *new;
pthread_mutex_lock(&server.listen_lock);
if ((new = malloc(sizeof(*new)))) {
memset(new, 0, sizeof(*new));
new->woomera = woomera;
new->next = server.listeners;
server.listeners = new;
} else {
log_printf(0, server.log, "Memory Error adding listener!\n");
}
pthread_mutex_unlock(&server.listen_lock);
}
static int wanpipe_send_dtmf(struct woomera_interface *woomera, char *digits)
{
struct media_session *ms = woomera_get_ms(woomera);
char *cur = NULL;
int wrote = 0;
int err;
if (!ms) {
return -EINVAL;
}
if (!ms->dtmf_buffer) {
log_printf(3, woomera->log, "Allocate DTMF Buffer....");
err=switch_buffer_create_dynamic(&ms->dtmf_buffer, 1024, server.dtmf_size, 0);
if (err != 0) {
log_printf(0, woomera->log, "Failed to allocate DTMF Buffer!\n");
return -ENOMEM;
} else {
log_printf(3, woomera->log, "SUCCESS!\n");
}
}
log_printf(3, woomera->log, "Sending DTMF %s\n",digits);
for (cur = digits; *cur; cur++) {
if ((wrote = teletone_mux_tones(&ms->tone_session,
&ms->tone_session.TONES[(int)*cur]))) {
pthread_mutex_lock(&woomera->dtmf_lock);
err=switch_buffer_write(ms->dtmf_buffer, ms->tone_session.buffer, wrote * 2);
pthread_mutex_unlock(&woomera->dtmf_lock);
log_printf(3, woomera->log, "Sending DTMF %s Wrote=%i (err=%i)\n",
digits,wrote*2,err);
} else {
log_printf(0, woomera->log, "Error: Sending DTMF %s (err=%i)\n",
digits,wrote);
}
}
ms->skip_read_frames = 200;
return 0;
}
static struct woomera_interface *alloc_woomera(void)
{
struct woomera_interface *woomera = NULL;
if ((woomera = malloc(sizeof(struct woomera_interface)))) {
memset(woomera, 0, sizeof(struct woomera_interface));
woomera->chan = -1;
woomera->span = -1;
woomera->log = server.log;
woomera->debug = server.debug;
woomera->call_id = 1;
woomera->event_queue = NULL;
woomera->q931_rel_cause_topbx=SIGBOOST_RELEASE_CAUSE_NORMAL;
woomera->q931_rel_cause_tosig=SIGBOOST_RELEASE_CAUSE_NORMAL;
woomera_set_interface(woomera, "w-1g-1");
}
return woomera;
}
static struct woomera_interface *new_woomera_interface(int socket, struct sockaddr_in *sock_addr, int len)
{
struct woomera_interface *woomera = NULL;
if (socket < 0) {
log_printf(0, server.log, "Critical: Invalid Socket on new interface!\n");
return NULL;
}
if ((woomera = alloc_woomera())) {
if (socket >= 0) {
no_nagle(socket);
woomera->socket = socket;
}
if (sock_addr && len) {
memcpy(&woomera->addr, sock_addr, len);
}
}
return woomera;
}
static char *woomera_message_header(struct woomera_message *wmsg, char *key)
{
int x = 0;
char *value = NULL;
for (x = 0 ; x < wmsg->last ; x++) {
if (!strcasecmp(wmsg->names[x], key)) {
value = wmsg->values[x];
break;
}
}
return value;
}
#if 1
static int waitfor_socket(int fd, int timeout, int flags)
{
struct pollfd pfds[1];
int res;
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = flags;
res = poll(pfds, 1, timeout);
if (res > 0) {
if(pfds[0].revents & POLLIN) {
res = 1;
} else if ((pfds[0].revents & POLLERR)) {
res = -1;
} else if ((pfds[0].revents & POLLNVAL)) {
res = -2;
#if 0
log_printf(0, server.log,"System Warning: Poll Event NVAL (0x%X) (fd=%i)!\n",
pfds[0].revents, fd);
#endif
} else {
#if 0
log_printf(0, server.log,"System Error: Poll Event Error no event (0x%X) (fd=%i)!\n",
pfds[0].revents,fd);
#endif
res = -1;
}
}
return res;
}
static int waitfor_tx_socket(int fd, int timeout, int flags)
{
struct pollfd pfds[1];
int res;
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = flags;
res = poll(pfds, 1, timeout);
if (res > 0) {
if (pfds[0].revents & POLLOUT) {
res = 1;
} else if ((pfds[0].revents & POLLERR)) {
res = -1;
} else if ((pfds[0].revents & POLLNVAL)) {
res = -2;
#if 0
log_printf(0, server.log,"System Warning: Poll Event NVAL (0x%X) (fd=%i)!\n",
pfds[0].revents, fd);
#endif
} else {
#if 0
log_printf(0, server.log,"System Error: Poll Event Error no event (0x%X) (fd=%i)!\n",
pfds[0].revents,fd);
#endif
res = -1;
}
}
return res;
}
#else
static int waitfor_socket(int fd, int timeout, int flags)
{
struct pollfd pfds[1];
int res;
int errflags = (POLLERR | POLLHUP | POLLNVAL);
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = flags | errflags;
res = poll(pfds, 1, timeout);
if (res > 0) {
if(pfds[0].revents & POLLIN) {
res = 1;
} else if ((pfds[0].revents & errflags)) {
res = -1;
} else {
#if 0
log_printf(0, server.log,"System Error: Poll Event Error no event (0x%X)!\n",
pfds[0].revents);
#endif
res = -1;
}
}
return res;
}
#endif
#if 1
static int waitfor_2sockets(int fda, int fdb, char *a, char *b, int timeout)
{
struct pollfd pfds[2];
int res = 0;
int errflags = (POLLERR | POLLHUP | POLLNVAL);
if (fda < 0 || fdb < 0) {
return -1;
}
*a=0;
*b=0;
memset(pfds, 0, sizeof(pfds));
pfds[0].fd = fda;
pfds[1].fd = fdb;
pfds[0].events = POLLIN | errflags;
pfds[1].events = POLLIN | errflags;
if ((res = poll(pfds, 2, timeout)) > 0) {
res = 1;
if ((pfds[0].revents & errflags) || (pfds[1].revents & errflags)) {
res = -1;
} else {
if ((pfds[0].revents & POLLIN)) {
*a=1;
res++;
}
if ((pfds[1].revents & POLLIN)) {
*b=1;
res++;
}
}
if (res == 1) {
/* No event found what to do */
res=-1;
}
}
return res;
}
#endif
static struct media_session *media_session_new(struct woomera_interface *woomera)
{
struct media_session *ms = NULL;
int x;
char *p;
int span,chan;
span=woomera->span;
chan=woomera->chan;
log_printf(2, server.log,"Starting new MEDIA session [%s] [%s]\n",
woomera->interface,woomera->raw?woomera->raw:"N/A");
if ((ms = malloc(sizeof(struct media_session)))) {
memset(ms, 0, sizeof(struct media_session));
if (woomera->loop_tdm != 1) {
for(x = 0; x < strlen(woomera->raw) ; x++) {
if (woomera->raw[x] == ':') {
break;
}
if (woomera->raw[x] == '/') {
break;
}
}
ms->ip = strndup(woomera->raw, x);
time(&ms->started);
p = woomera->raw + (x+1);
ms->port = atoi(p);
}
time(&ms->started);
woomera_set_ms(woomera,ms);
ms->woomera = woomera;
/* Setup artificial DTMF stuff */
memset(&ms->tone_session, 0, sizeof(ms->tone_session));
if (teletone_init_session(&ms->tone_session, 0, NULL, NULL)) {
log_printf(0, server.log, "ERROR: Failed to initialize TONE [w%ig%i]!\n",
span+1,chan+1);
}
ms->tone_session.rate = SMG_DTMF_RATE;
ms->tone_session.duration = server.dtmf_on * (ms->tone_session.rate / 1000);
ms->tone_session.wait = server.dtmf_off * (ms->tone_session.rate / 1000);
teletone_dtmf_detect_init (&ms->dtmf_detect, SMG_DTMF_RATE);
} else {
log_printf(0, server.log, "ERROR: Memory Alloc Failed [w%ig%i]!\n",
span+1,chan+1);
}
return ms;
}
static void media_session_free(struct media_session *ms)
{
if (ms->ip) {
free(ms->ip);
}
teletone_destroy_session(&ms->tone_session);
switch_buffer_destroy(&ms->dtmf_buffer);
ms->woomera = NULL;
free(ms);
}
static int create_udp_socket(struct media_session *ms, char *local_ip, int local_port, char *ip, int port)
{
int rc;
struct hostent *result, *local_result;
char buf[512], local_buf[512];
int err = 0;
log_printf(5,server.log,"LocalIP %s:%d IP %s:%d \n",local_ip, local_port, ip, port);
memset(&ms->remote_hp, 0, sizeof(ms->remote_hp));
memset(&ms->local_hp, 0, sizeof(ms->local_hp));
if ((ms->socket = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
gethostbyname_r(ip, &ms->remote_hp, buf, sizeof(buf), &result, &err);
gethostbyname_r(local_ip, &ms->local_hp, local_buf, sizeof(local_buf), &local_result, &err);
if (result && local_result) {
ms->remote_addr.sin_family = ms->remote_hp.h_addrtype;
memcpy((char *) &ms->remote_addr.sin_addr.s_addr, ms->remote_hp.h_addr_list[0], ms->remote_hp.h_length);
ms->remote_addr.sin_port = htons(port);
ms->local_addr.sin_family = ms->local_hp.h_addrtype;
memcpy((char *) &ms->local_addr.sin_addr.s_addr, ms->local_hp.h_addr_list[0], ms->local_hp.h_length);
ms->local_addr.sin_port = htons(local_port);
rc = bind(ms->socket, (struct sockaddr *) &ms->local_addr, sizeof(ms->local_addr));
if (rc < 0) {
close(ms->socket);
ms->socket = -1;
log_printf(5,server.log,
"Failed to bind LocalIP %s:%d IP %s:%d (%s)\n",
local_ip, local_port, ip, port,strerror(errno));
}
/* OK */
} else {
log_printf(0,server.log,
"Failed to get hostbyname LocalIP %s:%d IP %s:%d (%s)\n",
local_ip, local_port, ip, port,strerror(errno));
}
} else {
log_printf(0,server.log,
"Failed to create/allocate UDP socket\n");
}
return ms->socket;
}
static int next_media_port(void)
{
int port;
pthread_mutex_lock(&server.media_udp_port_lock);
port = ++server.next_media_port;
if (port > WOOMERA_MAX_MEDIA_PORT) {
server.next_media_port = WOOMERA_MIN_MEDIA_PORT;
port = WOOMERA_MIN_MEDIA_PORT;
}
pthread_mutex_unlock(&server.media_udp_port_lock);
return port;
}
static int woomera_dtmf_transmit(struct media_session *ms, int mtu)
{
struct woomera_interface *woomera = ms->woomera;
int bread;
unsigned char dtmf[1024];
unsigned char dtmf_law[1024];
sangoma_api_hdr_t hdrframe;
int i;
int slin_len = mtu*2;
short *data;
int used;
int res;
int err;
int txdtmf=0;
memset(&hdrframe,0,sizeof(hdrframe));
if (!ms->dtmf_buffer) {
return -1;
}
for (;;) {
if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) {
break;
}
res = waitfor_tx_socket(ms->sangoma_sock, -1, POLLERR | POLLOUT);
if (res <= 0) {
break;
}
#ifdef CODEC_LAW_DEFAULT
pthread_mutex_lock(&woomera->dtmf_lock);
if ((used=switch_buffer_inuse(ms->dtmf_buffer)) <= 0) {
pthread_mutex_unlock(&woomera->dtmf_lock);
break;
}
bread = switch_buffer_read(ms->dtmf_buffer, dtmf, slin_len);
pthread_mutex_unlock(&woomera->dtmf_lock);
if (bread <= 0) {
break;
}
log_printf(3,woomera->log,"%s: Write DTMF Got %d bytes MTU=%i Coding=%i Used=%i\n",
woomera->interface,bread,mtu,ms->hw_coding,used);
data=(short*)dtmf;
for (i=0;i<mtu;i++) {
if (ms->hw_coding) {
/* ALAW */
dtmf_law[i] = linear_to_alaw((int)data[i]);
} else {
/* ULAW */
dtmf_law[i] = linear_to_ulaw((int)data[i]);
}
}
err=sangoma_sendmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
dtmf_law, mtu, 0);
if (err != mtu) {
log_printf(0, woomera->log, "Error: Failed to TX to TDM API on DTMF (err=%i mtu=%i)!\n",err,mtu);
}
txdtmf++;
ms->skip_write_frames++;
#else
...
pthread_mutex_lock(&woomera->dtmf_lock);
bread = switch_buffer_read(ms->dtmf_buffer, dtmf, mtu);
pthread_mutex_unlock(&woomera->dtmf_lock);
log_printf(3,woomera->log,"%s: Write DTMF Got %d bytes\n",
woomera->interface,bread);
sangoma_sendmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
dtmf, mtu, 0);
txdtmf++;
ms->skip_write_frames++;
#endif
}
if (txdtmf) {
return 0;
} else {
return -1;
}
}
static void media_loop_run(struct media_session *ms)
{
struct woomera_interface *woomera = ms->woomera;
int sangoma_frame_len = 160;
int errs=0;
int res=0;
wanpipe_tdm_api_t tdm_api;
unsigned char circuit_frame[1024];
char filename[100];
FILE *filed=NULL;
int loops=0;
sangoma_api_hdr_t hdrframe;
memset(&hdrframe,0,sizeof(hdrframe));
memset(circuit_frame,0,sizeof(circuit_frame));
ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1);
log_printf(1, server.log, "Media Loop Started %s fd=%i\n",
woomera->interface,ms->sangoma_sock);
if (ms->sangoma_sock < 0) {
log_printf(0, server.log, "WANPIPE MEDIA Socket Error (%s) if=[%s] [w%ig%i]\n",
strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1);
errs++;
} else {
if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) {
errs++;
}
if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) {
errs++;
}
if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) {
errs++;
}
sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api);
sangoma_tdm_disable_hwec(ms->sangoma_sock,&tdm_api);
}
if (errs) {
log_printf(0, server.log, "Media Loop: failed to open tdm device %s\n",
woomera->interface);
return;
}
if (server.loop_trace) {
sprintf(filename,"/smg/w%ig%i-loop.trace",woomera->span+1,woomera->chan+1);
unlink(filename);
filed = safe_fopen(filename, "w");
}
while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
!woomera_test_flag(woomera, WFLAG_MEDIA_END) &&
((res = waitfor_socket(ms->sangoma_sock, 1000, POLLERR | POLLIN)) >= -2)) {
if (res == 0) {
//log_printf(4, server.log, "%s: TDM UDP Timeout !!!\n",
// woomera->interface);
/* NENAD Timeout thus just continue */
continue;
}
if (res == -2) {
close_socket(&ms->sangoma_sock);
ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1);
log_printf(0, server.log, "Media Loop Restart %s\n",
woomera->interface);
continue;
}
if (res < 0 ){
log_printf(0, server.log, "Media Loop Socket error %s\n",
woomera->interface);
break;
}
res = sangoma_readmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
circuit_frame,
sizeof(circuit_frame), 0);
if (res < 0) {
log_printf(0, server.log, "TDM Loop ReadMsg Error: %s\n",
strerror(errno), woomera->interface);
break;
}
if (server.loop_trace && filed != NULL) {
int i;
for (i=0;i<res;i++) {
fprintf(filed,"%02X ", circuit_frame[i]);
if (i && (i % 16) == 0) {
fprintf(filed,"\n");
}
}
fprintf(filed,"\n");
}
sangoma_sendmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
circuit_frame,
res, 0);
res=0;
loops++;
}
if (res < 0) {
log_printf(2, server.log, "Media Loop: socket error %s (fd=%i)!\n",
woomera->interface, ms->sangoma_sock);
}
if (server.loop_trace && filed != NULL) {
fclose(filed);
}
sangoma_tdm_enable_hwec(ms->sangoma_sock,&tdm_api);
sleep(1);
close_socket(&ms->sangoma_sock);
log_printf(1, server.log, "Media Loop Finished %s Master=%i MediaEnd=%i Loops=%i\n",
woomera->interface,
woomera_test_flag(&server.master_connection, WFLAG_RUNNING),
woomera_test_flag(woomera, WFLAG_MEDIA_END),loops);
return;
}
#ifdef WP_HPTDM_API
static int media_rx_ready(void *p, unsigned char *data, int len)
{
struct media_session *ms = (struct media_session *)p;
if (ms->udp_sock < 0) {
return -1;
}
return sendto(ms->udp_sock,
data,len, 0,
(struct sockaddr *) &ms->remote_addr,
sizeof(ms->remote_addr));
}
#endif
static void *media_thread_run(void *obj)
{
struct media_session *ms = obj;
struct woomera_interface *woomera = ms->woomera;
int sangoma_frame_len = 160;
int local_port, x = 0, errs = 0, res = 0, packet_len = 0;
//int udp_cnt=0;
struct woomera_event wevent;
wanpipe_tdm_api_t tdm_api;
FILE *tx_fd=NULL;
int sock_timeout=200;
if (woomera_test_flag(woomera, WFLAG_MEDIA_END) ||
!woomera->interface ||
woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(2, server.log,
"MEDIA session for [%s] Cancelled! (ptr=%p)\n",
woomera->interface,woomera);
/* In this case the call will be closed via woomera_thread_run
* function. And the process table will be cleard there */
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING);
media_session_free(ms);
pthread_exit(NULL);
return NULL;
}
log_printf(2, server.log, "MEDIA session for [%s] started (ptr=%p loop=%i)\n",
woomera->interface,woomera,woomera->loop_tdm);
if (woomera->loop_tdm) {
media_loop_run(ms);
ms->udp_sock=-1;
woomera_set_flag(woomera, WFLAG_HANGUP);
woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING);
goto media_thread_exit;
}
for(x = 0; x < 1000 ; x++) {
local_port = next_media_port();
if ((ms->udp_sock = create_udp_socket(ms, server.media_ip, local_port, ms->ip, ms->port)) > -1) {
break;
}
}
if (ms->udp_sock < 0) {
log_printf(0, server.log, "UDP Socket Error (%s) [%s] LocalPort=%d\n",
strerror(errno), woomera->interface, local_port);
errs++;
} else {
#ifdef WP_HPTDM_API
hp_tdm_api_span_t *span=hptdmspan[woomera->span+1];
if (!span || !span->init) {
errs++;
} else {
hp_tdm_api_usr_callback_t usr_callback;
memset(&usr_callback,0,sizeof(usr_callback));
usr_callback.p = ms;
usr_callback.rx_avail = media_rx_ready;
if (span->open_chan(span, &usr_callback, &ms->tdmchan,woomera->chan+1)) {
errs++;
}
}
#else
if ((ms->sangoma_sock = sangoma_create_socket_by_name(woomera->interface, NULL)) < 0) {
log_printf(0, server.log, "WANPIPE Socket Error (%s) if=[%s] [w%ig%i]\n",
strerror(errno), woomera->interface, woomera->span+1, woomera->chan+1);
errs++;
} else {
# ifdef CODEC_LAW_DEFAULT
if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_NONE) < 0 ) {
errs++;
}
# else
if (sangoma_tdm_set_codec(ms->sangoma_sock, &tdm_api, WP_SLINEAR) < 0 ) {
errs++;
}
# endif
if (sangoma_tdm_flush_bufs(ms->sangoma_sock, &tdm_api)) {
errs++;
}
if (sangoma_tdm_set_usr_period(ms->sangoma_sock, &tdm_api, 20) < 0 ) {
errs++;
}
# ifdef CODEC_LAW_DEFAULT
# ifdef LIBSANGOMA_GET_HWCODING
ms->hw_coding=sangoma_tdm_get_hw_coding(ms->sangoma_sock, &tdm_api);
if (ms->hw_coding < 0) {
errs++;
}
# else
# error "libsangoma missing hwcoding feature: not up to date!"
# endif
# endif
sangoma_frame_len = sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api);
}
#endif
}
#ifdef WP_HPTDM_API
/* No tdm thread */
#else
if (!errs &&
launch_media_tdm_thread(woomera)) {
errs++;
}
#endif
if (!errs) {
unsigned char udp_frame[4096];
sangoma_api_hdr_t hdrframe;
memset(&hdrframe,0,sizeof(hdrframe));
memset(udp_frame,0,sizeof(udp_frame));
#ifdef DOTRACE
int fdin, fdout;
char path_in[512], path_out[512];
#endif
#if 0
new_woomera_event_printf(&wevent,
"EVENT MEDIA %s AUDIO%s"
"Raw-Audio: %s:%d%s"
"Call-ID: %s%s"
"Raw-Format: %s%s"
,
woomera->interface,
WOOMERA_LINE_SEPERATOR,
server.media_ip,
local_port,
WOOMERA_LINE_SEPERATOR,
woomera->interface,
WOOMERA_LINE_SEPERATOR,
ms->hw_coding ?"ALAW":"ULAW",
WOOMERA_RECORD_SEPERATOR
);
#else
new_woomera_event_printf(&wevent,
"EVENT MEDIA %s AUDIO%s"
"Unique-Call-Id: %s%s"
"Raw-Audio: %s:%d%s"
"Call-ID: %s%s"
"Raw-Format: PCM-16%s"
,
woomera->interface,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
server.media_ip,
local_port,
WOOMERA_LINE_SEPERATOR,
woomera->interface,
WOOMERA_LINE_SEPERATOR,
WOOMERA_RECORD_SEPERATOR
);
#endif
enqueue_event(woomera, &wevent, EVENT_FREE_DATA);
#ifdef DOTRACE
sprintf(path_in, "/tmp/debug-in.%d.raw", tc);
sprintf(path_out, "/tmp/debug-out.%d.raw", tc++);
fdin = open(path_in, O_WRONLY | O_CREAT, O_TRUNC, 0600);
fdout = open(path_out, O_WRONLY | O_CREAT, O_TRUNC, 0600);
#endif
if (server.out_tx_test) {
tx_fd=fopen("/smg/sound.raw","rb");
if (!tx_fd){
log_printf(0,server.log, "FAILED TO OPEN Sound file!\n");
}
}
while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
!woomera_test_flag(woomera, WFLAG_MEDIA_END) &&
!woomera_test_flag(woomera, WFLAG_HANGUP) &&
(res = waitfor_socket(ms->udp_sock, sock_timeout, POLLERR | POLLIN)) >= 0) {
unsigned int fromlen = sizeof(struct sockaddr_in);
if (res == 0) {
if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) {
sock_timeout=(sangoma_frame_len/codec_sample);
} else {
sock_timeout=200;
}
if (ms->skip_write_frames > 0) {
ms->skip_write_frames--;
}
log_printf(4, server.log, "%s: UDP Sock Timeout !!!\n",
woomera->interface);
/* NENAD Timeout thus just continue */
continue;
}
if ((packet_len = recvfrom(ms->udp_sock, udp_frame, sizeof(udp_frame),
MSG_DONTWAIT, (struct sockaddr *) &ms->local_addr, &fromlen)) < 1) {
log_printf(2, server.log, "UDP Recv Error: %s\n",strerror(errno));
break;
}
#if 0
log_printf(6, server.log, "%s: UDP Receive %i !!!\n",
woomera->interface,packet_len);
#endif
if (packet_len > 0) {
#if 0
/* NC: This can cause skb_over panic must be retested */
if (packet_len != sangoma_frame_len && ms->udp_sync_cnt <= 5) {
/* Assume that we will always receive SLINEAR here */
sangoma_tdm_set_usr_period(ms->sangoma_sock,
&tdm_api, packet_len/codec_sample);
sangoma_frame_len =
sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api);
log_printf(3, server.log,
"%s: UDP TDM Period ReSync to Len=%i %ims (udp=%i) \n",
woomera->interface,sangoma_frame_len,
sangoma_frame_len/codec_sample,packet_len);
if (++ms->udp_sync_cnt >= 6) {
sangoma_tdm_set_usr_period(ms->sangoma_sock,
&tdm_api, 20);
sangoma_frame_len =
sangoma_tdm_get_usr_mtu_mru(ms->sangoma_sock,&tdm_api);
log_printf(0, server.log,
"%s: UDP TDM Period Force ReSync to 20ms \n",
woomera->interface);
}
}
#endif
if (!server.out_tx_test) {
if (woomera_dtmf_transmit(ms,sangoma_frame_len) == 0) {
sock_timeout=(sangoma_frame_len/codec_sample);
/* For sanity sake if we are doing the out test
* dont take any chances force tx udp data */
if (ms->skip_write_frames > 0) {
ms->skip_write_frames--;
}
continue;
} else {
sock_timeout=200;
}
if (ms->skip_write_frames > 0) {
ms->skip_write_frames--;
continue;
}
}
if (server.out_tx_test && tx_fd &&
fread((void*)udp_frame,
sizeof(char),
packet_len,tx_fd) <= 0) {
sangoma_get_full_cfg(ms->sangoma_sock,&tdm_api);
fclose(tx_fd);
tx_fd=NULL;
}
#ifdef WP_HPTDM_API
if (ms->tdmchan->push) {
ms->tdmchan->push(ms->tdmchan,udp_frame,packet_len);
}
#else
sangoma_sendmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
udp_frame,
packet_len, 0);
#endif
}
#if 0
if (woomera->span == 1 && woomera->chan == 1) {
udp_cnt++;
if (udp_cnt && udp_cnt % 1000 == 0) {
log_printf(0, server.log, "%s: MEDIA UDP TX RX CNT %i %i\n",
woomera->interface,udp_cnt,packet_len);
}
}
#endif
}
if (res < 0) {
log_printf(2, server.log, "Media Thread: socket error !\n");
}
}
new_woomera_event_printf(&wevent,
"EVENT HANGUP %s%s"
"Unique-Call-Id: %s%s"
"Start-Time: %ld%s"
"End-Time: %ld%s"
"Answer-Time: %ld%s"
"Call-ID: %s%s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
woomera->interface,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
time(&ms->started),
WOOMERA_LINE_SEPERATOR,
time(NULL),
WOOMERA_LINE_SEPERATOR,
time(&ms->answered),
WOOMERA_LINE_SEPERATOR,
woomera->interface,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(woomera->q931_rel_cause_topbx),
WOOMERA_LINE_SEPERATOR,
woomera->q931_rel_cause_topbx,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
media_thread_exit:
if (woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) {
woomera_set_flag(woomera, WFLAG_MEDIA_END);
while(woomera_test_flag(woomera, WFLAG_MEDIA_TDM_RUNNING)) {
usleep(1000);
sched_yield();
}
}
close_socket(&ms->udp_sock);
close_socket(&ms->sangoma_sock);
if (tx_fd){
fclose(tx_fd);
tx_fd=NULL;
}
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_set_ms(woomera,NULL);
woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING);
media_session_free(ms);
log_printf(2, server.log, "MEDIA session for [%s] ended (ptr=%p)\n",
woomera->interface,woomera);
pthread_exit(NULL);
return NULL;
}
static void *media_tdm_thread_run(void *obj)
{
struct media_session *ms = obj;
struct woomera_interface *woomera = ms->woomera;
int res = 0;
//int tdm_cnt=0;
//wanpipe_tdm_api_t tdm_api;
unsigned char circuit_frame[1024];
sangoma_api_hdr_t hdrframe;
memset(&hdrframe,0,sizeof(hdrframe));
memset(circuit_frame,0,sizeof(circuit_frame));
if (woomera_test_flag(woomera, WFLAG_MEDIA_END) || !woomera->interface) {
log_printf(2, server.log, "MEDIA TDM session for [%s] Cancelled! (ptr=%p)\n",
woomera->interface,woomera);
/* In this case the call will be closed via woomera_thread_run
* function. And the process table will be cleard there */
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING);
pthread_exit(NULL);
return NULL;
}
log_printf(2, server.log, "MEDIA TDM session for [%s] started (ptr=%p)\n",
woomera->interface,woomera);
while ( woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
!woomera_test_flag(woomera, WFLAG_MEDIA_END) &&
(res = waitfor_socket(ms->sangoma_sock, 1000, POLLERR | POLLIN)) >= -2) {
if (res == 0) {
//log_printf(4, server.log, "%s: TDM UDP Timeout !!!\n",
// woomera->interface);
/* NENAD Timeout thus just continue */
continue;
} else if (res == -2) {
close_socket(&ms->sangoma_sock);
ms->sangoma_sock = sangoma_open_tdmapi_span_chan(woomera->span+1, woomera->chan+1);
if (ms->sangoma_sock < 0) {
log_printf(0, server.log, "Media TDM Restart Failed%s\n",
woomera->interface);
break;
}
log_printf(0, server.log, "Media TDM Restart %s\n",
woomera->interface);
continue;
} else if (res < 0) {
log_printf(0, server.log, "Media TDM Sangoma Socket Error %s\n",
woomera->interface);
break;
}
res = sangoma_readmsg_socket(ms->sangoma_sock,
&hdrframe,
sizeof(hdrframe),
circuit_frame,
sizeof(circuit_frame), 0);
if (res < 0) {
log_printf(0, server.log, "TDM ReadMsg Error: %s\n", strerror(errno));
break;
}
res = sendto(ms->udp_sock,
circuit_frame,
res, 0,
(struct sockaddr *) &ms->remote_addr,
sizeof(ms->remote_addr));
if (res < 0) {
log_printf(2, server.log, "UDP Sento Error: %s\n", strerror(errno));
break;
}
#if 0
if (woomera->span == 1 && woomera->chan == 1) {
tdm_cnt++;
if (tdm_cnt && tdm_cnt % 1000 == 0) {
log_printf(0, server.log, "%s: MEDIA TDM TX RX CNT %i %i\n",
woomera->interface,tdm_cnt,res);
}
}
#endif
}
if (res < 0) {
log_printf(2, server.log, "Media TDM Thread: socket error !\n");
}
log_printf(2, server.log, "MEDIA TDM session for [%s] ended (ptr=%p)\n",
woomera->interface,woomera);
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING);
pthread_exit(NULL);
return NULL;
}
/* This function must be called with process_lock
* because it modifies shared process_table */
static int launch_media_thread(struct woomera_interface *woomera)
{
pthread_attr_t attr;
int result = -1;
struct media_session *ms;
if ((ms = media_session_new(woomera))) {
result = pthread_attr_init(&attr);
//pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
//pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, MGD_STACK_SIZE);
woomera_set_flag(woomera, WFLAG_MEDIA_RUNNING);
result = pthread_create(&ms->thread, &attr, media_thread_run, ms);
if (result) {
log_printf(0, server.log, "%s: Error: Creating Thread! %s\n",
__FUNCTION__,strerror(errno));
woomera_clear_flag(woomera, WFLAG_MEDIA_RUNNING);
media_session_free(woomera->ms);
}
pthread_attr_destroy(&attr);
} else {
log_printf(0, server.log, "Failed to start new media session\n");
}
return result;
}
static int launch_media_tdm_thread(struct woomera_interface *woomera)
{
pthread_attr_t attr;
int result = -1;
struct media_session *ms = woomera_get_ms(woomera);
if (!ms) {
return result;
}
result = pthread_attr_init(&attr);
//pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
//pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, MGD_STACK_SIZE);
woomera_set_flag(woomera, WFLAG_MEDIA_TDM_RUNNING);
result = pthread_create(&ms->thread, &attr, media_tdm_thread_run, ms);
if (result) {
log_printf(0, server.log, "%s: Error: Creating Thread! %s\n",
__FUNCTION__,strerror(errno));
woomera_clear_flag(woomera, WFLAG_MEDIA_TDM_RUNNING);
}
pthread_attr_destroy(&attr);
return result;
}
static struct woomera_interface * launch_woomera_loop_thread(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
char callid[20];
sprintf(callid, "w%dg%d", event->span+1,event->chan+1);
if ((woomera = alloc_woomera())) {
woomera->chan = event->chan;
woomera->span = event->span;
woomera->log = server.log;
woomera->debug = server.debug;
woomera->call_id = 1;
woomera->event_queue = NULL;
woomera->loop_tdm=1;
} else {
log_printf(0, server.log, "Critical ERROR: memory/socket error\n");
return NULL;
}
woomera_set_interface(woomera,callid);
pthread_mutex_lock(&server.process_lock);
server.process_table[event->span][event->chan].dev = woomera;
pthread_mutex_unlock(&server.process_lock);
if (launch_woomera_thread(woomera)) {
pthread_mutex_lock(&server.process_lock);
server.process_table[event->span][event->chan].dev = NULL;
memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
free(woomera);
log_printf(0, server.log, "Critical ERROR: memory/socket error\n");
return NULL;
}
return woomera;
}
static int woomera_message_parse(struct woomera_interface *woomera, struct woomera_message *wmsg, int timeout)
{
char *cur, *cr, *next = NULL, *eor = NULL;
char buf[2048];
int res = 0, bytes = 0, sanity = 0;
struct timeval started, ended;
int elapsed, loops = 0;
int failto = 0;
int packet = 0;
memset(wmsg, 0, sizeof(*wmsg));
if (woomera->socket < 0 ) {
log_printf(2, woomera->log, WOOMERA_DEBUG_PREFIX "%s Invalid Socket! %d\n",
woomera->interface,woomera->socket);
return -1;
}
if (woomera_test_flag(woomera, WFLAG_MEDIA_END) ||
woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(5, woomera->log, WOOMERA_DEBUG_PREFIX
"%s MEDIA END or HANGUP !\n",
woomera->interface);
return -1;
}
gettimeofday(&started, NULL);
memset(buf, 0, sizeof(buf));
if (timeout < 0) {
timeout = abs(timeout);
failto = 1;
} else if (timeout == 0) {
timeout = -1;
}
while (!(eor = strstr(buf, WOOMERA_RECORD_SEPERATOR))) {
if (sanity > 1000) {
log_printf(2, woomera->log, WOOMERA_DEBUG_PREFIX "%s Failed Sanity Check!\n[%s]\n\n", woomera->interface, buf);
return -1;
}
if ((res = waitfor_socket(woomera->socket, 1000, POLLERR | POLLIN) > 0)) {
res = recv(woomera->socket, buf, sizeof(buf), MSG_PEEK);
if (res > 1) {
packet++;
}
if (!strncmp(buf, WOOMERA_LINE_SEPERATOR, 2)) {
res = read(woomera->socket, buf, 2);
return 0;
}
if (res == 0) {
sanity++;
/* Looks Like it's time to go! */
if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) ||
woomera_test_flag(woomera, WFLAG_MEDIA_END) ||
woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(5, woomera->log, WOOMERA_DEBUG_PREFIX
"%s MEDIA END or HANGUP \n", woomera->interface);
return -1;
}
ysleep(1000);
continue;
} else if (res < 0) {
log_printf(3, woomera->log, WOOMERA_DEBUG_PREFIX
"%s error during packet retry (err=%i) Loops#%d (%s)\n",
woomera->interface, res, loops,
strerror(errno));
return res;
} else if (loops) {
ysleep(100000);
}
}
gettimeofday(&ended, NULL);
elapsed = (((ended.tv_sec * 1000) + ended.tv_usec / 1000) - ((started.tv_sec * 1000) + started.tv_usec / 1000));
if (res < 0) {
log_printf(2, woomera->log, WOOMERA_DEBUG_PREFIX "%s Bad RECV\n",
woomera->interface);
return res;
} else if (res == 0) {
sanity++;
/* Looks Like it's time to go! */
if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) ||
woomera_test_flag(woomera, WFLAG_MEDIA_END) ||
woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(5, woomera->log, WOOMERA_DEBUG_PREFIX
"%s MEDIA END or HANGUP \n", woomera->interface);
return -1;
}
ysleep(1000);
continue;
}
if (packet && loops > 150) {
log_printf(1, woomera->log, WOOMERA_DEBUG_PREFIX
"%s Timeout waiting for packet.\n",
woomera->interface);
return -1;
}
if (timeout > 0 && (elapsed > timeout)) {
log_printf(1, woomera->log, WOOMERA_DEBUG_PREFIX
"%s Timeout [%d] reached\n",
woomera->interface, timeout);
return failto ? -1 : 0;
}
if (woomera_test_flag(woomera, WFLAG_EVENT)) {
/* BRB! we have an Event to deliver....*/
return 0;
}
/* what're we still doing here? */
if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) ||
!woomera_test_flag(woomera, WFLAG_RUNNING)) {
log_printf(2, woomera->log, WOOMERA_DEBUG_PREFIX
"%s MASTER RUNNING or RUNNING!\n", woomera->interface);
return -1;
}
loops++;
}
*eor = '\0';
bytes = strlen(buf) + 4;
memset(buf, 0, sizeof(buf));
res = read(woomera->socket, buf, bytes);
next = buf;
if (woomera->debug > 1) {
log_printf(3, woomera->log, "%s:WOOMERA RX MSG: %s\n",woomera->interface,buf);
}
while ((cur = next)) {
if ((cr = strstr(cur, WOOMERA_LINE_SEPERATOR))) {
*cr = '\0';
next = cr + (sizeof(WOOMERA_LINE_SEPERATOR) - 1);
if (!strcmp(next, WOOMERA_RECORD_SEPERATOR)) {
break;
}
}
if (!cur || !*cur) {
break;
}
if (!wmsg->last) {
char *cmd, *id, *args;
woomera_set_flag(wmsg, MFLAG_EXISTS);
cmd = cur;
if ((id = strchr(cmd, ' '))) {
*id = '\0';
id++;
if ((args = strchr(id, ' '))) {
*args = '\0';
args++;
strncpy(wmsg->command_args, args, sizeof(wmsg->command_args)-1);
}
strncpy(wmsg->callid, id, sizeof(wmsg->callid)-1);
}
strncpy(wmsg->command, cmd, sizeof(wmsg->command)-1);
} else {
char *name, *val;
name = cur;
if ((val = strchr(name, ':'))) {
*val = '\0';
val++;
while (*val == ' ') {
*val = '\0';
val++;
}
strncpy(wmsg->values[wmsg->last-1], val, WOOMERA_STRLEN);
}
strncpy(wmsg->names[wmsg->last-1], name, WOOMERA_STRLEN);
if (name && val && !strcasecmp(name, "content-type")) {
woomera_set_flag(wmsg, MFLAG_CONTENT);
bytes = atoi(val);
}
if (name && val && !strcasecmp(name, "content-length")) {
woomera_set_flag(wmsg, MFLAG_CONTENT);
bytes = atoi(val);
}
}
wmsg->last++;
}
wmsg->last--;
if (bytes && woomera_test_flag(wmsg, MFLAG_CONTENT)) {
read(woomera->socket, wmsg->body,
(bytes > sizeof(wmsg->body)) ? sizeof(wmsg->body) : bytes);
}
return woomera_test_flag(wmsg, MFLAG_EXISTS);
}
static struct woomera_interface *pull_from_holding_tank(int index, int span , int chan)
{
struct woomera_interface *woomera = NULL;
if (index < 1 || index >= CORE_TANK_LEN) {
if (index != 0) {
log_printf(0, server.log, "%s Error on invalid TANK INDEX = %i\n",
__FUNCTION__,index);
}
return NULL;
}
pthread_mutex_lock(&server.ht_lock);
if (server.holding_tank[index] &&
server.holding_tank[index] != &woomera_dead_dev) {
woomera = server.holding_tank[index];
/* Block this index until the call is completed */
server.holding_tank[index] = &woomera_dead_dev;
woomera->timeout = 0;
woomera->index = 0;
woomera->span=span;
woomera->chan=chan;
}
pthread_mutex_unlock(&server.ht_lock);
return woomera;
}
static void clear_from_holding_tank(int index, struct woomera_interface *woomera)
{
if (index < 1 || index >= CORE_TANK_LEN) {
if (index != 0) {
log_printf(0, server.log, "%s Error on invalid TANK INDEX = %i\n",
__FUNCTION__,index);
}
return;
}
pthread_mutex_lock(&server.ht_lock);
if (server.holding_tank[index] == &woomera_dead_dev) {
server.holding_tank[index] = NULL;
} else if (woomera && server.holding_tank[index] == woomera) {
server.holding_tank[index] = NULL;
} else if (server.holding_tank[index]) {
log_printf(0, server.log, "Error: Holding tank index %i not cleared %p !\n",
index, server.holding_tank[index]);
}
pthread_mutex_unlock(&server.ht_lock);
return;
}
static struct woomera_interface *peek_from_holding_tank(int index)
{
struct woomera_interface *woomera = NULL;
if (index < 1 || index >= CORE_TANK_LEN) {
if (index != 0) {
log_printf(0, server.log, "%s Error on invalid TANK INDEX = %i\n",
__FUNCTION__,index);
}
return NULL;
}
pthread_mutex_lock(&server.ht_lock);
if (server.holding_tank[index] &&
server.holding_tank[index] != &woomera_dead_dev) {
woomera = server.holding_tank[index];
}
pthread_mutex_unlock(&server.ht_lock);
return woomera;
}
static int add_to_holding_tank(struct woomera_interface *woomera)
{
int next, i, found=0;
pthread_mutex_lock(&server.ht_lock);
for (i=0;i<CORE_TANK_LEN;i++) {
next = ++server.holding_tank_index;
if (server.holding_tank_index >= CORE_TANK_LEN) {
next = server.holding_tank_index = 1;
}
if (next == 0) {
log_printf(0, server.log, "\nCritical Error on TANK INDEX == 0\n");
continue;
}
if (server.holding_tank[next]) {
continue;
}
found=1;
break;
}
if (!found) {
/* This means all tank vales are busy
* should never happend */
pthread_mutex_unlock(&server.ht_lock);
log_printf(0, server.log, "\nCritical Error failed to obtain a TANK INDEX\n");
return 0;
}
server.holding_tank[next] = woomera;
woomera->timeout = time(NULL) + 100;
pthread_mutex_unlock(&server.ht_lock);
return next;
}
static int handle_woomera_media_accept_answer(struct woomera_interface *woomera,
struct woomera_message *wmsg,
int media, int answer, int accept)
{
char *raw = woomera_message_header(wmsg, "raw-audio");
log_printf(4, woomera->log, "WOOMERA CMD: %s [%s]\n",
wmsg->command, woomera->interface);
if (woomera_test_flag(woomera, WFLAG_HANGUP) ||
!woomera_test_flag(woomera, WFLAG_RUNNING) ||
woomera_test_flag(woomera, WFLAG_MEDIA_END)) {
log_printf(2, server.log,
"ERROR! call was cancelled MEDIA on HANGUP or MEDIA END!\n");
woomera->timeout=0;
return -1;
}
log_printf(3, server.log,"WOOMERA: GOT %s EVENT: [%s] RAW=%s\n",
wmsg->command,wmsg->callid,raw);
if (raw &&
woomera->raw == NULL &&
!woomera_test_flag(woomera, WFLAG_RAW_MEDIA_STARTED)) {
woomera_set_flag(woomera, WFLAG_RAW_MEDIA_STARTED);
woomera_set_raw(woomera, raw);
if (launch_media_thread(woomera)) {
struct woomera_event wevent;
log_printf(4, server.log,"ERROR: Failed to Launch Call [%s]\n",
woomera->interface);
#if 0
new_woomera_event_printf(&wevent, "501 call was cancelled!%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
#endif
new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s"
"Unique-Call-Id: %s%s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
wmsg->callid,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(21),
WOOMERA_LINE_SEPERATOR,
21,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_RUNNING);
woomera->timeout=0;
return -1;
}
}
if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) {
log_printf(0, server.log,"ERROR! Monitor Thread not running!\n");
} else {
if (accept) {
if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) {
woomera_set_flag(woomera,WFLAG_CALL_ACKED);
isup_exec_command(woomera->span,
woomera->chan,
-1,
SIGBOOST_EVENT_CALL_START_ACK,
0);
}
}
if (answer) {
struct media_session *ms;
pthread_mutex_lock(&woomera->ms_lock);
if ((ms=woomera->ms)) {
time(&woomera->ms->answered);
}
pthread_mutex_unlock(&woomera->ms_lock);
if (ms) {
if (!autoacm && !woomera_test_flag(woomera,WFLAG_CALL_ACKED)) {
woomera_set_flag(woomera,WFLAG_CALL_ACKED);
isup_exec_command(woomera->span,
woomera->chan,
-1,
SIGBOOST_EVENT_CALL_START_ACK,
0);
}
isup_exec_command(woomera->span,
woomera->chan,
-1,
SIGBOOST_EVENT_CALL_ANSWERED,
0);
log_printf(2, server.log,
"Sent SIGBOOST_EVENT_CALL_ANSWERED [w%dg%d]\n",
woomera->span+1,woomera->chan+1);
} else {
struct woomera_event wevent;
log_printf(0, server.log,
"WOOMERA ANSWER: FAILED [%s] no Media \n",
wmsg->command,wmsg->callid);
new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s"
"Unique-Call-Id: %s%s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
wmsg->callid,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(21),
WOOMERA_LINE_SEPERATOR,
21,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_RUNNING);
woomera->timeout=0;
return -1;
}
}
}
{
struct woomera_event wevent;
new_woomera_event_printf(&wevent, "200 %s OK%s"
"Unique-Call-Id: %s%s",
answer ? "ANSWER" :
accept ? "ACCEPT" : "MEDIA",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
}
return 0;
}
static int handle_woomera_call_start (struct woomera_interface *woomera,
struct woomera_message *wmsg)
{
char *raw = woomera_message_header(wmsg, "raw-audio");
call_signal_event_t event;
char *calling = woomera_message_header(wmsg, "local-number");
#ifdef SMG_CALLING_NAME
char *calling_name = woomera_message_header(wmsg, "local-name");
#endif
char *presentation = woomera_message_header(wmsg, "Presentation");
char *screening = woomera_message_header(wmsg, "Screening");
char *rdnis = woomera_message_header(wmsg, "RDNIS");
char *called = wmsg->callid;
char *grp = wmsg->callid;
char *p;
int cause = 34;
int tg = 0;
if (smg_check_all_busy()) {
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket,
"405 SMG Server All Ckt Busy!%s", WOOMERA_RECORD_SEPERATOR);
log_printf(3, woomera->log, "SMG Server Full %d (ckt busy cnt=%i)\n",
server.call_count, server.all_ckt_busy);
return -1;
}
log_printf(2, woomera->log, "New Call %d/%d\n", server.call_count, server.max_calls);
if ((p = strchr(called, '/'))) {
*p = '\0';
called = p+1;
tg = atoi(grp+1) - 1;
if (tg < 0) {
tg=0;
}
}
woomera->trunk_group=tg;
if (raw) {
woomera_set_raw(woomera, raw);
}
woomera->index = add_to_holding_tank(woomera);
if (woomera->index < 1) {
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket,
"405 SMG Server All Tanks Busy!%s",
WOOMERA_RECORD_SEPERATOR);
log_printf(0, woomera->log, "Error: Call Tank Full (Call Cnt=%i)\n",
server.call_count);
return -1;
}
woomera->index_hold = woomera->index;
call_signal_call_init(&event, calling, called, woomera->index);
if (presentation) {
event.calling_number_presentation = atoi(presentation);
} else {
event.calling_number_presentation = 0;
}
if (screening) {
event.calling_number_screening_ind = atoi(screening);
} else {
event.calling_number_screening_ind = 0;
}
if (rdnis && strlen(rdnis)) {
strncpy((char*)event.redirection_string,rdnis,
sizeof(event.redirection_string)-1);
log_printf(3,server.log,"RDNIS %s\n", rdnis);
}
#ifdef SMG_CALLING_NAME
if (calling_name) {
strncpy((char*)event.calling_name,calling_name,
sizeof(event.calling_name)-1);
}
#endif
event.trunk_group = tg;
if (call_signal_connection_write(&server.mcon, &event) <= 0) {
log_printf(0, server.log,
"Critical System Error: Failed to tx on ISUP socket [%s]: %s\n",
strerror(errno));
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket,
"405 SMG Signalling Contestion!%s",
WOOMERA_RECORD_SEPERATOR);
return -1;
}
socket_printf(woomera->socket, "100 Trying%s", WOOMERA_RECORD_SEPERATOR);
log_printf(2, server.log, "Call Called Event [Setup ID: %d] TG=%d\n",
woomera->index,tg);
return 0;
}
static void interpret_command(struct woomera_interface *woomera, struct woomera_message *wmsg)
{
int answer = 0, media = 0, accept=0;
char *unique_id;
int cause=0;
if (!strcasecmp(wmsg->command, "call")) {
int err;
if (strlen(woomera->session) != 0) {
/* Call has already been placed */
socket_printf(woomera->socket, "400 Error Call already in progress %s",
WOOMERA_RECORD_SEPERATOR);
log_printf(0,server.log,"Woomera RX Call Even while call in progress!\n");
return;
}
err=handle_woomera_call_start(woomera,wmsg);
if (err) {
woomera_set_flag(woomera, WFLAG_HANGUP);
}
return;
} else if (!strcasecmp(wmsg->command, "bye") || !strcasecmp(wmsg->command, "quit")) {
char *cause = woomera_message_header(wmsg, "cause");
char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code");
if (cause) {
log_printf(3, woomera->log, "Bye Cause Received: [%s]\n", cause);
}
if (q931cause && atoi(q931cause)) {
woomera_set_cause_tosig(woomera,atoi(q931cause));
}
log_printf(2, woomera->log, "WOOMERA CMD: Bye Received: [%s]\n", woomera->interface);
woomera_clear_flag(woomera, WFLAG_RUNNING);
socket_printf(woomera->socket, "200 Connection closed%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
return;
} else if (!strcasecmp(wmsg->command, "listen")) {
if (woomera_test_flag(woomera, WFLAG_LISTENING)) {
socket_printf(woomera->socket, "405 Listener already started%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
} else {
woomera_set_flag(woomera, WFLAG_LISTENING);
add_listener(woomera);
if (!strcmp(wmsg->callid,"MASTER")) {
woomera_set_flag(woomera, WFLAG_MASTER_DEV);
log_printf(0,woomera->log, "Starting MASTER Listen Device!\n");
master_reset=0;
}
socket_printf(woomera->socket, "%s",
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "200 Listener enabled%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
}
return;
} else if ((media = !strcasecmp(wmsg->command, "debug"))) {
int debug_level=atoi(wmsg->callid);
if (debug_level < 10) {
server.debug=debug_level;
log_printf(0,server.log,"SMG Debugging set to %i (window=%i)\n",server.debug,server.mcon.txwindow);
}
return;
}
unique_id = woomera_message_header(wmsg, "Unique-Call-Id");
if (!unique_id) {
cause=111;
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "400 Woomera cmd without uniquie id%s"
WOOMERA_RECORD_SEPERATOR);
log_printf(2,server.log,"Woomera RX Event (%s) without unique id!\n",wmsg->command);
woomera_set_flag(woomera, WFLAG_HANGUP);
return;
}
if (strlen(woomera->session) == 0) {
struct woomera_interface *session_woomera=NULL;
char *session=NULL;
int span, chan;
char ifname[100];
/* If session does not exist this is an incoming call */
sscanf(unique_id, "w%dg%d", &span, &chan);
span--;
chan--;
log_printf(3, woomera->log,
"WOOMERA Got CMD %s Span=%d Chan=%d from session %s\n",
wmsg->command,span,chan,unique_id);
if (smg_validate_span_chan(span,chan) != 0) {
cause=34;
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "404 Invalid span/chan in session%s"
WOOMERA_RECORD_SEPERATOR);
log_printf(2, woomera->log,
"WOOMERA Warning invalid span chan in session %s %s\n",
wmsg->command,unique_id);
woomera_set_flag(woomera, WFLAG_HANGUP);
return;
}
pthread_mutex_lock(&server.process_lock);
session = server.process_table[span][chan].session;
session_woomera = server.process_table[span][chan].dev;
if (session_woomera) {
pthread_mutex_unlock(&server.process_lock);
cause=34;
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "404 Session not found%s"
WOOMERA_RECORD_SEPERATOR);
log_printf(0, woomera->log, "WOOMERA Error channel in use %s %s\n",
wmsg->command,unique_id);
woomera_set_flag(woomera, WFLAG_HANGUP);
return;
}
if (!session || strlen(session) == 0 ||
strncmp(session,unique_id,sizeof(woomera->session))){
pthread_mutex_unlock(&server.process_lock);
cause=34;
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "404 Invalid/Expired Session%s"
WOOMERA_RECORD_SEPERATOR);
log_printf(3, woomera->log,
"WOOMERA Warning: Cmd=%s with invalid session %s (orig=%s)\n",
wmsg->command,unique_id,session?session:"N/A");
woomera_set_flag(woomera, WFLAG_HANGUP);
return;
}
server.process_table[span][chan].dev=woomera;
strncpy(woomera->session,unique_id,sizeof(woomera->session));
sprintf(ifname,"w%dg%d",span+1,chan+1);
woomera_set_interface(woomera, ifname);
woomera->span=span;
woomera->chan=chan;
pthread_mutex_unlock(&server.process_lock);
log_printf(3, woomera->log, "WOOMERA Got New If=%s Session %s\n",
woomera->interface, woomera->session);
} else if (strncmp(woomera->session,unique_id,sizeof(woomera->session))) {
cause=34;
socket_printf(woomera->socket, "EVENT HANGUP %s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(cause),
WOOMERA_LINE_SEPERATOR,
cause,
WOOMERA_RECORD_SEPERATOR);
socket_printf(woomera->socket, "404 Session Mis-match%s"
WOOMERA_RECORD_SEPERATOR);
woomera_set_flag(woomera, WFLAG_HANGUP);
return;
}
if (!strcasecmp(wmsg->command, "dtmf")) {
log_printf(3, woomera->log, "WOOMERA CMD: DTMF Received: [%s] Digit %s Body %s\n",
woomera->interface, wmsg->command_args, wmsg->body);
wanpipe_send_dtmf(woomera,wmsg->body);
socket_printf(woomera->socket, "200 DTMF OK%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
} else if (!strcasecmp(wmsg->command, "hangup")) {
int chan = -1, span = -1;
char *cause = woomera_message_header(wmsg, "cause");
char *q931cause = woomera_message_header(wmsg, "Q931-Cause-Code");
if (q931cause && atoi(q931cause)) {
woomera_set_cause_tosig(woomera,atoi(q931cause));
}
span=woomera->span;
chan=woomera->chan;
log_printf(3, woomera->log, "WOOMERA CMD: Hangup Received: [%s] MEDIA EXIST Cause=%s\n",
woomera->interface,cause);
if (smg_validate_span_chan(span,chan) != 0) {
socket_printf(woomera->socket, "405 No Such Channel%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
return;
}
log_printf(2, woomera->log, "Hangup Received: [w%dg%d]\n",
span+1,chan+1);
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_RUNNING);
socket_printf(woomera->socket, "200 HANGUP OK%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
} else if (!strcasecmp(wmsg->command, "proceed")) {
log_printf(3, woomera->log, "WOOMERA CMD: %s [%s]\n",
wmsg->command, woomera->interface);
socket_printf(woomera->socket,
"200 %s PROCEED OK%s"
"Unique-Call-Id: %s%s",
wmsg->callid,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
} else if ((media = !strcasecmp(wmsg->command, "media")) ||
(answer = !strcasecmp(wmsg->command, "answer")) ||
(accept = !strcasecmp(wmsg->command, "accept"))) {
handle_woomera_media_accept_answer(woomera, wmsg, media,answer,accept);
} else {
log_printf(0, server.log,"WOOMERA INVALID EVENT: %s [%s] \n",
wmsg->command,wmsg->callid);
socket_printf(woomera->socket, "501 Command '%s' not implemented%s",
wmsg->command, WOOMERA_RECORD_SEPERATOR);
}
}
/*
EVENT INCOMING 1
Remote-Address: 10.3.3.104
Remote-Number:
Remote-Name: Anthony Minessale!8668630501
Protocol: H.323
User-Agent: Post Increment Woomera 1.0alpha1 (OpenH323 v1.17.2) 9/61
H323-Call-Id: 887b1ff8-bb1f-da11-85c0-0007e98988c4
Local-Number: 996
Start-Time: Fri, 09 Sep 2005 12:25:14 -0400
Local-Name: root
*/
static void handle_call_answer(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
int kill = 0;
pthread_mutex_lock(&server.process_lock);
woomera = server.process_table[event->span][event->chan].dev;
pthread_mutex_unlock(&server.process_lock);
if (woomera) {
char callid[80];
struct woomera_event wevent;
if (!woomera->raw) {
log_printf(1, server.log, "Refusing to answer call with no media!\n");
kill++;
goto handle_call_answer_end;
}
if (woomera_test_flag(woomera, WFLAG_ANSWER)) {
log_printf(1, server.log, "Refusing to double-answer a call!\n");
kill++;
goto handle_call_answer_end;
}
woomera_set_flag(woomera, WFLAG_ANSWER);
if (woomera->span != event->span || woomera->chan != event->chan) {
log_printf(1, server.log, "Refusing to start media on a different channel from the one we agreed on.!\n");
kill++;
goto handle_call_answer_end;
}
if (woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(1, server.log, "Refusing to answer a dead call!\n");
kill++;
} else {
int err;
err=0;
sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1);
woomera_set_interface(woomera, callid);
#ifndef WOOMERA_EARLY_MEDIA
err=launch_media_thread(woomera);
if (err) {
log_printf(0, server.log,"ERROR: Failed to Launch Call [%s]\n",
woomera->interface);
#if 0
new_woomera_event_printf(&wevent, "501 call was cancelled!%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
#endif
new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s"
"Unique-Call-Id: %s%s"
"Q931-Cause-Code: %d%s"
"Cause: %s%s",
woomera->interface,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
21,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(21),
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
kill++;
}
#endif
if (!kill) {
new_woomera_event_printf(&wevent, "EVENT CONNECT w%dg%d%s"
"Unique-Call-Id: %s%s",
event->span+1,
event->chan+1,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
}
}
} else {
log_printf(1, server.log, "Answer requested on non-existant session. [w%dg%d]\n",
event->span+1, event->chan+1);
kill++;
}
handle_call_answer_end:
if (kill) {
if (woomera) {
woomera_set_flag(woomera,WFLAG_MEDIA_END);
} else {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED,
SIGBOOST_RELEASE_CAUSE_NORMAL);
log_printf(1, server.log, "Sent CALL STOP to Answer without session [w%dg%d]\n",
event->span+1, event->chan+1);
}
}
}
static void handle_call_start_ack(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
struct woomera_event wevent;
int kill = 0;
if ((woomera = peek_from_holding_tank(event->call_setup_id))) {
char callid[80];
if (woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(1, server.log, "Refusing to ack a dead call!\n");
kill++;
} else {
int err, span, chan;
pull_from_holding_tank(event->call_setup_id,event->span,event->chan);
sprintf(callid, "w%dg%d", event->span + 1, event->chan + 1);
span = event->span;
chan = event->chan;
woomera_set_flag(woomera,WFLAG_CALL_ACKED);
woomera_set_interface(woomera, callid);
pthread_mutex_lock(&server.process_lock);
server.process_table[span][chan].dev = woomera;
sprintf(woomera->session,"%s-%i-%i",callid,rand(),rand());
sprintf(server.process_table[span][chan].session,"%s-%s",
callid,woomera->session);
pthread_mutex_unlock(&server.process_lock);
#ifdef WOOMERA_EARLY_MEDIA
err=launch_media_thread(woomera);
if (err) {
log_printf(0, server.log,"ERROR: Failed to Launch Call [%s]\n",
woomera->interface);
#if 0
new_woomera_event_printf(&wevent, "501 call was cancelled!%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
#endif
new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s"
"Unique-Call-Id:%s%s"
"Q931-Cause-Code: %d%s"
"Cause: %s%s",
woomera->interface,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
21,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(21),
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
kill++;
}
#endif
if (!kill) {
new_woomera_event_printf(&wevent, "201 Accepted%s"
"Unique-Call-Id: %s%s",
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
new_woomera_event_printf(&wevent, "EVENT PROCEED w%dg%d%s"
"Channel-Name: g%d/%d%s"
"Unique-Call-Id: %s%s",
event->span+1,
event->chan+1,
WOOMERA_LINE_SEPERATOR,
woomera->trunk_group+1,
(event->span*31)+event->chan+1,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
log_printf(2, server.log, "Call Answered Event ID = %d Device = w%dg%d!\n",
event->call_setup_id,woomera->span+1,woomera->chan+1);
}
}
} else {
log_printf(1, server.log,
"Event (START ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n",
event->event_id, event->call_setup_id,event->span+1, event->chan+1);
kill++;
}
if (kill) {
if (woomera) {
woomera_set_flag(woomera,WFLAG_MEDIA_END);
} else {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED,
SIGBOOST_RELEASE_CAUSE_NORMAL);
log_printf(1, server.log, "Sent CALL STOP to CALL START ACK without session [w%dg%d]\n",
event->span+1, event->chan+1);
}
}
}
static void handle_call_start_nack(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
int span=-1, chan=-1;
int ack=0;
/* Always ACK the incoming NACK
* Send out the NACK ACK before pulling the TANK, because
* if we send after the pull, the outgoing call could send
* a message to boost with the pulled TANK value before
* we send a NACK ACK */
if (smg_validate_span_chan(event->span,event->chan) == 0) {
span=event->span;
chan=event->chan;
}
if (event->call_setup_id > 0) {
woomera=peek_from_holding_tank(event->call_setup_id);
}
if (woomera) {
struct woomera_event wevent;
if (woomera_test_flag(woomera, WFLAG_HANGUP)) {
log_printf(0, server.log, "Event CALL START NACK on hungup call [%d]!\n",
event->call_setup_id);
ack++;
} else {
woomera_set_cause_topbx(woomera,event->release_cause);
woomera_set_flag(woomera, (WFLAG_HANGUP|WFLAG_HANGUP_NACK_ACK));
new_woomera_event_printf(&wevent, "EVENT HANGUP w%dg%d%s"
"Unique-Call-Id: %s%s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
event->span+1,
event->chan+1,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(woomera->q931_rel_cause_topbx),
WOOMERA_LINE_SEPERATOR,
woomera->q931_rel_cause_topbx,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
woomera_set_flag(woomera, WFLAG_HANGUP);
woomera_clear_flag(woomera, WFLAG_RUNNING);
/* Do not ack here, let woomera thread ack it */
ack=0;
}
} else if (event->call_setup_id == 0 &&
smg_validate_span_chan(event->span,event->chan) == 0) {
pthread_mutex_lock(&server.process_lock);
woomera = server.process_table[event->span][event->chan].dev;
if (woomera) {
if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) &&
!woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) {
/* Only if we are not already waiting for hangup */
server.process_table[event->span][event->chan].dev=NULL;
} else if (woomera_test_flag(woomera, WFLAG_HANGUP)) {
/* At this point call is already hang up */
woomera=NULL;
} else {
/* At this point call is already hang up */
woomera=NULL;
}
}
memset(server.process_table[event->span][event->chan].session,
0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
if (woomera) {
log_printf(2, server.log, "Event START NACK on w%dg%d ptr=%p ms=%p\n",
woomera->span+1,woomera->chan+1,woomera,woomera->ms);
if (!woomera_test_flag(woomera,WFLAG_HANGUP)){
woomera_set_cause_topbx(woomera,event->release_cause);
}
woomera_set_flag(woomera,
(WFLAG_HANGUP|WFLAG_MEDIA_END|WFLAG_HANGUP_NACK_ACK));
/* Nack Ack will be sent by the woomera thread */
ack=0;
} else {
/* Valid state when we are not in autoacm mode */
ack++;
log_printf(3, server.log, "Event: NACK no woomera on span chan [w%dg%d]!\n",
event->span+1, event->chan+1);
}
} else {
log_printf(0, server.log,
"Error: Start Nack Invalid State Should not happen [%d] [w%dg%d]!\n",
event->call_setup_id, event->span+1, event->chan+1);
ack++;
}
if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY) {
log_printf(0, server.log, "WARNING: All ckt busy!\n");
smg_all_ckt_busy();
}
#warning "Ignoring CALL GAP"
#if 0
if (event->release_cause == SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP) {
log_printf(0, server.log, "WARNING: Call Gapping Detected!\n");
smg_all_ckt_gap();
}
#endif
if (ack) {
span=0;
chan=0;
if (smg_validate_span_chan(event->span,event->chan) == 0) {
span=event->span;
chan=event->chan;
}
isup_exec_command(span,
chan,
event->call_setup_id,
SIGBOOST_EVENT_CALL_START_NACK_ACK,
0);
if (!woomera) {
log_printf(2, server.log, "Event (NACK ACK) %d referrs to a non-existant session (%d) [w%dg%d]!\n",
event->event_id,event->call_setup_id, event->span+1, event->chan+1);
}
}
}
static void handle_call_loop_start(call_signal_event_t *event)
{
struct woomera_interface *woomera;
char callid[20];
char *session;
pthread_mutex_lock(&server.process_lock);
if (server.process_table[event->span][event->chan].dev) {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK,
SIGBOOST_RELEASE_CAUSE_BUSY);
log_printf(1, server.log,
"Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n",
event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev);
pthread_mutex_unlock(&server.process_lock);
return;
}
sprintf(callid, "w%dg%d", event->span+1,event->chan+1);
sprintf(server.process_table[event->span][event->chan].session,
"%s-%i-%i",callid,rand(),rand());
session=server.process_table[event->span][event->chan].session;
server.process_table[event->span][event->chan].dev = NULL;
pthread_mutex_unlock(&server.process_lock);
woomera=launch_woomera_loop_thread(event);
if (woomera == NULL) {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK,
SIGBOOST_RELEASE_CAUSE_BUSY);
log_printf(1, server.log,
"Sent (From Handle Loop START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg] ptr=%d\n",
event->span+1, event->chan+1, server.process_table[event->span][event->chan].dev);
}
woomera_set_flag(woomera,WFLAG_CALL_ACKED);
return;
}
static void handle_call_start(call_signal_event_t *event)
{
struct woomera_event wevent;
char callid[20];
char *session;
pthread_mutex_lock(&server.process_lock);
if (server.process_table[event->span][event->chan].dev) {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK,
SIGBOOST_RELEASE_CAUSE_BUSY);
log_printf(0, server.log,
"Sent (From Handle START) Call Busy SIGBOOST_EVENT_CALL_START_NACK [w%dg%d]\n",
event->span+1, event->chan+1);
pthread_mutex_unlock(&server.process_lock);
return;
}
sprintf(callid, "w%dg%d", event->span+1,event->chan+1);
sprintf(server.process_table[event->span][event->chan].session,
"%s-%i-%i",callid,rand(),rand());
session=server.process_table[event->span][event->chan].session;
server.process_table[event->span][event->chan].dev = NULL;
pthread_mutex_unlock(&server.process_lock);
if (autoacm) {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_START_ACK,
0);
}
new_woomera_event_printf(&wevent, "EVENT INCOMING w%dg%d%s"
"Unique-Call-Id: %s%s"
"Remote-Number: %s%s"
"Remote-Name: %s%s"
"Protocol: SS7%s"
"User-Agent: sangoma_mgd%s"
"Local-Number: %s%s"
"Channel-Name: g%d/%d%s"
"Trunk-Group: %d%s"
"Presentation: %d%s"
"Screening: %d%s"
"RDNIS: %s%s"
,
event->span+1,
event->chan+1,
WOOMERA_LINE_SEPERATOR,
session,
WOOMERA_LINE_SEPERATOR,
event->calling_number_digits,
WOOMERA_LINE_SEPERATOR,
#ifdef SMG_CALLING_NAME
event->calling_name,
#else
"",
#endif
WOOMERA_LINE_SEPERATOR,
WOOMERA_LINE_SEPERATOR,
WOOMERA_LINE_SEPERATOR,
event->called_number_digits,
WOOMERA_LINE_SEPERATOR,
event->trunk_group+1,
(event->span*31)+event->chan+1,
WOOMERA_LINE_SEPERATOR,
event->trunk_group+1,
WOOMERA_LINE_SEPERATOR,
event->calling_number_presentation,
WOOMERA_LINE_SEPERATOR,
event->calling_number_screening_ind,
WOOMERA_LINE_SEPERATOR,
event->redirection_string,
WOOMERA_RECORD_SEPERATOR
);
if (!enqueue_event_on_listeners(&wevent)) {
pthread_mutex_lock(&server.process_lock);
server.process_table[event->span][event->chan].dev = NULL;
memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
if (autoacm) {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED,
SIGBOOST_RELEASE_CAUSE_BUSY);
} else {
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK,
SIGBOOST_RELEASE_CAUSE_BUSY);
}
log_printf(0, server.log,
"CALL INCOMING: Enqueue Error Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d]\n",
event->span+1, event->chan+1);
}
destroy_woomera_event_data(&wevent);
}
static void handle_gap_abate(call_signal_event_t *event)
{
log_printf(0, server.log, "NOTICE: GAP Cleared!\n",
event->span+1, event->chan+1);
smg_clear_ckt_gap();
}
static void handle_restart_ack(call_signal_connection_t *mcon,call_signal_event_t *event)
{
mcon->rxseq_reset =0;
}
static void handle_call_stop(call_signal_event_t *event)
{
struct woomera_interface *woomera;
int ack=0;
pthread_mutex_lock(&server.process_lock);
woomera = server.process_table[event->span][event->chan].dev;
if (woomera) {
if (!woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT) &&
!woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT)) {
/* Only if we are not already waiting for hangup */
server.process_table[event->span][event->chan].dev=NULL;
} else if (woomera_test_flag(woomera, WFLAG_HANGUP)) {
/* At this point call is already hangup */
woomera=NULL;
} else {
/* At this point call is already hangup */
woomera=NULL;
}
}
memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
if (woomera) {
woomera_set_cause_topbx(woomera,event->release_cause);
woomera_set_flag(woomera,
(WFLAG_MEDIA_END|WFLAG_HANGUP|WFLAG_HANGUP_ACK));
/* We have to close the socket because
At this point we are release span chan */
log_printf(3, server.log, "Event CALL STOP on w%dg%d ptr=%p ms=%p\n",
woomera->span+1,woomera->chan+1,woomera,woomera->ms);
} else {
ack++;
}
if (ack) {
/* At this point we have already sent our STOP so its safe to ACK */
isup_exec_command(event->span,
event->chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED_ACK,
0);
}
if (!woomera){
/* This is allowed on incoming call if remote app does not answer it */
log_printf(3, server.log, "Event CALL STOP referrs to a non-existant session [w%dg%d]!\n",
event->span+1, event->chan+1);
}
}
static void handle_heartbeat(call_signal_event_t *event)
{
if (!woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) {
log_printf(0, server.log,"ERROR! Monitor Thread not running!\n");
} else {
int err=call_signal_connection_write(&server.mcon, event);
if (err <= 0) {
log_printf(0, server.log,
"Critical System Error: Failed to tx on ISUP socket [%s]: %s\n",
strerror(errno));
}
}
return;
}
static void handle_call_stop_ack(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
pthread_mutex_lock(&server.process_lock);
woomera = server.process_table[event->span][event->chan].dev;
server.process_table[event->span][event->chan].dev=NULL;
memset(server.process_table[event->span][event->chan].session,0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
if (woomera) {
log_printf(2, server.log, "Stop Ack on [w%dg%d] [Setup ID: %d] [%s]!\n",
event->span+1, event->chan+1, event->call_setup_id,
woomera->interface);
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK);
} else {
log_printf(2, server.log, "Event CALL_STOP_ACK(%d) referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n",
event->event_id, event->span+1, event->chan+1, event->call_setup_id);
}
/* No need for us to do any thing here */
return;
}
static void handle_call_start_nack_ack(call_signal_event_t *event)
{
struct woomera_interface *woomera = NULL;
if ((woomera=pull_from_holding_tank(event->call_setup_id,-1,-1))) {
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
} else if (event->call_setup_id == 0 &&
smg_validate_span_chan(event->span,event->chan) == 0) {
pthread_mutex_lock(&server.process_lock);
woomera = server.process_table[event->span][event->chan].dev;
server.process_table[event->span][event->chan].dev=NULL;
memset(server.process_table[event->span][event->chan].session,
0,SMG_SESSION_NAME_SZ);
pthread_mutex_unlock(&server.process_lock);
if (woomera) {
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
} else {
log_printf(0, server.log,
"Event NACK ACK [w%dg%d] with valid span/chan no dev!\n",
event->span+1, event->chan+1);
}
} else {
log_printf(2, server.log,
"Event NACK ACK referrs to a non-existant session [w%dg%d] [Setup ID: %d]!\n",
event->span+1, event->chan+1, event->call_setup_id);
}
if (event->call_setup_id > 0) {
clear_from_holding_tank(event->call_setup_id, NULL);
}
/* No need for us to do any thing here */
return;
}
#if 0
static void validate_number(unsigned char *s)
{
unsigned char *p;
for (p = s; *p; p++) {
if (*p < 48 || *p > 57) {
log_printf(2, server.log, "Encountered a non-numeric character [%c]!\n", *p);
*p = '\0';
break;
}
}
}
#endif
static int parse_ss7_event(call_signal_connection_t *mcon, call_signal_event_t *event)
{
int ret = 0;
#if 0
validate_number((unsigned char*)event->called_number_digits);
validate_number((unsigned char*)event->calling_number_digits);
#endif
#if 1
log_printf(2, server.log,
"RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
call_signal_event_id_name(event->event_id),
event->event_id,
event->span+1,
event->chan+1,
event->release_cause,
event->call_setup_id,
event->fseqno,
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
);
#endif
#if 0
log_printf(2, server.log, "RX EVENT\n");
log_printf(2, server.log, "===================================\n");
log_printf(2, server.log, " rType: %s (%0x HEX)\n",
call_signal_event_id_name(event->event_id),event->event_id);
log_printf(2, server.log, " rSpan: [%d]\n",event->span+1);
log_printf(2, server.log, " rChan: [%d]\n",event->chan+1);
log_printf(2, server.log, " rCalledNum: %s\n",
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"));
log_printf(2, server.log, " rCallingNum: %s\n",
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A"));
log_printf(2, server.log, " rCause: %d\n",event->release_cause);
log_printf(2, server.log, " rInterface: [w%dg%d]\n",event->span+1,event->chan+1);
log_printf(2, server.log, " rEvent ID: [%d]\n",event->event_id);
log_printf(2, server.log, " rSetup ID: [%d]\n",event->call_setup_id);
log_printf(2, server.log, " rSeq: [%d]\n",event->fseqno);
log_printf(2, server.log, "===================================\n");
#endif
#if 0
log_printf(2, server.log,
"\nRX EVENT\n"
"===================================\n"
" rType: %s (%0x HEX)\n"
" rSpan: [%d]\n"
" rChan: [%d]\n"
" rCalledNum: %s\n"
" rCallingNum: %s\n"
" rCause: %s\n"
" rInterface : [w%dg%d]\n"
" rEvent ID : [%d]\n"
" rSetup ID: [%d]\n"
" rSeq: [%d]\n"
"===================================\n"
"\n",
call_signal_event_id_name(event->event_id),
event->event_id,
event->span+1,
event->chan+1,
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A"),
release_to_string(event->release_cause),
event->span+1,
event->chan+1,
event->event_id,
event->call_setup_id,
event->fseqno
);
#endif
switch(event->event_id) {
case SIGBOOST_EVENT_CALL_START:
handle_call_start(event);
break;
case SIGBOOST_EVENT_CALL_STOPPED:
handle_call_stop(event);
break;
case SIGBOOST_EVENT_CALL_START_ACK:
handle_call_start_ack(event);
break;
case SIGBOOST_EVENT_CALL_START_NACK:
handle_call_start_nack(event);
break;
case SIGBOOST_EVENT_CALL_ANSWERED:
handle_call_answer(event);
break;
case SIGBOOST_EVENT_HEARTBEAT:
handle_heartbeat(event);
break;
case SIGBOOST_EVENT_CALL_START_NACK_ACK:
handle_call_start_nack_ack(event);
break;
case SIGBOOST_EVENT_CALL_STOPPED_ACK:
handle_call_stop_ack(event);
break;
case SIGBOOST_EVENT_INSERT_CHECK_LOOP:
handle_call_loop_start(event);
break;
case SIGBOOST_EVENT_REMOVE_CHECK_LOOP:
handle_call_stop(event);
break;
case SIGBOOST_EVENT_SYSTEM_RESTART_ACK:
handle_restart_ack(mcon,event);
break;
case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE:
handle_gap_abate(event);
break;
default:
log_printf(0, server.log, "Warning no handler implemented for [%s]\n",
call_signal_event_id_name(event->event_id));
break;
}
return ret;
}
static void *monitor_thread_run(void *obj)
{
int ss = 0;
int policy=0,priority=0;
char a=0,b=0;
call_signal_connection_t *mcon=&server.mcon;
call_signal_connection_t *mconp=&server.mconp;
pthread_mutex_lock(&server.thread_count_lock);
server.thread_count++;
pthread_mutex_unlock(&server.thread_count_lock);
woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING);
if (call_signal_connection_open(mcon,
mcon->cfg.local_ip,
mcon->cfg.local_port,
mcon->cfg.remote_ip,
mcon->cfg.remote_port) < 0) {
log_printf(0, server.log, "Error: Opening MCON Socket [%d] %s\n",
mcon->socket,strerror(errno));
exit(-1);
}
if (call_signal_connection_open(mconp,
mconp->cfg.local_ip,
mconp->cfg.local_port,
mconp->cfg.remote_ip,
mconp->cfg.remote_port) < 0) {
log_printf(0, server.log, "Error: Opening MCONP Socket [%d] %s\n",
mconp->socket,strerror(errno));
exit(-1);
}
mcon->log = server.log;
mconp->log = server.log;
isup_exec_command(0,
0,
-1,
SIGBOOST_EVENT_SYSTEM_RESTART,
0);
mcon->rxseq_reset=1;
smg_get_current_priority(&policy,&priority);
log_printf(1, server.log, "Open udp socket [%d] [%d]\n",
mcon->socket,mconp->socket);
log_printf(1, server.log, "Monitor Thread Started (%i:%i)\n",policy,priority);
while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) {
#if 0
ss = waitfor_socket(server.mcon.socket, 1000, POLLERR | POLLIN);
#else
ss = waitfor_2sockets(mcon->socket,
mconp->socket,
&a, &b, 1000);
#endif
if (ss > 0) {
call_signal_event_t *event=NULL;
int i=0;
if (b) {
mcon_retry_priority:
if ((event = call_signal_connection_readp(mconp,i))) {
struct timeval current;
struct timeval difftime;
gettimeofday(&current,NULL);
timersub (&current, &event->tv, &difftime);
log_printf(3, server.log, "Socket Event P [%s] T=%d:%d\n",
call_signal_event_id_name(event->event_id),
difftime.tv_sec, difftime.tv_usec);
parse_ss7_event(mconp,event);
if (++i < 10) {
goto mcon_retry_priority;
}
} else if (errno != EAGAIN) {
ss=-1;
log_printf(0, server.log,
"Error: Reading from Boost P Socket! (%i) %s\n",
errno,strerror(errno));
break;
}
}
i=0;
if (a) {
mcon_retry:
if ((event = call_signal_connection_read(mcon,i))) {
struct timeval current;
struct timeval difftime;
gettimeofday(&current,NULL);
timersub (&current, &event->tv, &difftime);
log_printf(3, server.log, "Socket Event [%s] T=%d:%d\n",
call_signal_event_id_name(event->event_id),
difftime.tv_sec, difftime.tv_usec);
parse_ss7_event(mcon,event);
if (++i < 50) {
goto mcon_retry;
}
} else if (errno != EAGAIN) {
ss=-1;
log_printf(0, server.log,
"Error: Reading from Boost Socket! (%i) %s\n",
errno,strerror(errno));
break;
}
}
}
if (ss < 0){
log_printf(0, server.log, "Thread Run: Select Socket Error!\n");
break;
}
}
log_printf(1, server.log, "Close udp socket [%d] [%d]\n",
mcon->socket,mconp->socket);
call_signal_connection_close(&server.mcon);
call_signal_connection_close(&server.mconp);
pthread_mutex_lock(&server.thread_count_lock);
server.thread_count--;
pthread_mutex_unlock(&server.thread_count_lock);
woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING);
log_printf(0, server.log, "Monitor Thread Ended\n");
return NULL;
}
static void woomera_loop_thread_run(struct woomera_interface *woomera)
{
int err=launch_media_thread(woomera);
if (err) {
log_printf(0, server.log, "Failed to start loop media thread\n");
woomera_set_flag(woomera,
(WFLAG_HANGUP|WFLAG_MEDIA_END));
woomera_clear_flag(woomera, WFLAG_RUNNING);
return;
}
while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) &&
!woomera_test_flag(woomera, WFLAG_MEDIA_END) &&
!woomera_test_flag(woomera, WFLAG_HANGUP)) {
sleep(1);
continue;
}
woomera_clear_flag(woomera, WFLAG_RUNNING);
log_printf(2, server.log, "Woomera Session: For Loop Test exiting %s\n",woomera->interface);
return;
}
static void *woomera_thread_run(void *obj)
{
struct woomera_interface *woomera = obj;
struct woomera_message wmsg;
struct woomera_event wevent;
char *event_string;
int mwi;
int err;
int policy=0, priority=0;
int span = -1, chan = -1;
woomera_message_init(&wmsg);
smg_get_current_priority(&policy,&priority);
log_printf(2, server.log, "WOOMERA session started (ptr=%p : loop=%i)(%i:%i) Index=%i\n",
woomera,woomera->loop_tdm,policy,priority, woomera->index);
pthread_mutex_lock(&server.thread_count_lock);
server.thread_count++;
pthread_mutex_unlock(&server.thread_count_lock);
pthread_mutex_init(&woomera->queue_lock, NULL);
pthread_mutex_init(&woomera->ms_lock, NULL);
pthread_mutex_init(&woomera->dtmf_lock, NULL);
pthread_mutex_init(&woomera->vlock, NULL);
pthread_mutex_init(&woomera->flags_lock, NULL);
if (woomera->loop_tdm) {
woomera_loop_thread_run(woomera);
goto woomera_session_close;
}
err=socket_printf(woomera->socket,
"EVENT HELLO Sangoma Media Gateway%s"
"Supported-Protocols: TDM%s"
"Version: %s%s"
"Remote-Address: %s%s"
"Remote-Port: %d%s"
"Raw-Format: %s%s",
WOOMERA_LINE_SEPERATOR,
WOOMERA_LINE_SEPERATOR,
SMG_VERSION, WOOMERA_LINE_SEPERATOR,
inet_ntoa(woomera->addr.sin_addr), WOOMERA_LINE_SEPERATOR,
ntohs(woomera->addr.sin_port), WOOMERA_LINE_SEPERATOR,
server.hw_coding?"ALAW":"ULAW", WOOMERA_RECORD_SEPERATOR
);
if (err) {
log_printf(0, server.log, "Woomera session socket failure! (ptr=%p)\n",
woomera);
woomera_clear_flag(woomera, WFLAG_RUNNING);
goto woomera_session_close;
}
while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING) &&
woomera_test_flag(woomera, WFLAG_RUNNING) &&
!woomera_test_flag(woomera, WFLAG_MEDIA_END) &&
!woomera_test_flag(woomera, WFLAG_HANGUP) &&
!master_reset) {
mwi = woomera_message_parse(woomera, &wmsg, WOOMERA_HARD_TIMEOUT);
if (mwi >= 0) {
if (mwi) {
interpret_command(woomera, &wmsg);
} else if (woomera_test_flag(woomera, WFLAG_EVENT)){
while ((event_string = dequeue_event(woomera))) {
if (socket_printf(woomera->socket, "%s", event_string)) {
woomera_set_flag(woomera, WFLAG_MEDIA_END);
woomera_clear_flag(woomera, WFLAG_RUNNING);
free(event_string);
log_printf(4, server.log,
"WOOMERA session (ptr=%p) print string error\n",
woomera);
break;
}
free(event_string);
}
woomera_clear_flag(woomera, WFLAG_EVENT);
}
if (woomera->timeout > 0 && time(NULL) >= woomera->timeout) {
/* Sent the hangup only after we sent a NACK */
log_printf(2, server.log,
"WOOMERA session (ptr=%p) Call Timedout ! [%s] (Timeout=%d)\n",
woomera,woomera->interface,woomera->timeout);
/* Let the Index check below send a NACK */
if (woomera->index) {
woomera_set_flag(woomera, WFLAG_HANGUP);
}
break;
}
} else {
log_printf(3, server.log, "WOOMERA session (ptr=%p) [%s] READ MSG Error %i \n",
woomera,woomera->interface,mwi);
break;
}
}
woomera_session_close:
log_printf(2, server.log, "WOOMERA session (ptr=%p) is dying [%s]: SR=%d WR=%d WF=0x%04X\n",
woomera,woomera->interface,
woomera_test_flag(&server.master_connection, WFLAG_RUNNING),
woomera_test_flag(woomera, WFLAG_RUNNING),
woomera->flags);
if (woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) {
woomera_set_flag(woomera, WFLAG_MEDIA_END);
while(woomera_test_flag(woomera, WFLAG_MEDIA_RUNNING)) {
usleep(100);
sched_yield();
}
}
/***********************************************
* Identify the SPAN CHAN to be used below
***********************************************/
chan = woomera->chan;
span = woomera->span;
if (!woomera_test_flag(woomera, WFLAG_HANGUP)) {
/* The call was not HUNGUP. This is the last check,
If the call is valid, hungup the call if the call
was never up the keep going */
if (smg_validate_span_chan(span,chan) == 0) {
if (!woomera->index) {
if (autoacm || woomera_test_flag(woomera,WFLAG_CALL_ACKED)) {
woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK);
isup_exec_command(span,
chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED,
woomera->q931_rel_cause_tosig);
woomera_set_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK_SENT);
log_printf(3, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] ptr=%p\n",
span+1, chan+1,woomera->interface,woomera);
} else {
woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
isup_exec_command(span,
chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK,
woomera->q931_rel_cause_tosig);
woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT);
log_printf(3, woomera->log, "Woomera Sent SIGBOOST_EVENT_CALL_START_NACK [w%dg%d] [%s] ptr=%p\n",
span+1, chan+1,woomera->interface,woomera);
}
} else {
log_printf(0, woomera->log, "Woomera Not Sent CALL STOPPED - Instead NACK [w%dg%d] [%s] ptr=%p\n",
span+1, chan+1,woomera->interface,woomera);
}
}else{
/* This can happend if an outgoing call times out
or gets hungup before it gets acked. Its not a
failure */
if (!woomera->index) {
/* In this case we really failed to tx STOP */
log_printf(0, woomera->log, "FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED [w%dg%d] [%s] Index=%d ptr=%p\n",
span+1, chan+1, woomera->interface, woomera->index, woomera);
}
}
woomera_set_flag(woomera, WFLAG_HANGUP);
}
woo_re_hangup:
/* We must send a STOP ACK to boost telling it that we are done */
if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) {
/* SMG received a HANGUP from boost.
We must now send back the ACK to the HANGUP.
Boost will not release channel until we
ACK the hangup */
if (smg_validate_span_chan(span,chan) == 0) {
isup_exec_command(span,
chan,
-1,
SIGBOOST_EVENT_CALL_STOPPED_ACK,
woomera->q931_rel_cause_tosig);
log_printf(3, woomera->log,
"Sent (Ack) to SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] ptr=%p\n",
span+1,chan+1,woomera->interface,woomera);
}else{
/* This should never happen! If it does
we broke protocol */
log_printf(0, woomera->log,
"FAILED: Woomera (R) SIGBOOST_EVENT_CALL_STOPPED_ACK [w%dg%d] [%s] Index=%d ptr=%p\n",
span+1, chan+1, woomera->interface, woomera->index, woomera);
}
woomera_clear_flag(woomera, WFLAG_HANGUP_ACK);
woomera_set_flag(woomera, WFLAG_HANGUP);
}
if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) {
/* SMG received a NACK from boost during call startup.
We must now send back the ACK to the NACK.
Boost will not release channel until we
ACK the NACK */
if (smg_validate_span_chan(span,chan) == 0) {
isup_exec_command(span,
chan,
-1,
SIGBOOST_EVENT_CALL_START_NACK_ACK,
woomera->q931_rel_cause_tosig);
log_printf(3, woomera->log,
"Sent (Nack Ack) to SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] ptr=%p\n",
span+1,chan+1,woomera->interface,woomera);
woomera->index=0;
} else if (woomera->index) {
isup_exec_command(0,
0,
woomera->index,
SIGBOOST_EVENT_CALL_START_NACK_ACK,
woomera->q931_rel_cause_tosig);
woomera->index=0;
} else {
log_printf(0, woomera->log,
"FAILED: Sent (Nack Ack) SIGBOOST_EVENT_CALL_START_NACK_ACK [w%dg%d] [%s] Index=%d ptr=%p\n",
span+1, chan+1, woomera->interface, woomera->index, woomera);
}
woomera_clear_flag(woomera, WFLAG_HANGUP_NACK_ACK);
}
if (woomera->index) {
int index = woomera->index;
new_woomera_event_printf(&wevent, "EVENT HANGUP %s%s"
"Unique-Call-Id: %s%s"
"Timeout: %ld%s"
"Cause: %s%s"
"Q931-Cause-Code: %d%s",
woomera->interface,
WOOMERA_LINE_SEPERATOR,
woomera->session,
WOOMERA_LINE_SEPERATOR,
woomera->timeout,
WOOMERA_LINE_SEPERATOR,
q931_rel_to_str(18),
WOOMERA_LINE_SEPERATOR,
18,
WOOMERA_RECORD_SEPERATOR
);
enqueue_event(woomera, &wevent,EVENT_FREE_DATA);
while ((event_string = dequeue_event(woomera))) {
socket_printf(woomera->socket, "%s", event_string);
free(event_string);
}
if (peek_from_holding_tank(index)) {
woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
isup_exec_command(0,
0,
index,
SIGBOOST_EVENT_CALL_START_NACK,
0);
woomera_set_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK_SENT);
log_printf(2, woomera->log,
"Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] .. WAITING FOR NACK ACK\n",
index);
} else {
log_printf(1, woomera->log,
"Error Failed to Sent SIGBOOST_EVENT_CALL_START_NACK [Setup ID: %d] - index stale!\n",
index);
}
}
if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) {
int timeout_cnt=0;
int overall_cnt=0;
/* SMG sent NACK to boost, however we have to wait
for boost to give us the ACK back before we
release resources. */
while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK)) {
timeout_cnt++;
if (timeout_cnt > 4000) { //30sec timeout
timeout_cnt=0;
overall_cnt++;
log_printf(0, woomera->log,
"Waiting for NACK ACK [Setup ID: %d] ... \n",
woomera->index);
}
if (overall_cnt > 10) { //300sec timeotu
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
break;
}
if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING)) {
break;
}
usleep(5000);
sched_yield();
}
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_NACK_ACK);
if (overall_cnt > 10) {
log_printf(0, woomera->log,
"Waiting for NACK ACK [Setup ID: %d] .. TIMEOUT on NACK ACK\n",
index);
} else {
log_printf(2, woomera->log,
"Waiting for NACK ACK [Setup ID: %d] .. GOT NACK ACK\n",
index);
}
}
if (woomera_test_flag(woomera, WFLAG_EVENT)){
while ((event_string = dequeue_event(woomera))) {
socket_printf(woomera->socket, "%s", event_string);
free(event_string);
}
woomera_clear_flag(woomera, WFLAG_EVENT);
}
if (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) {
int timeout_cnt=0;
int overall_cnt=0;
/* SMG sent HANGUP to boost, however we have to wait
for boost to give us the ACK back before we
release resources. */
log_printf(2, woomera->log,
"Waiting for STOPPED ACK [%s] [id=%i]... \n",
woomera->interface,woomera->index_hold);
while (woomera_test_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK)) {
timeout_cnt++;
if (timeout_cnt > 4000) { //10sec timeout
timeout_cnt=0;
overall_cnt++;
log_printf(0, woomera->log,
"Waiting for STOPPED ACK [%s] [id=%i] ... \n",
woomera->interface,woomera->index_hold);
}
if (overall_cnt > 10) { //100sec
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK);
break;
}
if (!woomera_test_flag(&server.master_connection, WFLAG_RUNNING) ||
server.process_table[span][chan].dev != woomera) {
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK);
break;
}
usleep(5000);
sched_yield();
}
woomera_clear_flag(woomera, WFLAG_WAIT_FOR_STOPPED_ACK);
if (overall_cnt > 10) {
log_printf(0, woomera->log,
"Wait TIMEDOUT on STOPPED ACK [%s] [id=%i]... \n",
woomera->interface,woomera->index_hold);
} else {
log_printf(2, woomera->log,
"Wait GOT STOPPED ACK [%s] [id=%i]... \n",
woomera->interface,woomera->index_hold);
}
}
/*****************************************************
* We must wait for WFLAG_WAIT_FOR_STOPPED_ACK here
* so that STOP_ACK can access the woomera
*****************************************************/
if (smg_validate_span_chan(span,chan) == 0) {
log_printf(2, woomera->log,
"WOOMERA Clearing Processs Table ... \n",
woomera->interface);
pthread_mutex_lock(&server.process_lock);
if (server.process_table[span][chan].dev == woomera){
server.process_table[span][chan].dev = NULL;
memset(server.process_table[span][chan].session,0,SMG_SESSION_NAME_SZ);
}
pthread_mutex_unlock(&server.process_lock);
}
#if 0
//Used for testing
if (1) {
int chan = woomera->chan;
int span = woomera->span;
if (smg_validate_span_chan(span,chan) == 0) {
pthread_mutex_lock(&server.process_lock);
/* This is possible in case media thread dies on startup */
if (server.process_table[span][chan]){
log_printf(0, server.log,
"Sanity Span Chan Still in use: [w%dg%d] [%s] Index=%d ptr=%p\n",
span+1, chan+1, woomera->interface, woomera->index, woomera);
//server.process_table[span][chan] = NULL;
}
pthread_mutex_unlock(&server.process_lock);
}
}
#endif
usleep(3000000);
/* Sanity Check */
if (woomera_test_flag(woomera, WFLAG_HANGUP_ACK)) {
log_printf(0, woomera->log,
"Woomera MISSED HANGUP ACK: Retry HANGUP ACK\n");
goto woo_re_hangup;
}
if (woomera_test_flag(woomera, WFLAG_HANGUP_NACK_ACK)) {
log_printf(0, woomera->log,
"Woomera MISSED HANGUP ACK: Retry HANGUP NACK ACK\n");
goto woo_re_hangup;
}
/* This is where we actually pull the index
* out of the tank. We had to keep the tank
* value until the end of the call. Tank is only
* used on outgoing calls. */
if (woomera->index_hold >= 1) {
clear_from_holding_tank(woomera->index_hold, woomera);
woomera->index_hold=0;
}
log_printf(2, woomera->log, "Woomera Thread Finished %u\n", (unsigned long) woomera->thread);
close_socket(&woomera->socket);
woomera->socket=-1;
/* delete queue */
while ((event_string = dequeue_event(woomera))) {
free(event_string);
}
if (woomera_test_flag(woomera, WFLAG_LISTENING)) {
del_listener(woomera);
}
log_printf(2, server.log, "WOOMERA session for [%s] stopped (ptr=%p)\n",
woomera->interface,woomera);
if (woomera_test_flag(woomera, WFLAG_MASTER_DEV)) {
log_printf(0,server.log,"MASTER Thread Stopped (ptr=%p)\n",woomera);
master_reset=0;
}
pthread_mutex_destroy(&woomera->queue_lock);
pthread_mutex_destroy(&woomera->ms_lock);
pthread_mutex_destroy(&woomera->dtmf_lock);
pthread_mutex_destroy(&woomera->vlock);
pthread_mutex_destroy(&woomera->flags_lock);
woomera_set_raw(woomera, NULL);
woomera_set_interface(woomera, NULL);
woomera_message_clear(&wmsg);
free(woomera);
pthread_mutex_lock(&server.thread_count_lock);
server.call_count--;
server.thread_count--;
pthread_mutex_unlock(&server.thread_count_lock);
pthread_exit(NULL);
return NULL;
}
static int launch_woomera_thread(struct woomera_interface *woomera)
{
int result = 0;
pthread_attr_t attr;
result = pthread_attr_init(&attr);
//pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
//pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, MGD_STACK_SIZE);
woomera_set_flag(woomera, WFLAG_RUNNING);
result = pthread_create(&woomera->thread, &attr, woomera_thread_run, woomera);
if (result) {
log_printf(0, server.log, "%s: Error: Creating Thread! (%i) %s\n",
__FUNCTION__,result,strerror(errno));
woomera_clear_flag(woomera, WFLAG_RUNNING);
}
pthread_attr_destroy(&attr);
return result;
}
static int launch_monitor_thread(void)
{
pthread_attr_t attr;
int result = 0;
struct sched_param param;
param.sched_priority = 10;
result = pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, MGD_STACK_SIZE);
result = pthread_attr_setschedparam (&attr, &param);
log_printf(0,server.log,"%s: Old Priority =%i res=%i \n",__FUNCTION__,
param.sched_priority,result);
woomera_set_flag(&server.master_connection, WFLAG_MONITOR_RUNNING);
result = pthread_create(&server.monitor_thread, &attr, monitor_thread_run, NULL);
if (result) {
log_printf(0, server.log, "%s: Error: Creating Thread! %s\n",
__FUNCTION__,strerror(errno));
woomera_clear_flag(&server.master_connection, WFLAG_MONITOR_RUNNING);
}
pthread_attr_destroy(&attr);
return result;
}
#ifdef WP_HPTDM_API
static void *hp_tdmapi_span_run(void *obj)
{
hp_tdm_api_span_t *span = obj;
int err;
log_printf(0,server.log,"Starting %s span!\n",span->ifname);
while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) {
if (!span->run_span) {
break;
}
err = span->run_span(span);
if (err) {
break;
}
}
if (span->close_span) {
span->close_span(span);
}
sleep(3);
log_printf(0,server.log,"Stopping %s span!\n",span->ifname);
pthread_exit(NULL);
}
static int launch_hptdm_api_span_thread(int span)
{
pthread_attr_t attr;
int result = 0;
struct sched_param param;
param.sched_priority = 5;
result = pthread_attr_init(&attr);
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
pthread_attr_setschedpolicy(&attr, SCHED_RR);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_attr_setstacksize(&attr, MGD_STACK_SIZE);
result = pthread_attr_setschedparam (&attr, &param);
result = pthread_create(&server.monitor_thread, &attr, hp_tdmapi_span_run, &hptdmspan[span]);
if (result) {
log_printf(0, server.log, "%s: Error: Creating Thread! %s\n",
__FUNCTION__,strerror(errno));
}
pthread_attr_destroy(&attr);
return result;
}
#endif
static int configure_server(void)
{
struct woomera_config cfg;
char *var, *val;
int cnt = 0;
server.dtmf_intr_ch = -1;
if (!woomera_open_file(&cfg, server.config_file)) {
log_printf(0, server.log, "open of %s failed\n", server.config_file);
return 0;
}
while (woomera_next_pair(&cfg, &var, &val)) {
if (!strcasecmp(var, "boost_local_ip")) {
strncpy(server.mcon.cfg.local_ip, val,
sizeof(server.mcon.cfg.local_ip) -1);
strncpy(server.mconp.cfg.local_ip, val,
sizeof(server.mconp.cfg.local_ip) -1);
cnt++;
} else if (!strcasecmp(var, "boost_local_port")) {
server.mcon.cfg.local_port = atoi(val);
server.mconp.cfg.local_port =
server.mcon.cfg.local_port+1;
cnt++;
} else if (!strcasecmp(var, "boost_remote_ip")) {
strncpy(server.mcon.cfg.remote_ip, val,
sizeof(server.mcon.cfg.remote_ip) -1);
strncpy(server.mconp.cfg.remote_ip, val,
sizeof(server.mconp.cfg.remote_ip) -1);
cnt++;
} else if (!strcasecmp(var, "boost_remote_port")) {
server.mcon.cfg.remote_port = atoi(val);
server.mconp.cfg.remote_port =
server.mcon.cfg.remote_port+1;
cnt++;
} else if (!strcasecmp(var, "logfile_path")) {
if (!server.logfile_path) {
server.logfile_path = strdup(val);
}
} else if (!strcasecmp(var, "woomera_port")) {
server.port = atoi(val);
} else if (!strcasecmp(var, "debug_level")) {
server.debug = atoi(val);
} else if (!strcasecmp(var, "out_tx_test")) {
server.out_tx_test = atoi(val);
} else if (!strcasecmp(var, "loop_trace")) {
server.loop_trace = atoi(val);
} else if (!strcasecmp(var, "rxgain")) {
server.rxgain = atoi(val);
} else if (!strcasecmp(var, "txgain")) {
server.txgain = atoi(val);
} else if (!strcasecmp(var, "dtmf_on_duration")){
server.dtmf_on = atoi(val);
} else if (!strcasecmp(var, "dtmf_off_duration")){
server.dtmf_off = atoi(val);
} else if (!strcasecmp(var, "dtmf_inter_ch_duration")){
server.dtmf_intr_ch = atoi(val);
} else if (!strcasecmp(var, "max_calls")) {
int max = atoi(val);
if (max > 0) {
server.max_calls = max;
}
} else if (!strcasecmp(var, "autoacm")) {
int max = atoi(val);
if (max >= 0) {
autoacm=max;
}
} else if (!strcasecmp(var, "media_ip")) {
strncpy(server.media_ip, val, sizeof(server.media_ip) -1);
} else {
log_printf(0, server.log, "Invalid Option %s at line %d!\n", var, cfg.lineno);
}
}
/* Post initialize */
if (server.dtmf_on == 0){
server.dtmf_on=SMG_DTMF_ON;
}
if (server.dtmf_off == 0) {
server.dtmf_off=SMG_DTMF_OFF;
}
if (server.dtmf_intr_ch == -1) {
server.dtmf_intr_ch = 0;
}
server.dtmf_size=(server.dtmf_on+server.dtmf_off)*10*2;
log_printf(0,server.log, "DTMF On=%i Off=%i IntrCh=%i Size=%i\n",
server.dtmf_on,server.dtmf_off,server.dtmf_intr_ch,server.dtmf_size);
woomera_close_file(&cfg);
return cnt == 4 ? 1 : 0;
}
static int main_thread(void)
{
struct sockaddr_in sock_addr, client_addr;
struct woomera_interface *new_woomera;
int client_sock = -1, pid = 0;
unsigned int len = 0;
FILE *tmp;
if ((server.master_connection.socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
log_printf(0, server.log, "socket() failed\n");
return 1;
}
memset(&sock_addr, 0, sizeof(sock_addr)); /* Zero out structure */
sock_addr.sin_family = AF_INET; /* Internet address family */
sock_addr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
sock_addr.sin_port = htons(server.port); /* Local port */
/* Bind to the local address */
if (bind(server.master_connection.socket, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) {
log_printf(0, server.log, "bind(%d) failed\n", server.port);
return 1;
}
/* Mark the socket so it will listen for incoming connections */
if (listen(server.master_connection.socket, MAXPENDING) < 0) {
log_printf(0, server.log, "listen() failed\n");
return 1;
}
if ((pid = get_pid_from_file(PIDFILE))) {
log_printf(0, stderr, "pid %d already exists.\n", pid);
exit(0);
}
if (!(tmp = safe_fopen(PIDFILE, "w"))) {
log_printf(0, stderr, "Error creating pidfile %s\n", PIDFILE);
return 1;
} else {
fprintf(tmp, "%d", getpid());
fclose(tmp);
tmp = NULL;
}
no_nagle(server.master_connection.socket);
#if 0
if (1) {
int span,chan;
call_signal_event_t event;
#if 0
span=1;
chan=30;
event.span=span;
event.chan=chan;
launch_woomera_loop_thread(&event);
#else
for (span=0;span<8;span++) {
for (chan=0;chan<31;chan++) {
event.span=span;
event.chan=chan;
launch_woomera_loop_thread(&event);
}
}
#endif
}
#endif
#ifdef WP_HPTDM_API
if (1) {
int span;
for (span=0;span<16;span++) {
hptdmspan[span] = sangoma_hptdm_api_span_init(span);
if (!hptdmspan[span]) {
break;
} else {
log_printf(0, server.log, "HP TDM API Span: %d configured...\n",
span);
launch_hptdm_api_span_thread(span);
}
}
}
#endif
log_printf(1, server.log, "Main Process Started: Woomera Ready port: %d\n", server.port);
while (woomera_test_flag(&server.master_connection, WFLAG_RUNNING) &&
woomera_test_flag(&server.master_connection, WFLAG_MONITOR_RUNNING)) {
/* Set the size of the in-out parameter */
len = sizeof(client_addr);
/* Wait for a client to connect */
if ((client_sock = accept(server.master_connection.socket, (struct sockaddr *) &client_addr, &len)) < 0) {
log_printf(0, server.log, "accpet() failed\n");
return 1;
}
if ((new_woomera = new_woomera_interface(client_sock, &client_addr, len))) {
log_printf(2, server.log, "Starting Thread for New Connection %s:%d Sock=%d\n",
inet_ntoa(new_woomera->addr.sin_addr),
ntohs(new_woomera->addr.sin_port),
client_sock);
pthread_mutex_lock(&server.thread_count_lock);
server.call_count++;
pthread_mutex_unlock(&server.thread_count_lock);
if (launch_woomera_thread(new_woomera)) {
socket_printf(new_woomera->socket,
"501 call was cancelled!%s",
WOOMERA_RECORD_SEPERATOR);
close_socket(&new_woomera->socket);
new_woomera->socket=-1;
free(new_woomera);
}
} else {
log_printf(0, server.log, "Critical ERROR: memory/socket error\n");
}
}
log_printf(1, server.log, "Main Process End\n");
return 0;
}
static int do_ignore(int sig)
{
return 0;
}
static int do_shut(int sig)
{
woomera_clear_flag(&server.master_connection, WFLAG_RUNNING);
close_socket(&server.master_connection.socket);
log_printf(1, server.log, "Caught SIG %d, Closing Master Socket!\n", sig);
return 0;
}
static int sangoma_tdm_init (int span)
{
#ifdef LIBSANGOMA_GET_HWCODING
wanpipe_tdm_api_t tdm_api;
int fd=sangoma_open_tdmapi_span(span);
if (fd < 0 ){
return -1;
} else {
server.hw_coding=sangoma_tdm_get_hw_coding(fd,&tdm_api);
close_socket(&fd);
}
#else
#error "libsangoma missing hwcoding feature: not up to date!"
#endif
return 0;
}
static int woomera_startup(int argc, char **argv)
{
int x = 0, pid = 0, bg = 0;
char *cfg=NULL, *debug=NULL, *arg=NULL;
while((arg = argv[x++])) {
if (!strcasecmp(arg, "-hup")) {
if (! (pid = get_pid_from_file(PIDFILE))) {
log_printf(0, stderr, "Error reading pidfile %s\n", PIDFILE);
exit(1);
} else {
log_printf(0, stderr, "Killing PID %d\n", pid);
kill(pid, SIGHUP);
sleep(1);
exit(0);
}
} else if (!strcasecmp(arg, "-term") || !strcasecmp(arg, "--term")) {
if (! (pid = get_pid_from_file(PIDFILE))) {
log_printf(0, stderr, "Error reading pidfile %s\n", PIDFILE);
exit(1);
} else {
log_printf(0, stderr, "Killing PID %d\n", pid);
kill(pid, SIGTERM);
unlink(PIDFILE);
sleep(1);
exit(0);
}
} else if (!strcasecmp(arg, "-version")) {
fprintf(stdout, "\nSangoma Media Gateway: Version %s\n\n", SMG_VERSION);
exit(0);
} else if (!strcasecmp(arg, "-help")) {
fprintf(stdout, "%s\n%s [-help] | [ -version] | [-hup] | [-wipe] | [[-bg] | [-debug <level>] | [-cfg <path>] | [-log <path>]]\n\n", WELCOME_TEXT, argv[0]);
exit(0);
} else if (!strcasecmp(arg, "-wipe")) {
unlink(PIDFILE);
} else if (!strcasecmp(arg, "-bg")) {
bg = 1;
} else if (!strcasecmp(arg, "-g")) {
coredump = 1;
} else if (!strcasecmp(arg, "-debug")) {
if (argv[x] && *(argv[x]) != '-') {
debug = argv[x++];
}
} else if (!strcasecmp(arg, "-cfg")) {
if (argv[x] && *(argv[x]) != '-') {
cfg = argv[x++];
}
} else if (!strcasecmp(arg, "-log")) {
if (argv[x] && *(argv[x]) != '-') {
server.logfile_path = strdup(argv[x++]);
}
} else if (*arg == '-') {
log_printf(0, stderr, "Unknown Option %s\n", arg);
fprintf(stdout, "%s\n%s [-help] | [-hup] | [-wipe] | [[-bg] | [-debug <level>] | [-cfg <path>] | [-log <path>]]\n\n", WELCOME_TEXT, argv[0]);
exit(1);
}
}
if (1){
int spn;
for (spn=1;spn<=WOOMERA_MAX_SPAN;spn++) {
if (sangoma_tdm_init(spn) == 0) {
break;
}
}
if (spn>WOOMERA_MAX_SPAN) {
printf("\nError: Failed to access a channel on spans 1-16\n");
printf(" Please start Wanpipe TDM API drivers\n");
return 0;
}
}
if (bg && (pid = fork())) {
log_printf(0, stderr, "Backgrounding!\n");
return 0;
}
q931_cause_setup();
server.port = 42420;
server.debug = 0;
strcpy(server.media_ip, "127.0.0.1");
server.next_media_port = WOOMERA_MIN_MEDIA_PORT;
server.log = stdout;
server.master_connection.socket = -1;
server.master_connection.event_queue = NULL;
server.config_file = cfg ? cfg : "/etc/sangoma_mgd.conf";
pthread_mutex_init(&server.listen_lock, NULL);
pthread_mutex_init(&server.ht_lock, NULL);
pthread_mutex_init(&server.process_lock, NULL);
pthread_mutex_init(&server.media_udp_port_lock, NULL);
pthread_mutex_init(&server.thread_count_lock, NULL);
pthread_mutex_init(&server.master_connection.queue_lock, NULL);
pthread_mutex_init(&server.master_connection.flags_lock, NULL);
pthread_mutex_init(&server.mcon.lock, NULL);
server.master_connection.chan = -1;
server.master_connection.span = -1;
if (!configure_server()) {
log_printf(0, server.log, "configuration failed!\n");
return 0;
}
#ifndef USE_SYSLOG
if (server.logfile_path) {
if (!(server.log = safe_fopen(server.logfile_path, "a"))) {
log_printf(0, stderr, "Error setting logfile %s!\n", server.logfile_path);
server.log = stderr;
return 0;
}
}
#endif
if (debug) {
server.debug = atoi(debug);
}
if (coredump) {
struct rlimit l;
memset(&l, 0, sizeof(l));
l.rlim_cur = RLIM_INFINITY;
l.rlim_max = RLIM_INFINITY;
if (setrlimit(RLIMIT_CORE, &l)) {
log_printf(0, stderr, "Warning: Failed to disable core size limit: %s\n",
strerror(errno));
}
}
#ifdef __LINUX__
if (geteuid() && coredump) {
if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) < 0) {
log_printf(0, stderr, "Warning: Failed to disable core size limit for non-root: %s\n",
strerror(errno));
}
}
#endif
(void) signal(SIGINT,(void *) do_shut);
(void) signal(SIGPIPE,(void *) do_ignore);
(void) signal(SIGHUP,(void *) do_shut);
woomera_set_flag(&server.master_connection, WFLAG_RUNNING);
if (launch_monitor_thread()) {
woomera_clear_flag(&server.master_connection, WFLAG_RUNNING);
return 0;
}
fprintf(stderr, "%s", WELCOME_TEXT);
log_printf(0, stderr, "Woomera STARTUP Complete. [AutoACM=%i]\n",autoacm);
return 1;
}
static int woomera_shutdown(void)
{
char *event_string;
int told = 0, loops = 0;
close_socket(&server.master_connection.socket);
pthread_mutex_destroy(&server.listen_lock);
pthread_mutex_destroy(&server.ht_lock);
pthread_mutex_destroy(&server.process_lock);
pthread_mutex_destroy(&server.media_udp_port_lock);
pthread_mutex_destroy(&server.thread_count_lock);
pthread_mutex_destroy(&server.master_connection.queue_lock);
pthread_mutex_destroy(&server.master_connection.flags_lock);
pthread_mutex_destroy(&server.mcon.lock);
woomera_clear_flag(&server.master_connection, WFLAG_RUNNING);
if (server.logfile_path) {
free(server.logfile_path);
server.logfile_path = NULL;
}
/* delete queue */
while ((event_string = dequeue_event(&server.master_connection))) {
free(event_string);
}
while(server.thread_count > 0) {
loops++;
if (loops % 1000 == 0) {
told = 0;
}
if (loops > 10000) {
log_printf(0, server.log, "Red Alert! threads did not stop\n");
assert(server.thread_count == 0);
}
if (told != server.thread_count) {
log_printf(1, server.log, "Waiting For %d thread%s.\n",
server.thread_count, server.thread_count == 1 ? "" : "s");
told = server.thread_count;
}
ysleep(10000);
}
unlink(PIDFILE);
log_printf(0, stderr, "Woomera SHUTDOWN Complete.\n");
return 0;
}
int main(int argc, char *argv[])
{
int ret = 0;
mlockall(MCL_FUTURE);
server.hw_coding=0;
openlog (ps_progname ,LOG_PID, LOG_LOCAL2);
if (! (ret = woomera_startup(argc, argv))) {
exit(0);
}
ret = main_thread();
woomera_shutdown();
return ret;
}
/** EMACS **
* Local variables:
* mode: C
* c-basic-offset: 4
* End:
*/