Newer
Older
Mark Spencer
committed
/*
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2005, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief Channel Management
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#ifdef __linux__
#include <linux/zaptel.h>
#else
#include <zaptel.h>
#endif /* __linux__ */
#ifndef ZT_TIMERPING
#error "You need newer zaptel! Please cvs update zaptel"
#endif
#endif
Kevin P. Fleming
committed
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
Kevin P. Fleming
committed
#include "asterisk/musiconhold.h"
#include "asterisk/logger.h"
#include "asterisk/say.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/translate.h"
#include "asterisk/manager.h"
#include "asterisk/chanvars.h"
#include "asterisk/linkedlists.h"
#include "asterisk/indications.h"
#include "asterisk/monitor.h"
#include "asterisk/causes.h"
Kevin P. Fleming
committed
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/transcap.h"
Kevin P. Fleming
committed
#include "asterisk/devicestate.h"
Kevin P. Fleming
committed
struct channel_spy_trans {
int last_format;
struct ast_trans_pvt *path;
};
struct ast_channel_spy_list {
struct channel_spy_trans read_translator;
struct channel_spy_trans write_translator;
AST_LIST_HEAD_NOLOCK(, ast_channel_spy) list;
};
/* 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
Kevin P. Fleming
committed
/*
* Prevent new channel allocation if shutting down.
*/
Kevin P. Fleming
committed
Mark Spencer
committed
unsigned long global_fin = 0, global_fout = 0;
/* XXX Lock appropriately in more functions XXX */
const struct ast_channel_tech *tech;
Kevin P. Fleming
committed
};
static struct chanlist *backends = NULL;
/*
* the list of channels we have
*/
static struct ast_channel *channels = NULL;
Kevin P. Fleming
committed
/* Protect the channel list, both backends and channels.
*/
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
const struct ast_cause {
int cause;
const char *desc;
} causes[] = {
{ AST_CAUSE_UNALLOCATED, "Unallocated (unassigned) number" },
{ AST_CAUSE_NO_ROUTE_TRANSIT_NET, "No route to specified transmit network" },
{ AST_CAUSE_NO_ROUTE_DESTINATION, "No route to destination" },
{ AST_CAUSE_CHANNEL_UNACCEPTABLE, "Channel unacceptable" },
{ AST_CAUSE_CALL_AWARDED_DELIVERED, "Call awarded and being delivered in an established channel" },
{ AST_CAUSE_NORMAL_CLEARING, "Normal Clearing" },
{ AST_CAUSE_USER_BUSY, "User busy" },
{ AST_CAUSE_NO_USER_RESPONSE, "No user responding" },
{ AST_CAUSE_NO_ANSWER, "User alerting, no answer" },
{ AST_CAUSE_CALL_REJECTED, "Call Rejected" },
{ AST_CAUSE_NUMBER_CHANGED, "Number changed" },
{ AST_CAUSE_DESTINATION_OUT_OF_ORDER, "Destination out of order" },
{ AST_CAUSE_INVALID_NUMBER_FORMAT, "Invalid number format" },
{ AST_CAUSE_FACILITY_REJECTED, "Facility rejected" },
{ AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "Response to STATus ENQuiry" },
{ AST_CAUSE_NORMAL_UNSPECIFIED, "Normal, unspecified" },
{ AST_CAUSE_NORMAL_CIRCUIT_CONGESTION, "Circuit/channel congestion" },
{ AST_CAUSE_NETWORK_OUT_OF_ORDER, "Network out of order" },
{ AST_CAUSE_NORMAL_TEMPORARY_FAILURE, "Temporary failure" },
{ AST_CAUSE_SWITCH_CONGESTION, "Switching equipment congestion" },
{ AST_CAUSE_ACCESS_INFO_DISCARDED, "Access information discarded" },
{ AST_CAUSE_REQUESTED_CHAN_UNAVAIL, "Requested channel not available" },
{ AST_CAUSE_PRE_EMPTED, "Pre-empted" },
{ AST_CAUSE_FACILITY_NOT_SUBSCRIBED, "Facility not subscribed" },
{ AST_CAUSE_OUTGOING_CALL_BARRED, "Outgoing call barred" },
{ AST_CAUSE_INCOMING_CALL_BARRED, "Incoming call barred" },
{ AST_CAUSE_BEARERCAPABILITY_NOTAUTH, "Bearer capability not authorized" },
{ AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, "Bearer capability not available" },
{ AST_CAUSE_BEARERCAPABILITY_NOTIMPL, "Bearer capability not implemented" },
{ AST_CAUSE_CHAN_NOT_IMPLEMENTED, "Channel not implemented" },
{ AST_CAUSE_FACILITY_NOT_IMPLEMENTED, "Facility not implemented" },
{ AST_CAUSE_INVALID_CALL_REFERENCE, "Invalid call reference value" },
{ AST_CAUSE_INCOMPATIBLE_DESTINATION, "Incompatible destination" },
{ AST_CAUSE_INVALID_MSG_UNSPECIFIED, "Invalid message unspecified" },
{ AST_CAUSE_MANDATORY_IE_MISSING, "Mandatory information element is missing" },
{ AST_CAUSE_MESSAGE_TYPE_NONEXIST, "Message type nonexist." },
{ AST_CAUSE_WRONG_MESSAGE, "Wrong message" },
{ AST_CAUSE_IE_NONEXIST, "Info. element nonexist or not implemented" },
{ AST_CAUSE_INVALID_IE_CONTENTS, "Invalid information element contents" },
{ AST_CAUSE_WRONG_CALL_STATE, "Message not compatible with call state" },
{ AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "Recover on timer expiry" },
{ AST_CAUSE_MANDATORY_IE_LENGTH_ERROR, "Mandatory IE length error" },
{ AST_CAUSE_PROTOCOL_ERROR, "Protocol error, unspecified" },
{ AST_CAUSE_INTERWORKING, "Interworking, unspecified" },
};
static int show_channeltypes(int fd, int argc, char *argv[])
{
#define FORMAT "%-10.10s %-30.30s %-12.12s %-12.12s %-12.12s\n"
ast_cli(fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
ast_cli(fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
ast_cli(fd, FORMAT, cl->tech->type, cl->tech->description,
(cl->tech->devicestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no");
}
ast_mutex_unlock(&chlock);
return RESULT_SUCCESS;
#undef FORMAT
}
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 };
/*--- ast_check_hangup: Checks to see if a channel is needing hang up */
int ast_check_hangup(struct ast_channel *chan)
{
time_t myt;
/* if soft hangup flag, return true */
if (chan->_softhangup)
return 1;
/* 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;
/* 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);
/*--- ast_begin_shutdown: Initiate system shutdown */
void ast_begin_shutdown(int hangup)
{
struct ast_channel *c;
shutting_down = 1;
if (hangup) {
ast_mutex_lock(&chlock);
ast_mutex_unlock(&chlock);
/*--- ast_active_channels: returns number of active/allocated channels */
int ast_active_channels(void)
{
struct ast_channel *c;
int cnt = 0;
ast_mutex_lock(&chlock);
ast_mutex_unlock(&chlock);
/*--- ast_cancel_shutdown: Cancel a shutdown in progress */
void ast_cancel_shutdown(void)
{
shutting_down = 0;
}
/*--- ast_shutting_down: Returns non-zero if Asterisk is being shut down */
int ast_shutting_down(void)
{
return shutting_down;
}
/*--- ast_channel_setwhentohangup: Set when to hangup channel */
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
struct ast_frame fr = { AST_FRAME_NULL, };
if (offset)
chan->whentohangup = myt + offset;
else
chan->whentohangup = 0;
ast_queue_frame(chan, &fr);
/*--- ast_channel_cmpwhentohangup: Compare a offset with when to hangup channel */
int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset)
{
time_t whentohangup;
if (chan->whentohangup == 0) {
if (offset == 0)
return (0);
else
return (-1);
} else {
if (offset == 0)
return (1);
else {
whentohangup = offset + time (NULL);
if (chan->whentohangup < whentohangup)
return (1);
else if (chan->whentohangup == whentohangup)
return (0);
else
return (-1);
}
}
}
/*--- ast_channel_register: Register a new telephony channel in Asterisk */
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);
Kevin P. Fleming
committed
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
Kevin P. Fleming
committed
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
382
383
384
385
386
387
388
389
390
391
392
393
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;
}
ast_mutex_unlock(&chlock);
}
const struct ast_channel_tech *ast_get_channel_tech(const char *name)
{
struct chanlist *chanls;
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
return NULL;
}
for (chanls = backends; chanls; chanls = chanls->next) {
if (strcasecmp(name, chanls->tech->type))
continue;
ast_mutex_unlock(&chlock);
return chanls->tech;
}
ast_mutex_unlock(&chlock);
return NULL;
}
/*--- ast_cause2str: Gives the string form of a given hangup cause */
const char *ast_cause2str(int cause)
{
int x;
for (x=0; x < sizeof(causes) / sizeof(causes[0]); x++)
if (causes[x].cause == cause)
return causes[x].desc;
return "Unknown";
}
/*--- ast_state2str: Gives the string form of a given channel state */
/* 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;
/*--- ast_transfercapability2str: Gives the string form of a given transfer capability */
char *ast_transfercapability2str(int transfercapability)
{
switch(transfercapability) {
case AST_TRANS_CAP_SPEECH:
return "SPEECH";
case AST_TRANS_CAP_DIGITAL:
return "DIGITAL";
case AST_TRANS_CAP_RESTRICTED_DIGITAL:
return "RESTRICTED_DIGITAL";
case AST_TRANS_CAP_3_1K_AUDIO:
return "3K1AUDIO";
case AST_TRANS_CAP_DIGITAL_W_TONES:
return "DIGITAL_W_TONES";
case AST_TRANS_CAP_VIDEO:
return "VIDEO";
default:
return "UNKNOWN";
}
}
/*--- ast_best_codec: Pick the best codec */
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,
};
/* Find the first prefered codec in the format given */
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)",
};
/*--- ast_channel_alloc: Create a new channel structure */
struct ast_channel *ast_channel_alloc(int needqueue)
/* If shutting down, don't allocate any new channels */
if (shutting_down) {
ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
}
if (!tmp) {
ast_log(LOG_WARNING, "Channel allocation failed: Out of memory\n");
return NULL;
}
memset(tmp, 0, sizeof(struct ast_channel));
tmp->sched = sched_context_create();
if (!tmp->sched) {
ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
free(tmp);
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, "Channel allocation failed: Can't create alert pipe!\n");
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);
/* 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;
strcpy(tmp->name, "**Unkown**");
/* 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);
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(headp);
strcpy(tmp->context, "default");
ast_copy_string(tmp->language, defaultlanguage, sizeof(tmp->language));
strcpy(tmp->exten, "s");
tmp->priority = 1;
tmp->amaflags = ast_default_amaflags;
ast_copy_string(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode));
tmp->tech = &null_tech;
Kevin P. Fleming
committed
ast_mutex_lock(&chlock);
tmp->next = channels;
channels = tmp;
ast_mutex_unlock(&chlock);
/*--- ast_queue_frame: Queue an outgoing media frame */
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);
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;
}
/* 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);
/*--- ast_queue_hangup: Queue a hangup frame for channel */
Mark Spencer
committed
int ast_queue_hangup(struct ast_channel *chan)
{
struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
/* Yeah, let's not change a lock-critical value without locking */
if (!ast_mutex_trylock(&chan->lock)) {
chan->_softhangup |= AST_SOFTHANGUP_DEV;
ast_mutex_unlock(&chan->lock);
}
Mark Spencer
committed
return ast_queue_frame(chan, &f);
/*--- ast_queue_control: Queue a control frame */
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);
/*--- ast_channel_defer_dtmf: Set defer DTMF flag on channel */
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
int pre = 0;
pre = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
/*--- ast_channel_undefer_dtmf: Unset defer DTMF flag on channel */
void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
if (chan)
ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
Kevin P. Fleming
committed
/*
* Helper function to find channels. It supports these modes:
*
* prev != NULL : get channel next in list after prev
* name != NULL : get channel with matching name
* name != NULL && namelen != 0 : get channel whose name starts with prefix
* exten != NULL : get channel whose exten or macroexten matches
* context != NULL && exten != NULL : get channel whose context or macrocontext
*
* It returns with the channel's lock held. If getting the individual lock fails,
Kevin P. Fleming
committed
* unlock and retry quickly up to 10 times, then give up.
*
* XXX Note that this code has cost O(N) because of the need to verify
* that the object is still on the global list.
*
* XXX also note that accessing fields (e.g. c->name in ast_log())
* can only be done with the lock held or someone could delete the
* object while we work on it. This causes some ugliness in the code.
* Note that removing the first ast_log() may be harmful, as it would
* shorten the retry period and possibly cause failures.
* We should definitely go for a better scheme that is deadlock-free.
*/
static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
const char *name, const int namelen,
const char *context, const char *exten)
const char *msg = prev ? "deadlock" : "initial deadlock";
Kevin P. Fleming
committed
int retries, done;
struct ast_channel *c;
for (retries = 0; retries < 10; retries++) {
ast_mutex_lock(&chlock);
for (c = channels; c; c = c->next) {
Kevin P. Fleming
committed
/* want head of list */
if (!name && !exten)
Kevin P. Fleming
committed
break;
if (name) {
/* want match by full name */
if (!namelen) {
if (!strcasecmp(c->name, name))
break;
else
continue;
}
/* want match by name prefix */
if (!strncasecmp(c->name, name, namelen))
} else if (exten) {
/* want match by context and exten */
if (context && (strcasecmp(c->context, context) &&
strcasecmp(c->macrocontext, context)))
continue;
/* match by exten */
if (strcasecmp(c->exten, exten) &&
strcasecmp(c->macroexten, exten))
continue;
else
break;
Kevin P. Fleming
committed
} else if (c == prev) { /* found, return c->next */
c = c->next;
break;
Kevin P. Fleming
committed
/* exit if chan not found or mutex acquired successfully */
done = (c == NULL) || (ast_mutex_trylock(&c->lock) == 0);
/* this is slightly unsafe, as we _should_ hold the lock to access c->name */
if (!done && c)
ast_log(LOG_DEBUG, "Avoiding %s for '%s'\n", msg, c->name);
ast_mutex_unlock(&chlock);
Kevin P. Fleming
committed
if (done)
return c;
usleep(1);
Mark Spencer
committed
}
Kevin P. Fleming
committed
/*
* c is surely not null, but we don't have the lock so cannot
* access c->name
*/
ast_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n",
msg, c, retries);
return NULL;
}
/*--- ast_channel_walk_locked: Browse channels in use */
struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
{
return channel_find_locked(prev, NULL, 0, NULL, NULL);
/*--- ast_get_channel_by_name_locked: Get channel by name and lock it */
Kevin P. Fleming
committed
struct ast_channel *ast_get_channel_by_name_locked(const char *name)
return channel_find_locked(NULL, name, 0, NULL, NULL);
Kevin P. Fleming
committed
}
/*--- ast_get_channel_by_name_prefix_locked: Get channel by name prefix and lock it */
struct ast_channel *ast_get_channel_by_name_prefix_locked(const char *name, const int namelen)
{
return channel_find_locked(NULL, name, namelen, NULL, NULL);
}
/*--- ast_walk_channel_by_name_prefix_locked: Get next channel by name prefix and lock it */
struct ast_channel *ast_walk_channel_by_name_prefix_locked(struct ast_channel *chan, const char *name, const int namelen)
{
return channel_find_locked(chan, name, namelen, NULL, NULL);
}
/*--- ast_get_channel_by_exten_locked: Get channel by exten (and optionally context) and lock it */
struct ast_channel *ast_get_channel_by_exten_locked(const char *exten, const char *context)
{
return channel_find_locked(NULL, NULL, 0, context, exten);
/*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */
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;
}
/*--- ast_safe_sleep: Wait, look for hangups */
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);
}
/*--- ast_channel_free: Free a channel structure */
void ast_channel_free(struct ast_channel *chan)
{
struct ast_channel *last=NULL, *cur;
char name[AST_CHANNEL_NAME];
ast_mutex_lock(&chlock);
if (cur == chan) {
if (last)
last->next = cur->next;
else
channels = cur->next;
break;
}
last = cur;
}
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);
}
if (chan->sched)
sched_context_destroy(chan->sched);
ast_copy_string(name, chan->name, sizeof(name));
/* 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 */
Kevin P. Fleming
committed
while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
ast_var_delete(vardata);
ast_mutex_unlock(&chlock);
Mark Spencer
committed
ast_device_state_changed_literal(name);
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
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 = calloc(1, sizeof(*chan->spies)))) {
ast_log(LOG_WARNING, "Memory allocation failure\n");
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)
{