Skip to content
Snippets Groups Projects
channel.c 123 KiB
Newer Older
 * 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>
Mark Spencer's avatar
Mark Spencer committed
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
Mark Spencer's avatar
Mark Spencer committed
#include <unistd.h>
Mark Spencer's avatar
Mark Spencer committed
#include <math.h>			/* For PI */
#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's avatar
Kevin P. Fleming committed
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
Kevin P. Fleming's avatar
Kevin P. Fleming committed
#include "asterisk/chanspy.h"
#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"
#include "asterisk/callerid.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/transcap.h"
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;
};

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 */
#endif
/*! Prevent new channel allocation if shutting down. */
Mark Spencer's avatar
Mark Spencer committed
static int shutting_down = 0;
AST_MUTEX_DEFINE_STATIC(uniquelock);
Mark Spencer's avatar
Mark Spencer committed
static int uniqueint = 0;
Mark Spencer's avatar
Mark Spencer committed
/* XXX Lock appropriately in more functions XXX */
Mark Spencer's avatar
Mark Spencer committed
struct chanlist {
	const struct ast_channel_tech *tech;
/*! the list of registered channel types */
static AST_LIST_HEAD_NOLOCK_STATIC(backends, chanlist);
/*! 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 */
const struct ast_cause {
	int cause;
	const char *desc;
} 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_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_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_CALL_REJECTED, "CALL_REJECTED", "Call Rejected" },
	{ AST_CAUSE_NUMBER_CHANGED, "NUMBER_CHANGED", "Number changed" },
	{ 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_PRE_EMPTED, "PRE_EMPTED", "Pre-empted" },
	{ 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_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"
Russell Bryant's avatar
Russell Bryant committed
	struct chanlist *cl;
	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;
	}
		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_cli(fd, "----------\n%d channel drivers registered.\n", count_chan);
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_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"
		
	);

	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[] =
"       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 */
Mark Spencer's avatar
Mark Spencer committed
int ast_check_hangup(struct ast_channel *chan)
{
	if (chan->_softhangup)		/* yes if soft hangup flag set */
	if (!chan->tech_pvt)		/* yes if no technology private data */
	if (!chan->whentohangup)	/* no if no hangup scheduled */
	if (chan->whentohangup > time(NULL)) 	/* no if hangup time has not come yet. */
	chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;	/* record event */
Mark Spencer's avatar
Mark Spencer committed
	return 1;
}

static int ast_check_hangup_locked(struct ast_channel *chan)
{
	int res;
	ast_channel_lock(chan);
	res = ast_check_hangup(chan);
	ast_channel_unlock(chan);
/*! \brief Initiate system shutdown */
Mark Spencer's avatar
Mark Spencer committed
void ast_begin_shutdown(int hangup)
{
	struct ast_channel *c;
	shutting_down = 1;
	if (hangup) {
		AST_LIST_TRAVERSE(&channels, c, chan_list)
Mark Spencer's avatar
Mark Spencer committed
			ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
/*! \brief returns number of active/allocated channels */
Mark Spencer's avatar
Mark Spencer committed
int ast_active_channels(void)
{
	struct ast_channel *c;
	int cnt = 0;
	AST_LIST_TRAVERSE(&channels, c, chan_list)
Mark Spencer's avatar
Mark Spencer committed
		cnt++;
Mark Spencer's avatar
Mark Spencer committed
	return cnt;
}

/*! \brief Cancel a shutdown in progress */
Mark Spencer's avatar
Mark Spencer committed
void ast_cancel_shutdown(void)
{
	shutting_down = 0;
}

/*! \brief Returns non-zero if Asterisk is being shut down */
Mark Spencer's avatar
Mark Spencer committed
int ast_shutting_down(void)
{
	return shutting_down;
}

/*! \brief Set when to hangup channel */
Mark Spencer's avatar
Mark Spencer committed
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)
		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;
Mark Spencer's avatar
Mark Spencer committed
	if (option_debug)
		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);

Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

void ast_channel_unregister(const struct ast_channel_tech *tech)
{

	if (option_debug)
		ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);

	AST_LIST_TRAVERSE_SAFE_BEGIN(&backends, chan, list) {
				ast_verbose(VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
			break;	
}

const struct ast_channel_tech *ast_get_channel_tech(const char *name)
{
	struct chanlist *chanls;
	if (AST_LIST_LOCK(&channels)) {
		ast_log(LOG_WARNING, "Unable to lock channel tech list\n");
		return NULL;
	}

	AST_LIST_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 < sizeof(causes) / sizeof(causes[0]); 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 < sizeof(causes) / sizeof(causes[0]); x++)
		if (strncasecmp(causes[x].name, name, strlen(causes[x].name)) == 0)
			return causes[x].cause;

	return -1;
}
	 
/*! \brief Gives the string form of a given channel state */
Mark Spencer's avatar
Mark Spencer committed
char *ast_state2str(int state)
{
	/* XXX Not reentrant XXX */
	static char localtmp[256];
Mark Spencer's avatar
Mark Spencer 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";
	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";
	}
}
/*! \brief Pick the best codec */
Mark Spencer's avatar
Mark Spencer committed
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;
Mark Spencer's avatar
Mark Spencer committed
	{
		/*! Okay, ulaw is used by all telephony equipment, so start with it */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_ULAW,
		/*! Unless of course, you're a silly European, so then prefer ALAW */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_ALAW,
		/*! Okay, well, signed linear is easy to translate into other stuff */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_SLINEAR,
		/*! G.726 is standard ADPCM */
		/*! ADPCM has great sound quality and is still pretty easy to translate */
Mark Spencer's avatar
Mark Spencer committed
		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 */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_GSM,
		/*! iLBC is not too bad */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_ILBC,
		/*! Speex is free, but computationally more expensive than GSM */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_SPEEX,
		/*! Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
		    to use it */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_LPC10,
		/*! G.729a is faster than 723 and slightly less expensive */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_G729A,
		/*! Down to G.723.1 which is proprietary but at least designed for voice */
Mark Spencer's avatar
Mark Spencer committed
		AST_FORMAT_G723_1,
	};
	
	
	/* Find the first preferred codec in the format given */
	for (x=0; x < (sizeof(prefs) / sizeof(prefs[0]) ); x++)
