/*
 * $Id: util.c,v 1.17 2009/02/01 23:33:37 c4chris Exp $
 */

#include "os.h"

/*
 * util.c - miscellaneous utility routines.
 *
 * David A. Curry				Jeffrey C. Mogul
 * Purdue University				Digital Equipment Corporation
 * Engineering Computer Network			Western Research Laboratory
 * 1285 Electrical Engineering Building		250 University Avenue
 * West Lafayette, IN 47907-1285		Palo Alto, CA 94301
 * davy@ecn.purdue.edu				mogul@decwrl.dec.com
 * 
 */
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <curses.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#ifdef IRIX40
#include <mntent.h>
#include <sys/sysmacros.h>
#endif
#ifdef SUNOS4
#include <mntent.h>
#include <exportent.h>
#include <sys/sysmacros.h>
#endif
#ifdef ultrix
#include <sys/mount.h>
#endif
#ifdef __osf__
#include <sys/mount.h>
#endif
#ifdef SVR4
#include <sys/mnttab.h>
#include <sys/sysmacros.h>
#endif
#ifdef LINUX
#include <mntent.h>
#endif

#include "nfswatch.h"
#include "externs.h"
#include "screen.h"

static int auth_comp(const void *, const void *);
static int is_exported(dev_t);
static int nfs_comp(const void *, const void *);
static int fil_comp(const void *, const void *);
static int prc_comp(const void *, const void *);
static int clnt_comp(const void *, const void *);

/*
 * clear_vars - set interval counters to zero.
 */
static void
clear_vars(void)
{
	register int i;

	int_pkt_total = 0;
	int_pkt_drops = 0;
	int_dst_pkt_total = 0;

	for (i = 0; i < PKT_NCOUNTERS; i++)
		pkt_counters[i].pc_interval = 0;

	for (i = 0; i < nnfscounters; i++) {
		memset(nfs_counters[i].nc_proc, 0,
			MAXNFS3PROC * sizeof(Counter));

		nfs_counters[i].nc_interval = 0;
	}

	for (i = 0; i < nfilecounters; i++) {
		memset(fil_counters[i].fc_proc, 0,
			MAXNFS3PROC * sizeof(Counter));

		fil_counters[i].fc_interval = 0;
	}

	for (i = 0; i < MAXNFSPROC; i++) {
		prc_counters[NFSv2][i].pr_interval = 0;
		prc_counters[NFSv2][i].pr_complete = 0;
		prc_counters[NFSv2][i].pr_response = 0;
		prc_counters[NFSv2][i].pr_respsqr = 0;
		prc_counters[NFSv2][i].pr_maxresp = 0;
	}

	for (i = 0; i < MAXNFS3PROC; i++) {
		prc_counters[NFSv3][i].pr_interval = 0;
		prc_counters[NFSv3][i].pr_complete = 0;
		prc_counters[NFSv3][i].pr_response = 0;
		prc_counters[NFSv3][i].pr_respsqr = 0;
		prc_counters[NFSv3][i].pr_maxresp = 0;
	}

	for (i = 0; i < nclientcounters; i++)
		clnt_counters[i].cl_interval = 0;

	for (i = 0; i < nauthcounters; i++)
		auth_counters[i].ac_interval = 0;

	for (i = 0; i < NFSCALLHASHSIZE; i++)
		nfs_calls[i].proto = 0;
}

/*
 * prtime - convert a time to hh:mm:ss.
 */
char *
prtime(time_t sec)
{
	int hh, mm, ss;
	static char tbuf[16];

	hh = sec / 3600;
	sec %= 3600;

	mm = sec / 60;
	sec %= 60;

	ss = sec;

	(void) sprintf(tbuf, "%02d:%02d:%02d", hh, mm, ss);
	return(tbuf);
}

/*
 * error - print an error message preceded by the program name.
 */
void
error(char *str)
{
	char buf[BUFSIZ];

	(void) sprintf(buf, "%s: %s", pname, str);
	(void) perror(buf);
}

/*
 * finish - clean up and exit.
 */
void
finish(int code)
{
#ifndef USE_LINUX
	int i;
#endif

	/*
	 * Close the nit device.
	 */
#ifdef USE_LINUX
	if (ls.pcap != NULL)
	    pcap_close(ls.pcap);
#else
	for (i = 0; i < ninterfaces; i++) {
	    if (if_fd[i] >= 0)
		(void) close(if_fd[i]);
	}
#endif

	/*
	 * End curses.
	 */
	if (screen_inited) {
#ifdef nocrmode
		(void) nocrmode();
#else
		(void) nocbreak();
#endif
		(void) echo();

		(void) move(SCR_PROMPT_Y, SCR_PROMPT_X0);
		(void) clrtoeol();
		(void) refresh();
		(void) endwin();
	}

	if (logging) {
		(void) fprintf(logfp, "#\n# endlog\n#\n");
		(void) fclose(logfp);
	}

	(void) putchar('\n');

	if (code < 0)
		(void) exit(-code);

	(void) exit(0);
}

