/*
 * 5799-WZQ (C) COPYRIGHT IBM CORPORATION 1987
 * LICENSED MATERIALS - PROPERTY OF IBM
 * REFER TO COPYRIGHT INSTRUCTIONS FORM NUMBER G120-2083
 */
/* $Header:main.c 12.0$ */
/* $ACIS:main.c 12.0$ */
/* $Source: /ibm/acis/usr/src/ucb/netstat/RCS/main.c,v $ */

#ifndef lint
static char *rcsid = "$Header:main.c 12.0$";
#endif

/*
 * Copyright (c) 1983 Regents of the University of California.
 * All rights reserved.  The Berkeley software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#ifndef lint
char copyright[] =
"@(#) Copyright (c) 1983 Regents of the University of California.\n\
 All rights reserved.\n";
#endif not lint

#ifndef lint
static char sccsid[] = "@(#)main.c	5.7 (Berkeley) 5/22/86";
#endif not lint

#include <sys/param.h>
#include <sys/vmmac.h>
#include <sys/socket.h>
#ifdef vax
#include <machine/pte.h>
#endif
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <nlist.h>
#include <stdio.h>

struct nlist nl[] = {
#define	N_MBSTAT	0
	{ "_mbstat" },
#define	N_IPSTAT	1
	{ "_ipstat" },
#define	N_TCB		2
	{ "_tcb" },
#define	N_TCPSTAT	3
	{ "_tcpstat" },
#define	N_UDB		4
	{ "_udb" },
#define	N_UDPSTAT	5
	{ "_udpstat" },
#define	N_RAWCB		6
	{ "_rawcb" },
#ifdef vax
#define	N_SYSMAP	7
	{ "_Sysmap" },
#define	N_SYSSIZE	8
	{ "_Syssize" },
#endif
#ifdef ibm032
#define N_HATIPT	7
	{ "_MMU_HATIPT" },
#define N_HASHMASK	8
	{ "_MMU_HASHMASK" },
#endif
#ifdef ibm370
#define N_SYSMAP	7
	{ "_Sysmap"},
#define N_SYSSIZE	8
	{ "Syssize"},
#endif ibm370
#define	N_IFNET		9
	{ "_ifnet" },
#define	N_HOSTS		10
	{ "_hosts" },
#define	N_RTHOST	11
	{ "_rthost" },
#define	N_RTNET		12
	{ "_rtnet" },
#define	N_ICMPSTAT	13
	{ "_icmpstat" },
#define	N_RTSTAT	14
	{ "_rtstat" },
#define	N_NFILE		15
	{ "_nfile" },
#define	N_FILE		16
	{ "_file" },
#define	N_UNIXSW	17
	{ "_unixsw" },
#define N_RTHASHSIZE	18
	{ "_rthashsize" },
#define N_IDP		19
	{ "_nspcb"},
#define N_IDPSTAT	20
	{ "_idpstat"},
#define N_SPPSTAT	21
	{ "_spp_istat"},
#define N_NSERR		22
	{ "_ns_errstat"},
	"",
};

/* internet protocols */
extern	int protopr();
extern	int tcp_stats(), udp_stats(), ip_stats(), icmp_stats();
extern	int nsprotopr();
extern	int spp_stats(), idp_stats(), nserr_stats();

struct protox {
	u_char	pr_index;		/* index into nlist of cb head */
	u_char	pr_sindex;		/* index into nlist of stat block */
	u_char	pr_wanted;		/* 1 if wanted, 0 otherwise */
	int	(*pr_cblocks)();	/* control blocks printing routine */
	int	(*pr_stats)();		/* statistics printing routine */
	char	*pr_name;		/* well-known name */
} protox[] = {
	{ N_TCB,	N_TCPSTAT,	1,	protopr,
	  tcp_stats,	"tcp" },
	{ N_UDB,	N_UDPSTAT,	1,	protopr,
	  udp_stats,	"udp" },
	{ -1,		N_IPSTAT,	1,	0,
	  ip_stats,	"ip" },
	{ -1,		N_ICMPSTAT,	1,	0,
	  icmp_stats,	"icmp" },
	{ -1,		-1,		0,	0,
	  0,		0 }
};

