Newer
Older
Mark Spencer
committed
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
* Copyright (C) 1999-2004, Digium, Inc.
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <sys/poll.h>
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/say.h>
#include <asterisk/manager.h>
#include <asterisk/chanvars.h>
#include <asterisk/linkedlists.h>
James Golovich
committed
#include <asterisk/lock.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#else
#include <zaptel.h>
#endif /* __linux__ */
#ifndef ZT_TIMERPING
#error "You need newer zaptel! Please cvs update zaptel"
#endif
/* uncomment if you have problems with 'monitoring' synchronized files */
#if 0
#define MONITOR_CONSTANT_DELAY
#define MONITOR_DELAY 150 * 8 /* 150 ms of MONITORING DELAY */
#endif
struct chanlist {
char type[80];
char description[80];
int capabilities;
Mark Spencer
committed
struct ast_channel * (*requester)(const char *type, int format, void *data, int *cause);
int (*devicestate)(void *data);
/* Protect the channel list (highly unlikely that two things would change
it at the same time, but still! */
int ast_check_hangup(struct ast_channel *chan)
{
time_t myt;
/* if soft hangup flag, return true */
if (chan->_softhangup) return 1;
/* if no private structure, return true */
if (!chan->pvt->pvt) return 1;
/* if no hangup scheduled, just return here */
if (!chan->whentohangup) return 0;
time(&myt); /* get current time */
/* return, if not yet */
if (chan->whentohangup > myt) return 0;
static int ast_check_hangup_locked(struct ast_channel *chan)
{
int res;
ast_mutex_lock(&chan->lock);
res = ast_check_hangup(chan);
ast_mutex_unlock(&chan->lock);
void ast_begin_shutdown(int hangup)
{
struct ast_channel *c;
shutting_down = 1;
if (hangup) {
ast_mutex_lock(&chlock);
ast_mutex_unlock(&chlock);
}
}
int ast_active_channels(void)
{
struct ast_channel *c;
int cnt = 0;
ast_mutex_lock(&chlock);
c = channels;
while(c) {
cnt++;
c = c->next;
}
ast_mutex_unlock(&chlock);
return cnt;
}
void ast_cancel_shutdown(void)
{
shutting_down = 0;
}
int ast_shutting_down(void)
{
return shutting_down;
}
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
if (offset)
chan->whentohangup = myt + offset;
else
chan->whentohangup = 0;
int ast_channel_register(const char *type, const char *description, int capabilities,
Mark Spencer
committed
struct ast_channel *(*requester)(const char *type, int format, void *data, int *cause))
return ast_channel_register_ex(type, description, capabilities, requester, NULL);
int ast_channel_register_ex(const char *type, const char *description, int capabilities,
Mark Spencer
committed
struct ast_channel *(*requester)(const char *type, int format, void *data, int *cause),
int (*devicestate)(void *data))
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chan = backends;
if (!strcasecmp(type, chan->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
ast_mutex_unlock(&chlock);
return -1;
}
last = chan;
chan = chan->next;
}
chan = malloc(sizeof(struct chanlist));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_mutex_unlock(&chlock);
strncpy(chan->type, type, sizeof(chan->type)-1);
strncpy(chan->description, description, sizeof(chan->description)-1);
chan->capabilities = capabilities;
chan->requester = requester;
chan->devicestate = devicestate;
chan->next = NULL;
if (last)
last->next = chan;
else
backends = chan;
if (option_debug)
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
ast_mutex_unlock(&chlock);
/* XXX Not reentrant XXX */
static char localtmp[256];
switch(state) {
case AST_STATE_DOWN:
return "Down";
case AST_STATE_RESERVED:
return "Rsrvd";
case AST_STATE_OFFHOOK:
return "OffHook";
case AST_STATE_DIALING:
return "Dialing";
case AST_STATE_RING:
return "Ring";
case AST_STATE_RINGING:
return "Ringing";
case AST_STATE_UP:
return "Up";
case AST_STATE_BUSY:
return "Busy";
default:
snprintf(localtmp, sizeof(localtmp), "Unknown (%d)\n", state);
return localtmp;
int ast_best_codec(int fmts)
{
/* This just our opinion, expressed in code. We are asked to choose
the best codec to use, given no information */
int x;
static int prefs[] =
{
/* Okay, ulaw is used by all telephony equipment, so start with it */
AST_FORMAT_ULAW,
/* Unless of course, you're a silly European, so then prefer ALAW */
AST_FORMAT_ALAW,
/* Okay, well, signed linear is easy to translate into other stuff */
AST_FORMAT_SLINEAR,
/* G.726 is standard ADPCM */
AST_FORMAT_G726,
/* ADPCM has great sound quality and is still pretty easy to translate */
AST_FORMAT_ADPCM,
/* Okay, we're down to vocoders now, so pick GSM because it's small and easier to
translate and sounds pretty good */
AST_FORMAT_GSM,
/* Speex is free, but computationally more expensive than GSM */
AST_FORMAT_SPEEX,
/* Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
to use it */
AST_FORMAT_LPC10,
/* G.729a is faster than 723 and slightly less expensive */
AST_FORMAT_G729A,
/* Down to G.723.1 which is proprietary but at least designed for voice */
AST_FORMAT_G723_1,
};
for (x=0;x<sizeof(prefs) / sizeof(prefs[0]); x++)
if (fmts & prefs[x])
return prefs[x];
ast_log(LOG_WARNING, "Don't know any of 0x%x formats\n", fmts);
return 0;
}
struct ast_channel *ast_channel_alloc(int needqueue)
{
struct ast_channel *tmp;
struct ast_channel_pvt *pvt;
/* If shutting down, don't allocate any new channels */
if (shutting_down)
return NULL;
ast_mutex_lock(&chlock);
tmp = malloc(sizeof(struct ast_channel));
if (tmp) {
pvt = malloc(sizeof(struct ast_channel_pvt));
if (pvt) {
memset(pvt, 0, sizeof(struct ast_channel_pvt));
tmp->sched = sched_context_create();
if (tmp->sched) {
#ifdef ZAPTEL_OPTIMIZATIONS
tmp->timingfd = open("/dev/zap/timer", O_RDWR);
if (tmp->timingfd > -1) {
/* Check if timing interface supports new
ping/pong scheme */
flags = 1;
if (!ioctl(tmp->timingfd, ZT_TIMERPONG, &flags))
needqueue = 0;
}
#else
tmp->timingfd = -1;
#endif
if (needqueue &&
pipe(pvt->alertpipe)) {
ast_log(LOG_WARNING, "Alert pipe creation failed!\n");
free(pvt);
free(tmp);
tmp = NULL;
pvt = NULL;
} else {
if (needqueue) {
flags = fcntl(pvt->alertpipe[0], F_GETFL);
fcntl(pvt->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(pvt->alertpipe[1], F_GETFL);
fcntl(pvt->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
} else
/* Make sure we've got it done right if they don't */
pvt->alertpipe[0] = pvt->alertpipe[1] = -1;
/* Always watch the alertpipe */
tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0];
/* And timing pipe */
tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
tmp->pvt = pvt;
/* Initial state */
tmp->_state = AST_STATE_DOWN;
tmp->stack = -1;
tmp->streamid = -1;
tmp->appl = NULL;
tmp->data = NULL;
snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++);
ast_mutex_init(&tmp->lock);
strncpy(tmp->context, "default", sizeof(tmp->context)-1);
strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1);
strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
tmp->priority=1;
tmp->amaflags = ast_default_amaflags;
strncpy(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode)-1);
tmp->next = channels;
channels= tmp;
}
} else {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
free(tmp);
tmp = NULL;
}
} else {
ast_log(LOG_WARNING, "Out of memory\n");
free(tmp);
tmp = NULL;
}
} else
ast_log(LOG_WARNING, "Out of memory\n");
ast_mutex_unlock(&chlock);
Mark Spencer
committed
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
{
struct ast_frame *f;
struct ast_frame *prev, *cur;
int blah = 1;
int qlen = 0;
f = ast_frdup(fin);
if (!f) {
ast_log(LOG_WARNING, "Unable to duplicate frame\n");
return -1;
}
Mark Spencer
committed
ast_mutex_lock(&chan->lock);
prev = NULL;
cur = chan->pvt->readq;
while(cur) {
Mark Spencer
committed
if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
/* Don't bother actually queueing anything after a hangup */
ast_frfree(f);
ast_mutex_unlock(&chan->lock);
return 0;
}
prev = cur;
cur = cur->next;
qlen++;
}
/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen > 128)) {
if (fin->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
CRASH;
} else {
ast_log(LOG_DEBUG, "Dropping voice to exceptionally long queue on %s\n", chan->name);
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
if (prev)
prev->next = f;
else
chan->pvt->readq = f;
if (chan->pvt->alertpipe[1] > -1) {
if (write(chan->pvt->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
chan->name, f->frametype, f->subclass, qlen, strerror(errno));
#ifdef ZAPTEL_OPTIMIZATIONS
} else if (chan->timingfd > -1) {
ioctl(chan->timingfd, ZT_TIMERPING, &blah);
#endif
} else if (chan->blocking) {
pthread_kill(chan->blocker, SIGURG);
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
Mark Spencer
committed
int ast_queue_hangup(struct ast_channel *chan)
{
struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
chan->_softhangup |= AST_SOFTHANGUP_DEV;
Mark Spencer
committed
return ast_queue_frame(chan, &f);
Mark Spencer
committed
int ast_queue_control(struct ast_channel *chan, int control)
{
struct ast_frame f = { AST_FRAME_CONTROL, };
f.subclass = control;
Mark Spencer
committed
return ast_queue_frame(chan, &f);
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
int pre = 0;
if (chan) {
pre = chan->deferdtmf;
chan->deferdtmf = 1;
}
return pre;
}
void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
if (chan)
chan->deferdtmf = 0;
}
struct ast_channel *ast_channel_walk_locked(struct ast_channel *prev)
/* Returns next channel (locked) */
struct ast_channel *l, *ret;
Mark Spencer
committed
int retries = 0;
retry:
ast_mutex_lock(&chlock);
if (l) {
if (ast_mutex_trylock(&l->lock)) {
if (retries < 10)
ast_log(LOG_DEBUG, "Avoiding initial deadlock for '%s'\n", l->name);
ast_log(LOG_WARNING, "Avoided initial deadlock for '%s', %d retries!\n", l->name, retries);
ast_mutex_unlock(&chlock);
if (retries < 10) {
usleep(1);
retries++;
goto retry;
} else
return NULL;
}
}
ast_mutex_unlock(&chlock);
return l;
}
while(l) {
if (l == prev)
ret = l->next;
l = l->next;
}
Mark Spencer
committed
if (ret) {
if (ast_mutex_trylock(&ret->lock)) {
if (retries < 10)
ast_log(LOG_DEBUG, "Avoiding deadlock for '%s'\n", ret->name);
else
ast_log(LOG_WARNING, "Avoided deadlock for '%s', %d retries!\n", ret->name, retries);
ast_mutex_unlock(&chlock);
if (retries < 10) {
usleep(1);
retries++;
goto retry;
} else
return NULL;
}
}
ast_mutex_unlock(&chlock);
struct ast_channel *ast_get_channel_by_name_locked(char *channame)
{
struct ast_channel *chan;
chan = ast_channel_walk_locked(NULL);
while(chan) {
if (!strcasecmp(chan->name, channame))
return chan;
ast_mutex_unlock(&chan->lock);
}
return NULL;
}
int ast_safe_sleep_conditional( struct ast_channel *chan, int ms,
int (*cond)(void*), void *data )
{
struct ast_frame *f;
while(ms > 0) {
if( cond && ((*cond)(data) == 0 ) )
return 0;
ms = ast_waitfor(chan, ms);
if (ms <0)
return -1;
if (ms > 0) {
f = ast_read(chan);
if (!f)
return -1;
ast_frfree(f);
}
}
return 0;
}
int ast_safe_sleep(struct ast_channel *chan, int ms)
{
struct ast_frame *f;
while(ms > 0) {
ms = ast_waitfor(chan, ms);
if (ms <0)
return -1;
if (ms > 0) {
f = ast_read(chan);
if (!f)
return -1;
ast_frfree(f);
}
}
return 0;
}
static void free_cid(struct ast_callerid *cid)
{
if (cid->cid_dnid)
free(cid->cid_dnid);
if (cid->cid_num)
free(cid->cid_num);
if (cid->cid_name)
free(cid->cid_name);
if (cid->cid_ani)
free(cid->cid_ani);
if (cid->cid_rdnis)
free(cid->cid_rdnis);
}
void ast_channel_free(struct ast_channel *chan)
{
struct ast_channel *last=NULL, *cur;
char name[AST_CHANNEL_NAME];
ast_mutex_lock(&chlock);
cur = channels;
while(cur) {
if (cur == chan) {
if (last)
last->next = cur->next;
else
channels = cur->next;
break;
}
last = cur;
cur = cur->next;
}
if (!cur)
ast_log(LOG_WARNING, "Unable to find channel in list\n");
else {
/* Lock and unlock the channel just to be sure nobody
has it locked still */
ast_mutex_lock(&cur->lock);
ast_mutex_unlock(&cur->lock);
}
if (chan->pvt->pvt)
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
strncpy(name, chan->name, sizeof(name)-1);
/* Stop monitoring */
if (chan->monitor) {
chan->monitor->stop( chan, 0 );
}
/* Free translatosr */
if (chan->pvt->readtrans)
ast_translator_free_path(chan->pvt->readtrans);
if (chan->pvt->writetrans)
ast_translator_free_path(chan->pvt->writetrans);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
ast_mutex_destroy(&chan->lock);
/* Close pipes if appropriate */
if ((fd = chan->pvt->alertpipe[0]) > -1)
close(fd);
if ((fd = chan->pvt->alertpipe[1]) > -1)
close(fd);
if ((fd = chan->timingfd) > -1)
close(fd);
while(f) {
fp = f;
f = f->next;
ast_frfree(fp);
}
/* loop over the variables list, freeing all data and deleting list items */
/* no need to lock the list, as the channel is already locked */
while (!AST_LIST_EMPTY(headp)) { /* List Deletion. */
vardata = AST_LIST_FIRST(headp);
AST_LIST_REMOVE_HEAD(headp, entries);
// printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata));
ast_var_delete(vardata);
ast_mutex_unlock(&chlock);
ast_device_state_changed(name);
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
if (option_debug)
ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
/* Inform channel driver that we need to be hung up, if it cares */
Mark Spencer
committed
ast_queue_frame(chan, &f);
/* Interrupt any poll call or such */
if (chan->blocking)
pthread_kill(chan->blocker, SIGURG);
return res;
}
int ast_softhangup(struct ast_channel *chan, int cause)
{
int res;
ast_mutex_lock(&chan->lock);
ast_mutex_unlock(&chan->lock);
static void free_translation(struct ast_channel *clone)
{
if (clone->pvt->writetrans)
ast_translator_free_path(clone->pvt->writetrans);
if (clone->pvt->readtrans)
ast_translator_free_path(clone->pvt->readtrans);
clone->pvt->writetrans = NULL;
clone->pvt->readtrans = NULL;
clone->pvt->rawwriteformat = clone->nativeformats;
clone->pvt->rawreadformat = clone->nativeformats;
}
int ast_hangup(struct ast_channel *chan)
{
int res = 0;
/* Don't actually hang up a channel that will masquerade as someone else, or
if someone is going to masquerade as us */
ast_mutex_lock(&chan->lock);
Mark Spencer
committed
if (ast_do_masquerade(chan))
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
}
if (chan->masq) {
ast_log(LOG_WARNING, "%s getting hung up, but someone is trying to masq into us?!?\n", chan->name);
ast_mutex_unlock(&chan->lock);
/* If this channel is one which will be masqueraded into something,
mark it as a zombie already, so we know to free it later */
if (chan->masqr) {
chan->zombie=1;
if (chan->stream)
ast_closestream(chan->stream);
if (chan->vstream)
ast_closestream(chan->vstream);
if (chan->sched)
sched_context_destroy(chan->sched);
/* Clear any tone stuff remaining */
if (chan->generatordata)
chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL;
chan->generator = NULL;
if (chan->cdr) {
/* End the CDR if it hasn't already */
ast_cdr_end(chan->cdr);
/* Post and Free the CDR */
ast_cdr_post(chan->cdr);
ast_cdr_free(chan->cdr);
}
if (chan->blocking) {
ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
"is blocked by thread %ld in procedure %s! Expect a failure\n",
(long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
if (!chan->zombie) {
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
if (chan->pvt->hangup)
res = chan->pvt->hangup(chan);
} else
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
ast_mutex_unlock(&chan->lock);
"Uniqueid: %s\r\n"
"Cause: %i\r\n",
chan->name, chan->uniqueid, chan->hangupcause);
void ast_channel_unregister(const char *type)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return;
}
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", type);
chan = backends;
while(chan) {
if (!strcasecmp(chan->type, type)) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
int ast_answer(struct ast_channel *chan)
{
/* Stop if we're a zombie or need a soft hangup */
if (chan->zombie || ast_check_hangup(chan)) {
ast_mutex_unlock(&chan->lock);
void ast_deactivate_generator(struct ast_channel *chan)
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
ast_settimeout(chan, 0, NULL, NULL);
static int generator_force(void *data)
{
/* Called if generator doesn't have data */
void *tmp;
int res;
int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
struct ast_channel *chan = data;
tmp = chan->generatordata;
chan->generatordata = NULL;
generate = chan->generator->generate;
res = generate(chan, tmp, 0, 160);
chan->generatordata = tmp;
if (res) {
ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
ast_deactivate_generator(chan);
}
return 0;
}
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
{
int res = 0;
ast_mutex_lock(&chan->lock);
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
if ((chan->generatordata = gen->alloc(chan, params))) {
ast_settimeout(chan, 160, generator_force, chan);
ast_mutex_unlock(&chan->lock);
return res;
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval start, now;
int spoint;
struct pollfd *pfds;
pfds = alloca(sizeof(struct pollfd) * n);
if (!pfds) {
ast_log(LOG_WARNING, "alloca failed! bad things will happen.\n");
return -1;
}
if (*ms > 0)
gettimeofday(&start, NULL);
y = 0;
pfds[y].fd = fds[x];
pfds[y].events = POLLIN | POLLPRI;
y++;
res = poll(pfds, y, *ms);
if (res < 0) {
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
else
*ms = 0;
return -1;
}
if (fds[x] > -1) {
if ((res = ast_fdisset(pfds, fds[x], y, &spoint))) {
winner = fds[x];
if (exception) {
if (res & POLLPRI)
*exception = -1;
else
*exception = 0;
}
}
if (*ms > 0) {
long passed;
gettimeofday(&now, NULL);
passed = (now.tv_sec - start.tv_sec) * 1000;
passed += (now.tv_usec - start.tv_usec) / 1000;
if (passed <= *ms)
*ms -= passed;
else
*ms = 0;
}
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
int *exception, int *outfd, int *ms)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval start, end;
struct pollfd *pfds;
long rms;
int x, y, max;
int spoint;
time_t now = 0;
long whentohangup = 0, havewhen = 0, diff;
pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
if (!pfds) {
ast_log(LOG_WARNING, "alloca failed! bad things will happen.\n");
*outfd = -1;
return NULL;
}
/* Perform any pending masquerades */
for (x=0;x<n;x++) {
ast_mutex_lock(&c[x]->lock);
if (!havewhen)
time(&now);
diff = c[x]->whentohangup - now;
if (!havewhen || (diff < whentohangup)) {
havewhen++;
whentohangup = diff;
}
}
Mark Spencer
committed
if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n");
*ms = -1;
ast_mutex_unlock(&c[x]->lock);
ast_mutex_unlock(&c[x]->lock);
if (havewhen) {
if ((*ms < 0) || (whentohangup * 1000 < *ms)) {
rms = whentohangup * 1000;
pfds[max].fd = c[x]->fds[y];
pfds[max].events = POLLIN | POLLPRI;
max++;