예제 #1
0
FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
{
	uint8_t i = 0;
	ftdm_time_t currtime = 0;
	ftdm_time_t prevtime = 0;

	ftdm_stream_handle_t stream = { 0 };
	FTDM_STANDARD_STREAM(stream);
	if (!fchan->history[0].file) {
		stream.write_function(&stream, "-- No state history --\n");
		return stream.data;
	}

	stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s", 
			"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");

	for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
		if (!fchan->history[i].file) {
			break;
		}
		write_history_entry(fchan, &stream, i, &prevtime);
	}

	for (i = 0; i < fchan->hindex; i++) {
		write_history_entry(fchan, &stream, i, &prevtime);
	}

	currtime = ftdm_current_time_in_ms();

	stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));

	return stream.data;
}
예제 #2
0
static void tap_pri_put_pcall(pritap_t *pritap, void *callref)
{
    int i;
    int crv;
    int tstcrv;

    if (!callref) {
        ftdm_log(FTDM_LOG_ERROR, "Cannot put pcall for null callref in span %s\n", pritap->span->name);
        return;
    }

    ftdm_mutex_lock(pritap->pcalls_lock);

    crv = tap_pri_get_crv(pritap->pri, callref);
    for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) {
        if (!pritap->pcalls[i].callref) {
            continue;
        }
        tstcrv = tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref);
        if (tstcrv == crv) {
            if (pritap->pcalls[i].inuse) {
                ftdm_log(FTDM_LOG_DEBUG, "releasing slot %d in span %s used by callref %d/%p\n", i,
                         pritap->span->name, crv, pritap->pcalls[i].callref);
                pritap->pcalls[i].inuse = 0;
            }
        }
    }

    ftdm_mutex_unlock(pritap->pcalls_lock);
}
예제 #3
0
static passive_call_t *tap_pri_get_pcall_bycrv(pritap_t *pritap, int crv)
{
    int i;
    int tstcrv;

    ftdm_mutex_lock(pritap->pcalls_lock);

    for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) {
        tstcrv = pritap->pcalls[i].callref ? tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref) : 0;
        if (pritap->pcalls[i].callref && tstcrv == crv) {
            if (pritap->pcalls[i].inuse) {
                ftdm_mutex_unlock(pritap->pcalls_lock);
                return &pritap->pcalls[i];
            }
            /* This just means the crv is being re-used in another call before this one was destroyed */
            ftdm_log(FTDM_LOG_DEBUG, "Found crv %d in slot %d of span %s with call %p but is no longer in use\n",
                     crv, i, pritap->span->name, pritap->pcalls[i].callref);
        }
    }

    ftdm_log(FTDM_LOG_DEBUG, "crv %d was not found active in span %s\n", crv, pritap->span->name);

    ftdm_mutex_unlock(pritap->pcalls_lock);

    return NULL;
}
ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan)
{
	const char* args;
	char *p;
	char *type = NULL;
	char *target = NULL;
	ftdm_status_t status = FTDM_FAIL;
	sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
	unsigned i;

	args = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_arg");
	if (ftdm_strlen_zero(args)) {
		ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because call_transfer_arg variable is not set\n");
		goto done;
	}

	type = ftdm_strdup(args);
	if ((p = strchr(type, '/'))) {
		target = ftdm_strdup(p+1);
		*p = '\0';
	}

	if (ftdm_strlen_zero(type) || ftdm_strlen_zero(target)) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid parameters for transfer %s, expected <type>/<target>\n", args);
		goto done;
	}

	if (sngisdn_info->transfer_data.type != SNGISDN_TRANSFER_NONE) {
		ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because an existing transfer transfer is pending (%s)\n", sngisdn_transfer_type2str(sngisdn_info->transfer_data.type));
		goto done;
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer requested type:%s target:%s\n", type, target);
	for (i = 0; i < ftdm_array_len(transfer_interfaces); i++ ) {
		if (!strcasecmp(transfer_interfaces[i].name, type)) {
			/* Depending on the transfer type, the transfer function may change the
			 * channel state to UP, or last_state, but the transfer function will always result in
			 * an immediate state change if FTDM_SUCCESS is returned */

			status = transfer_interfaces[i].func(ftdmchan, transfer_interfaces[i].type, target);
			goto done;
		}
	}

	ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid transfer type:%s\n", type);

done:
	if (status != FTDM_SUCCESS) {
		ftdm_set_state(ftdmchan, ftdmchan->last_state);
	}

	ftdm_safe_free(type);
	ftdm_safe_free(target);
	return status;
}
예제 #5
0
FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
{
	ftdm_time_t diff;
	ftdm_channel_state_t state;
	ftdm_channel_state_t last_state;
	uint8_t hindex = 0;

	if (!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) {
		ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Cannot cancel state change from %s to %s, it was already processed\n", 
				ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
		return FTDM_FAIL;
	}

	if (fchan->state_status != FTDM_STATE_STATUS_NEW) {
		ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Failed to cancel state change from %s to %s, state is not new anymore\n", 
				ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
		return FTDM_FAIL;
	}

	/* compute the last history index */
	hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
	diff = fchan->history[hindex].end_time - fchan->history[hindex].time;

	/* go back in time and revert the state to the previous state */
	state = fchan->state;
	last_state = fchan->last_state;

	fchan->state = fchan->last_state;
	fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
	fchan->last_state = fchan->history[hindex].last_state;
	fchan->hindex = hindex;

	/* clear the state change flag */
	ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);

	/* ack any pending indications as cancelled */
	ftdm_ack_indication(fchan, fchan->indication, FTDM_ECANCELED);

	/* wake up anyone sleeping waiting for the state change to complete, it won't ever be completed */
	if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
		ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
		ftdm_interrupt_signal(fchan->state_completed_interrupt);
	}

	/* NOTE
	 * we could potentially also take out the channel from the pendingchans queue, but I believe is easier just leave it,
	 * the only side effect will be a call to ftdm_channel_advance_states() for a channel that has nothing to advance */
	ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Cancelled state change from %s to %s in %llums\n", 
			ftdm_channel_state2str(last_state), ftdm_channel_state2str(state), diff);
	
	return FTDM_SUCCESS;
}
예제 #6
0
/*
 * This is a tricky function with some side effects, some explanation needed ...
 *
 * The libpri stack process HDLC frames, then finds Q921 frames and Q931 events, each time
 * it finds a new Q931 event, checks if the crv of that event matches a known call in the internal
 * list found in the PRI control block (for us, one control block per span), if it does not find
 * the call, allocates a new one and then sends the event up to the user (us, ftmod_pritap in this case)
 *
 * The user is then expected to destroy the call when done with it (on hangup), but things get tricky here
 * because in ftmod_pritap we do not destroy the call right away to be sure we only destroy it when no one
 * else needs that pointer, therefore we decide to delay the destruction of the call pointer until later
 * when a new call comes which triggers the garbage collecting code in this function
 *
 * Now, what happens if a new call arrives right away with the same crv than the last call? the pri stack
 * does *not* allocate a new call pointer because is still a known call and we must therefore re-use the
 * same call pointer
 *
 * This function accepts a pointer to a callref, even a NULL one. When callref is NULL we search for an
 * available slot so the caller of this function can use it to store a new callref pointer. In the process
 * we also scan for slots that still have a callref pointer but are no longer in use (inuse=0) and we
 * destroy that callref and clear the slot (memset). The trick is, we only do this if the callref to
 * be garbage collected is NOT the one provided by the parameter callref, of course! otherwise we may
 * be freeing a pointer to a callref for a new call that used an old (recycled) callref!
 */