Mark Spencer's avatar
Mark Spencer committed
		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",
Mark Spencer's avatar
Mark Spencer committed
	.description = "Null channel (should not see this)",
/*! \brief Create a new channel structure */
Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *ast_channel_alloc(int needqueue)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_channel *tmp;
Mark Spencer's avatar
Mark Spencer committed
	int x;
Mark Spencer's avatar
Mark Spencer committed
	int flags;
Mark Spencer's avatar
Mark Spencer committed
	/* 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");
Mark Spencer's avatar
Mark Spencer committed
		return NULL;
	if (!(tmp = ast_calloc(1, sizeof(*tmp))))
	if (!(tmp->sched = sched_context_create())) {
		ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
	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->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;
	}

	if (needqueue) {
		if (pipe(tmp->alertpipe)) {
			ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n");
Mark Spencer's avatar
Mark Spencer committed
			free(tmp);
			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);
Mark Spencer's avatar
Mark Spencer committed
		}
	} 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];
	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;
	ast_mutex_lock(&uniquelock);
	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);
	AST_LIST_HEAD_INIT_NOLOCK(&tmp->datastores);
	ast_string_field_set(tmp, language, defaultlanguage);
	tmp->priority = 1;
	tmp->amaflags = ast_default_amaflags;
	ast_string_field_set(tmp, accountcode, ast_default_accountcode);
	AST_LIST_INSERT_HEAD(&channels, tmp, chan_list);
Mark Spencer's avatar
Mark Spencer committed
	return tmp;
}

/*! \brief Queue an outgoing media frame */
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_frame *f;
	struct ast_frame *prev, *cur;
	int blah = 1;
	int qlen = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Build us a copy and free the original one */
	if (!(f = ast_frdup(fin))) {
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_WARNING, "Unable to duplicate frame\n");
		return -1;
	}
	ast_channel_lock(chan);
Mark Spencer's avatar
Mark Spencer committed
	prev = NULL;
Russell Bryant's avatar
Russell Bryant committed
	for (cur = chan->readq; cur; cur = cur->next) {
		if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
			/* Don't bother actually queueing anything after a hangup */
			ast_frfree(f);
			ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
		prev = cur;
		qlen++;
	}
	/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
	if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen  > 128)) {
Mark Spencer's avatar
Mark Spencer committed
		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);
			ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
			return 0;
		}
	}
Mark Spencer's avatar
Mark Spencer committed
	if (prev)
		prev->next = f;
	else
		chan->readq = f;
	if (chan->alertpipe[1] > -1) {
		if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
Mark Spencer's avatar
Mark Spencer committed
			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));
	} 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's avatar
Mark Spencer committed
	}
	ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

/*! \brief Queue a hangup frame for channel */
int ast_queue_hangup(struct ast_channel *chan)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	/* Yeah, let's not change a lock-critical value without locking */
	if (!ast_channel_trylock(chan)) {
Kevin P. Fleming's avatar
Kevin P. Fleming committed
		chan->_softhangup |= AST_SOFTHANGUP_DEV;
		ast_channel_unlock(chan);
Kevin P. Fleming's avatar
Kevin P. Fleming committed
	}
/*! \brief Queue a control frame */
int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_frame f = { AST_FRAME_CONTROL, };

	f.subclass = control;

	return ast_queue_frame(chan, &f);
}