struct protox nsprotox[] = {
	{ N_IDP,	N_IDPSTAT,	1,	nsprotopr,
	  idp_stats,	"idp" },
	{ N_IDP,	N_SPPSTAT,	1,	nsprotopr,
	  spp_stats,	"spp" },
	{ -1,		N_NSERR,	1,	0,
	  nserr_stats,	"ns_err" },
	{ -1,		-1,		0,	0,
	  0,		0 }
};

char	*system = "/vmunix";
char	*kmemf = "/dev/kmem";
int	kmem;
int	kflag;
int	Aflag;
int	aflag;
int	hflag;
int	iflag;
int	mflag;
int	nflag;
int	rflag;
int	sflag;
int	tflag;
int	fflag;
int	interval;
char	*interface;
int	unit;
char	usage[] = "[ -Aaihmnrst ] [-f address_family] [-I interface] [ interval ] [ system ] [ core ]";
int	debug;

int	af = AF_UNSPEC;

main(argc, argv)
	int argc;
	char *argv[];
{
	int i;
	char *cp, *name;
	register struct protoent *p;
	register struct protox *tp;

	name = argv[0];
	argc--, argv++;
  	while (argc > 0 && **argv == '-') {
		for (cp = &argv[0][1]; *cp; cp++)
		switch(*cp) {

		case 'A':
			Aflag++;
			break;

		case 'a':
			aflag++;
			break;

		case 'h':
			hflag++;
			break;

		case 'i':
			iflag++;
			break;

		case 'm':
			mflag++;
			break;

		case 'n':
			nflag++;
			break;

		case 'r':
			rflag++;
			break;

		case 's':
			sflag++;
			break;

		case 't':
			tflag++;
			break;

		case 'u':
			af = AF_UNIX;
			break;

		case 'f':
			argv++;
			argc--;
			if (strcmp(*argv, "ns") == 0)
				af = AF_NS;
			else if (strcmp(*argv, "inet") == 0)
				af = AF_INET;
			else if (strcmp(*argv, "unix") == 0)
				af = AF_UNIX;
			else {
				fprintf(stderr, "%s: unknown address family\n",
					*argv);
				exit(10);
			}
			break;
			
		case 'I':
			iflag++;
			if (*(interface = cp + 1) == 0) {
				if ((interface = argv[1]) == 0)
					break;
				argv++;
				argc--;
			}
			for (cp = interface; isalpha(*cp); cp++)
				;
			unit = atoi(cp);
			*cp-- = 0;
			break;

		case 'd':
			debug++;
			break;
		default:
use:
			printf("usage: %s %s\n", name, usage);
			exit(1);
		}
		argv++, argc--;
	}
	if (argc > 0 && isdigit(argv[0][0])) {
		interval = atoi(argv[0]);
		if (interval <= 0)
			goto use;
		argv++, argc--;
		iflag++;
	}
	if (argc > 0) {
		system = *argv;
		argv++, argc--;
	}
	nlist(system, nl);
	if (nl[0].n_type == 0) {
		fprintf(stderr, "%s: no namelist\n", system);
		exit(1);
	}
	if (argc > 0) {
		kmemf = *argv;
		kflag++;
	}
	kmem = open(kmemf, 0);
	if (kmem < 0) {
		fprintf(stderr, "cannot open ");
		perror(kmemf);
		exit(1);
	}
	if (kflag) 
		init_sysmap();
	if (mflag) {
		mbpr(nl[N_MBSTAT].n_value);
		exit(0);
	}
	/*
	 * Keep file descriptors open to avoid overhead
	 * of open/close on each call to get* routines.
	 */
	sethostent(1);
	setnetent(1);
	if (iflag) {
		intpr(interval, nl[N_IFNET].n_value);
		exit(0);
	}
	if (hflag) {
		hostpr(nl[N_HOSTS].n_value);
		exit(0);
	}
	if (rflag) {
		if (sflag)
			rt_stats(nl[N_RTSTAT].n_value);
		else
			routepr(nl[N_RTHOST].n_value, nl[N_RTNET].n_value,
				nl[N_RTHASHSIZE].n_value);
		exit(0);
	}
    if (af == AF_INET || af == AF_UNSPEC) {
	setprotoent(1);
	setservent(1);
	while (p = getprotoent()) {

		for (tp = protox; tp->pr_name; tp++)
			if (strcmp(tp->pr_name, p->p_name) == 0)
				break;
		if (tp->pr_name == 0 || tp->pr_wanted == 0)
			continue;
		if (sflag) {
			if (tp->pr_stats)
				(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
					p->p_name);
		} else
			if (tp->pr_cblocks)
				(*tp->pr_cblocks)(nl[tp->pr_index].n_value,
					p->p_name);
	}
	endprotoent();
    }
    if (af == AF_NS || af == AF_UNSPEC) {
	for (tp = nsprotox; tp->pr_name; tp++) {
		if (sflag) {
			if (tp->pr_stats)
				(*tp->pr_stats)(nl[tp->pr_sindex].n_value,
					tp->pr_name);
		} else
			if (tp->pr_cblocks)
				(*tp->pr_cblocks)(nl[tp->pr_index].n_value,
					tp->pr_name);
	}
    }
    if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag)
	    unixpr(nl[N_NFILE].n_value, nl[N_FILE].n_value,
		nl[N_UNIXSW].n_value);
    exit(0);
}