static passive_call_t *tap_pri_get_pcall(pritap_t *pritap, void *callref)
{
    int i;
    int crv;

    ftdm_mutex_lock(pritap->pcalls_lock);

    for (i = 0; i < ftdm_array_len(pritap->pcalls); i++) {
        /* If this slot has a call reference
         * and it is different than the *callref provided to us
         * and is no longer in use,
         * then it is time to garbage collect it ... */
        if (pritap->pcalls[i].callref  && callref != pritap->pcalls[i].callref && !pritap->pcalls[i].inuse) {
            crv = tap_pri_get_crv(pritap->pri, pritap->pcalls[i].callref);
            /* garbage collection */
            ftdm_log(FTDM_LOG_DEBUG, "Garbage collecting callref %d/%p from span %s in slot %d\n",
                     crv, pritap->pcalls[i].callref, pritap->span->name, i);
            pri_passive_destroycall(pritap->pri, pritap->pcalls[i].callref);
            memset(&pritap->pcalls[i], 0, sizeof(pritap->pcalls[0]));
        }
        if (callref == pritap->pcalls[i].callref) {
            if (callref == NULL) {
                pritap->pcalls[i].inuse = 1;
                ftdm_log(FTDM_LOG_DEBUG, "Enabling callref slot %d in span %s\n", i, pritap->span->name);
            } else if (!pritap->pcalls[i].inuse) {
                crv = tap_pri_get_crv(pritap->pri, callref);
                ftdm_log(FTDM_LOG_DEBUG, "Recyclying callref slot %d in span %s for callref %d/%p\n",
                         i, pritap->span->name, crv, callref);
                memset(&pritap->pcalls[i], 0, sizeof(pritap->pcalls[0]));
                pritap->pcalls[i].callref = callref;
                pritap->pcalls[i].inuse = 1;
            }

            ftdm_mutex_unlock(pritap->pcalls_lock);

            return &pritap->pcalls[i];
        }
    }

    ftdm_mutex_unlock(pritap->pcalls_lock);

    return NULL;
}
예제 #7
0
FT_DECLARE(ftdm_status_t) ftdm_backtrace_walk(void (* callback)(const int tid, const void *addr, const char *symbol, void *priv), void *priv)
{
	void *stacktrace[FTDM_BACKTRACE_MAX];
	char **symbols = NULL;
	size_t size = 0;
	pid_t tid = 0;
	int si = 0;

	if (!callback) {
		return FTDM_EINVAL;
	}

	tid = syscall(SYS_gettid);

	size = backtrace(stacktrace, ftdm_array_len(stacktrace));
	symbols = backtrace_symbols(stacktrace, size);

	for (si = 0; si < size; si++) {
		callback(tid, stacktrace[si], symbols[si], priv);
	}

	free(symbols);
	return FTDM_SUCCESS;
}
예제 #8
0
FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
{
	uint8_t hindex = 0;
	ftdm_time_t diff = 0;
	ftdm_channel_state_t state = fchan->state;

	if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) {
		ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, 
				"State change flag set but state is not completed\n");
		return FTDM_SUCCESS;
	}

	ftdm_usrmsg_free(&fchan->usrmsg);
	
	ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);

	if (state == FTDM_CHANNEL_STATE_PROGRESS) {
		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
	} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);	
		ftdm_test_and_set_media(fchan);
	} else if (state == FTDM_CHANNEL_STATE_UP) {
		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
		ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED);	
		ftdm_test_and_set_media(fchan);
	} else if (state == FTDM_CHANNEL_STATE_DIALING) {
		ftdm_sigmsg_t msg;
		memset(&msg, 0, sizeof(msg));
		msg.channel = fchan;
		msg.event_id = FTDM_SIGEVENT_DIALING;
		ftdm_span_send_signal(fchan->span, &msg);
	} else if (state == FTDM_CHANNEL_STATE_HANGUP) {
		ftdm_set_echocancel_call_end(fchan);
	}

	/* MAINTENANCE WARNING
	 * we're assuming an indication performed 
	 * via state change will involve a single state change */
	ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS);

	hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
	
	ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n");

	fchan->history[hindex].end_time = ftdm_current_time_in_ms();

	fchan->state_status = FTDM_STATE_STATUS_COMPLETED;

	diff = fchan->history[hindex].end_time - fchan->history[hindex].time;

	ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n", 
			ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
	

	if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
		ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
		ftdm_interrupt_signal(fchan->state_completed_interrupt);
	}

	return FTDM_SUCCESS;
}
예제 #9
0
static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
{
	ftdm_status_t status;
	int ok = 1;
	int waitms = DEFAULT_WAIT_TIME;	

	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
				ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
		return FTDM_FAIL;
	}

	if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, 
		"Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n",
		ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state),
		ftdm_state_status2str(ftdmchan->state_status));
		return FTDM_FAIL;
	}

	if (ftdmchan->state == state) {
		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
		return FTDM_FAIL;
	}

	if (!ftdmchan->state_completed_interrupt) {
		status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET);
		if (status != FTDM_SUCCESS) {
			ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT, 
					"Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
			return status;
		}
	}


	if (ftdmchan->span->state_map) {
		ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
		goto end;
	}

	/* basic core state validation (by-passed if the signaling module provides a state_map) */
	switch(ftdmchan->state) {
	case FTDM_CHANNEL_STATE_HANGUP:
	case FTDM_CHANNEL_STATE_TERMINATING:
		{
			ok = 0;
			switch(state) {
			case FTDM_CHANNEL_STATE_DOWN:
			case FTDM_CHANNEL_STATE_BUSY:
			case FTDM_CHANNEL_STATE_RESTART:
				ok = 1;
				break;
			default:
				break;
			}
		}
		break;
	case FTDM_CHANNEL_STATE_UP:
		{
			ok = 1;
			switch(state) {
			case FTDM_CHANNEL_STATE_PROGRESS:
			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			case FTDM_CHANNEL_STATE_RING:
				ok = 0;
				break;
			default:
				break;
			}
		}
		break;
	case FTDM_CHANNEL_STATE_DOWN:
		{
			ok = 0;
			
			switch(state) {
			case FTDM_CHANNEL_STATE_DIALTONE:
			case FTDM_CHANNEL_STATE_COLLECT:
			case FTDM_CHANNEL_STATE_DIALING:
			case FTDM_CHANNEL_STATE_RING:
			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
			case FTDM_CHANNEL_STATE_PROGRESS:				
			case FTDM_CHANNEL_STATE_IDLE:				
			case FTDM_CHANNEL_STATE_GET_CALLERID:
			case FTDM_CHANNEL_STATE_GENRING:
				ok = 1;
				break;
			default:
				break;
			}
		}
		break;
	case FTDM_CHANNEL_STATE_BUSY:
		{
			switch(state) {
			case FTDM_CHANNEL_STATE_UP:
				ok = 0;
				break;
			default:
				break;
			}
		}
		break;
	case FTDM_CHANNEL_STATE_RING:
		{
			switch(state) {
			case FTDM_CHANNEL_STATE_UP:
				ok = 1;
				break;
			default:
				break;
			}
		}
		break;
	default:
		break;
	}

