/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: sadc.c 1.4 89/10/26 $"

/*
	sadc.c - writes system activity binary data from /dev/kmem to a
		file or stdout.
	Usage: sadc [t n] [file]
		if t and n are not specified, it writes
		a dummy record to data file. This usage is
		particularly used at system booting.
		If t and n are specified, it writes system data n times to
		file every t seconds.
		In both cases, if file is not specified, it writes
		data to stdout.
*/

/*
 * This is a port of sadc to DYNIX/ptx.  The original structure (as it was)
 * has been preserved with all the extraneous #ifdef's stripped.
 */

/* $Log:	sadc.c,v $
 */


#include <sys/types.h>
#include <sys/param.h>
#include <sys/sysmacros.h>
#define	INKERNEL
#include <sys/file.h>
#undef	INKERNEL
#include <sys/dir.h>
#include <sys/signal.h>
#include <sys/user.h>
#include <sys/vm.h>
#include <sys/buf.h>
#include <sys/pte.h>
#include <sys/plocal.h>
#include <sys/engine.h>
#include <sys/timer.h>
#include <sys/proc.h>
#include <sys/fcntl.h>
#include <sys/flock.h>
#include <sys/var.h>
#include <sys/sec.h>
#include <sys/stat.h>
#include <sys/vnode.h>
#define	INKERNEL
#include <sys/ufsinode.h>
#undef	INKERNEL
#include <sys/mman.h>
#include <sys/stats_disk.h>
#include <sys/stats_tape.h>
#include <nlist.h>
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include "sysinfo.h"
#include "sa.h"

#define X_NENGINE	0
#define X_ENGINE	1
#define X_NONLINE	2
#define X_MEMSTATS	3
#define X_PROCSTATE	4
#define X_V		5
#define X_PROC		6
#define X_INODE		7
#define X_FILE		8
#define	X_FLCKINFO	9
#define X_RESOURCE_OVF	10
#define X_SWAP_NFREE	11
#define X_DS_LIST       12
#define X_DS_NDISKS     13
#define X_DS_ARRAYSIZE  14
#define X_TP_LIST       15
#define X_TP_NTAPES     16
#define X_TP_ARRAYSIZE  17


struct nlist setup[] = {
	{ "Nengine" },
	{ "engine" },
	{ "nonline" },
	{ "mem_stats" },
	{ "procstate", },
	{ "v" },
	{ "proc" },
	{ "inode" },
	{ "file" },
	{ "flckinfo" },
	{ "resource_ovf" },
	{ "swap_nfree" },
	{ "ds_list" },
	{ "ds_ndisks" },
	{ "ds_arraysize" },
	{ "tp_list" },
	{ "tp_ntapes" },
	{ "tp_arraysize" },
	{ "" },
};

#define min(a,b)	((a) > (b) ? (b) : (a))

struct sa d;			/* A system activity record */

struct var tbl;
struct flckinfo flckinfo;

struct stat ubuf,syb;
char *flnm = "/tmp/sa.adrfl";
static int fa = 0;

static int ndevs;		/* number devices monitored */
static int recsz;

int f;				/* file descriptor for /dev/kmem */

/*
 * DYNIX/ptx(R) specific performance statistic related stuff
 */

unsigned Nengine;
int pagesize;

/*
 * Kernel addresses and pointers to the (inode, file, proc) tables as mapped
 * into sadc's address space.
 * In DYNIX, these are allocated dynamically so that what nlist() gets
 * is only the address of the pointer.
 */

struct inode *inodep;		/* kernel address of inode table */
struct file *filep;		/* kernel address of file table */
struct proc *procp;		/* kernel address of proc table */

struct inode *inode;		/* ptr to inode table */
struct file *file;		/* ptr to file table */
struct proc *proc;		/* ptr to proc table */

/*
 * ppriv is used to map the processor "private" data which contains
 * the struct vmmeter kept by each processor
 */
struct ppriv_pages *ppriv;

/*
 * avail_devp is a pointer to the next available entry in the devio
 * array.  It is used to allocate entries for the various types of
 * I/O devices.
 */
