Newer
Older
Mark Spencer
committed
/*
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, 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
*
* \author Mark Spencer <markster@digium.com>
*/
#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"
Tilghman Lesher
committed
#include "asterisk/sha1.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
/*! Prevent new channel allocation if shutting down. */
Kevin P. Fleming
committed
AST_MUTEX_DEFINE_STATIC(uniquelock);
Mark Spencer
committed
unsigned long global_fin = 0, global_fout = 0;
/* XXX Lock appropriately in more functions XXX */
const struct ast_channel_tech *tech;
Russell Bryant
committed
AST_LIST_ENTRY(chanlist) list;
Kevin P. Fleming
committed
};
/*! the list of registered channel types */
Russell Bryant
committed
static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
Kevin P. Fleming
committed
/*! the list of channels we have. Note that the lock for this list is used for
both the channels list and the backends list. */
static AST_LIST_HEAD_STATIC(channels, ast_channel);
/*! map AST_CAUSE's to readable string representations */
117
118
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
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" },
};
struct ast_variable *ast_channeltype_list(void)
{
struct chanlist *cl;
struct ast_variable *var=NULL, *prev = NULL;
AST_LIST_TRAVERSE(&backends, cl, list) {
if (prev) {
if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description)))
prev = prev->next;
} else {
var = ast_variable_new(cl->tech->type, cl->tech->description);
prev = var;
}
}
return var;
}
static int show_channeltypes(int fd, int argc, char *argv[])
{
#define FORMAT "%-10.10s %-40.40s %-12.12s %-12.12s %-12.12s\n"
int count_chan = 0;
ast_cli(fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
ast_cli(fd, FORMAT, "----------", "-----------", "-----------", "-----------", "--------");
if (AST_LIST_LOCK(&channels)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
Russell Bryant
committed
AST_LIST_TRAVERSE(&backends, cl, list) {
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_LIST_UNLOCK(&channels);
ast_cli(fd, "----------\n%d channel drivers registered.\n", count_chan);
return RESULT_SUCCESS;
#undef FORMAT
}
static int show_channeltype(int fd, int argc, char *argv[])
{
struct chanlist *cl = NULL;
if (argc != 3)
return RESULT_SHOWUSAGE;
if (AST_LIST_LOCK(&channels)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return RESULT_FAILURE;
}
AST_LIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(cl->tech->type, argv[2], strlen(cl->tech->type))) {
break;
}
}
if (!cl) {
ast_cli(fd, "\n%s is not a registered channel driver.\n", argv[2]);
AST_LIST_UNLOCK(&channels);
return RESULT_FAILURE;
ast_cli(fd,
"-- Info about channel driver: %s --\n"
" Device State: %s\n"
" Indication: %s\n"
" Transfer : %s\n"
" Capabilities: %d\n"
" Send Digit: %s\n"
" Send HTML : %s\n"
" Image Support: %s\n"
" Text Support: %s\n",
cl->tech->type,
(cl->tech->devicestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no",
(cl->tech->capabilities) ? cl->tech->capabilities : -1,
(cl->tech->send_digit) ? "yes" : "no",
(cl->tech->send_html) ? "yes" : "no",
(cl->tech->send_image) ? "yes" : "no",
(cl->tech->send_text) ? "yes" : "no"
);
AST_LIST_UNLOCK(&channels);
return RESULT_SUCCESS;
}
static char *complete_channeltypes(const char *line, const char *word, int pos, int state)
{
struct chanlist *cl;
int which = 0;
int wordlen;
char *ret = NULL;
if (pos != 2)
return NULL;
wordlen = strlen(word);
AST_LIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(word, cl->tech->type, wordlen) && ++which > state) {
ret = strdup(cl->tech->type);
break;
}
}
return ret;
}
static char show_channeltypes_usage[] =
"Usage: show channeltypes\n"
" Shows available channel types registered in your Asterisk server.\n";
static char show_channeltype_usage[] =
"Usage: show channeltype <name>\n"
" Show details about the specified channel type, <name>.\n";
static struct ast_cli_entry cli_show_channeltypes =
{ { "show", "channeltypes", NULL }, show_channeltypes, "Show available channel types", show_channeltypes_usage };
static struct ast_cli_entry cli_show_channeltype =
{ { "show", "channeltype", NULL }, show_channeltype, "Give more details on that channel type", show_channeltype_usage, complete_channeltypes };
/*! \brief Checks to see if a channel is needing hang up */
int ast_check_hangup(struct ast_channel *chan)
{
if (chan->_softhangup) /* yes if soft hangup flag set */
return 1;
if (!chan->tech_pvt) /* yes if no technology private data */
return 1;
if (!chan->whentohangup) /* no if no hangup scheduled */
return 0;
if (chan->whentohangup > time(NULL)) /* no if hangup time has not come yet. */
return 0;
chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT; /* record event */
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);
/*! \brief Initiate system shutdown */
void ast_begin_shutdown(int hangup)
{
struct ast_channel *c;
shutting_down = 1;
if (hangup) {
AST_LIST_LOCK(&channels);
AST_LIST_TRAVERSE(&channels, c, chan_list)
AST_LIST_UNLOCK(&channels);
/*! \brief returns number of active/allocated channels */
int ast_active_channels(void)
{
struct ast_channel *c;
int cnt = 0;
AST_LIST_LOCK(&channels);
AST_LIST_TRAVERSE(&channels, c, chan_list)
AST_LIST_UNLOCK(&channels);
/*! \brief Cancel a shutdown in progress */
void ast_cancel_shutdown(void)
{
shutting_down = 0;
}
/*! \brief Returns non-zero if Asterisk is being shut down */
int ast_shutting_down(void)
{
return shutting_down;
}
/*! \brief Set when to hangup channel */
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
chan->whentohangup = offset ? time(NULL) + offset : 0;
ast_queue_frame(chan, &ast_null_frame);
/*! \brief 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) {
return (offset == 0) ? 0 : -1;
} else {
if (offset == 0) /* XXX why is this special ? */
return (1);
else {
whentohangup = offset + time (NULL);
if (chan->whentohangup < whentohangup)
return (1);
else if (chan->whentohangup == whentohangup)
return (0);
else
return (-1);
}
}
}
/*! \brief Register a new telephony channel in Asterisk */
int ast_channel_register(const struct ast_channel_tech *tech)
struct chanlist *chan;
AST_LIST_LOCK(&channels);
Russell Bryant
committed
AST_LIST_TRAVERSE(&backends, chan, list) {
if (!strcasecmp(tech->type, chan->tech->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
AST_LIST_UNLOCK(&channels);
if (!(chan = ast_malloc(sizeof(*chan)))) {
AST_LIST_UNLOCK(&channels);
Russell Bryant
committed
AST_LIST_INSERT_HEAD(&backends, chan, list);
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_LIST_UNLOCK(&channels);
Kevin P. Fleming
committed
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
Russell Bryant
committed
struct chanlist *chan;
Kevin P. Fleming
committed
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
AST_LIST_LOCK(&channels);
Kevin P. Fleming
committed
Russell Bryant
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
Kevin P. Fleming
committed
if (chan->tech == tech) {
Russell Bryant
committed
AST_LIST_REMOVE_CURRENT(&backends, list);
Kevin P. Fleming
committed
free(chan);
if (option_verbose > 1)
Russell Bryant
committed
ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
break;
Kevin P. Fleming
committed
}
}
Russell Bryant
committed
AST_LIST_TRAVERSE_SAFE_END
Kevin P. Fleming
committed
AST_LIST_UNLOCK(&channels);
Kevin P. Fleming
committed
}
const struct ast_channel_tech *ast_get_channel_tech(const char *name)
{
struct chanlist *chanls;
Russell Bryant
committed
const struct ast_channel_tech *ret = NULL;
Kevin P. Fleming
committed
if (AST_LIST_LOCK(&channels)) {
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
return NULL;
}
Russell Bryant
committed
AST_LIST_TRAVERSE(&backends, chanls, list) {
if (!strcasecmp(name, chanls->tech->type)) {
ret = chanls->tech;
break;
}
Kevin P. Fleming
committed
}
AST_LIST_UNLOCK(&channels);
Russell Bryant
committed
return ret;
Kevin P. Fleming
committed
}
/*! \brief 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";
}
/*! \brief 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;
/*! \brief 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";
}
}
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 */
/*! Unless of course, you're a silly European, so then prefer ALAW */
/*! Okay, well, signed linear is easy to translate into other stuff */
AST_FORMAT_G726,
/*! ADPCM has great sound quality and is still pretty easy to translate */
/*! Okay, we're down to vocoders now, so pick GSM because it's small and easier to
translate and sounds pretty good */
/*! Speex is free, but computationally more expensive than GSM */
/*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
to use it */
/*! G.729a is faster than 723 and slightly less expensive */
/*! Down to G.723.1 which is proprietary but at least designed for voice */
/* Find the first preferred 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)",
};
/*! \brief Create a new channel structure */
struct ast_channel *ast_channel_alloc(int needqueue)
struct varshead *headp;
/* 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_calloc(1, sizeof(*tmp))))
return NULL;
if (!(tmp->sched = sched_context_create())) {
ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
free(tmp);
return NULL;
}
ast_string_field_init(tmp, 128);
/* Don't bother initializing the last two FD here, because they
will *always* be set just a few lines down (AST_TIMING_FD,
AST_ALERT_FD). */
for (x=0; x<AST_MAX_FDS - 2; 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);
} else /* 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_ALERT_FD] = tmp->alertpipe[0];
/* And timing pipe */
tmp->fds[AST_TIMING_FD] = tmp->timingfd;
ast_string_field_set(tmp, name, "**Unknown**");
/* Initial state */
tmp->_state = AST_STATE_DOWN;
tmp->streamid = -1;
tmp->appl = NULL;
tmp->data = NULL;
tmp->fin = global_fin;
tmp->fout = global_fout;
if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME))
ast_string_field_build(tmp, uniqueid, "%li.%d", (long) time(NULL), uniqueint++);
else
ast_string_field_build(tmp, uniqueid, "%s-%li.%d", ast_config_AST_SYSTEM_NAME, (long) time(NULL), uniqueint++);
ast_mutex_unlock(&uniquelock);
headp = &tmp->varshead;
ast_mutex_init(&tmp->lock);
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(headp);
strcpy(tmp->context, "default");
ast_string_field_set(tmp, language, defaultlanguage);
strcpy(tmp->exten, "s");
tmp->priority = 1;
tmp->amaflags = ast_default_amaflags;
ast_string_field_set(tmp, accountcode, ast_default_accountcode);
tmp->tech = &null_tech;
AST_LIST_LOCK(&channels);
AST_LIST_INSERT_HEAD(&channels, tmp, chan_list);
AST_LIST_UNLOCK(&channels);
/*! \brief 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;
if (!(f = ast_frdup(fin))) {
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);
/*! \brief 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);
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);
/*! \brief 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);
/*! \brief 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);
* \brief Helper function to find channels.
*
*
* 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.
* \note XXX Note that this code has cost O(N) because of the need to verify
Kevin P. Fleming
committed
* that the object is still on the global list.
*
* \note XXX also note that accessing fields (e.g. c->name in ast_log())
Kevin P. Fleming
committed
* 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_LIST_LOCK(&channels);
AST_LIST_TRAVERSE(&channels, c, chan_list) {
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 = AST_LIST_NEXT(c, chan_list);
Kevin P. Fleming
committed
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_LIST_UNLOCK(&channels);
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;
}
Kevin P. Fleming
committed
struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
{
return channel_find_locked(prev, NULL, 0, NULL, NULL);
/*! \brief 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
}
/*! \brief Get channel by name prefix and lock it */
Kevin P. Fleming
committed
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);
/*! \brief 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);
/*! \brief 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);
/*! \brief Wait, look for hangups and condition arg */
int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data)
while (ms > 0) {
if (cond && ((*cond)(data) == 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)
{
return ast_safe_sleep_conditional(chan, ms, NULL, NULL);
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);
}
/*! \brief Free a channel structure */
void ast_channel_free(struct ast_channel *chan)
{
char name[AST_CHANNEL_NAME];
AST_LIST_LOCK(&channels);
AST_LIST_REMOVE(&channels, chan, chan_list);
/* Lock and unlock the channel just to be sure nobody
has it locked still */
ast_mutex_lock(&chan->lock);
ast_mutex_unlock(&chan->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);
/* Free translators */
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_string_field_free_all(chan);
AST_LIST_UNLOCK(&channels);
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",