448 lines
12 KiB
C
448 lines
12 KiB
C
/*
|
|
* Copyright (c) 1997, 2002 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 - timer/timing support routines
|
|
* ------------------------------------------
|
|
*
|
|
* $FreeBSD$
|
|
*
|
|
* last edit-date: [Sat May 13 13:13:13 2006]
|
|
*
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include "isdnd.h"
|
|
|
|
static int hr_callgate(void);
|
|
static void handle_reserved(cfg_entry_t *cep, time_t now);
|
|
static void handle_active(cfg_entry_t *cep, time_t now);
|
|
static void recover_illegal(cfg_entry_t *cep);
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* recover from illegal state
|
|
*---------------------------------------------------------------------------*/
|
|
static void
|
|
recover_illegal(cfg_entry_t *cep)
|
|
{
|
|
llog(LL_ERR, "recover_illegal: ERROR, entry %s attempting disconnect!", cep->name);
|
|
sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
|
|
llog(LL_ERR, "recover_illegal: ERROR, entry %s - reset state/cdid!", cep->name);
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* start the timer
|
|
*---------------------------------------------------------------------------*/
|
|
void
|
|
start_timer(cfg_entry_t *cep, int seconds)
|
|
{
|
|
cep->timerval = cep->timerremain = seconds;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* stop the timer
|
|
*---------------------------------------------------------------------------*/
|
|
void
|
|
stop_timer(cfg_entry_t *cep)
|
|
{
|
|
cep->timerval = cep->timerremain = 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* callgate for handle_recovery()
|
|
*---------------------------------------------------------------------------*/
|
|
static int
|
|
hr_callgate(void)
|
|
{
|
|
static int tv_first = 1;
|
|
static struct timeval tv_last;
|
|
struct timeval tv_now;
|
|
|
|
/* there must be 1 sec minimum between calls to this section */
|
|
|
|
if(tv_first)
|
|
{
|
|
gettimeofday(&tv_last, NULL);
|
|
tv_first = 0;
|
|
}
|
|
|
|
gettimeofday(&tv_now, NULL);
|
|
|
|
if((tv_now.tv_sec - tv_last.tv_sec) < 1)
|
|
{
|
|
|
|
DBGL(DL_TIME, (llog(LL_DBG, "time < 1 - last %ld:%ld now %ld:%ld",
|
|
tv_last.tv_sec, tv_last.tv_usec,
|
|
tv_now.tv_sec, tv_now.tv_usec)));
|
|
return(1);
|
|
}
|
|
else if((tv_now.tv_sec - tv_last.tv_sec) == 1)
|
|
{
|
|
if(((1000000 - tv_last.tv_usec) + tv_now.tv_usec) < 900000)
|
|
{
|
|
DBGL(DL_TIME, (llog(LL_DBG, "time < 900000us - last %ld:%ld now %ld:%ld",
|
|
tv_last.tv_sec, tv_last.tv_usec,
|
|
tv_now.tv_sec, tv_now.tv_usec)));
|
|
return(1);
|
|
}
|
|
}
|
|
|
|
DBGL(DL_TIME, (llog(LL_DBG, "time OK! - last %ld:%ld now %ld:%ld",
|
|
tv_last.tv_sec, tv_last.tv_usec,
|
|
tv_now.tv_sec, tv_now.tv_usec)));
|
|
|
|
gettimeofday(&tv_last, NULL);
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* timeout, recovery and retry handling
|
|
*---------------------------------------------------------------------------*/
|
|
void
|
|
handle_recovery(void)
|
|
{
|
|
cfg_entry_t *cep = NULL;
|
|
int i;
|
|
time_t now;
|
|
|
|
if(hr_callgate()) /* last call to handle_recovery < 1 sec ? */
|
|
return; /* yes, exit */
|
|
|
|
now = time(NULL); /* get current time */
|
|
|
|
/* walk thru all entries, look for work to do */
|
|
|
|
for(i=0; i < nentries; i++)
|
|
{
|
|
cep = &cfg_entry_tab[i]; /* ptr to config entry */
|
|
|
|
if(cep->budget_callbackperiod && cep->budget_callbackncalls)
|
|
{
|
|
if(cep->budget_callbackperiod_time <= now)
|
|
{
|
|
DBGL(DL_BDGT, (llog(LL_DBG, "%s: new cback-budget-period (%d s, %d left)",
|
|
cep->name, cep->budget_callbackperiod, cep->budget_callbackncalls_cnt)));
|
|
cep->budget_callbackperiod_time = now + cep->budget_callbackperiod;
|
|
cep->budget_callbackncalls_cnt = cep->budget_callbackncalls;
|
|
}
|
|
}
|
|
|
|
if(cep->budget_calloutperiod && cep->budget_calloutncalls)
|
|
{
|
|
if(cep->budget_calloutperiod_time <= now)
|
|
{
|
|
DBGL(DL_BDGT, (llog(LL_DBG, "%s: new cout-budget-period (%d s, %d left)",
|
|
cep->name, cep->budget_calloutperiod, cep->budget_calloutncalls_cnt)));
|
|
cep->budget_calloutperiod_time = now + cep->budget_calloutperiod;
|
|
cep->budget_calloutncalls_cnt = cep->budget_calloutncalls;
|
|
}
|
|
}
|
|
|
|
switch(cep->cdid)
|
|
{
|
|
case CDID_UNUSED: /* entry unused */
|
|
continue;
|
|
break;
|
|
|
|
case CDID_RESERVED: /* entry reserved */
|
|
handle_reserved(cep, now);
|
|
break;
|
|
|
|
default: /* entry in use */
|
|
handle_active(cep, now);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* timeout, recovery and retry handling for active entry
|
|
*---------------------------------------------------------------------------*/
|
|
static void
|
|
handle_active(cfg_entry_t *cep, time_t now)
|
|
{
|
|
switch(cep->state)
|
|
{
|
|
case ST_ACCEPTED:
|
|
if(cep->timerval && (--(cep->timerremain)) <= 0)
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_active: entry %s, TIMEOUT !!!", cep->name)));
|
|
cep->timerval = cep->timerremain = 0;
|
|
next_state(cep, EV_TIMO);
|
|
}
|
|
break;
|
|
|
|
case ST_ALERT:
|
|
if(cep->alert_time > 0)
|
|
{
|
|
cep->alert_time--;
|
|
}
|
|
else
|
|
{
|
|
llog(LL_CHD, "%05d %s answering: incoming call from %s to %s",
|
|
cep->cdid, cep->name,
|
|
cep->real_phone_incoming.number,
|
|
cep->local_phone_incoming.number);
|
|
next_state(cep, EV_MCI);
|
|
}
|
|
break;
|
|
|
|
case ST_ILL:
|
|
recover_illegal(cep);
|
|
break;
|
|
|
|
default:
|
|
/* check hangup flag: if active, close connection */
|
|
|
|
if(cep->hangup)
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_active: entry %s, hangup request!", cep->name)));
|
|
cep->hangup = 0;
|
|
next_state(cep, EV_DRQ);
|
|
}
|
|
|
|
/* check maximum connect time reached */
|
|
|
|
if(cep->maxconnecttime > 0 && cep->connect_time > 0)
|
|
{
|
|
int connecttime = (int)difftime(now, cep->connect_time);
|
|
if(connecttime > cep->maxconnecttime)
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG,
|
|
"handle_active: entry %s, maxconnecttime %d reached!",
|
|
cep->name, cep->maxconnecttime)));
|
|
next_state(cep, EV_DRQ);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if shorthold mode is rates based, check if
|
|
* we entered a time with a new unit length
|
|
*/
|
|
|
|
if(cep->unitlengthsrc == ULSRC_RATE)
|
|
{
|
|
int connecttime = (int)difftime(now, cep->connect_time);
|
|
|
|
if((connecttime > 1) &&
|
|
(connecttime % 60))
|
|
{
|
|
int newrate = get_current_rate(cep, 0);
|
|
|
|
if(newrate != cep->unitlength)
|
|
{
|
|
DBGL(DL_MSG, (llog(LL_DBG, "handle_active: rates unit length updated %d -> %d", cep->unitlength, newrate)));
|
|
|
|
cep->unitlength = newrate;
|
|
|
|
unitlen_chkupd(cep);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*
|
|
* timeout, recovery and retry handling for reserved entry
|
|
*---------------------------------------------------------------------------*/
|
|
static void
|
|
handle_reserved(cfg_entry_t *cep, time_t now)
|
|
{
|
|
time_t waittime;
|
|
|
|
switch(cep->state)
|
|
{
|
|
case ST_DIALRTMRCHD: /* wait for dial retry time reached */
|
|
|
|
if(cep->dialrandincr)
|
|
waittime = cep->randomtime;
|
|
else
|
|
waittime = cep->recoverytime;
|
|
|
|
|
|
if(now > (cep->last_dial_time + waittime))
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: entry %s, dial retry request!", cep->name)));
|
|
cep->state = ST_DIALRETRY;
|
|
|
|
if((cep->cdid = get_cdid()) == 0)
|
|
{
|
|
llog(LL_ERR, "handle_reserved: dialretry get_cdid() returned 0!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
|
|
if((setup_dialout(cep)) == GOOD)
|
|
{
|
|
sendm_connect_req(cep);
|
|
}
|
|
else
|
|
{
|
|
llog(LL_ERR, "handle_reserved: dialretry setup_dialout returned ERROR!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
|
|
case ST_ACB_WAITDIAL: /* active callback wait for time between disconnect and dial */
|
|
|
|
if(now > (cep->last_release_time + cep->callbackwait))
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: entry %s, callback dial!", cep->name)));
|
|
cep->state = ST_ACB_DIAL;
|
|
|
|
if((cep->cdid = get_cdid()) == 0)
|
|
{
|
|
llog(LL_ERR, "handle_reserved: callback get_cdid() returned 0!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
|
|
select_first_dialno(cep);
|
|
|
|
if((setup_dialout(cep)) == GOOD)
|
|
{
|
|
sendm_connect_req(cep);
|
|
}
|
|
else
|
|
{
|
|
llog(LL_ERR, "handle_reserved: callback setup_dialout returned ERROR!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ST_ACB_DIALFAIL: /* callback to remote failed */
|
|
|
|
if(cep->dialrandincr)
|
|
waittime = cep->randomtime + cep->recoverytime;
|
|
else
|
|
waittime = cep->recoverytime;
|
|
|
|
if(now > (cep->last_release_time + waittime))
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: entry %s, callback dial retry request!", cep->name)));
|
|
cep->state = ST_ACB_DIAL;
|
|
|
|
if((cep->cdid = get_cdid()) == 0)
|
|
{
|
|
llog(LL_ERR, "handle_reserved: callback dialretry get_cdid() returned 0!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
|
|
if((setup_dialout(cep)) == GOOD)
|
|
{
|
|
sendm_connect_req(cep);
|
|
}
|
|
else
|
|
{
|
|
llog(LL_ERR, "handle_reserved: callback dialretry setup_dialout returned ERROR!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ST_PCB_WAITCALL: /* wait for remote calling back */
|
|
|
|
if(now > (cep->last_release_time + cep->calledbackwait))
|
|
{
|
|
cep->dial_count++;
|
|
|
|
if(cep->dial_count < cep->dialretries)
|
|
{
|
|
/* inside normal retry cycle */
|
|
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: entry %s, retry calledback dial #%d!",
|
|
cep->name, cep->dial_count)));
|
|
cep->state = ST_PCB_DIAL;
|
|
|
|
if((cep->cdid = get_cdid()) == 0)
|
|
{
|
|
llog(LL_ERR, "handle_reserved: calledback get_cdid() returned 0!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
select_next_dialno(cep);
|
|
|
|
if((setup_dialout(cep)) == GOOD)
|
|
{
|
|
sendm_connect_req(cep);
|
|
}
|
|
else
|
|
{
|
|
llog(LL_ERR, "handle_reserved: calledback setup_dialout returned ERROR!");
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* retries exhausted */
|
|
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: calledback dial retries exhausted")));
|
|
dialresponse(cep, DSTAT_TFAIL);
|
|
cep->cdid = CDID_UNUSED;
|
|
cep->dial_count = 0;
|
|
cep->state = ST_IDLE;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ST_DOWN: /* interface was taken down */
|
|
|
|
if(now > (cep->went_down_time + cep->downtime))
|
|
{
|
|
DBGL(DL_RCVRY, (llog(LL_DBG, "handle_reserved: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
|
|
if_up(cep);
|
|
cep->state = ST_IDLE;
|
|
cep->cdid = CDID_UNUSED;
|
|
}
|
|
break;
|
|
|
|
case ST_ILL: /* illegal state reached, recover ! */
|
|
|
|
recover_illegal(cep);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* EOF */
|