/*
 * setup_pkt_counters - set up packet counter screen coordinates.
 */
void
setup_pkt_counters(void)
{
	register int i, j;

	memset(pkt_counters, 0, PKT_NCOUNTERS * sizeof(PacketCounter));

	/*
	 * Set up the strings.
	 */
	/* Width                                          1         2  */
	/*                                       12345678901234567890  */
	pkt_counters[PKT_NFS3READ].pc_name    = "NFS3 Read";
	pkt_counters[PKT_NFS3WRITE].pc_name   = "NFS3 Write";
	pkt_counters[PKT_NFSREAD].pc_name     = "NFS Read";
	pkt_counters[PKT_NFSWRITE].pc_name    = "NFS Write";
	pkt_counters[PKT_NFSMOUNT].pc_name    = "NFS Mount";
	pkt_counters[PKT_PORTMAP].pc_name     = "Port Mapper";
	pkt_counters[PKT_RPCAUTH].pc_name     = "RPC Authorization";
	pkt_counters[PKT_OTHERRPC].pc_name    = "Other RPC Packets";
	pkt_counters[PKT_TCP].pc_name         = "TCP Packets";
	pkt_counters[PKT_UDP].pc_name         = "UDP Packets";
	pkt_counters[PKT_ICMP].pc_name        = "ICMP Packets";
	pkt_counters[PKT_ROUTING].pc_name     = "Routing Control";
	pkt_counters[PKT_ARP].pc_name         = "Addr Resolution";
	pkt_counters[PKT_RARP].pc_name        = "Rev Addr Resol";
	pkt_counters[PKT_BROADCAST].pc_name   = "Ether/FDDI Bdcst";
	pkt_counters[PKT_OTHER].pc_name       = "Other Packets";

	/*
	 * Set screen coordinates for everything.
	 */
	for (i = 0, j = PKT_NCOUNTERS/2; i < PKT_NCOUNTERS/2; i++, j++) {
		pkt_counters[i].pc_namex = SCR_PKT_NAME_X;
		pkt_counters[j].pc_namex = SCR_PKT_NAME_X + SCR_MIDDLE;
		pkt_counters[i].pc_namey = SCR_PKT_Y + i;
		pkt_counters[j].pc_namey = SCR_PKT_Y + i;

		pkt_counters[i].pc_intx = SCR_PKT_INT_X;
		pkt_counters[j].pc_intx = SCR_PKT_INT_X + SCR_MIDDLE;
		pkt_counters[i].pc_inty = SCR_PKT_Y + i;
		pkt_counters[j].pc_inty = SCR_PKT_Y + i;

		pkt_counters[i].pc_totx = SCR_PKT_TOT_X;
		pkt_counters[j].pc_totx = SCR_PKT_TOT_X + SCR_MIDDLE;
		pkt_counters[i].pc_toty = SCR_PKT_Y + i;
		pkt_counters[j].pc_toty = SCR_PKT_Y + i;

		pkt_counters[i].pc_pctx = SCR_PKT_PCT_X;
		pkt_counters[j].pc_pctx = SCR_PKT_PCT_X + SCR_MIDDLE;
		pkt_counters[i].pc_pcty = SCR_PKT_Y + i;
		pkt_counters[j].pc_pcty = SCR_PKT_Y + i;
	}
}

/*
 * setup_nfs_counters- setup NFS counter screen coordinates, file system
 *		       names.
 */
