Skip to content
Snippets Groups Projects
channel.c 114 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_softhangup_nolock(chan, cause);
    
    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 ast_channel_spy_queue *queue;
    	struct ast_channel_spy_queue *other_queue;
    	struct channel_spy_trans *trans;
    	struct ast_frame *last;
    
    	trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
    
    	AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
    		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;
    				}
    
    			}
    
    			for (last = queue->head; last && last->next; last = last->next);
    			if (last)
    				last->next = ast_frdup(translated_frame);
    			else
    				queue->head = ast_frdup(translated_frame);
    		} 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;
    			}
    
    			for (last = queue->head; last && last->next; last = last->next);
    			if (last)
    				last->next = ast_frdup(f);
    			else
    				queue->head = ast_frdup(f);
    
    		queue->samples += f->samples;
    
    		if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
    			if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
    				other_queue = (dir == SPY_WRITE) ? &spy->read_queue : &spy->write_queue;
    
    				if (other_queue->samples == 0) {
    					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);
    					ast_mutex_unlock(&spy->lock);
    					continue;
    				}
    			}
    
    			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 */
    
    	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);
    
    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_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free_translation(chan);
    
    	if (chan->stream) 		/* Close audio stream */
    
    		ast_closestream(chan->stream);
    
    	if (chan->vstream)		/* Close video stream */
    
    		ast_closestream(chan->vstream);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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);
    
    	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_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
    
    		ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	switch(chan->_state) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_STATE_RINGING:
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_STATE_RING:
    
    		if (chan->tech->answer)
    			res = chan->tech->answer(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(chan, AST_STATE_UP);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->cdr)
    			ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    	case AST_STATE_UP:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->cdr)
    			ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    void ast_deactivate_generator(struct ast_channel *chan)
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (chan->generator && chan->generator->release)
    
    			chan->generator->release(chan, chan->generatordata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator = NULL;
    
    		chan->fds[AST_GENERATOR_FD] = -1;
    
    		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
    
    		ast_settimeout(chan, 0, NULL, NULL);
    
    	ast_mutex_unlock(&chan->lock);
    }
    
    static int generator_force(void *data)
    {
    	/* Called if generator doesn't have data */
    	void *tmp;
    	int res;
    	int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
    	struct ast_channel *chan = data;
    	tmp = chan->generatordata;
    	chan->generatordata = NULL;
    	generate = chan->generator->generate;
    	res = generate(chan, tmp, 0, 160);
    	chan->generatordata = tmp;
    	if (res) {
    		ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
    		ast_deactivate_generator(chan);
    	}
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
    {
    
    	int res = 0;
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (chan->generator && chan->generator->release)
    			chan->generator->release(chan, chan->generatordata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = NULL;
    	}
    
    	ast_prod(chan);
    
    	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_mutex_unlock(&chan->lock);
    
    	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;
    
    	if (!(pfds = alloca(sizeof(*pfds) * sz)) || !(fdmap = alloca(sizeof(*fdmap) * sz))) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (outfd)
    
    		*outfd = -99999;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (exception)
    		*exception = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Perform any pending masquerades */
    
    	for (x=0; x < n; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c[x]->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Masquerade failed\n");
    				*ms = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    		}
    
    		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_mutex_unlock(&c[x]->lock);
    				return c[x];
    			}
    			if (!whentohangup || (diff < whentohangup))
    				whentohangup = diff;
    		}
    
    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)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int oldms = ms;
    
    	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;
    
    int ast_waitfordigit(struct ast_channel *c, int ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* XXX Should I be merged with waitfordigit_full XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c, AST_FLAG_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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (ms > 0) {
    			/* Read something */
    			f = ast_read(c);
    			if (f) {
    
    				if (f->frametype == AST_FRAME_DTMF)
    
    Mark Spencer's avatar
    Mark Spencer committed
    					result = f->subclass;
    				ast_frfree(f);
    			} else
    				result = -1;
    		}
    	}
    	return result;
    }
    
    
    int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
    
    {
    	int res = -1;
    #ifdef ZAPTEL_OPTIMIZATIONS
    	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)
    
    {
    	struct ast_frame *f;
    	struct ast_channel *rchan;
    	int outfd;
    
    	/* 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. */
    
    		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) {
    			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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int blah;
    
    	int prestate;
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    	int (*func)(void *);
    	void *data;
    
    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");
    			f = NULL;
    		} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return f;
    	}
    
    	/* 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
    		return NULL;
    	}
    
    	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 */
    		memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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->alertpipe[0] > -1) {
    		read(chan->alertpipe[0], &blah, sizeof(blah));
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    
    	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) {
    
    			ast_log(LOG_NOTICE, "Oooh, there's a PING!\n");
    
    			if (!chan->readq || !chan->readq->next) {
    
    				/* Acknowledge PONG unless we need it again */
    #if 0
    				ast_log(LOG_NOTICE, "Sending a PONG!\n");
    #endif				
    				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);
    			func = chan->timingfunc;
    			data = chan->timingdata;
    
    			if (func) {
    #if 0
    				ast_log(LOG_DEBUG, "Calling private function\n");
    #endif			
    				func(data);
    			} else {
    				blah = 0;
    				ast_mutex_lock(&chan->lock);
    				ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
    				chan->timingdata = NULL;
    				ast_mutex_unlock(&chan->lock);
    			}
    
    		} else
    			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
    
    	/* Check for AST_GENERATOR_FD if not null.  If so, call generator with -1
    	   arguments now so it can do whatever it needs to. */
    	if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
    		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 */
    
    		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 (prestate == AST_STATE_UP) {
    					ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
    
    				}
    				/* 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);
    
    			}
    			break;
    		case AST_FRAME_DTMF_BEGIN:
    			ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
    			break;
    		case AST_FRAME_DTMF_END:
    			ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
    			break;
    		case AST_FRAME_VOICE:
    			if (dropaudio) {
    				ast_frfree(f);
    
    			} else if (!(f->subclass & chan->nativeformats)) {
    				/* This frame can't be from the current native formats -- drop it on the
    				   floor */
    				ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
    					chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
    				ast_frfree(f);
    
    			} else {
    				if (chan->spies)
    					queue_frame_to_spies(chan, f, SPY_READ);
    				
    				if (chan->monitor && chan->monitor->read_stream ) {
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    
    					int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
    					if (jump >= 0) {
    						if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    						chan->insmpl += jump + 4 * f->samples;
    					} else
    						chan->insmpl+= f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #else
    
    					int jump = chan->outsmpl - chan->insmpl;
    					if (jump - MONITOR_DELAY >= 0) {
    						if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
    						chan->insmpl += jump;
    					} else
    						chan->insmpl += f->samples;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #endif
    
    					if (chan->monitor->state == AST_MONITOR_RUNNING) {
    						if (ast_writestream(chan->monitor->read_stream, f) < 0)
    							ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
    					}
    				}
    
    				if (chan->readtrans) {
    					if (!(f = ast_translate(chan->readtrans, f, 1)))
    
    				/* Run generator sitting on the line if timing device not available
    				* and synchronous generation of outgoing frames is necessary       */
    				if (chan->generatordata &&  !ast_internal_timing_enabled(chan)) {
    
    					void *tmp;
    					int res;
    					int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
    
    					tmp = chan->generatordata;
    					chan->generatordata = NULL;
    					generate = chan->generator->generate;
    					res = generate(chan, tmp, f->datalen, f->samples);
    					chan->generatordata = tmp;
    					if (res) {
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
    
    				} else if (f->frametype == AST_FRAME_CNG) {
    					if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
    
    						if (option_debug > 1)
    							ast_log(LOG_DEBUG, "Generator got CNG, switching to timed mode\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    	} else {
    		/* Make sure we always return NULL in the future */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    		if (chan->generator)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* End the CDR if appropriate */
    		if (chan->cdr)
    			ast_cdr_end(chan->cdr);