/*
 * Seek into the kernel for a value.
 */
klseek(fd, base, off)
	int fd, base, off;
{

	if (kflag) {
		base = vtop(base);
		if (base == -1)
			panic("bad vtop");
	}
	lseek(fd, base, off);
}

char *
plural(n)
	int n;
{

	return (n != 1 ? "s" : "");
}

#ifdef vax
struct	pte *Sysmap;

vtop(base)
int base;
{
	base &= 0x7fffffff;
	return(ctob(Sysmap[btop(base)].pg_pfnum) + (base & PGOFSET));
}

init_sysmap()
{
	off_t off;

	off = nl[N_SYSMAP].n_value & 0x7fffffff;
	if (off == 0) {
		fprintf(stderr,"no Sysmap\n");
		exit(1);
	}
	lseek(kmem, off, 0);
	nl[N_SYSSIZE].n_value *= 4;
	Sysmap = (struct pte *)malloc(nl[N_SYSSIZE].n_value);
	if (Sysmap == 0) {
		perror("Sysmap");
		exit(1);
	}
	kread(kmem, Sysmap, nl[N_SYSSIZE].n_value);
}
#endif

#ifdef ibm032
#include <machine/mmu.h>

#define SEG_MASK	0xf0000000	/* mask for segment number */
struct hatipt_entry *MMU_HATIPT;
int MMU_HASHMASK;

/*
 * read in hat/ipt so that we can do virtual to real translations
 */
init_sysmap()
{

	register int ptr = wfetch(nl[N_HATIPT].n_value & ~SEG_MASK);
	register int hash = wfetch(nl[N_HASHMASK].n_value & ~SEG_MASK);
	register int size = (hash+1) * sizeof (struct hatipt_entry);

	MMU_HASHMASK = hash;
	if ((MMU_HATIPT = (struct hatipt_entry *) calloc(size, 1)) == 0)
		panic("Sysmap");
	readinfo((char *) MMU_HATIPT, ptr & ~SEG_MASK, size, "hatipt");
}


/*
 * do virtual to real translation using the hat/ipt and the 
 * contents of the segment registers
 */