struct dev_stats *avail_devp = d.devio;

/*
 * Disk stats
 */
struct disk_stats *dstats;
int ndisks;			/* number of disks monitored */
struct disk_stats_list *ds_list; /* kernel addr of first chunk in list */
unsigned ds_arraysize;
struct dev_stats *diskdevp;	/* ptr to portion of devio array allocated */
				/* to disk statistics */

/*
 * Tape stats
 */
struct tape_stats *tpstats;
int ntapes;			/* number of tapes monitored */
struct tape_stats_list *tp_list; /* kernel addr of first chunk in list */
unsigned tp_arraysize;
struct dev_stats *tapedevp;	/* ptr to portion of devio array allocated */
				/* to tape statistics */

extern char	*malloc();
extern time_t	time();
extern off_t	lseek();
extern mode_t	umask();
extern unsigned short getegid();
extern void	exit(), perror(), free();
extern unsigned sleep();
extern int	getpagesize();
extern char	*mapkmem();	/* forward reference */

main(argc, argv)
	char **argv;
	int argc;
{
	int ct, i;
	unsigned ti;
	int fp;
	long seconds;
	struct stat buf;
	char *fname;
	mode_t cmask;		/* umask */

	pagesize = getpagesize();

	ct = (argc >= 3) ? atoi(argv[2]) : 0;
	seconds = time((long *) 0);
	ti = (argc >= 3) ? atoi(argv[1]) : 0;

	/*
	 * check if the address file is there and not older
	 * than /unix, if so, read the offsets in.
	 * Otherwise, search /unix name list to get the offsets
	 * and create an address file for later usage.
	 */
	if (stat("/unix",&syb) == -1)
		perrexit("sadc: stat /unix failed");
	if ((stat(flnm,&ubuf) == -1) ||
		 (ubuf.st_mtime <= syb.st_ctime))
			goto creafl;
	if ((fa = open(flnm,2)) == -1 ||
			read(fa,setup,sizeof setup) != sizeof setup) {
		if (close(fa) == -1)
			perrexit("sadc: addr file close failed");
		if (unlink(flnm) == -1)
			perrexit("sadc: addr file unlink failed");
creafl:
		cmask = umask((mode_t)0);
		if ((fa = open(flnm, O_RDWR|O_CREAT|O_TRUNC, 0664)) == -1)
			perrexit("sadc: addr file create failed");
		(void) umask(cmask);
		(void) chown(flnm, 0, (gid_t) getegid());

		/*
		 * search name list to get offsets
		 */
		if (nlist("/unix", setup) == -1) {
			(void) fprintf(stderr, "sadc: nlist of /unix failed\n");
			exit(1);
		}

		/*
		 * write offsets to address file
		 */
		if (write(fa, (char *)setup, sizeof(setup)) != sizeof(setup))
			perrexit("sadc: addr file write failed");
		if (close (fa) == -1)
			perrexit("sadc: addr file close failed");
	}
	/*
	 * open /dev/kmem
	 */
	if((f = open("/dev/kmem", 0)) == -1)
		perrexit("sadc: kmem open");

	/*
	 * Get the addresses of the file, inode, and proc tables
	 */
	if (lseek(f, (off_t)setup[X_FILE].n_value, 0) == -1) {
		perrexit("sadc: file pointer lseek");
	}
	if (read(f, (char *)&filep, sizeof(filep)) != sizeof(filep)) {
		perrexit("sadc: file pointer read");
	}
	if (lseek(f, (off_t)setup[X_INODE].n_value, 0) == -1) {
		perrexit("sadc: inode pointer lseek");
	}
	if (read(f, (char *)&inodep, sizeof(inodep)) != sizeof(inodep)) {
		perrexit("sadc: inode pointer read");
	}
	if (lseek(f, (off_t)setup[X_PROC].n_value, 0) == -1) {
		perrexit("sadc: proc pointer lseek");
	}
	if (read(f, (char *)&procp, sizeof(procp)) != sizeof(procp)) {
		perrexit("sadc: proc pointer read");
	}

	/*
	 * Do necessary setup for access the DYNIX/ptx(R) statistics
	 */
	synth_setup();

	/*
	 * Setup for the disk and tape statistics
	 */
	disk_setup();
	tape_setup();
	/*
	 * Get number of disk drives and number of tape drives
	 */
	ndevs = ndisks + ntapes;

	recsz = sizeof(struct sa) - sizeof(d.devio) + ndevs*sizeof(d.devio[0]);

	if (argc == 3 || argc == 1) {

		/*
		 * no data file is specified, direct data to stdout
		 */
		fp = 1;
		/*
		 * write header record
		 */
		if (write(fp, (char *)&ndevs, sizeof(ndevs)) != sizeof(ndevs))
			perrexit("sadc: header write");
	} else {
		fname = (argc==2) ? argv[1] : argv[3];
		/*	check if the data file is there	*/
		/*	check if data file is too old	*/
		if ((stat(fname,&buf) == -1) ||
		 ((seconds - buf.st_mtime) > 86400))
			goto credfl;
		if ((fp = open(fname,2)) == -1){
credfl:

			/*
			 * data file does not exist:
			 * create one and write the header record.
			 */
			if ((fp = creat(fname,00644)) == -1)
				perrexit("sadc: create data file");
			(void) close(fp);
			if ((fp = open (fname,2)) == -1)
				perrexit("sadc: open data file");
			if (lseek(fp,0L,0) == -1)
				perrexit("sadc: header lseek");
			if (write(fp, (char *)&ndevs, sizeof(ndevs))
							!= sizeof(ndevs))
				perrexit("sadc: header write");
		} else {

			/*
			 * data file exist:
			 * position the write pointer to the last good record.
			 */
			if (lseek(fp,
			    -(off_t)((buf.st_size - sizeof(ndevs)) % recsz),2)
			    == -1) {
				perrexit("sadc: last record lseek");
			}
		}
	}

	/*
	 * if n =0 , write the additional dummy record
	 */
	if (ct == 0){
		for (i=0;i<4;i++)
			d.si.cpu[i] = -300;
		d.ts = seconds;
		if (write(fp, (char *)&d, (unsigned) recsz) != recsz)
			perrexit("sadc: dummy record write");
	}

	/*
	 * read in v (struct var which contains table sizes)
	 */
	if (setup[X_V].n_value == 0) {
		(void) fprintf(stderr, "sadc: No struct v\n");
		exit(1);
	}
	if(lseek(f,(long)setup[X_V].n_value,0) == -1)
		perrexit("sadc: struct v lseek");
	if(read(f, &tbl, sizeof(tbl)) == -1)
		perrexit("sadc: struct v read");

	/*
	 * Map the inode, file and proc tables.
	 */

	inode = (struct inode *) mapkmem((char *)inodep,
				(int)(tbl.v_inode * sizeof(struct inode)));
	file = (struct file *) mapkmem((char *)filep,
				(int)(tbl.v_file * sizeof(struct file)));
	proc = (struct proc *) mapkmem((char *)procp,
				(int)(tbl.v_proc * sizeof(struct proc)));

	for(;;) {
		/*
		 * Synthesize sysinfo and minfo from the DYNIX/ptx
		 * performance statistics.  Obtaining these is combined
		 * into one function since pieces of sysinfo and minfo come
		 * from the same DYNIX/ptx(R) structures.
		 */
		synth_stats();

		/*
		 * compute size of system table
		 */
		d.szinode = inodetbl();
		d.szfile = filetbl();
		d.szproc = proctbl();

		if ( setup[X_FLCKINFO].n_value != 0 ) {
			if (lseek(f, (off_t)setup[X_FLCKINFO].n_value, 0) == -1)
				perrexit("sadc: flock lseek");
			if (read(f, &flckinfo, sizeof(struct flckinfo)) == -1) {
				perrexit("sadc: flock read");
			}
		}
		/*
		 * record system table sizes
		 */
		d.szlckr = flckinfo.reccnt;
		/*
		 * record maximum sizes of system tables
		 */
		d.mszlckr = flckinfo.recs;

		/*
		 * record maximum sizes of system tables
		 */
		d.mszinode = tbl.v_inode;
		d.mszfile = tbl.v_file;
		d.mszproc = tbl.v_proc;

		/*
		 * record system tables overflows
		 */
		synth_tbl_ovf();
		/*
		 * get time stamp
		 */
		d.ts = time ((long *) 0);

		/*
		 * Collect I/O stats
		 */
		disk_stats();
		tape_stats();

				
		/*
		 * write data to data file from structure d
		 */
		if (write(fp, (char *)&d, (unsigned) recsz) != recsz)
			perrexit("sadc: record write");
		if(--ct > 0)
			(void) sleep(ti);
		else {
			if (close(fp) == -1)
				perrexit("sadc: data file close");
			exit(0);
		}
	}
}

