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>
/*** MODULEINFO
<support_level>core</support_level>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/_private.h"
Kevin P. Fleming
committed
#include <math.h>
Kevin P. Fleming
committed
#include "asterisk/paths.h" /* use ast_config_AST_SYSTEM_NAME */
Kevin P. Fleming
committed
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/mod_format.h"
Kevin P. Fleming
committed
#include "asterisk/sched.h"
#include "asterisk/channel.h"
#include "asterisk/musiconhold.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/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"
#include "asterisk/threadstorage.h"
Kevin P. Fleming
committed
#include "asterisk/slinfactory.h"
Joshua Colp
committed
#include "asterisk/audiohook.h"
#include "asterisk/autochan.h"
#include "asterisk/stringfields.h"
#include "asterisk/channel_internal.h"
#include "asterisk/stasis_channels.h"
#include "asterisk/max_forwards.h"
Kevin P. Fleming
committed
Matthew Jordan
committed
/*** DOCUMENTATION
***/
Joshua Colp
committed
#ifdef HAVE_EPOLL
#include <sys/epoll.h>
#endif
#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
#if defined(HAVE_PRI)
#include "libpri.h"
#endif /* defined(HAVE_PRI) */
#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */
Joshua Colp
committed
struct ast_epoll_data {
struct ast_channel *chan;
int which;
};
/* 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 */
unsigned long global_fin, global_fout;
Mark Spencer
committed
Russell Bryant
committed
AST_THREADSTORAGE(state2str_threadbuf);
#define STATE2STR_BUFSIZE 32
/*! Default amount of time to use when emulating a digit as a begin and end
* 100ms */
#define AST_DEFAULT_EMULATE_DTMF_DURATION 100
#define DEFAULT_AMA_FLAGS AST_AMA_DOCUMENTATION
/*! Minimum amount of time between the end of the last digit and the beginning
* of a new one - 45ms */
#define AST_MIN_DTMF_GAP 45
const struct ast_channel_tech *tech;
Russell Bryant
committed
AST_LIST_ENTRY(chanlist) list;
Kevin P. Fleming
committed
};
/*! \brief the list of registered channel types */
static AST_RWLIST_HEAD_STATIC(backends, chanlist);
Kevin P. Fleming
committed
#ifdef LOW_MEMORY
#define NUM_CHANNEL_BUCKETS 61
#else
#define NUM_CHANNEL_BUCKETS 1567
#endif
/*! \brief All active channels on the system */
static struct ao2_container *channels;
/*! \brief map AST_CAUSE's to readable string representations
Joshua Colp
committed
const char *name;
};
static const struct causes_map causes[] = {
Joshua Colp
committed
{ AST_CAUSE_UNALLOCATED, "UNALLOCATED", "Unallocated (unassigned) number" },
{ AST_CAUSE_NO_ROUTE_TRANSIT_NET, "NO_ROUTE_TRANSIT_NET", "No route to specified transmit network" },
{ AST_CAUSE_NO_ROUTE_DESTINATION, "NO_ROUTE_DESTINATION", "No route to destination" },
{ AST_CAUSE_MISDIALLED_TRUNK_PREFIX, "MISDIALLED_TRUNK_PREFIX", "Misdialed trunk prefix" },
Joshua Colp
committed
{ AST_CAUSE_CHANNEL_UNACCEPTABLE, "CHANNEL_UNACCEPTABLE", "Channel unacceptable" },
{ AST_CAUSE_CALL_AWARDED_DELIVERED, "CALL_AWARDED_DELIVERED", "Call awarded and being delivered in an established channel" },
{ AST_CAUSE_PRE_EMPTED, "PRE_EMPTED", "Pre-empted" },
{ AST_CAUSE_NUMBER_PORTED_NOT_HERE, "NUMBER_PORTED_NOT_HERE", "Number ported elsewhere" },
Joshua Colp
committed
{ AST_CAUSE_NORMAL_CLEARING, "NORMAL_CLEARING", "Normal Clearing" },
{ AST_CAUSE_USER_BUSY, "USER_BUSY", "User busy" },
{ AST_CAUSE_NO_USER_RESPONSE, "NO_USER_RESPONSE", "No user responding" },
{ AST_CAUSE_NO_ANSWER, "NO_ANSWER", "User alerting, no answer" },
{ AST_CAUSE_SUBSCRIBER_ABSENT, "SUBSCRIBER_ABSENT", "Subscriber absent" },
Joshua Colp
committed
{ AST_CAUSE_CALL_REJECTED, "CALL_REJECTED", "Call Rejected" },
{ AST_CAUSE_NUMBER_CHANGED, "NUMBER_CHANGED", "Number changed" },
{ AST_CAUSE_REDIRECTED_TO_NEW_DESTINATION, "REDIRECTED_TO_NEW_DESTINATION", "Redirected to new destination" },
{ AST_CAUSE_ANSWERED_ELSEWHERE, "ANSWERED_ELSEWHERE", "Answered elsewhere" },
Joshua Colp
committed
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
{ AST_CAUSE_DESTINATION_OUT_OF_ORDER, "DESTINATION_OUT_OF_ORDER", "Destination out of order" },
{ AST_CAUSE_INVALID_NUMBER_FORMAT, "INVALID_NUMBER_FORMAT", "Invalid number format" },
{ AST_CAUSE_FACILITY_REJECTED, "FACILITY_REJECTED", "Facility rejected" },
{ AST_CAUSE_RESPONSE_TO_STATUS_ENQUIRY, "RESPONSE_TO_STATUS_ENQUIRY", "Response to STATus ENQuiry" },
{ AST_CAUSE_NORMAL_UNSPECIFIED, "NORMAL_UNSPECIFIED", "Normal, unspecified" },
{ AST_CAUSE_NORMAL_CIRCUIT_CONGESTION, "NORMAL_CIRCUIT_CONGESTION", "Circuit/channel congestion" },
{ AST_CAUSE_NETWORK_OUT_OF_ORDER, "NETWORK_OUT_OF_ORDER", "Network out of order" },
{ AST_CAUSE_NORMAL_TEMPORARY_FAILURE, "NORMAL_TEMPORARY_FAILURE", "Temporary failure" },
{ AST_CAUSE_SWITCH_CONGESTION, "SWITCH_CONGESTION", "Switching equipment congestion" },
{ AST_CAUSE_ACCESS_INFO_DISCARDED, "ACCESS_INFO_DISCARDED", "Access information discarded" },
{ AST_CAUSE_REQUESTED_CHAN_UNAVAIL, "REQUESTED_CHAN_UNAVAIL", "Requested channel not available" },
{ AST_CAUSE_FACILITY_NOT_SUBSCRIBED, "FACILITY_NOT_SUBSCRIBED", "Facility not subscribed" },
{ AST_CAUSE_OUTGOING_CALL_BARRED, "OUTGOING_CALL_BARRED", "Outgoing call barred" },
{ AST_CAUSE_INCOMING_CALL_BARRED, "INCOMING_CALL_BARRED", "Incoming call barred" },
{ AST_CAUSE_BEARERCAPABILITY_NOTAUTH, "BEARERCAPABILITY_NOTAUTH", "Bearer capability not authorized" },
{ AST_CAUSE_BEARERCAPABILITY_NOTAVAIL, "BEARERCAPABILITY_NOTAVAIL", "Bearer capability not available" },
{ AST_CAUSE_BEARERCAPABILITY_NOTIMPL, "BEARERCAPABILITY_NOTIMPL", "Bearer capability not implemented" },
{ AST_CAUSE_CHAN_NOT_IMPLEMENTED, "CHAN_NOT_IMPLEMENTED", "Channel not implemented" },
{ AST_CAUSE_FACILITY_NOT_IMPLEMENTED, "FACILITY_NOT_IMPLEMENTED", "Facility not implemented" },
{ AST_CAUSE_INVALID_CALL_REFERENCE, "INVALID_CALL_REFERENCE", "Invalid call reference value" },
{ AST_CAUSE_INCOMPATIBLE_DESTINATION, "INCOMPATIBLE_DESTINATION", "Incompatible destination" },
{ AST_CAUSE_INVALID_MSG_UNSPECIFIED, "INVALID_MSG_UNSPECIFIED", "Invalid message unspecified" },
{ AST_CAUSE_MANDATORY_IE_MISSING, "MANDATORY_IE_MISSING", "Mandatory information element is missing" },
{ AST_CAUSE_MESSAGE_TYPE_NONEXIST, "MESSAGE_TYPE_NONEXIST", "Message type nonexist." },
{ AST_CAUSE_WRONG_MESSAGE, "WRONG_MESSAGE", "Wrong message" },
{ AST_CAUSE_IE_NONEXIST, "IE_NONEXIST", "Info. element nonexist or not implemented" },
{ AST_CAUSE_INVALID_IE_CONTENTS, "INVALID_IE_CONTENTS", "Invalid information element contents" },
{ AST_CAUSE_WRONG_CALL_STATE, "WRONG_CALL_STATE", "Message not compatible with call state" },
{ AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE, "RECOVERY_ON_TIMER_EXPIRE", "Recover on timer expiry" },
{ AST_CAUSE_MANDATORY_IE_LENGTH_ERROR, "MANDATORY_IE_LENGTH_ERROR", "Mandatory IE length error" },
{ AST_CAUSE_PROTOCOL_ERROR, "PROTOCOL_ERROR", "Protocol error, unspecified" },
{ AST_CAUSE_INTERWORKING, "INTERWORKING", "Interworking, unspecified" },
struct ast_variable *ast_channeltype_list(void)
{
struct chanlist *cl;
struct ast_variable *var = NULL, *prev = NULL;
AST_RWLIST_RDLOCK(&backends);
AST_RWLIST_TRAVERSE(&backends, cl, list) {
Steve Murphy
committed
if ((prev->next = ast_variable_new(cl->tech->type, cl->tech->description, "")))
prev = prev->next;
} else {
Steve Murphy
committed
var = ast_variable_new(cl->tech->type, cl->tech->description, "");
AST_RWLIST_UNLOCK(&backends);
#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
static const char *party_number_ton2str(int ton)
{
switch ((ton >> 4) & 0x07) {
case PRI_TON_INTERNATIONAL:
return "International";
case PRI_TON_NATIONAL:
return "National";
case PRI_TON_NET_SPECIFIC:
return "Network Specific";
case PRI_TON_SUBSCRIBER:
return "Subscriber";
case PRI_TON_ABBREVIATED:
return "Abbreviated";
case PRI_TON_RESERVED:
return "Reserved";
case PRI_TON_UNKNOWN:
default:
}
return "Unknown";
}
#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */
#if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
static const char *party_number_plan2str(int plan)
{
#if defined(HAVE_PRI)
switch (plan & 0x0F) {
default:
case PRI_NPI_UNKNOWN:
break;
case PRI_NPI_E163_E164:
return "Public (E.163/E.164)";
case PRI_NPI_X121:
return "Data (X.121)";
case PRI_NPI_F69:
return "Telex (F.69)";
case PRI_NPI_NATIONAL:
return "National Standard";
case PRI_NPI_PRIVATE:
return "Private";
case PRI_NPI_RESERVED:
return "Reserved";
}
#endif /* defined(HAVE_PRI) */
return "Unknown";
}
#endif /* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */
/*! \brief Show channel types - CLI command */
static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define FORMAT "%-15.15s %-40.40s %-12.12s %-12.12s %-12.12s\n"
int count_chan = 0;
switch (cmd) {
case CLI_INIT:
e->command = "core show channeltypes";
e->usage =
"Usage: core show channeltypes\n"
" Lists available channel types registered in your\n"
" Asterisk server.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 3)
return CLI_SHOWUSAGE;
ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Indications", "Transfer");
ast_cli(a->fd, FORMAT, "-----------", "-----------", "-----------", "-----------", "-----------");
AST_RWLIST_RDLOCK(&backends);
AST_RWLIST_TRAVERSE(&backends, cl, list) {
ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
(cl->tech->devicestate) ? "yes" : "no",
(cl->tech->indicate) ? "yes" : "no",
(cl->tech->transfer) ? "yes" : "no");
AST_RWLIST_UNLOCK(&backends);
ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
return CLI_SUCCESS;
#undef FORMAT
}
static char *complete_channeltypes(struct ast_cli_args *a)
{
struct chanlist *cl;
int which = 0;
int wordlen;
char *ret = NULL;
if (a->pos != 3)
return NULL;
wordlen = strlen(a->word);
AST_RWLIST_RDLOCK(&backends);
AST_RWLIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(a->word, cl->tech->type, wordlen) && ++which > a->n) {
ret = ast_strdup(cl->tech->type);
break;
}
}
AST_RWLIST_UNLOCK(&backends);
return ret;
}
/*! \brief Show details about a channel driver - CLI command */
static char *handle_cli_core_show_channeltype(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct chanlist *cl = NULL;
Alexander Traud
committed
struct ast_str *codec_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
switch (cmd) {
case CLI_INIT:
e->command = "core show channeltype";
e->usage =
"Usage: core show channeltype <name>\n"
" Show details about the specified channel type, <name>.\n";
return NULL;
case CLI_GENERATE:
return complete_channeltypes(a);
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
AST_RWLIST_RDLOCK(&backends);
AST_RWLIST_TRAVERSE(&backends, cl, list) {
if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
break;
}
if (!cl) {
ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
AST_RWLIST_UNLOCK(&backends);
return CLI_FAILURE;
ast_cli(a->fd,
"-- Info about channel driver: %s --\n"
" Device State: %s\n"
" Indication: %s\n"
" Transfer : %s\n"
" Digit Begin: %s\n"
" Digit End: %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",
ast_format_cap_get_names(cl->tech->capabilities, &codec_buf),
(cl->tech->send_digit_begin) ? "yes" : "no",
(cl->tech->send_digit_end) ? "yes" : "no",
(cl->tech->send_html) ? "yes" : "no",
(cl->tech->send_image) ? "yes" : "no",
(cl->tech->send_text) ? "yes" : "no"
);
AST_RWLIST_UNLOCK(&backends);
return CLI_SUCCESS;
}
static struct ast_cli_entry cli_channel[] = {
Jason Parker
committed
AST_CLI_DEFINE(handle_cli_core_show_channeltypes, "List available channel types"),
AST_CLI_DEFINE(handle_cli_core_show_channeltype, "Give more details on that channel type")
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
static struct ast_frame *kill_read(struct ast_channel *chan)
{
/* Hangup channel. */
return NULL;
}
static struct ast_frame *kill_exception(struct ast_channel *chan)
{
/* Hangup channel. */
return NULL;
}
static int kill_write(struct ast_channel *chan, struct ast_frame *frame)
{
/* Hangup channel. */
return -1;
}
static int kill_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
/* No problem fixing up the channel. */
return 0;
}
static int kill_hangup(struct ast_channel *chan)
{
ast_channel_tech_pvt_set(chan, NULL);
return 0;
}
/*!
* \brief Kill the channel channel driver technology descriptor.
*
* \details
* The purpose of this channel technology is to encourage the
* channel to hangup as quickly as possible.
*
* \note Used by DTMF atxfer and zombie channels.
*/
const struct ast_channel_tech ast_kill_tech = {
.type = "Kill",
.description = "Kill channel (should not see this)",
.read = kill_read,
.exception = kill_exception,
.write = kill_write,
.fixup = kill_fixup,
.hangup = kill_hangup,
};
/*! \brief Checks to see if a channel is needing hang up */
int ast_check_hangup(struct ast_channel *chan)
{
if (ast_channel_softhangup_internal_flag(chan)) /* yes if soft hangup flag set */
return 1;
if (ast_tvzero(*ast_channel_whentohangup(chan))) /* no if no hangup scheduled */
return 0;
if (ast_tvdiff_ms(*ast_channel_whentohangup(chan), ast_tvnow()) > 0) /* no if hangup time has not come yet. */
return 0;
ast_debug(4, "Hangup time has come: %" PRIi64 "\n", ast_tvdiff_ms(*ast_channel_whentohangup(chan), ast_tvnow()));
ast_test_suite_event_notify("HANGUP_TIME", "Channel: %s", ast_channel_name(chan));
ast_channel_softhangup_internal_flag_add(chan, AST_SOFTHANGUP_TIMEOUT); /* record event */
int ast_check_hangup_locked(struct ast_channel *chan)
res = ast_check_hangup(chan);
Richard Mudgett
committed
void ast_channel_softhangup_withcause_locked(struct ast_channel *chan, int causecode)
{
ast_channel_lock(chan);
if (causecode > 0) {
ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
ast_channel_name(chan), causecode, ast_channel_hangupcause(chan));
ast_channel_hangupcause_set(chan, causecode);
}
ast_softhangup_nolock(chan, AST_SOFTHANGUP_EXPLICIT);
ast_channel_unlock(chan);
}
static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
{
struct ast_channel *chan = obj;
ast_softhangup(chan, AST_SOFTHANGUP_SHUTDOWN);
return 0;
}
void ast_softhangup_all(void)
ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
/*! \brief returns number of active/allocated channels */
return channels ? ao2_container_count(channels) : 0;
int ast_undestroyed_channels(void)
{
return ast_atomic_fetchadd_int(&chancount, 0);
}
/*! \brief Set when to hangup channel */
void ast_channel_setwhentohangup_tv(struct ast_channel *chan, struct timeval offset)
if (ast_tvzero(offset)) {
ast_channel_whentohangup_set(chan, &offset);
} else {
struct timeval tv = ast_tvadd(offset, ast_tvnow());
ast_channel_whentohangup_set(chan, &tv);
}
ast_queue_frame(chan, &ast_null_frame);
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
struct timeval when = { offset, };
ast_channel_setwhentohangup_tv(chan, when);
/*! \brief Compare a offset with when to hangup channel */
int ast_channel_cmpwhentohangup_tv(struct ast_channel *chan, struct timeval offset)
struct timeval whentohangup;
if (ast_tvzero(*ast_channel_whentohangup(chan)))
return ast_tvzero(offset) ? 0 : -1;
if (ast_tvzero(offset))
whentohangup = ast_tvadd(offset, ast_tvnow());
return ast_tvdiff_ms(whentohangup, *ast_channel_whentohangup(chan));
}
int ast_channel_cmpwhentohangup(struct ast_channel *chan, time_t offset)
{
struct timeval when = { offset, };
return ast_channel_cmpwhentohangup_tv(chan, when);
}
/*! \brief Register a new telephony channel in Asterisk */
int ast_channel_register(const struct ast_channel_tech *tech)
struct chanlist *chan;
AST_RWLIST_WRLOCK(&backends);
AST_RWLIST_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_RWLIST_UNLOCK(&backends);
if (!(chan = ast_calloc(1, sizeof(*chan)))) {
AST_RWLIST_UNLOCK(&backends);
AST_RWLIST_INSERT_HEAD(&backends, chan, list);
ast_debug(1, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
ast_verb(2, "Registered channel type '%s' (%s)\n", chan->tech->type, chan->tech->description);
AST_RWLIST_UNLOCK(&backends);
Kevin P. Fleming
committed
void ast_channel_unregister(const struct ast_channel_tech *tech)
{
Russell Bryant
committed
struct chanlist *chan;
Kevin P. Fleming
committed
ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
Kevin P. Fleming
committed
AST_RWLIST_WRLOCK(&backends);
Kevin P. Fleming
committed
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
Kevin P. Fleming
committed
if (chan->tech == tech) {
AST_LIST_REMOVE_CURRENT(list);
Tilghman Lesher
committed
ast_free(chan);
ast_verb(2, "Unregistered channel type '%s'\n", tech->type);
Kevin P. Fleming
committed
}
}
Kevin P. Fleming
committed
AST_RWLIST_UNLOCK(&backends);
Kevin P. Fleming
committed
}
/*! \brief Get handle to channel driver based on name */
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
AST_RWLIST_RDLOCK(&backends);
Kevin P. Fleming
committed
AST_RWLIST_TRAVERSE(&backends, chanls, list) {
Russell Bryant
committed
if (!strcasecmp(name, chanls->tech->type)) {
ret = chanls->tech;
break;
}
Kevin P. Fleming
committed
}
AST_RWLIST_UNLOCK(&backends);
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 < ARRAY_LEN(causes); x++) {
if (causes[x].cause == cause)
return causes[x].desc;
return "Unknown";
}
Joshua Colp
committed
/*! \brief Convert a symbolic hangup cause to number */
int ast_str2cause(const char *name)
{
int x;
for (x = 0; x < ARRAY_LEN(causes); x++)
if (!strncasecmp(causes[x].name, name, strlen(causes[x].name)))
Joshua Colp
committed
return causes[x].cause;
return -1;
}
static struct stasis_message *create_channel_snapshot_message(struct ast_channel *channel)
{
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
if (!ast_channel_snapshot_type()) {
return NULL;
}
ast_channel_lock(channel);
snapshot = ast_channel_snapshot_create(channel);
ast_channel_unlock(channel);
if (!snapshot) {
return NULL;
}
return stasis_message_create(ast_channel_snapshot_type(), snapshot);
}
static void publish_cache_clear(struct ast_channel *chan)
{
RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup);
clear_msg = create_channel_snapshot_message(chan);
if (!clear_msg) {
return;
}
message = stasis_cache_clear_create(clear_msg);
stasis_publish(ast_channel_topic(chan), message);
}
/*! \brief Gives the string form of a given channel state.
*
* \note This function is not reentrant.
*
* \param state
*/
const char *ast_state2str(enum ast_channel_state state)
char *buf;
Joshua Colp
committed
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";
case AST_STATE_DIALING_OFFHOOK:
return "Dialing Offhook";
case AST_STATE_PRERING:
return "Pre-ring";
case AST_STATE_MUTE:
return "Mute";
if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE)))
return "Unknown";
snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%u)", state);
return buf;
/*! \brief Gives the string form of a given transfer capability */
char *ast_transfercapability2str(int transfercapability)
{
Joshua Colp
committed
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";
}
}
Matthew Jordan
committed
/*! \brief Channel technology used to extract a channel from a running application. The
* channel created with this technology will be immediately hung up - most external
* applications won't ever want to see this.
*/
static const struct ast_channel_tech surrogate_tech = {
.type = "Surrogate",
.description = "Surrogate channel used to pull channel from an application",
.properties = AST_CHAN_TP_INTERNAL,
};
static const struct ast_channel_tech null_tech = {
.type = "NULL",
.description = "Null channel (should not see this)",
};
static void ast_channel_destructor(void *obj);
static void ast_dummy_channel_destructor(void *obj);
static int ast_channel_by_uniqueid_cb(void *obj, void *arg, void *data, int flags);
static int does_id_conflict(const char *uniqueid)
{
struct ast_channel *conflict;
int length = 0;
if (ast_strlen_zero(uniqueid)) {
return 0;
}
conflict = ast_channel_callback(ast_channel_by_uniqueid_cb, (char *) uniqueid, &length, OBJ_NOLOCK);
if (conflict) {
ast_log(LOG_ERROR, "Channel Unique ID '%s' already in use by channel %s(%p)\n",
uniqueid, ast_channel_name(conflict), conflict);
ast_channel_unref(conflict);
return 1;
}
return 0;
}
/*! \brief Create a new channel structure */
Matthew Jordan
committed
static struct ast_channel * attribute_malloc __attribute__((format(printf, 15, 0)))
__ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char *cid_name,
const char *acctcode, const char *exten, const char *context, const struct ast_assigned_ids *assignedids,
Matthew Jordan
committed
const struct ast_channel *requestor, enum ama_flags amaflag, struct ast_endpoint *endpoint,
const char *file, int line,
const char *function, const char *name_fmt, va_list ap)
struct varshead *headp;
char *tech = "", *tech2 = NULL;
struct ast_format_cap *nativeformats;
struct ast_sched_context *schedctx;
struct ast_timer *timer;
Matthew Jordan
committed
const struct ast_channel_tech *channel_tech;
/* If shutting down, don't allocate any new channels */
ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
}
if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor, assignedids, requestor))) {
/* Channel structure allocation failure. */
return NULL;
}
ast_channel_stage_snapshot(tmp);
if (!(nativeformats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
/*
* Aborting the channel creation. We do not need to complete staging
* the channel snapshot because the channel has not been finalized or
* linked into the channels container yet. Nobody else knows about
* this channel nor will anybody ever know about it.
*/
return ast_channel_unref(tmp);
ast_format_cap_append(nativeformats, ast_format_none, 0);
ast_channel_nativeformats_set(tmp, nativeformats);
ao2_ref(nativeformats, -1);
ast_channel_set_rawwriteformat(tmp, ast_format_none);
ast_channel_set_rawreadformat(tmp, ast_format_none);
ast_channel_set_writeformat(tmp, ast_format_none);
ast_channel_set_readformat(tmp, ast_format_none);
/*
* Init file descriptors to unopened state so
* the destructor can know not to close them.
*/
ast_channel_timingfd_set(tmp, -1);
ast_channel_internal_alertpipe_clear(tmp);
ast_channel_internal_fd_clear_all(tmp);
ast_channel_epfd_set(tmp, epoll_create(25));
#endif
if (!(schedctx = ast_sched_context_create())) {
ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
}
ast_channel_sched_set(tmp, schedctx);
ast_party_dialed_init(ast_channel_dialed(tmp));
ast_party_caller_init(ast_channel_caller(tmp));
ast_party_connected_line_init(ast_channel_connected(tmp));
Joshua Colp
committed
ast_party_connected_line_init(ast_channel_connected_indicated(tmp));
ast_party_redirecting_init(ast_channel_redirecting(tmp));
Mark Michelson
committed
if (cid_name) {
ast_channel_caller(tmp)->id.name.valid = 1;
ast_channel_caller(tmp)->id.name.str = ast_strdup(cid_name);
if (!ast_channel_caller(tmp)->id.name.str) {
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
Mark Michelson
committed
}
}
if (cid_num) {
ast_channel_caller(tmp)->id.number.valid = 1;
ast_channel_caller(tmp)->id.number.str = ast_strdup(cid_num);
if (!ast_channel_caller(tmp)->id.number.str) {
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
Mark Michelson
committed
}
Mark Michelson
committed
}
if ((timer = ast_timer_open())) {
ast_channel_timer_set(tmp, timer);
if (strcmp(ast_timer_get_name(ast_channel_timer(tmp)), "timerfd")) {
ast_channel_timingfd_set(tmp, ast_timer_fd(ast_channel_timer(tmp)));
}
if (needqueue && ast_channel_internal_alertpipe_init(tmp)) {
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
}
/* Always watch the alertpipe */
ast_channel_set_fd(tmp, AST_ALERT_FD, ast_channel_internal_alert_readfd(tmp));
/* And timing pipe */
ast_channel_set_fd(tmp, AST_TIMING_FD, ast_channel_timingfd(tmp));
/* Initial state */
ast_channel_state_set(tmp, state);
ast_channel_hold_state_set(tmp, AST_CONTROL_UNHOLD);
ast_channel_streamid_set(tmp, -1);
ast_channel_vstreamid_set(tmp, -1);
ast_channel_fin_set(tmp, global_fin);
ast_channel_fout_set(tmp, global_fout);
now = ast_tvnow();
ast_channel_creationtime_set(tmp, &now);
ast_channel_internal_setup_topics(tmp);
if (!ast_strlen_zero(name_fmt)) {
/* Almost every channel is calling this function, and setting the name via the ast_string_field_build() call.
* And they all use slightly different formats for their name string.
* This means, to set the name here, we have to accept variable args, and call the string_field_build from here.
* This means, that the stringfields must have a routine that takes the va_lists directly, and
* uses them to build the string, instead of forming the va_lists internally from the vararg ... list.
* This new function was written so this can be accomplished.
*/
ast_channel_name_build_va(tmp, name_fmt, ap);
tech = ast_strdupa(ast_channel_name(tmp));
if ((slash = strchr(tech, '/'))) {
if ((slash2 = strchr(slash + 1, '/'))) {
tech2 = slash + 1;
*slash2 = '\0';
}
} else {
/*
* Start the string with '-' so it becomes an empty string
* in the destructor.
*/
ast_channel_name_set(tmp, "-**Unknown**");
if (amaflag != AST_AMA_NONE) {
ast_channel_amaflags_set(tmp, amaflag);
} else {
ast_channel_amaflags_set(tmp, DEFAULT_AMA_FLAGS);
if (!ast_strlen_zero(acctcode)) {
ast_channel_accountcode_set(tmp, acctcode);
Matthew Jordan
committed
ast_channel_language_set(tmp, ast_defaultlanguage);
ast_channel_context_set(tmp, S_OR(context, "default"));
ast_channel_exten_set(tmp, S_OR(exten, "s"));
ast_channel_priority_set(tmp, 1);
headp = ast_channel_varshead(tmp);
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(headp);
Richard Mudgett
committed
ast_pbx_hangup_handler_init(tmp);
AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
AST_LIST_HEAD_INIT_NOLOCK(ast_channel_autochans(tmp));
Matthew Jordan
committed
channel_tech = ast_get_channel_tech(tech);
if (!channel_tech && !ast_strlen_zero(tech2)) {
channel_tech = ast_get_channel_tech(tech2);
}
if (channel_tech) {
ast_channel_tech_set(tmp, channel_tech);
} else {
ast_channel_tech_set(tmp, &null_tech);
}
/* You might scream "locking inversion" at seeing this but it is actually perfectly fine.
* Since the channel was just created nothing can know about it yet or even acquire it.
*/
ast_channel_lock(tmp);
ao2_lock(channels);
if (assignedids && (does_id_conflict(assignedids->uniqueid) || does_id_conflict(assignedids->uniqueid2))) {
ast_channel_internal_errno_set(AST_CHANNEL_ERROR_ID_EXISTS);
ao2_unlock(channels);
ast_channel_unlock(tmp);
/* See earlier channel creation abort comment above. */
return ast_channel_unref(tmp);
}
/* Finalize and link into the channels container. */
ast_channel_internal_finalize(tmp);
ast_atomic_fetchadd_int(&chancount, +1);
ao2_link_flags(channels, tmp, OBJ_NOLOCK);
ao2_unlock(channels);
Matthew Jordan
committed
if (endpoint) {
ast_endpoint_add_channel(endpoint, tmp);
}
Matthew Jordan
committed
* And now, since the channel structure is built, and has its name, let