Newer
Older
Mark Spencer
committed
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
* Copyright (C) 1999 - 2005, 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 <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/musiconhold.h>
#include <asterisk/say.h>
#include <asterisk/cli.h>
#include <asterisk/manager.h>
#include <asterisk/chanvars.h>
#include <asterisk/linkedlists.h>
James Golovich
committed
#include <asterisk/lock.h>
#include <asterisk/app.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
#include "asterisk.h"
/* 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
Mark Spencer
committed
unsigned long global_fin = 0, global_fout = 0;
const struct ast_channel_tech *tech;
/* Protect the channel list (highly unlikely that two things would change
it at the same time, but still! */
static int show_channeltypes(int fd, int argc, char *argv[])
{
#define FORMAT "%-7.7s %-50.50s\n"
struct chanlist *cl = backends;
ast_cli(fd, FORMAT, "Type", "Description");
ast_cli(fd, FORMAT, "------", "-----------");
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
while (cl) {
ast_cli(fd, FORMAT, cl->tech->type, cl->tech->description);
cl = cl->next;
}
ast_mutex_unlock(&chlock);
return RESULT_SUCCESS;
}
static char show_channeltypes_usage[] =
"Usage: show channeltypes\n"
" Shows available channel types registered in your Asterisk server.\n";
static struct ast_cli_entry cli_show_channeltypes =
{ { "show", "channeltypes", NULL }, show_channeltypes, "Show available channel types", show_channeltypes_usage };
int ast_check_hangup(struct ast_channel *chan)
{
time_t myt;
/* if soft hangup flag, return true */
/* if no technology private data, return true */
if (!chan->tech_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 struct ast_channel_tech *tech)
struct chanlist *chan;
ast_mutex_lock(&chlock);
if (!strcasecmp(tech->type, chan->tech->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
ast_mutex_unlock(&chlock);
chan = malloc(sizeof(*chan));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_mutex_unlock(&chlock);
chan->tech = tech;
chan->next = backends;
backends = chan;
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->tech->type,
chan->tech->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;
}
static const struct ast_channel_tech null_tech = {
.type = "NULL",
.description "Null channel (should not see this)",
};
struct ast_channel *ast_channel_alloc(int needqueue)
/* If shutting down, don't allocate any new channels */
if (shutting_down)
return NULL;
ast_mutex_lock(&chlock);
if (!tmp) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_mutex_unlock(&chlock);
return NULL;
}
memset(tmp, 0, sizeof(struct ast_channel));
tmp->sched = sched_context_create();
if (!tmp->sched) {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
free(tmp);
ast_mutex_unlock(&chlock);
return NULL;
}
for (x=0;x<AST_MAX_FDS - 1;x++)
tmp->fds[x] = -1;
#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) {
if (pipe(tmp->alertpipe)) {
ast_log(LOG_WARNING, "Alert pipe creation failed!\n");
ast_mutex_unlock(&chlock);
return NULL;
} else {
flags = fcntl(tmp->alertpipe[0], F_GETFL);
fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(tmp->alertpipe[1], F_GETFL);
fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/* Make sure we've got it done right if they don't */
tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
/* Always watch the alertpipe */
tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0];
/* And timing pipe */
tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
/* Initial state */
tmp->_state = AST_STATE_DOWN;
tmp->streamid = -1;
tmp->appl = NULL;
tmp->data = NULL;
tmp->fin = global_fin;
tmp->fout = global_fout;
snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++);
headp = &tmp->varshead;
ast_mutex_init(&tmp->lock);
AST_LIST_HEAD_INIT(headp);
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->tech = &null_tech;
tmp->next = channels;
channels = tmp;
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);
cur = chan->readq;
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);
chan->readq = f;
if (chan->alertpipe[1] > -1) {
if (write(chan->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 (ast_test_flag(chan, AST_FLAG_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 = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
}
return pre;
}
void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
if (chan)
ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
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->tech_pvt) {
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
free(chan->tech_pvt);
}
strncpy(name, chan->name, sizeof(name)-1);
/* Stop monitoring */
if (chan->monitor) {
chan->monitor->stop( chan, 0 );
}
Mark Spencer
committed
/* If there is native format music-on-hold state, free it */
if(chan->music_state)
ast_moh_cleanup(chan);
if (chan->readtrans)
ast_translator_free_path(chan->readtrans);
if (chan->writetrans)
ast_translator_free_path(chan->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);
if ((fd = chan->alertpipe[0]) > -1)
if ((fd = chan->alertpipe[1]) > -1)
if ((fd = chan->timingfd) > -1)
close(fd);
f = chan->readq;
chan->readq = NULL;
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_REMOVE_HEAD(headp, entries);
/* printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(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 (ast_test_flag(chan, AST_FLAG_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->writetrans)
ast_translator_free_path(clone->writetrans);
if (clone->readtrans)
ast_translator_free_path(clone->readtrans);
clone->writetrans = NULL;
clone->readtrans = NULL;
clone->rawwriteformat = clone->nativeformats;
clone->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) {
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 (ast_test_flag(chan, AST_FLAG_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 (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
if (chan->tech->hangup)
res = chan->tech->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 struct ast_channel_tech *tech)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
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 (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
if (chan->tech->answer)
res = chan->tech->answer(chan);
void ast_deactivate_generator(struct ast_channel *chan)
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
ast_clear_flag(chan, AST_FLAG_WRITE_INT);
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_ERROR, "Out of memory\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_ERROR, "Out of memory\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);