NetBSD-5.0.2/usr.sbin/isdn/isdnd/support.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *---------------------------------------------------------------------------
 *
 *	i4b daemon - misc support routines
 *	----------------------------------
 *
 *	$Id: support.c,v 1.14 2005/06/02 05:54:44 lukem Exp $ 
 *
 * $FreeBSD$
 *
 *      last edit-date: [Wed Oct  4 18:24:27 2000]
 *
 *---------------------------------------------------------------------------*/

#include "isdnd.h"

static int isvalidtime(struct cfg_entry *cep);

static SLIST_HEAD(, isdn_ctrl_state) isdn_ctrl_list =
    SLIST_HEAD_INITIALIZER(isdn_ctrl_list);

static SIMPLEQ_HEAD(, cfg_entry) cfg_entry_list =
    SIMPLEQ_HEAD_INITIALIZER(cfg_entry_list);
	
/*---------------------------------------------------------------------------*
 *	find an active entry by driver type and driver unit
 *---------------------------------------------------------------------------*/
struct cfg_entry *
find_active_entry_by_driver(int drivertype, int driverunit)
{
	struct cfg_entry *cep = NULL;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		if (!((cep->usrdevice == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if (isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d, time not valid!", cep->index)));
			continue;
		}
		
		/* found */
		
		if (cep->cdid == CDID_UNUSED)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_UNUSED !",
				cep->index, cep->usrdevicename, driverunit)));
			return(NULL);
		}
		else if (cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_RESERVED!",
				cep->index, cep->usrdevicename, driverunit)));
			return(NULL);
		}
		return(cep);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
struct cfg_entry *
find_by_device_for_dialout(int drivertype, int driverunit)
{
	struct cfg_entry *cep;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		/* compare driver type and unit */

		if (!((cep->usrdevice == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if (isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, time not valid!", cep->index)));
			continue;
		}
		
		/* found, check if already reserved */
		
		if (cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, cdid reserved!", cep->index)));
			return(NULL);
		}

		/* check if this entry is already in use ? */
		
		if (cep->cdid != CDID_UNUSED)	
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, cdid in use", cep->index)));
			return(NULL);
		}

		if ((setup_dialout(cep)) == GOOD)
		{
			/* found an entry to be used for calling out */
		
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: found entry %d!", cep->index)));
			return(cep);
		}
		else
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, setup_dialout() failed!", cep->index)));
			return(NULL);
		}
	}

	DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: no entry found!")));
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
struct cfg_entry *
find_by_device_for_dialoutnumber(int drivertype, int driverunit, int cmdlen, char *cmd)
{
	struct cfg_entry *cep;
	int j;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		/* compare driver type and unit */

		if (!((cep->usrdevice == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if (isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, time not valid!", cep->index)));
			continue;
		}

		/* found, check if already reserved */
		
		if (cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid reserved!", cep->index)));
			return(NULL);
		}

		/* check if this entry is already in use ? */
		
		if (cep->cdid != CDID_UNUSED)	
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid in use", cep->index)));
			return(NULL);
		}

		/* check number and copy to cep->remote_numbers[] */
		
		for (j = 0; j < cmdlen; j++)
		{
			if (!(isdigit((unsigned char)*(cmd+j))))
			{
				DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, dial string contains non-digit at pos %d", cep->index, j)));
				return(NULL);
			}
			/* fill in number to dial */
			cep->remote_numbers[0].number[j] = *(cmd+j);
		}				
		cep->remote_numbers[0].number[j] = '\0';
		cep->remote_numbers_count = 1;

		if ((setup_dialout(cep)) == GOOD)
		{
			/* found an entry to be used for calling out */
		
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: found entry %d!", cep->index)));
			return(cep);
		}
		else
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, setup_dialout() failed!", cep->index)));
			return(NULL);
		}
	}

	DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: no entry found!")));
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
int
setup_dialout(struct cfg_entry *cep)
{
	struct isdn_ctrl_state *ctrl;
	int i;

	if (cep->isdncontroller < 0) {
		/* we are free to choose a controller */
		for (ctrl = get_first_ctrl_state(); ctrl; ctrl = NEXT_CTRL(ctrl)) {
			if (get_controller_state(ctrl) != CTRL_UP)
				continue;
			switch (cep->isdnchannel) {
			case CHAN_ANY:
				for (i = 0; i < ctrl->nbch; i++)
				{
					if (ret_channel_state(ctrl, i)
					    == CHAN_IDLE)
						break;
				}
				if (i == ctrl->nbch)
					continue;
				break;
			default:
				if (ret_channel_state(ctrl, cep->isdnchannel)
				    != CHAN_IDLE)
					continue;
				break;
			}
			/* this controller looks ok */
			break;
		}
	} else {
		/* fixed controller in config, use that */
		ctrl = find_ctrl_state(cep->isdncontroller);
	}

	if (ctrl == NULL)
		return (ERROR);

	/* check controller operational */

	if (get_controller_state(ctrl) != CTRL_UP)
	{
		DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, controller is down", cep->name)));
		return(ERROR);
	}

	cep->isdncontrollerused = ctrl->isdnif;

	/* check channel available */

	switch (cep->isdnchannel)
	{
	case CHAN_ANY:
		for (i = 0; i < ctrl->nbch; i++)
		{
			if (ret_channel_state(ctrl, i) == CHAN_IDLE)
				break;
		}
		if (i == ctrl->nbch)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
			return(ERROR);
		}
		cep->isdnchannelused = CHAN_ANY;
		break;

	default:
		if (ret_channel_state(ctrl, cep->isdnchannel) != CHAN_IDLE)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
			return(ERROR);
		}
		cep->isdnchannelused = cep->isdnchannel;
		break;
	}

	DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s ok!", cep->name)));

	/* preset disconnect cause */
	
	SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
	SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_NORMAL);
	
	return(GOOD);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit
 *---------------------------------------------------------------------------*/
