Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / i4b / isdnd / timer.c
1 /*
2  * Copyright (c) 1997, 2001 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  * $FreeBSD: src/usr.sbin/i4b/isdnd/timer.c,v 1.6.2.2 2001/08/10 23:17:32 obrien Exp $
31  *
32  *      last edit-date: [Fri Jul 20 20:29:28 2001]
33  *
34  *---------------------------------------------------------------------------*/
35
36 #include "isdnd.h"
37
38 static int hr_callgate(void);
39 static void handle_reserved(cfg_entry_t *cep, time_t now);
40 static void handle_active(cfg_entry_t *cep, time_t now);
41 static void recover_illegal(cfg_entry_t *cep);
42
43 /*---------------------------------------------------------------------------*
44  *      recover from illegal state
45  *---------------------------------------------------------------------------*/
46 static void
47 recover_illegal(cfg_entry_t *cep)
48 {
49         log(LL_ERR, "recover_illegal: ERROR, entry %s attempting disconnect!", cep->name);
50         sendm_disconnect_req(cep, (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
51         log(LL_ERR, "recover_illegal: ERROR, entry %s - reset state/cdid!", cep->name);
52         cep->state = ST_IDLE;
53         cep->cdid = CDID_UNUSED;
54 }
55
56 /*---------------------------------------------------------------------------*
57  *      start the timer
58  *---------------------------------------------------------------------------*/
59 void
60 start_timer(cfg_entry_t *cep, int seconds)
61 {
62         cep->timerval = cep->timerremain = seconds;
63 }
64
65 /*---------------------------------------------------------------------------*
66  *      stop the timer
67  *---------------------------------------------------------------------------*/
68 void
69 stop_timer(cfg_entry_t *cep)
70 {
71         cep->timerval = cep->timerremain = 0;   
72 }
73
74 /*---------------------------------------------------------------------------*
75  *      callgate for handle_recovery()
76  *---------------------------------------------------------------------------*/
77 static int
78 hr_callgate(void)
79 {
80         static int tv_first = 1;
81         static struct timeval tv_last;
82         struct timeval tv_now;
83         
84         /* there must be 1 sec minimum between calls to this section */
85         
86         if(tv_first)
87         {
88                 gettimeofday(&tv_last, NULL);
89                 tv_first = 0;
90         }
91         
92         gettimeofday(&tv_now, NULL);
93         
94         if((tv_now.tv_sec - tv_last.tv_sec) < 1)
95         {
96         
97                 DBGL(DL_TIME, (log(LL_DBG, "time < 1 - last %ld:%ld now %ld:%ld",
98                                 tv_last.tv_sec, tv_last.tv_usec,
99                                 tv_now.tv_sec, tv_now.tv_usec)));
100                 return(1);
101         }
102         else if((tv_now.tv_sec - tv_last.tv_sec) == 1)
103         {
104                 if(((1000000 - tv_last.tv_usec) + tv_now.tv_usec) < 900000)
105                 {
106                         DBGL(DL_TIME, (log(LL_DBG, "time < 900000us - last %ld:%ld now %ld:%ld",
107                                         tv_last.tv_sec, tv_last.tv_usec,
108                                         tv_now.tv_sec, tv_now.tv_usec)));
109                         return(1);
110                 }
111         }
112         
113         DBGL(DL_TIME, (log(LL_DBG, "time OK! - last %ld:%ld now %ld:%ld",
114                         tv_last.tv_sec, tv_last.tv_usec,
115                         tv_now.tv_sec, tv_now.tv_usec)));
116         
117         gettimeofday(&tv_last, NULL);
118         
119         return(0);
120 }        
121
122 /*---------------------------------------------------------------------------*
123  *      timeout, recovery and retry handling
124  *---------------------------------------------------------------------------*/
125 void
126 handle_recovery(void)
127 {
128         cfg_entry_t *cep = NULL;
129         int i;
130         time_t now;
131         
132         if(hr_callgate())       /* last call to handle_recovery < 1 sec ? */
133                 return;         /* yes, exit */
134         
135         now = time(NULL);       /* get current time */
136         
137         /* walk thru all entries, look for work to do */
138         
139         for(i=0; i < nentries; i++)
140         {
141                 cep = &cfg_entry_tab[i];        /* ptr to config entry */
142         
143                 if(cep->budget_callbackperiod && cep->budget_callbackncalls)
144                 {
145                         if(cep->budget_callbackperiod_time <= now)
146                         {
147                                 DBGL(DL_BDGT, (log(LL_DBG, "%s: new cback-budget-period (%d s, %d left)",
148                                         cep->name, cep->budget_callbackperiod, cep->budget_callbackncalls_cnt)));
149                                 cep->budget_callbackperiod_time = now + cep->budget_callbackperiod;
150                                 cep->budget_callbackncalls_cnt = cep->budget_callbackncalls;
151                         }
152                 }
153
154                 if(cep->budget_calloutperiod && cep->budget_calloutncalls)
155                 {
156                         if(cep->budget_calloutperiod_time <= now)
157                         {
158                                 DBGL(DL_BDGT, (log(LL_DBG, "%s: new cout-budget-period (%d s, %d left)",
159                                         cep->name, cep->budget_calloutperiod, cep->budget_calloutncalls_cnt)));
160                                 cep->budget_calloutperiod_time = now + cep->budget_calloutperiod;
161                                 cep->budget_calloutncalls_cnt = cep->budget_calloutncalls;
162                         }
163                 }
164
165                 switch(cep->cdid)
166                 {
167                         case CDID_UNUSED:               /* entry unused */
168                                 continue;
169                                 break;
170         
171                         case CDID_RESERVED:             /* entry reserved */
172                                 handle_reserved(cep, now);
173                                 break;
174         
175                         default:                        /* entry in use */
176                                 handle_active(cep, now);
177                                 break;
178                 }
179         }
180 }               
181
182 /*---------------------------------------------------------------------------*
183  *      timeout, recovery and retry handling for active entry
184  *---------------------------------------------------------------------------*/
185 static void
186 handle_active(cfg_entry_t *cep, time_t now)
187 {
188         switch(cep->state)
189         {
190                 case ST_ACCEPTED:
191                         if(cep->timerval && (--(cep->timerremain)) <= 0)
192                         {
193                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, TIMEOUT !!!", cep->name)));
194                                 cep->timerval = cep->timerremain = 0;
195                                 next_state(cep, EV_TIMO);
196                         }
197                         break;
198                         
199                 case ST_ALERT:
200                         if(cep->alert_time > 0)
201                         {
202                                 cep->alert_time--;
203                         }
204                         else
205                         {
206                                 log(LL_CHD, "%05d %s answering: incoming call from %s to %s",
207                                         cep->cdid, cep->name, 
208                                         cep->real_phone_incoming,
209                                         cep->local_phone_incoming);
210                                 next_state(cep, EV_MCI);
211                         }
212                         break;
213                                 
214                 case ST_ILL:
215                         recover_illegal(cep);
216                         break;
217                         
218                 default:
219                         /* check hangup flag: if active, close connection */
220
221                         if(cep->hangup)
222                         {
223                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_active: entry %s, hangup request!", cep->name)));
224                                 cep->hangup = 0;
225                                 next_state(cep, EV_DRQ);
226                         }
227
228                         /* check maximum connect time reached */
229
230                         if(cep->maxconnecttime > 0 && cep->connect_time > 0)
231                         {
232                                 int connecttime = (int)difftime(now, cep->connect_time);
233                                 if(connecttime > cep->maxconnecttime)
234                                 {
235                                         DBGL(DL_RCVRY, (log(LL_DBG, 
236                                                 "handle_active: entry %s, maxconnecttime %d reached!",
237                                                 cep->name, cep->maxconnecttime)));
238                                         next_state(cep, EV_DRQ);
239                                 }
240                         }
241
242                         /*
243                          * if shorthold mode is rates based, check if
244                          * we entered a time with a new unit length
245                          */
246
247                         if(cep->unitlengthsrc == ULSRC_RATE)
248                         {
249                                 int connecttime = (int)difftime(now, cep->connect_time);
250
251                                 if((connecttime > 1) &&
252                                    (connecttime % 60))
253                                 {
254                                         int newrate = get_current_rate(cep, 0);
255         
256                                         if(newrate != cep->unitlength)
257                                         {
258                                                 DBGL(DL_MSG, (log(LL_DBG, "handle_active: rates unit length updated %d -> %d", cep->unitlength, newrate)));
259                         
260                                                 cep->unitlength = newrate;
261         
262                                                 unitlen_chkupd(cep);
263                                         }
264                                 }
265                         }
266                         break;
267         }
268 }
269
270 /*---------------------------------------------------------------------------*
271  *      timeout, recovery and retry handling for reserved entry
272  *---------------------------------------------------------------------------*/
273 static void
274 handle_reserved(cfg_entry_t *cep, time_t now)
275 {
276         time_t waittime;
277         
278         switch(cep->state)
279         {       
280                 case ST_DIALRTMRCHD:    /* wait for dial retry time reached */
281         
282                         if(cep->dialrandincr)
283                                 waittime = cep->randomtime;
284                         else
285                                 waittime = cep->recoverytime;
286         
287                                         
288                         if(now > (cep->last_dial_time + waittime))
289                         {
290                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, dial retry request!", cep->name)));
291                                 cep->state = ST_DIALRETRY;
292         
293                                 if((cep->cdid = get_cdid()) == 0)
294                                 {
295                                         log(LL_ERR, "handle_reserved: dialretry get_cdid() returned 0!");
296                                         cep->state = ST_IDLE;
297                                         cep->cdid = CDID_UNUSED;
298                                         return;
299                                 }
300
301                                 if((setup_dialout(cep)) == GOOD)
302                                 {
303                                         sendm_connect_req(cep);
304                                 }
305                                 else
306                                 {
307                                         log(LL_ERR, "handle_reserved: dialretry setup_dialout returned ERROR!");
308                                         cep->state = ST_IDLE;
309                                         cep->cdid = CDID_UNUSED;
310                                         return;
311                                 }                                       
312                         }
313                         break;
314                         
315                 
316                 case ST_ACB_WAITDIAL:   /* active callback wait for time between disconnect and dial */
317         
318                         if(now > (cep->last_release_time + cep->callbackwait))
319                         {
320                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial!", cep->name)));
321                                 cep->state = ST_ACB_DIAL;
322         
323                                 if((cep->cdid = get_cdid()) == 0)
324                                 {
325                                         log(LL_ERR, "handle_reserved: callback get_cdid() returned 0!");
326                                         cep->state = ST_IDLE;
327                                         cep->cdid = CDID_UNUSED;
328                                         return;
329                                 }
330
331                                 select_first_dialno(cep);
332
333                                 if((setup_dialout(cep)) == GOOD)
334                                 {
335                                         sendm_connect_req(cep);
336                                 }
337                                 else
338                                 {
339                                         log(LL_ERR, "handle_reserved: callback setup_dialout returned ERROR!");
340                                         cep->state = ST_IDLE;
341                                         cep->cdid = CDID_UNUSED;
342                                         return;
343                                 }                                       
344                         }
345                         break;
346         
347                 case ST_ACB_DIALFAIL:   /* callback to remote failed */
348         
349                         if(cep->dialrandincr)
350                                 waittime = cep->randomtime + cep->recoverytime;
351                         else
352                                 waittime = cep->recoverytime;
353         
354                         if(now > (cep->last_release_time + waittime))
355                         {
356                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, callback dial retry request!", cep->name)));
357                                 cep->state = ST_ACB_DIAL;
358         
359                                 if((cep->cdid = get_cdid()) == 0)
360                                 {
361                                         log(LL_ERR, "handle_reserved: callback dialretry get_cdid() returned 0!");
362                                         cep->state = ST_IDLE;
363                                         cep->cdid = CDID_UNUSED;
364                                         return;
365                                 }
366
367                                 if((setup_dialout(cep)) == GOOD)
368                                 {
369                                         sendm_connect_req(cep);
370                                 }
371                                 else
372                                 {
373                                         log(LL_ERR, "handle_reserved: callback dialretry setup_dialout returned ERROR!");
374                                         cep->state = ST_IDLE;
375                                         cep->cdid = CDID_UNUSED;
376                                         return;
377                                 }                                       
378                         }
379                         break;
380         
381                 case ST_PCB_WAITCALL:   /* wait for remote calling back */
382
383                         if(now > (cep->last_release_time + cep->calledbackwait))
384                         {
385                                 cep->dial_count++;
386         
387                                 if(cep->dial_count < cep->dialretries)
388                                 {
389                                         /* inside normal retry cycle */
390         
391                                         DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: entry %s, retry calledback dial #%d!",
392                                                 cep->name, cep->dial_count)));
393                                         cep->state = ST_PCB_DIAL;
394         
395                                         if((cep->cdid = get_cdid()) == 0)
396                                         {
397                                                 log(LL_ERR, "handle_reserved: calledback get_cdid() returned 0!");
398                                                 cep->state = ST_IDLE;
399                                                 cep->cdid = CDID_UNUSED;
400                                                 return;
401                                         }
402                                         select_next_dialno(cep);
403
404                                         if((setup_dialout(cep)) == GOOD)
405                                         {
406                                                 sendm_connect_req(cep);
407                                         }
408                                         else
409                                         {
410                                                 log(LL_ERR, "handle_reserved: calledback setup_dialout returned ERROR!");
411                                                 cep->state = ST_IDLE;
412                                                 cep->cdid = CDID_UNUSED;
413                                                 return;
414                                         }                                       
415                                 }
416                                 else
417                                 {
418                                         /* retries exhausted */
419         
420                                         DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: calledback dial retries exhausted")));
421                                         dialresponse(cep, DSTAT_TFAIL);
422                                         cep->cdid = CDID_UNUSED;
423                                         cep->dial_count = 0;
424                                         cep->state = ST_IDLE;
425                                 }
426                         }
427                         break;
428                         
429                 case ST_DOWN:   /* interface was taken down */
430
431                         if(now > (cep->went_down_time + cep->downtime))
432                         {
433                                 DBGL(DL_RCVRY, (log(LL_DBG, "handle_reserved: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
434                                 if_up(cep);
435                                 cep->state = ST_IDLE;
436                                 cep->cdid = CDID_UNUSED;
437                         }
438                         break;
439                         
440                 case ST_ILL:    /* illegal state reached, recover ! */
441                 
442                         recover_illegal(cep);
443                         break;
444         }
445 }
446
447 /* EOF */