/* * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * *--------------------------------------------------------------------------- * * i4b daemon - misc support routines * ---------------------------------- * * $Id: support.c,v 1.16 2012/03/01 22:31:48 joerg Exp $ * * $FreeBSD$ * * last edit-date: [Wed Oct 4 18:24:27 2000] * *---------------------------------------------------------------------------*/ #include "isdnd.h" static int isvalidtime(struct cfg_entry *cep); static SLIST_HEAD(, isdn_ctrl_state) isdn_ctrl_list = SLIST_HEAD_INITIALIZER(isdn_ctrl_list); static SIMPLEQ_HEAD(, cfg_entry) cfg_entry_list = SIMPLEQ_HEAD_INITIALIZER(cfg_entry_list); /*---------------------------------------------------------------------------* * find an active entry by driver type and driver unit *---------------------------------------------------------------------------*/ struct cfg_entry * find_active_entry_by_driver(int drivertype, int driverunit) { struct cfg_entry *cep = NULL; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { if (!((cep->usrdevice == drivertype) && (cep->usrdeviceunit == driverunit))) { continue; } /* check time interval */ if (isvalidtime(cep) == 0) { DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d, time not valid!", cep->index))); continue; } /* found */ if (cep->cdid == CDID_UNUSED) { DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_UNUSED !", cep->index, cep->usrdevicename, driverunit))); return(NULL); } else if (cep->cdid == CDID_RESERVED) { DBGL(DL_MSG, (logit(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_RESERVED!", cep->index, cep->usrdevicename, driverunit))); return(NULL); } return(cep); } return(NULL); } /*---------------------------------------------------------------------------* * find entry by drivertype and driverunit and setup for dialing out *---------------------------------------------------------------------------*/ struct cfg_entry * find_by_device_for_dialout(int drivertype, int driverunit) { struct cfg_entry *cep; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { /* compare driver type and unit */ if (!((cep->usrdevice == drivertype) && (cep->usrdeviceunit == driverunit))) { continue; } /* check time interval */ if (isvalidtime(cep) == 0) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, time not valid!", cep->index))); continue; } /* found, check if already reserved */ if (cep->cdid == CDID_RESERVED) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, cdid reserved!", cep->index))); return(NULL); } /* check if this entry is already in use ? */ if (cep->cdid != CDID_UNUSED) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, cdid in use", cep->index))); return(NULL); } if ((setup_dialout(cep)) == GOOD) { /* found an entry to be used for calling out */ DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: found entry %d!", cep->index))); return(cep); } else { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: entry %d, setup_dialout() failed!", cep->index))); return(NULL); } } DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialout: no entry found!"))); return(NULL); } /*---------------------------------------------------------------------------* * find entry by drivertype and driverunit and setup for dialing out *---------------------------------------------------------------------------*/ struct cfg_entry * find_by_device_for_dialoutnumber(int drivertype, int driverunit, int cmdlen, char *cmd) { struct cfg_entry *cep; int j; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { /* compare driver type and unit */ if (!((cep->usrdevice == drivertype) && (cep->usrdeviceunit == driverunit))) { continue; } /* check time interval */ if (isvalidtime(cep) == 0) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, time not valid!", cep->index))); continue; } /* found, check if already reserved */ if (cep->cdid == CDID_RESERVED) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid reserved!", cep->index))); return(NULL); } /* check if this entry is already in use ? */ if (cep->cdid != CDID_UNUSED) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid in use", cep->index))); return(NULL); } /* check number and copy to cep->remote_numbers[] */ for (j = 0; j < cmdlen; j++) { if (!(isdigit((unsigned char)*(cmd+j)))) { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, dial string contains non-digit at pos %d", cep->index, j))); return(NULL); } /* fill in number to dial */ cep->remote_numbers[0].number[j] = *(cmd+j); } cep->remote_numbers[0].number[j] = '\0'; cep->remote_numbers_count = 1; if ((setup_dialout(cep)) == GOOD) { /* found an entry to be used for calling out */ DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: found entry %d!", cep->index))); return(cep); } else { DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, setup_dialout() failed!", cep->index))); return(NULL); } } DBGL(DL_MSG, (logit(LL_DBG, "find_by_device_for_dialoutnumber: no entry found!"))); return(NULL); } /*---------------------------------------------------------------------------* * find entry by drivertype and driverunit and setup for dialing out *---------------------------------------------------------------------------*/ int setup_dialout(struct cfg_entry *cep) { struct isdn_ctrl_state *ctrl; int i; if (cep->isdncontroller < 0) { /* we are free to choose a controller */ for (ctrl = get_first_ctrl_state(); ctrl; ctrl = NEXT_CTRL(ctrl)) { if (get_controller_state(ctrl) != CTRL_UP) continue; switch (cep->isdnchannel) { case CHAN_ANY: for (i = 0; i < ctrl->nbch; i++) { if (ret_channel_state(ctrl, i) == CHAN_IDLE) break; } if (i == ctrl->nbch) continue; break; default: if (ret_channel_state(ctrl, cep->isdnchannel) != CHAN_IDLE) continue; break; } /* this controller looks ok */ break; } } else { /* fixed controller in config, use that */ ctrl = find_ctrl_state(cep->isdncontroller); } if (ctrl == NULL) return (ERROR); /* check controller operational */ if (get_controller_state(ctrl) != CTRL_UP) { DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, controller is down", cep->name))); return(ERROR); } cep->isdncontrollerused = ctrl->isdnif; /* check channel available */ switch (cep->isdnchannel) { case CHAN_ANY: for (i = 0; i < ctrl->nbch; i++) { if (ret_channel_state(ctrl, i) == CHAN_IDLE) break; } if (i == ctrl->nbch) { DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name))); return(ERROR); } cep->isdnchannelused = CHAN_ANY; break; default: if (ret_channel_state(ctrl, cep->isdnchannel) != CHAN_IDLE) { DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name))); return(ERROR); } cep->isdnchannelused = cep->isdnchannel; break; } DBGL(DL_MSG, (logit(LL_DBG, "setup_dialout: entry %s ok!", cep->name))); /* preset disconnect cause */ SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B); SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_NORMAL); return(GOOD); } /*---------------------------------------------------------------------------* * find entry by drivertype and driverunit *---------------------------------------------------------------------------*/ struct cfg_entry * get_cep_by_driver(int drivertype, int driverunit) { struct cfg_entry *cep; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { if (!((cep->usrdevice == drivertype) && (cep->usrdeviceunit == driverunit))) { continue; } /* check time interval */ if (isvalidtime(cep) == 0) { DBGL(DL_MSG, (logit(LL_DBG, "get_cep_by_driver: entry %d, time not valid!", cep->index))); continue; } DBGL(DL_MSG, (logit(LL_DBG, "get_cep_by_driver: found entry %d!", cep->index))); return(cep); } return(NULL); } /*---------------------------------------------------------------------------* * find a matching entry for an incoming call * * - not found/no match: log output with LL_CHD and return NULL * - found/match: make entry in free cep, return address *---------------------------------------------------------------------------*/ struct cfg_entry * find_matching_entry_incoming(msg_connect_ind_t *mp, int len) { struct cfg_entry *cep = NULL; static const char resvd_type[] = "reserverd"; static const char no_type[] = "no type"; static const char * const numbering_types[] = { "unknown", "international", "national", "network specific", "subscriber", "abbreviated", resvd_type, resvd_type, resvd_type }; const char * ntype; int i; /* older kernels do not deliver all the information */ if (((u_int8_t*)&mp->type_plan - (u_int8_t*)mp + (int)sizeof(mp->type_plan)) <= len) { ntype = numbering_types[(mp->type_plan & 0x70)>>4]; } else { ntype = no_type; } /* check for CW (call waiting) early */ if (mp->channel == CHAN_NO) { if (aliasing) { const char *src_tela = "ERROR-src_tela"; const char *dst_tela = "ERROR-dst_tela"; src_tela = get_alias(mp->src_telno); dst_tela = get_alias(mp->dst_telno); logit(LL_CHD, "%05d CW from %s (%s) to %s (%s) (no channel free)", mp->header.cdid, src_tela, ntype, dst_tela, mp->display); } else { logit(LL_CHD, "%05d call waiting from %s (%s) to %s (%s) (no channel free)", mp->header.cdid, mp->src_telno, ntype, mp->dst_telno, mp->display); } return(NULL); } SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { int n; struct isdn_ctrl_state *ctrl; /* check my number */ if (strncmp(cep->local_phone_incoming, mp->dst_telno, strlen(cep->local_phone_incoming))) { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s", cep->index, cep->local_phone_incoming, mp->dst_telno))); continue; } /* check all allowed remote number's for this entry */ for (n = 0; n < cep->incoming_numbers_count; n++) { incoming_number_t *in = &cep->remote_phone_incoming[n]; if (in->number[0] == '*') break; if (strncmp(in->number, mp->src_telno, strlen(in->number))) { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", cep->index, in->number, mp->src_telno))); } else break; } if (n >= cep->incoming_numbers_count) continue; /* check b protocol */ if (cep->b1protocol != mp->bprot) { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, bprot %d != incomingprot %d", cep->index, cep->b1protocol, mp->bprot))); continue; } /* is this entry currently in use ? */ if (cep->cdid != CDID_UNUSED) { if (cep->cdid == CDID_RESERVED) { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, cdid is reserved", cep->index))); } else if (cep->dialin_reaction == REACT_ACCEPT && cep->dialouttype == DIALOUT_CALLEDBACK) { /* * We might consider doing this even if this is * not a calledback config entry - BUT: there are * severe race conditions and timinig problems * ex. if both sides run I4B with no callback * delay - both may shutdown the outgoing call * and never be able to establish a connection. * In the called-back case this should not happen. */ DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, incoming call for callback in progress (cdid %05d)", cep->index, cep->cdid))); /* save the current call state, we're going to overwrite it with the * new incoming state below... */ cep->saved_call.cdid = cep->cdid; cep->saved_call.controller = cep->isdncontrollerused; cep->saved_call.channel = cep->isdnchannelused; } else { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, cdid in use", cep->index))); continue; /* yes, next */ } } /* check controller value ok */ ctrl = find_ctrl_state(mp->controller); if (ctrl == NULL) { logit(LL_CHD, "%05d %s incoming call with invalid controller %d", mp->header.cdid, cep->name, mp->controller); return(NULL); } /* check controller marked up */ if (get_controller_state(ctrl) != CTRL_UP) { logit(LL_CHD, "%05d %s incoming call, controller %d DOWN!", mp->header.cdid, cep->name, mp->controller); return(NULL); } /* check channel he wants */ switch (mp->channel) { case CHAN_ANY: for (i = 0; i < ctrl->nbch; i++) if (ret_channel_state(ctrl, i) == CHAN_IDLE) break; if (i == ctrl->nbch) { logit(LL_CHD, "%05d %s incoming call, no channel free!", mp->header.cdid, cep->name); return(NULL); } break; case CHAN_NO: logit(LL_CHD, "%05d %s incoming call, call waiting (no channel available)!", mp->header.cdid, cep->name); return(NULL); break; default: if ((ret_channel_state(ctrl, mp->channel)) != CHAN_IDLE) { logit(LL_CHD, "%05d %s incoming call, channel B%d not free!", mp->header.cdid, cep->name, mp->channel); return(NULL); } break; } /* check time interval */ if (isvalidtime(cep) == 0) { DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, time not valid!", cep->index))); continue; } /* found a matching entry */ cep->cdid = mp->header.cdid; cep->isdncontrollerused = mp->controller; cep->isdnchannelused = mp->channel; /*XXX*/ cep->disc_cause = 0; /* cp number to real one used */ strlcpy(cep->real_phone_incoming, mp->src_telno, sizeof(cep->real_phone_incoming)); /* copy display string */ strlcpy(cep->display, mp->display, sizeof(cep->display)); /* entry currently down ? */ if (cep->state == ST_DOWN) { msg_updown_ind_t mui; /* set interface up */ DBGL(DL_MSG, (logit(LL_DBG, "find_matching_entry_incoming: entry %d, ", cep->index))); mui.driver = cep->usrdevice; mui.driver_unit = cep->usrdeviceunit; mui.updown = SOFT_ENA; if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0) { logit(LL_ERR, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); error_exit(1, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); } cep->down_retry_count = 0; cep->state = ST_IDLE; } return(cep); } if (aliasing) { const char *src_tela = "ERROR-src_tela"; const char *dst_tela = "ERROR-dst_tela"; src_tela = get_alias(mp->src_telno); dst_tela = get_alias(mp->dst_telno); logit(LL_CHD, "%05d Call from %s (%s) to %s (%s)", mp->header.cdid, src_tela, ntype, dst_tela, mp->display); } else { logit(LL_CHD, "%05d incoming call from %s (%s) to %s (%s)", mp->header.cdid, mp->src_telno, ntype, mp->dst_telno, mp->display); } return(NULL); } /*---------------------------------------------------------------------------* * return address of ACTIVE config entry by controller and channel *---------------------------------------------------------------------------*/ struct cfg_entry * get_cep_by_cc(int ctrlr, int chan) { struct cfg_entry *cep; struct isdn_ctrl_state *cts; cts = find_ctrl_state(ctrlr); if (cts == NULL) return(NULL); if (chan < 0 || chan >= cts->nbch) return(NULL); SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { if ((cep->cdid != CDID_UNUSED) && (cep->cdid != CDID_RESERVED) && (cep->isdnchannelused == chan) && (cep->isdncontrollerused == ctrlr) && ((ret_channel_state(cts, chan)) == CHAN_RUN)) { return (cep); } } return(NULL); } /*---------------------------------------------------------------------------* * return address of config entry identified by cdid *---------------------------------------------------------------------------*/ struct cfg_entry * get_cep_by_cdid(int cdid) { struct cfg_entry *cep; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { if (cep->cdid == cdid || cep->saved_call.cdid == cdid) return(cep); } return(NULL); } /*---------------------------------------------------------------------------* * process AOCD charging messages *---------------------------------------------------------------------------*/ void handle_charge(struct cfg_entry *cep) { time_t now = time(NULL); if (cep->aoc_last == 0) /* no last timestamp yet ? */ { cep->aoc_last = now; /* add time stamp */ } else if (cep->aoc_now == 0) /* no current timestamp yet ? */ { cep->aoc_now = now; /* current timestamp */ } else { cep->aoc_last = cep->aoc_now; cep->aoc_now = now; cep->aoc_diff = cep->aoc_now - cep->aoc_last; cep->aoc_valid = AOC_VALID; } #ifdef USE_CURSES if (do_fullscreen) display_charge(cep); #endif #ifdef I4B_EXTERNAL_MONITOR if (do_monitor && accepted) monitor_evnt_charge(cep, cep->charge, 0); #endif if (cep->aoc_valid == AOC_VALID) { if (cep->aoc_diff != cep->unitlength) { DBGL(DL_MSG, (logit(LL_DBG, "handle_charge: AOCD unit length updated %d -> %jd secs", cep->unitlength, (intmax_t)cep->aoc_diff))); cep->unitlength = cep->aoc_diff; unitlen_chkupd(cep); } else { #ifdef NOTDEF DBGL(DL_MSG, (logit(LL_DBG, "handle_charge: AOCD unit length still %d secs", cep->unitlength))); #endif } } } /*---------------------------------------------------------------------------* * update kernel idle_time, earlyhup_time and unitlen_time *---------------------------------------------------------------------------*/ void unitlen_chkupd(struct cfg_entry *cep) { msg_timeout_upd_t tupd; tupd.cdid = cep->cdid; /* init the short hold data based on the shorthold algorithm type */ switch (cep->shorthold_algorithm) { case SHA_FIXU: tupd.shorthold_data.shorthold_algorithm = SHA_FIXU; tupd.shorthold_data.unitlen_time = cep->unitlength; tupd.shorthold_data.idle_time = cep->idle_time_out; tupd.shorthold_data.earlyhup_time = cep->earlyhangup; break; case SHA_VARU: tupd.shorthold_data.shorthold_algorithm = SHA_VARU; tupd.shorthold_data.unitlen_time = cep->unitlength; tupd.shorthold_data.idle_time = cep->idle_time_out; tupd.shorthold_data.earlyhup_time = 0; break; default: logit(LL_ERR, "unitlen_chkupd bad shorthold_algorithm %d", cep->shorthold_algorithm ); return; break; } if ((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0) { logit(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno)); error_exit(1, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno)); } } /*--------------------------------------------------------------------------* * this is intended to be called by do_exit and closes down all * active connections before the daemon exits or is reconfigured. *--------------------------------------------------------------------------*/ void close_allactive(void) { int i, j; struct cfg_entry *cep = NULL; struct isdn_ctrl_state *cst; j = 0; SLIST_FOREACH(cst, &isdn_ctrl_list, ctrlq) { if ((get_controller_state(cst)) != CTRL_UP) continue; for (i = 0; i < cst->nbch; i++) { if ((ret_channel_state(cst, i)) == CHAN_RUN) { if ((cep = get_cep_by_cc(cst->isdnif, i)) != NULL) { #ifdef USE_CURSES if (do_fullscreen) display_disconnect(cep); #endif #ifdef I4B_EXTERNAL_MONITOR monitor_evnt_disconnect(cep); #endif next_state(cep, EV_DRQ); j++; } } } } if (j) { logit(LL_DMN, "close_allactive: waiting for all connections terminated"); sleep(5); } SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) { if (cep->autoupdown & AUTOUPDOWN_DONE) { struct ifreq ifr; int r, s; s = socket(AF_INET, SOCK_DGRAM, 0); memset(&ifr, 0, sizeof ifr); snprintf(ifr.ifr_name, sizeof ifr.ifr_name, "%s%d", cep->usrdevicename, cep->usrdeviceunit); r = ioctl(s, SIOCGIFFLAGS, &ifr); if (r >= 0) { ifr.ifr_flags &= ~IFF_UP; ioctl(s, SIOCSIFFLAGS, &ifr); } close(s); cep->autoupdown &= ~AUTOUPDOWN_DONE; } } } /*--------------------------------------------------------------------------* * set an interface up *--------------------------------------------------------------------------*/ void if_up(struct cfg_entry *cep) { msg_updown_ind_t mui; /* set interface up */ DBGL(DL_MSG, (logit(LL_DBG, "if_up: taking %s%d up", cep->usrdevicename, cep->usrdeviceunit))); mui.driver = cep->usrdevice; mui.driver_unit = cep->usrdeviceunit; mui.updown = SOFT_ENA; if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0) { logit(LL_ERR, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); error_exit(1, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); } cep->down_retry_count = 0; #ifdef USE_CURSES if (do_fullscreen) display_updown(cep, 1); #endif #ifdef I4B_EXTERNAL_MONITOR monitor_evnt_updown(cep, 1); #endif } /*--------------------------------------------------------------------------* * set an interface down *--------------------------------------------------------------------------*/ void if_down(struct cfg_entry *cep) { msg_updown_ind_t mui; /* set interface up */ DBGL(DL_MSG, (logit(LL_DBG, "if_down: taking %s%d down", cep->usrdevicename, cep->usrdeviceunit))); mui.driver = cep->usrdevice; mui.driver_unit = cep->usrdeviceunit; mui.updown = SOFT_DIS; if ((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0) { logit(LL_ERR, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); error_exit(1, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno)); } cep->went_down_time = time(NULL); cep->down_retry_count = 0; #ifdef USE_CURSES if (do_fullscreen) display_updown(cep, 0); #endif #ifdef I4B_EXTERNAL_MONITOR monitor_evnt_updown(cep, 0); #endif } /*--------------------------------------------------------------------------* * send a dial response to (an interface in) the kernel *--------------------------------------------------------------------------*/ void dialresponse(struct cfg_entry *cep, int dstat) { msg_dialout_resp_t mdr; static const char *stattab[] = { "normal condition", "temporary failure", "permanent failure", "dialout not allowed" }; if (dstat < DSTAT_NONE || dstat > DSTAT_INONLY) { logit(LL_ERR, "dialresponse: dstat out of range %d!", dstat); return; } mdr.driver = cep->usrdevice; mdr.driver_unit = cep->usrdeviceunit; mdr.stat = dstat; mdr.cause = cep->disc_cause; if ((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0) { logit(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno)); error_exit(1, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno)); } DBGL(DL_DRVR, (logit(LL_DBG, "dialresponse: sent [%s]", stattab[dstat]))); } /*--------------------------------------------------------------------------* * screening/presentation indicator *--------------------------------------------------------------------------*/ void handle_scrprs(int cdid, int scr, int prs, const char *caller) { /* screening indicator */ if (scr < SCR_NONE || scr > SCR_NET) { logit(LL_ERR, "msg_connect_ind: invalid screening indicator value %d!", scr); } else { static const char *scrtab[] = { "no screening indicator", "sreening user provided, not screened", "screening user provided, verified & passed", "screening user provided, verified & failed", "screening network provided", }; if (extcallattr) { logit(LL_CHD, "%05d %s %s", cdid, caller, scrtab[scr]); } else { DBGL(DL_MSG, (logit(LL_DBG, "%s - %s", caller, scrtab[scr]))); } } /* presentation indicator */ if (prs < PRS_NONE || prs > PRS_RESERVED) { logit(LL_ERR, "msg_connect_ind: invalid presentation indicator value %d!", prs); } else { static const char *prstab[] = { "no presentation indicator", "presentation allowed", "presentation restricted", "number not available due to interworking", "reserved presentation value" }; if (extcallattr) { logit(LL_CHD, "%05d %s %s", cdid, caller, prstab[prs]); } else { DBGL(DL_MSG, (logit(LL_DBG, "%s - %s", caller, prstab[prs]))); } } } /*--------------------------------------------------------------------------* * check if the time is valid for an entry *--------------------------------------------------------------------------*/ static int isvalidtime(struct cfg_entry *cep) { time_t t; struct tm *tp; if (cep->day == 0) return(1); t = time(NULL); tp = localtime(&t); if (cep->day & HD) { if (isholiday(tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900)) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: holiday %d.%d.%d", tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900))); goto dayok; } } if (cep->day & (1 << tp->tm_wday)) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: day match"))); goto dayok; } return(0); dayok: if (cep->fromhr==0 && cep->frommin==0 && cep->tohr==0 && cep->tomin==0) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: no time specified, match!"))); return(1); } if (cep->tohr < cep->fromhr) { /* before 00:00 */ if ( (tp->tm_hour > cep->fromhr) || (tp->tm_hour == cep->fromhr && tp->tm_min > cep->frommin) ) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: tfromhr, cep->frommin, cep->tohr, cep->tomin, tp->tm_hour, tp->tm_min))); return(1); } /* after 00:00 */ if ( (tp->tm_hour < cep->tohr) || (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) ) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: tfromhr, cep->frommin, cep->tohr, cep->tomin, tp->tm_hour, tp->tm_min))); return(1); } } else if (cep->fromhr == cep->tohr) { if (tp->tm_min >= cep->frommin && tp->tm_min < cep->tomin) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: f=t, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!", cep->fromhr, cep->frommin, cep->tohr, cep->tomin, tp->tm_hour, tp->tm_min))); return(1); } } else { if ((tp->tm_hour > cep->fromhr && tp->tm_hour < cep->tohr) || (tp->tm_hour == cep->fromhr && tp->tm_min >= cep->frommin) || (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) ) { DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: t>f, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!", cep->fromhr, cep->frommin, cep->tohr, cep->tomin, tp->tm_hour, tp->tm_min))); return(1); } } DBGL(DL_MSG, (logit(LL_DBG, "isvalidtime: spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, no match!", cep->fromhr, cep->frommin, cep->tohr, cep->tomin, tp->tm_hour, tp->tm_min))); return(0); } struct cfg_entry * get_first_cfg_entry() { return (SIMPLEQ_FIRST(&cfg_entry_list)); } int count_cfg_entries() { int cnt; struct cfg_entry *cfe; cnt = 0; SIMPLEQ_FOREACH(cfe, &cfg_entry_list, cfgq) cnt++; return (cnt); } struct cfg_entry * find_cfg_entry(int idx) { struct cfg_entry *cep; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) if (cep->index == idx) return cep; return NULL; } int add_cfg_entry(struct cfg_entry *cfe) { struct cfg_entry *cep; int max = -1; SIMPLEQ_FOREACH(cep, &cfg_entry_list, cfgq) if (cep->index > max) max = cep->index; cfe->index = max; SIMPLEQ_INSERT_TAIL(&cfg_entry_list, cfe, cfgq); return max; } void remove_all_cfg_entries() { struct cfg_entry *cep; while (!SIMPLEQ_EMPTY(&cfg_entry_list)) { cep = SIMPLEQ_FIRST(&cfg_entry_list); SIMPLEQ_REMOVE_HEAD(&cfg_entry_list, cfgq); if (cep->ppp_expect_name) free(cep->ppp_expect_name); if (cep->ppp_expect_password) free(cep->ppp_expect_password); if (cep->ppp_send_name) free(cep->ppp_send_name); if (cep->ppp_send_password) free(cep->ppp_send_password); free(cep); } } struct isdn_ctrl_state * get_first_ctrl_state() { return SLIST_FIRST(&isdn_ctrl_list); } int count_ctrl_states() { int cnt = 0; struct isdn_ctrl_state *ctrl; SLIST_FOREACH(ctrl, &isdn_ctrl_list, ctrlq) cnt++; return (cnt); } void remove_all_ctrl_state() { struct isdn_ctrl_state *ctrl; while (!SLIST_EMPTY(&isdn_ctrl_list)) { ctrl = SLIST_FIRST(&isdn_ctrl_list); SLIST_REMOVE_HEAD(&isdn_ctrl_list, ctrlq); free(ctrl); } } struct isdn_ctrl_state * find_ctrl_state(int controller) { struct isdn_ctrl_state *ctrl; SLIST_FOREACH(ctrl, &isdn_ctrl_list, ctrlq) if (ctrl->isdnif == controller) return ctrl; return NULL; } int add_ctrl_state(struct isdn_ctrl_state *cstate) { SLIST_INSERT_HEAD(&isdn_ctrl_list, cstate, ctrlq); return 0; } int remove_ctrl_state(int controller) { struct isdn_ctrl_state *ctrl = find_ctrl_state(controller); struct cfg_entry *cep; int i; if (ctrl == NULL) return 0; if ((get_controller_state(ctrl)) == CTRL_UP) { for (i = 0; i < ctrl->nbch; i++) { if ((ret_channel_state(ctrl, i)) == CHAN_RUN) { if ((cep = get_cep_by_cc(controller, i)) != NULL) { #ifdef USE_CURSES if (do_fullscreen) display_disconnect(cep); #endif #ifdef I4B_EXTERNAL_MONITOR monitor_evnt_disconnect(cep); #endif cep->cdid = -1; cep->isdncontrollerused = -1; cep->isdnchannelused = -1; cep->state = ST_IDLE; } } } } SLIST_REMOVE(&isdn_ctrl_list, ctrl, isdn_ctrl_state, ctrlq); return 0; } /* EOF */