2 * Copyright (c) 1998 Martin Husemann. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 * 4. Altered versions must be plainly marked as such, and must not be
17 * misrepresented as being the original software and/or documentation.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 *---------------------------------------------------------------------------
33 * i4b daemon - network monitor server module
34 * ------------------------------------------
38 * last edit-date: [Sun May 30 10:33:05 1999]
42 *---------------------------------------------------------------------------*/
46 #ifndef I4B_EXTERNAL_MONITOR
47 /* dummy version of routines needed by config file parser
48 * (config files should be valid with and without external montioring
49 * support compiled into the daemon) */
50 void monitor_clear_rights()
52 int monitor_start_rights(const char *clientspec)
54 void monitor_add_rights(int rights_mask)
56 void monitor_fixup_rights()
62 #include <sys/socket.h>
64 #ifndef I4B_NOTCPIP_MONITOR
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
70 VARA_DECL(struct monitor_rights) rights = VARA_INITIALIZER;
71 #define INITIAL_RIGHTS_ALLOC 10 /* most users will have one or two entries */
73 static int local_rights = -1; /* index of entry for local socket, -1 if none */
75 /* for each active monitor connection we have one of this: */
76 struct monitor_connection {
77 int sock; /* socket for this connection */
78 int rights; /* active rights for this connection */
79 int events; /* bitmask of events client is interested in */
81 static VARA_DECL(struct monitor_connection) connections = VARA_INITIALIZER;
82 #define INITIAL_CONNECTIONS_ALLOC 30 /* high guess */
84 /* derive channel number from config pointer */
85 #define CHNO(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
87 /* local prototypes */
88 static int cmp_rights(const void *a, const void *b);
89 static int monitor_command(int con_index, int fd, int rights);
90 static void cmd_dump_rights(int fd, int rights, BYTE *cmd);
91 static void cmd_dump_mcons(int fd, int rights, BYTE *cmd);
92 static void cmd_reread_cfg(int fd, int rights, BYTE *cmd);
93 static void cmd_hangup(int fd, int rights, BYTE *cmd);
94 static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes);
95 static int anybody(int mask);
98 * Due to the way we structure config files, the rights for an external
99 * monitor might be stated in multiple steps. First a call to
100 * monitor_start_rights opens an entry. Further (optional) calls to
101 * montior_add_rights assemble additional rights for this "current"
102 * entry. When closing the sys-file section of the config file, the
103 * "current" entry becomes invalid.
105 static int cur_add_entry = -1;
108 * Initialize the monitor server module. This affects only active
109 * connections, the access rights are not modified here!
114 VARA_EMPTY(connections);
124 /* Close all open connections. */
125 VARA_FOREACH(connections, i)
126 close(VARA_AT(connections, i).sock);
128 /* Remove their descriptions */
129 VARA_EMPTY(connections);
133 * Initialize access rights. No active connections are affected!
135 void monitor_clear_rights()
142 * Add an entry to the access lists. The clientspec either is
143 * the name of the local socket or a host- or networkname or
144 * numeric ip/host-bit-len spec.
146 int monitor_start_rights(const char *clientspec)
149 struct monitor_rights r;
151 /* initialize the new rights entry */
152 memset(&r, 0, sizeof r);
154 /* check clientspec */
155 if (*clientspec == '/') {
156 struct sockaddr_un sa;
158 /* this is a local socket spec, check if we already have one */
159 if (VARA_VALID(rights, local_rights))
161 /* does it fit in a local socket address? */
162 if (strlen(clientspec) > sizeof sa.sun_path)
163 return I4BMAR_LENGTH;
165 strcpy(r.name, clientspec);
166 #ifndef I4B_NOTCPIP_MONITOR
168 /* remote entry, parse host/net and cidr */
169 char hostname[FILENAME_MAX];
171 p = strchr(clientspec, '/');
173 struct hostent *host;
175 /* must be a host spec */
177 host = gethostbyname(clientspec);
180 memcpy(&hn, host->h_addr_list[0], sizeof hn);
181 r.net = (u_int32_t)ntohl(hn);
183 /* must be net/cidr spec */
187 int num = strtol(p+1, NULL, 10);
188 if (num < 0 || num > 32)
193 if (l >= sizeof hostname)
194 return I4BMAR_LENGTH;
195 strncpy(hostname, clientspec, l);
197 net = getnetbyname(hostname);
199 r.net = (u_int32_t)inet_network(hostname);
201 r.net = (u_int32_t)net->n_net;
207 /* check for duplicate entry */
208 VARA_FOREACH(rights, i)
209 if (VARA_AT(rights, i).mask == r.mask &&
210 VARA_AT(rights, i).net == r.net &&
211 VARA_AT(rights, i).local == r.local)
217 /* entry ok, add it to the collection */
218 cur_add_entry = i = VARA_NUM(rights);
219 VARA_ADD_AT(rights, i, struct monitor_rights, INITIAL_RIGHTS_ALLOC);
220 memcpy(&VARA_AT(rights, i), &r, sizeof r);
224 DBGL(DL_RCCF, (log(LL_DBG, "system: monitor = %s", clientspec)));
230 * Add rights to the currently constructed entry - if any.
232 void monitor_add_rights(int rights_mask)
234 if (cur_add_entry < 0) return; /* noone under construction */
236 VARA_AT(rights, cur_add_entry).rights |= rights_mask;
238 DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-access = 0x%x", rights_mask)));
242 * All rights have been added now. Sort the to get most specific
243 * host/net masks first, so we can travel the list and use the first
244 * match for actual rights.
246 void monitor_fixup_rights()
250 /* no more rights may be added to the current entry */
253 /* sort the rights array */
254 qsort(VARA_PTR(rights), VARA_NUM(rights), sizeof(struct monitor_rights), cmp_rights);
256 /* now the local entry may have moved, update its index */
257 if (VARA_VALID(rights, local_rights)) {
259 VARA_FOREACH(rights, i) {
260 if (VARA_AT(rights, i).local) {
268 /* comparator for rights */
269 static int cmp_rights(const void *a, const void *b)
272 struct monitor_rights const * pa = (struct monitor_rights const*)a;
273 struct monitor_rights const * pb = (struct monitor_rights const*)b;
275 /* local sorts first */
279 /* which is the less specific netmask? */
281 if ((pb->mask & mask) == 0)
283 /* are the entries disjunct? */
284 if ((pa->net & mask) != (pb->net & mask)) {
285 /* simply compare net part of address */
286 return ((pa->net & mask) < (pb->net & mask)) ? -1 : 1;
288 /* One entry is part of the others net. We already now "mask" is
289 * the netmask of the less specific (i.e. greater) one */
290 return (pa->mask == mask) ? 1 : -1;
293 #ifndef I4B_NOTCPIP_MONITOR
295 * Check if access rights for a remote socket are specified and
296 * create this socket. Return -1 otherwise.
298 int monitor_create_remote_socket(int portno)
300 struct sockaddr_in sa;
302 int remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
303 if (remotesockfd == -1) {
304 log(LL_ERR, "could not create remote monitor socket, errno = %d", errno);
308 if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) {
309 log(LL_ERR, "could not setsockopt, errno = %d", errno);
312 memset(&sa, 0, sizeof sa);
313 sa.sin_len = sizeof sa;
314 sa.sin_family = AF_INET;
315 sa.sin_port = htons(portno);
316 sa.sin_addr.s_addr = htonl(INADDR_ANY);
317 if (bind(remotesockfd, (struct sockaddr *)&sa, sizeof sa) == -1) {
318 log(LL_ERR, "could not bind remote monitor socket to port %d, errno = %d", portno, errno);
321 if (listen(remotesockfd, 0)) {
322 log(LL_ERR, "could not listen on monitor socket, errno = %d", errno);
331 * Check if access rights for a local socket are specified and
332 * create this socket. Return -1 otherwise.
334 int monitor_create_local_socket()
337 struct sockaddr_un sa;
339 /* check for a local entry */
340 if (!VARA_VALID(rights, local_rights))
343 /* create and setup socket */
344 s = socket(AF_LOCAL, SOCK_STREAM, 0);
346 log(LL_ERR, "could not create local monitor socket, errno = %d", errno);
349 unlink(VARA_AT(rights, local_rights).name);
350 memset(&sa, 0, sizeof sa);
351 sa.sun_len = sizeof sa;
352 sa.sun_family = AF_LOCAL;
353 strcpy(sa.sun_path, VARA_AT(rights, local_rights).name);
354 if (bind(s, (struct sockaddr *)&sa, SUN_LEN(&sa))) {
355 log(LL_ERR, "could not bind local monitor socket [%s], errno = %d", VARA_AT(rights, local_rights).name, errno);
358 chmod(VARA_AT(rights, local_rights).name, 0500);
360 log(LL_ERR, "could not listen on local monitor socket, errno = %d", errno);
368 * Prepare a fd_set for a select call. Add all our local
369 * filedescriptors to the set, increment max_fd if appropriate.
371 void monitor_prepselect(fd_set *selset, int *max_fd)
375 VARA_FOREACH(connections, i) {
376 int fd = VARA_AT(connections, i).sock;
384 * Check if the result from a select call indicates something
387 void monitor_handle_input(fd_set *selset)
391 VARA_FOREACH(connections, i) {
392 int fd = VARA_AT(connections, i).sock;
393 if (FD_ISSET(fd, selset)) {
394 /* handle command from this client */
395 if (monitor_command(i, fd, VARA_AT(connections, i).rights) != 0) {
396 /* broken or closed connection */
397 log(LL_DBG, "monitor connection #%d closed", i);
398 VARA_REMOVEAT(connections, i);
404 /* all connections gone? */
405 if (VARA_NUM(connections) == 0)
410 * Try new incoming connection on the given socket.
411 * Setup client descriptor and send initial data.
413 void monitor_handle_connect(int sockfd, int is_local)
415 struct monitor_connection *con;
416 #ifndef I4B_NOTCPIP_MONITOR
417 struct sockaddr_in ia;
420 struct sockaddr_un ua;
421 BYTE idata[I4B_MON_IDATA_SIZE];
422 int fd = -1, s, i, r_mask;
423 char source[FILENAME_MAX];
425 /* accept the connection */
428 fd = accept(sockfd, (struct sockaddr *)&ua, &s);
429 strcpy(source, "local connection");
430 #ifndef I4B_NOTCPIP_MONITOR
433 fd = accept(sockfd, (struct sockaddr *)&ia, &s);
434 snprintf(source, sizeof source, "tcp/ip connection from %s\n",
435 inet_ntoa(ia.sin_addr));
436 memcpy(&ha, &ia.sin_addr.s_addr, sizeof ha);
441 /* check the access rights of this connection */
443 VARA_FOREACH(rights, i) {
444 struct monitor_rights *r = &VARA_AT(rights, i);
450 #ifndef I4B_NOTCPIP_MONITOR
452 if ((ha & r->mask) == r->net) {
461 /* no rights - go away */
462 log(LL_DBG, "monitor access denied: %s", source);
468 i = VARA_NUM(connections);
469 VARA_ADD_AT(connections, i, struct monitor_connection, INITIAL_CONNECTIONS_ALLOC);
470 con = &VARA_AT(connections, i);
471 memset(con, 0, sizeof *con);
473 con->rights = r_mask;
474 log(LL_DBG, "monitor access granted, rights = %x, #%d, %s",
477 /* send initial data */
478 I4B_PREP_CMD(idata, I4B_MON_IDATA_CODE);
479 I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMAJOR, MPROT_VERSION);
480 I4B_PUT_2B(idata, I4B_MON_IDATA_VERSMINOR, MPROT_REL);
481 I4B_PUT_2B(idata, I4B_MON_IDATA_NUMCTRL, ncontroller);
482 I4B_PUT_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask);
483 write(fd, idata, sizeof idata);
485 for (i = 0; i < ncontroller; i++) {
486 BYTE ictrl[I4B_MON_ICTRL_SIZE];
487 I4B_PREP_CMD(ictrl, I4B_MON_ICTRL_CODE);
488 I4B_PUT_STR(ictrl, I4B_MON_ICTRL_NAME, name_of_controller(isdn_ctrl_tab[i].ctrl_type, isdn_ctrl_tab[i].card_type));
489 I4B_PUT_2B(ictrl, I4B_MON_ICTRL_BUSID, 0);
490 I4B_PUT_4B(ictrl, I4B_MON_ICTRL_FLAGS, 0);
491 I4B_PUT_4B(ictrl, I4B_MON_ICTRL_NCHAN, 2);
492 write(fd, ictrl, sizeof ictrl);
496 /* dump all monitor rights */
497 static void cmd_dump_rights(int fd, int r_mask, BYTE *cmd)
500 BYTE drini[I4B_MON_DRINI_SIZE];
501 BYTE dr[I4B_MON_DR_SIZE];
503 I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE);
504 I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, VARA_NUM(rights));
505 write(fd, drini, sizeof drini);
507 VARA_FOREACH(rights, i) {
508 I4B_PREP_EVNT(dr, I4B_MON_DR_CODE);
509 I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, VARA_AT(rights, i).rights);
510 I4B_PUT_4B(dr, I4B_MON_DR_NET, VARA_AT(rights, i).net);
511 I4B_PUT_4B(dr, I4B_MON_DR_MASK, VARA_AT(rights, i).mask);
512 I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, VARA_AT(rights, i).local);
513 write(fd, dr, sizeof dr);
517 /* rescan config file */
518 static void cmd_reread_cfg(int fd, int rights, BYTE *cmd)
523 /* drop one connection */
524 static void cmd_hangup(int fd, int rights, BYTE *cmd)
526 int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
527 hangup_channel(channel);
530 /* dump all active monitor connections */
531 static void cmd_dump_mcons(int fd, int rights, BYTE *cmd)
534 BYTE dcini[I4B_MON_DCINI_SIZE];
536 I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE);
537 I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, VARA_NUM(connections));
538 write(fd, dcini, sizeof dcini);
540 VARA_FOREACH(connections, i) {
541 #ifndef I4B_NOTCPIP_MONITOR
543 struct sockaddr_in name;
545 BYTE dc[I4B_MON_DC_SIZE];
547 I4B_PREP_EVNT(dc, I4B_MON_DC_CODE);
548 I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, VARA_AT(connections, i).rights);
549 #ifndef I4B_NOTCPIP_MONITOR
550 namelen = sizeof name;
551 if (getpeername(VARA_AT(connections, i).sock, (struct sockaddr*)&name, &namelen) == 0)
552 memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr);
554 write(fd, dc, sizeof dc);
559 * Handle a command from the given socket. The client
560 * has rights as specified in the rights parameter.
561 * Return non-zero if connection is closed.
563 static int monitor_command(int con_index, int fd, int rights)
565 char cmd[I4B_MAX_MON_CLIENT_CMD];
567 /* command dispatch table */
568 typedef void (*cmd_func_t)(int fd, int rights, BYTE *cmd);
570 cmd_func_t call; /* function to execute */
571 u_int rights; /* necessary rights */
575 /* 1 */ { cmd_dump_rights, I4B_CA_COMMAND_FULL },
576 /* 2 */ { cmd_dump_mcons, I4B_CA_COMMAND_FULL },
577 /* 3 */ { cmd_reread_cfg, I4B_CA_COMMAND_FULL },
578 /* 4 */ { cmd_hangup, I4B_CA_COMMAND_FULL },
580 #define NUMCMD (sizeof cmd_tab / sizeof cmd_tab[0])
585 /* Network transfer may deliver two or more packets concatenated.
586 * Peek at the header and read only one event at a time... */
587 ioctl(fd, FIONREAD, &u);
588 if (u < I4B_MON_CMD_HDR) {
590 log(LL_ERR, "monitor #%d, read 0 bytes", con_index);
591 /* socket closed by peer */
595 return 0; /* not enough data there yet */
598 bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK);
600 if (bytes < I4B_MON_CMD_HDR)
602 log(LL_ERR, "monitor #%d, read only %d bytes", con_index, bytes);
603 return 0; /* errh? something must be wrong... */
606 bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
608 if (bytes >= sizeof cmd)
611 log(LL_ERR, "monitor #%d, garbage on connection", con_index);
615 /* now we know the size, it fits, so lets read it! */
616 if (read(fd, cmd, bytes) <= 0)
618 log(LL_ERR, "monitor #%d, read <= 0", con_index);
624 code = I4B_GET_2B(cmd, I4B_MON_CMD);
626 /* special case: may modify our connection descriptor, is
627 * beyound all rights checks */
628 if (code == I4B_MON_CCMD_SETMASK) {
630 u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR);
631 u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR);
633 int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS);
634 VARA_AT(connections, con_index).events = events & rights;
638 if (code < 0 || code >= NUMCMD) {
639 log(LL_ERR, "illegal command from client #%d: code = %d\n",
643 if (cmd_tab[code].call == NULL)
645 if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights)
646 cmd_tab[code].call(fd, rights, cmd);
652 * Check if somebody would receive an event with this mask.
653 * We are lazy and try to avoid assembling unneccesary packets.
654 * Return 0 if no one interested, nonzero otherwise.
656 static int anybody(int mask)
660 VARA_FOREACH(connections, i)
661 if ((VARA_AT(connections, i).events & mask) == mask)
668 * Send an event to every connection interested in this kind of
671 static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes)
675 VARA_FOREACH(connections, i) {
676 if ((VARA_AT(connections, i).events & mask) == mask) {
677 int fd = VARA_AT(connections, i).sock;
678 write(fd, pkt, bytes);
684 * Post a logfile event
686 void monitor_evnt_log(int prio, const char * what, const char * msg)
688 BYTE evnt[I4B_MON_LOGEVNT_SIZE];
691 if (!anybody(I4B_CA_EVNT_I4B)) return;
694 I4B_PREP_EVNT(evnt, I4B_MON_LOGEVNT_CODE);
695 I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_TSTAMP, (long)now);
696 I4B_PUT_4B(evnt, I4B_MON_LOGEVNT_PRIO, prio);
697 I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_WHAT, what);
698 I4B_PUT_STR(evnt, I4B_MON_LOGEVNT_MSG, msg);
700 monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
704 * Post a charging event on the connection described
705 * by the given config entry.
707 void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate)
709 int chno = CHNO(cep);
710 int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
712 BYTE evnt[I4B_MON_CHRG_SIZE];
714 if (!anybody(mask)) return;
717 I4B_PREP_EVNT(evnt, I4B_MON_CHRG_CODE);
718 I4B_PUT_4B(evnt, I4B_MON_CHRG_TSTAMP, (long)now);
719 I4B_PUT_4B(evnt, I4B_MON_CHRG_CHANNEL, chno);
720 I4B_PUT_4B(evnt, I4B_MON_CHRG_UNITS, units);
721 I4B_PUT_4B(evnt, I4B_MON_CHRG_ESTIMATED, estimate ? 1 : 0);
723 monitor_broadcast(mask, evnt, sizeof evnt);
727 * Post a connection event
729 void monitor_evnt_connect(cfg_entry_t *cep)
731 BYTE evnt[I4B_MON_CONNECT_SIZE];
732 char devname[I4B_MAX_MON_STRING];
733 int chno = CHNO(cep);
734 int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
737 if (!anybody(mask)) return;
740 snprintf(devname, sizeof devname, "%s%d", bdrivername(cep->usrdevicename), cep->usrdeviceunit);
741 I4B_PREP_EVNT(evnt, I4B_MON_CONNECT_CODE);
742 I4B_PUT_4B(evnt, I4B_MON_CONNECT_TSTAMP, (long)now);
743 I4B_PUT_4B(evnt, I4B_MON_CONNECT_DIR, cep->direction == DIR_OUT ? 1 : 0);
744 I4B_PUT_4B(evnt, I4B_MON_CONNECT_CHANNEL, chno);
745 I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name);
746 I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname);
747 I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming);
748 I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->remote_phone_dialout);
750 monitor_broadcast(mask, evnt, sizeof evnt);
754 * Post a disconnect event
756 void monitor_evnt_disconnect(cfg_entry_t *cep)
758 BYTE evnt[I4B_MON_DISCONNECT_SIZE];
759 int chno = CHNO(cep);
760 int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
763 if (!anybody(mask)) return;
766 I4B_PREP_EVNT(evnt, I4B_MON_DISCONNECT_CODE);
767 I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_TSTAMP, (long)now);
768 I4B_PUT_4B(evnt, I4B_MON_DISCONNECT_CHANNEL, chno);
770 monitor_broadcast(mask, evnt, sizeof evnt);
774 * Post an up/down event
776 void monitor_evnt_updown(cfg_entry_t *cep, int up)
778 BYTE evnt[I4B_MON_UPDOWN_SIZE];
779 int chno = CHNO(cep);
780 int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
783 if (!anybody(mask)) return;
786 I4B_PREP_EVNT(evnt, I4B_MON_UPDOWN_CODE);
787 I4B_PUT_4B(evnt, I4B_MON_UPDOWN_TSTAMP, (long)now);
788 I4B_PUT_4B(evnt, I4B_MON_UPDOWN_CHANNEL, chno);
789 I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up);
791 monitor_broadcast(mask, evnt, sizeof evnt);
794 void hangup_channel(int channel)
797 cfg_entry_t * cep = NULL;
799 for (i = 0; i < ncontroller; i++)
801 if(isdn_ctrl_tab[i].state != CTRL_UP)
803 if(isdn_ctrl_tab[i].stateb1 != CHAN_IDLE)
805 cep = get_cep_by_cc(i, 0);
806 if (cep != NULL && CHNO(cep) == channel)
809 if(isdn_ctrl_tab[i].stateb2 != CHAN_IDLE)
811 cep = get_cep_by_cc(i, 1);
812 if (cep != NULL && CHNO(cep) == channel)
824 #endif /* I4B_EXTERNAL_MONITOR */