vtop(where)
	register unsigned int where;
{
	register struct hatipt_entry *ipte;
	register unsigned int rpage, addrtag, vpage, sid;
	register int probes;
	unsigned int page_off;
#define MAX_PROBES 100
	sid = MMU_SID_SYSTEM;
	vpage = btop(where & 0x0FFFFFFF);
	page_off = where & PGOFSET;
	ipte = &MMU_HATIPT[MMU_HASH(sid, vpage)];
if (debug>1) printf("vtop:  where=%x,sid=%x,vpage=%x,ipte=%x\n",where,sid,vpage,ipte);/* DEBUG */
	/* make sure there is an ipt chain */
	if (MMU_ENDCHAIN(ipte->hat_ptr)) { /* hat pointer empty...not found */
if (debug>1) printf("hat chain empty\n");/* DEBUG */
		printf("bad virtual address\n");
		return (-1);
	}
	/* chase down the hat ptr */
	if (debug>1) printf("get_hatptr(ipte=%x)=%x\n",ipte,get_hatptr(ipte));/* DEBUG */
	ipte = &MMU_HATIPT[get_hatptr(ipte) & MMU_HASHMASK];
	if (debug>1) printf("ipte now at %x\n",ipte);/* DEBUG */
	addrtag = (sid << MMU_VPAGE_BITS) | vpage;
	if (debug>1) printf("addrtag=%x\n",addrtag);/* DEBUG */
	/* check addrtag of first element of ipt chain */
	if ((ipte->key_addrtag & 0x1fffffff) == addrtag) {
					  /* same, return vpage + page offset from where 
					  */
if (debug>1) printf("first on chain,=%x\n",ctob(ipte-MMU_HATIPT)+page_off);/* DEBUG */
		return (ctob(ipte - MMU_HATIPT) + page_off);
	}
	/* for second element ff. */
	probes = 0;
	while (!MMU_ENDCHAIN(get_iptptr(ipte))) { /* while there is a next element */
		/* point to the next element */
		ipte = &MMU_HATIPT[get_iptptr(ipte) & MMU_HASHMASK];
if (debug>1) printf("checking ipte at %x\n",ipte);/* DEBUG */
		/* check addrtag for this element against desired one */
		if ((ipte->key_addrtag & 0x1fffffff) == addrtag) {
					  /* same, return vpage + page offset from where 
					  */
if (debug>1) printf("found it, = %x\n",ctob(ipte-MMU_HATIPT)+page_off);/* DEBUG */
			return (ctob(ipte - MMU_HATIPT) + page_off);
		}
		if (probes++ >= MAX_PROBES) {
if (debug>1) printf("VTOP:  loop chasing down an ipt chain!!!\n");/* DEBUG */
			return (-1);
		}
	}
if (debug>1) printf("end of chain...not found\n");/* DEBUG */
	printf("bad virtual address\n");
	return (-1);
}

wfetch(addr)
{
	int value;

	readinfo((char *) &value, addr, sizeof value, "?");
	return(value);
}

readinfo(value,addr,length,name)
char *value;
char *name;
{
	if (debug) printf("read %d bytes of %s from %x\n",length,name,addr);
	lseek(kmem, (long) addr, 0);
	if (read(kmem, value, length) != length)
		panic("read");
}

#endif ibm032
 
#ifdef ibm370

vtop(address_370)
int address_370;
{
	return(-1);
}

init_sysmap()
{
}

#endif ibm370

#ifdef CMU && MACH_VM 
off_t
vtophys(loc)
long	loc;
struct  pte  *Sysmap;       
{
	register	p;
	off_t	newloc;

	newloc = loc & ~0xc0000000;
	p = btop(newloc);
	if ((loc & 0xc0000000) == 0) {
		fprintf(stderr, "Vtophys: translating non-kernel address\n");
		return((off_t) -1);
	}
	if (p >= Syssize) {
		fprintf(stderr, "Vtophys: page out of bound (%d>=%d)\n",
			p, Syssize);
		return((off_t) -1);
	}
	if (Sysmap[p].pg_v == 0
	&& (Sysmap[p].pg_fod || Sysmap[p].pg_pfnum == 0)) {
		fprintf(stderr, "Vtophys: page not valid\n");
		return((off_t) -1);
	}
	loc = (long) (ptob(Sysmap[p].pg_pfnum) + (loc & PGOFSET));
	return(loc);
}
init_sysmap()
{
	/* We must do the sys map first because klseek uses it */
	long	addr;

	Syssize = nl[X_SYSSIZE].n_value;
	Sysmap = (struct pte *)
		calloc((unsigned) Syssize, sizeof (struct pte));
	if (Sysmap == NULL) {
		fprintf(stderr, "Out of space for Sysmap\n");
		exit(1);
	}
	addr = (long) nl[X_SYSMAP].n_value;
	addr &= ~0x80000000;
	(void) lseek(kmem, addr, 0);
	read(kmem, (char *) Sysmap, Syssize * sizeof (struct pte));
}
#endif	CMU && MACH_VM

panic(str)
char *str;
{
	perror(str);
	exit(1);
}

kread(des, buff, length)
char *buff;
{
	if (read(des, buff, length) != length)
		panic("kread");
}
