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 
40 static int hr_callgate(void);
41 static void handle_reserved(struct cfg_entry *cep, time_t now);
42 static void handle_active(struct cfg_entry *cep, time_t now);
43 static void recover_illegal(struct cfg_entry *cep);
44 
45 /*---------------------------------------------------------------------------*
46  *	recover from illegal state
47  *---------------------------------------------------------------------------*/
48 static void
recover_illegal(struct cfg_entry * cep)49 recover_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  *---------------------------------------------------------------------------*/
61 void
start_timer(struct cfg_entry * cep,int seconds)62 start_timer(struct cfg_entry *cep, int seconds)
63 {
64 	cep->timerval = cep->timerremain = seconds;
65 }
66 
67 /*---------------------------------------------------------------------------*
68  *	stop the timer
69  *---------------------------------------------------------------------------*/
70 void
stop_timer(struct cfg_entry * cep)71 stop_timer(struct cfg_entry *cep)
72 {
73 	cep->timerval = cep->timerremain = 0;
74 }
75 
76 /*---------------------------------------------------------------------------*
77  *	callgate for handle_recovery()
78  *---------------------------------------------------------------------------*/
79 static int
hr_callgate(void)80 hr_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  *---------------------------------------------------------------------------*/
127 void
handle_recovery(void)128 handle_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  *---------------------------------------------------------------------------*/
184 static void
handle_active(struct cfg_entry * cep,time_t now)185 handle_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  *---------------------------------------------------------------------------*/
258 static void
handle_reserved(struct cfg_entry * cep,time_t now)259 handle_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