Skip to content
Snippets Groups Projects
channel.c 85 KiB
Newer Older
Mark Spencer's avatar
Mark Spencer committed
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Channel Management
 * 
 * Copyright (C) 1999 - 2005, Digium, Inc.
Mark Spencer's avatar
Mark Spencer committed
 *
 * Mark Spencer <markster@digium.com>
Mark Spencer's avatar
Mark Spencer committed
 *
 * This program is free software, distributed under the terms of
 * the GNU General Public License
 */

#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 */
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/pbx.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/frame.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/musiconhold.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/logger.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/file.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/translate.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/manager.h>
#include <asterisk/chanvars.h>
#include <asterisk/linkedlists.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/indications.h>
#include <asterisk/monitor.h>
Martin Pycko's avatar
 
Martin Pycko committed
#include <asterisk/causes.h>
#include <asterisk/utils.h>
#include <asterisk/app.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#ifdef __linux__
#include <linux/zaptel.h>
#else
#include <zaptel.h>
#endif /* __linux__ */
#error "You need newer zaptel!  Please cvs update zaptel"
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
Mark Spencer's avatar
Mark Spencer committed
static int shutting_down = 0;
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;
Mark Spencer's avatar
Mark Spencer committed
	struct chanlist *next;
} *backends = NULL;
Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *channels = NULL;

Mark Spencer's avatar
Mark Spencer committed
/* Protect the channel list (highly unlikely that two things would change
   it at the same time, but still! */
   
AST_MUTEX_DEFINE_STATIC(chlock);
static int show_channeltypes(int fd, int argc, char *argv[])
{
#define FORMAT  "%-7.7s  %-50.50s\n"
	struct chanlist *cl = backends;
	ast_cli(fd, FORMAT, "Type", "Description");
	ast_cli(fd, FORMAT, "------", "-----------");
	if (ast_mutex_lock(&chlock)) {
		ast_log(LOG_WARNING, "Unable to lock channel list\n");
		return -1;
	}
	while (cl) {
		ast_cli(fd, FORMAT, cl->tech->type, cl->tech->description);
		cl = cl->next;
	}
	ast_mutex_unlock(&chlock);
	return RESULT_SUCCESS;

}

static char show_channeltypes_usage[] = 
"Usage: show channeltypes\n"
"       Shows available channel types registered in your Asterisk server.\n";

static struct ast_cli_entry cli_show_channeltypes = 
	{ { "show", "channeltypes", NULL }, show_channeltypes, "Show available channel types", show_channeltypes_usage };

Mark Spencer's avatar
Mark Spencer committed
int ast_check_hangup(struct ast_channel *chan)
{
time_t	myt;

	  /* if soft hangup flag, return true */
Mark Spencer's avatar
Mark Spencer committed
	if (chan->_softhangup) return 1;
	  /* if no technology private data, return true */
	if (!chan->tech_pvt) return 1;
Mark Spencer's avatar
Mark Spencer committed
	  /* if no hangup scheduled, just return here */
	if (!chan->whentohangup) return 0;
	time(&myt); /* get current time */
	  /* return, if not yet */
	if (chan->whentohangup > myt) return 0;
Mark Spencer's avatar
Mark Spencer committed
	chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
Mark Spencer's avatar
Mark Spencer committed
	return 1;
}