void
setup_nfs_counters(void)
{
#if defined(ultrix) || defined(__osf__)
	register int i, j;
#endif
	FILE *fp;
	struct stat st;
	register NFSCounter *nc;
#ifdef IRIX40
	register struct mntent *mnt;
#endif
#ifdef SUNOS4
	register struct mntent *mnt;
#endif
#ifdef SVR4
	struct mnttab mnttab;
	register struct mnttab *mnt = &mnttab;
#endif
#ifdef ultrix
	int dummy, nmnts;
	static struct fs_data fsData[MAXEXPORT];
#endif
#ifdef	__osf__
	int flags, nmnts;
	static struct statfs *mntbufp;
#endif
#ifdef LINUX
	struct mntent *mnt;
#endif

	memset(nfs_counters, 0, MAXEXPORT * sizeof(NFSCounter));

	/*
	 * If we're not watching our own host, we can't look
	 * for mounted file systems.
	 */
	if (strcmp(myhost, dsthost) != 0) {
		sort_nfs_counters();
		learnfs = 1;
		return;
	}

#ifdef IRIX40
	/*
	 * Open the list of mounted file systems.
	 */
	if ((fp = setmntent(MOUNTED, "r")) == NULL) {
		error(MOUNTED);
		finish(-1);
	}

	nc = nfs_counters;

	/*
	 * Save the first MAXEXPORT file systems of type "4.2"
	 * which have been exported.  These are the ones which can
	 * be mounted through NFS.
	 */
	while ((mnt = getmntent(fp)) != NULL) {
		if (strcmp(mnt->mnt_type, MNTTYPE_EFS) != 0
#ifdef MNTTYPE_XFS
		    && strcmp(mnt->mnt_type, MNTTYPE_XFS) != 0
#endif
#ifndef	IRIX51
		    && strcmp(mnt->mnt_type, MNTTYPE_BELL) != 0
#endif
		    )
			continue;

		if (nnfscounters < MAXEXPORT) {
			if (stat(mnt->mnt_dir, &st) < 0)
				continue;

			/*
			 * Not exported; skip it.
			 */
			if (!is_exported(st.st_dev))
				continue;

			nc->nc_dev.Major = major(st.st_dev);
			nc->nc_dev.Minor = minor(st.st_dev);
			nc->nc_name = strdup(mnt->mnt_dir);

			nnfscounters++;
			nc++;
		}
	}

	(void) endmntent(fp);
#endif /* IRIX40 */

#ifdef SUNOS4
	/*
	 * Open the list of mounted file systems.
	 */
	if ((fp = setmntent(MOUNTED, "r")) == NULL) {
		error(MOUNTED);
		finish(-1);
	}

	nc = nfs_counters;

	/*
	 * Save the first MAXEXPORT file systems of type "4.2"
	 * which have been exported.  These are the ones which can
	 * be mounted through NFS.
	 */
	while ((mnt = getmntent(fp)) != NULL) {
		if (strcmp(mnt->mnt_type, MNTTYPE_42) != 0
#ifdef MNTTYPE_PC
		    && strcmp(mnt->mnt_type, MNTTYPE_PC) != 0
#endif
		    && strcmp(mnt->mnt_type, "hsfs") != 0)
			continue;

		if (nnfscounters < MAXEXPORT) {
			if (stat(mnt->mnt_dir, &st) < 0)
				continue;

			/*
			 * Not exported; skip it.
			 */
			if (!is_exported(st.st_dev))
				continue;

			nc->nc_dev.Major = major(st.st_dev);
			nc->nc_dev.Minor = minor(st.st_dev);
			nc->nc_name = strdup(mnt->mnt_dir);

			nnfscounters++;
			nc++;
		}
	}

	(void) endmntent(fp);
#endif /* SUNOS4 */

#ifdef SVR4
	/*
	 * Open the list of mounted file systems.
	 */
	if ((fp = fopen(MOUNTTABLE, "r")) == NULL) {
		error(MOUNTTABLE);
		finish(-1);
	}

	nc = nfs_counters;

	/*
	 * Save the first MAXEXPORT file systems of type "ufs" or "sysv"
	 * which have been shared.  These are the ones which can
	 * be mounted through NFS.
	 */
	while (getmntent(fp, mnt) == 0) {
		if (strcmp(mnt->mnt_fstype, "ufs") != 0 &&
		    strcmp(mnt->mnt_fstype, "hsfs") != 0 &&
		    strcmp(mnt->mnt_fstype, "pcfs") != 0 &&
		    strcmp(mnt->mnt_fstype, "sysv") != 0)
			continue;

		if (nnfscounters < MAXEXPORT) {
			if (stat(mnt->mnt_mountp, &st) < 0)
				continue;

			/*
			 * Not exported; skip it.
			 */
			if (!is_exported(st.st_dev))
				continue;

#ifdef SUNOS5
			nc->nc_dev.Major = getemajor(st.st_dev);
			nc->nc_dev.Minor = geteminor(st.st_dev);
#else
			nc->nc_dev.Major = major(st.st_dev);
			nc->nc_dev.Minor = minor(st.st_dev);
#endif
			nc->nc_name = strdup(mnt->mnt_mountp);

			nnfscounters++;
			nc++;
		}
	}

	(void) fclose(fp);
#endif /* SVR4 */

#ifdef ultrix
	/*
	 * Get the mounted file information.
	 */
	dummy = 0;
	nmnts = getmnt(&dummy, fsData, sizeof(fsData), NOSTAT_MANY, 0);

	if (nmnts < 0) {
		error("getmnt");
		finish(-1);
	}

	nc = nfs_counters;

	/*
	 * Save the first MAXEXPORT file systems which could have been
	 * exported.  These are what can be mounted through NFS.
	 */
	if (nmnts > MAXEXPORT)
		nmnts = MAXEXPORT;

	for (i=0; i < nmnts; i++) {
		if (fsData[i].fd_flags & M_LOCAL) {
			nc->nc_dev.Major = major(fsData[i].fd_dev);
			nc->nc_dev.Minor = minor(fsData[i].fd_dev);
			nc->nc_name = strdup(fsData[i].fd_path);

			nnfscounters++;
			nc++;
		}
	}
#endif /* ultrix */

#ifdef __osf__
	/*
	 * Get the mounted file information.
	 */
	flags = MNT_NOWAIT;	/* avoid hangups on dead NFS servers */
	nmnts = getmntinfo(&mntbufp, flags);
	if (nmnts < 0) {
		error("getmntinfo");
		finish(-1);
	}

	nc = nfs_counters;

	/*
	 * Save the first MAXEXPORT file systems which could have been
	 * exported.  These are what can be mounted through NFS.
	 */
	for (i=0; i < nmnts; i++) {
		if (mntbufp[i].f_flags & M_LOCAL) {
			/*
			 * This is cheating: fsid is "opaque" but we
			 * know what it really holds ...
			 */
			u_long ldev;
			ldev = *(u_long *)&(mntbufp[i].f_fsid);
			nc->nc_dev.Major = major(ldev);
			nc->nc_dev.Minor = minor(ldev);
			nc->nc_name = strdup(mntbufp[i].f_mntonname);

			nnfscounters++;
			nc++;
			if ((nc - nfs_counters) > MAXEXPORT)
			    break;
		}
	}
#endif /* __osf__ */

#ifdef LINUX
	nc = nfs_counters;
	if (access("/var/lib/nfs/etab", R_OK) == 0) {
	  char buf[PATH_MAX << 1];
	  /*
	   * Open the list of exported file systems.
	   */
	  if ((fp = fopen("/var/lib/nfs/etab", "r")) == NULL) {
	    error("/var/lib/nfs/etab");
	    finish(-1);
	  }
	  while (fgets(buf, PATH_MAX << 1, fp) != NULL) {
	    int i, j;
	    char *s = strchr(buf, '\t');
	    if (s == NULL || buf[0] != '/')
	      continue;
	    *s = 0;
	    for (i = 0; i < nnfscounters; i++)
	      if (strcmp(buf, nfs_counters[i].nc_name) == 0)
		break;
	    if (i < nnfscounters)
	      continue;
	    if ((s = strstr(s + 1, "fsid=")) != NULL) {
	      /* I guess Major == 0 does not exist for a filesystem...  */
	      nc->nc_dev.Minor = atoi(s + 5);
	      nc->nc_name = strdup(buf);
	      nnfscounters++;
	      nc++;
	    } else {
	      if (stat(buf, &st) < 0)
		continue;
	      /*
	       * Not exported; skip it.
	       */
	      if (!is_exported(st.st_dev))
		continue;
	      nc->nc_dev.Major = major(st.st_dev);
	      nc->nc_dev.Minor = minor(st.st_dev);
	      /*
	       * Find common ancestor when Major and Minor are equal
	       */
	      for (i = 0; i < nnfscounters; i++)
		if (nfs_counters[i].nc_dev.Major == nc->nc_dev.Major
		    && nfs_counters[i].nc_dev.Minor == nc->nc_dev.Minor)
		  break;
	      if (i < nnfscounters) {
		j = 0;
		while (nfs_counters[i].nc_name[j] != 0
		       && buf[j] != 0
		       && nfs_counters[i].nc_name[j] == buf[j])
		  j += 1;
		while (nfs_counters[i].nc_name[j] != 0)
		  if (j == 1 || nfs_counters[i].nc_name[j] == '/')
		    nfs_counters[i].nc_name[j] = 0;
		  else
		    j -= 1;
		continue;
	      }
	      nc->nc_name = strdup(buf);
	      nnfscounters++;
	      nc++;
	    }
	    if (nnfscounters >= MAXEXPORT)
	      break;
	  }
	  fclose(fp);
	} else {
	  /*
	   * Open the list of mounted file systems.
	   */
	  if ((fp = setmntent(_PATH_MOUNTED, "r")) == NULL) {
		error(_PATH_MOUNTED);
      		finish(-1);
	  }

	  while ((mnt = getmntent(fp)) != NULL) {
		if (strcmp(mnt->mnt_type, "proc") == 0
		    || strcmp(mnt->mnt_type, "devpts") == 0
		    || strcmp(mnt->mnt_type, "usbdevfs") == 0
		    || strcmp(mnt->mnt_type, "tmpfs") == 0)
			continue;

		if (nnfscounters < MAXEXPORT) {
			if (stat(mnt->mnt_dir, &st) < 0)
				continue;

			/*
			 * Not exported; skip it.
			 */
			if (!is_exported(st.st_dev))
				continue;

			nc->nc_dev.Major = major(st.st_dev);
			nc->nc_dev.Minor = minor(st.st_dev);
			nc->nc_name = strdup(mnt->mnt_dir);

			nnfscounters++;
			nc++;
		}
  	  }

	  endmntent(fp);
	}
#endif /* LINUX */

	sort_nfs_counters();
}

