Newer
Older
Joshua Colp
committed
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
}
/* Allocate memory for datastore and clear it */
datastore = ast_calloc(1, sizeof(*datastore));
if (datastore == NULL) {
return NULL;
}
datastore->info = info;
if (uid != NULL) {
datastore->uid = ast_strdup(uid);
}
return datastore;
}
int ast_channel_datastore_free(struct ast_datastore *datastore)
{
int res = 0;
/* Using the destroy function (if present) destroy the data */
if (datastore->info->destroy != NULL && datastore->data != NULL) {
datastore->info->destroy(datastore->data);
datastore->data = NULL;
}
/* Free allocated UID memory */
if (datastore->uid != NULL) {
free(datastore->uid);
datastore->uid = NULL;
}
/* Finally free memory used by ourselves */
free(datastore);
datastore = NULL;
return res;
}
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
{
int res = 0;
Joshua Colp
committed
AST_LIST_INSERT_HEAD(&chan->datastores, datastore, entry);
Joshua Colp
committed
return res;
}
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
{
struct ast_datastore *datastore2 = NULL;
int res = -1;
/* Find our position and remove ourselves */
Joshua Colp
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore2, entry) {
Joshua Colp
committed
if (datastore2 == datastore) {
Joshua Colp
committed
AST_LIST_REMOVE_CURRENT(&chan->datastores, entry);
Joshua Colp
committed
res = 0;
break;
}
}
AST_LIST_TRAVERSE_SAFE_END
return res;
}
struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid)
{
struct ast_datastore *datastore = NULL;
if (info == NULL)
return NULL;
Joshua Colp
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, entry) {
Joshua Colp
committed
if (datastore->info == info) {
if (uid != NULL && datastore->uid != NULL) {
if (!strcasecmp(uid, datastore->uid)) {
/* Matched by type AND uid */
break;
}
} else {
/* Matched by type at least */
break;
}
}
}
AST_LIST_TRAVERSE_SAFE_END
return datastore;
}
Kevin P. Fleming
committed
int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
Kevin P. Fleming
committed
if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
spy->type, chan->name);
return -1;
}
Kevin P. Fleming
committed
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->read_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->write_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
(spy->write_queue.format != AST_FORMAT_SLINEAR))) {
ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
return -1;
}
if (!chan->spies) {
if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
Kevin P. Fleming
committed
return -1;
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
} else {
AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
Kevin P. Fleming
committed
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
ast_cond_init(&spy->trigger, NULL);
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
}
ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
spy->type, chan->name);
return 0;
}
void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
{
struct ast_channel_spy *spy;
if (!chan->spies)
return;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
Kevin P. Fleming
committed
ast_mutex_lock(&spy->lock);
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
Kevin P. Fleming
committed
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_signal(&spy->trigger);
}
ast_mutex_unlock(&spy->lock);
Kevin P. Fleming
committed
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
}
}
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
{
ast_cond_wait(&spy->trigger, &spy->lock);
}
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
{
struct ast_frame *f;
if (!chan->spies)
return;
AST_LIST_REMOVE(&chan->spies->list, spy, list);
ast_mutex_lock(&spy->lock);
for (f = spy->read_queue.head; f; f = spy->read_queue.head) {
spy->read_queue.head = f->next;
ast_frfree(f);
}
for (f = spy->write_queue.head; f; f = spy->write_queue.head) {
spy->write_queue.head = f->next;
ast_frfree(f);
}
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger);
ast_mutex_unlock(&spy->lock);
ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
spy->type, chan->name);
if (AST_LIST_EMPTY(&chan->spies->list)) {
if (chan->spies->read_translator.path)
ast_translator_free_path(chan->spies->read_translator.path);
if (chan->spies->write_translator.path)
ast_translator_free_path(chan->spies->write_translator.path);
free(chan->spies);
chan->spies = NULL;
}
}
static void detach_spies(struct ast_channel *chan)
Kevin P. Fleming
committed
{
struct ast_channel_spy *spy;
if (!chan->spies)
return;
/* Marking the spies as done is sufficient. Chanspy or spy users will get the picture. */
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
ast_mutex_lock(&spy->lock);
if (spy->status == CHANSPY_RUNNING)
spy->status = CHANSPY_DONE;
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_signal(&spy->trigger);
ast_mutex_unlock(&spy->lock);
}
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
ast_channel_spy_remove(chan, spy);
AST_LIST_TRAVERSE_SAFE_END;
/*! \brief Softly hangup a channel, don't lock */
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 */
ast_queue_frame(chan, &ast_null_frame);
/* Interrupt any poll call or such */
if (ast_test_flag(chan, AST_FLAG_BLOCKING))
/*! \brief Softly hangup a channel, lock */
int ast_softhangup(struct ast_channel *chan, int cause)
{
int res;
Kevin P. Fleming
committed
enum spy_direction {
SPY_READ,
SPY_WRITE,
};
#define SPY_QUEUE_SAMPLE_LIMIT 4000 /* half of one second */
static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f, enum spy_direction dir)
Kevin P. Fleming
committed
struct ast_frame *translated_frame = NULL;
struct ast_channel_spy *spy;
struct channel_spy_trans *trans;
Kevin P. Fleming
committed
trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
struct ast_frame *last;
struct ast_frame *f1; /* the frame to append */
struct ast_channel_spy_queue *queue;
Kevin P. Fleming
committed
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
ast_mutex_lock(&spy->lock);
queue = (dir == SPY_READ) ? &spy->read_queue : &spy->write_queue;
if ((queue->format == AST_FORMAT_SLINEAR) && (f->subclass != AST_FORMAT_SLINEAR)) {
if (!translated_frame) {
if (trans->path && (trans->last_format != f->subclass)) {
ast_translator_free_path(trans->path);
trans->path = NULL;
}
if (!trans->path) {
ast_log(LOG_DEBUG, "Building translator from %s to SLINEAR for spies on channel %s\n",
ast_getformatname(f->subclass), chan->name);
if ((trans->path = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass)) == NULL) {
ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n",
ast_getformatname(f->subclass), ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
continue;
} else {
trans->last_format = f->subclass;
}
}
if (!(translated_frame = ast_translate(trans->path, f, 0))) {
ast_log(LOG_ERROR, "Translation to %s failed, dropping frame for spies\n",
ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
break;
}
Kevin P. Fleming
committed
}
f1 = translated_frame;
Kevin P. Fleming
committed
} else {
if (f->subclass != queue->format) {
ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
spy->type, chan->name,
ast_getformatname(queue->format), ast_getformatname(f->subclass));
ast_mutex_unlock(&spy->lock);
continue;
}
/* duplicate and append f1 to the tail */
f1 = ast_frdup(f1);
for (last = queue->head; last && last->next; last = last->next)
;
if (last)
last->next = f1;
else
queue->head = f1;
Kevin P. Fleming
committed
queue->samples += f->samples;
if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_WRITE) {
ast_set_flag(spy, CHANSPY_TRIGGER_WRITE);
ast_clear_flag(spy, CHANSPY_TRIGGER_READ);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to write-trigger mode\n",
spy->type, chan->name);
Kevin P. Fleming
committed
}
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_READ) {
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to read-trigger mode\n",
spy->type, chan->name);
}
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Triggering queue flush for spy '%s' on '%s'\n",
spy->type, chan->name);
ast_set_flag(spy, CHANSPY_TRIGGER_FLUSH);
ast_cond_signal(&spy->trigger);
} else {
if (option_debug)
ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
struct ast_frame *drop = queue->head;
queue->samples -= drop->samples;
queue->head = drop->next;
ast_frfree(drop);
Kevin P. Fleming
committed
}
}
} else {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_READ)
ast_cond_signal(&spy->trigger);
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_WRITE)
ast_cond_signal(&spy->trigger);
break;
}
}
ast_mutex_unlock(&spy->lock);
Kevin P. Fleming
committed
if (translated_frame)
ast_frfree(translated_frame);
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 */
Kevin P. Fleming
committed
detach_spies(chan); /* get rid of spies */
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);
/* 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) /* Close audio stream */
if (chan->vstream) /* Close video stream */
if (chan->sched) {
chan->sched = NULL;
}
if (chan->generatordata) /* Clear any tone stuff remaining */
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_detach(chan->cdr); /* Post and Free the CDR */
Kevin P. Fleming
committed
chan->cdr = NULL;
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);
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
manager_event(EVENT_FLAG_CALL, "Hangup",
"Cause: %d\r\n"
"Cause-txt: %s\r\n",
chan->name,
chan->uniqueid,
chan->hangupcause,
ast_cause2str(chan->hangupcause)
);
return res;
}
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);
chan->fds[AST_GENERATOR_FD] = -1;
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)
{
Kevin P. Fleming
committed
Kevin P. Fleming
committed
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
Kevin P. Fleming
committed
if (gen->alloc && !(chan->generatordata = gen->alloc(chan, params))) {
res = -1;
Kevin P. Fleming
committed
}
if (!res) {
ast_settimeout(chan, 160, generator_force, chan);
Kevin P. Fleming
committed
Kevin P. Fleming
committed
/*! \brief Wait for x amount of time on a file descriptor to have input. */
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
/*! \brief Wait for x amount of time on a file descriptor to have input. */
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
struct pollfd *pfds;
long rms;
int x, y, max;
time_t now = 0;
struct fdmap {
int chan;
int fdno;
} *fdmap;
sz = n * AST_MAX_FDS + nfds;
if (!(pfds = alloca(sizeof(*pfds) * sz)) || !(fdmap = alloca(sizeof(*fdmap) * sz))) {
ast_log(LOG_ERROR, "Out of memory\n");
*outfd = -1;
return NULL;
}
Mark Spencer
committed
if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n");
*ms = -1;
if (c[x]->whentohangup) {
if (!whentohangup)
time(&now);
diff = c[x]->whentohangup - now;
if (diff < 1) {
/* Should already be hungup */
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
return c[x];
}
if (!whentohangup || (diff < whentohangup))
whentohangup = diff;
}
if (whentohangup) {
rms = (whentohangup - now) * 1000; /* timeout in milliseconds */
if (*ms >= 0 && *ms < rms) /* original *ms still smaller */
rms = *ms;
/*
* Build the pollfd array, putting the channels' fds first,
* followed by individual fds. Order is important because
* individual fd's must have priority over channel fds.
*/
for (x=0; x<n; x++) {
for (y=0; y<AST_MAX_FDS; y++) {
fdmap[max].fdno = y; /* fd y is linked to this pfds */
fdmap[max].chan = x; /* channel x is linked to this pfds */
max += ast_add_fd(&pfds[max], c[x]->fds[y]);
/* Add the individual fds */
for (x=0; x<nfds; x++) {
fdmap[max].chan = -1;
max += ast_add_fd(&pfds[max], fds[x]);
if (*ms > 0)
Kevin P. Fleming
committed
start = ast_tvnow();
if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
do {
int kbrms = rms;
if (kbrms > 600000)
kbrms = 600000;
res = poll(pfds, max, kbrms);
if (!res)
rms -= kbrms;
} while (!res && (rms > 0));
} else {
res = poll(pfds, max, rms);
}
for (x=0; x<n; x++)
ast_clear_flag(c[x], AST_FLAG_BLOCKING);
if (res < 0) { /* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
if (whentohangup) { /* if we have a timeout, check who expired */
time(&now);
for (x=0; x<n; x++) {
if (c[x]->whentohangup && now >= c[x]->whentohangup) {
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
if (winner == NULL)
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
if (res == 0) { /* no fd ready, reset timeout and done */
*ms = 0; /* XXX use 0 since we may not have an exact timeout. */
return winner;
}
/*
* Then check if any channel or fd has a pending event.
* Remember to check channels first and fds last, as they
* must have priority on setting 'winner'
*/
for (x = 0; x < max; x++) {
res = pfds[x].revents;
if (res == 0)
continue;
if (fdmap[x].chan >= 0) { /* this is a channel */
winner = c[fdmap[x].chan]; /* override previous winners */
if (res & POLLPRI)
ast_set_flag(winner, AST_FLAG_EXCEPTION);
else
ast_clear_flag(winner, AST_FLAG_EXCEPTION);
winner->fdno = fdmap[x].fdno;
} else { /* this is an fd */
if (outfd)
*outfd = pfds[x].fd;
if (exception)
*exception = (res & POLLPRI) ? -1 : 0;
winner = NULL;
}
}
if (*ms > 0) {
Kevin P. Fleming
committed
*ms -= ast_tvdiff_ms(ast_tvnow(), start);
if (*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)
{
int oldms = ms; /* -1 if no timeout */
ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
if ((ms < 0) && (oldms < 0))
ms = 0;
/* XXX never to be called with ms = -1 */
Kevin P. Fleming
committed
int ast_waitfordigit(struct ast_channel *c, int ms)
return ast_waitfordigit_full(c, ms, -1, -1);
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
Kevin P. Fleming
committed
#ifdef HAVE_ZAPTEL
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;
}
Kevin P. Fleming
committed
int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
return -1;
/* Wait for a digit, no more than ms milliseconds total. */
while (ms) {
struct ast_channel *rchan;
int outfd;
Kevin P. Fleming
committed
errno = 0;
rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
if (!rchan && outfd < 0 && ms) {
Kevin P. Fleming
committed
if (errno == 0 || errno == EINTR)
continue;
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;
int res;
struct ast_frame *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);
return 0; /* Time is up */
static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
struct ast_frame *f = NULL; /* the return value */
/* this function is very long so make sure there is only one return
* point at the end (there is only one exception to this).
*/
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
} else {
f = &ast_null_frame;
}
goto done;
}
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
goto done;
if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !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 from the buffer */
memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
f = &chan->dtmff;
goto done;
/* Read and ignore anything on the alertpipe, but read only
one sizeof(blah) per frame that we send from it */
if (chan->alertpipe[0] > -1)
read(chan->alertpipe[0], &blah, sizeof(blah));
Kevin P. Fleming
committed
#ifdef HAVE_ZAPTEL
if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
ast_clear_flag(chan, AST_FLAG_EXCEPTION);
/* IF we can't get event, assume it's an expired as-per the old interface */
res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
blah = ZT_EVENT_TIMER_EXPIRED;
if (blah == ZT_EVENT_TIMER_PING) {
if (!chan->readq || !chan->readq->next) {
/* Acknowledge PONG unless we need it again */
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);
if (chan->timingfunc) {
/* save a copy of func/data before unlocking the channel */
int (*func)(void *) = chan->timingfunc;
void *data = chan->timingdata;
ast_channel_unlock(chan);
func(data);
} else {
blah = 0;
ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
chan->timingdata = NULL;
}
/* cannot 'goto done' because the channel is already unlocked */
return &ast_null_frame;
} else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
/* if the AST_GENERATOR_FD is set, call the generator with args
* set to -1 so it can do whatever it needs to.
*/
void *tmp = chan->generatordata;
chan->generatordata = NULL; /* reset to let ast_write get through */
chan->generator->generate(chan, tmp, -1, -1);
chan->generatordata = tmp;
f = &ast_null_frame;
goto done;
if (chan->readq) {
f = chan->readq;
chan->readq = f->next;
Kevin P. Fleming
committed
f->next = NULL;
if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
if (chan->tech->exception)
f = chan->tech->exception(chan);
else {
ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
f = &ast_null_frame;
ast_clear_flag(chan, AST_FLAG_EXCEPTION);
} else if (chan->tech->read)
f = chan->tech->read(chan);
else
ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
Kevin P. Fleming
committed
if (f) {
/* if the channel driver returned more than one frame, stuff the excess
into the readq for the next ast_read call
*/
if (f->next) {
chan->readq = f->next;
f->next = NULL;
}
Kevin P. Fleming
committed
switch (f->frametype) {
case AST_FRAME_CONTROL:
if (f->subclass == AST_CONTROL_ANSWER) {
if (prestate == AST_STATE_UP) {
ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
f = &ast_null_frame;
Kevin P. Fleming
committed
}
/* Answer the CDR */
ast_setstate(chan, AST_STATE_UP);
ast_cdr_answer(chan->cdr);
}
break;
case AST_FRAME_DTMF:
ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
if (ast_test_flag(chan, AST_FLAG_DEFER_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 = &ast_null_frame;
Kevin P. Fleming
committed
}
break;
case AST_FRAME_DTMF_BEGIN:
ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
break;
case AST_FRAME_DTMF_END:
ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
break;
case AST_FRAME_VOICE:
if (dropaudio) {
ast_frfree(f);
f = &ast_null_frame;
Kevin P. Fleming
committed
} else 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));
ast_frfree(f);
f = &ast_null_frame;
Kevin P. Fleming
committed
} else {
if (chan->spies)
queue_frame_to_spies(chan, f, SPY_READ);
if (chan->monitor && chan->monitor->read_stream ) {
Kevin P. Fleming
committed
int jump = chan->outsmpl - chan->insmpl - 4 * 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 + 4 * f->samples;
} else
chan->insmpl+= f->samples;
Kevin P. Fleming
committed
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