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