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