]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/i4b/isdnd/monitor.c
This commit was generated by cvs2svn to compensate for changes in r53654,
[FreeBSD/FreeBSD.git] / usr.sbin / i4b / isdnd / monitor.c
1 /*
2  *   Copyright (c) 1998 Martin Husemann. All rights reserved.
3  *
4  *   Redistribution and use in source and binary forms, with or without
5  *   modification, are permitted provided that the following conditions
6  *   are met:
7  *
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.
18  *   
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
29  *   SUCH DAMAGE.
30  *
31  *---------------------------------------------------------------------------
32  *
33  *      i4b daemon - network monitor server module
34  *      ------------------------------------------
35  *
36  * $FreeBSD$
37  *
38  *      last edit-date: [Sun May 30 10:33:05 1999]
39  *
40  *      -mh     created
41  *
42  *---------------------------------------------------------------------------*/
43
44 #include "isdnd.h"
45
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()
51 { }
52 int monitor_start_rights(const char *clientspec)
53 { return I4BMAR_OK; }
54 void monitor_add_rights(int rights_mask)
55 { }
56 void monitor_fixup_rights()
57 { }
58 #else
59
60 #include "monitor.h"
61 #include "vararray.h"
62 #include <sys/socket.h>
63 #include <sys/un.h>
64 #ifndef I4B_NOTCPIP_MONITOR
65 #include <netinet/in.h>
66 #include <arpa/inet.h>
67 #include <netdb.h>
68 #endif
69
70 VARA_DECL(struct monitor_rights) rights = VARA_INITIALIZER;
71 #define INITIAL_RIGHTS_ALLOC    10      /* most users will have one or two entries */
72
73 static int local_rights = -1;   /* index of entry for local socket, -1 if none */
74
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 */
80 };
81 static VARA_DECL(struct monitor_connection) connections = VARA_INITIALIZER;
82 #define INITIAL_CONNECTIONS_ALLOC       30      /* high guess */
83
84 /* derive channel number from config pointer */
85 #define CHNO(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
86
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);
96
97 /*
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.
104  */
105 static int cur_add_entry = -1;
106
107 /*
108  * Initialize the monitor server module. This affects only active
109  * connections, the access rights are not modified here!
110  */
111 void monitor_init()
112 {
113         accepted = 0;
114         VARA_EMPTY(connections);
115 }
116
117 /*
118  * Prepare for exit
119  */
120 void monitor_exit()
121 {
122         int i;
123
124         /* Close all open connections. */
125         VARA_FOREACH(connections, i)
126                 close(VARA_AT(connections, i).sock);
127
128         /* Remove their descriptions */
129         VARA_EMPTY(connections);
130 }
131
132 /*
133  * Initialize access rights. No active connections are affected!
134  */
135 void monitor_clear_rights()
136 {
137         VARA_EMPTY(rights);
138         cur_add_entry = -1;
139 }
140
141 /*
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.
145  */
146 int monitor_start_rights(const char *clientspec)
147 {
148         int i;
149         struct monitor_rights r;
150
151         /* initialize the new rights entry */
152         memset(&r, 0, sizeof r);
153
154         /* check clientspec */
155         if (*clientspec == '/') {
156                 struct sockaddr_un sa;
157
158                 /* this is a local socket spec, check if we already have one */
159                 if (VARA_VALID(rights, local_rights))
160                         return I4BMAR_DUP;
161                 /* does it fit in a local socket address? */
162                 if (strlen(clientspec) > sizeof sa.sun_path)
163                         return I4BMAR_LENGTH;
164                 r.local = 1;
165                 strcpy(r.name, clientspec);
166 #ifndef I4B_NOTCPIP_MONITOR
167         } else {
168                 /* remote entry, parse host/net and cidr */
169                 char hostname[FILENAME_MAX];
170                 char *p;
171                 p = strchr(clientspec, '/');
172                 if (!p) {
173                         struct hostent *host;
174                         u_int32_t hn;
175                         /* must be a host spec */
176                         r.mask = ~0;
177                         host = gethostbyname(clientspec);
178                         if (!host)
179                                 return I4BMAR_NOIP;
180                         memcpy(&hn, host->h_addr_list[0], sizeof hn);
181                         r.net = (u_int32_t)ntohl(hn);
182                 } else if (p[1]) {
183                         /* must be net/cidr spec */
184                         int l;
185                         struct netent *net;
186                         u_int32_t s = ~0U;
187                         int num = strtol(p+1, NULL, 10);
188                         if (num < 0 || num > 32)
189                                 return I4BMAR_CIDR;
190                         s >>= num;
191                         s ^= ~0U;
192                         l = p - clientspec;
193                         if (l >= sizeof hostname)
194                                 return I4BMAR_LENGTH;
195                         strncpy(hostname, clientspec, l);
196                         hostname[l] = '\0';
197                         net = getnetbyname(hostname);
198                         if (net == NULL)
199                                 r.net = (u_int32_t)inet_network(hostname);
200                         else
201                                 r.net = (u_int32_t)net->n_net;
202                         r.mask = s;
203                         r.net &= s;
204                 } else
205                         return I4BMAR_CIDR;
206
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)
212                                 return I4BMAR_DUP;
213 #endif
214         }
215         r.rights = 0;
216
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);
221         if (r.local)
222                 local_rights = i;
223
224         DBGL(DL_RCCF, (log(LL_DBG, "system: monitor = %s", clientspec)));
225         
226         return I4BMAR_OK;
227 }
228
229 /*
230  * Add rights to the currently constructed entry - if any.
231  */
232 void monitor_add_rights(int rights_mask)
233 {
234         if (cur_add_entry < 0) return;  /* noone under construction */
235
236         VARA_AT(rights, cur_add_entry).rights |= rights_mask;
237
238         DBGL(DL_RCCF, (log(LL_DBG, "system: monitor-access = 0x%x", rights_mask)));
239 }
240
241 /*
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.
245  */
246 void monitor_fixup_rights()
247 {
248         int i;
249
250         /* no more rights may be added to the current entry */
251         cur_add_entry = -1;
252         
253         /* sort the rights array */
254         qsort(VARA_PTR(rights), VARA_NUM(rights), sizeof(struct monitor_rights), cmp_rights);
255
256         /* now the local entry may have moved, update its index */
257         if (VARA_VALID(rights, local_rights)) {
258                 local_rights = -1;
259                 VARA_FOREACH(rights, i) {
260                         if (VARA_AT(rights, i).local) {
261                                 local_rights = i;
262                                 break;
263                         }
264                 }
265         }       
266 }
267
268 /* comparator for rights */
269 static int cmp_rights(const void *a, const void *b)
270 {
271         u_int32_t mask;
272         struct monitor_rights const * pa = (struct monitor_rights const*)a;
273         struct monitor_rights const * pb = (struct monitor_rights const*)b;
274
275         /* local sorts first */
276         if (pa->local)
277                 return -1;
278
279         /* which is the less specific netmask? */
280         mask = pa->mask;
281         if ((pb->mask & mask) == 0)
282                 mask = pb->mask;
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;
287         }
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;
291 }
292
293 #ifndef I4B_NOTCPIP_MONITOR
294 /*
295  * Check if access rights for a remote socket are specified and
296  * create this socket. Return -1 otherwise.
297  */
298 int monitor_create_remote_socket(int portno)
299 {
300         struct sockaddr_in sa;
301         int val;
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);
305                 exit(1);
306         }
307         val = 1;
308         if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) {
309                 log(LL_ERR, "could not setsockopt, errno = %d", errno);
310                 exit(1);
311         }
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);
319                 exit(1);
320         }
321         if (listen(remotesockfd, 0)) {
322                 log(LL_ERR, "could not listen on monitor socket, errno = %d", errno);
323                 exit(1);
324         }
325
326         return remotesockfd;
327 }
328 #endif
329
330 /*
331  * Check if access rights for a local socket are specified and
332  * create this socket. Return -1 otherwise.
333  */
334 int monitor_create_local_socket()
335 {
336         int s;
337         struct sockaddr_un sa;
338
339         /* check for a local entry */
340         if (!VARA_VALID(rights, local_rights))
341                 return -1;
342
343         /* create and setup socket */
344         s = socket(AF_LOCAL, SOCK_STREAM, 0);
345         if (s == -1) {
346                 log(LL_ERR, "could not create local monitor socket, errno = %d", errno);
347                 exit(1);
348         }
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);
356                 exit(1);
357         }
358         chmod(VARA_AT(rights, local_rights).name, 0500);
359         if (listen(s, 0)) {
360                 log(LL_ERR, "could not listen on local monitor socket, errno = %d", errno);
361                 exit(1);
362         }
363
364         return s;
365 }
366
367 /*
368  * Prepare a fd_set for a select call. Add all our local
369  * filedescriptors to the set, increment max_fd if appropriate.
370  */
371 void monitor_prepselect(fd_set *selset, int *max_fd)
372 {
373         int i;
374
375         VARA_FOREACH(connections, i) {
376                 int fd = VARA_AT(connections, i).sock;
377                 if (fd > *max_fd)
378                         *max_fd = fd;
379                 FD_SET(fd, selset);
380         }
381 }
382
383 /*
384  * Check if the result from a select call indicates something
385  * to do for us.
386  */
387 void monitor_handle_input(fd_set *selset)
388 {
389         int i;
390
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);
399                                 i--;
400                         }
401                 }
402         }
403
404         /* all connections gone? */
405         if (VARA_NUM(connections) == 0)
406                 accepted = 0;
407 }
408
409 /*
410  * Try new incoming connection on the given socket.
411  * Setup client descriptor and send initial data.
412  */
413 void monitor_handle_connect(int sockfd, int is_local)
414 {
415         struct monitor_connection *con;
416 #ifndef I4B_NOTCPIP_MONITOR
417         struct sockaddr_in ia;
418         u_int32_t ha = 0;
419 #endif
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];
424
425         /* accept the connection */
426         if (is_local) {
427                 s = sizeof ua;
428                 fd = accept(sockfd, (struct sockaddr *)&ua, &s);
429                 strcpy(source, "local connection");
430 #ifndef I4B_NOTCPIP_MONITOR
431         } else {
432                 s = sizeof ia;
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);
437                 ha = ntohl(ha);
438 #endif
439         }
440
441         /* check the access rights of this connection */
442         r_mask = 0;
443         VARA_FOREACH(rights, i) {
444                 struct monitor_rights *r = &VARA_AT(rights, i);
445                 if (r->local) {
446                         if (is_local) {
447                                 r_mask = r->rights;
448                                 break;
449                         }
450 #ifndef I4B_NOTCPIP_MONITOR
451                 } else {
452                         if ((ha & r->mask) == r->net) {
453                                 r_mask = r->rights;
454                                 break;
455                         }
456 #endif
457                 }
458         }
459
460         if (r_mask == 0) {
461                 /* no rights - go away */
462                 log(LL_DBG, "monitor access denied: %s", source);
463                 close(fd);
464                 return;
465         }
466
467         accepted = 1;
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);
472         con->sock = fd;
473         con->rights = r_mask;
474         log(LL_DBG, "monitor access granted, rights = %x, #%d, %s",
475                 r_mask, i, source);
476
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);
484
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);
493         }
494 }
495
496 /* dump all monitor rights */
497 static void cmd_dump_rights(int fd, int r_mask, BYTE *cmd)
498 {
499         int i;
500         BYTE drini[I4B_MON_DRINI_SIZE];
501         BYTE dr[I4B_MON_DR_SIZE];
502
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);
506
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);
514         }
515 }
516
517 /* rescan config file */
518 static void cmd_reread_cfg(int fd, int rights, BYTE *cmd)
519 {
520         rereadconfig(42);
521 }
522
523 /* drop one connection */
524 static void cmd_hangup(int fd, int rights, BYTE *cmd)
525 {
526         int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
527         hangup_channel(channel);
528 }
529
530 /* dump all active monitor connections */
531 static void cmd_dump_mcons(int fd, int rights, BYTE *cmd)
532 {
533         int i;
534         BYTE dcini[I4B_MON_DCINI_SIZE];
535
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);
539
540         VARA_FOREACH(connections, i) {
541 #ifndef I4B_NOTCPIP_MONITOR
542                 int namelen;
543                 struct sockaddr_in name;
544 #endif
545                 BYTE dc[I4B_MON_DC_SIZE];
546
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);
553 #endif
554                 write(fd, dc, sizeof dc);
555         }
556 }
557
558 /*
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.
562  */
563 static int monitor_command(int con_index, int fd, int rights)
564 {
565         char cmd[I4B_MAX_MON_CLIENT_CMD];
566         u_int code;
567         /* command dispatch table */
568         typedef void (*cmd_func_t)(int fd, int rights, BYTE *cmd);
569         static struct {
570                 cmd_func_t call;        /* function to execute */
571                 u_int rights;           /* necessary rights */
572         } cmd_tab[] =
573         {
574         /* 0 */ { NULL, 0 },
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 },
579         };
580 #define NUMCMD  (sizeof cmd_tab / sizeof cmd_tab[0])
581
582         u_long u;
583         int bytes;
584
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) {
589                 if (u == 0) {
590                         log(LL_ERR, "monitor #%d, read 0 bytes", con_index);
591                         /* socket closed by peer */
592                         close(fd);
593                         return 1;
594                 }
595                 return 0;       /* not enough data there yet */
596         }
597
598         bytes = recv(fd, cmd, I4B_MON_CMD_HDR, MSG_PEEK);
599
600         if (bytes < I4B_MON_CMD_HDR)
601         {
602                 log(LL_ERR, "monitor #%d, read only %d bytes", con_index, bytes);
603                 return 0;       /* errh? something must be wrong... */
604         }
605
606         bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
607
608         if (bytes >= sizeof cmd)
609         {
610                 close(fd);
611                 log(LL_ERR, "monitor #%d, garbage on connection", con_index);
612                 return 1;
613         }
614
615         /* now we know the size, it fits, so lets read it! */
616         if (read(fd, cmd, bytes) <= 0)
617         {
618                 log(LL_ERR, "monitor #%d, read <= 0", con_index);
619                 close(fd);
620                 return 1;
621         }
622
623         /* decode command */
624         code = I4B_GET_2B(cmd, I4B_MON_CMD);
625
626         /* special case: may modify our connection descriptor, is
627          * beyound all rights checks */
628         if (code == I4B_MON_CCMD_SETMASK) {
629                 /*
630                 u_int major = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMAJOR);
631                 u_int minor = I4B_GET_2B(cmd, I4B_MON_ICLIENT_VERMINOR);
632                 */
633                 int events = I4B_GET_4B(cmd, I4B_MON_ICLIENT_EVENTS);
634                 VARA_AT(connections, con_index).events = events & rights;
635                 return 0;
636         }
637
638         if (code < 0 || code >= NUMCMD) {
639                 log(LL_ERR, "illegal command from client #%d: code = %d\n",
640                         con_index, code);
641                 return 0;
642         }
643         if (cmd_tab[code].call == NULL)
644                 return 0;
645         if ((cmd_tab[code].rights & rights) == cmd_tab[code].rights)
646                 cmd_tab[code].call(fd, rights, cmd);
647
648         return 0;
649 }
650
651 /*
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.
655  */
656 static int anybody(int mask)
657 {
658         int i;
659
660         VARA_FOREACH(connections, i)
661                 if ((VARA_AT(connections, i).events & mask) == mask)
662                         return 1;
663
664         return 0;
665 }
666
667 /*
668  * Send an event to every connection interested in this kind of
669  * event
670  */
671 static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes)
672 {
673         int i;
674
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);
679                 }
680         }
681 }
682
683 /*
684  * Post a logfile event
685  */
686 void monitor_evnt_log(int prio, const char * what, const char * msg)
687 {
688         BYTE evnt[I4B_MON_LOGEVNT_SIZE];
689         time_t now;
690
691         if (!anybody(I4B_CA_EVNT_I4B)) return;
692
693         time(&now);
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);
699
700         monitor_broadcast(I4B_CA_EVNT_I4B, evnt, sizeof evnt);
701 }
702
703 /*
704  * Post a charging event on the connection described
705  * by the given config entry.
706  */
707 void monitor_evnt_charge(cfg_entry_t *cep, int units, int estimate)
708 {
709         int chno = CHNO(cep);
710         int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
711         time_t now;
712         BYTE evnt[I4B_MON_CHRG_SIZE];
713
714         if (!anybody(mask)) return;
715
716         time(&now);
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);
722
723         monitor_broadcast(mask, evnt, sizeof evnt);
724 }
725
726 /*
727  * Post a connection event
728  */
729 void monitor_evnt_connect(cfg_entry_t *cep)
730 {
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;
735         time_t now;
736
737         if (!anybody(mask)) return;
738
739         time(&now);
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);
749
750         monitor_broadcast(mask, evnt, sizeof evnt);
751 }
752
753 /*
754  * Post a disconnect event
755  */
756 void monitor_evnt_disconnect(cfg_entry_t *cep)
757 {
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;
761         time_t now;
762
763         if (!anybody(mask)) return;
764
765         time(&now);
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);
769
770         monitor_broadcast(mask, evnt, sizeof evnt);
771 }
772
773 /*
774  * Post an up/down event
775  */
776 void monitor_evnt_updown(cfg_entry_t *cep, int up)
777 {
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;
781         time_t now;
782
783         if (!anybody(mask)) return;
784
785         time(&now);
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);
790
791         monitor_broadcast(mask, evnt, sizeof evnt);
792 }
793
794 void hangup_channel(int channel)
795 {
796         int i;
797         cfg_entry_t * cep = NULL;
798         
799         for (i = 0; i < ncontroller; i++)
800         {       
801                 if(isdn_ctrl_tab[i].state != CTRL_UP)
802                         continue;
803                 if(isdn_ctrl_tab[i].stateb1 != CHAN_IDLE)
804                 {
805                         cep = get_cep_by_cc(i, 0);
806                         if (cep != NULL && CHNO(cep) == channel)
807                                 goto found;
808                 }
809                 if(isdn_ctrl_tab[i].stateb2 != CHAN_IDLE)
810                 {
811                         cep = get_cep_by_cc(i, 1);
812                         if (cep != NULL && CHNO(cep) == channel)
813                                 goto found;
814                 }
815         }
816         /* not found */
817         return;
818
819 found:
820         cep->hangup = 1;
821         return;
822 }
823
824 #endif  /* I4B_EXTERNAL_MONITOR */