Merge from vendor branch LIBEVENT:
[dragonfly.git] / usr.sbin / i4b / isdnmonitor / main.c
1 /*
2  *   Copyright (c) 1998,1999 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 client
34  *      -----------------------------------
35  *
36  *      $Id: main.c,v 1.35 2000/08/24 11:48:57 hm Exp $
37  *
38  * $FreeBSD: src/usr.sbin/i4b/isdnmonitor/main.c,v 1.7.2.1 2001/08/01 17:45:06 obrien Exp $
39  * $DragonFly: src/usr.sbin/i4b/isdnmonitor/main.c,v 1.5 2005/11/25 00:58:52 swildner Exp $
40  *
41  *      last edit-date: [Mon Dec 13 21:52:11 1999]
42  *
43  *---------------------------------------------------------------------------*/
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <stdarg.h>
48 #include <string.h>
49 #include <signal.h>
50 #include <time.h>
51 #include <errno.h>
52 #ifndef WIN32
53 #include <unistd.h>
54 #include <netdb.h>
55 #endif
56 #include <sys/types.h>
57 #ifndef WIN32
58 #include <sys/socket.h>
59 #include <sys/ioctl.h>
60 #include <sys/un.h>
61 #include <netinet/in.h>
62 #include <arpa/inet.h>
63 #else
64 #include <stdarg.h>
65 #include <windows.h>
66 extern char     *optarg;
67 int getopt(int nargc, char * const nargv[], const char *ostr);
68 #define close(f)        closesocket(f)
69 #define sleep(s)        Sleep(s*1000)
70 #define vsnprintf       _vsnprintf
71 #define ssize_t long
72 #endif
73 #ifdef ERROR
74 #undef ERROR
75 #endif
76
77 #define MAIN
78 #include "monprivate.h"
79 #undef MAIN
80
81 #ifndef AF_LOCAL
82 #define AF_LOCAL AF_UNIX
83 #endif
84
85 #ifdef DEBUG
86 #include <ctype.h>
87 #endif
88
89 #include "monitor.h"
90
91 /*
92  * Local function prototypes
93  */
94 static int connect_local(char *sockpath);
95 static int connect_remote(char *host, int portno);
96 static void usage();
97 static void mloop();
98 static void handle_input();
99 static void print_menu();
100 static void print_logevent(time_t tstamp, int prio, char * what, char * msg);
101 static void print_charge(time_t tstamp, int controller, int channel, int units, int estimated);
102 static void print_connect(time_t tstamp, int dir, int controller, int channel, char * cfgname, char * devname, char * remphone, char * locphone);
103 static void print_disconnect(time_t tstamp, int controller, int channel);
104 static void print_updown(time_t tstamp, int contoller, int channel, int isup);
105 static void handle_event(u_int8_t *msg, int len);
106 #ifdef DEBUG
107 static void dump_event(u_int8_t *msg, int len, int readflag);
108 #endif
109
110 static ssize_t sock_read(int fd, void *buf, size_t nbytes);
111 static ssize_t sock_write(int fd, void *buf, size_t nbytes);
112
113 static void mprintf(char *fmt, ...);
114
115 /*
116  * Global variables
117  */
118 static int debug = 0;
119 #define DBG_DUMPALL     0x01
120 #define DBG_PSEND       0x02
121
122 static int monsock = -1;
123 static int state = ST_INIT;
124 static int sub_state = 0;
125 static int sub_state_count = 0;
126
127 static int isdn_major = 0;
128 static int isdn_minor = 0;
129 static u_int32_t rights = 0;
130
131 static char *logfilename = NULL;
132 static FILE *lfp = NULL;
133
134 /*---------------------------------------------------------------------------
135  *      Display usage and exit
136  *---------------------------------------------------------------------------*/
137 static void
138 usage(void)
139 {
140         fprintf(stderr, "\n");
141         fprintf(stderr, "isdnmonitor - version %02d.%02d.%d, %s %s (protocol %02d.%02d)\n", VERSION, REL, STEP, __DATE__, __TIME__, MPROT_VERSION, MPROT_REL);
142 #ifdef FOREIGN
143         fprintf(stderr, "  usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-p port]\n");
144 #else
145         fprintf(stderr, "  usage: isdnmonitor [-c] [-d val] [-f name] [-h host] [-l path] [-p port]\n");
146 #endif
147         fprintf(stderr, "    -c        switch to curses fullscreen output\n");        
148         fprintf(stderr, "    -d <val>  debug flags (see source ...)\n");
149         fprintf(stderr, "    -dn       no debug output on fullscreen display\n");
150         fprintf(stderr, "    -f <name> filename to log output to\n");
151         fprintf(stderr, "    -h <host> hostname/address to connect to\n");        
152 #ifndef FOREIGN
153         fprintf(stderr, "    -l <path> pathname to local domain socket to connect to\n");
154 #endif
155         fprintf(stderr, "    -p <port> portnumber to use to connect to remote host\n");
156         exit(1);
157 }
158
159 /*---------------------------------------------------------------------------
160  *      Parse command line, startup monitor client
161  *---------------------------------------------------------------------------*/
162 int
163 main(int argc, char **argv)
164 {
165         int i;
166
167 #ifdef WIN32
168         WSADATA wsCaps;
169         WSAStartup(MAKEWORD(2, 0), &wsCaps);
170 #endif
171
172         portno = DEF_MONPORT;
173         devbuf[0] = '\0';
174
175 #ifndef FOREIGN 
176         while((i = getopt(argc, argv, "cd:f:h:p:l:")) != -1)
177 #else
178         while((i = getopt(argc, argv, "cd:f:h:p:")) != -1)
179 #endif
180         {
181                 switch(i)
182                 {
183                         case 'c':
184                                 fullscreen = 1;
185                                 break;
186                         case 'd':
187                                 if(*optarg == 'n')
188                                 {
189                                         debug_noscreen = 1;
190                                 }
191                                 else
192                                 {
193                                         if((sscanf(optarg, "%i", &debug)) != 1)
194                                                 usage();
195                                 }
196                                 break;
197                         case 'f':
198                                 logfilename = optarg;
199                                 break;
200                         case 'h':
201                                 hostname = optarg;
202                                 break;
203 #ifndef FOREIGN
204                         case 'l':
205                                 sockpath = optarg;
206                                 break;
207 #endif
208                         case 'p':
209                                 if((sscanf(optarg, "%i", &portno)) != 1)
210                                         usage();
211                                 break;
212                         default:
213                                 usage();
214                                 break;
215                 }
216         }
217
218 #ifndef FOREIGN
219         if(hostname && sockpath)
220         {
221                 fprintf(stderr, "Error: can not use local socket path on remote machine\n"
222                                 "conflicting options -h and -l!\n");
223                 return 1;
224         }
225
226         if(sockpath)
227         {
228                 monsock = connect_local(sockpath);
229         }
230         else if(hostname)
231 #else
232         if(hostname)
233 #endif
234
235         {
236                 monsock = connect_remote(hostname, portno);
237         }
238         else
239         {
240                 usage();
241         }
242
243         if(monsock == -1)
244         {
245                 fprintf(stderr, "Could not connect to i4b isdn daemon.\n");
246                 return 1;
247         }
248
249         if(logfilename != NULL)
250         {
251                 if((lfp = fopen(logfilename, "w")) == NULL)
252                 {
253                         fprintf(stderr, "could not open logfile [%s], %s\n", logfilename, strerror(errno));
254                         exit(1);
255                 }
256         }
257
258 #ifndef WIN32
259         signal(SIGPIPE, SIG_IGN);
260 #endif
261                 
262         mloop();
263
264         close(monsock);
265
266         return 0;
267 }
268
269 /*---------------------------------------------------------------------------
270  *      Connect via tcp/ip.
271  *      Return socket if successfull, -1 on error.
272  ---------------------------------------------------------------------------*/
273 static int
274 connect_remote(char *host, int portno)
275 {
276         struct sockaddr_in sa;
277         struct hostent *h;
278         int remotesockfd;
279
280         h = gethostbyname(host);
281
282         if(!h)
283         {
284                 fprintf(stderr, "could not resolve hostname '%s'\n", host);
285                 exit(1);
286         }
287
288         remotesockfd = socket(AF_INET, SOCK_STREAM, 0);
289
290         if(remotesockfd == -1)
291         {
292                 fprintf(stderr, "could not create remote monitor socket: %s\n", strerror(errno));
293                 exit(1);
294         }
295
296         memset(&sa, 0, sizeof(sa));
297
298 #ifdef BSD4_4
299         sa.sin_len = sizeof(sa);
300 #endif
301         sa.sin_family = AF_INET;
302         sa.sin_port = htons(portno);
303
304         memcpy(&sa.sin_addr.s_addr, h->h_addr_list[0], sizeof(sa.sin_addr.s_addr));
305
306         if(connect(remotesockfd, (struct sockaddr *)&sa, sizeof(sa)) == -1)
307         {
308                 fprintf(stderr, "could not connect remote monitor: %s\n", strerror(errno));
309                 exit(1);
310         }
311
312         return remotesockfd;
313 }
314
315 #ifndef FOREIGN
316 /*---------------------------------------------------------------------------
317  *      Connect local.
318  *      Return socket on success, -1 on failure.
319  *---------------------------------------------------------------------------*/
320 static int
321 connect_local(char *sockpath)
322 {
323         int s;
324         struct sockaddr_un sa;
325
326         /* check path length */
327         if(strlen(sockpath) >= sizeof(sa.sun_path))
328         {
329                 fprintf(stderr, "pathname to long for local socket: %s\n",
330                         sockpath);
331                 exit(1);
332         }
333
334         /* create and setup socket */
335         s = socket(AF_LOCAL, SOCK_STREAM, 0);
336
337         if(s == -1)
338         {
339                 fprintf(stderr, "could not create local monitor socket:%s\n", strerror(errno));
340                 exit(1);
341         }
342
343         memset(&sa, 0, sizeof(sa));
344
345         sa.sun_len = sizeof(sa);
346         sa.sun_family = AF_LOCAL;
347         strcpy(sa.sun_path, sockpath);
348
349         if(connect(s, (struct sockaddr *)&sa, sizeof(sa)))
350         {
351                 fprintf(stderr, "could not connect local monitor socket [%s]: %s\n", sockpath, strerror(errno));
352         }
353
354         return s;
355 }
356 #endif
357
358 /*---------------------------------------------------------------------------*
359  *      data from keyboard available, read and process it 
360  *---------------------------------------------------------------------------*/
361 #ifndef WIN32
362 static void
363 kbdrdhdl(void)
364 {
365         int ch = getch();
366                 
367         switch(ch)
368         {
369                 case 0x0c:      /* control L */
370                         wrefresh(curscr);
371                         break;
372                 
373                 case '\n':
374                 case '\r':
375                         do_menu();
376                         break;
377         }
378 }
379 #endif
380
381 /*---------------------------------------------------------------------------
382  *      main event loop
383  *---------------------------------------------------------------------------*/
384 static void
385 mloop(void)
386 {
387         for(;;)
388         {
389                 fd_set rd, wr, ex;
390
391                 FD_ZERO(&rd);
392                 FD_ZERO(&wr);
393                 FD_ZERO(&ex);
394                 FD_SET(fileno(stdin), &rd);
395                 FD_SET(monsock, &rd);
396
397                 select(monsock+1, &rd, &wr, &ex, NULL);
398
399                 if(FD_ISSET(fileno(stdin), &rd))
400                 {
401 #ifndef WIN32
402                         if(fullscreen && curses_ready)
403                                 kbdrdhdl();
404                         else
405 #endif
406                              if(!fullscreen)
407                                 handle_input();
408                         else
409                                 getchar();
410                 }
411
412                 if(FD_ISSET(monsock, &rd))
413                 {
414                         u_int8_t buf[8192];
415                         int bytes, ret;
416
417                         /* Network transfer may deliver two or more packets concatenated.
418                          * Peek at the header and read only one event at a time... */
419
420                         bytes = recv(monsock, buf, I4B_MON_EVNT_HDR, MSG_PEEK);
421
422                         if(bytes == 0)
423                         {
424                                 close(monsock);
425
426 #ifndef WIN32
427                                 if(curses_ready)
428                                 {
429                                         endwin();
430                                         curses_ready = 0;
431                                 }
432 #endif
433                                 
434                                 mprintf("remote isdnd has closed our connection\n");
435                                 exit(0);
436                         }
437                         else if(bytes < 0)
438                         {
439                                 fprintf(stderr, "recv error: %s\n", strerror(errno));
440                                 close(monsock);
441                                 exit(1);
442                         }
443
444                         if (bytes < I4B_MON_EVNT_HDR)
445                                 continue;       /* errh? something must be wrong... */
446
447                         bytes = I4B_GET_2B(buf, I4B_MON_EVNT_LEN);
448
449                         if(bytes >= sizeof(buf))
450                         {
451                                 fprintf(stderr, "mloop: socket recv buffer overflow %d!\n", bytes);
452                                 break;
453                         }
454
455                         /* now we know the size, it fits, so lets read it! */
456                         
457                         ret = sock_read(monsock, buf, bytes);
458
459                         if(ret == 0)
460                         {
461                                 close(monsock);
462 #ifndef WIN32
463                                 if(curses_ready)
464                                         endwin();
465 #endif
466                                 mprintf("remote isdnd has closed our connection\n");
467                                 exit(0);
468                         }
469                         else if(ret < 0)
470                         {
471                                 mprintf("error reading from isdnd: %s", strerror(errno));
472                                 break;
473                         }
474 #ifdef DEBUG
475                         if(debug & DBG_DUMPALL)
476                                 dump_event(buf, ret, 1);
477 #endif
478                         handle_event(buf, ret);
479                 }
480         }
481 }
482
483 #ifdef DEBUG
484 /*
485  * Dump a complete event packet.
486  */
487 static void
488 dump_event(u_int8_t *msg, int len, int read)
489 {
490         int i;
491
492         if(read)
493                 mprintf("read from socket:");
494         else
495                 mprintf("write to socket:");
496
497         for(i = 0; i < len; i++)
498         {
499                 if(i % 8 == 0)
500                         mprintf("\n%02d: ", i);
501                 mprintf("0x%02x %c  ", msg[i], isprint(msg[i]) ? msg[i] : '.');
502         }
503         mprintf("\n");
504 }
505 #endif
506
507 static void
508 print_logevent(time_t tstamp, int prio, char * what, char * msg)
509 {
510         char buf[256];
511         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
512         mprintf("log: %s prio %d what=%s msg=%s\n", buf, prio, what, msg);
513
514 #ifndef WIN32
515         if(fullscreen)
516         {
517                 if((!debug_noscreen) || (debug_noscreen && (((strcmp(what, "DBG"))) != 0)))
518                 {
519 /*
520  * FreeBSD-current integrated ncurses. Since then it is no longer possible
521  * to write to the last column in the logfilewindow without causing an
522  * automatic newline to occur resulting in a blank line in that window.
523  */
524 #ifdef __DragonFly__
525 #include <osreldate.h>
526 #endif
527 #if defined(__DragonFly__)
528 #warning "FreeBSD ncurses is buggy: write to last column = auto newline!"
529                         wprintw(lower_w, "%s %s %-.*s\n", buf, what,
530                                 COLS-((strlen(buf))+(strlen(what))+3), msg);
531 #else
532                         wprintw(lower_w, "%s %s %-.*s\n", buf, what,
533                                 (int)(COLS-((strlen(buf))+(strlen(what))+2)), msg);
534 #endif
535                         wrefresh(lower_w);
536                 }
537         }
538 #endif
539 }
540
541 static void
542 print_charge(time_t tstamp, int controller, int channel, int units, int estimated)
543 {
544         char buf[256];
545         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
546         mprintf("%s: controller %d, channel %d, charge = %d%s\n",
547                 buf, controller, channel, units, estimated ? " (estimated)" : "");
548 #ifndef WIN32
549         if(fullscreen)
550         {
551                 if(estimated)
552                         display_ccharge(CHPOS(controller, channel), units);
553                 else
554                         display_charge(CHPOS(controller, channel), units);
555         }
556 #endif
557 }
558
559 /*
560  * Print a connect event.
561  * A real monitor would allocate state info for "channel" on this
562  * event.
563  *
564  * Parameters:
565  *      tstamp:         server time of event
566  *      outgoing:       0 = incoming, 1 = outgoing
567  *      controller:     controller number
568  *      channel:        channel no, used to identify this connection until disconnect
569  *      cfgname:        name of config entry/connection
570  *      devname:        device used (e.g. isp0)
571  *      remphone:       phone no of remote side
572  *      locphone:       local phone no
573  */
574 static void
575 print_connect(time_t tstamp, int outgoing, int controller, int channel,
576               char *cfgname, char *devname, char *remphone, char *locphone)
577 {
578         char buf[256];
579
580         if(channel == 0)
581                 remstate[controller].ch1state = 1;
582         else
583                 remstate[controller].ch2state = 1;
584
585         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
586
587         if(outgoing)
588                 mprintf("%s: calling out to '%s' [from msn: '%s']",
589                         buf, remphone, locphone);
590         else
591                 mprintf("%s: incoming call from '%s' [to msn: '%s']",
592                         buf, remphone, locphone);
593         mprintf(", controller %d, channel %d, config '%s' on device '%s'\n",
594                 controller, channel, cfgname, devname);
595
596 #ifndef WIN32
597         if(fullscreen)
598                 display_connect(CHPOS(controller, channel), outgoing, cfgname, remphone, devname);
599 #endif
600 }
601
602 /*
603  * Print a disconnect event.
604  * A real monitor could free the "per connection" state
605  * for this channel now
606  */
607 static void
608 print_disconnect(time_t tstamp, int controller, int channel)
609 {
610         char buf[256];
611
612         if(channel == 0)
613                 remstate[controller].ch1state = 0;
614         else
615                 remstate[controller].ch2state = 0;
616
617         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
618
619         mprintf("%s: controller %d, channel %d disconnected\n",
620                 buf, controller, channel);
621
622 #ifndef WIN32
623         if(fullscreen)
624                 display_disconnect(CHPOS(controller, channel));
625 #endif
626 }
627
628 /*
629  * Print an up- or down event
630  */
631 static void
632 print_updown(time_t tstamp, int controller, int channel, int isup)
633 {
634         char buf[256];
635         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
636         mprintf("%s: channel %d is %s\n",
637                 buf, channel, isup ? "up" : "down");
638 }
639
640 /*
641  * Print l1 / l2 status
642  */
643 static void
644 print_l12stat(time_t tstamp, int controller, int layer, int state)
645 {
646         char buf[256];
647         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
648
649         mprintf("%s: layer %d change on controller %d: %s\n",
650                 buf, layer, controller, state ? "up" : "down");
651 #ifndef WIN32
652         if(fullscreen)
653                 display_l12stat(controller, layer, state);
654 #endif
655 }
656
657 /*
658  * Print TEI
659  */
660 static void
661 print_tei(time_t tstamp, int controller, int tei)
662 {
663         char buf[256];
664         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
665
666         mprintf("%s: controller %d, TEI is %d\n",
667                 buf, controller, tei);
668
669 #ifndef WIN32
670         if(fullscreen)
671                 display_tei(controller, tei);
672 #endif
673 }
674
675 /*
676  * Print accounting information
677  */
678 static void
679 print_acct(time_t tstamp, int controller, int channel, int obytes, int obps,
680            int ibytes, int ibps)
681 {
682         char buf[256];
683         strftime(buf, sizeof(buf), I4B_TIME_FORMAT, localtime(&tstamp));
684
685         mprintf("%s: controller %d, channel %d: %d obytes, %d obps, %d ibytes, %d ibps\n",
686                 buf, controller, channel, obytes, obps, ibytes, ibps);
687 #ifndef WIN32
688         if(fullscreen)
689                 display_acct(CHPOS(controller, channel), obytes, obps, ibytes, ibps);
690 #endif
691 }
692
693 static void
694 print_initialization(void)
695 {
696 #ifndef WIN32
697         if(fullscreen)
698         {
699                 if(curses_ready == 0)
700                         init_screen();
701         }
702         else
703 #endif
704         {
705                 print_menu();
706         }
707 }
708
709 /*
710  * Dispatch one message received from the daemon.
711  */
712 static void
713 handle_event(u_int8_t *msg, int len)
714 {
715         u_int8_t cmd[I4B_MON_ICLIENT_SIZE];
716         int local;      
717         u_int32_t net;
718         u_int32_t mask;
719         u_int32_t who;
720         static int first = 1;
721         
722         switch(state)
723         {
724                 case ST_INIT:   /* initial data */
725
726                         isdn_major = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMAJOR);
727                         isdn_minor = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMINOR);
728                         nctrl = I4B_GET_2B(msg, I4B_MON_IDATA_NUMCTRL);
729                         nentries = I4B_GET_2B(msg, I4B_MON_IDATA_NUMENTR);
730                         rights = I4B_GET_4B(msg, I4B_MON_IDATA_CLACCESS);
731
732                         mprintf("remote protocol version is %02d.%02d\n", isdn_major, isdn_minor);
733
734                         if(isdn_major != MPROT_VERSION || isdn_minor != MPROT_REL)
735                         {
736                                 fprintf(stderr, "ERROR, remote protocol version mismatch:\n");
737                                 fprintf(stderr, "\tremote major version is %02d, local major version is %02d\n", isdn_major, MPROT_VERSION);
738                                 fprintf(stderr, "\tremote minor version is %02d, local minor version is %02d\n", isdn_minor, MPROT_REL);
739                                 exit(1);
740                         }
741
742                         mprintf("our rights = 0x%x\n", rights);
743
744                         sub_state = 0;                          
745                         first = 1;
746                         
747                         if(nctrl > 0)
748                         {
749                                 state = ST_ICTRL;
750                         }
751                         else if(nentries > 0)
752                         {
753                                 state = ST_IDEV;
754                         }
755                         else
756                         {
757                                 state = ST_ANYEV;
758                                 sleep(2);
759                                 print_initialization();
760                         }
761                         
762                         /* set maximum event mask */
763                         I4B_PREP_CMD(cmd, I4B_MON_CCMD_SETMASK);
764                         I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMAJOR, MPROT_VERSION);
765                         I4B_PUT_2B(cmd, I4B_MON_ICLIENT_VERMINOR, MPROT_REL);
766                         I4B_PUT_4B(cmd, I4B_MON_ICLIENT_EVENTS, ~0U);
767
768 #ifdef DEBUG
769                         if(debug & DBG_DUMPALL)
770                                 dump_event(cmd, sizeof(cmd), 0);
771 #endif
772                         
773                         if((sock_write(monsock, cmd, sizeof(cmd))) == -1)
774                         {
775                                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
776                                 exit(1);
777                         }
778                         break;
779
780                 case ST_ICTRL:  /* initial controller list */
781                         if(first)
782                         {
783                                 first = 0;
784                                 mprintf("%d controller(s) found:\n", nctrl);
785                         }
786                         mprintf("\tcontroller %d: %s\n", sub_state++, msg+I4B_MON_ICTRL_NAME);
787
788                         if(sub_state >= nctrl)
789                         {
790                                 sub_state = 0;
791                                 first = 1;
792                                 if(nentries > 0)
793                                 {
794                                         state = ST_IDEV; /* end of list reached */
795                                 }
796                                 else
797                                 {
798                                         state = ST_ANYEV;
799                                         sleep(2);
800                                         print_initialization();
801                                 }
802                         }
803                         break;
804
805                 case ST_IDEV:   /* initial entry devicename list */
806                         if(first)
807                         {
808                                 first = 0;
809                                 mprintf("%d entries found:\n", nentries);
810                         }
811                         
812                         mprintf("\tentry %d: device %s\n", sub_state++, msg+I4B_MON_IDEV_NAME);
813
814                         strcat(devbuf, msg+I4B_MON_IDEV_NAME);
815                         /* strcat(devbuf, " "); */
816                         
817                         if(sub_state >= nentries)
818                         {
819                                 first = 1;
820                                 state = ST_ANYEV; /* end of list reached */
821                                 sub_state = 0;
822                                 sleep(2);
823                                 print_initialization();
824                         }
825                         break;
826
827                 case ST_ANYEV: /* any event */
828                         switch(I4B_GET_2B(msg, I4B_MON_EVNT))
829                         {
830                                 case I4B_MON_DRINI_CODE:
831                                         state = ST_RIGHT;       /* list of rights entries will follow */
832                                         sub_state = 0;
833                                         sub_state_count = I4B_GET_2B(msg, I4B_MON_DRINI_COUNT);
834                                         mprintf("monitor rights:\n");
835                                         break;
836
837                                 case I4B_MON_DCINI_CODE:
838                                         state = ST_CONNS;
839                                         sub_state = 0;
840                                         sub_state_count = I4B_GET_2B(msg, I4B_MON_DCINI_COUNT);
841                                         mprintf("monitor connections:\n");
842                                         break;
843
844                                 case I4B_MON_LOGEVNT_CODE:
845                                         print_logevent(I4B_GET_4B(msg, I4B_MON_LOGEVNT_TSTAMP),
846                                                 I4B_GET_4B(msg, I4B_MON_LOGEVNT_PRIO),
847                                                 msg+I4B_MON_LOGEVNT_WHAT,
848                                                 msg+I4B_MON_LOGEVNT_MSG);
849                                         break;
850
851                                 case I4B_MON_CHRG_CODE:
852                                         print_charge(I4B_GET_4B(msg, I4B_MON_CHRG_TSTAMP),
853                                                 I4B_GET_4B(msg, I4B_MON_CHRG_CTRL),
854                                                 I4B_GET_4B(msg, I4B_MON_CHRG_CHANNEL),
855                                                 I4B_GET_4B(msg, I4B_MON_CHRG_UNITS),
856                                                 I4B_GET_4B(msg, I4B_MON_CHRG_ESTIMATED));
857                                         break;
858                                         
859                                 case I4B_MON_CONNECT_CODE:
860                                         print_connect(
861                                                 I4B_GET_4B(msg, I4B_MON_CONNECT_TSTAMP),
862                                                 I4B_GET_4B(msg, I4B_MON_CONNECT_DIR),
863                                                 I4B_GET_4B(msg, I4B_MON_CONNECT_CTRL),
864                                                 I4B_GET_4B(msg, I4B_MON_CONNECT_CHANNEL),
865                                                 msg+I4B_MON_CONNECT_CFGNAME,
866                                                 msg+I4B_MON_CONNECT_DEVNAME,
867                                                 msg+I4B_MON_CONNECT_REMPHONE,
868                                                 msg+I4B_MON_CONNECT_LOCPHONE);
869                                         break;
870                                         
871                                 case I4B_MON_DISCONNECT_CODE:
872                                         print_disconnect(
873                                                 I4B_GET_4B(msg, I4B_MON_DISCONNECT_TSTAMP),
874                                                 I4B_GET_4B(msg, I4B_MON_DISCONNECT_CTRL),
875                                                 I4B_GET_4B(msg, I4B_MON_DISCONNECT_CHANNEL));
876                                         break;
877                                         
878                                 case I4B_MON_UPDOWN_CODE:
879                                         print_updown(
880                                                 I4B_GET_4B(msg, I4B_MON_UPDOWN_TSTAMP),
881                                                 I4B_GET_4B(msg, I4B_MON_UPDOWN_CTRL),
882                                                 I4B_GET_4B(msg, I4B_MON_UPDOWN_CHANNEL),
883                                                 I4B_GET_4B(msg, I4B_MON_UPDOWN_ISUP));
884                                         break;
885                                 case I4B_MON_L12STAT_CODE:
886                                         print_l12stat(
887                                                 I4B_GET_4B(msg, I4B_MON_L12STAT_TSTAMP),
888                                                 I4B_GET_4B(msg, I4B_MON_L12STAT_CTRL),
889                                                 I4B_GET_4B(msg, I4B_MON_L12STAT_LAYER),
890                                                 I4B_GET_4B(msg, I4B_MON_L12STAT_STATE));
891                                         break;
892                                 case I4B_MON_TEI_CODE:
893                                         print_tei(
894                                                 I4B_GET_4B(msg, I4B_MON_TEI_TSTAMP),
895                                                 I4B_GET_4B(msg, I4B_MON_TEI_CTRL),
896                                                 I4B_GET_4B(msg, I4B_MON_TEI_TEI));
897                                         break;
898                                 case I4B_MON_ACCT_CODE:
899                                         print_acct(
900                                                 I4B_GET_4B(msg, I4B_MON_ACCT_TSTAMP),
901                                                 I4B_GET_4B(msg, I4B_MON_ACCT_CTRL),
902                                                 I4B_GET_4B(msg, I4B_MON_ACCT_CHAN),
903                                                 I4B_GET_4B(msg, I4B_MON_ACCT_OBYTES),
904                                                 I4B_GET_4B(msg, I4B_MON_ACCT_OBPS),
905                                                 I4B_GET_4B(msg, I4B_MON_ACCT_IBYTES),
906                                                 I4B_GET_4B(msg, I4B_MON_ACCT_IBPS));
907                                         break;
908                                 default:
909                                         mprintf("unknown event code: %d\n", I4B_GET_2B(msg, I4B_MON_EVNT));
910                         }
911                         break;
912
913                 case ST_RIGHT:  /* one record in a list of monitor rights */
914                         rights = I4B_GET_4B(msg, I4B_MON_DR_RIGHTS);
915                         net = I4B_GET_4B(msg, I4B_MON_DR_NET);
916                         mask = I4B_GET_4B(msg, I4B_MON_DR_MASK);
917                         local = I4B_GET_1B(msg, I4B_MON_DR_LOCAL);
918
919                         if(local)
920                         {
921                                 mprintf("\tlocal: rights = %x\n", rights);
922                         }
923                         else
924                         {
925                                 mprintf("\tfrom: %d.%d.%d.%d, mask %d.%d.%d.%d, rights = %x\n",
926                                         (net >> 24) & 0x00ff, (net >> 16) & 0x00ff, (net >> 8) & 0x00ff, net & 0x00ff,
927                                         (mask >> 24) & 0x00ff, (mask >> 16) & 0x00ff, (mask >> 8) & 0x00ff, mask & 0x00ff,
928                                         rights);
929                         }
930
931                         sub_state++;
932
933                         if(sub_state >= sub_state_count)
934                         {
935                                 state = ST_ANYEV;
936                                 print_initialization();
937                         }
938                         break;
939
940                 case ST_CONNS:
941                         who = I4B_GET_4B(msg, I4B_MON_DC_WHO);
942                         rights = I4B_GET_4B(msg, I4B_MON_DC_RIGHTS);
943
944                         mprintf("\tfrom: %d.%d.%d.%d, rights = %x\n",
945                                 (who >> 24) & 0x00ff, (who >> 16) & 0x00ff, (who >> 8) & 0x00ff, who & 0x00ff,
946                                 rights);
947
948                         sub_state++;
949
950                         if(sub_state >= sub_state_count)
951                         {
952                                 state = ST_ANYEV;
953                                 print_initialization();
954                         }
955                         break;
956
957                 default:
958                         mprintf("unknown event from remote: local state = %d, evnt = %x, len = %d\n",
959                                 state, I4B_GET_2B(msg, I4B_MON_EVNT), len);
960         }
961 }
962
963 /*
964  * Process input from user
965  */
966 static void
967 handle_input(void)
968 {
969         char buf[1024];
970         int channel, controller;
971         
972         fgets(buf, sizeof(buf), stdin);
973
974         switch(atoi(buf))
975         {
976                 case 1:
977                     {
978                         u_int8_t cmd[I4B_MON_DUMPRIGHTS_SIZE];
979                         I4B_PREP_CMD(cmd, I4B_MON_DUMPRIGHTS_CODE);
980 #ifdef DEBUG
981                         if(debug & DBG_DUMPALL)
982                                 dump_event(cmd, I4B_MON_DUMPRIGHTS_SIZE, 0);
983 #endif
984
985                         if((sock_write(monsock, cmd, I4B_MON_DUMPRIGHTS_SIZE)) == -1)
986                         {
987                                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
988                                 exit(1);
989                         }
990                     }
991                     break;
992
993                 case 2:
994                     {
995                         u_int8_t cmd[I4B_MON_DUMPMCONS_SIZE];
996                         I4B_PREP_CMD(cmd, I4B_MON_DUMPMCONS_CODE);
997 #ifdef DEBUG
998                         if(debug & DBG_DUMPALL)
999                                 dump_event(cmd, I4B_MON_DUMPMCONS_CODE, 0);
1000 #endif
1001
1002                         if((sock_write(monsock, cmd, I4B_MON_DUMPMCONS_SIZE)) == -1)
1003                         {
1004                                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
1005                                 exit(1);
1006                         }                       
1007                     }
1008                     break;
1009
1010                 case 3:
1011                     {
1012                         u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
1013                         I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
1014 #ifdef DEBUG
1015                         if(debug & DBG_DUMPALL)
1016                                 dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
1017 #endif
1018
1019                         if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
1020                         {
1021                                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
1022                                 exit(1);
1023                         }
1024                     }
1025                     break;
1026                         
1027                 case 4:
1028                     {
1029                         u_int8_t cmd[I4B_MON_HANGUP_SIZE];
1030                         I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
1031                         
1032                         printf("Which controller you wish to hangup? ");
1033                         fgets(buf, sizeof(buf), stdin);
1034                         controller = atoi(buf);
1035                         I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, controller);
1036
1037                         printf("Which channel do you wish to hangup? ");
1038                         fgets(buf, sizeof(buf), stdin);
1039                         channel = atoi(buf);
1040                         I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, channel);
1041                         
1042 #ifdef DEBUG
1043                         if(debug & DBG_DUMPALL)
1044                                 dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
1045 #endif
1046
1047                         if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
1048                         {
1049                                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
1050                                 exit(1);
1051                         }                       
1052                     }
1053                     break;
1054
1055                 case 9:
1056                         close(monsock);
1057                         exit(0);
1058                         break;
1059
1060                 default:
1061                         print_menu();
1062                         break;
1063         }
1064 }
1065
1066 void
1067 reread(void)
1068 {
1069         u_int8_t cmd[I4B_MON_CFGREREAD_SIZE];
1070         I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
1071 #ifdef DEBUG
1072         if(debug & DBG_DUMPALL)
1073                 dump_event(cmd, I4B_MON_CFGREREAD_CODE, 0);
1074 #endif
1075         if((sock_write(monsock, cmd, I4B_MON_CFGREREAD_SIZE)) == -1)
1076         {
1077                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
1078                 exit(1);
1079         }
1080 }
1081
1082 void
1083 hangup(int ctrl, int chan)
1084 {
1085         u_int8_t cmd[I4B_MON_HANGUP_SIZE];
1086
1087         I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
1088         I4B_PUT_4B(cmd, I4B_MON_HANGUP_CTRL, ctrl);
1089         I4B_PUT_4B(cmd, I4B_MON_HANGUP_CHANNEL, chan);
1090                         
1091 #ifdef DEBUG
1092         if(debug & DBG_DUMPALL)
1093                 dump_event(cmd, I4B_MON_HANGUP_CHANNEL, 0);
1094 #endif
1095
1096         if((sock_write(monsock, cmd, I4B_MON_HANGUP_SIZE)) == -1)
1097         {
1098                 fprintf(stderr, "sock_write failed: %s\n", strerror(errno));
1099                 exit(1);
1100         }                       
1101 }
1102
1103 /*
1104  * Display menu
1105  */
1106 static void
1107 print_menu(void)
1108 {
1109         if(!fullscreen)
1110         {
1111                 printf("Menu: <1> display rights,     <2> display monitor connections,\n");
1112                 printf("      <3> reread config file, <4> hangup \n");
1113                 printf("      <9> quit isdnmonitor\n");
1114                 fflush(stdout);
1115         }
1116 }
1117
1118 static ssize_t
1119 sock_read(int fd, void *buf, size_t nbytes)
1120 {
1121         size_t nleft;
1122         ssize_t nread;
1123         unsigned char *ptr;
1124
1125         ptr = buf;
1126         nleft = nbytes;
1127
1128         while(nleft > 0)
1129         {
1130                 if((nread = read(fd, ptr, nleft)) < 0)
1131                 {
1132                         if(errno == EINTR)
1133                         {
1134                                 nread = 0;
1135                         }
1136                         else
1137                         {
1138                                 return(-1);
1139                         }
1140                 }
1141                 else if(nread == 0)
1142                 {
1143                         break; /* EOF */
1144                 }
1145
1146                 nleft -= nread;
1147                 ptr += nread;
1148         }
1149         return(nbytes - nleft);
1150 }
1151
1152 static ssize_t
1153 sock_write(int fd, void *buf, size_t nbytes)
1154 {
1155         size_t nleft;
1156         ssize_t nwritten;
1157         unsigned char *ptr;
1158
1159         ptr = buf;
1160         nleft = nbytes;
1161
1162         while(nleft > 0)
1163         {
1164                 if((nwritten = write(fd, ptr, nleft)) <= 0)
1165                 {
1166                         if(errno == EINTR)
1167                         {
1168                                 nwritten = 0;
1169                         }
1170                         else
1171                         {
1172                                 return(-1);
1173                         }
1174                 }
1175
1176                 nleft -= nwritten;
1177                 ptr += nwritten;
1178         }
1179         return(nbytes);
1180 }
1181
1182 static void
1183 mprintf(char *fmt, ...)
1184 {
1185 #define PRBUFLEN 1024
1186         char buffer[PRBUFLEN];
1187         va_list ap;
1188
1189         va_start(ap, fmt);
1190         vsnprintf(buffer, PRBUFLEN-1, fmt, ap);
1191         va_end(ap);
1192
1193         if(!fullscreen || (fullscreen && (!curses_ready)))
1194                 printf("%s", buffer);
1195         
1196         if(logfilename != NULL)
1197         {
1198                 fprintf(lfp, "%s", buffer);
1199                 fflush(lfp);
1200         }
1201 }
1202
1203 /* EOF */