/*
 * sort_nfs_counters - sort and assign places on the screen
 */
void
sort_nfs_counters(void)
{
	register int i, j;

	(void) qsort(nfs_counters, nnfscounters, sizeof(NFSCounter), nfs_comp);

	/*
	 * Set screen coordinates for the ones which will be
	 * displayed.
	 */
	for (i = 0, j = NFSLINES/2; i < NFSLINES/2; i++, j++) {
		nfs_counters[i].nc_namex = SCR_NFS_NAME_X;
		nfs_counters[j].nc_namex = SCR_NFS_NAME_X + SCR_MIDDLE;
		nfs_counters[i].nc_namey = SCR_NFS_Y + i;
		nfs_counters[j].nc_namey = SCR_NFS_Y + i;

		nfs_counters[i].nc_intx = SCR_NFS_INT_X;
		nfs_counters[j].nc_intx = SCR_NFS_INT_X + SCR_MIDDLE;
		nfs_counters[i].nc_inty = SCR_NFS_Y + i;
		nfs_counters[j].nc_inty = SCR_NFS_Y + i;

		nfs_counters[i].nc_totx = SCR_NFS_TOT_X;
		nfs_counters[j].nc_totx = SCR_NFS_TOT_X + SCR_MIDDLE;
		nfs_counters[i].nc_toty = SCR_NFS_Y + i;
		nfs_counters[j].nc_toty = SCR_NFS_Y + i;

		nfs_counters[i].nc_pctx = SCR_NFS_PCT_X;
		nfs_counters[j].nc_pctx = SCR_NFS_PCT_X + SCR_MIDDLE;
		nfs_counters[i].nc_pcty = SCR_NFS_Y + i;
		nfs_counters[j].nc_pcty = SCR_NFS_Y + i;
	}
}

