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