Skip to content
Snippets Groups Projects
channel.c 55.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Channel Management
     * 
    
    Mark Spencer's avatar
    Mark Spencer committed
     * Copyright (C) 1999, Mark Spencer
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <pthread.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/channel_pvt.h>
    #include <asterisk/logger.h>
    #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>
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int shutting_down = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* XXX Lock appropriately in more functions XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #ifdef DEBUG_MUTEX
    /* Convenient mutex debugging functions */
    #define PTHREAD_MUTEX_LOCK(a) __PTHREAD_MUTEX_LOCK(__FUNCTION__, a)
    #define PTHREAD_MUTEX_UNLOCK(a) __PTHREAD_MUTEX_UNLOCK(__FUNCTION__, a)
    
    static int __PTHREAD_MUTEX_LOCK(char *f, pthread_mutex_t *a) {
    	ast_log(LOG_DEBUG, "Locking %p (%s)\n", a, f); 
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_pthread_mutex_lock(a);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static int __PTHREAD_MUTEX_UNLOCK(char *f, pthread_mutex_t *a) {
    	ast_log(LOG_DEBUG, "Unlocking %p (%s)\n", a, f); 
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_pthread_mutex_unlock(a);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    #else
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define PTHREAD_MUTEX_LOCK(a) ast_pthread_mutex_lock(a)
    #define PTHREAD_MUTEX_UNLOCK(a) ast_pthread_mutex_unlock(a)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct chanlist {
    	char type[80];
    	char description[80];
    	int capabilities;
    	struct ast_channel * (*requester)(char *type, int format, void *data);
    	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! */
       
    
    Mark Spencer's avatar
    Mark Spencer committed
    static pthread_mutex_t chlock = AST_MUTEX_INITIALIZER;
    
    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 private structure, return true */
    	if (!chan->pvt->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;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_begin_shutdown(int hangup)
    {
    	struct ast_channel *c;
    	shutting_down = 1;
    	if (hangup) {
    		PTHREAD_MUTEX_LOCK(&chlock);
    		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;
    		}
    		PTHREAD_MUTEX_UNLOCK(&chlock);
    	}
    }
    
    int ast_active_channels(void)
    {
    	struct ast_channel *c;
    	int cnt = 0;
    	PTHREAD_MUTEX_LOCK(&chlock);
    	c = channels;
    	while(c) {
    		cnt++;
    		c = c->next;
    	}
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    	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)
    {
    time_t	myt;
    
    	time(&myt);
    
    Mark Spencer's avatar
    Mark Spencer committed
            if (offset)
    	  chan->whentohangup = myt + offset;
            else
              chan->whentohangup = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_register(char *type, char *description, int capabilities,
    		struct ast_channel *(*requester)(char *type, int format, void *data))
    {
    	struct chanlist *chan, *last=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (PTHREAD_MUTEX_LOCK(&chlock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return -1;
    	}
    	chan = backends;
    	while(chan) {
    		if (!strcasecmp(type, chan->type)) {
    			ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    		last = chan;
    		chan = chan->next;
    	}
    	chan = malloc(sizeof(struct chanlist));
    	if (!chan) {
    		ast_log(LOG_WARNING, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	strncpy(chan->type, type, sizeof(chan->type)-1);
    	strncpy(chan->description, description, sizeof(chan->description)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->capabilities = capabilities;
    	chan->requester = requester;
    	chan->next = NULL;
    	if (last)
    		last->next = chan;
    	else
    		backends = chan;
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
    	else if (option_verbose > 1)
    		ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    char *ast_state2str(int state)
    {
    	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:
    		return "Unknown";
    	}
    }
    
    
    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,
    		/* 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
    		/* 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,
    		/* Last and least, MP3 which was of course never designed for real-time voice */
    		AST_FORMAT_MP3,
    	};
    	
    	
    	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;
    }
    
    
    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;
    	struct ast_channel_pvt *pvt;
    
    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
    	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
    	PTHREAD_MUTEX_LOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tmp = malloc(sizeof(struct ast_channel));
    	memset(tmp, 0, sizeof(struct ast_channel));
    	if (tmp) {
    		pvt = malloc(sizeof(struct ast_channel_pvt));
    		if (pvt) {
    			memset(pvt, 0, sizeof(struct ast_channel_pvt));
    			tmp->sched = sched_context_create();
    			if (tmp->sched) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				for (x=0;x<AST_MAX_FDS - 1;x++)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					tmp->fds[x] = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				if (needqueue &&  
    					pipe(pvt->alertpipe)) {
    					ast_log(LOG_WARNING, "Alert pipe creation failed!\n");
    					free(pvt);
    					free(tmp);
    					tmp = NULL;
    					pvt = NULL;
    				} else {
    					/* Make sure we've got it done right if they don't */
    					if (needqueue) {
    						flags = fcntl(pvt->alertpipe[0], F_GETFL);
    						fcntl(pvt->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
    						flags = fcntl(pvt->alertpipe[1], F_GETFL);
    						fcntl(pvt->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
    					} else
    						pvt->alertpipe[0] = pvt->alertpipe[1] = -1;
    					/* Always watch the alertpipe */
    					tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0];
    					strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
    					tmp->pvt = pvt;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					/* Initial state */
    					tmp->_state = AST_STATE_DOWN;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					tmp->stack = -1;
    					tmp->streamid = -1;
    					tmp->appl = NULL;
    					tmp->data = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					tmp->fin = 0;
    					tmp->fout = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					headp=&tmp->varshead;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					ast_pthread_mutex_init(&tmp->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				        AST_LIST_HEAD_INIT(headp);
    					tmp->vars=ast_var_assign("tempvar","tempval");
    					AST_LIST_INSERT_HEAD(headp,tmp->vars,entries);
    
    Mark Spencer's avatar
    Mark Spencer committed
    					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->next = channels;
    					channels= tmp;
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    				ast_log(LOG_WARNING, "Unable to create schedule context\n");
    				free(tmp);
    				tmp = NULL;
    			}
    		} else {
    			ast_log(LOG_WARNING, "Out of memory\n");
    			free(tmp);
    			tmp = NULL;
    		}
    	} else 
    		ast_log(LOG_WARNING, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return tmp;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int lock)
    {
    	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;
    	}
    	if (lock)
    		ast_pthread_mutex_lock(&chan->lock);
    	prev = NULL;
    	cur = chan->pvt->readq;
    	while(cur) {
    		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);
    			ast_frfree(fin);
    			if (lock)
    				ast_pthread_mutex_unlock(&chan->lock);
    			return 0;
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (prev)
    		prev->next = f;
    	else
    		chan->pvt->readq = f;
    	if (chan->pvt->alertpipe[1] > -1) {
    		if (write(chan->pvt->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));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if (lock)
    		ast_pthread_mutex_unlock(&chan->lock);
    	return 0;
    }
    
    int ast_queue_hangup(struct ast_channel *chan, int lock)
    {
    	struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
    
    	chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ast_queue_frame(chan, &f, lock);
    }
    
    int ast_queue_control(struct ast_channel *chan, int control, int lock)
    {
    	struct ast_frame f = { AST_FRAME_CONTROL, };
    	f.subclass = control;
    	return ast_queue_frame(chan, &f, lock);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_defer_dtmf(struct ast_channel *chan)
    {
    	int pre = 0;
    	if (chan) {
    		pre = chan->deferdtmf;
    		chan->deferdtmf = 1;
    	}
    	return pre;
    }
    
    void ast_channel_undefer_dtmf(struct ast_channel *chan)
    {
    	if (chan)
    		chan->deferdtmf = 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_channel *ast_channel_walk(struct ast_channel *prev)
    {
    	struct ast_channel *l, *ret=NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_LOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	l = channels;
    	if (!prev) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return l;
    	}
    	while(l) {
    		if (l == prev)
    			ret = l->next;
    		l = l->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_safe_sleep_conditional(	struct ast_channel *chan, int ms,
    								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;
    }
    
    
    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;
    	
    	headp=&chan->varshead;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_LOCK(&chlock);
    
    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");
    	if (chan->pvt->pvt)
    		ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
    
    	/* Stop monitoring */
    	if (chan->monitor) {
    		chan->monitor->stop( chan, 0 );
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Free translatosr */
    	if (chan->pvt->readtrans)
    		ast_translator_free_path(chan->pvt->readtrans);
    	if (chan->pvt->writetrans)
    		ast_translator_free_path(chan->pvt->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);
    	if (chan->dnid)
    		free(chan->dnid);
    	if (chan->callerid)
    		free(chan->callerid);	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->ani)
    		free(chan->ani);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->rdnis)
    		free(chan->rdnis);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	pthread_mutex_destroy(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Close pipes if appropriate */
    	if ((fd = chan->pvt->alertpipe[0]) > -1)
    		close(fd);
    	if ((fd = chan->pvt->alertpipe[1]) > -1)
    		close(fd);
    	f = chan->pvt->readq;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->pvt->readq = NULL;
    
    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_FIRST(headp);
    	            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->pvt);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->pvt = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    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;
    	ast_queue_frame(chan, &f, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Interrupt any select call or such */
    	if (chan->blocking)
    		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;
    	ast_pthread_mutex_lock(&chan->lock);
    	res = ast_softhangup_nolock(chan, cause);
    	ast_pthread_mutex_unlock(&chan->lock);
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int ast_do_masquerade(struct ast_channel *original);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static void free_translation(struct ast_channel *clone)
    {
    	if (clone->pvt->writetrans)
    		ast_translator_free_path(clone->pvt->writetrans);
    	if (clone->pvt->readtrans)
    		ast_translator_free_path(clone->pvt->readtrans);
    	clone->pvt->writetrans = NULL;
    	clone->pvt->readtrans = NULL;
    	clone->pvt->rawwriteformat = clone->nativeformats;
    	clone->pvt->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
    	ast_pthread_mutex_lock(&chan->lock);
    	if (chan->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (ast_do_masquerade(chan)) 
    			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
    		ast_pthread_mutex_unlock(&chan->lock);
    
    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) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_pthread_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->zombie=1;
    		return 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free_translation(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->stream)
    		ast_stopstream(chan);
    	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);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->blocking) {
    		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",
    					pthread_self(), chan->name, chan->blocker, chan->blockproc);
    		CRASH;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->zombie) {
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
    		if (chan->pvt->hangup)
    			res = chan->pvt->hangup(chan);
    	} else
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
    			
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Hangup", 
    			"Channel: %s\r\n",
    			chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_channel_free(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    void ast_channel_unregister(char *type)
    {
    	struct chanlist *chan, *last=NULL;
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (PTHREAD_MUTEX_LOCK(&chlock)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return;
    	}
    	chan = backends;
    	while(chan) {
    		if (!strcasecmp(chan->type, type)) {
    			if (last)
    				last->next = chan->next;
    			else
    				backends = backends->next;
    			free(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return;
    		}
    		last = chan;
    		chan = chan->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	PTHREAD_MUTEX_UNLOCK(&chlock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    int ast_answer(struct ast_channel *chan)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->zombie || ast_check_hangup(chan)) 
    
    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:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->pvt->answer)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = chan->pvt->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
    		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
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_deactivate_generator(struct ast_channel *chan)
    {
    	if (chan->generatordata) {
    		chan->generator->release(chan, chan->generatordata);
    		chan->generatordata = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->writeinterrupt = 0;
    	}
    }
    
    int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
    {
    	if (chan->generatordata) {
    		chan->generator->release(chan, chan->generatordata);
    		chan->generatordata = NULL;
    	}
    	if ((chan->generatordata = gen->alloc(chan, params))) {
    		chan->generator = gen;
    	} else {
    		return -1;
    	}
    	return 0;
    }
    
    
    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 tv;
    	fd_set rfds, efds;
    	int res;
    	int x, max=-1;
    	int winner = -1;
    	
    	tv.tv_sec = *ms / 1000;
    	tv.tv_usec = (*ms % 1000) * 1000;
    	FD_ZERO(&rfds);
    	FD_ZERO(&efds);
    	for (x=0;x<n;x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (fds[x] > -1) {
    			FD_SET(fds[x], &rfds);
    			FD_SET(fds[x], &efds);
    			if (fds[x] > max)
    				max = fds[x];
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	if (*ms >= 0) 
    		res = select(max + 1, &rfds, NULL, &efds, &tv);
    	else
    		res = select(max + 1, &rfds, NULL, &efds, NULL);
    
    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++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if ((fds[x] > -1) && (FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (exception)
    				*exception = FD_ISSET(fds[x], &efds);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			winner = fds[x];
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    	*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
    	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 tv;
    	fd_set rfds, efds;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int x, y, max=-1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *winner = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (outfd)
    		*outfd = -1;
    	if (exception)
    		*exception = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Perform any pending masquerades */
    	for (x=0;x<n;x++) {
    		if (c[x]->masq) {
    			if (ast_do_masquerade(c[x])) {
    				ast_log(LOG_WARNING, "Masquerade failed\n");
    				*ms = -1;
    				return NULL;
    			}
    		}
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	tv.tv_sec = *ms / 1000;
    	tv.tv_usec = (*ms % 1000) * 1000;
    	FD_ZERO(&rfds);
    	FD_ZERO(&efds);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<n;x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (y=0;y<AST_MAX_FDS;y++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (c[x]->fds[y] > -1) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				FD_SET(c[x]->fds[y], &rfds);
    				FD_SET(c[x]->fds[y], &efds);
    				if (c[x]->fds[y] > max)
    					max = c[x]->fds[y];
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CHECK_BLOCKING(c[x]);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<nfds; x++) {
    		FD_SET(fds[x], &rfds);
    		FD_SET(fds[x], &efds);
    		if (fds[x] > max)
    			max = fds[x];
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (*ms >= 0) 
    		res = select(max + 1, &rfds, NULL, &efds, &tv);
    	else
    		res = select(max + 1, &rfds, NULL, &efds, NULL);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if (res < 0) {
    		for (x=0;x<n;x++) 
    			c[x]->blocking = 0;
    		/* Simulate a timeout if we were interrupted */
    		if (errno != EINTR)
    			*ms = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else {
    			/* Just an interrupt */
    #if 0
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*ms = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif			
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<n;x++) {
    		c[x]->blocking = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (y=0;y<AST_MAX_FDS;y++) {
    			if (c[x]->fds[y] > -1) {
    				if ((FD_ISSET(c[x]->fds[y], &rfds) || FD_ISSET(c[x]->fds[y], &efds)) && !winner) {
    					/* Set exception flag if appropriate */
    					if (FD_ISSET(c[x]->fds[y], &efds))
    						c[x]->exception = 1;
    					c[x]->fdno = y;
    					winner = c[x];
    				}
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<nfds;x++) {
    		if ((FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && !winner) {
    			if (outfd)
    				*outfd = fds[x];
    			if (FD_ISSET(fds[x], &efds) && exception)
    				*exception = 1;
    			winner = NULL;
    		}
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
    	return winner;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
    {
    	return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_waitfor(struct ast_channel *c, int ms)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *chan;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int oldms = ms;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan = ast_waitfor_n(&c, 1, &ms);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (ms < 0) {
    		if (oldms < 0)
    			return 0;
    		else
    			return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ms;
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    char ast_waitfordigit(struct ast_channel *c, int ms)
    {
    
    	/* XXX Should I be merged with waitfordigit_full XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_frame *f;
    	char result = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (c->zombie || ast_check_hangup(c)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Wait for a digit, no more than ms milliseconds total. */
    	while(ms && !result) {
    		ms = ast_waitfor(c, ms);
    		if (ms < 0) /* Error */
    			result = -1; 
    		else if (ms > 0) {
    			/* Read something */
    			f = ast_read(c);
    			if (f) {
    				if (f->frametype == AST_FRAME_DTMF) 
    					result = f->subclass;
    				ast_frfree(f);
    			} else
    				result = -1;
    		}
    	}
    	return result;
    }
    
    
    char ast_waitfordigit_full(struct ast_channel *c, int ms, int audio, int ctrl)
    {
    	struct ast_frame *f;
    	char result = 0;
    	struct ast_channel *rchan;
    	int outfd;
    	/* Stop if we're a zombie or need a soft hangup */
    	if (c->zombie || ast_check_hangup(c)) 
    		return -1;
    	/* Wait for a digit, no more than ms milliseconds total. */
    	while(ms && !result) {
    		rchan = ast_waitfor_nandfds(&c, 1, &audio, (audio > -1) ? 1 : 0, NULL, &outfd, &ms);
    		if ((!rchan) && (outfd < 0) && (ms)) /* Error */
    			result = -1; 
    		else if (outfd > -1) {
    			result = 1;
    		} else if (rchan) {
    			/* Read something */
    			f = ast_read(c);
    			if (f) {
    				if (f->frametype == AST_FRAME_DTMF) 
    					result = f->subclass;
    				ast_frfree(f);
    			} else
    				result = -1;
    		}
    	}
    	return result;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_frame *ast_read(struct ast_channel *chan)
    {
    	struct ast_frame *f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int blah;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	static struct ast_frame null_frame = 
    	{
    		AST_FRAME_NULL,
    	};
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_pthread_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->masq) {
    		if (ast_do_masquerade(chan)) {
    			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    			f = NULL;
    		} else
    			f =  &null_frame;
    		pthread_mutex_unlock(&chan->lock);
    		return f;
    	}
    
    	/* Stop if we're a zombie or need a soft hangup */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->zombie || ast_check_hangup(chan)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->generator)
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_mutex_unlock(&chan->lock);
    		return NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->deferdtmf && strlen(chan->dtmfq)) {
    		/* We have DTMF that has been deferred.  Return it now */
    		chan->dtmff.frametype = AST_FRAME_DTMF;
    		chan->dtmff.subclass = chan->dtmfq[0];
    		/* Drop first digit */
    		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
    		pthread_mutex_unlock(&chan->lock);
    		return &chan->dtmff;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Read and ignore anything on the alertpipe, but read only
    	   one sizeof(blah) per frame that we send from it */
    	if (chan->pvt->alertpipe[0] > -1) {
    		read(chan->pvt->alertpipe[0], &blah, sizeof(blah));
    	}
    
    	/* Check for pending read queue */
    	if (chan->pvt->readq) {
    		f = chan->pvt->readq;
    		chan->pvt->readq = f->next;
    		/* Interpret hangup and return NULL */
    		if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP))
    			f = NULL;
    	} else {
    		chan->blocker = pthread_self();
    		if (chan->exception) {
    			if (chan->pvt->exception) 
    				f = chan->pvt->exception(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else {
    				ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
    				f = &null_frame;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Clear the exception flag */
    			chan->exception = 0;
    		} else
    		if (chan->pvt->read)
    			f = chan->pvt->read(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
    	}