/*
 * setup_fil_counters- setup file counter stuff.
 */
void
setup_fil_counters(void)
{
	FILE *fp;
	struct stat st;
	register int i, j;
	char fname[MAXPATHLEN];
	register FileCounter *fc;

	memset(fil_counters, 0, MAXEXPORT * sizeof(FileCounter));

	/*
	 * If we're not watching our own host, we can't look
	 * for individual files.
	 */
	if (strcmp(myhost, dsthost) != 0)
		return;

	/*
	 * Open the list of files to watch.
	 */
	if ((fp = fopen(filelist, "r")) == NULL) {
		error(filelist);
		finish(-1);
	}

	fc = fil_counters;

	/*
	 * Save the first MAXEXPORT file systems of type "4.2"
	 * which have been exported.  These are the ones which can
	 * be mounted through NFS.
	 */
	while (fgets(fname, sizeof(fname), fp) != NULL) {
		fname[strlen(fname)-1] = '\0';

		if (nfilecounters < MAXEXPORT) {
			if (lstat(fname, &st) < 0)
				continue;

#ifndef SVR4
			/*
			 * Not on an exported file system; skip it.
			 */
			if (!is_exported(st.st_dev)) {
				(void) fprintf(stderr, "warning: \"%s\" is not on an exported file system.\n", fname);
				continue;
			}
#endif

			fc->fc_dev.Major = major(st.st_dev);
			fc->fc_dev.Minor = minor(st.st_dev);
			fc->fc_ino = st.st_ino;
			fc->fc_name = strdup(fname);

			nfilecounters++;
			fc++;
		}
	}

	(void) fclose(fp);

	(void) qsort(fil_counters, nfilecounters, sizeof(FileCounter), fil_comp);

	/*
	 * Set screen coordinates for the ones which will be
	 * displayed.
	 */
	for (i = 0, j = NFSLINES/2; i < NFSLINES/2; i++, j++) {
		fil_counters[i].fc_namex = SCR_NFS_NAME_X;
		fil_counters[j].fc_namex = SCR_NFS_NAME_X + SCR_MIDDLE;
		fil_counters[i].fc_namey = SCR_NFS_Y + i;
		fil_counters[j].fc_namey = SCR_NFS_Y + i;

		fil_counters[i].fc_intx = SCR_NFS_INT_X;
		fil_counters[j].fc_intx = SCR_NFS_INT_X + SCR_MIDDLE;
		fil_counters[i].fc_inty = SCR_NFS_Y + i;
		fil_counters[j].fc_inty = SCR_NFS_Y + i;

		fil_counters[i].fc_totx = SCR_NFS_TOT_X;
		fil_counters[j].fc_totx = SCR_NFS_TOT_X + SCR_MIDDLE;
		fil_counters[i].fc_toty = SCR_NFS_Y + i;
		fil_counters[j].fc_toty = SCR_NFS_Y + i;

		fil_counters[i].fc_pctx = SCR_NFS_PCT_X;
		fil_counters[j].fc_pctx = SCR_NFS_PCT_X + SCR_MIDDLE;
		fil_counters[i].fc_pcty = SCR_NFS_Y + i;
		fil_counters[j].fc_pcty = SCR_NFS_Y + i;
	}
}

/*
 * sort_prc_counters - sort and assign places on the screen
 */
