1/*
2 * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 *---------------------------------------------------------------------------
26 *
27 *	i4b daemon - timer/timing support routines
28 *	------------------------------------------
29 *
30 *	$Id: timer.c,v 1.4 2003/10/06 09:43:27 itojun Exp $
31 *
32 * $FreeBSD$
33 *
34 *      last edit-date: [Tue May  2 15:58:31 2000]
35 *
36 *---------------------------------------------------------------------------*/
37
38#include "isdnd.h"
39
40static int hr_callgate(void);
41static void handle_reserved(struct cfg_entry *cep, time_t now);
42static void handle_active(struct cfg_entry *cep, time_t now);
43static void recover_illegal(struct cfg_entry *cep);
44
45/*---------------------------------------------------------------------------*
46 *	recover from illegal state
47 *---------------------------------------------------------------------------*/
48static void
49recover_illegal(struct cfg_entry *cep)
50{
51	logit(LL_ERR, "recover_illegal: ERROR, entry %s attempting disconnect!", cep->name);
52	sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
53	logit(LL_ERR, "recover_illegal: ERROR, entry %s - reset state/cdid!", cep->name);
54	cep->state = ST_IDLE;
55	cep->cdid = CDID_UNUSED;
56}
57
58/*---------------------------------------------------------------------------*
59 *	start the timer
60 *---------------------------------------------------------------------------*/
61void
62start_timer(struct cfg_entry *cep, int seconds)
63{
64	cep->timerval = cep->timerremain = seconds;
65}
66
67/*---------------------------------------------------------------------------*
68 *	stop the timer
69 *---------------------------------------------------------------------------*/
70void
71stop_timer(struct cfg_entry *cep)
72{
73	cep->timerval = cep->timerremain = 0;
74}
75
76/*---------------------------------------------------------------------------*
77 *	callgate for handle_recovery()
78 *---------------------------------------------------------------------------*/
79static int
80hr_callgate(void)
81{
82	static int tv_first = 1;
83	static struct timeval tv_last;
84	struct timeval tv_now;
85
86	/* there must be 1 sec minimum between calls to this section */
87
88	if (tv_first)
89	{
90		gettimeofday(&tv_last, NULL);
91		tv_first = 0;
92	}
93
94	gettimeofday(&tv_now, NULL);
95
96	if ((tv_now.tv_sec - tv_last.tv_sec) < 1)
97	{
98
99		DBGL(DL_TIME, (logit(LL_DBG, "time < 1 - last %ld:%ld now %ld:%ld",
100				tv_last.tv_sec, tv_last.tv_usec,
101				tv_now.tv_sec, tv_now.tv_usec)));
102		return(1);
103	}
104	else if ((tv_now.tv_sec - tv_last.tv_sec) == 1)
105	{
106		if (((1000000 - tv_last.tv_usec) + tv_now.tv_usec) < 900000)
107		{
108			DBGL(DL_TIME, (logit(LL_DBG, "time < 900000us - last %ld:%ld now %ld:%ld",
109					tv_last.tv_sec, tv_last.tv_usec,
110					tv_now.tv_sec, tv_now.tv_usec)));
111			return(1);
112		}
113	}
114
115	DBGL(DL_TIME, (logit(LL_DBG, "time OK! - last %ld:%ld now %ld:%ld",
116			tv_last.tv_sec, tv_last.tv_usec,
117			tv_now.tv_sec, tv_now.tv_usec)));
118
119	gettimeofday(&tv_last, NULL);
120
121	return(0);
122}
123
124/*---------------------------------------------------------------------------*
125 *	timeout, recovery and retry handling
126 *---------------------------------------------------------------------------*/
127void
128handle_recovery(void)
129{
130	struct cfg_entry *cep = NULL;
131	time_t now;
132
133	if (hr_callgate())	/* last call to handle_recovery < 1 sec ? */
134		return;		/* yes, exit */
135
136	now = time(NULL);	/* get current time */
137
138	/* walk thru all entries, look for work to do */
139
140	for (cep = get_first_cfg_entry(); cep; cep = NEXT_CFE(cep)) {
141
142		if (cep->budget_callbackperiod && cep->budget_callbackncalls)
143		{
144			if (cep->budget_callbackperiod_time <= now)
145			{
146				DBGL(DL_BDGT, (logit(LL_DBG, "%s: new cback-budget-period (%d s, %d left)",
147					cep->name, cep->budget_callbackperiod, cep->budget_callbackncalls_cnt)));
148				cep->budget_callbackperiod_time = now + cep->budget_callbackperiod;
149				cep->budget_callbackncalls_cnt = cep->budget_callbackncalls;
150			}
151		}
152
153		if (cep->budget_calloutperiod && cep->budget_calloutncalls)
154		{
155			if (cep->budget_calloutperiod_time <= now)
156			{
157				DBGL(DL_BDGT, (logit(LL_DBG, "%s: new cout-budget-period (%d s, %d left)",
158					cep->name, cep->budget_calloutperiod, cep->budget_calloutncalls_cnt)));
159				cep->budget_calloutperiod_time = now + cep->budget_calloutperiod;
160				cep->budget_calloutncalls_cnt = cep->budget_calloutncalls;
161			}
162		}
163
164		switch (cep->cdid)
165		{
166		case CDID_UNUSED:		/* entry unused */
167			continue;
168			break;
169
170		case CDID_RESERVED:		/* entry reserved */
171			handle_reserved(cep, now);
172			break;
173
174		default:			/* entry in use */
175			handle_active(cep, now);
176			break;
177		}
178	}
179}
180
181/*---------------------------------------------------------------------------*
182 *	timeout, recovery and retry handling for active entry
183 *---------------------------------------------------------------------------*/
184static void
185handle_active(struct cfg_entry *cep, time_t now)
186{
187	switch (cep->state)
188	{
189	case ST_ACCEPTED:
190		if (cep->timerval && (--(cep->timerremain)) <= 0)
191		{
192			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_active: entry %s, TIMEOUT !!!", cep->name)));
193			cep->timerval = cep->timerremain = 0;
194			next_state(cep, EV_TIMO);
195		}
196		break;
197
198	case ST_ALERT:
199		if (cep->alert_time > 0)
200		{
201			cep->alert_time--;
202		}
203		else
204		{
205			logit(LL_CHD, "%05d %s answering: incoming call from %s to %s",
206				cep->cdid, cep->name,
207				cep->real_phone_incoming,
208				cep->local_phone_incoming);
209			next_state(cep, EV_MCI);
210		}
211		break;
212
213	case ST_ILL:
214		recover_illegal(cep);
215		break;
216
217	default:
218		/* check hangup flag: if active, close connection */
219
220		if (cep->hangup)
221		{
222			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_active: entry %s, hangup request!", cep->name)));
223			cep->hangup = 0;
224			next_state(cep, EV_DRQ);
225		}
226
227		/*
228		 * if shorthold mode is rates based, check if
229		 * we entered a time with a new unit length
230		 */
231
232		if (cep->unitlengthsrc == ULSRC_RATE)
233		{
234			int connecttime = (int)difftime(now, cep->connect_time);
235
236			if ((connecttime > 1) &&
237			   (connecttime % 60))
238			{
239				int newrate = get_current_rate(cep, 0);
240
241				if (newrate != cep->unitlength)
242				{
243					DBGL(DL_MSG, (logit(LL_DBG, "handle_active: rates unit length updated %d -> %d", cep->unitlength, newrate)));
244
245					cep->unitlength = newrate;
246
247					unitlen_chkupd(cep);
248				}
249			}
250		}
251		break;
252	}
253}
254
255/*---------------------------------------------------------------------------*
256 *	timeout, recovery and retry handling for reserved entry
257 *---------------------------------------------------------------------------*/
258static void
259handle_reserved(struct cfg_entry *cep, time_t now)
260{
261	time_t waittime;
262
263	switch (cep->state)
264	{
265	case ST_DIALRTMRCHD:	/* wait for dial retry time reached */
266
267		if (cep->dialrandincr)
268			waittime = cep->randomtime;
269		else
270			waittime = cep->recoverytime;
271
272
273		if (now > (cep->last_dial_time + waittime))
274		{
275			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: entry %s, dial retry request!", cep->name)));
276			cep->state = ST_DIALRETRY;
277
278			if ((cep->cdid = get_cdid()) == 0)
279			{
280				logit(LL_ERR, "handle_reserved: dialretry get_cdid() returned 0!");
281				cep->state = ST_IDLE;
282				cep->cdid = CDID_UNUSED;
283				return;
284			}
285
286			if ((setup_dialout(cep)) == GOOD)
287			{
288				sendm_connect_req(cep);
289			}
290			else
291			{
292				logit(LL_ERR, "handle_reserved: dialretry setup_dialout returned ERROR!");
293				cep->state = ST_IDLE;
294				cep->cdid = CDID_UNUSED;
295				return;
296			}
297		}
298		break;
299
300
301	case ST_ACB_WAITDIAL: 	/* active callback wait for time between disconnect and dial */
302
303		if (now > (cep->last_release_time + cep->callbackwait))
304		{
305			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: entry %s, callback dial!", cep->name)));
306			cep->state = ST_ACB_DIAL;
307
308			if ((cep->cdid = get_cdid()) == 0)
309			{
310				logit(LL_ERR, "handle_reserved: callback get_cdid() returned 0!");
311				cep->state = ST_IDLE;
312				cep->cdid = CDID_UNUSED;
313				return;
314			}
315
316			select_first_dialno(cep);
317
318			if ((setup_dialout(cep)) == GOOD)
319			{
320				sendm_connect_req(cep);
321			}
322			else
323			{
324				logit(LL_ERR, "handle_reserved: callback setup_dialout returned ERROR!");
325				cep->state = ST_IDLE;
326				cep->cdid = CDID_UNUSED;
327				return;
328			}
329		}
330		break;
331
332	case ST_ACB_DIALFAIL:	/* callback to remote failed */
333
334		if (cep->dialrandincr)
335			waittime = cep->randomtime + cep->recoverytime;
336		else
337			waittime = cep->recoverytime;
338
339		if (now > (cep->last_release_time + waittime))
340		{
341			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: entry %s, callback dial retry request!", cep->name)));
342			cep->state = ST_ACB_DIAL;
343
344			if ((cep->cdid = get_cdid()) == 0)
345			{
346				logit(LL_ERR, "handle_reserved: callback dialretry get_cdid() returned 0!");
347				cep->state = ST_IDLE;
348				cep->cdid = CDID_UNUSED;
349				return;
350			}
351
352			if ((setup_dialout(cep)) == GOOD)
353			{
354				sendm_connect_req(cep);
355			}
356			else
357			{
358				logit(LL_ERR, "handle_reserved: callback dialretry setup_dialout returned ERROR!");
359				cep->state = ST_IDLE;
360				cep->cdid = CDID_UNUSED;
361				return;
362			}
363		}
364		break;
365
366	case ST_PCB_WAITCALL:	/* wait for remote calling back */
367
368		if (now > (cep->last_release_time + cep->calledbackwait))
369		{
370			cep->dial_count++;
371
372			if (cep->dial_count < cep->dialretries)
373			{
374				/* inside normal retry cycle */
375
376				DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: entry %s, retry calledback dial #%d!",
377					cep->name, cep->dial_count)));
378				cep->state = ST_PCB_DIAL;
379
380				if ((cep->cdid = get_cdid()) == 0)
381				{
382					logit(LL_ERR, "handle_reserved: calledback get_cdid() returned 0!");
383					cep->state = ST_IDLE;
384					cep->cdid = CDID_UNUSED;
385					return;
386				}
387				select_next_dialno(cep);
388
389				if ((setup_dialout(cep)) == GOOD)
390				{
391					sendm_connect_req(cep);
392				}
393				else
394				{
395					logit(LL_ERR, "handle_reserved: calledback setup_dialout returned ERROR!");
396					cep->state = ST_IDLE;
397					cep->cdid = CDID_UNUSED;
398					return;
399				}
400			}
401			else
402			{
403				/* retries exhausted */
404
405				DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: calledback dial retries exhausted")));
406				dialresponse(cep, DSTAT_TFAIL);
407				cep->cdid = CDID_UNUSED;
408				cep->dial_count = 0;
409				cep->state = ST_IDLE;
410			}
411		}
412		break;
413
414	case ST_DOWN:	/* interface was taken down */
415
416		if (now > (cep->went_down_time + cep->downtime))
417		{
418			DBGL(DL_RCVRY, (logit(LL_DBG, "handle_reserved: taking %s%d up", cep->usrdevicename, cep->usrdeviceunit)));
419			if_up(cep);
420			cep->state = ST_IDLE;
421			cep->cdid = CDID_UNUSED;
422		}
423		break;
424
425	case ST_ILL:	/* illegal state reached, recover ! */
426
427		recover_illegal(cep);
428		break;
429	}
430}
431
432/* EOF */
433