struct cfg_entry *
get_cep_by_driver(int drivertype, int driverunit)
{
	struct cfg_entry *cep;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		if (!((cep->usrdevice == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if (isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "get_cep_by_driver: entry %d, time not valid!", cep->index)));
			continue;
		}		

		DBGL(DL_MSG, (logit(LL_DBG, "get_cep_by_driver: found entry %d!", cep->index)));
		return(cep);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find a matching entry for an incoming call
 *
 *	- not found/no match: log output with LL_CHD and return NULL
 *	- found/match: make entry in free cep, return address
 *---------------------------------------------------------------------------*/
struct cfg_entry *
find_matching_entry_incoming(msg_connect_ind_t *mp, int len)
{
	struct cfg_entry *cep = NULL;
	static const char resvd_type[] = "reserverd";
	static const char no_type[] = "no type";
	static const char * const numbering_types[] = {
		"unknown",
		"international",
		"national",
		"network specific",
		"subscriber",
		"abbreviated",
		resvd_type,
		resvd_type,
		resvd_type
	};
	const char * ntype;
	int i;

	/* older kernels do not deliver all the information */	
	if (((u_int8_t*)&mp->type_plan - (u_int8_t*)mp + sizeof(mp->type_plan)) <= len) {
		ntype = numbering_types[(mp->type_plan & 0x70)>>4];
	} else {
		ntype = no_type;
	}

	/* check for CW (call waiting) early */

	if (mp->channel == CHAN_NO)
	{
		if (aliasing)
	        {
			char *src_tela = "ERROR-src_tela";
			char *dst_tela = "ERROR-dst_tela";
	
	                src_tela = get_alias(mp->src_telno);
	                dst_tela = get_alias(mp->dst_telno);
	
			logit(LL_CHD, "%05d <unknown> CW from %s (%s) to %s (%s) (no channel free)",
				mp->header.cdid, src_tela, ntype, dst_tela, mp->display);
		}
		else
		{
			logit(LL_CHD, "%05d <unknown> call waiting from %s (%s) to %s (%s) (no channel free)",
				mp->header.cdid, mp->src_telno, ntype, mp->dst_telno, mp->display);
		}
		return(NULL);
	}
	
	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		int n;
		struct isdn_ctrl_state *ctrl;

		/* check my number */

		if (strncmp(cep->local_phone_incoming, mp->dst_telno, strlen(cep->local_phone_incoming)))
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s",
				cep->index, cep->local_phone_incoming, mp->dst_telno)));
			continue;
		}

		/* check all allowed remote number's for this entry */

		for (n = 0; n < cep->incoming_numbers_count; n++)
		{
			incoming_number_t *in = &cep->remote_phone_incoming[n];
			if (in->number[0] == '*')
				break;
			if (strncmp(in->number, mp->src_telno, strlen(in->number)))
			{
				DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s",
					cep->index, in->number, mp->src_telno)));
			}
			else
				break;
		}
		if (n >= cep->incoming_numbers_count)
			continue;
				
		/* check b protocol */

		if (cep->b1protocol != mp->bprot)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, bprot %d != incomingprot %d",
				cep->index, cep->b1protocol, mp->bprot)));
			continue;
		}

		/* is this entry currently in use ? */

		if (cep->cdid != CDID_UNUSED)
		{
			if (cep->cdid == CDID_RESERVED)
			{
				DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, cdid is reserved", cep->index)));
			}
			else if (cep->dialin_reaction == REACT_ACCEPT
				 && cep->dialouttype == DIALOUT_CALLEDBACK)
			{
				/*
				 * We might consider doing this even if this is
				 * not a calledback config entry - BUT: there are
				 * severe race conditions and timinig problems
				 * ex. if both sides run I4B with no callback
				 * delay - both may shutdown the outgoing call
				 * and never be able to establish a connection.
				 * In the called-back case this should not happen.
				 */
				DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, incoming call for callback in progress (cdid %05d)", cep->index, cep->cdid)));

				/* save the current call state, we're going to overwrite it with the
				 * new incoming state below... */
				cep->saved_call.cdid = cep->cdid;
				cep->saved_call.controller = cep->isdncontrollerused;
				cep->saved_call.channel = cep->isdnchannelused;
			}
			else
			{
				DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, cdid in use", cep->index)));
				continue;	/* yes, next */
			}
		}

		/* check controller value ok */
		ctrl = find_ctrl_state(mp->controller);

		if (ctrl == NULL)
		{
			logit(LL_CHD, "%05d %s incoming call with invalid controller %d",
                        	mp->header.cdid, cep->name, mp->controller);
			return(NULL);
		}

		/* check controller marked up */

		if (get_controller_state(ctrl) != CTRL_UP)
		{
			logit(LL_CHD, "%05d %s incoming call, controller %d DOWN!",
                        	mp->header.cdid, cep->name, mp->controller);
			return(NULL);
		}

		/* check channel he wants */

		switch (mp->channel)
		{
		case CHAN_ANY:
			for (i = 0; i < ctrl->nbch; i++)
				if (ret_channel_state(ctrl, i) == CHAN_IDLE)
					break;
			if (i == ctrl->nbch)
			{
				logit(LL_CHD, "%05d %s incoming call, no channel free!",
					mp->header.cdid, cep->name);
				return(NULL);
			}
			break;

		case CHAN_NO:
			logit(LL_CHD, "%05d %s incoming call, call waiting (no channel available)!",
				mp->header.cdid, cep->name);
			return(NULL);
			break;

		default:
			if ((ret_channel_state(ctrl, mp->channel)) != CHAN_IDLE)
			{
				logit(LL_CHD, "%05d %s incoming call, channel B%d not free!",
					mp->header.cdid, cep->name, mp->channel);
				return(NULL);
			}
			break;
		}

		/* check time interval */
		
		if (isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, time not valid!", cep->index)));
			continue;
		}
		
		/* found a matching entry */

		cep->cdid = mp->header.cdid;
		cep->isdncontrollerused = mp->controller;
		cep->isdnchannelused = mp->channel;