/*
 * Perform the necessary setup to facilitate reading the DYNIX/ptx
 * statistics necessary to synthesize struct sysinfo and struct minfo.
 */

synth_setup()
{
	struct engine *engine;	/* array of engine structures */
	struct engine *pengine;	/* kernel addresses of engine structures */

	/*
	 * Read number of processors (engines)
	 */
	if (setup[X_NENGINE].n_value) {
		if (lseek(f, (long)setup[X_NENGINE].n_value, 0) == -1)
			perrexit("sadc: Nengine lseek");
		if (read(f, (char *)&Nengine, sizeof(Nengine)) !=
							sizeof(Nengine))
			perrexit("sadc: Nengine read");
	} else {
		(void) fprintf(stderr, "sadc: no processors!\n");
		exit(2);
	}

	/*
	 * Get pointer to engine structures
	 */
	if (setup[X_ENGINE].n_value) {
		if (lseek(f, (long)setup[X_ENGINE].n_value, 0) == -1)
			perrexit("sadc: engine lseek");
		if (read(f, (char *)&pengine, sizeof(pengine)) !=
							sizeof(pengine))
			perrexit("sadc: engine read");
	} else {
		(void) fprintf(stderr, "sadc: no engine structures!\n");
		exit(2);
	}

	engine = (struct engine *)malloc(Nengine * sizeof(struct engine));

	/*
	 * Get pointer to engine structures
	 */
	if (lseek(f, (long) pengine, 0) == -1)
		perrexit("sadc: engine tbl lseek");
	if (read(f, (char *)engine, Nengine * sizeof(struct engine)) !=
						Nengine * sizeof(struct engine))
		perrexit("sadc: engine tbl read");

	ppriv = (struct ppriv_pages *) mapkmem((char *)engine[0].e_local,
				(int)(Nengine * SZPPRIV));
	(void) free(engine);
}

