diff options
Diffstat (limited to 'usr.sbin/i4b/isdnd/monitor.c')
-rw-r--r-- | usr.sbin/i4b/isdnd/monitor.c | 811 |
1 files changed, 811 insertions, 0 deletions
diff --git a/usr.sbin/i4b/isdnd/monitor.c b/usr.sbin/i4b/isdnd/monitor.c new file mode 100644 index 0000000..884df13 --- /dev/null +++ b/usr.sbin/i4b/isdnd/monitor.c @@ -0,0 +1,811 @@ +/* + * Copyright (c) 1998 Martin Husemann. 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. + * 3. Neither the name of the author nor the names of any co-contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * 4. Altered versions must be plainly marked as such, and must not be + * misrepresented as being the original software and/or documentation. + * + * 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 - network monitor server module + * ------------------------------------------ + * + * $Id: monitor.c,v 1.6 1998/08/10 13:55:24 hm Exp $ + * + * last edit-date: [Fri Jul 17 18:07:00 1998] + * + * -mh created + * + *---------------------------------------------------------------------------*/ + +#include "isdnd.h" + +#ifndef I4B_EXTERNAL_MONITOR +/* dummy version of routines needed by config file parser + * (config files should be valid with and without external montioring + * support compiled into the daemon) */ +void monitor_clear_rights() +{ } +int monitor_start_rights(const char *clientspec) +{ return I4BMAR_OK; } +void monitor_add_rights(int rights_mask) +{ } +void monitor_fixup_rights() +{ } +#else + +#include "monitor.h" +#include "vararray.h" +#include <sys/socket.h> +#include <sys/un.h> +#ifndef I4B_NOTCPIP_MONITOR +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#endif + +VARA_DECL(struct monitor_rights) rights = VARA_INITIALIZER; +#define INITIAL_RIGHTS_ALLOC 10 /* most users will have one or two entries */ + +static int local_rights = -1; /* index of entry for local socket, -1 if none */ + +/* for each active monitor connection we have one of this: */ +struct monitor_connection { + int sock; /* socket for this connection */ + int rights; /* active rights for this connection */ + int events; /* bitmask of events client is interested in */ +}; +static VARA_DECL(struct monitor_connection) connections = VARA_INITIALIZER; +#define INITIAL_CONNECTIONS_ALLOC 30 /* high guess */ + +/* derive channel number from config pointer */ +#define CHNO(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused) + +/* local prototypes */ +static int cmp_rights(const void *a, const void *b); +static int monitor_command(int con_index, int fd, int rights); +static void cmd_dump_rights(int fd, int rights, BYTE *cmd); +static void cmd_dump_mcons(int fd, int rights, BYTE *cmd); +static void cmd_reread_cfg(int fd, int rights, BYTE *cmd); +static void cmd_hangup(int fd, int rights, BYTE *cmd); +static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes); +static int anybody(int mask); + +/* + * Due to the way we structure config files, the rights for an external + * monitor might be stated in multiple steps. First a call to + * monitor_start_rights opens an entry. Further (optional) calls to + * montior_add_rights assemble additional rights for this "current" + * entry. When closing the sys-file section of the config file, the + * "current" entry becomes invalid. + */ +static int cur_add_entry = -1; + +/* + * Initialize the monitor server module. This affects only active + * connections, the access rights are not modified here! + */ +void monitor_init() +{ + accepted = 0; + VARA_EMPTY(connections); +} + +/* + * Prepare for exit + */ +void monitor_exit() +{ + int i; + + /* Close all open connections. */ + VARA_FOREACH(connections, i) + close(VARA_AT(connections, i).sock); + + /* Remove their descriptions */ + VARA_EMPTY(connections); +} + +/* + * Initialize access rights. No active connections are affected! + */ +void monitor_clear_rights() +{ + VARA_EMPTY(rights); + cur_add_entry = -1; +} + +/* + * Add an entry to the access lists. The clientspec either is + * the name of the local socket or a host- or networkname or + * numeric ip/host-bit-len spec. + */ +int monitor_start_rights(const char *clientspec) +{ + int i; + struct monitor_rights r; + + /* initialize the new rights entry */ + memset(&r, 0, sizeof r); + + /* check clientspec */ + if (*clientspec == '/') { + struct sockaddr_un sa; + + /* this is a local socket spec, check if we already have one */ + if (VARA_VALID(rights, local_rights)) + return I4BMAR_DUP; + /* does it fit in a local socket address? */ + if (strlen(clientspec) > sizeof sa.sun_path) + return I4BMAR_LENGTH; + r.local = 1; + strcpy(r.name, clientspec); +#ifndef I4B_NOTCPIP_MONITOR + } else { + /* remote entry, parse host/net and cidr */ + char hostname[FILENAME_MAX]; + char *p; + p = strchr(clientspec, '/'); + if (!p) { + struct hostent *host; + u_int32_t hn; + /* must be a host spec */ + r.mask = ~0; + host = gethostbyname(clientspec); + if (!host) + return I4BMAR_NOIP; + memcpy(&hn, host->h_addr_list[0], sizeof hn); + r.net = (u_int32_t)ntohl(hn); + } else if (p[1]) { + /* must be net/cidr spec */ + int l; + struct netent *net; + u_int32_t s = ~0U; + int num = strtol(p+1, NULL, 10); + if (num < 0 || num > 32) + return I4BMAR_CIDR; + s >>= num; + s ^= ~0U; + l = p - clientspec; + if (l >= sizeof hostname) + return I4BMAR_LENGTH; + strncpy(hostname, clientspec, l); + hostname[l] = '\0'; + net = getnetbyname(hostname); + if (net == NULL) + r.net = (u_int32_t)inet_network(hostname); + else + r.net = (u_int32_t)net->n_net; + r.mask = s; + r.net &= s; + } else + return I4BMAR_CIDR; + + /* check for duplicate entry */ + VARA_FOREACH(rights, i) + if (VARA_AT(rights, i).mask == r.mask && + VARA_AT(rights, i).net == r.net && + VARA_AT(rights, i).local == r.local) + return I4BMAR_DUP; +#endif + } + r.rights = 0; + + /* entry ok, add it to the collection */ + cur_add_entry = i = VARA_NUM(rights); + VARA_ADD_AT(rights, i, struct monitor_rights, INITIAL_RIGHTS_ALLOC); + memcpy(&VARA_AT(rights, i), &r, sizeof r); + if (r.local) + local_rights = i; + + log(LL_DBG, "system: monitor = %s", clientspec); + + return I4BMAR_OK; +} + +/* + * Add rights to the currently constructed entry - if any. + */ +void monitor_add_rights(int rights_mask) +{ + if (cur_add_entry < 0) return; /* noone under construction */ + + VARA_AT(rights, cur_add_entry).rights |= rights_mask; + + log(LL_DBG, "system: monitor-access = 0x%x", rights_mask); +} + +/* + * All rights have been added now. Sort the to get most specific + * host/net masks first, so we can travel the list and use the first + * match for actual rights. + */ +void monitor_fixup_rights() +{ + int i; + + /* no more rights may be added to the current entry */ + cur_add_entry = -1; + + /* sort the rights array */ + qsort(VARA_PTR(rights), VARA_NUM(rights), sizeof(struct monitor_rights), cmp_rights); + + /* now the local entry may have moved, update its index */ + if (VARA_VALID(rights, local_rights)) { + local_rights = -1; + VARA_FOREACH(rights, i) { + if (VARA_AT(rights, i).local) { + local_rights = i; + break; + } + } + } +} + +/* comparator for rights */ +static int cmp_rights(const void *a, const void *b) +{ + u_int32_t mask; + struct monitor_rights const * pa = (struct monitor_rights const*)a; + struct monitor_rights const * pb = (struct monitor_rights const*)b; + + /* local sorts first */ + if (pa->local) + return -1; + + /* which is the less specific netmask? */ + mask = pa->mask; + if ((pb->mask & mask) == 0) + mask = pb->mask; + /* are the entries disjunct? */ + if ((pa->net & mask) != (pb->net & mask)) { + /* simply compare net part of address */ + return ((pa->net & mask) < (pb->net & mask)) ? -1 : 1; + } + /* One entry is part of the others net. We already now "mask" is + * the netmask of the less specific (i.e. greater) one */ + return (pa->mask == mask) ? 1 : -1; +} + +#ifndef I4B_NOTCPIP_MONITOR +/* + * Check if access rights for a remote socket are specified and + * create this socket. Return -1 otherwise. + */ +int monitor_create_remote_socket(int portno) +{ + struct sockaddr_in sa; + int val; + int remotesockfd = socket(AF_INET, SOCK_STREAM, 0); + if (remotesockfd == -1) { + log(LL_ERR, "could not create remote monitor socket, errno = %d", errno); + exit(1); + } + val = 1; + if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) { + log(LL_ERR, "could not setsockopt, errno = %d", errno); + exit(1); + } + memset(&sa, 0, sizeof sa); + sa.sin_len = sizeof sa; + sa.sin_family = AF_INET; + sa.sin_port = htons(portno); + sa.sin_addr.s_addr = htonl(INADDR_ANY); + if (bind(remotesockfd, (struct sockaddr *)&sa, sizeof sa) == -1) { + log(LL_ERR, "could not bind remote monitor socket to port %d, errno = %d", portno, errno); + exit(1); + } + if (listen(remotesockfd, 0)) { + log(LL_ERR, "could not listen on monitor socket, errno = %d", errno); + exit(1); + } + + return remotesockfd; +} +#endif + +/* + * Check if access rights for a local socket are specified and + * create this socket. Return -1 otherwise. + */ +int monitor_create_local_socket() +{ + int s; + struct sockaddr_un sa; + + /* check for a local entry */ + if (!VARA_VALID(rights, local_rights)) + return -1; + + /* create and setup socket */ + s = socket(AF_LOCAL, SOCK_STREAM, 0); + if (s == -1) { + log(LL_ERR, "could not create local monitor socket, errno = %d", errno); + exit(1); + } + unlink(VARA_AT(rights, local_rights).name); + memset(&sa, 0, sizeof sa); + sa.sun_len = sizeof sa; + sa.sun_family = AF_LOCAL; + strcpy(sa.sun_path, VARA_AT(rights, local_rights).name); + if (bind(s, (struct sockaddr *)&sa, sizeof sa)) { + log(LL_ERR, "could not bind local monitor socket [%s], errno = %d", VARA_AT(rights, local_rights).name, errno); + exit(1); + } + if (listen(s, 0)) { + log(LL_ERR, "could not listen on local monitor socket, errno = %d", errno); + exit(1); + } + + return s; +} + +/* + * Prepare a fd_set for a select call. Add all our local + * filedescriptors to the set, increment max_fd if appropriate. + */ +void monitor_prepselect(fd_set *selset, int *max_fd) +{ + int i; + + VARA_FOREACH(connections, i) { + int fd = VARA_AT(connections, i).sock; + if (fd > *max_fd) + *max_fd = fd; + FD_SET(fd, selset); + } +} + +/* + * Check if the result from a select call indicates something + * to do for us. + */ +void monitor_handle_input(fd_set *selset) +{ + int i; + + VARA_FOREACH(connections, i) { + int fd = VARA_AT(connections, i).sock; + if (FD_ISSET(fd, selset)) { + /* handle command from this client */ + if (monitor_command(i, fd, VARA_AT(connections, i).rights) != 0) { + /* broken or closed connection */ + log(LL_DBG, "monitor connection #%d closed", i); + VARA_REMOVEAT(connections, i); + i--; + } + } + } + + /* all connections gone? */ + if (VARA_NUM(connections) == 0) + accepted = 0; +} + +/* + * Try new incoming connection on the given socket. + * Setup client descriptor and send initial data. + */ +void monitor_handle_connect(int sockfd, int is_local) +{ + struct monitor_connection *con; +#ifndef I4B_NOTCPIP_MONITOR + struct sockaddr_in ia; + u_int32_t ha = 0; +#endif + struct sockaddr_un ua; + BYTE idata[I4B_MON_IDATA_SIZE]; + int fd = -1, s, i, r_mask; + char source[FILENAME_MAX]; + + /* accept the connection */ + if (is_local) { + s = sizeof ua; + fd = accept(sockfd, (struct sockaddr *)&ua, &s); + strcpy(source, "local connection"); +#ifndef I4B_NOTCPIP_MONITOR + } else { + s = sizeof ia; + fd = accept(sockfd, (struct sockaddr *)&ia, &s); + snprintf(source, sizeof source, "tcp/ip connection from %s\n", + inet_ntoa(ia.sin_addr)); + memcpy(&ha, &ia.sin_addr.s_addr, sizeof ha); + ha = ntohl(ha); +#endif + } + + /* check the access rights of this connection */ + r_mask = 0; + VARA_FOREACH(rights, i) { + struct monitor_rights *r = &VARA_AT(rights, i); + if (r->local) { + if (is_local) { + r_mask = r->rights; + break; + } +#ifndef I4B_NOTCPIP_MONITOR + } else { + if ((ha & r->mask) == r->net) { + r_mask = r->rights; + break; + } +#endif + } + } + + if (r_mask == 0) { + /* no rights - go away */ + log(LL_DBG, "monitor access denied: %s", source); + close(fd); + return; + } + + accepted = 1; + i = VARA_NUM(connections); + VARA_ADD_AT(connections, i, struct monitor_connection, INITIAL_CONNECTIONS_ALLOC); + con = &VARA_AT(connections, i); + memset(con, 0, sizeof *con); + con->sock = fd; + con->rights = r_mask; + log(LL_DBG, "monitor access granted, rights = %x, #%d, %s", + r_mask, i, source); + + /* send initial data */ + I4B_PREP_CMD(idata, I4B_MON_IDATA_CODE); + I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMAJOR, MPROT_VERSION); + I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMINOR, MPROT_REL); + I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, ncontroller); + I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask); + write(fd, idata, sizeof idata); + + for (i = 0; i < ncontroller; i++) { + BYTE ictrl[I4B_MON_ICTRL_SIZE]; + I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE); + I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, name_of_controller(isdn_ctrl_tab[i].ctrl_type, isdn_ctrl_tab[i].card_type)); + I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, 0); + I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0); + I4B_PUT_4B(ictrl, I4B_MON_ICTRL_NCHAN, 2); + write(fd, ictrl, sizeof ictrl); + } +} + +/* dump all monitor rights */ +static void cmd_dump_rights(int fd, int r_mask, BYTE *cmd) +{ + int i; + BYTE drini[I4B_MON_DRINI_SIZE]; + BYTE dr[I4B_MON_DR_SIZE]; + + I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE); + I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, VARA_NUM(rights)); + write(fd, drini, sizeof drini); + + VARA_FOREACH(rights, i) { + I4B_PREP_EVNT(dr, I4B_MON_DR_CODE); + I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, VARA_AT(rights, i).rights); + I4B_PUT_4B(dr, I4B_MON_DR_NET, VARA_AT(rights, i).net); + I4B_PUT_4B(dr, I4B_MON_DR_MASK, VARA_AT(rights, i).mask); + I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, VARA_AT(rights, i).local); + write(fd, dr, sizeof dr); + } +} + +/* rescan config file */ +static void cmd_reread_cfg(int fd, int rights, BYTE *cmd) +{ + rereadconfig(42); +} + +/* drop one connection */ +static void cmd_hangup(int fd, int rights, BYTE *cmd) +{ + int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL); + hangup_channel(channel); +} + +/* dump all active monitor connections */ +static void cmd_dump_mcons(int fd, int rights, BYTE *cmd) +{ + int i; + BYTE dcini[I4B_MON_DCINI_SIZE]; + + I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE); + I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, VARA_NUM(connections)); + write(fd, dcini, sizeof dcini); + + VARA_FOREACH(connections, i) { +#ifndef I4B_NOTCPIP_MONITOR + int namelen; + struct sockaddr_in name; +#endif + BYTE dc[I4B_MON_DC_SIZE]; + + I4B_PREP_EVNT(dc, I4B_MON_DC_CODE); + I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, VARA_AT(connections, i).rights); +#ifndef I4B_NOTCPIP_MONITOR + namelen = sizeof name; + if (getpeername(VARA_AT(connections, i).sock, (struct sockaddr*)&name, &namelen) == 0) + memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr); +#endif + write(fd, dc, sizeof dc); + } +} + +/* + * Handle a command from the given socket. The client + * has rights as specified in the rights parameter. + * Return non-zero if connection is closed. + */ +static int monitor_command(int con_index, int fd, int rights) +{ + char cmd[I4B_MAX_MON_CLIENT_CMD]; + u_int code; + /* command dispatch table */ + typedef void (*cmd_func_t)(int fd, int rights, BYTE *cmd); + static struct { + cmd_func_t call; /* function to execute */ + u_int rights; /* necessary rights */ + } cmd_tab[] = + { + /* 0 */ { NULL, 0 }, + /* 1 */ { cmd_dump_rights, I4B_CA_COMMAND_FULL }, + /* 2 */ { cmd_dump_mcons, I4B_CA_COMMAND_FULL }, + /* 3 */ { cmd_reread_cfg, I4B_CA_COMMAND_FULL }, + /* 4 */ { cmd_hangup, I4B_CA_COMMAND_FULL }, + }; +#define NUMCMD (sizeof cmd_tab / sizeof cmd_tab[0]) + + u_long u; + int bytes; + + /* Network transfer may deliver two or more packets concatenated. + * Peek at the header and read only one event at a time... */ + ioctl(fd, FIONREAD, &u); + if (u < I4B_MON_CMD_HDR) { + if (u == 0) { + /* socket closed by peer */ + close(fd); + return 1; + } + return 0; /* not enough data there yet */ + } + bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK); + if (bytes < I4B_MON_CMD_HDR) + return 0; /* errh? something must be wrong... */ + bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN); + if (bytes >= sizeof cmd) { + close(fd); + log(LL_ERR, "garbage on monitor connection #%d, closing it", con_index); + return 1; + } + /* now we know the size, it fits, so lets read it! */ + if (read(fd, cmd, bytes) <= 0) { + close(fd); + return 1; + } + + /* decode command */ + code = I4B_GET_2B(cmd, I4B_MON_CMD); + + /* special case: may modify our connection descriptor, is + * beyound all rights checks */ + if (code == I4B_MON_CCMD_SETMASK) { + /* + u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR); + u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR); + */ + int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS); + VARA_AT(connections, con_index).events = events & rights; + return 0; + } + + if (code < 0 || code >= NUMCMD) { + log(LL_ERR, "illegal command from client #%d: code = %d\n", + con_index, code); + return 0; + } + if (cmd_tab[code].call == NULL) + return 0; + if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights) + cmd_tab[code].call(fd, rights, cmd); + + return 0; +} + +/* + * Check if somebody would receive an event with this mask. + * We are lazy and try to avoid assembling unneccesary packets. + * Return 0 if no one interested, nonzero otherwise. + */ +static int anybody(int mask) +{ + int i; + + VARA_FOREACH(connections, i) + if ((VARA_AT(connections, i).events & mask) == mask) + return 1; + + return 0; +} + +/* + * Send an event to every connection interested in this kind of + * event + */ +static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes) +{ + int i; + + VARA_FOREACH(connections, i) { + if ((VARA_AT(connections, i).events & mask) == mask) { + int fd = VARA_AT(connections, i).sock; + write(fd, pkt, bytes); + } + } +} + +/* + * Post a logfile event + */ +void monitor_evnt_log(int prio, const char * what, const char * msg) +{ + BYTE evnt[I4B_MON_LOGEVNT_SIZE]; + time_t now; + + if (!anybody(I4B_CA_EVNT_I4B)) return; + + time(&now); + I4B_PREP_EVNT(evnt, I4B_MON_LOGEVNT_CODE); + I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_TSTAMP, (long)now); + I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_PRIO, prio); + I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_WHAT, what); + I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_MSG, msg); + + monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt); +} + +/* + * Post a charging event on the connection described + * by the given config entry. + */ +void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate) +{ + int chno = CHNO(cep); + int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; + time_t now; + BYTE evnt[I4B_MON_CHRG_SIZE]; + + if (!anybody(mask)) return; + + time(&now); + I4B_PREP_EVNT(evnt, I4B_MON_CHRG_CODE); + I4B_PUT_4B(evnt, I4B_MON_CHRG_TSTAMP, (long)now); + I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, chno); + I4B_PUT_4B(evnt, I4B_MON_CHRG_UNITS, units); + I4B_PUT_4B(evnt, I4B_MON_CHRG_ESTIMATED, estimate ? 1 : 0); + + monitor_broadcast(mask, evnt, sizeof evnt); +} + +/* + * Post a connection event + */ +void monitor_evnt_connect(cfg_entry_t *cep) +{ + BYTE evnt[I4B_MON_CONNECT_SIZE]; + char devname[I4B_MAX_MON_STRING]; + int chno = CHNO(cep); + int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; + time_t now; + + if (!anybody(mask)) return; + + time(&now); + snprintf(devname, sizeof devname, "%s%d", bdrivername(cep->usrdevicename), cep->usrdeviceunit); + I4B_PREP_EVNT(evnt, I4B_MON_CONNECT_CODE); + I4B_PUT_4B(evnt, I4B_MON_CONNECT_TSTAMP, (long)now); + I4B_PUT_4B(evnt, I4B_MON_CONNECT_DIR, cep->direction == DIR_OUT ? 1 : 0); + I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, chno); + I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name); + I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname); + I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming); + I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->remote_phone_dialout); + + monitor_broadcast(mask, evnt, sizeof evnt); +} + +/* + * Post a disconnect event + */ +void monitor_evnt_disconnect(cfg_entry_t *cep) +{ + BYTE evnt[I4B_MON_DISCONNECT_SIZE]; + int chno = CHNO(cep); + int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; + time_t now; + + if (!anybody(mask)) return; + + time(&now); + I4B_PREP_EVNT(evnt, I4B_MON_DISCONNECT_CODE); + I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_TSTAMP, (long)now); + I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, chno); + + monitor_broadcast(mask, evnt, sizeof evnt); +} + +/* + * Post an up/down event + */ +void monitor_evnt_updown(cfg_entry_t *cep, int up) +{ + BYTE evnt[I4B_MON_UPDOWN_SIZE]; + int chno = CHNO(cep); + int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; + time_t now; + + if (!anybody(mask)) return; + + time(&now); + I4B_PREP_EVNT(evnt, I4B_MON_UPDOWN_CODE); + I4B_PUT_4B(evnt, I4B_MON_UPDOWN_TSTAMP, (long)now); + I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, chno); + I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up); + + monitor_broadcast(mask, evnt, sizeof evnt); +} + +void hangup_channel(int channel) +{ + int i; + cfg_entry_t * cep = NULL; + + for (i = 0; i < ncontroller; i++) + { + if(isdn_ctrl_tab[i].state != CTRL_UP) + continue; + if(isdn_ctrl_tab[i].stateb1 != CHAN_IDLE) + { + cep = get_cep_by_cc(i, 0); + if (cep != NULL && CHNO(cep) == channel) + goto found; + } + if(isdn_ctrl_tab[i].stateb2 != CHAN_IDLE) + { + cep = get_cep_by_cc(i, 1); + if (cep != NULL && CHNO(cep) == channel) + goto found; + } + } + /* not found */ + return; + +found: + cep->hangup = 1; + return; +} + +#endif /* I4B_EXTERNAL_MONITOR */ |