/* $NetBSD: monitor.c,v 1.16 2009/04/16 05:56:32 lukem Exp $ */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Martin Husemann . * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ #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 #include #ifndef I4B_NOTCPIP_MONITOR #include #include #include #endif static TAILQ_HEAD(rights_q, monitor_rights) rights = TAILQ_HEAD_INITIALIZER(rights); static struct monitor_rights * local_rights = NULL; /* entry for local socket */ /* for each active monitor connection we have one of this: */ struct monitor_connection { TAILQ_ENTRY(monitor_connection) connections; int sock; /* socket for this connection */ int rights; /* active rights for this connection */ int events; /* bitmask of events client is interested in */ char source[FILENAME_MAX]; }; static TAILQ_HEAD(connections_tq, monitor_connection) connections = TAILQ_HEAD_INITIALIZER(connections); /* local prototypes */ struct monitor_rights * monitor_next_rights(const struct monitor_rights *r); static int cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb); static int monitor_command(struct monitor_connection *con, int fd, int rights); static void cmd_dump_rights(int fd, int rights, u_int8_t *cmd, const char * source); static void cmd_dump_mcons(int fd, int rights, u_int8_t *cmd, const char * source); static void cmd_reread_cfg(int fd, int rights, u_int8_t *cmd, const char * source); static void cmd_hangup(int fd, int rights, u_int8_t *cmd, const char * source); static void monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes); static int anybody(int mask); static void hangup_channel(int controller, int channel, const char *source); static ssize_t sock_read(int fd, void *buf, size_t nbytes); static ssize_t sock_write(int fd, void *buf, size_t nbytes); /* * 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 struct monitor_rights * cur_add_entry = NULL; /*--------------------------------------------------------------------------- * Initialize the monitor server module. This affects only active * connections, the access rights are not modified here! *---------------------------------------------------------------------------*/ void monitor_init(void) { struct monitor_connection * con; accepted = 0; while ((con = TAILQ_FIRST(&connections)) != NULL) { TAILQ_REMOVE(&connections, con, connections); free(con); } } /*--------------------------------------------------------------------------- * Prepare for exit *---------------------------------------------------------------------------*/ void monitor_exit(void) { struct monitor_connection *c; /* Close all open connections. */ while((c = TAILQ_FIRST(&connections)) != NULL) { close(c->sock); TAILQ_REMOVE(&connections, c, connections); free(c); } } /*--------------------------------------------------------------------------- * Initialize access rights. No active connections are affected! *---------------------------------------------------------------------------*/ void monitor_clear_rights(void) { struct monitor_rights *r; while ((r = TAILQ_FIRST(&rights)) != NULL) { TAILQ_REMOVE(&rights, r, list); free(r); } cur_add_entry = NULL; local_rights = NULL; } /*--------------------------------------------------------------------------- * 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) { 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 (local_rights != NULL) return I4BMAR_DUP; /* does it fit in a local socket address? */ if (strlen(clientspec) > sizeof sa.sun_path) return I4BMAR_LENGTH; r.local = 1; strlcpy(r.name, clientspec, sizeof(r.name)); #ifndef I4B_NOTCPIP_MONITOR } else { /* remote entry, parse host/net and cidr */ struct monitor_rights * rp; 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 >= (int)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 */ for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list)) { if (rp->mask == r.mask && rp->net == r.net && rp->local == r.local) { return I4BMAR_DUP; } } #endif } r.rights = 0; /* entry ok, add it to the collection */ cur_add_entry = malloc(sizeof(r)); memcpy(cur_add_entry, &r, sizeof(r)); TAILQ_INSERT_TAIL(&rights, cur_add_entry, list); if (r.local) local_rights = cur_add_entry; DBGL(DL_RCCF, (logit(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 == NULL) return; /* noone under construction */ cur_add_entry->rights |= rights_mask; DBGL(DL_RCCF, (logit(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(void) { struct monitor_rights * cur, * test, * next; /* no more rights may be added to the current entry */ cur_add_entry = NULL; /* sort the rights */ for (next = NULL, cur = TAILQ_FIRST(&rights); cur != NULL; cur = next) { next = TAILQ_NEXT(cur, list); for (test = TAILQ_FIRST(&rights); test != NULL && test != cur; test = TAILQ_NEXT(test, list)) { if (cmp_rights(cur, test) > 0) { /* move cur up the list and insert before test */ TAILQ_REMOVE(&rights, cur, list); if (test == TAILQ_FIRST(&rights)) TAILQ_INSERT_HEAD(&rights, cur, list); else TAILQ_INSERT_BEFORE(test, cur, list); break; } } } } /*--------------------------------------------------------------------------- * comparator for rights *---------------------------------------------------------------------------*/ static int cmp_rights(const struct monitor_rights *pa, const struct monitor_rights *pb) { u_int32_t mask; /* 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; remotesockfd = socket(AF_INET, SOCK_STREAM, 0); if (remotesockfd == -1) { logit(LL_MER, "could not create remote monitor socket: %s", strerror(errno)); return(-1); } val = 1; if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) { logit(LL_MER, "could not setsockopt: %s", strerror(errno)); return(-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) { logit(LL_MER, "could not bind remote monitor socket to port %d: %s", portno, strerror(errno)); return(-1); } if (listen(remotesockfd, 0)) { logit(LL_MER, "could not listen on monitor socket: %s", strerror(errno)); return(-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(void) { int s; struct sockaddr_un sa; /* check for a local entry */ if (local_rights == NULL) return(-1); /* create and setup socket */ s = socket(AF_LOCAL, SOCK_STREAM, 0); if (s == -1) { logit(LL_MER, "could not create local monitor socket, errno = %d", errno); return(-1); } unlink(local_rights->name); memset(&sa, 0, sizeof sa); sa.sun_len = sizeof sa; sa.sun_family = AF_LOCAL; strlcpy(sa.sun_path, local_rights->name, sizeof(sa.sun_path)); if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa))) { logit(LL_MER, "could not bind local monitor socket [%s], errno = %d", local_rights->name, errno); return(-1); } chmod(local_rights->name, 0600); if (listen(s, 0)) { logit(LL_MER, "could not listen on local monitor socket, errno = %d", errno); return(-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) { struct monitor_connection * con; for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections)) { int fd = con->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) { struct monitor_connection * con, * next; for (next = NULL, con = TAILQ_FIRST(&connections); con != NULL; con = next) { int fd = con->sock; next = TAILQ_NEXT(con, connections); if (FD_ISSET(fd, selset)) { /* handle command from this client */ if (monitor_command(con, fd, con->rights) != 0) { /* broken or closed connection */ char source[FILENAME_MAX]; strlcpy(source, con->source, sizeof(source)); TAILQ_REMOVE(&connections, con, connections); free(con); logit(LL_DMN, "monitor closed from %s", source ); } } } /* all connections gone? */ if (TAILQ_FIRST(&connections) == NULL) 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; struct monitor_rights *rp; struct isdn_ctrl_state *ctrl; struct cfg_entry *cfe; int n; #ifndef I4B_NOTCPIP_MONITOR struct sockaddr_in ia; u_int32_t ha = 0; #endif struct sockaddr_un ua; socklen_t s; u_int8_t idata[I4B_MON_IDATA_SIZE]; int fd = -1, r_mask, t_events; char source[FILENAME_MAX]; /* accept the connection */ if (is_local) { s = sizeof ua; fd = accept(sockfd, (struct sockaddr *)&ua, &s); strlcpy(source, "local", sizeof(source)); #ifndef I4B_NOTCPIP_MONITOR } else { struct hostent *hp; s = sizeof ia; fd = accept(sockfd, (struct sockaddr *)&ia, &s); hp = gethostbyaddr((char *)&ia.sin_addr, 4, AF_INET); if (hp == NULL) snprintf(source, sizeof source, "%s (%s)", inet_ntoa(ia.sin_addr), inet_ntoa(ia.sin_addr)); else snprintf(source, sizeof source, "%s (%s)", hp->h_name, 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; for (rp = TAILQ_FIRST(&rights); rp != NULL; rp = TAILQ_NEXT(rp, list)) { if (rp->local) { if (is_local) { r_mask = rp->rights; break; } #ifndef I4B_NOTCPIP_MONITOR } else { if ((ha & rp->mask) == rp->net) { r_mask = rp->rights; break; } #endif } } if (r_mask == 0) { /* no rights - go away */ logit(LL_MER, "monitor access denied from %s", source); close(fd); return; } accepted = 1; con = malloc(sizeof(struct monitor_connection)); memset(con, 0, sizeof *con); TAILQ_INSERT_TAIL(&connections, con, connections); con->sock = fd; con->rights = r_mask; strlcpy(con->source, source, sizeof(con->source)); logit(LL_DMN, "monitor opened from %s rights 0x%x", source, r_mask); /* 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); n = count_ctrl_states(); I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, n); n = count_cfg_entries(); I4B_PUT_2B(idata, I4B_MON_IDATA_NUMENTR, n); I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask); if ((sock_write(fd, idata, sizeof idata)) == -1) { logit(LL_MER, "monitor_handle_connect: sock_write 1 error - %s", strerror(errno)); } for (ctrl = get_first_ctrl_state(); ctrl; ctrl = NEXT_CTRL(ctrl)) { u_int8_t ictrl[I4B_MON_ICTRL_SIZE]; char ctrl_desc[100]; snprintf(ctrl_desc, sizeof(ctrl_desc), "%s: %s", ctrl->device_name, ctrl->controller); I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE); I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, ctrl_desc); I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, ctrl->isdnif); I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0); I4B_PUT_2B(ictrl, I4B_MON_ICTRL_NCHAN, 2); if ((sock_write(fd, ictrl, sizeof ictrl)) == -1) { logit(LL_MER, "monitor_handle_connect: sock_write 2 error - %s", strerror(errno)); } } /* send device names from entries */ for (cfe = get_first_cfg_entry(); cfe; cfe = NEXT_CFE(cfe)) { u_int8_t ictrl[I4B_MON_IDEV_SIZE]; char nbuf[64]; snprintf(nbuf, sizeof(nbuf), "%s%d ", cfe->usrdevicename, cfe->usrdeviceunit); I4B_PREP_CMD(ictrl, I4B_MON_IDEV_CODE); /*XXX*/ I4B_PUT_2B(ictrl, I4B_MON_IDEV_STATE, 1); I4B_PUT_STR(ictrl, I4B_MON_IDEV_NAME, nbuf); if ((sock_write(fd, ictrl, sizeof ictrl)) == -1) { logit(LL_MER, "monitor_handle_connect: sock_write 3 error - %s", strerror(errno)); } } /*XXX*/ t_events = con->events; /*XXX*/ con->events = -1; /* current state of controller(s) */ for (ctrl = get_first_ctrl_state(); ctrl; ctrl = NEXT_CTRL(ctrl)) { monitor_evnt_tei(ctrl->isdnif, ctrl->tei); monitor_evnt_l12stat(ctrl->isdnif, LAYER_ONE, ctrl->l1stat); monitor_evnt_l12stat(ctrl->isdnif, LAYER_TWO, ctrl->l2stat); } /* current state of entries */ for (cfe = get_first_cfg_entry(); cfe; cfe = NEXT_CFE(cfe)) { if (cfe->state == ST_CONNECTED) { monitor_evnt_connect(cfe); monitor_evnt_acct(cfe); monitor_evnt_charge(cfe, cfe->charge, 1); } } /*XXX*/ con->events = t_events; } /*--------------------------------------------------------------------------- * dump all monitor rights *---------------------------------------------------------------------------*/ static void cmd_dump_rights(int fd, int r_mask, u_int8_t *cmd, const char *source) { struct monitor_rights * r; int num_rights; u_int8_t drini[I4B_MON_DRINI_SIZE]; u_int8_t dr[I4B_MON_DR_SIZE]; for (num_rights = 0, r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list)) num_rights++; I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE); I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, num_rights); if ((sock_write(fd, drini, sizeof drini)) == -1) { logit(LL_MER, "cmd_dump_rights: sock_write 1 error - %s", strerror(errno)); } for (r = TAILQ_FIRST(&rights); r != NULL; r = TAILQ_NEXT(r, list)) { I4B_PREP_EVNT(dr, I4B_MON_DR_CODE); I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, r->rights); I4B_PUT_4B(dr, I4B_MON_DR_NET, r->net); I4B_PUT_4B(dr, I4B_MON_DR_MASK, r->mask); I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, r->local); if ((sock_write(fd, dr, sizeof dr)) == -1) { logit(LL_MER, "cmd_dump_rights: sock_write 2 error - %s", strerror(errno)); } } } /*--------------------------------------------------------------------------- * rescan config file *---------------------------------------------------------------------------*/ static void cmd_reread_cfg(int fd, int _rights, u_int8_t *cmd, const char * source) { rereadconfig(42); } /*--------------------------------------------------------------------------- * drop one connection *---------------------------------------------------------------------------*/ static void cmd_hangup(int fd, int _rights, u_int8_t *cmd, const char * source) { int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL); int ctrl = I4B_GET_4B(cmd, I4B_MON_HANGUP_CTRL); hangup_channel(ctrl, channel, source); } /*--------------------------------------------------------------------------- * dump all active monitor connections *---------------------------------------------------------------------------*/ static void cmd_dump_mcons(int fd, int _rights, u_int8_t *cmd, const char * source) { int num_connections; struct monitor_connection *con; u_int8_t dcini[I4B_MON_DCINI_SIZE]; for (num_connections = 0, con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections)) num_connections++; I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE); I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, num_connections); if ((sock_write(fd, dcini, sizeof dcini)) == -1) { logit(LL_MER, "cmd_dump_mcons: sock_write 1 error - %s", strerror(errno)); } for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections)) { #ifndef I4B_NOTCPIP_MONITOR socklen_t namelen; struct sockaddr_in name; #endif u_int8_t dc[I4B_MON_DC_SIZE]; I4B_PREP_EVNT(dc, I4B_MON_DC_CODE); I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, con->rights); #ifndef I4B_NOTCPIP_MONITOR namelen = sizeof name; if (getpeername(con->sock, (struct sockaddr*)&name, &namelen) == 0) memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr); #endif if ((sock_write(fd, dc, sizeof dc)) == -1) { logit(LL_MER, "cmd_dump_mcons: sock_write 2 error - %s", strerror(errno)); } } } /*--------------------------------------------------------------------------- * 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(struct monitor_connection * con, int fd, int mcrights) { char cmd[I4B_MAX_MON_CLIENT_CMD]; u_int code; /* command dispatch table */ typedef void (*cmd_func_t)(int fd, int rights, u_int8_t *cmd, const char *source); 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]) int avail, bytes, err; /* Network transfer may deliver two or more packets concatenated. * Peek at the header and read only one event at a time... */ avail = 0; err = ioctl(fd, FIONREAD, &avail); if (err == -1 || avail < I4B_MON_CMD_HDR) { if (err == -1 && errno == EINTR) return 0; /* try again later */ if (err == -1 || avail == 0) { /* logit(LL_MER, "monitor read 0 bytes"); */ /* 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) { logit(LL_MER, "monitor read only %d bytes", bytes); return 0; /* errh? something must be wrong... */ } bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN); if (bytes >= (int)sizeof cmd) { close(fd); logit(LL_MER, "monitor: garbage on connection"); return 1; } /* now we know the size, it fits, so lets read it! */ if (sock_read(fd, cmd, bytes) <= 0) { logit(LL_MER, "monitor: sock_read <= 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) { /*XXX*/ /* 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); con->events = events & mcrights; return 0; } if (code >= NUMCMD) { logit(LL_MER, "illegal command from client, code = %d\n", code); return 0; } if (cmd_tab[code].call == NULL) return 0; if ((cmd_tab[code].rights & mcrights) == cmd_tab[code].rights) cmd_tab[code].call(fd, mcrights, (u_char *)cmd, con->source); 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) { struct monitor_connection * con; for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections)) { if ((con->events & mask) == mask) return 1; } return 0; } /*--------------------------------------------------------------------------- * exec hangup command *---------------------------------------------------------------------------*/ static void hangup_channel(int controller, int channel, const char *source) { struct cfg_entry * cep = NULL; struct isdn_ctrl_state * ctrl = NULL; int i; ctrl = find_ctrl_state(controller); if (ctrl != NULL) { if (ctrl->state != CTRL_UP) return; for (i = 0; i < ctrl->nbch; i++) { if (ctrl->stateb[i] != CHAN_IDLE) { cep = get_cep_by_cc(controller, i); if (cep != NULL && cep->isdnchannelused == channel && cep->isdncontrollerused == controller) goto found; } } } /* not found */ return; found: logit(LL_CHD, "%05d %s manual disconnect (remote from %s)", cep->cdid, cep->name, source); cep->hangup = 1; return; } /*--------------------------------------------------------------------------- * Send an event to every connection interested in this kind of * event *---------------------------------------------------------------------------*/ static void monitor_broadcast(int mask, u_int8_t *pkt, size_t bytes) { struct monitor_connection *con; for (con = TAILQ_FIRST(&connections); con != NULL; con = TAILQ_NEXT(con, connections)) { if ((con->events & mask) == mask) { int fd = con->sock; if ((sock_write(fd, pkt, bytes)) == -1) { logit(LL_MER, "monitor_broadcast: sock_write error - %s", strerror(errno)); } } } } /*--------------------------------------------------------------------------- * Post a logfile event *---------------------------------------------------------------------------*/ void monitor_evnt_log(int prio, const char * what, const char * msg) { u_int8_t 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(struct cfg_entry *cep, int units, int estimate) { int mask; time_t now; u_int8_t evnt[I4B_MON_CHRG_SIZE]; mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; 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_CTRL, cep->isdncontrollerused); I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, cep->isdnchannelused); 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(struct cfg_entry *cep) { u_int8_t evnt[I4B_MON_CONNECT_SIZE]; char devnam[I4B_MAX_MON_STRING]; int mask; time_t now; mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; if (!anybody(mask)) return; time(&now); snprintf(devnam, sizeof devnam, "%s%d", 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_CTRL, cep->isdncontrollerused); I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, cep->isdnchannelused); I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name); I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devnam); if (cep->direction == DIR_OUT) { I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->remote_phone_dialout); I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_dialout); } else { I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming); I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->local_phone_incoming); } monitor_broadcast(mask, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * Post a disconnect event *---------------------------------------------------------------------------*/ void monitor_evnt_disconnect(struct cfg_entry *cep) { u_int8_t evnt[I4B_MON_DISCONNECT_SIZE]; int mask; time_t now; mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; 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_CTRL, cep->isdncontrollerused); I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, cep->isdnchannelused); monitor_broadcast(mask, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * Post an up/down event *---------------------------------------------------------------------------*/ void monitor_evnt_updown(struct cfg_entry *cep, int up) { u_int8_t evnt[I4B_MON_UPDOWN_SIZE]; int mask; time_t now; mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT; 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_CTRL, cep->isdncontrollerused); I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, cep->isdnchannelused); I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up); monitor_broadcast(mask, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * Post a Layer1/2 status change event *---------------------------------------------------------------------------*/ void monitor_evnt_l12stat(int controller, int layer, int state) { u_int8_t evnt[I4B_MON_L12STAT_SIZE]; time_t now; if (!anybody(I4B_CA_EVNT_I4B)) return; time(&now); I4B_PREP_EVNT(evnt, I4B_MON_L12STAT_CODE); I4B_PUT_4B(evnt, I4B_MON_L12STAT_TSTAMP, (long)now); I4B_PUT_4B(evnt, I4B_MON_L12STAT_CTRL, controller); I4B_PUT_4B(evnt, I4B_MON_L12STAT_LAYER, layer); I4B_PUT_4B(evnt, I4B_MON_L12STAT_STATE, state); monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * Post a TEI change event *---------------------------------------------------------------------------*/ void monitor_evnt_tei(int controller, int tei) { u_int8_t evnt[I4B_MON_TEI_SIZE]; time_t now; if (!anybody(I4B_CA_EVNT_I4B)) return; time(&now); I4B_PREP_EVNT(evnt, I4B_MON_TEI_CODE); I4B_PUT_4B(evnt, I4B_MON_TEI_TSTAMP, (long)now); I4B_PUT_4B(evnt, I4B_MON_TEI_CTRL, controller); I4B_PUT_4B(evnt, I4B_MON_TEI_TEI, tei); monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * Post an accounting event *---------------------------------------------------------------------------*/ void monitor_evnt_acct(struct cfg_entry *cep) { u_int8_t evnt[I4B_MON_ACCT_SIZE]; time_t now; if (!anybody(I4B_CA_EVNT_I4B)) return; time(&now); I4B_PREP_EVNT(evnt, I4B_MON_ACCT_CODE); I4B_PUT_4B(evnt, I4B_MON_ACCT_TSTAMP, (long)now); I4B_PUT_4B(evnt, I4B_MON_ACCT_CTRL, cep->isdncontrollerused); I4B_PUT_4B(evnt, I4B_MON_ACCT_CHAN, cep->isdnchannelused); I4B_PUT_4B(evnt, I4B_MON_ACCT_OBYTES, cep->outbytes); I4B_PUT_4B(evnt, I4B_MON_ACCT_OBPS, cep->outbps); I4B_PUT_4B(evnt, I4B_MON_ACCT_IBYTES, cep->inbytes); I4B_PUT_4B(evnt, I4B_MON_ACCT_IBPS, cep->inbps); monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt); } /*--------------------------------------------------------------------------- * read from a socket *---------------------------------------------------------------------------*/ static ssize_t sock_read(int fd, void *buf, size_t nbytes) { size_t nleft; ssize_t nread; unsigned char *ptr; ptr = buf; nleft = nbytes; while(nleft > 0) { if ((nread = read(fd, ptr, nleft)) < 0) { if (errno == EINTR) { nread = 0; } else { return(-1); } } else if (nread == 0) { break; /* EOF */ } nleft -= nread; ptr += nread; } return(nbytes - nleft); } /*--------------------------------------------------------------------------- * write to a socket *---------------------------------------------------------------------------*/ static ssize_t sock_write(int fd, void *buf, size_t nbytes) { size_t nleft; ssize_t nwritten; unsigned char *ptr; ptr = buf; nleft = nbytes; while(nleft > 0) { if ((nwritten = write(fd, ptr, nleft)) <= 0) { if (errno == EINTR) { nwritten = 0; } else { return(-1); } } nleft -= nwritten; ptr += nwritten; } return(nbytes); } struct monitor_rights * monitor_next_rights(const struct monitor_rights *r) { if (r == NULL) return TAILQ_FIRST(&rights); else return TAILQ_NEXT(r, list); } #endif /* I4B_EXTERNAL_MONITOR */