/*! \brief Queue a control frame with payload */
int ast_queue_control_data(struct ast_channel *chan, enum ast_control_frame_type control,
			   const void *data, size_t datalen)
{
	struct ast_frame f = { AST_FRAME_CONTROL, };

Mark Spencer's avatar
Mark Spencer committed
	f.subclass = control;
	f.data = (void *) data;
	f.datalen = datalen;

/*! \brief Set defer DTMF flag on channel */
Mark Spencer's avatar
Mark Spencer committed
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
	int pre = 0;
Mark Spencer's avatar
Mark Spencer committed
	if (chan) {
		pre = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
		ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
Mark Spencer's avatar
Mark Spencer committed
	}
	return pre;
}

/*! \brief Unset defer DTMF flag on channel */
Mark Spencer's avatar
Mark Spencer committed
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.
 *
 * 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,
 * 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
 * that the object is still on the global list.
 *
 * \note 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)
Mark Spencer's avatar
Mark Spencer committed
{
	const char *msg = prev ? "deadlock" : "initial deadlock";
	struct ast_channel *c;

	for (retries = 0; retries < 10; retries++) {
		AST_LIST_TRAVERSE(&channels, c, chan_list) {
			if (prev) {	/* look for next item */
				if (c != prev)	/* not this one */
					continue;
				/* found, prepare to return c->next */
				c = AST_LIST_NEXT(c, chan_list);
			} else if (name) { /* want match by name */
				if ( (!namelen && strcasecmp(c->name, name)) ||
				     (namelen && strncasecmp(c->name, name, namelen)) )
					continue;	/* name match failed */
			} else if (exten) {
				if (context && strcasecmp(c->context, context) &&
						strcasecmp(c->macrocontext, context))
					continue;	/* context match failed */
				if (strcasecmp(c->exten, exten) &&
						strcasecmp(c->macroexten, exten))
					continue;	/* exten match failed */
			/* if we get here, c points to the desired record */
			break;
		/* exit if chan not found or mutex acquired successfully */
		/* this is slightly unsafe, as we _should_ hold the lock to access c->name */
		done = c == NULL || ast_channel_trylock(c) == 0;
		if (!done)
			ast_log(LOG_DEBUG, "Avoiding %s for channel '%p'\n", msg, c);
		usleep(1);	/* give other threads a chance before retrying */
	/*
 	 * c is surely not null, but we don't have the lock so cannot
	 * access c->name
	 */
	ast_log(LOG_DEBUG, "Failure, could not lock '%p' after %d retries!\n",
/*! \brief 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);
/*! \brief Get channel by name and lock it */
struct ast_channel *ast_get_channel_by_name_locked(const char *name)
	return channel_find_locked(NULL, name, 0, NULL, NULL);
/*! \brief 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);
/*! \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)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_frame *f;

	while (ms > 0) {
		if (cond && ((*cond)(data) == 0))
Mark Spencer's avatar
Mark Spencer committed
			return 0;
		ms = ast_waitfor(chan, ms);
Mark Spencer's avatar
Mark Spencer committed
			return -1;
		if (ms > 0) {
			f = ast_read(chan);
			if (!f)
				return -1;
			ast_frfree(f);
		}
	}
	return 0;
}

/*! \brief Wait, look for hangups */
Mark Spencer's avatar
Mark Spencer committed
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 */
Mark Spencer's avatar
Mark Spencer committed
void ast_channel_free(struct ast_channel *chan)
{
Mark Spencer's avatar
Mark Spencer committed
	int fd;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_var_t *vardata;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_frame *f, *fp;
Mark Spencer's avatar
Mark Spencer committed
	struct varshead *headp;
	struct ast_datastore *datastore = NULL;
	char name[AST_CHANNEL_NAME];
Mark Spencer's avatar
Mark Spencer committed
	
	headp=&chan->varshead;
	
	AST_LIST_REMOVE(&channels, chan, chan_list);
	/* Lock and unlock the channel just to be sure nobody
	   has it locked still */
	ast_channel_lock(chan);
	ast_channel_unlock(chan);
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
	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 );
	}
	/* 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);
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
	free_cid(&chan->cid);
Mark Spencer's avatar
Mark Spencer committed
	/* Close pipes if appropriate */
	if ((fd = chan->alertpipe[0]) > -1)
Mark Spencer's avatar
Mark Spencer committed
		close(fd);
	if ((fd = chan->alertpipe[1]) > -1)
Mark Spencer's avatar
Mark Spencer committed
		close(fd);
	if ((fd = chan->timingfd) > -1)
		close(fd);
Mark Spencer's avatar
Mark Spencer committed
	while(f) {
		fp = f;
		f = f->next;
		ast_frfree(fp);