static int ast_check_hangup_locked(struct ast_channel *chan)
{
	int res;
	res = ast_check_hangup(chan);
Mark Spencer's avatar
Mark Spencer committed
void ast_begin_shutdown(int hangup)
{
	struct ast_channel *c;
	shutting_down = 1;
	if (hangup) {
Mark Spencer's avatar
Mark Spencer committed
		c = channels;
		while(c) {
Mark Spencer's avatar
Mark Spencer committed
			ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
Mark Spencer's avatar
Mark Spencer committed
			c = c->next;
		}
Mark Spencer's avatar
Mark Spencer committed
	}
}

int ast_active_channels(void)
{
	struct ast_channel *c;
	int cnt = 0;
Mark Spencer's avatar
Mark Spencer committed
	c = channels;
	while(c) {
		cnt++;
		c = c->next;
	}
Mark Spencer's avatar
Mark Spencer committed
	return cnt;
}

void ast_cancel_shutdown(void)
{
	shutting_down = 0;
}

int ast_shutting_down(void)
{
	return shutting_down;
}

Mark Spencer's avatar
Mark Spencer committed
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
James Golovich's avatar
James Golovich committed
	time_t	myt;
Mark Spencer's avatar
Mark Spencer committed

	time(&myt);
James Golovich's avatar
James Golovich committed
	if (offset)
		chan->whentohangup = myt + offset;
	else
		chan->whentohangup = 0;
int ast_channel_register(const struct ast_channel_tech *tech)
	struct chanlist *chan;

	ast_mutex_lock(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	chan = backends;
James Golovich's avatar
James Golovich committed
	while (chan) {
		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;
		}
		chan = chan->next;
	}
Mark Spencer's avatar
Mark Spencer committed
	if (!chan) {
		ast_log(LOG_WARNING, "Out of memory\n");
Mark Spencer's avatar
Mark Spencer committed
		return -1;
	}
	chan->tech = tech;
	chan->next = backends;
	backends = chan;

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;
}

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;
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;
	static int prefs[] = 
	{
		/* Okay, ulaw is used by all telephony equipment, so start with it */
		AST_FORMAT_ULAW,
		/* Unless of course, you're a silly European, so then prefer ALAW */
		AST_FORMAT_ALAW,
		/* Okay, well, signed linear is easy to translate into other stuff */
		AST_FORMAT_SLINEAR,
		/* G.726 is standard ADPCM */
		AST_FORMAT_G726,
Mark Spencer's avatar
Mark Spencer committed
		/* ADPCM has great sound quality and is still pretty easy to translate */
		AST_FORMAT_ADPCM,
		/* Okay, we're down to vocoders now, so pick GSM because it's small and easier to
		   translate and sounds pretty good */
		AST_FORMAT_GSM,
Mark Spencer's avatar
Mark Spencer committed
		/* iLBC is not too bad */
		AST_FORMAT_ILBC,
Mark Spencer's avatar
Mark Spencer committed
		/* Speex is free, but computationally more expensive than GSM */
		AST_FORMAT_SPEEX,
Mark Spencer's avatar
Mark Spencer committed
		/* Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
		   to use it */
		AST_FORMAT_LPC10,
Mark Spencer's avatar
Mark Spencer committed
		/* G.729a is faster than 723 and slightly less expensive */
		AST_FORMAT_G729A,
Mark Spencer's avatar
Mark Spencer committed
		/* Down to G.723.1 which is proprietary but at least designed for voice */
		AST_FORMAT_G723_1,
	};
	
	
	for (x=0;x<sizeof(prefs) / sizeof(prefs[0]); x++)
		if (fmts & prefs[x])
			return prefs[x];
	ast_log(LOG_WARNING, "Don't know any of 0x%x formats\n", fmts);
	return 0;
}

static const struct ast_channel_tech null_tech = {
	.type = "NULL",
	.description "Null channel (should not see this)",
};

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;
	struct varshead *headp;        
	        
Mark Spencer's avatar
Mark Spencer committed
	/* If shutting down, don't allocate any new channels */
	if (shutting_down)
		return NULL;
Mark Spencer's avatar
Mark Spencer committed
	tmp = malloc(sizeof(struct ast_channel));
	if (!tmp) {
		ast_log(LOG_WARNING, "Out of memory\n");
		ast_mutex_unlock(&chlock);
		return NULL;
	}

	memset(tmp, 0, sizeof(struct ast_channel));
	tmp->sched = sched_context_create();
	if (!tmp->sched) {
		ast_log(LOG_WARNING, "Unable to create schedule context\n");
		free(tmp);
		ast_mutex_unlock(&chlock);
		return NULL;
	}
	
	for (x=0;x<AST_MAX_FDS - 1;x++)
		tmp->fds[x] = -1;

	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, "Alert pipe creation failed!\n");
Mark Spencer's avatar
Mark Spencer committed
			free(tmp);
			ast_mutex_unlock(&chlock);
			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_MAX_FDS-1] = tmp->alertpipe[0];
	/* And timing pipe */
	tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
	strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
	/* Initial state */
	tmp->_state = AST_STATE_DOWN;
	tmp->streamid = -1;
	tmp->appl = NULL;
	tmp->data = NULL;
	tmp->fin = global_fin;
	tmp->fout = global_fout;
	snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++);
	headp = &tmp->varshead;
	ast_mutex_init(&tmp->lock);
	AST_LIST_HEAD_INIT(headp);
	strncpy(tmp->context, "default", sizeof(tmp->context)-1);
	strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1);
	strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
	tmp->priority = 1;
	tmp->amaflags = ast_default_amaflags;
	strncpy(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode)-1);

	tmp->tech = &null_tech;

	tmp->next = channels;
	channels = tmp;

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

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 */
Mark Spencer's avatar
Mark Spencer committed
	f = ast_frdup(fin);
	if (!f) {
		ast_log(LOG_WARNING, "Unable to duplicate frame\n");
		return -1;
	}