/*
 * Synthesize the System V sysinfo and minfo structures from the statistics
 * collected by DYNIX/ptx(R) and materialize them in the sa structure
 */
synth_stats()
{
	register ulong *upp;
	register ulong *usp;
	register struct plocal *p;
	register i;
	register struct ppriv_pages *prp; 
	unsigned nonline = 0;
	int swap_nfree;			/* free swap space */
	struct vmmeter psum;
	struct procstate procstate;	/* Sample of procstate struct */
	struct mem_stats mem_stats;	/* Sample of mem_stats struct */

	/*
	 * Get number of online processors
	 */
	if (lseek(f, (long) setup[X_NONLINE].n_value, 0) == -1)
		perrexit("sadc: nonline lseek");
	if (read(f, (char *) &nonline, sizeof(nonline)) != sizeof(nonline))
		perrexit("sadc: nonline read");

	if (nonline == 0) {
		(void) fprintf(stderr, "sadc: no online processors!\n");
		exit(2);
	}

	/*
	 * Get mem_stats and procstate
	 */
	if (setup[X_MEMSTATS].n_value) {
		if (lseek(f, (long)setup[X_MEMSTATS].n_value, 0) == -1)
			perrexit("sadc: memstats lseek");
		if (read(f, (char *)&mem_stats, sizeof(struct mem_stats)) !=
						sizeof(struct mem_stats))
			perrexit("sadc: memstats read");
	} else {
		(void) memset((char *) &mem_stats, 0, sizeof(struct mem_stats));
	}

	if (setup[X_PROCSTATE].n_value) {
		if (lseek(f, (long)setup[X_PROCSTATE].n_value, 0) == -1)
			perrexit("sadc: procstate lseek");
		if (read(f, (char *)&procstate, sizeof(struct procstate)) !=
						sizeof(struct procstate))
			perrexit("sadc: procstate read");
	} else {
		(void) memset((char *) &procstate, 0, sizeof(struct procstate));
	}

	/*
	 * Get the free swap space
	 */
	if (setup[X_SWAP_NFREE].n_value) {
		if (lseek(f, (off_t)setup[X_SWAP_NFREE].n_value, 0) == -1)
			perrexit("sadc: swap_nfree lseek");
		if (read(f, (char *)&swap_nfree, sizeof(swap_nfree)) !=
						sizeof(swap_nfree))
			perrexit("sadc: swap_nfree read");
	} else {
		(void) memset((char *) &swap_nfree, 0, sizeof(swap_nfree));
	}


	/*
	 * Sum the per processor data
	 */
	(void) memset((char *)&psum, 0, sizeof(struct vmmeter));

	for (i = 0, prp = ppriv; i < Nengine; i++, prp++) {
		p = (struct plocal *) prp->pp_local;
		usp = &(psum.v_first);
		for (upp = &(p->cnt.v_first); upp < &(p->cnt.v_last);
								upp++, usp++)
			*usp += *upp;
	}

	/*
	 * Synthesize sysinfo (ugly, but someone's gotta do it)
	 */
	(void) memset((char *) &d.si, 0, sizeof(struct sysinfo));

	d.si.cpu[CPU_IDLE] = psum.v_time[V_CPU_IDLE] / nonline;
	d.si.cpu[CPU_USER] = psum.v_time[V_CPU_USER] / nonline;
	d.si.cpu[CPU_KERNEL] = psum.v_time[V_CPU_KERNEL] / nonline;
	d.si.cpu[CPU_WAIT] = procstate.ps_cpuwait / nonline;

	d.si.wait[W_IO] = procstate.ps_wait[PS_W_IO] / nonline;
	d.si.wait[W_SWAP] = (procstate.ps_wait[PS_W_SWAP] +
				procstate.ps_wait[PS_W_PAGE]) / nonline;
	d.si.wait[W_PIO] = procstate.ps_wait[PS_W_PIO] / nonline;
	d.si.bread = psum.v_bread;
	d.si.bwrite = psum.v_bwrite;
	d.si.lread = psum.v_lread;
	d.si.lwrite = psum.v_lwrite + psum.v_delayedwrite;
	d.si.phread = psum.v_phread;
	d.si.phwrite = psum.v_phwrite;

	d.si.swapin = psum.v_swpin;
	d.si.swapout = psum.v_swpout;
	/*
	 * Convert to 512 byte blocks swapped in/out (from pages)
	 */
	d.si.bswapin = psum.v_pswpin * NBPG / 512;
	d.si.bswapout = psum.v_pswpout * NBPG / 512;

	d.si.pswitch = psum.v_swtch;
	d.si.syscall = psum.v_syscall;
	d.si.sysread = psum.v_sysread;
	d.si.syswrite = psum.v_syswrite;
	d.si.sysfork = psum.v_cntfork;
	d.si.sysexec = psum.v_cntexec;

	d.si.runque = procstate.ps_rqsum;
	d.si.runocc = procstate.ps_rqocc;
	d.si.swpque = procstate.ps_swinqsum;
	d.si.swpocc = procstate.ps_swinqocc;

	d.si.iget = psum.v_iget;
	d.si.namei = psum.v_lookup;
	d.si.dirblk = psum.v_dirblk;

	d.si.readch = psum.v_readch;
	d.si.writech = psum.v_writech;

	d.si.rcvint = psum.v_rcvint;
	d.si.xmtint = psum.v_xmtint;
	d.si.mdmint = psum.v_mdmint;
	d.si.rawch = psum.v_rawch;
	d.si.canch = psum.v_canch;
	d.si.outch = psum.v_outch;

	d.si.msg = psum.v_msg;
	d.si.sema = psum.v_sema;

	/*
	 * Synthesize minfo
	 */

	d.mi.freemem[0] = mem_stats.m_freemem[0];
	d.mi.freemem[1] = mem_stats.m_freemem[1];

	d.mi.freeswap = swap_nfree;
	
	d.mi.vfault = psum.v_faults;
	d.mi.demand = psum.v_pgpgin_file + psum.v_zfod;
	d.mi.swap = psum.v_pgpgin_swap;
	d.mi.cache = psum.v_pgfrec + psum.v_pgdrec;
	d.mi.file = psum.v_pgpgin_file;
}

