Skip to content
Snippets Groups Projects
channel.c 91.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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 */
    
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    #include <sys/ioctl.h>
    #ifdef __linux__
    #include <linux/zaptel.h>
    #else
    #include <zaptel.h>
    #endif /* __linux__ */
    #ifndef ZT_TIMERPING
    #error "You need newer zaptel!  Please cvs update zaptel"
    #endif
    #endif
    
    
    #include "asterisk/pbx.h"
    #include "asterisk/frame.h"
    #include "asterisk/sched.h"
    #include "asterisk/options.h"
    #include "asterisk/channel.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/utils.h"
    #include "asterisk/lock.h"
    #include "asterisk/app.h"
    #include "asterisk/transcap.h"
    
    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  "%-10.10s  %-50.50s %-12.12s\n"
    
    	ast_cli(fd, FORMAT, "Type", "Description",       "Devicestate");
    	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->tech->devicestate)?"yes":"no");
    
    		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 };
    
    
    /*--- ast_check_hangup: 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 soft hangup flag, return true */
    	if (chan->_softhangup) 
    		return 1;
    	/* if no technology private data, return true */
    	if (!chan->tech_pvt) 
    		return 1;
    	/* if no hangup scheduled, just return here */
    	if (!chan->whentohangup) 
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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);
    
    /*--- ast_begin_shutdown: 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) {
    
    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;
    		}
    
    /*--- ast_active_channels: 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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	c = channels;
    	while(c) {
    		cnt++;
    		c = c->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return cnt;
    }
    
    
    /*--- ast_cancel_shutdown: Cancel a shutdown in progress */
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_cancel_shutdown(void)
    {
    	shutting_down = 0;
    }
    
    
    /*--- ast_shutting_down: Returns non-zero if Asterisk is being shut down */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_shutting_down(void)
    {
    	return shutting_down;
    }
    
    
    /*--- ast_channel_setwhentohangup: Set when to hangup channel */
    
    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;
    
    /*--- ast_channel_register: Register a new telephony channel in Asterisk */
    
    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;
    }
    
    
    /*--- ast_state2str: 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;
    
    /*--- ast_transfercapability2str: 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";
    	}
    }
    
    /*--- ast_best_codec: 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;
    	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,
    	};
    	
    	
    
    	/* Find the first prefered 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)",
    
    /*--- ast_channel_alloc: 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;
    
    	struct varshead *headp;        
    	        
    
    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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = malloc(sizeof(struct ast_channel));
    
    		ast_log(LOG_WARNING, "Channel allocation failed: Out of memory\n");
    
    		return NULL;
    	}
    
    	memset(tmp, 0, sizeof(struct ast_channel));
    	tmp->sched = sched_context_create();
    	if (!tmp->sched) {
    
    		ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
    
    	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_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;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmp;
    }
    
    
    /*--- ast_queue_frame: 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 */
    
    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;
    }
    
    
    /*--- ast_queue_hangup: 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 };
    
    	chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    /*--- ast_queue_control: Queue a control frame */
    
    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;
    
    /*--- ast_channel_defer_dtmf: 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;
    	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;
    }
    
    
    /*--- ast_channel_undefer_dtmf: 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);
    
    /*--- ast_channel_walk_locked: Browse channels in use */
    
    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;
    		}
    	}
    
    /*--- ast_get_channel_by_name_locked: Get channel by name and lock it */
    
    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);
    
    /*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */
    
    int ast_safe_sleep_conditional(	struct ast_channel *chan, int ms,
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	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;
    }
    
    
    /*--- ast_safe_sleep: Wait, look for hangups */
    
    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);
    }
    
    
    /*--- ast_channel_free: Free a channel structure */
    
    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);
    
    	            ast_var_delete(vardata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free(chan);
    
    
    	ast_device_state_changed(name);
    
    static void ast_spy_detach(struct ast_channel *chan) 
    {
    	struct ast_channel_spy *chanspy;
    	int to=3000;
    	int sleepms = 100;
    
    	for (chanspy = chan->spiers; chanspy; chanspy = chanspy->next) {
    		if (chanspy->status == CHANSPY_RUNNING) {
    			chanspy->status = CHANSPY_DONE;
    		}
    	}
    
    	/* signal all the spys to get lost and allow them time to unhook themselves 
    	   god help us if they don't......
    	*/
    	while (chan->spiers && to >= 0) {
    		ast_safe_sleep(chan, sleepms);
    		to -= sleepms;
    	}
    	chan->spiers = NULL;
    	return;
    }
    
    
    /*--- ast_softhangup_nolock: Softly hangup a channel, don't lock */
    
    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;
    }
    
    
    /*--- ast_softhangup_nolock: Softly hangup a channel, lock */
    
    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;
    }
    
    
    static void ast_queue_spy_frame(struct ast_channel_spy *spy, struct ast_frame *f, int pos) 
    {
    	struct ast_frame *tmpf = NULL;
    	int count = 0;
    
    	ast_mutex_lock(&spy->lock);
    	for (tmpf=spy->queue[pos]; tmpf && tmpf->next; tmpf=tmpf->next) {
    		count++;
    	}
    
    	if (count > 1000) {
    
    		struct ast_frame *freef, *headf;
    
    
    		ast_log(LOG_ERROR, "Too Many frames queued at once, flushing cache.\n");
    
    		headf = spy->queue[pos];
    		/* deref the queue right away so it looks empty */
    		spy->queue[pos] = NULL;
    		tmpf = headf;
    		/* free the wasted frames */
    		while (tmpf) {
    			freef = tmpf;
    			tmpf = tmpf->next;
    			ast_frfree(freef);
    		}
    		ast_mutex_unlock(&spy->lock);
    		return;
    	}
    
    	if (tmpf) {
    		tmpf->next = ast_frdup(f);
    	} else {
    		spy->queue[pos] = ast_frdup(f);
    	}
    
    	ast_mutex_unlock(&spy->lock);
    }
    
    
    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;
    
    /*--- ast_hangup: Hangup a channel */
    
    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 */
    
    	
    	ast_spy_detach(chan);		/* get rid of spies */
    
    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) 		/* Close audio stream */
    
    		ast_closestream(chan->stream);
    
    	if (chan->vstream)		/* Close video stream */
    
    		ast_closestream(chan->vstream);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->sched)
    		sched_context_destroy(chan->sched);
    
    	
    	if (chan->generatordata)	/* Clear any tone stuff remaining */ 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator->release(chan, chan->generatordata);
    	chan->generatordata = NULL;
    	chan->generator = NULL;
    
    	if (chan->cdr) {		/* End the CDR if it hasn't already */ 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_end(chan->cdr);
    
    		ast_cdr_detach(chan->cdr);	/* Post and Free the CDR */ 
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	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"
    
    			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);