void
sort_prc_counters(void)
{
	register int proto;
	register int i;
	register int numlines;

	(void) qsort(prc_counters[NFSv2], MAXNFSPROC, sizeof(ProcCounter), prc_comp);
	(void) qsort(prc_counters[NFSv3], MAXNFSPROC, sizeof(ProcCounter), prc_comp);

	/* Create indirection index */
	for (proto = NFSv2; proto <= NFSHIGHPROTO; proto++) {
	    for (i = 0; i < MAXNFSPROC; i++) {
		prc_countmap[proto][prc_counters[proto][i].pr_type] = i;
	    }

	    /*
	    * Set screen coordinates for the ones which will be
	    * displayed.
	    * NOTE: Unlike other displays, this one has just one column!
	    */
	    numlines = MAXNFS3PROC; /* pick the largest of all the protos */
	    if (NFSPROCLINES < numlines)
		numlines = NFSPROCLINES;

	    for (i = 0; i < numlines; i++) {
		    prc_counters[proto][i].pr_namex = SCR_NFS_NAME_X;
		    prc_counters[proto][i].pr_namey = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_intx = SCR_NFS_INT_X;
		    prc_counters[proto][i].pr_inty = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_totx = SCR_NFS_TOT_X;
		    prc_counters[proto][i].pr_toty = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_pctx = SCR_NFS_PCT_X;
		    prc_counters[proto][i].pr_pcty = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_compx = SCR_NFS_COMP_X;
		    prc_counters[proto][i].pr_compy = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_respx = SCR_NFS_RESP_X;
		    prc_counters[proto][i].pr_respy = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_rsqrx = SCR_NFS_RSQR_X;
		    prc_counters[proto][i].pr_rsqry = SCR_NFS_Y + i;

		    prc_counters[proto][i].pr_rmaxx = SCR_NFS_RMAX_X;
		    prc_counters[proto][i].pr_rmaxy = SCR_NFS_Y + i;
	    }
    }
}

/*
 * setup_proc_counters- setup procedure counter stuff.
 */
void
setup_proc_counters(void)
{
	register int i;

	memset(prc_counters, 0, MAXNFSPROC * sizeof(ProcCounter) * NFSHIGHPROTO);

	for (i = 0; i < MAXNFSPROC; i++) {
	    prc_counters[NFSv2][i].pr_type = i;
	    prc_counters[NFSv2][i].pr_name = nfs_procnams[NFSv2][i];
	}
	for (i = 0; i < MAXNFS3PROC; i++) {
	    prc_counters[NFSv3][i].pr_type = i;
	    prc_counters[NFSv3][i].pr_name = nfs_procnams[NFSv3][i];
	}

	sort_prc_counters();
}

/*
 * sort_clnt_counters - sort and assign places on the screen
 */
void
sort_clnt_counters(void)
{
	register int i, j;
	register int numlines;

	(void) qsort(clnt_counters, nclientcounters, sizeof(ClientCounter),
			clnt_comp);
	ClientHashRebuild();

	/*
	 * Set screen coordinates for the ones which will be
	 * displayed.
	 */
	numlines = nclientcounters;
	if (numlines & 1)	/* round up to even number; nfswatch.h	*/
	    numlines++;		/* must set MAXCLIENTS to even number	*/
	if (NFSLINES < numlines)
	    numlines = NFSLINES;

	for (i = 0, j = numlines/2; i < numlines/2; i++, j++) {
		clnt_counters[i].cl_namex = SCR_NFS_NAME_X;
		clnt_counters[j].cl_namex = SCR_NFS_NAME_X + SCR_MIDDLE;
		clnt_counters[i].cl_namey = SCR_NFS_Y + i;
		clnt_counters[j].cl_namey = SCR_NFS_Y + i;

		clnt_counters[i].cl_intx = SCR_NFS_INT_X;
		clnt_counters[j].cl_intx = SCR_NFS_INT_X + SCR_MIDDLE;
		clnt_counters[i].cl_inty = SCR_NFS_Y + i;
		clnt_counters[j].cl_inty = SCR_NFS_Y + i;

		clnt_counters[i].cl_totx = SCR_NFS_TOT_X;
		clnt_counters[j].cl_totx = SCR_NFS_TOT_X + SCR_MIDDLE;
		clnt_counters[i].cl_toty = SCR_NFS_Y + i;
		clnt_counters[j].cl_toty = SCR_NFS_Y + i;

		clnt_counters[i].cl_pctx = SCR_NFS_PCT_X;
		clnt_counters[j].cl_pctx = SCR_NFS_PCT_X + SCR_MIDDLE;
		clnt_counters[i].cl_pcty = SCR_NFS_Y + i;
		clnt_counters[j].cl_pcty = SCR_NFS_Y + i;
	}
}

/*
 * setup_clnt_counters- setup client counter stuff.
 */
void
setup_clnt_counters(void)
{
	memset(clnt_counters, 0, MAXCLIENTS * sizeof(ClientCounter));

	sort_clnt_counters();
}

/*
 * sort_auth_counters - sort and assign places on the screen.
 */