end:

	if (!ok) {
		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
		goto done;
	}

	ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
	ftdmchan->last_state = ftdmchan->state; 
	ftdmchan->state = state;
	ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
	ftdmchan->history[ftdmchan->hindex].file = file;
	ftdmchan->history[ftdmchan->hindex].func = func;
	ftdmchan->history[ftdmchan->hindex].line = line;
	ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
	ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
	ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
	ftdmchan->history[ftdmchan->hindex].end_time = 0;
	ftdmchan->hindex++;
	if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
		ftdmchan->hindex = 0;
	}
	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);

	ftdm_mutex_lock(ftdmchan->span->mutex);
	ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
	if (ftdmchan->span->pendingchans) {
		ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
	}
	ftdm_mutex_unlock(ftdmchan->span->mutex);

	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
		/* the channel should not block waiting for state processing */
		goto done;
	}

	if (!waitrq) {
		/* no waiting was requested */
		goto done;
	}

	/* let's wait for the state change to be completed by the signaling stack */
	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);

	ftdm_mutex_unlock(ftdmchan->mutex);

	status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms);

	ftdm_mutex_lock(ftdmchan->mutex);

	if (status != FTDM_SUCCESS) {
		ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
		ftdm_log_chan_ex(ftdmchan, file, func, line, 
				FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n",
				ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
		ok = 0;
		goto done;
	}
done:
	return ok ? FTDM_SUCCESS : FTDM_FAIL;
}
예제 #10
0
FT_DECLARE(ftdm_status_t) ftdm_interrupt_multiple_wait(ftdm_interrupt_t *interrupts[], ftdm_size_t size, int ms)
{
	int numdevices = 0;
	unsigned i;
#if defined(__WINDOWS__)
	DWORD res = 0;
	HANDLE ints[20];
	if (size > (ftdm_array_len(ints)/2)) {
		/* improve if needed: dynamically allocate the list of interrupts *only* when exceeding the default size */
		ftdm_log(FTDM_LOG_CRIT, "Unsupported size of interrupts: %d, implement me!\n", size);
		return FTDM_FAIL;
	}

	for (i = 0; i < size; i++) {
		ints[i] = interrupts[i]->event;
		if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
			ints[size+numdevices] = interrupts[i]->device;
			numdevices++;
		}
	}

	res = WaitForMultipleObjects((DWORD)size+numdevices, ints, FALSE, ms >= 0 ? ms : INFINITE);

	switch (res) {
	case WAIT_TIMEOUT:
		return FTDM_TIMEOUT;
	case WAIT_FAILED:
	case WAIT_ABANDONED: /* is it right to fail with abandoned? */
		return FTDM_FAIL;
	default:
		if (res >= (size+numdevices)) {
			ftdm_log(FTDM_LOG_ERROR, "Error waiting for freetdm interrupt event (WaitForSingleObject returned %d)\n", res);
			return FTDM_FAIL;
		}
		/* fall-through to FTDM_SUCCESS at the end of the function */
	}