synth_tbl_ovf()
{
	struct resource_ovf ovf;

	if (setup[X_RESOURCE_OVF].n_value) {
		if (lseek(f, (off_t)setup[X_RESOURCE_OVF].n_value, 0) == -1)
			perrexit("sadc: resource_ovf lseek");
		if (read(f, (char *)&ovf, sizeof(struct resource_ovf)) == -1)
			perrexit("sadc: resource_ovf read");
	}
	d.inodeovf = ovf.ov_inode;
	d.fileovf = ovf.ov_file;
	d.procovf = ovf.ov_proc;
}

/*
 * Do initial setup for access to the disk statistics kept by DYNIX/ptx.
 */

disk_setup()
{

	if (setup[X_DS_NDISKS].n_value == 0 || setup[X_DS_LIST].n_value == 0 ||
			setup[X_DS_ARRAYSIZE].n_value == 0) {
		ndisks = 0;
		return;
	}

	/*
	 * Get number of disks
	 */
	if (lseek(f, (long)setup[X_DS_NDISKS].n_value, 0) == -1)
		perrexit("sadc: ndisks lseek");

	if (read(f, (char *)&ndisks, sizeof(ndisks)) != sizeof(ndisks))
		perrexit("sadc: ndisks read");

	/*
	 * Get the address of the disk statistics list
	 */
	if (lseek(f, (long)setup[X_DS_LIST].n_value, 0) == -1)
		perrexit("sadc: disk stats list lseek");

	if (read(f, (char *)&ds_list, sizeof(ds_list)) != sizeof(ds_list))
		perrexit("sadc: disk stats list read");

	/*
	 * get the number of disk stats structures allocated in a list element
	 */
	if (lseek(f, (long)setup[X_DS_ARRAYSIZE].n_value, 0) == -1)
		perrexit("sadc: ds_arraysize lseek");

	if (read(f, (char *)&ds_arraysize, sizeof(ds_arraysize)) !=
							sizeof(ds_arraysize))
		perrexit("sadc: ds_arraysize read");

	/*
	 * Allocate storage for the disk stats
	 */
	dstats = (struct disk_stats *)malloc(ndisks*sizeof(struct disk_stats));
	if (dstats == NULL) {
		(void) fprintf(stderr, "sadc: no memory for disk stats\n");
		exit(1);
	}
	/*
	 * Allocate a portion of the devio array for the disk stats
	 */
	diskdevp = avail_devp;
	avail_devp += ndisks;
}