void
sort_auth_counters(void)
{
	register int i, j;
	register int numlines;

	(void) qsort(auth_counters, nauthcounters, sizeof(AuthCounter),
		     auth_comp);

	/*
	 * Set screen coordinates for the ones which will be displayed.
	 */
	numlines = nauthcounters;

	if (numlines & 1)
		numlines++;

	if (NFSLINES < numlines)
		numlines = NFSLINES;

	for (i = 0, j = numlines / 2; i < numlines / 2; i++, j++) {
		auth_counters[i].ac_namex = SCR_NFS_NAME_X;
		auth_counters[j].ac_namex = SCR_NFS_NAME_X + SCR_MIDDLE;
		auth_counters[i].ac_namey = SCR_NFS_Y + i;
		auth_counters[j].ac_namey = SCR_NFS_Y + i;

		auth_counters[i].ac_intx = SCR_NFS_INT_X;
		auth_counters[j].ac_intx = SCR_NFS_INT_X + SCR_MIDDLE;
		auth_counters[i].ac_inty = SCR_NFS_Y + i;
		auth_counters[j].ac_inty = SCR_NFS_Y + i;

		auth_counters[i].ac_totx = SCR_NFS_TOT_X;
		auth_counters[j].ac_totx = SCR_NFS_TOT_X + SCR_MIDDLE;
		auth_counters[i].ac_toty = SCR_NFS_Y + i;
		auth_counters[j].ac_toty = SCR_NFS_Y + i;

		auth_counters[i].ac_pctx = SCR_NFS_PCT_X;
		auth_counters[j].ac_pctx = SCR_NFS_PCT_X + SCR_MIDDLE;
		auth_counters[i].ac_pcty = SCR_NFS_Y + i;
		auth_counters[j].ac_pcty = SCR_NFS_Y + i;
	}
}

/*
 * auth_comp - compare authentication counters for qsort.
 */
static int
auth_comp(const void *pa, const void *pb)
{
	AuthCounter *a = (AuthCounter *) pa;
	AuthCounter *b = (AuthCounter *) pb;
	if (sortbyusage) {
		if ((long) b->ac_interval == (long) a->ac_interval)
			return((long) b->ac_total - (long) a->ac_total);
		else
			return((long) b->ac_interval - (long) a->ac_interval);
	}
	else {
		return(strcmp(a->ac_name, b->ac_name));
	}
}

/*
 * is_exported - return whether or not a file system is exported.
 */
#ifdef SUNOS4
static int
is_exported(dev_t dev)
{
	FILE *fp;
	struct stat st;
	register dev_t *exp;
	static int nexported = -1;
	register struct exportent *xent;
	static dev_t exported[MAXEXPORT];

	/*
	 * First time through, read the export table and
	 * save all the device numbers.
	 */
	if (nexported < 0) {
		/*
		 * If there's no export file, it must
		 * not be exported.
		 */
		if ((fp = setexportent(TABFILE)) == NULL)
			return(FALSE);

		nexported = 0;

		while ((xent = getexportent(fp)) != NULL) {
			if (stat(xent->xent_dirname, &st) < 0)
				continue;

			if (nexported < MAXEXPORT)
				exported[nexported++] = st.st_dev;
		}

		(void) endexportent(fp);
	}

	/*
	 * Search the exported device numbers for this device number.
	 */
	for (exp = exported; exp < &exported[nexported]; exp++) {
		if (dev == *exp)
			return(TRUE);
	}

	return(FALSE);
}
#endif /* SUNOS4 */

#ifdef SVR4
static int
is_exported(dev_t dev)
{
	FILE *fp;
	register int i;
	struct stat st;
	static int nshared = -1;
	static dev_t shared[MAXEXPORT];
	char line[BUFSIZ], path[BUFSIZ];

	if (nshared < 0) {
		nshared = 0;

		if ((fp = fopen(SHARETAB, "r")) == NULL)
			return(0);

		while (fgets(line, sizeof(line), fp) != NULL) {
			if (*line == '#' || *line == '\n')
				continue;

			sscanf(line, "%s", path);

			if (stat(path, &st) < 0)
				continue;

			if (nshared < MAXEXPORT)
				shared[nshared++] = st.st_dev;
		}

		fclose(fp);
	}

	for (i=0; i < nshared; i++) {
		if (shared[i] == dev)
			return(1);
	}

	return(0);
}
#endif /* SVR4 */

#if defined(sgi) || defined(ultrix) || defined(__osf__) || defined(LINUX)
static int
is_exported(dev_t dev
#if defined(__GNUC__) && !defined(__APPLE_CC__)
	      __attribute__ ((unused))
#endif
	   )
{
	return(TRUE);
}
#endif

/*
 * nfs_comp - compare NFS counters for qsort.
 */
static int
nfs_comp(const void *pa, const void *pb)
{
	NFSCounter *a = (NFSCounter *) pa;
	NFSCounter *b = (NFSCounter *) pb;
	if (sortbyusage) {
	    if (((long)b->nc_interval) == ((long)a->nc_interval))
		return(((long)b->nc_total) - ((long)a->nc_total));
	    else
		return(((long)b->nc_interval) - ((long)a->nc_interval));
	}
	else
		return(strcmp(a->nc_name, b->nc_name));
}

/*
 * fil_comp = compare file counters for qsort.
 */
