3c143ba9cd
is possible to support 30-channel primary rate adapters. Mostly from jml@cubical.fi via FreeBSD. * Follow kernel naming trend: bri => isdnif
1288 lines
34 KiB
C
1288 lines
34 KiB
C
/* $NetBSD: monitor.c,v 1.13 2004/03/28 20:49:22 pooka 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 <martin@NetBSD.org>.
|
|
*
|
|
* 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. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* 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 <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#ifndef I4B_NOTCPIP_MONITOR
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#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 >= 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;
|
|
u_int8_t idata[I4B_MON_IDATA_SIZE];
|
|
int fd = -1, s, 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
|
|
int 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 rights)
|
|
{
|
|
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 >= 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 & rights;
|
|
return 0;
|
|
}
|
|
|
|
if (code < 0 || 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 & rights) == cmd_tab[code].rights)
|
|
cmd_tab[code].call(fd, rights, 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 devname[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(devname, sizeof devname, "%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, devname);
|
|
|
|
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 */
|