/*
 * disk_stats() reads the disk statistics from the kernel
 * and updates the devio array in the sa structure
 */
disk_stats()
{
	register int i;
	register struct disk_stats *dsp;
	register struct dev_stats *dp;
	struct disk_stats_list *dl;
	struct disk_stats_list dlist;
	int j, n;

	/*
	 * Read the disk stats by traversing the list of arrays of disk_stats
	 * structures.  Be careful to only read stats for the number of disks
	 * configured for when monitor started up in case one was added later.
	 */

	dl = ds_list;
	n = ndisks;	/* only read stats for the number of disks cfg'd */
	dsp = dstats;

	while (dl != NULL && n > 0) {
		int n_to_read;

		if (lseek(f, (off_t)dl, 0) == -1) {
			perrexit("sadc: dlist lseek error: ");
		}
		if (read(f, (char *)&dlist, sizeof(struct disk_stats_list))
					!= sizeof(struct disk_stats_list)) {
			perrexit("sadc: dlist read error: ");
		}
		if (lseek(f, (off_t)dlist.dl_stats, 0) == -1) {
			perrexit("sadc: dstats lseek error: ");
		}
		n_to_read = min(n, dlist.dl_used);
		if (read(f, (char *)dsp, n_to_read *
				sizeof(struct disk_stats)) !=
				n_to_read * sizeof(struct disk_stats)) {
			perrexit("sadc: dstats read error: ");
		}
		dsp += n_to_read;
		n -= n_to_read;
		dl = dlist.dl_next;
	}

	/*
	 * Now, convert the stats to the form desired by sar
	 */
	for (dsp = dstats, dp = diskdevp, i = 0; i < ndisks; i++, dsp++, dp++) {
		(void) strcpy(dp->d_name, dsp->ds_name);
		/*
		 * Accumulate number of R/W's and blocks transferred
		 */
		dp->d_ops = 0;
		dp->d_blks = 0;
		for (j = 0; j < B_TYPES; j++) {
			dp->d_ops += dsp->ds_reads[j] + dsp->ds_writes[j];
			dp->d_blks += dsp->ds_rdblks[j] + dsp->ds_wtblks[j];
		}
		/*
		 * Add in "other", probably ioctl, operations
		 */
		dp->d_ops += dsp->ds_misc;
		dp->d_blks += dsp->ds_miscblks;
		/*
		 * Convert the active and response times to milliseconds
		 * They are kept in microseconds by DYNIX/ptx.
		 */
		dp->d_active = (dsp->ds_active[1] * 4294967296.0 +
			dsp->ds_active[0]) / 1000;
		dp->d_resp = (dsp->ds_resp[1] * 4294967296.0 +
			dsp->ds_resp[0]) / 1000;
	}
}


