Skip to content
Snippets Groups Projects
channel.c 328 KiB
Newer Older
  • Learn to ignore specific revisions
  •  * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * 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.
     *
    
    Mark Spencer's avatar
    Mark Spencer committed
     * 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>
     ***/
    
    
    #include "asterisk.h"
    
    ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/time.h>
    #include <signal.h>
    
    #include "asterisk/paths.h"	/* use ast_config_AST_SYSTEM_NAME */
    
    #include "asterisk/pbx.h"
    #include "asterisk/frame.h"
    
    #include "asterisk/mod_format.h"
    
    #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"
    
    #include "asterisk/callerid.h"
    
    #include "asterisk/utils.h"
    #include "asterisk/lock.h"
    #include "asterisk/app.h"
    #include "asterisk/transcap.h"
    
    #include "asterisk/threadstorage.h"
    
    #include "asterisk/framehook.h"
    
    #include "asterisk/timing.h"
    
    #include "asterisk/stringfields.h"
    
    #include "asterisk/global_datastores.h"
    
    #include "asterisk/data.h"
    
    #include "asterisk/channel_internal.h"
    
    #include "asterisk/features.h"
    
    #include "asterisk/bridge.h"
    
    #include "asterisk/test.h"
    
    #include "asterisk/stasis_channels.h"
    
    #include "asterisk/max_forwards.h"
    
    #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    #if defined(HAVE_PRI)
    #include "libpri.h"
    #endif	/* defined(HAVE_PRI) */
    
    #endif	/* defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED) */
    
    struct ast_epoll_data {
    	struct ast_channel *chan;
    	int which;
    };
    
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    /* 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 */
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #endif
    
    static int chancount;
    
    unsigned long global_fin, global_fout;
    
    AST_THREADSTORAGE(state2str_threadbuf);
    
    /*! 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
    
    /*! \brief List of channel drivers */
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct chanlist {
    
    	const struct ast_channel_tech *tech;
    
    /*! \brief the list of registered channel types */
    
    static AST_RWLIST_HEAD_STATIC(backends, chanlist);
    
    #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
    
     *
     * \ref causes.h
    */
    
    struct causes_map {
    
    };
    
    static const struct causes_map causes[] = {
    
    	{ 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" },
    
    	{ 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" },
    
    	{ 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" },
    
    	{ 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" },
    
    	{ 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) {
    
    			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, "");
    
    #if defined(KEEP_TILL_CHANNEL_PARTY_NUMBER_INFO_NEEDED)
    
    static const char *party_number_ton2str(int ton)
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    #if defined(HAVE_PRI)
    
    	switch ((ton >> 4) & 0x07) {
    
    Richard Mudgett's avatar
    Richard Mudgett committed
    #endif	/* defined(HAVE_PRI) */
    
    #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)";
    
    		return "Data (X.121)";
    
    		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"
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct chanlist *cl;
    
    	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, "-----------", "-----------", "-----------", "-----------", "-----------");
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    
    	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");
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    
    	ast_cli(a->fd, "----------\n%d channel drivers registered.\n", count_chan);
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    
    }
    
    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;
    		}
    	}
    
    /*! \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 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_TRAVERSE(&backends, cl, list) {
    
    		if (!strncasecmp(cl->tech->type, a->argv[3], strlen(cl->tech->type)))
    
    		ast_cli(a->fd, "\n%s is not a registered channel driver.\n", a->argv[3]);
    
    		"-- Info about channel driver: %s --\n"
    		"  Device State: %s\n"
    		"    Indication: %s\n"
    		"     Transfer : %s\n"
    
    		"  Capabilities: %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"
    
    static struct ast_cli_entry cli_channel[] = {
    
    	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")
    
    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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_check_hangup(struct ast_channel *chan)
    {
    
    	if (ast_channel_softhangup_internal_flag(chan))		/* yes if soft hangup flag set */
    
    	if (ast_tvzero(*ast_channel_whentohangup(chan)))	/* no if no hangup scheduled */
    
    	if (ast_tvdiff_ms(*ast_channel_whentohangup(chan), ast_tvnow()) > 0)		/* no if hangup time has not come yet. */
    
    	ast_debug(4, "Hangup time has come: %" PRIi64 "\n", ast_tvdiff_ms(*ast_channel_whentohangup(chan), ast_tvnow()));
    
    Kinsey Moore's avatar
    Kinsey Moore committed
    	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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 1;
    }
    
    
    int ast_check_hangup_locked(struct ast_channel *chan)
    
    	ast_channel_lock(chan);
    
    	res = ast_check_hangup(chan);
    
    	ast_channel_unlock(chan);
    
    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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	ao2_callback(channels, OBJ_NODATA | OBJ_MULTIPLE, ast_channel_softhangup_cb, NULL);
    
    /*! \brief returns number of active/allocated channels */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_active_channels(void)
    {
    
    	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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	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;
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    
    Joshua Colp's avatar
    Joshua Colp committed
    		return 1;
    
    
    	whentohangup = ast_tvadd(offset, ast_tvnow());
    
    Joshua Colp's avatar
    Joshua Colp committed
    
    
    	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)
    
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	}
    
    	if (!(chan = ast_calloc(1, sizeof(*chan)))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Russell Bryant's avatar
    Russell Bryant committed
    	chan->tech = tech;
    
    	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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /*! \brief Unregister channel driver */
    
    void ast_channel_unregister(const struct ast_channel_tech *tech)
    {
    
    	ast_debug(1, "Unregistering channel type '%s'\n", tech->type);
    
    	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
    
    			AST_LIST_REMOVE_CURRENT(list);
    
    			ast_verb(2, "Unregistered channel type '%s'\n", tech->type);
    
    	AST_LIST_TRAVERSE_SAFE_END;
    
    /*! \brief Get handle to channel driver based on name */
    
    const struct ast_channel_tech *ast_get_channel_tech(const char *name)
    {
    	struct chanlist *chanls;
    
    	AST_RWLIST_TRAVERSE(&backends, chanls, list) {
    
    		if (!strcasecmp(name, chanls->tech->type)) {
    			ret = chanls->tech;
    			break;
    		}
    
    /*! \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;
    
    /*! \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)))
    
    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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    		if (!(buf = ast_threadstorage_get(&state2str_threadbuf, STATE2STR_BUFSIZE)))
    			return "Unknown";
    
    		snprintf(buf, STATE2STR_BUFSIZE, "Unknown (%u)", state);
    
    /*! \brief Gives the string form of a given transfer capability */
    
    char *ast_transfercapability2str(int 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";
    	}
    }
    
    /*! \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",
    
    Mark Spencer's avatar
    Mark Spencer committed
    	.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 */
    
    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,
    
    		       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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct ast_channel *tmp;
    
    	char *tech = "", *tech2 = NULL;
    
    	struct ast_format_cap *nativeformats;
    	struct ast_sched_context *schedctx;
    	struct ast_timer *timer;
    
    	struct timeval now;
    
    	const struct ast_channel_tech *channel_tech;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* If shutting down, don't allocate any new channels */
    
    	if (ast_shutting_down()) {
    
    		ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    
    	if (!(tmp = ast_channel_internal_alloc(ast_channel_destructor, assignedids, requestor))) {
    
    		/* Channel structure allocation failure. */
    
    	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.
    		 */
    
    	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));
    
    	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. */
    
    	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));
    
    	ast_party_connected_line_init(ast_channel_connected_indicated(tmp));
    
    	ast_party_redirecting_init(ast_channel_redirecting(tmp));
    
    		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. */
    
    		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. */
    
    	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);
    	}
    
    	ast_channel_set_fd(tmp, AST_ALERT_FD, ast_channel_internal_alert_readfd(tmp));
    
    	ast_channel_set_fd(tmp, AST_TIMING_FD, ast_channel_timingfd(tmp));
    
    	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)) {
    
    		char *slash, *slash2;
    
    		/* 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);
    
    	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);
    
    	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_datastores(tmp));
    	AST_LIST_HEAD_INIT_NOLOCK(ast_channel_autochans(tmp));
    
    	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);
    
    	if (endpoint) {
    		ast_endpoint_add_channel(endpoint, tmp);
    	}
    
    
    	 * And now, since the channel structure is built, and has its name, let