Skip to content
Snippets Groups Projects
channel.c 123 KiB
Newer Older
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);
	/* Destroy the jitterbuffer */
	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)
	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)
{
	ast_cond_wait(&spy->trigger, &spy->lock);
}

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);
		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);
					ast_cdr_answer(chan->cdr);
				}
			}
			break;
		case AST_FRAME_DTMF:
			ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
			if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
				if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
					chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
				else
					ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);