Mark Spencer's avatar
Mark Spencer committed
	prev = NULL;
Mark Spencer's avatar
Mark Spencer committed
	while(cur) {
		if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
			/* Don't bother actually queueing anything after a hangup */
			ast_frfree(f);
			ast_mutex_unlock(&chan->lock);
			return 0;
		}
Mark Spencer's avatar
Mark Spencer committed
		prev = cur;
		cur = cur->next;
		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);
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));
#ifdef ZAPTEL_OPTIMIZATIONS
	} else if (chan->timingfd > -1) {
		ioctl(chan->timingfd, ZT_TIMERPING, &blah);
#endif				
	} else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
		pthread_kill(chan->blocker, SIGURG);
Mark Spencer's avatar
Mark Spencer committed
	}
Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

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 };
	chan->_softhangup |= AST_SOFTHANGUP_DEV;
int ast_queue_control(struct ast_channel *chan, int control)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_frame f = { AST_FRAME_CONTROL, };
	f.subclass = control;
Mark Spencer's avatar
Mark Spencer committed
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
	int pre = 0;
	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;
}

void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
	if (chan)
		ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
struct ast_channel *ast_channel_walk_locked(struct ast_channel *prev)
Mark Spencer's avatar
Mark Spencer committed
{
	/* Returns next channel (locked) */
	struct ast_channel *l, *ret;
Mark Spencer's avatar
Mark Spencer committed
	l = channels;
	if (!prev) {
		if (l) {
			if (ast_mutex_trylock(&l->lock)) {
				if (retries < 10)
Mark Spencer's avatar
Mark Spencer committed
					ast_log(LOG_DEBUG, "Avoiding initial deadlock for '%s'\n", l->name);
Mark Spencer's avatar
Mark Spencer committed
					ast_log(LOG_WARNING, "Avoided initial deadlock for '%s', %d retries!\n", l->name, retries);
				ast_mutex_unlock(&chlock);
				if (retries < 10) {
					usleep(1);
					retries++;
					goto retry;
				} else
					return NULL;
			}
		}
Mark Spencer's avatar
Mark Spencer committed
		return l;
	}
	while(l) {
		if (l == prev)
			ret = l->next;
		l = l->next;
	}
	if (ret) {
		if (ast_mutex_trylock(&ret->lock)) {
			if (retries < 10)
				ast_log(LOG_DEBUG, "Avoiding deadlock for '%s'\n", ret->name);
			else
				ast_log(LOG_WARNING, "Avoided deadlock for '%s', %d retries!\n", ret->name, retries);
			ast_mutex_unlock(&chlock);
			if (retries < 10) {
				usleep(1);
				retries++;
				goto retry;
			} else
				return NULL;
		}
	}
struct ast_channel *ast_get_channel_by_name_locked(char *channame)
{
	struct ast_channel *chan;
	chan = ast_channel_walk_locked(NULL);
	while(chan) {
		if (!strcasecmp(chan->name, channame))
			return chan;
		ast_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		chan = ast_channel_walk_locked(chan);
int ast_safe_sleep_conditional(	struct ast_channel *chan, int ms,
Mark Spencer's avatar
Mark Spencer committed
								int (*cond)(void*), void *data )
{
	struct ast_frame *f;

	while(ms > 0) {
		if( cond && ((*cond)(data) == 0 ) )
			return 0;
		ms = ast_waitfor(chan, ms);
		if (ms <0)
			return -1;
		if (ms > 0) {
			f = ast_read(chan);
			if (!f)
				return -1;
			ast_frfree(f);
		}
	}
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
int ast_safe_sleep(struct ast_channel *chan, int ms)
{
	struct ast_frame *f;
	while(ms > 0) {
		ms = ast_waitfor(chan, ms);
		if (ms <0)
			return -1;
		if (ms > 0) {
			f = ast_read(chan);
			if (!f)
				return -1;
			ast_frfree(f);
		}
	}
	return 0;
}

static void free_cid(struct ast_callerid *cid)
{
	if (cid->cid_dnid)
		free(cid->cid_dnid);
	if (cid->cid_num)
		free(cid->cid_num);	
	if (cid->cid_name)
		free(cid->cid_name);	
	if (cid->cid_ani)
		free(cid->cid_ani);
	if (cid->cid_rdnis)
		free(cid->cid_rdnis);
}

Mark Spencer's avatar
Mark Spencer committed
void ast_channel_free(struct ast_channel *chan)
{
	struct ast_channel *last=NULL, *cur;
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;
	char name[AST_CHANNEL_NAME];
Mark Spencer's avatar
Mark Spencer committed
	
	headp=&chan->varshead;
	
Mark Spencer's avatar
Mark Spencer committed
	cur = channels;
	while(cur) {
		if (cur == chan) {
			if (last)
				last->next = cur->next;
			else
				channels = cur->next;
			break;
		}
		last = cur;
		cur = cur->next;
	}
	if (!cur)
		ast_log(LOG_WARNING, "Unable to find channel in list\n");
	else {
		/* Lock and unlock the channel just to be sure nobody
		   has it locked still */
		ast_mutex_lock(&cur->lock);
		ast_mutex_unlock(&cur->lock);
	}
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);

	strncpy(name, chan->name, sizeof(name)-1);
	
	/* 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);

Mark Spencer's avatar
Mark Spencer committed
	/* Free translatosr */
	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
	if (chan->pbx) 
		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);
	}
Mark Spencer's avatar
Mark Spencer committed
	
	/* loop over the variables list, freeing all data and deleting list items */
	/* no need to lock the list, as the channel is already locked */
	
	while (!AST_LIST_EMPTY(headp)) {           /* List Deletion. */
	            vardata = AST_LIST_REMOVE_HEAD(headp, entries);
/*	            printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata)); */
	            ast_var_delete(vardata);
Mark Spencer's avatar
Mark Spencer committed
	free(chan);

	ast_device_state_changed(name);
Mark Spencer's avatar
Mark Spencer committed
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
Mark Spencer's avatar
Mark Spencer committed
{
	int res = 0;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_frame f = { AST_FRAME_NULL };
Mark Spencer's avatar
Mark Spencer committed
	if (option_debug)
		ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
Mark Spencer's avatar
Mark Spencer committed
	/* Inform channel driver that we need to be hung up, if it cares */
Mark Spencer's avatar
Mark Spencer committed
	chan->_softhangup |= cause;
	/* Interrupt any poll call or such */
	if (ast_test_flag(chan, AST_FLAG_BLOCKING))
Mark Spencer's avatar
Mark Spencer committed
		pthread_kill(chan->blocker, SIGURG);
	return res;
}

Mark Spencer's avatar
Mark Spencer committed
int ast_softhangup(struct ast_channel *chan, int cause)
{
	int res;
Mark Spencer's avatar
Mark Spencer committed
	res = ast_softhangup_nolock(chan, cause);
Mark Spencer's avatar
Mark Spencer committed
	return res;
}

Mark Spencer's avatar
Mark Spencer committed
static void free_translation(struct ast_channel *clone)
{
	if (clone->writetrans)
		ast_translator_free_path(clone->writetrans);
	if (clone->readtrans)
		ast_translator_free_path(clone->readtrans);
	clone->writetrans = NULL;
	clone->readtrans = NULL;
	clone->rawwriteformat = clone->nativeformats;
	clone->rawreadformat = clone->nativeformats;
Mark Spencer's avatar
Mark Spencer committed
int ast_hangup(struct ast_channel *chan)
{
	int res = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Don't actually hang up a channel that will masquerade as someone else, or
	   if someone is going to masquerade as us */
Mark Spencer's avatar
Mark Spencer committed
	if (chan->masq) {
Mark Spencer's avatar
Mark Spencer committed
			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
	}

	if (chan->masq) {
		ast_log(LOG_WARNING, "%s getting hung up, but someone is trying to masq into us?!?\n", chan->name);
Mark Spencer's avatar
Mark Spencer committed
		return 0;
Mark Spencer's avatar
Mark Spencer committed
	}
Mark Spencer's avatar
Mark Spencer committed
	/* If this channel is one which will be masqueraded into something, 
	   mark it as a zombie already, so we know to free it later */
	if (chan->masqr) {
		ast_set_flag(chan, AST_FLAG_ZOMBIE);
		ast_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		return 0;
	}
Mark Spencer's avatar
Mark Spencer committed
	free_translation(chan);
	if (chan->stream) 
		ast_closestream(chan->stream);
	if (chan->vstream)
		ast_closestream(chan->vstream);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->sched)
		sched_context_destroy(chan->sched);
Mark Spencer's avatar
Mark Spencer committed
	/* Clear any tone stuff remaining */
	if (chan->generatordata)
		chan->generator->release(chan, chan->generatordata);
	chan->generatordata = NULL;
	chan->generator = NULL;
Mark Spencer's avatar
Mark Spencer committed
	if (chan->cdr) {
		/* End the CDR if it hasn't already */
		ast_cdr_end(chan->cdr);
		/* Post and Free the CDR */
		ast_cdr_post(chan->cdr);
		ast_cdr_free(chan->cdr);
	}
	if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
Mark Spencer's avatar
Mark Spencer committed
		ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
					"is blocked by thread %ld in procedure %s!  Expect a failure\n",
					(long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
Mark Spencer's avatar
Mark Spencer committed
		CRASH;
	}
	if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
Mark Spencer's avatar
Mark Spencer committed
		if (option_debug)
			ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
		if (chan->tech->hangup)
			res = chan->tech->hangup(chan);
Mark Spencer's avatar
Mark Spencer committed
	} else
		if (option_debug)
			ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
			
Mark Spencer's avatar
Mark Spencer committed
	manager_event(EVENT_FLAG_CALL, "Hangup", 
Mark Spencer's avatar
Mark Spencer committed
			"Channel: %s\r\n"
James Golovich's avatar
James Golovich committed
			"Uniqueid: %s\r\n"
			"Cause: %i\r\n",
			chan->name, chan->uniqueid, chan->hangupcause);
Mark Spencer's avatar
Mark Spencer committed
	ast_channel_free(chan);
Mark Spencer's avatar
Mark Spencer committed
	return res;
}

void ast_channel_unregister(const struct ast_channel_tech *tech)
Mark Spencer's avatar
Mark Spencer committed
{
	struct chanlist *chan, *last=NULL;
Mark Spencer's avatar
Mark Spencer committed
	if (option_debug)
		ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);

	ast_mutex_lock(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	chan = backends;
	while (chan) {
		if (chan->tech == tech) {
Mark Spencer's avatar
Mark Spencer committed
			if (last)
				last->next = chan->next;
			else
				backends = backends->next;
			free(chan);

			if (option_verbose > 1)
				ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);

Mark Spencer's avatar
Mark Spencer committed
			return;
		}
		last = chan;
		chan = chan->next;
	}
Mark Spencer's avatar
Mark Spencer committed
}

int ast_answer(struct ast_channel *chan)
{
Mark Spencer's avatar
Mark Spencer committed
	int res = 0;
	ast_mutex_lock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	/* Stop if we're a zombie or need a soft hangup */
	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
		ast_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		return -1;
Mark Spencer's avatar
Mark Spencer committed
	switch(chan->_state) {
Mark Spencer's avatar
Mark Spencer committed
	case AST_STATE_RINGING:
Mark Spencer's avatar
Mark Spencer committed
	case AST_STATE_RING:
		if (chan->tech->answer)
			res = chan->tech->answer(chan);
Mark Spencer's avatar
Mark Spencer committed
		ast_setstate(chan, AST_STATE_UP);
Mark Spencer's avatar
Mark Spencer committed
		if (chan->cdr)
			ast_cdr_answer(chan->cdr);
Mark Spencer's avatar
Mark Spencer committed
		ast_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		return res;
Mark Spencer's avatar
Mark Spencer committed
		break;
	case AST_STATE_UP:
Mark Spencer's avatar
Mark Spencer committed
		if (chan->cdr)
			ast_cdr_answer(chan->cdr);
Mark Spencer's avatar
Mark Spencer committed
		break;
Mark Spencer's avatar
Mark Spencer committed
	}
	ast_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

void ast_deactivate_generator(struct ast_channel *chan)
Mark Spencer's avatar
Mark Spencer committed
{
	ast_mutex_lock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->generatordata) {
		if (chan->generator && chan->generator->release) 
			chan->generator->release(chan, chan->generatordata);
Mark Spencer's avatar
Mark Spencer committed
		chan->generatordata = NULL;
Mark Spencer's avatar
Mark Spencer committed
		chan->generator = NULL;
		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
		ast_settimeout(chan, 0, NULL, NULL);
Mark Spencer's avatar
Mark Spencer committed
	}
	ast_mutex_unlock(&chan->lock);
}
static int generator_force(void *data)
{
	/* Called if generator doesn't have data */
	void *tmp;
	int res;
	int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
	struct ast_channel *chan = data;
	tmp = chan->generatordata;
	chan->generatordata = NULL;
	generate = chan->generator->generate;
	res = generate(chan, tmp, 0, 160);
	chan->generatordata = tmp;
	if (res) {
		ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
		ast_deactivate_generator(chan);
	}
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
{
	int res = 0;
	ast_mutex_lock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->generatordata) {
		if (chan->generator && chan->generator->release)
			chan->generator->release(chan, chan->generatordata);
Mark Spencer's avatar
Mark Spencer committed
		chan->generatordata = NULL;
	}
	ast_prod(chan);
Mark Spencer's avatar
Mark Spencer committed
	if ((chan->generatordata = gen->alloc(chan, params))) {
		ast_settimeout(chan, 160, generator_force, chan);
Mark Spencer's avatar
Mark Spencer committed
		chan->generator = gen;
	} else {
		res = -1;
Mark Spencer's avatar
Mark Spencer committed
	}
	ast_mutex_unlock(&chan->lock);
	return res;
Mark Spencer's avatar
Mark Spencer committed
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Mark Spencer's avatar
Mark Spencer committed
{
	/* Wait for x amount of time on a file descriptor to have input.  */
	struct timeval start, now;
Mark Spencer's avatar
Mark Spencer committed
	int res;
Mark Spencer's avatar
Mark Spencer committed
	int winner = -1;
	int spoint;
	struct pollfd *pfds;
	pfds = alloca(sizeof(struct pollfd) * n);
	if (!pfds) {
		ast_log(LOG_ERROR, "Out of memory\n");
		return -1;
	}
	if (*ms > 0)
		gettimeofday(&start, NULL);
	y = 0;
Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<n;x++) {
Mark Spencer's avatar
Mark Spencer committed
		if (fds[x] > -1) {
			pfds[y].fd = fds[x];
			pfds[y].events = POLLIN | POLLPRI;
			y++;
Mark Spencer's avatar
Mark Spencer committed
		}
Mark Spencer's avatar
Mark Spencer committed
	}
	res = poll(pfds, y, *ms);
Mark Spencer's avatar
Mark Spencer committed
	if (res < 0) {
		/* Simulate a timeout if we were interrupted */
		if (errno != EINTR)
			*ms = -1;
		else
			*ms = 0;
		return -1;
	}
Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<n;x++) {
		if (fds[x] > -1) {
			if ((res = ast_fdisset(pfds, fds[x], y, &spoint))) {
				winner = fds[x];
				if (exception) {
					if (res & POLLPRI)
						*exception = -1;
					else
						*exception = 0;
				}
			}
Mark Spencer's avatar
Mark Spencer committed
		}
Mark Spencer's avatar
Mark Spencer committed
	}
	if (*ms > 0) {
		long passed;
		gettimeofday(&now, NULL);
		passed = (now.tv_sec - start.tv_sec) * 1000;
		passed += (now.tv_usec - start.tv_usec) / 1000;
		if (passed <= *ms)
			*ms -= passed;
		else
			*ms = 0;
	}
Mark Spencer's avatar
Mark Spencer committed
	return winner;
}

Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, 
	int *exception, int *outfd, int *ms)
Mark Spencer's avatar
Mark Spencer committed
{
	/* Wait for x amount of time on a file descriptor to have input.  */
	struct timeval start, end;
	struct pollfd *pfds;
Mark Spencer's avatar
Mark Spencer committed
	int res;
	long rms;
	int x, y, max;
	int spoint;
	long whentohangup = 0, havewhen = 0, diff;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_channel *winner = NULL;

	pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
	if (!pfds) {
		ast_log(LOG_ERROR, "Out of memory\n");
Mark Spencer's avatar
Mark Spencer committed
	if (outfd)
		*outfd = -99999;
Mark Spencer's avatar
Mark Spencer committed
	if (exception)
		*exception = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Perform any pending masquerades */
	for (x=0;x<n;x++) {
		if (c[x]->whentohangup) {
			diff = c[x]->whentohangup - now;
			if (!havewhen || (diff < whentohangup)) {
				havewhen++;
				whentohangup = diff;
			}
		}
Mark Spencer's avatar
Mark Spencer committed
		if (c[x]->masq) {
Mark Spencer's avatar
Mark Spencer committed
				ast_log(LOG_WARNING, "Masquerade failed\n");
				*ms = -1;
Mark Spencer's avatar
Mark Spencer committed
				return NULL;