Newer
Older
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.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>
#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;
struct ast_channel * (*requester)(char *type, int format, void *data);
int (*devicestate)(void *data);
/* Protect the channel list (highly unlikely that two things would change
it at the same time, but still! */
static ast_mutex_t chlock = AST_MUTEX_INITIALIZER;
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)
{
time_t myt;
time(&myt);
int ast_channel_register(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data))
return ast_channel_register_ex(type, description, capabilities, requester, NULL);
}
int ast_channel_register_ex(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data),
int (*devicestate)(void *data))
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chan = backends;
while(chan) {
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);
tmp->vars=ast_var_assign("tempvar","tempval");
AST_LIST_INSERT_HEAD(headp,tmp->vars,entries);
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) {
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) */
ast_mutex_lock(&chlock);
if (l)
ast_mutex_lock(&l->lock);
ast_mutex_unlock(&chlock);
return l;
}
while(l) {
if (l == prev)
ret = l->next;
l = l->next;
}
if (ret)
ast_mutex_lock(&ret->lock);
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;
}
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);
if (chan->dnid)
free(chan->dnid);
if (chan->callerid)
free(chan->callerid);
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);
return res;
}
void ast_channel_unregister(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 */
void ast_deactivate_generator(struct ast_channel *chan)
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
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))) {
chan->generator = gen;
} else {
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++;
if (fds[x] > -1) {
pfds[max].fd = fds[x];
pfds[max].events = POLLIN | POLLPRI;
max++;
}
if (*ms > 0)
gettimeofday(&start, NULL);
res = poll(pfds, max, rms);
if (res < 0) {
for (x=0;x<n;x++)
c[x]->blocking = 0;
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
if (havewhen)
time(&now);
if (havewhen && c[x]->whentohangup && (now > c[x]->whentohangup)) {
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
if (!winner)
winner = c[x];
}
for (y=0;y<AST_MAX_FDS;y++) {
if (c[x]->fds[y] > -1) {
if ((res = ast_fdisset(pfds, c[x]->fds[y], max, &spoint))) {
if (res & POLLPRI)
c[x]->exception = -1;
else
c[x]->exception = 0;
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
if (fds[x] > -1) {
if ((res = ast_fdisset(pfds, fds[x], max, &spoint))) {
if (outfd)
*outfd = fds[x];
if (exception) {
if (res & POLLPRI)
*exception = -1;
else
*exception = 0;
}
winner = NULL;
}
}
}
if (*ms > 0) {
long diff;
gettimeofday(&end, NULL);
diff = (end.tv_sec - start.tv_sec) * 1000;
diff += (end.tv_usec - start.tv_usec) / 1000;
if (diff < *ms)
*ms -= diff;
else
*ms = 0;
struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
{
return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
}
int ast_waitfor(struct ast_channel *c, int ms)
{
if (ms < 0) {
if (oldms < 0)
return 0;
else
return -1;
}
}
char ast_waitfordigit(struct ast_channel *c, int ms)
{
/* XXX Should I be merged with waitfordigit_full XXX */
/* Stop if we're a zombie or need a soft hangup */
/* Wait for a digit, no more than ms milliseconds total. */
while(ms && !result) {
ms = ast_waitfor(c, ms);
if (ms < 0) /* Error */
result = -1;
else if (ms > 0) {
/* Read something */
f = ast_read(c);
if (f) {
if (f->frametype == AST_FRAME_DTMF)
result = f->subclass;
ast_frfree(f);
} else
result = -1;
}
}
return result;
}
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
{
int res = -1;
#ifdef ZAPTEL_OPTIMIZATIONS
if (c->timingfd > -1) {
if (!func) {
samples = 0;
data = 0;
}
ast_log(LOG_DEBUG, "Scheduling timer at %d sample intervals\n", samples);
res = ioctl(c->timingfd, ZT_TIMERCONFIG, &samples);
c->timingfunc = func;
c->timingdata = data;
}
#endif
return res;
}
char ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
{
struct ast_frame *f;
struct ast_channel *rchan;
int outfd;
/* Stop if we're a zombie or need a soft hangup */
if (c->zombie || ast_check_hangup(c))
return -1;
/* Wait for a digit, no more than ms milliseconds total. */
while(ms) {
rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
if ((!rchan) && (outfd < 0) && (ms)) {
ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
return -1;
} else if (outfd > -1) {
/* The FD we were watching has something waiting */
return 1;
} else if (rchan) {
f = ast_read(c);
if(!f) {
return -1;
}
switch(f->frametype) {
case AST_FRAME_DTMF:
res = f->subclass;
return res;
case AST_FRAME_CONTROL:
switch(f->subclass) {
case AST_CONTROL_HANGUP:
ast_frfree(f);
return -1;
case AST_CONTROL_RINGING:
case AST_CONTROL_ANSWER:
/* Unimportant */
break;
default:
ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
}
case AST_FRAME_VOICE:
/* Write audio if appropriate */
if (audiofd > -1)
write(audiofd, f->data, f->datalen);
}
/* Ignore */
ast_frfree(f);
struct ast_frame *ast_read(struct ast_channel *chan)
{
struct ast_frame *f = NULL;
#ifdef ZAPTEL_OPTIMIZATIONS
int (*func)(void *);
void *data;
int res;
static struct ast_frame null_frame =
{
AST_FRAME_NULL,
};
ast_mutex_lock(&chan->lock);
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
f = NULL;
} else
ast_mutex_unlock(&chan->lock);
return f;
}
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_unlock(&chan->lock);
if (!chan->deferdtmf && !ast_strlen_zero(chan->dtmfq)) {
/* We have DTMF that has been deferred. Return it now */
chan->dtmff.frametype = AST_FRAME_DTMF;
chan->dtmff.subclass = chan->dtmfq[0];
/* Drop first digit */
memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
ast_mutex_unlock(&chan->lock);
/* Read and ignore anything on the alertpipe, but read only
one sizeof(blah) per frame that we send from it */
if (chan->pvt->alertpipe[0] > -1) {
read(chan->pvt->alertpipe[0], &blah, sizeof(blah));
}
#ifdef ZAPTEL_OPTIMIZATIONS
if ((chan->timingfd > -1) && (chan->fdno == AST_MAX_FDS - 2) && chan->exception) {
chan->exception = 0;
blah = -1;
/* IF we can't get event, assume it's an expired as-per the old interface */
res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
if (res)
blah = ZT_EVENT_TIMER_EXPIRED;
if (blah == ZT_EVENT_TIMER_PING) {
ast_log(LOG_NOTICE, "Oooh, there's a PING!\n");
if (!chan->pvt->readq || !chan->pvt->readq->next) {
/* Acknowledge PONG unless we need it again */
#if 0
ast_log(LOG_NOTICE, "Sending a PONG!\n");
#endif
if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
}
}
} else if (blah == ZT_EVENT_TIMER_EXPIRED) {
ioctl(chan->timingfd, ZT_TIMERACK, &blah);
func = chan->timingfunc;
data = chan->timingdata;
ast_mutex_unlock(&chan->lock);
if (func) {
#if 0
ast_log(LOG_DEBUG, "Calling private function\n");
#endif
func(data);
} else {
blah = 0;
ast_mutex_lock(&chan->lock);
ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
chan->timingdata = NULL;
ast_mutex_unlock(&chan->lock);
}
return f;
} else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
/* Check for pending read queue */
if (chan->pvt->readq) {
f = chan->pvt->readq;
chan->pvt->readq = f->next;
/* Interpret hangup and return NULL */
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
ast_frfree(f);
} else {
chan->blocker = pthread_self();
if (chan->exception) {
if (chan->pvt->exception)
f = chan->pvt->exception(chan);
else {
ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
f = &null_frame;
}
/* Clear the exception flag */
chan->exception = 0;
} else
if (chan->pvt->read)
f = chan->pvt->read(chan);
ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
}
if (!(f->subclass & chan->nativeformats)) {
/* This frame can't be from the current native formats -- drop it on the
floor */
ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
} else {
if (chan->monitor && chan->monitor->read_stream ) {
#ifndef MONITOR_CONSTANT_DELAY
int jump = chan->outsmpl - chan->insmpl - 2 * f->samples;
if (jump >= 0) {
if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
chan->insmpl += jump + 2 * f->samples;
} else
chan->insmpl+= f->samples;
#else
int jump = chan->outsmpl - chan->insmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
chan->insmpl += jump;
} else
chan->insmpl += f->samples;
#endif
if (ast_writestream(chan->monitor->read_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
}
if (chan->pvt->readtrans) {
f = ast_translate(chan->pvt->readtrans, f, 1);
if (!f)
f = &null_frame;
}
/* Make sure we always return NULL in the future */
chan->_softhangup |= AST_SOFTHANGUP_DEV;
if (chan->generator)
/* End the CDR if appropriate */
if (chan->cdr)
ast_cdr_end(chan->cdr);
} else if (chan->deferdtmf && f->frametype == AST_FRAME_DTMF) {
if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
else
ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
f = &null_frame;
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
/* Answer the CDR */
/* Run any generator sitting on the line */
if (f && (f->frametype == AST_FRAME_VOICE) && chan->generatordata) {
/* Mask generator data temporarily */
void *tmp;
int res;
int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
tmp = chan->generatordata;
chan->generatordata = NULL;
generate = chan->generator->generate;
res = generate(chan, tmp, f->datalen, f->samples);
chan->generatordata = tmp;
if (res) {
ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
if (chan->fin & 0x80000000)
ast_frame_dump(chan->name, f, "<<");
if ((chan->fin & 0x7fffffff) == 0x7fffffff)
chan->fin &= 0x80000000;
else
chan->fin++;
int ast_indicate(struct ast_channel *chan, int condition)
{
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_unlock(&chan->lock);
if (!chan->pvt->indicate || res) {
/*
* Device does not support (that) indication, lets fake
* it by doing our own tone generation. (PM2002)
*/
if (condition >= 0) {
const struct tone_zone_sound *ts = NULL;
switch (condition) {
case AST_CONTROL_RINGING:
ts = ast_get_indication_tone(chan->zone, "ring");
break;
case AST_CONTROL_BUSY:
ts = ast_get_indication_tone(chan->zone, "busy");
break;
case AST_CONTROL_CONGESTION:
ts = ast_get_indication_tone(chan->zone, "congestion");
break;
}
if (ts && ts->data[0]) {
ast_log(LOG_DEBUG, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
ast_playtones_start(chan,0,ts->data, 1);
} else if (condition == AST_CONTROL_PROGRESS) {
/* ast_playtones_stop(chan); */
} else {
/* not handled */
ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
}
}
else ast_playtones_stop(chan);
}
int ast_recvchar(struct ast_channel *chan, int timeout)
{
int res,ourto,c;
struct ast_frame *f;
ourto = timeout;
for(;;)
{
if (ast_check_hangup(chan)) return -1;
res = ast_waitfor(chan,ourto);
if (res <= 0) /* if timeout */
{
return 0;
}
ourto = res;
f = ast_read(chan);
if (f == NULL) return -1; /* if hangup */
if ((f->frametype == AST_FRAME_CONTROL) &&
(f->subclass == AST_CONTROL_HANGUP)) return -1; /* if hangup */
if (f->frametype == AST_FRAME_TEXT) /* if a text frame */
ast_frfree(f);
return(c);
}
ast_frfree(f);
}
}
int ast_sendtext(struct ast_channel *chan, char *text)
{
int res = 0;
/* Stop if we're a zombie or need a soft hangup */
CHECK_BLOCKING(chan);
if (chan->pvt->send_text)
res = chan->pvt->send_text(chan, text);
chan->blocking = 0;
return res;
}
static int do_senddigit(struct ast_channel *chan, char digit)
{
int res = -1;
if (chan->pvt->send_digit)
res = chan->pvt->send_digit(chan, digit);
if (!chan->pvt->send_digit || res) {
/*
* Device does not support DTMF tones, lets fake
* it by doing our own generation. (PM2002)
*/
static const char* dtmf_tones[] = {
"!941+1336/100,!0/100", /* 0 */
"!697+1209/100,!0/100", /* 1 */
"!697+1336/100,!0/100", /* 2 */
"!697+1477/100,!0/100", /* 3 */
"!770+1209/100,!0/100", /* 4 */
"!770+1336/100,!0/100", /* 5 */
"!770+1477/100,!0/100", /* 6 */
"!852+1209/100,!0/100", /* 7 */
"!852+1336/100,!0/100", /* 8 */
"!852+1477/100,!0/100", /* 9 */
"!697+1633/100,!0/100", /* A */
"!770+1633/100,!0/100", /* B */
"!852+1633/100,!0/100", /* C */
"!941+1633/100,!0/100", /* D */
"!941+1209/100,!0/100", /* * */
"!941+1477/100,!0/100" }; /* # */
ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0);
ast_playtones_start(chan,0,dtmf_tones[14], 0);
ast_playtones_start(chan,0,dtmf_tones[15], 0);
ast_log(LOG_DEBUG, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name);
int ast_senddigit(struct ast_channel *chan, char digit)
{
return do_senddigit(chan, digit);
}
int ast_prod(struct ast_channel *chan)
{
struct ast_frame a = { AST_FRAME_VOICE };
char nothing[128];
/* Send an empty audio frame to get things moving */
if (chan->_state != AST_STATE_UP) {
ast_log(LOG_DEBUG, "Prodding channel '%s'\n", chan->name);
a.subclass = chan->pvt->rawwriteformat;
a.data = nothing + AST_FRIENDLY_OFFSET;
if (ast_write(chan, &a))
ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
{
int res;
if (!chan->pvt->write_video)
return 0;
res = ast_write(chan, fr);
if (!res)
res = 1;
return res;
}
int ast_write(struct ast_channel *chan, struct ast_frame *fr)
{
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
if (chan->zombie || ast_check_hangup(chan)) {
ast_mutex_unlock(&chan->lock);
/* Handle any pending masquerades */
if (chan->masq) {
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
ast_mutex_unlock(&chan->lock);
ast_mutex_unlock(&chan->lock);
if (chan->generatordata) {
if (chan->writeinterrupt)
ast_mutex_unlock(&chan->lock);
if (chan->fout & 0x80000000)
ast_frame_dump(chan->name, fr, ">>");
CHECK_BLOCKING(chan);
switch(fr->frametype) {
case AST_FRAME_CONTROL:
/* XXX Interpret control frames XXX */
ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
break;
case AST_FRAME_DTMF:
case AST_FRAME_TEXT:
if (chan->pvt->send_text)
res = chan->pvt->send_text(chan, (char *) fr->data);
break;
case AST_FRAME_VIDEO:
/* XXX Handle translation of video codecs one day XXX */
if (chan->pvt->write_video)
res = chan->pvt->write_video(chan, fr);
else
res = 0;
break;
if (chan->pvt->write) {
if (chan->pvt->writetrans) {
f = ast_translate(chan->pvt->writetrans, fr, 0);
if( chan->monitor &&
chan->monitor->write_stream &&
f && ( f->frametype == AST_FRAME_VOICE ) ) {
#ifndef MONITOR_CONSTANT_DELAY
int jump = chan->insmpl - chan->outsmpl - 2 * f->samples;
if (jump >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump + f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump + 2 * f->samples;
} else
chan->outsmpl += f->samples;
#else
int jump = chan->insmpl - chan->outsmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump;
} else
chan->outsmpl += f->samples;
#endif
if (ast_writestream(chan->monitor->write_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
}
if (f && (f != fr))
ast_frfree(f);
/* Consider a write failure to force a soft hangup */
if (res < 0)
else {
if ((chan->fout & 0x7fffffff) == 0x7fffffff)
chan->fout &= 0x80000000;
else
chan->fout++;
ast_mutex_unlock(&chan->lock);
Mark Spencer
committed
int ast_set_write_format(struct ast_channel *chan, int fmts)
{
int fmt;
int native;
int res;
Mark Spencer
committed
ast_mutex_lock(&chan->lock);
native = chan->nativeformats;
fmt = fmts;
res = ast_translator_best_choice(&native, &fmt);
if (res < 0) {
ast_log(LOG_NOTICE, "Unable to find a path from %s to %s\n",
ast_getformatname(fmts), ast_getformatname(chan->nativeformats));
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
/* Now we have a good choice for both. We'll write using our native format. */
chan->pvt->rawwriteformat = native;
/* User perspective is fmt */
chan->writeformat = fmt;
/* Free any write translation we have right now */
if (chan->pvt->writetrans)
ast_translator_free_path(chan->pvt->writetrans);
/* Build a translation path from the user write format to the raw writing format */
chan->pvt->writetrans = ast_translator_build_path(chan->pvt->rawwriteformat, chan->writeformat);
ast_log(LOG_DEBUG, "Set channel %s to write format %s\n", chan->name, ast_getformatname(chan->writeformat));
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
Mark Spencer
committed
int ast_set_read_format(struct ast_channel *chan, int fmts)
{
int fmt;
int native;
int res;
Mark Spencer
committed
ast_mutex_lock(&chan->lock);
native = chan->nativeformats;
fmt = fmts;
/* Find a translation path from the native read format to one of the user's read formats */
res = ast_translator_best_choice(&fmt, &native);
if (res < 0) {
ast_log(LOG_NOTICE, "Unable to find a path from %s to %s\n",
ast_getformatname(chan->nativeformats), ast_getformatname(fmts));
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
/* Now we have a good choice for both. We'll write using our native format. */
chan->pvt->rawreadformat = native;
/* User perspective is fmt */
chan->readformat = fmt;
/* Free any read translation we have right now */
if (chan->pvt->readtrans)
ast_translator_free_path(chan->pvt->readtrans);
/* Build a translation path from the raw read format to the user reading format */
chan->pvt->readtrans = ast_translator_build_path(chan->readformat, chan->pvt->rawreadformat);
ast_log(LOG_DEBUG, "Set channel %s to read format %s\n",
chan->name, ast_getformatname(chan->readformat));
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
Martin Pycko
committed
struct ast_channel *__ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate, char *callerid, struct outgoing_helper *oh)
{
int state = 0;
struct ast_channel *chan;
struct ast_frame *f;
chan = ast_request(type, format, data);
if (chan) {
Martin Pycko
committed
if (oh) {
char *tmp, *var;
/* JDG chanvar */
if (oh->variable)
variable = ast_strdupa(oh->variable);
else
variable = NULL;
tmp = variable;
/* FIXME replace this call with strsep NOT*/
Martin Pycko
committed
while( (var = strtok_r(NULL, "|", &tmp)) ) {
pbx_builtin_setvar( chan, var );
} /* /JDG */
Martin Pycko
committed
if (oh->callerid && *oh->callerid)
Martin Pycko
committed
ast_set_callerid(chan, oh->callerid, 1);
Martin Pycko
committed
if (oh->account && *oh->account)
ast_cdr_setaccount(chan, oh->account);
Martin Pycko
committed
}
if (callerid && !ast_strlen_zero(callerid))
Martin Pycko
committed
if (!ast_call(chan, data, 0)) {
while(timeout && (chan->_state != AST_STATE_UP)) {
res = ast_waitfor(chan, timeout);
if (res < 0) {
/* Something not cool, or timed out */
break;
}
/* If done, break out */
if (!res)
break;
if (timeout > -1)
timeout = res;
f = ast_read(chan);
if (!f) {
state = AST_CONTROL_HANGUP;
break;
}
if (f->frametype == AST_FRAME_CONTROL) {
if (f->subclass == AST_CONTROL_RINGING)
state = AST_CONTROL_RINGING;
else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
state = f->subclass;
break;
} else if (f->subclass == AST_CONTROL_ANSWER) {
state = f->subclass;
} else if (f->subclass == AST_CONTROL_PROGRESS) {
/* Ignore */
} else if (f->subclass == -1) {
/* Ignore -- just stopping indications */
} else {
ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
}
}
ast_frfree(f);
}
ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
} else
ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
if (chan) {
/* Final fixups */
if (oh) {
if (oh->context && *oh->context)
strncpy(chan->context, oh->context, sizeof(chan->context) - 1);
if (oh->exten && *oh->exten)
strncpy(chan->exten, oh->exten, sizeof(chan->exten) - 1);
chan->priority = oh->priority;
}
if (chan->_state == AST_STATE_UP)
state = AST_CONTROL_ANSWER;
}
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
if (chan && res <= 0) {
if (!chan->cdr) {
chan->cdr = ast_cdr_alloc();
if (chan->cdr)
ast_cdr_init(chan->cdr, chan);
}
if (chan->cdr) {
char tmp[256];
sprintf(tmp, "%s/%s",type,(char *)data);
ast_cdr_setapp(chan->cdr,"Dial",tmp);
ast_cdr_update(chan);
ast_cdr_start(chan->cdr);
ast_cdr_end(chan->cdr);
/* If the cause wasn't handled properly */
if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
ast_cdr_failed(chan->cdr);
} else
ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
ast_hangup(chan);
chan = NULL;
}
Martin Pycko
committed
struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate, char *callerid)
{
return __ast_request_and_dial(type, format, data, timeout, outstate, callerid, NULL);
}
struct ast_channel *ast_request(char *type, int format, void *data)
{
struct chanlist *chan;
struct ast_channel *c = NULL;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return NULL;
}
chan = backends;
while(chan) {
if (!strcasecmp(type, chan->type)) {
capabilities = chan->capabilities;
fmt = format;
res = ast_translator_best_choice(&fmt, &capabilities);
if (res < 0) {
ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->capabilities, format);
ast_mutex_unlock(&chlock);
ast_mutex_unlock(&chlock);
if (c->_state == AST_STATE_DOWN) {
manager_event(EVENT_FLAG_CALL, "Newchannel",
"Channel: %s\r\n"
"State: %s\r\n"
"Callerid: %s\r\n"
"Uniqueid: %s\r\n",
c->name, ast_state2str(c->_state), c->callerid ? c->callerid : "<unknown>", c->uniqueid);
}
}
chan = chan->next;
}
if (!chan)
ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
ast_mutex_unlock(&chlock);
int ast_parse_device_state(char *device)
{
char name[AST_CHANNEL_NAME] = "";
char *cut;
struct ast_channel *chan;
chan = ast_channel_walk_locked(NULL);
while (chan) {
strncpy(name, chan->name, sizeof(name)-1);
ast_mutex_unlock(&chan->lock);
cut = strchr(name,'-');
if (cut)
if (!strcmp(name, device))
chan = ast_channel_walk_locked(chan);
}
return AST_DEVICE_UNKNOWN;
}
int ast_device_state(char *device)
{
char tech[AST_MAX_EXTENSION] = "";
char *number;
struct chanlist *chanls;
int res = 0;
strncpy(tech, device, sizeof(tech)-1);
number = strchr(tech, '/');
if (!number) {
}
*number = 0;
number++;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chanls = backends;
while(chanls) {
if (!strcasecmp(tech, chanls->type)) {
ast_mutex_unlock(&chlock);
if (!chanls->devicestate)
return ast_parse_device_state(device);
else {
res = chanls->devicestate(number);
if (res == AST_DEVICE_UNKNOWN)
return ast_parse_device_state(device);
else
return res;
}
}
chanls = chanls->next;
}
ast_mutex_unlock(&chlock);
return AST_DEVICE_INVALID;
}
int ast_call(struct ast_channel *chan, char *addr, int timeout)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
If the remote end does not answer within the timeout, then do NOT hang up, but
return anyway. */
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
if (chan->pvt->call)
res = chan->pvt->call(chan, addr, timeout);
ast_mutex_unlock(&chan->lock);
int ast_transfer(struct ast_channel *chan, char *dest)
{
/* Place an outgoing call, but don't wait any longer than timeout ms before returning.
If the remote end does not answer within the timeout, then do NOT hang up, but
return anyway. */
int res = -1;
/* Stop if we're a zombie or need a soft hangup */
ast_mutex_lock(&chan->lock);
if (!chan->zombie && !ast_check_hangup(chan)) {
if (chan->pvt->transfer) {
res = chan->pvt->transfer(chan, dest);
if (!res)
res = 1;
} else
res = 0;
}
ast_mutex_unlock(&chan->lock);
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
{
int pos=0;
int to = ftimeout;
char d;
/* XXX Merge with full version? XXX */
/* Stop if we're a zombie or need a soft hangup */
d = ast_waitstream(c, AST_DIGIT_ANY);
ast_stopstream(c);
if (!d)
d = ast_waitfordigit(c, to);
} else {
d = ast_waitfordigit(c, to);
}
if (d < 0)
return -1;
if (d == 0) {
s[pos]='\0';
return 1;
}
return 0;
}
to = timeout;
} while(1);
/* Never reached */
return 0;
}
int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
{
int pos=0;
int to = ftimeout;
char d;
/* Stop if we're a zombie or need a soft hangup */
if (c->zombie || ast_check_hangup(c))
return -1;
if (!len)
return -1;
do {
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
ast_stopstream(c);
usleep(1000);
if (!d)
d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
} else {
d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
}
if (d < 0)
return -1;
if (d == 0) {
s[pos]='\0';
return 1;
}
if (d == 1) {
s[pos]='\0';
return 2;
}
if (!strchr(enders, d))
s[pos++] = d;
if (strchr(enders, d) || (pos >= len)) {
s[pos]='\0';
return 0;
}
to = timeout;
} while(1);
/* Never reached */
return 0;
}
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
int ast_channel_supports_html(struct ast_channel *chan)
{
if (chan->pvt->send_html)
return 1;
return 0;
}
int ast_channel_sendhtml(struct ast_channel *chan, int subclass, char *data, int datalen)
{
if (chan->pvt->send_html)
return chan->pvt->send_html(chan, subclass, data, datalen);
return -1;
}
int ast_channel_sendurl(struct ast_channel *chan, char *url)
{
if (chan->pvt->send_html)
return chan->pvt->send_html(chan, AST_HTML_URL, url, strlen(url) + 1);
return -1;
}
int ast_channel_make_compatible(struct ast_channel *chan, struct ast_channel *peer)
{
int peerf;
int chanf;
int res;
ast_mutex_lock(&peer->lock);
ast_mutex_unlock(&peer->lock);
ast_mutex_lock(&chan->lock);
ast_mutex_unlock(&chan->lock);
res = ast_translator_best_choice(&peerf, &chanf);
if (res < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", chan->name, chan->nativeformats, peer->name, peer->nativeformats);
return -1;
}
/* Set read format on channel */
Mark Spencer
committed
res = ast_set_read_format(chan, peerf);
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", chan->name, chanf);
return -1;
}
/* Set write format on peer channel */
Mark Spencer
committed
res = ast_set_write_format(peer, peerf);
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", peer->name, peerf);
return -1;
}
/* Now we go the other way */
peerf = peer->nativeformats;
chanf = chan->nativeformats;
res = ast_translator_best_choice(&chanf, &peerf);
if (res < 0) {
ast_log(LOG_WARNING, "No path to translate from %s(%d) to %s(%d)\n", peer->name, peer->nativeformats, chan->name, chan->nativeformats);
return -1;
}
/* Set writeformat on channel */
Mark Spencer
committed
res = ast_set_write_format(chan, chanf);
ast_log(LOG_WARNING, "Unable to set write format on channel %s to %d\n", chan->name, chanf);
return -1;
}
/* Set read format on peer channel */
Mark Spencer
committed
res = ast_set_read_format(peer, chanf);
ast_log(LOG_WARNING, "Unable to set read format on channel %s to %d\n", peer->name, peerf);
int ast_channel_masquerade(struct ast_channel *original, struct ast_channel *clone)
{
struct ast_frame null = { AST_FRAME_NULL, };
int res = -1;
ast_mutex_lock(&original->lock);
while(ast_mutex_trylock(&clone->lock)) {
ast_mutex_unlock(&original->lock);
usleep(1);
ast_mutex_lock(&original->lock);
}
ast_log(LOG_DEBUG, "Planning to masquerade %s into the structure of %s\n",
clone->name, original->name);
if (original->masq) {
ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
original->masq->name, original->name);
} else if (clone->masqr) {
ast_log(LOG_WARNING, "%s is already going to masquerade as %s\n",
clone->name, clone->masqr->name);
} else {
original->masq = clone;
clone->masqr = original;
ast_queue_frame(original, &null);
ast_queue_frame(clone, &null);
ast_log(LOG_DEBUG, "Done planning to masquerade %s into the structure of %s\n", original->name, clone->name);
res = 0;
}
ast_mutex_unlock(&clone->lock);
ast_mutex_unlock(&original->lock);
return res;
void ast_change_name(struct ast_channel *chan, char *newname)
{
char tmp[256];
strncpy(tmp, chan->name, 256);
strncpy(chan->name, newname, sizeof(chan->name) - 1);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\nUniqueid: %s\r\n", tmp, chan->name, chan->uniqueid);
Mark Spencer
committed
int ast_do_masquerade(struct ast_channel *original)
struct ast_channel_pvt *p;
struct ast_channel *clone = original->masq;
int rformat = original->readformat;
int wformat = original->writeformat;
char newn[100];
char orig[100];
char masqn[100];
char zombn[100];
ast_log(LOG_DEBUG, "Actually Masquerading %s(%d) into the structure of %s(%d)\n",
clone->name, clone->_state, original->name, original->_state);
/* XXX This is a seriously wacked out operation. We're essentially putting the guts of
the clone channel into the original channel. Start by killing off the original
channel's backend. I'm not sure we're going to keep this function, because
while the features are nice, the cost is very high in terms of pure nastiness. XXX */
Mark Spencer
committed
/* We need the clone's lock, too */
ast_mutex_lock(&clone->lock);
Mark Spencer
committed
ast_log(LOG_DEBUG, "Got clone lock on '%s' at %p\n", clone->name, &clone->lock);
/* Having remembered the original read/write formats, we turn off any translation on either
one */
free_translation(clone);
free_translation(original);
/* Unlink the masquerade */
original->masq = NULL;
clone->masqr = NULL;
/* Save the original name */
strncpy(orig, original->name, sizeof(orig) - 1);
/* Save the new name */
strncpy(newn, clone->name, sizeof(newn) - 1);
/* Create the masq name */
snprintf(masqn, sizeof(masqn), "%s<MASQ>", newn);
strncpy(original->name, newn, sizeof(original->name)-1);
strncpy(clone->name, masqn, sizeof(clone->name) - 1);
/* Notify any managers of the change, first the masq then the other */
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", newn, masqn);
manager_event(EVENT_FLAG_CALL, "Rename", "Oldname: %s\r\nNewname: %s\r\n", orig, newn);
p = original->pvt;
original->pvt = clone->pvt;
clone->pvt = p;
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
/* Save any pending frames on both sides. Start by counting
* how many we're going to need... */
prev = NULL;
cur = clone->pvt->readq;
x = 0;
while(cur) {
x++;
prev = cur;
cur = cur->next;
}
/* If we had any, prepend them to the ones already in the queue, and
* load up the alertpipe */
if (prev) {
prev->next = original->pvt->readq;
original->pvt->readq = clone->pvt->readq;
clone->pvt->readq = NULL;
if (original->pvt->alertpipe[1] > -1) {
for (i=0;i<x;i++)
write(original->pvt->alertpipe[1], &x, sizeof(x));
}
}
Loading
Loading full blame...