static int
fil_comp(const void *pa, const void *pb)
{
	FileCounter *a = (FileCounter *) pa;
	FileCounter *b = (FileCounter *) pb;
	if (sortbyusage) {
	    if (((long)b->fc_interval) == ((long)a->fc_interval))
		return(((long)b->fc_total) - ((long)a->fc_total));
	    else
		return(((long)b->fc_interval) - ((long)a->fc_interval));
	}
	else
		return(strcmp(a->fc_name, b->fc_name));
}

/*
 * prc_comp - compare procedure counters for qsort.
 */
static int
prc_comp(const void *pa, const void *pb)
{
	ProcCounter *a = (ProcCounter *) pa;
	ProcCounter *b = (ProcCounter *) pb;
	if (sortbyusage) {
	    if (((long)b->pr_interval) == ((long)a->pr_interval))
		return(((long)b->pr_total) - ((long)a->pr_total));
	    else
		return(((long)b->pr_interval) - ((long)a->pr_interval));
	}
	else
		return(strcmp(a->pr_name, b->pr_name));
}

/*
 * clnt_comp - compare client counters for qsort.
 */
static int
clnt_comp(const void *pa, const void *pb)
{
	ClientCounter *a = (ClientCounter *) pa;
	ClientCounter *b = (ClientCounter *) pb;
	if (sortbyusage) {
	    if (((long)b->cl_interval) == ((long)a->cl_interval))
		return(((long)b->cl_total) - ((long)a->cl_total));
	    else
		return(((long)b->cl_interval) - ((long)a->cl_interval));
	}
	else
		return(strcmp(a->cl_name, b->cl_name));
}

/*
 * usage - print a usage message and exit.
 */
void
usage(void)
{
        fprintf(stderr, "Usage: %s [-dst host] [-src host]", pname);
	fprintf(stderr, " [-server host] [-all] [-dev device]\n");
	fprintf(stderr, "       [-allif] [-f filelist] [-lf logfile] ");
	fprintf(stderr, " [-sf snapfile] [-map mapfile]\n");
	fprintf(stderr, "       [-T maxtime] [-t timeout] [-fs]");
	fprintf(stderr, " [-if] [-auth] [-procs] [-procs3] [-clients]\n");
	fprintf(stderr, "       [-usage] [-l] [-bg]\n");
							
	finish(-1);
}

/*
 * wakeup - wake up for a screen update.
 */
void
wakeup(int sig
#if defined(__GNUC__) && !defined(__APPLE_CC__)
	      __attribute__ ((unused))
#endif
      )
{
	struct timeval tv;

	/*
	 * See if we've exceeded the total time to run.
	 */
	gettimeofday(&tv, 0);

	if ((totaltime > 0) && ((tv.tv_sec - starttime.tv_sec) > totaltime))
		finish(0);

	/*
	 * Re-sort and re-do the labels.
	 */
	if (sortbyusage) {
		sort_nfs_counters();
		sort_prc_counters();
		sort_clnt_counters();
		sort_auth_counters();
	}

	if (!bgflag) {
		label_screen();
		update_screen();
	}

	if (logging)
		update_logfile();

	clear_vars();
}

/*
 * dlt_name - return string giving name for a data link type code
 */
char *
dlt_name(int dlt)
{
	switch(dlt) {
	case DLT_NULL:
		return("no link-layer encapsulation");
	case DLT_EN10MB:
		return("Ethernet");
	case DLT_EN3MB:
		return("Experimental Ethernet");
	case DLT_AX25:
		return("Amateur Radio AX.25");
	case DLT_PRONET:
		return("ProNET");
	case DLT_CHAOS:
		return("Chaosnet");
	case DLT_IEEE802:
		return("IEEE802");
	case DLT_ARCNET:
		return("ARCNET");
	case DLT_SLIP:
		return("SLIP");
	case DLT_PPP:
		return("PPP");
	case DLT_FDDI:
		return("FDDI");
	default:
		return("[unknown LAN type]");
	}
}

static int	nummap = 0;
static char	*fs_maps[1024];

void
setup_map_file(void)
{
	int len;
	FILE *fp;
	char fs[BUFSIZ], alias[BUFSIZ];
	char line[BUFSIZ], trans[BUFSIZ];

	if ((fp = fopen(mapfile, "r")) == NULL) {
		error(mapfile);
		finish(-1);
	}

	while (fgets(line, sizeof(line), fp) != NULL) {
		if (sscanf(line, "%s %s", fs, alias) != 2)
			continue;

		strcpy(trans, fs);
		len = strlen(fs) + 1;

		strcpy(trans + len, alias);
		len += strlen(alias) + 1;

		if ((fs_maps[nummap] = malloc(len)) == NULL) {
			(void) fprintf(stderr, "%s: out of memory.\n", pname);
			finish(-1);
		}

		bcopy(trans, fs_maps[nummap], len);
		nummap++;
	}

	fclose(fp);
}

char *
map_fs_name(char *fs)
{
	int i;

	for (i=0; i < nummap; i++) {
		if (strcmp(fs, fs_maps[i]) == 0)
			return(fs_maps[i] + strlen(fs_maps[i]) + 1);
	}

	return(fs);
}