/*
 * Do initial setup for access to the tape statistics kept by DYNIX/ptx.
 */

tape_setup()
{

	if (setup[X_TP_NTAPES].n_value == 0 || setup[X_TP_LIST].n_value == 0 ||
			setup[X_TP_ARRAYSIZE].n_value == 0) {
		ntapes = 0;
		return;
	}

	/*
	 * Get number of tapes
	 */
	if (lseek(f, (long)setup[X_TP_NTAPES].n_value, 0) == -1)
		perrexit("sadc: ntapes lseek");

	if (read(f, (char *)&ntapes, sizeof(ntapes)) != sizeof(ntapes))
		perrexit("sadc: ntapes read");

	/*
	 * Get the address of the tape statistics list
	 */
	if (lseek(f, (long)setup[X_TP_LIST].n_value, 0) == -1)
		perrexit("sadc: tape stats list lseek");

	if (read(f, (char *)&tp_list, sizeof(tp_list)) != sizeof(tp_list))
		perrexit("sadc: tape stats list read");

	/*
	 * get the number of tape stats structures allocated in a list element
	 */
	if (lseek(f, (long)setup[X_TP_ARRAYSIZE].n_value, 0) == -1)
		perrexit("sadc: tp_arraysize lseek");

	if (read(f, (char *)&tp_arraysize, sizeof(tp_arraysize)) !=
							sizeof(tp_arraysize))
		perrexit("sadc: tp_arraysize read");

	/*
	 * Allocate storage for the tape stats
	 */
	tpstats = (struct tape_stats *)malloc(ntapes*sizeof(struct tape_stats));
	if (tpstats == NULL) {
		(void) fprintf(stderr, "sadc: no memory for tape stats\n");
		exit(1);
	}

	/*
	 * Allocate a portion of the devio array for the tape stats
	 */
	tapedevp = avail_devp;
	avail_devp += ntapes;
}