#elif defined(__linux__)
	int res = 0;
	char pipebuf[255];
	struct pollfd ints[size*2];

	memset(&ints, 0, sizeof(ints));

	for (i = 0; i < size; i++) {
		ints[i].events = POLLIN;
		ints[i].revents = 0;
		ints[i].fd = interrupts[i]->readfd;
		if (interrupts[i]->device != FTDM_INVALID_SOCKET) {
			ints[size+numdevices].events = POLLIN;
			ints[size+numdevices].revents = 0;
			ints[size+numdevices].fd = interrupts[i]->device;
			numdevices++;
		}
	}

	res = poll(ints, size + numdevices, ms);

	if (res == -1) {
		ftdm_log(FTDM_LOG_CRIT, "interrupt poll failed (%s)\n", strerror(errno));
		return FTDM_FAIL;
	}

	if (res == 0) {
		return FTDM_TIMEOUT;
	}

	/* check for events in the pipes, NOT in the devices */
	for (i = 0; i < size; i++) {
		if (ints[i].revents & POLLIN) {
			res = read(ints[i].fd, pipebuf, sizeof(pipebuf));
			if (res == -1) {
				ftdm_log(FTDM_LOG_CRIT, "reading interrupt descriptor failed (%s)\n", strerror(errno));
			}
		}
	}
#else
#endif
	return FTDM_SUCCESS;
}