Skip to content
Snippets Groups Projects
channel.c 124 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    		f = f->next;
    		ast_frfree(fp);
    	}
    
    	/* Get rid of each of the data stores on the channel */
    
    	while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
    
    		/* Free the data store */
    		ast_channel_datastore_free(datastore);
    
    	AST_LIST_HEAD_INIT_NOLOCK(&chan->datastores);
    
    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 ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
    		ast_var_delete(vardata);
    
    	if (chan->jb)
    		ast_jb_destroy(chan);
    
    	ast_string_field_free_all(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free(chan);
    
    struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid)
    {
    	struct ast_datastore *datastore = NULL;
    
    	/* Make sure we at least have type so we can identify this */
    	if (info == NULL) {
    		return NULL;
    	}
    
    	/* Allocate memory for datastore and clear it */
    	datastore = ast_calloc(1, sizeof(*datastore));
    	if (datastore == NULL) {
    		return NULL;
    	}
    
    	datastore->info = info;
    
    
    	datastore->uid = ast_strdup(uid);
    
    
    	return datastore;
    }
    
    int ast_channel_datastore_free(struct ast_datastore *datastore)
    {
    	int res = 0;
    
    	/* Using the destroy function (if present) destroy the data */
    	if (datastore->info->destroy != NULL && datastore->data != NULL) {
    		datastore->info->destroy(datastore->data);
    		datastore->data = NULL;
    	}
    
    	/* Free allocated UID memory */
    	if (datastore->uid != NULL) {
    		free(datastore->uid);
    		datastore->uid = NULL;
    	}
    
    	/* Finally free memory used by ourselves */
    	free(datastore);
    
    	return res;
    }
    
    int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
    {
    	int res = 0;
    
    
    	AST_LIST_INSERT_HEAD(&chan->datastores, datastore, entry);
    
    
    	return res;
    }
    
    int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
    {
    	struct ast_datastore *datastore2 = NULL;
    	int res = -1;
    
    	/* Find our position and remove ourselves */
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore2, entry) {
    
    			AST_LIST_REMOVE_CURRENT(&chan->datastores, entry);
    
    			res = 0;
    			break;
    		}
    	}
    	AST_LIST_TRAVERSE_SAFE_END
    
    	return res;
    }
    
    struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid)
    {
    	struct ast_datastore *datastore = NULL;
    	
    	if (info == NULL)
    		return NULL;
    
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, entry) {
    
    		if (datastore->info == info) {
    			if (uid != NULL && datastore->uid != NULL) {
    				if (!strcasecmp(uid, datastore->uid)) {
    					/* Matched by type AND uid */
    					break;
    				}
    			} else {
    				/* Matched by type at least */
    				break;
    			}
    		}
    	}
    	AST_LIST_TRAVERSE_SAFE_END
    
    	return datastore;
    }
    
    
    int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
    
    	/* Link the owner channel to the spy */
    	spy->chan = chan;
    
    
    	if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
    		ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
    			spy->type, chan->name);
    		return -1;
    	}
    
    	if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
    		ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
    			ast_getformatname(spy->read_queue.format));
    		return -1;
    	}
    
    	if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
    		ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
    			ast_getformatname(spy->write_queue.format));
    		return -1;
    	}
    
    	if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
    	    ((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
    	     (spy->write_queue.format != AST_FORMAT_SLINEAR))) {
    		ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
    			ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
    		return -1;
    	}
    
    	if (!chan->spies) {
    
    		if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
    
    
    		AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
    		AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
    	} else {
    		AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
    
    	if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
    		ast_cond_init(&spy->trigger, NULL);
    		ast_set_flag(spy, CHANSPY_TRIGGER_READ);
    		ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
    	}
    
    	ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
    		spy->type, chan->name);
    
    	return 0;
    }
    
    void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
    {
    	struct ast_channel_spy *spy;
    	
    	if (!chan->spies)
    		return;
    
    	AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
    
    		ast_mutex_lock(&spy->lock);
    		if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
    
    			spy->status = CHANSPY_STOP;
    
    			if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
    				ast_cond_signal(&spy->trigger);
    		}
    		ast_mutex_unlock(&spy->lock);
    
    	}
    }
    
    void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
    {
    
    	struct timeval tv;
    	struct timespec ts;
    
    	tv = ast_tvadd(ast_tvnow(), ast_samp2tv(50000, 1000));
    	ts.tv_sec = tv.tv_sec;
    	ts.tv_nsec = tv.tv_usec * 1000;
    
    	ast_cond_timedwait(&spy->trigger, &spy->lock, &ts);
    
    }
    
    void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
    {
    	struct ast_frame *f;
    
    	if (!chan->spies)
    		return;
    
    	AST_LIST_REMOVE(&chan->spies->list, spy, list);
    
    	ast_mutex_lock(&spy->lock);
    
    
    	for (f = spy->read_queue.head; f; f = spy->read_queue.head) {
    		spy->read_queue.head = f->next;
    		ast_frfree(f);
    	}
    	for (f = spy->write_queue.head; f; f = spy->write_queue.head) {
    		spy->write_queue.head = f->next;
    		ast_frfree(f);
    	}
    
    	if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
    		ast_cond_destroy(&spy->trigger);
    
    	ast_mutex_unlock(&spy->lock);
    
    	ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
    		spy->type, chan->name);
    
    	if (AST_LIST_EMPTY(&chan->spies->list)) {
    		if (chan->spies->read_translator.path)
    			ast_translator_free_path(chan->spies->read_translator.path);
    		if (chan->spies->write_translator.path)
    			ast_translator_free_path(chan->spies->write_translator.path);
    		free(chan->spies);
    		chan->spies = NULL;
    	}
    }
    
    
    static void detach_spies(struct ast_channel *chan)
    
    {
    	struct ast_channel_spy *spy;
    
    	if (!chan->spies)
    		return;
    
    	/* Marking the spies as done is sufficient.  Chanspy or spy users will get the picture. */
    	AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
    		ast_mutex_lock(&spy->lock);
    
    		spy->chan = NULL;
    
    		if (spy->status == CHANSPY_RUNNING)
    			spy->status = CHANSPY_DONE;
    		if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
    			ast_cond_signal(&spy->trigger);
    		ast_mutex_unlock(&spy->lock);
    	}
    
    	AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
    		ast_channel_spy_remove(chan, spy);
    	AST_LIST_TRAVERSE_SAFE_END;
    
    /*! \brief 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
    {
    	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, &ast_null_frame);
    
    	/* 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);
    
    /*! \brief Softly hangup a channel, lock */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_softhangup(struct ast_channel *chan, int cause)
    {
    	int res;
    
    	ast_channel_lock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_softhangup_nolock(chan, cause);
    
    	ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    enum spy_direction {
    	SPY_READ,
    	SPY_WRITE,
    };
    
    #define SPY_QUEUE_SAMPLE_LIMIT 4000			/* half of one second */
    
    static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f, enum spy_direction dir)
    
    	struct ast_frame *translated_frame = NULL;
    	struct ast_channel_spy *spy;
    	struct channel_spy_trans *trans;
    
    	trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
    
    	AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
    
    		struct ast_frame *last;
    		struct ast_frame *f1;	/* the frame to append */
    		struct ast_channel_spy_queue *queue;
    
    
    		ast_mutex_lock(&spy->lock);
    
    		queue = (dir == SPY_READ) ? &spy->read_queue : &spy->write_queue;
    
    		if ((queue->format == AST_FORMAT_SLINEAR) && (f->subclass != AST_FORMAT_SLINEAR)) {
    			if (!translated_frame) {
    				if (trans->path && (trans->last_format != f->subclass)) {
    					ast_translator_free_path(trans->path);
    					trans->path = NULL;
    				}
    				if (!trans->path) {
    					ast_log(LOG_DEBUG, "Building translator from %s to SLINEAR for spies on channel %s\n",
    						ast_getformatname(f->subclass), chan->name);
    					if ((trans->path = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass)) == NULL) {
    						ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n",
    							ast_getformatname(f->subclass), ast_getformatname(AST_FORMAT_SLINEAR));
    						ast_mutex_unlock(&spy->lock);
    						continue;
    					} else {
    						trans->last_format = f->subclass;
    					}
    				}
    
    				if (!(translated_frame = ast_translate(trans->path, f, 0))) {
    					ast_log(LOG_ERROR, "Translation to %s failed, dropping frame for spies\n",
    						ast_getformatname(AST_FORMAT_SLINEAR));
    					ast_mutex_unlock(&spy->lock);
    					break;
    				}
    
    		} else {
    			if (f->subclass != queue->format) {
    				ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
    					spy->type, chan->name,
    					ast_getformatname(queue->format), ast_getformatname(f->subclass));
    				ast_mutex_unlock(&spy->lock);
    				continue;
    			}
    
    		/* duplicate and append f1 to the tail */
    		f1 = ast_frdup(f1);
    
    
    Luigi Rizzo's avatar
    Luigi Rizzo committed
    		for (last = queue->head; last && last->next; last = last->next)
    			;
    
    		if (last)
    			last->next = f1;
    		else
    			queue->head = f1;
    
    		queue->samples += f->samples;
    
    		if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
    			if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
    
    				switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
    				case CHANSPY_TRIGGER_READ:
    					if (dir == SPY_WRITE) {
    						ast_set_flag(spy, CHANSPY_TRIGGER_WRITE);
    						ast_clear_flag(spy, CHANSPY_TRIGGER_READ);
    						if (option_debug)
    							ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to write-trigger mode\n",
    								spy->type, chan->name);
    
    					break;
    				case CHANSPY_TRIGGER_WRITE:
    					if (dir == SPY_READ) {
    						ast_set_flag(spy, CHANSPY_TRIGGER_READ);
    						ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
    						if (option_debug)
    							ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to read-trigger mode\n",
    								spy->type, chan->name);
    					}
    					break;
    				}
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Triggering queue flush for spy '%s' on '%s'\n",
    						spy->type, chan->name);
    				ast_set_flag(spy, CHANSPY_TRIGGER_FLUSH);
    				ast_cond_signal(&spy->trigger);
    			} else {
    				if (option_debug)
    					ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
    						spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
    				while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
    					struct ast_frame *drop = queue->head;
    					
    					queue->samples -= drop->samples;
    					queue->head = drop->next;
    					ast_frfree(drop);
    
    				}
    			}
    		} else {
    			switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
    			case CHANSPY_TRIGGER_READ:
    				if (dir == SPY_READ)
    					ast_cond_signal(&spy->trigger);
    				break;
    			case CHANSPY_TRIGGER_WRITE:
    				if (dir == SPY_WRITE)
    					ast_cond_signal(&spy->trigger);
    				break;
    			}
    		}
    
    		ast_mutex_unlock(&spy->lock);
    
    	if (translated_frame)
    		ast_frfree(translated_frame);
    
    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;
    
    /*! \brief 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_channel_lock(chan);
    
    	detach_spies(chan);		/* get rid of spies */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->masq) {
    
    		if (ast_do_masquerade(chan))
    
    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);
    
    		ast_channel_unlock(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* If this channel is one which will be masqueraded into something,
    
    Mark Spencer's avatar
    Mark Spencer committed
    	   mark it as a zombie already, so we know to free it later */
    	if (chan->masqr) {
    
    		ast_set_flag(chan, AST_FLAG_ZOMBIE);
    
    		ast_channel_unlock(chan);
    
    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
    		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 */
    
    	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
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
    
    	ast_channel_unlock(chan);
    
    	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: %d\r\n"
    			"Cause-txt: %s\r\n",
    
    			chan->hangupcause,
    			ast_cause2str(chan->hangupcause)
    			);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_channel_free(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int ast_answer(struct ast_channel *chan)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0;
    
    	ast_channel_lock(chan);
    
    	/* You can't answer an outbound call */
    
    	if (ast_test_flag(chan, AST_FLAG_OUTGOING)) {
    		ast_channel_unlock(chan);
    
    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_channel_unlock(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:
    
    		if (chan->tech->answer)
    			res = chan->tech->answer(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(chan, AST_STATE_UP);
    
    		ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    	case AST_STATE_UP:
    
    		ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	ast_channel_unlock(chan);
    
    void ast_deactivate_generator(struct ast_channel *chan)
    
    	ast_channel_lock(chan);
    
    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;
    
    		chan->fds[AST_GENERATOR_FD] = -1;
    
    		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
    
    		ast_settimeout(chan, 0, NULL, NULL);
    
    	ast_channel_unlock(chan);
    
    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_channel_lock(chan);
    
    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);
    
    	if (gen->alloc && !(chan->generatordata = gen->alloc(chan, params))) {
    		res = -1;
    
    		ast_settimeout(chan, 160, generator_force, chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator = gen;
    	}
    
    	ast_channel_unlock(chan);
    
    	return res;
    
    /*! \brief Wait for x amount of time on a file descriptor to have input.  */
    
    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
    {
    	int winner = -1;
    
    	ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return winner;
    }
    
    
    /*! \brief Wait for x amount of time on a file descriptor to have input.  */
    
    struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int *exception, int *outfd, int *ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct timeval start = { 0 , 0 };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    	long whentohangup = 0, diff;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *winner = NULL;
    
    	struct fdmap {
    		int chan;
    		int fdno;
    	} *fdmap;
    
    	sz = n * AST_MAX_FDS + nfds;
    
    	pfds = alloca(sizeof(*pfds) * sz);
    	fdmap = alloca(sizeof(*fdmap) * sz);
    
    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++) {
    
    		ast_channel_lock(c[x]);
    
    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;
    
    				ast_channel_unlock(c[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    		}
    
    		if (c[x]->whentohangup) {
    			if (!whentohangup)
    				time(&now);
    			diff = c[x]->whentohangup - now;
    			if (diff < 1) {
    				/* Should already be hungup */
    				c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
    
    				ast_channel_unlock(c[x]);
    
    				return c[x];
    			}
    			if (!whentohangup || (diff < whentohangup))
    				whentohangup = diff;
    		}
    
    		ast_channel_unlock(c[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* Wait full interval */
    
    	if (whentohangup) {
    		rms = (whentohangup - now) * 1000;	/* timeout in milliseconds */
    		if (*ms >= 0 && *ms < rms)		/* original *ms still smaller */
    			rms =  *ms;
    
    	/*
    	 * Build the pollfd array, putting the channels' fds first,
    	 * followed by individual fds. Order is important because
    	 * individual fd's must have priority over channel fds.
    	 */
    
    	for (x=0; x<n; x++) {
    		for (y=0; y<AST_MAX_FDS; y++) {
    			fdmap[max].fdno = y;  /* fd y is linked to this pfds */
    			fdmap[max].chan = x;  /* channel x is linked to this pfds */
    			max += ast_add_fd(&pfds[max], c[x]->fds[y]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CHECK_BLOCKING(c[x]);
    	}
    
    	/* Add the individual fds */
    	for (x=0; x<nfds; x++) {
    		fdmap[max].chan = -1;
    		max += ast_add_fd(&pfds[max], fds[x]);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (sizeof(int) == 4) {	/* XXX fix timeout > 600000 on linux x86-32 */
    
    		do {
    			int kbrms = rms;
    			if (kbrms > 600000)
    				kbrms = 600000;
    			res = poll(pfds, max, kbrms);
    			if (!res)
    				rms -= kbrms;
    		} while (!res && (rms > 0));
    	} else {
    		res = poll(pfds, max, rms);
    	}
    
    	for (x=0; x<n; x++)
    		ast_clear_flag(c[x], AST_FLAG_BLOCKING);
    	if (res < 0) { /* Simulate a timeout if we were interrupted */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	if (whentohangup) {   /* if we have a timeout, check who expired */
    
    		for (x=0; x<n; x++) {
    			if (c[x]->whentohangup && now >= c[x]->whentohangup) {
    				c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
    				if (winner == NULL)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					winner = c[x];
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (res == 0) { /* no fd ready, reset timeout and done */
    		*ms = 0;	/* XXX use 0 since we may not have an exact timeout. */
    		return winner;
    	}
    	/*
    	 * Then check if any channel or fd has a pending event.
    	 * Remember to check channels first and fds last, as they
    	 * must have priority on setting 'winner'
    	 */
    	for (x = 0; x < max; x++) {
    		res = pfds[x].revents;
    		if (res == 0)
    			continue;
    		if (fdmap[x].chan >= 0) {	/* this is a channel */
    			winner = c[fdmap[x].chan];	/* override previous winners */
    			if (res & POLLPRI)
    				ast_set_flag(winner, AST_FLAG_EXCEPTION);
    			else
    				ast_clear_flag(winner, AST_FLAG_EXCEPTION);
    			winner->fdno = fdmap[x].fdno;
    		} else {			/* this is an fd */
    			if (outfd)
    				*outfd = pfds[x].fd;
    			if (exception)
    				*exception = (res & POLLPRI) ? -1 : 0;
    			winner = NULL;
    		}
    
    		*ms -= ast_tvdiff_ms(ast_tvnow(), start);
    		if (*ms < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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)
    {
    
    	int oldms = ms;	/* -1 if no timeout */
    
    	ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
    	if ((ms < 0) && (oldms < 0))
    		ms = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ms;
    
    /* XXX never to be called with ms = -1 */
    
    int ast_waitfordigit(struct ast_channel *c, int ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	return ast_waitfordigit_full(c, ms, -1, -1);
    
    int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
    
    	if (c->timingfd > -1) {
    
    		if (!func) {
    			samples = 0;
    			data = 0;
    		}
    		ast_log(LOG_DEBUG, "Scheduling timer at %d sample intervals\n", samples);
    		res = ioctl(c->timingfd, ZT_TIMERCONFIG, &samples);
    		c->timingfunc = func;
    		c->timingdata = data;
    
    int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
    
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
    
    		return -1;
    	/* Wait for a digit, no more than ms milliseconds total. */
    
    	while (ms) {
    		struct ast_channel *rchan;
    		int outfd;
    
    
    		rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
    
    		if (!rchan && outfd < 0 && ms) {
    
    			ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
    			return -1;
    		} else if (outfd > -1) {
    			/* The FD we were watching has something waiting */
    			return 1;
    
    		} else if (rchan) {
    
    			int res;
    			struct ast_frame *f = ast_read(c);
    			if (!f)
    
    				return -1;
    
    			switch(f->frametype) {
    			case AST_FRAME_DTMF:
    				res = f->subclass;
    
    				ast_frfree(f);
    
    				return res;
    			case AST_FRAME_CONTROL:
    				switch(f->subclass) {
    				case AST_CONTROL_HANGUP:
    					ast_frfree(f);
    					return -1;
    				case AST_CONTROL_RINGING:
    				case AST_CONTROL_ANSWER:
    					/* Unimportant */
    					break;
    				default:
    					ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
    				}
    			case AST_FRAME_VOICE:
    				/* Write audio if appropriate */
    				if (audiofd > -1)
    					write(audiofd, f->data, f->datalen);
    			}
    			/* Ignore */
    			ast_frfree(f);
    
    	return 0; /* Time is up */
    
    static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	struct ast_frame *f = NULL;	/* the return value */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int blah;
    
    	int prestate;
    
    	/* this function is very long so make sure there is only one return
    	 * point at the end (there is only one exception to this).
    	 */
    
    	ast_channel_lock(chan);
    
    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");
    
    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)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->generator)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	prestate = chan->_state;
    
    	if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) {
    
    		/* We have DTMF that has been deferred.  Return it now */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->dtmff.frametype = AST_FRAME_DTMF;
    		chan->dtmff.subclass = chan->dtmfq[0];
    
    		/* Drop first digit from the buffer */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
    
    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 */
    
    		read(chan->alertpipe[0], &blah, sizeof(blah));
    
    	if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
    
    		ast_clear_flag(chan, AST_FLAG_EXCEPTION);
    
    		/* IF we can't get event, assume it's an expired as-per the old interface */
    		res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
    
    			blah = ZT_EVENT_TIMER_EXPIRED;
    
    		if (blah == ZT_EVENT_TIMER_PING) {
    
    			if (!chan->readq || !chan->readq->next) {
    
    				/* Acknowledge PONG unless we need it again */
    				if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
    					ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
    				}
    			}
    		} else if (blah == ZT_EVENT_TIMER_EXPIRED) {
    			ioctl(chan->timingfd, ZT_TIMERACK, &blah);
    
    			if (chan->timingfunc) {
    				/* save a copy of func/data before unlocking the channel */
    				int (*func)(void *) = chan->timingfunc;
    				void *data = chan->timingdata;
    				ast_channel_unlock(chan);
    
    				func(data);
    			} else {
    				blah = 0;
    				ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
    				chan->timingdata = NULL;
    
    				ast_channel_unlock(chan);
    
    			/* cannot 'goto done' because the channel is already unlocked */
    
    		} else
    			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
    
    	if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
    
    		/* if the AST_GENERATOR_FD is set, call the generator with args
    		 * set to -1 so it can do whatever it needs to.
    		 */
    
    		void *tmp = chan->generatordata;
    		chan->generatordata = NULL;     /* reset to let ast_write get through */
    		chan->generator->generate(chan, tmp, -1, -1);
    		chan->generatordata = tmp;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for pending read queue */
    
    	if (chan->readq) {
    		f = chan->readq;
    		chan->readq = f->next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Interpret hangup and return NULL */
    
    		/* XXX why not the same for frames from the channel ? */
    
    		if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
    
    			ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		chan->blocker = pthread_self();
    
    		if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
    
    			if (chan->tech->exception)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else {
    				ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Clear the exception flag */
    
    			ast_clear_flag(chan, AST_FLAG_EXCEPTION);
    
    		} else if (chan->tech->read)
    			f = chan->tech->read(chan);
    		else
    			ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
    
    	if (f) {
    		/* if the channel driver returned more than one frame, stuff the excess
    		   into the readq for the next ast_read call
    		*/
    		if (f->next) {
    			chan->readq = f->next;
    			f->next = NULL;
    		}
    
    		switch (f->frametype) {
    		case AST_FRAME_CONTROL:
    			if (f->subclass == AST_CONTROL_ANSWER) {
    
    				if (!ast_test_flag(chan, AST_FLAG_OUTGOING)) {
    					ast_log(LOG_DEBUG, "Ignoring answer on an inbound call!\n");
    
    					f = &ast_null_frame;
    				} else if (prestate == AST_STATE_UP) {
    
    				} else {
    					/* Answer the CDR */
    					ast_setstate(chan, AST_STATE_UP);