/*XXX*/		cep->disc_cause = 0;
		
		/* cp number to real one used */
		
		strlcpy(cep->real_phone_incoming, mp->src_telno,
		    sizeof(cep->real_phone_incoming));

		/* copy display string */
		
		strlcpy(cep->display, mp->display, sizeof(cep->display));
		
		/* entry currently down ? */
		
		if (cep->state == ST_DOWN)
		{
			msg_updown_ind_t mui;
			
			/* set interface up */
	
			DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, ", cep->index)));
	
			mui.driver = cep->usrdevice;
			mui.driver_unit = cep->usrdeviceunit;
			mui.updown = SOFT_ENA;
			
			if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
			{
				logit(LL_ERR, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
				error_exit(1, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
			}

			cep->down_retry_count = 0;
			cep->state = ST_IDLE;
		}
		return(cep);
	}

	if (aliasing)
        {
		char *src_tela = "ERROR-src_tela";
		char *dst_tela = "ERROR-dst_tela";

                src_tela = get_alias(mp->src_telno);
                dst_tela = get_alias(mp->dst_telno);

		logit(LL_CHD, "%05d Call from %s (%s) to %s (%s)",
			mp->header.cdid, src_tela, ntype, dst_tela, mp->display);
	}
	else
	{
		logit(LL_CHD, "%05d <unknown> incoming call from %s (%s) to %s (%s)",
			mp->header.cdid, mp->src_telno, ntype, mp->dst_telno, mp->display);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	return address of ACTIVE config entry by controller and channel
 *---------------------------------------------------------------------------*/
struct cfg_entry *
get_cep_by_cc(int ctrlr, int chan)
{
	struct cfg_entry *cep;
	struct isdn_ctrl_state *cts;

	cts = find_ctrl_state(ctrlr);
	if (cts ==  NULL)
		return(NULL);

	if (chan < 0 || chan >= cts->nbch)
		return(NULL);
		
	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {

		if ((cep->cdid != CDID_UNUSED)		&&
		   (cep->cdid != CDID_RESERVED)		&&
		   (cep->isdnchannelused == chan)	&&
		   (cep->isdncontrollerused == ctrlr)	&&
		   ((ret_channel_state(cts, chan)) == CHAN_RUN))
		{
			return (cep);
		}
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	return address of config entry identified by cdid
 *---------------------------------------------------------------------------*/
struct cfg_entry *
get_cep_by_cdid(int cdid)
{
	struct cfg_entry *cep;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {
		if (cep->cdid == cdid || cep->saved_call.cdid == cdid)
			return(cep);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	process AOCD charging messages
 *---------------------------------------------------------------------------*/
void
handle_charge(struct cfg_entry *cep)
{
	time_t now = time(NULL);

	if (cep->aoc_last == 0)		/* no last timestamp yet ? */
	{
		cep->aoc_last = now;	/* add time stamp */
	}
	else if (cep->aoc_now == 0)	/* no current timestamp yet ? */
	{
		cep->aoc_now = now;	/* current timestamp */
	}
	else
	{
		cep->aoc_last = cep->aoc_now;
		cep->aoc_now = now;
		cep->aoc_diff = cep->aoc_now - cep->aoc_last;
		cep->aoc_valid = AOC_VALID;
	}
	
#ifdef USE_CURSES
	if (do_fullscreen)
		display_charge(cep);
#endif

#ifdef I4B_EXTERNAL_MONITOR
	if (do_monitor && accepted)
		monitor_evnt_charge(cep, cep->charge, 0);
#endif

	if (cep->aoc_valid == AOC_VALID)
	{
		if (cep->aoc_diff != cep->unitlength)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "handle_charge: AOCD unit length updated %d -> %d secs", cep->unitlength, cep->aoc_diff)));

			cep->unitlength = cep->aoc_diff;

			unitlen_chkupd(cep);
		}
		else
		{
#ifdef NOTDEF
			DBGL(DL_MSG, (logit(LL_DBG, "handle_charge: AOCD unit length still %d secs", cep->unitlength)));
#endif
		}
	}
}

/*---------------------------------------------------------------------------*
 *	update kernel idle_time, earlyhup_time and unitlen_time
 *---------------------------------------------------------------------------*/
void
unitlen_chkupd(struct cfg_entry *cep)
{
	msg_timeout_upd_t tupd;

	tupd.cdid = cep->cdid;

	/* init the short hold data based on the shorthold algorithm type */
	
	switch (cep->shorthold_algorithm)
	{
	case SHA_FIXU:
		tupd.shorthold_data.shorthold_algorithm = SHA_FIXU;
		tupd.shorthold_data.unitlen_time = cep->unitlength;
		tupd.shorthold_data.idle_time = cep->idle_time_out;
		tupd.shorthold_data.earlyhup_time = cep->earlyhangup;
		break;

	case SHA_VARU:
		tupd.shorthold_data.shorthold_algorithm = SHA_VARU;
		tupd.shorthold_data.unitlen_time = cep->unitlength;
		tupd.shorthold_data.idle_time = cep->idle_time_out;
		tupd.shorthold_data.earlyhup_time = 0;
		break;
	default:
		logit(LL_ERR, "unitlen_chkupd bad shorthold_algorithm %d", cep->shorthold_algorithm );
		return;
		break;			
	}

	if ((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0)
	{
		logit(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
		error_exit(1, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
	}
}

/*--------------------------------------------------------------------------*
 *	this is intended to be called by do_exit and closes down all
 *	active connections before the daemon exits or is reconfigured.
 *--------------------------------------------------------------------------*/
void
close_allactive(void)
{
	int i, j;
	struct cfg_entry *cep = NULL;
	struct isdn_ctrl_state *cst;

	j = 0;
	
	SLIST_FOREACH(cst, &isdn_ctrl_list, ctrlq) {

		if ((get_controller_state(cst)) != CTRL_UP)
			continue;

		for (i = 0; i < cst->nbch; i++)
		{
			if ((ret_channel_state(cst, i)) == CHAN_RUN)
			{
				if ((cep = get_cep_by_cc(cst->isdnif, i))
				    != NULL)
				{
#ifdef USE_CURSES
					if (do_fullscreen)
						display_disconnect(cep);
#endif
#ifdef I4B_EXTERNAL_MONITOR
					monitor_evnt_disconnect(cep);
#endif
					next_state(cep, EV_DRQ);
					j++;
				}
			}
		}
	}

	if (j)
	{
		logit(LL_DMN, "close_allactive: waiting for all connections terminated");
		sleep(5);
	}

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) {
		if (cep->autoupdown & AUTOUPDOWN_DONE) {
			struct ifreq ifr;
			int r, s;

			s = socket(AF_INET, SOCK_DGRAM, 0);
			memset(&ifr, 0, sizeof ifr);
			snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "%s%d", cep->usrdevicename, cep->usrdeviceunit);
			r = ioctl(s, SIOCGIFFLAGS, &ifr);
			if (r >= 0) {
				ifr.ifr_flags &= ~IFF_UP;
				ioctl(s, SIOCSIFFLAGS, &ifr);
			}
			close(s);
			cep->autoupdown &= ~AUTOUPDOWN_DONE;
		}
	}

}

/*--------------------------------------------------------------------------*
 *	set an interface up
 *--------------------------------------------------------------------------*/
void
if_up(struct cfg_entry *cep)
{
	msg_updown_ind_t mui;
			
	/* set interface up */
	
	DBGL(DL_MSG, (logit(LL_DBG, "if_up: taking %s%d up", cep->usrdevicename, cep->usrdeviceunit)));
	
	mui.driver = cep->usrdevice;
	mui.driver_unit = cep->usrdeviceunit;
	mui.updown = SOFT_ENA;
			
	if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
	{
		logit(LL_ERR, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
		error_exit(1, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
	}
	cep->down_retry_count = 0;

#ifdef USE_CURSES
	if (do_fullscreen)
		display_updown(cep, 1);
#endif
#ifdef I4B_EXTERNAL_MONITOR
	monitor_evnt_updown(cep, 1);
#endif
	
}

/*--------------------------------------------------------------------------*
 *	set an interface down
 *--------------------------------------------------------------------------*/
void
if_down(struct cfg_entry *cep)
{
	msg_updown_ind_t mui;
			
	/* set interface up */
	
	DBGL(DL_MSG, (logit(LL_DBG, "if_down: taking %s%d down", cep->usrdevicename, cep->usrdeviceunit)));
	
	mui.driver = cep->usrdevice;
	mui.driver_unit = cep->usrdeviceunit;
	mui.updown = SOFT_DIS;
			
	if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
	{
		logit(LL_ERR, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
		error_exit(1, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
	}
	cep->went_down_time = time(NULL);
	cep->down_retry_count = 0;

#ifdef USE_CURSES
	if (do_fullscreen)
		display_updown(cep, 0);
#endif
#ifdef I4B_EXTERNAL_MONITOR
	monitor_evnt_updown(cep, 0);
#endif

}

/*--------------------------------------------------------------------------*
 *	send a dial response to (an interface in) the kernel 
 *--------------------------------------------------------------------------*/
void
dialresponse(struct cfg_entry *cep, int dstat)
{
	msg_dialout_resp_t mdr;

	static char *stattab[] = {
		"normal condition",
		"temporary failure",
		"permanent failure",
		"dialout not allowed"
	};

	if (dstat < DSTAT_NONE || dstat > DSTAT_INONLY)
	{
		logit(LL_ERR, "dialresponse: dstat out of range %d!", dstat);
		return;
	}
	
	mdr.driver = cep->usrdevice;
	mdr.driver_unit = cep->usrdeviceunit;
	mdr.stat = dstat;
	mdr.cause = cep->disc_cause;	
	
	if ((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0)
	{
		logit(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
		error_exit(1, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
	}

	DBGL(DL_DRVR, (logit(LL_DBG, "dialresponse: sent [%s]", stattab[dstat])));
}

/*--------------------------------------------------------------------------*
 *	screening/presentation indicator
 *--------------------------------------------------------------------------*/
void
handle_scrprs(int cdid, int scr, int prs, char *caller)
{
	/* screening indicator */
	
	if (scr < SCR_NONE || scr > SCR_NET)
	{
		logit(LL_ERR, "msg_connect_ind: invalid screening indicator value %d!", scr);
	}
	else
	{
		static char *scrtab[] = {
			"no screening indicator",
			"sreening user provided, not screened",
			"screening user provided, verified & passed",
			"screening user provided, verified & failed",
			"screening network provided", };

		if (extcallattr)
		{
			logit(LL_CHD, "%05d %s %s", cdid, caller, scrtab[scr]);
		}
		else
		{
			DBGL(DL_MSG, (logit(LL_DBG, "%s - %s", caller, scrtab[scr])));
		}
	}
			
	/* presentation indicator */
	
	if (prs < PRS_NONE || prs > PRS_RESERVED)
	{
		logit(LL_ERR, "msg_connect_ind: invalid presentation indicator value %d!", prs);
	}
	else
	{
		static char *prstab[] = {
			"no presentation indicator",
			"presentation allowed",
			"presentation restricted",
			"number not available due to interworking",
			"reserved presentation value" };
			
		if (extcallattr)
		{
			logit(LL_CHD, "%05d %s %s", cdid, caller, prstab[prs]);
		}
		else
		{
			DBGL(DL_MSG, (logit(LL_DBG, "%s - %s", caller, prstab[prs])));
		}
	}
}

/*--------------------------------------------------------------------------*
 *	check if the time is valid for an entry
 *--------------------------------------------------------------------------*/
static int 
isvalidtime(struct cfg_entry *cep)
{
	time_t t;
	struct tm *tp;

	if (cep->day == 0)
		return(1);

	t = time(NULL);
	tp = localtime(&t);

	if (cep->day & HD)
	{
		if (isholiday(tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900))
		{
			DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: holiday %d.%d.%d", tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900)));
			goto dayok;
		}
	}
	
	if (cep->day & (1 << tp->tm_wday))
	{
		DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: day match")));	
		goto dayok;
	}

	return(0);
	
dayok:
	if (cep->fromhr==0 && cep->frommin==0 && cep->tohr==0 && cep->tomin==0)
	{
		DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: no time specified, match!")));
		return(1);
	}

	if (cep->tohr < cep->fromhr)
	{
		/* before 00:00 */
		
		if ( (tp->tm_hour > cep->fromhr) ||
		    (tp->tm_hour == cep->fromhr && tp->tm_min > cep->frommin) )
		{
			DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: t<f-1, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}

		/* after 00:00 */
		
		if ( (tp->tm_hour < cep->tohr) ||
		    (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
		{
			DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: t<f-2, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}
	}
	else if (cep->fromhr == cep->tohr)
	{
		if (tp->tm_min >= cep->frommin && tp->tm_min < cep->tomin)
		{
			DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: f=t, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}
	}
	else
	{
		if ((tp->tm_hour > cep->fromhr && tp->tm_hour < cep->tohr) ||
		   (tp->tm_hour == cep->fromhr && tp->tm_min >= cep->frommin) ||
		   (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
		{
			DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: t>f, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			return(1);
		}
	}
	DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, no match!",
			cep->fromhr, cep->frommin,
			cep->tohr, cep->tomin,
			tp->tm_hour, tp->tm_min)));

	return(0);	
}

struct cfg_entry *
get_first_cfg_entry()
{
	return (SIMPLEQ_FIRST(&cfg_entry_list));
}

int count_cfg_entries()
{
	int cnt;
	struct cfg_entry *cfe;

	cnt = 0;
	SIMPLEQ_FOREACH(cfe, &cfg_entry_list, cfgq)
		cnt++;

	return (cnt);
}

struct cfg_entry *
find_cfg_entry(int index)
{
	struct cfg_entry *cep;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq)
		if (cep->index == index)
			return cep;
	return NULL;
}

int
add_cfg_entry(struct cfg_entry *cfe)
{
	struct cfg_entry *cep;
	int max = -1;

	SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq)
		if (cep->index > max)
			max = cep->index;

	cfe->index = max;
	SIMPLEQ_INSERT_TAIL(&cfg_entry_list, cfe, cfgq);
	return max;
}

void
remove_all_cfg_entries()
{
	struct cfg_entry *cep;

	while (!SIMPLEQ_EMPTY(&cfg_entry_list)) {
		cep = SIMPLEQ_FIRST(&cfg_entry_list);
		SIMPLEQ_REMOVE_HEAD(&cfg_entry_list, cfgq);

		if (cep->ppp_expect_name)
		    free(cep->ppp_expect_name);
		if (cep->ppp_expect_password)
		    free(cep->ppp_expect_password);
		if (cep->ppp_send_name)
		    free(cep->ppp_send_name);
		if (cep->ppp_send_password)
		    free(cep->ppp_send_password);
		
		free(cep);
	}
}

struct isdn_ctrl_state * get_first_ctrl_state()
{
	return SLIST_FIRST(&isdn_ctrl_list);
}

int count_ctrl_states()
{
	int cnt = 0;
	struct isdn_ctrl_state *ctrl;

	SLIST_FOREACH(ctrl, &isdn_ctrl_list, ctrlq)
		cnt++;

	return (cnt);
}

void remove_all_ctrl_state()
{
	struct isdn_ctrl_state *ctrl;

	while (!SLIST_EMPTY(&isdn_ctrl_list)) {
		ctrl = SLIST_FIRST(&isdn_ctrl_list);
		SLIST_REMOVE_HEAD(&isdn_ctrl_list, ctrlq);
		free(ctrl);
	}
}

struct isdn_ctrl_state *
find_ctrl_state(int controller)
{
	struct isdn_ctrl_state *ctrl;

	SLIST_FOREACH(ctrl, &isdn_ctrl_list, ctrlq)
		if (ctrl->isdnif == controller)
			return ctrl;
	return NULL;
}

int
add_ctrl_state(struct isdn_ctrl_state *cstate)
{
	SLIST_INSERT_HEAD(&isdn_ctrl_list, cstate, ctrlq);
	return 0;
}

int
remove_ctrl_state(int controller)
{
	struct isdn_ctrl_state *ctrl = find_ctrl_state(controller);
	struct cfg_entry *cep;
	int i;

	if (ctrl == NULL)
		return 0;

	if ((get_controller_state(ctrl)) == CTRL_UP) {

		for (i = 0; i < ctrl->nbch; i++)
		{
			if ((ret_channel_state(ctrl, i)) == CHAN_RUN) {
				if ((cep = get_cep_by_cc(controller, i))
				    != NULL)
				{
#ifdef USE_CURSES
					if (do_fullscreen)
						display_disconnect(cep);
#endif
#ifdef I4B_EXTERNAL_MONITOR
					monitor_evnt_disconnect(cep);
#endif
					cep->cdid = -1;
					cep->isdncontrollerused = -1;
					cep->isdnchannelused = -1;
					cep->state = ST_IDLE;
				}
			}
		}
	}

	SLIST_REMOVE(&isdn_ctrl_list, ctrl, isdn_ctrl_state, ctrlq);
	return 0;
}

/* EOF */