/*
 * tape_stats() reads the tape statistics from the kernel
 * and updates the devio array in the sa structure
 */
tape_stats()
{
	register int i;
	register struct tape_stats *tsp;
	register struct dev_stats *dp;
	struct tape_stats_list *tl;
	struct tape_stats_list tlist;
	int n;

	/*
	 * Read the tape stats by traversing the list of arrays of tape_stats
	 * structures.  Be careful to only read stats for the number of tapes
	 * configured for when monitor started up in case one was added later.
	 */

	tl = tp_list;
	n = ntapes;	/* only read stats for the number of tapes cfg'd */
	tsp = tpstats;

	while (tl != NULL && n > 0) {
		int n_to_read;

		if (lseek(f, (off_t)tl, 0) == -1) {
			perrexit("sadc: tlist lseek error: ");
		}
		if (read(f, (char *)&tlist, sizeof(struct tape_stats_list))
					!= sizeof(struct tape_stats_list)) {
			perrexit("sadc: tlist read error: ");
		}
		if (lseek(f, (off_t)tlist.tl_stats, 0) == -1) {
			perrexit("sadc: tpstats lseek error: ");
		}
		n_to_read = min(n, tlist.tl_used);
		if (read(f, (char *)tsp, n_to_read *
				sizeof(struct tape_stats)) !=
				n_to_read * sizeof(struct tape_stats)) {
			perrexit("sadc: tpstats read error: ");
		}
		tsp += n_to_read;
		n -= n_to_read;
		tl = tlist.tl_next;
	}

	/*
	 * Now, convert the stats to the form desired by sar
	 */
	for (tsp = tpstats, dp = tapedevp, i = 0; i < ntapes; i++,tsp++,dp++) {
		(void) strcpy(dp->d_name, tsp->tp_name);
		/*
		 * Accumulate number of operations and blocks transferred
		 */
		dp->d_ops = tsp->tp_reads + tsp->tp_writes + tsp->tp_misc;
		dp->d_blks = tsp->tp_rdblks + tsp->tp_wtblks;
		/*
		 * Convert the active and response times to milliseconds
		 * They are kept in microseconds by DYNIX/ptx.
		 */
		dp->d_active = (tsp->tp_active[1] * 4294967296.0 +
			tsp->tp_active[0]) / 1000;
		dp->d_resp = (tsp->tp_resp[1] * 4294967296.0 +
			tsp->tp_resp[0]) / 1000;
	}
}

inodetbl()
{
	register struct inode *ip;
	register i, n;

	for (i = n = 0, ip = inode; i < tbl.v_inode; i++, ip++)
		if (ip->i_vnode.v_count != 0)
			n++;

	return(n);
}

filetbl()
{
	register struct file *fp;
	register i, n;

	for (i = n = 0, fp = file; i < tbl.v_file; i++, fp++)
		if (fp->f_count != 0)
			n++;
	return(n);
}

proctbl()
{
	register struct proc *p;
	register i, n;

	for (i = n = 0, p = proc; i < tbl.v_proc; i++, p++)
		if(p->p_stat !=NULL)
			n++;
	return(n);
}

#define	MMAP_ADDR	0x100000	/* start mmap at 1 MB */

char *
mapkmem(kernaddr, size)
	char *kernaddr;
	int size;
{
	extern char *sbrk();
	off_t pos;
	int sz, off;
	char *retva;
	static char *va = (char *)MMAP_ADDR;

	pos = (unsigned)kernaddr & ~(pagesize-1);
	off = (unsigned)kernaddr - pos;
	sz = size + off;
	sz = (sz+pagesize-1) & ~(pagesize-1); 
	if (mmap(va, sz, PROT_READ, MAP_SHARED, f, pos) != 0) {
		perrexit("sadc: mmap failed");
	}
	retva = va;
	va += sz;
	return((char *) (retva + off));
}


perrexit(s)
char *s;
{
	perror(s);
	exit(2);
}
