/*	Copyright (c) 1984 1988 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.	*/

/* $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.
 */

#ident	"$Header: whodo.c 1.12 89/12/05 $"

/* '/etc/whodo -- combines info from utmp file and process table' */
#include   <stdio.h>
#include   <fcntl.h>
#include   <nlist.h>
#include   <time.h>
#include   <sys/types.h>
#include   <sys/param.h>
#include   <sys/devsw.h>

#ifdef  u3b
#include   <sys/seg.h>
#include   <sys/page.h>
#include   <sys/region.h>
#endif /* u3b */

#ifdef u3b2
#include   <sys/immu.h>
#include   <sys/psw.h>
#include   <sys/pcb.h>
#include   <sys/region.h>
#include   <sys/sbd.h>
#include   <sys/sys3b.h>
#endif /* u3b2 */

#ifdef i386
#include   <sys/timer.h>
#include   <sys/resource.h>
#include   <sys/vm.h>
#endif /* i386 */

#if i286
extern long lseek();
#include   <sys/memseg.h>
#include   <sys/dskseg.h>
#endif

#include   <utmp.h>
#include   <pwd.h>
#include   <sys/utsname.h>
#include   <sys/signal.h>
#include   <sys/session.h>
#include   <sys/vnode.h>
#include   <sys/user.h>
#include   <sys/proc.h>
#include   <sys/var.h>
#include   <sys/stat.h>
#include   <sys/dir.h>
#include   <sys/sysmacros.h>

#ifndef pdp11

#define KMEM    "/dev/kmem"
#else
#define KMEM    "/dev/mem"
#endif
#define MEM     "/dev/mem"
#define ENTRY   sizeof(struct psdata)
#define ERR     (-1)
#define PROC    nl[0]
#ifdef i386
#define VAR     nl[1]
#define SESS    nl[2]
#else /* i386 */
#define SWAP    nl[1]
#define VAR     nl[2]
#define SBR     nl[3]
#endif /* i386 */
#define error(str)      fprintf(stderr, "%s: %s\n", arg0, str)
#define prerror(str)    fprintf(stderr, "%s: %s: %s\n", \
                                arg0, str, sys_errlist[errno])

#if pdp11 || vax
#define PROC_STR        "_proc"
#define V_STR           "_v"
#define SWAPLO_STR      "_swplo"
#define SBRPTE_STR      "_sbrpte"
#endif
#if u3b || u370 || u3b15 || u3b2 || i286 || i386
#define PROC_STR        "proc"
#define V_STR           "v"
#define SESS_STR        "sess"
#define SWAPLO_STR      "swplo"
#endif


struct nlist nl[] = {
        { PROC_STR },
#ifdef i386
	{ V_STR },
	{ SESS_STR },
	{ "" },
#else /* i386 */
        { SWAPLO_STR },
        { V_STR },
#ifdef vax
        { SBRPTE_STR },
#endif
        { 0 }
#endif /* i386 */
};

#ifdef i386
/*
 * This ugly hack is done to minimize the number of #if's to change
 * sysV DIRSIZ into DYNIX/ptx(R) DYNIX_MAXNAMLEN.
 */
#undef  DIRSIZ
#ifdef OLD_TTYS
#define DIRSIZ	DYNIX_MAXNAMLEN
#else
#define DIRSIZ	12		/* size of the utmp ut_line field */
#endif /* OLD_TTYS */
#endif /* i386 */

/**
 * file /etc/ps_data is built by ps command,
 * only use 1st part (device info) and
 * 3rd part (/unix namelist info)
 **/
struct psdata {
        char    device[DIRSIZ+1];
        dev_t   dev;    /* major/minor of device */
} ;

#ifndef OLD_TTYS
/*
 * If OLD_TTYS is NOT defined...
 *
 * A linked list of device names is built on the fly for all devices
 * that follow the dev_to_ttyname() scheme.  The names returned by
 * dev_to_ttyname() will always fit into the utmp ut_line field (after
 * the '/dev/' is removed) so it seemed a waste to have DIRSIZ be any
 * bigger than that.
 *
 * The devices that live in /dev directly (ie, console, syscon, altcon)
 * will get cached with the ps_data file (since they are somewhat static).
 * In any case, the ps_data file will get rebuilt every reboot or when
 * /dev changes.  It is unlikely that any tty device in /dev will be
 * bigger than ut_line.  If so, the name is truncated.  This saves
 * LOTS of space in the ps_data file for big systems.
 */
struct devlst {				/* device linked list	*/
	struct devlst	*dnext;		/* next item pointer	*/
	struct psdata	adev;		/* one device		*/
} *devlst;
#endif /* OLD_TTYS */
struct udata {  /* only used for size of entry */
        ushort  userid;
        char    usernm[8];
} ;
int             ndevs;  /* number of dev info entries to follow */
struct psdata   *psptr;

struct proc        *pp;
struct session     *ss;
struct uproc {
#ifdef  pdp11
        char    p_stat;
        char    p_flag;
        ushort  p_uid;
        ushort  p_suid;
        short   p_pgrp;
        short   p_pid;
        short   p_ppid;
#else
#ifndef i386
        char    xx[18];
#endif /* ! i386 */
#endif
        dev_t   p_ttyd;
        time_t  p_time;
        char    p_comm[MAXCOMLEN+1];
};
#ifdef i386
struct uproc    *uproc;
#endif /* i386 */

extern time_t	time();
struct passwd   *getpwnam(), *getpwuid();
struct var      v;
unsigned        size;
unsigned        ssize;
unsigned long   Proc, V, Sess;
int             fd, kmem, memfd, swap;
char            *arg0;
#ifndef i386
long            swaplo;
#endif /* ! i386 */
#ifdef  vax
int             sbrpte;
#endif
extern int      errno;
extern char     *sys_errlist[];
extern char     *optarg;
int             uid;
#ifdef i386
char		*ublock;
struct user     *uarea;
#else
struct user	uarea;
#endif /* i386 */
dev_t		ttyd;

main(argc, argv)
int  argc;
char *argv[];
{
        register struct utmp    *ut;
        register struct tm      *tm;
        register struct proc    *p;
        register struct session  *s;
        struct user     *u, *getuarea();
        struct uproc    *up;
        unsigned long   utmpend;
        struct tm       *localtime();
        struct stat     sbuf;
        struct utsname  uts;
        char            *malloc();
        char            *getty();
        long            clock;
        int             aflag = 0;
        int             uflag = 0;
        int             c;

        arg0 = argv[0];

        /**
         * print current time and date
         **/
        time(&clock);
        printf("%s", ctime(&clock));

        /**
         * print system name
         **/
        uname(&uts);
        printf("%s\n", uts.sysname);

        /**
         * read in device info from ps_data file
         **/
        if((fd = open("/etc/ps_data", O_RDONLY)) == ERR) {
                prerror("open error of /etc/ps_data");
                exit(1);
        }
        /* first int tells how many entries follow */
        if(read(fd, (char *)&ndevs, sizeof(ndevs)) == ERR) {
                prerror("read error of size of device table info");
                exit(1);
        }
        if((psptr = (struct psdata *)malloc(ndevs*ENTRY)) == NULL) {
                prerror("malloc error of ps_data device table");
                exit(1);
        }
        if(read(fd, (char *)psptr, ndevs*ENTRY) == ERR) {
                prerror("read error of /etc/ps_data device info");
                exit(1);
        }
        /**
         * skip past uid data in /etc/ps_data
         **/
        if(read(fd, (char *)&size, sizeof(size)) == ERR) {
                prerror("read error of number of /etc/ps_data uid entries");
                exit(1);
        }
        if(lseek(fd, (long)(size*sizeof(struct udata)), 1) == ERR) {
                prerror("seek error of skipping uid entries in /etc/ps_data");
                exit(1);
        }
        /**
         * fill in the name list table from /etc/ps_data
         **/
        if(read(fd, (char *)nl, sizeof(nl)) == ERR) {
                prerror("read error of namelist data from /etc/ps_data");
                exit(1);
        }
        close(fd);

        /**
         * check offsets into memory for pointers
         **/
        if(PROC.n_value == 0) {
                error("can't find process table");
                exit(1);
        }
        if(VAR.n_value == 0) {
                error("can't find variables table");
                exit(1);
        }
        if(SESS.n_value == 0) {
                error("can't find session table");
                exit(1);
        }
#ifndef i386
        if((swaplo = (long)SWAP.n_value) == 0L) {
                error("can't find base of swap");
                exit(1);
        }
#endif /* ! i386 */
#ifdef  vax
        if(SBR.n_value == 0) {  /* sbrpte */
                error("can't find sbrpte symbol in namelist");
                exit(1);
        }
#endif
        Proc = PROC.n_value;
        V = VAR.n_value;
	Sess = SESS.n_value;
#ifdef  vax
        Proc &= 0x3fffffff;
        V &= 0x3fffffff;
        SBR.n_value &= 0x3fffffff;
        swaplo &= 0x3fffffff;
#endif

        /**
         * open memory file(s)
         **/
        if((kmem = open(KMEM, O_RDONLY)) == ERR) {
                prerror("open error of /dev/kmem");
                exit(1);
        }
        if((memfd  = open(MEM,  O_RDONLY)) == ERR) {
                prerror("open error of /dev/mem");
                exit(1);
        }

        swap = openswap();
#ifndef i386
        initswaplo();
#endif /* ! i386 */
#ifdef  vax
        initsbrpte();
#endif

        /**
         * read in system variable table
         **/
        if(lseek(kmem, (long)V, 0) == ERR) {
                prerror("seek error of variable table");
                exit(1);
        }
        if(read(kmem, (char *)&v, sizeof(struct var)) == ERR) {
                prerror("read error of variable table");
                exit(1);
        }

        /**
         * read in current /etc/utmp file
         **/
        if(stat(UTMP_FILE, &sbuf) == ERR) {
                prerror("stat error of /etc/utmp");
                exit(1);
        }
        size = (unsigned)sbuf.st_size;
        if((ut = (struct utmp *)malloc(size)) == NULL) {
                prerror("malloc error of utmp file");
                exit(1);
        }
        if((fd = open(UTMP_FILE, O_RDONLY)) == ERR) {
                prerror("open error of /etc/utmp");
                exit(1);
        }
        if(read(fd, (char *)ut, size) == ERR) {
                prerror("read error of /etc/utmp");
                exit(1);
        }
        utmpend = (unsigned long)ut + size;  /* ptr to end of utmp data */
        close(fd);

        /**
         * allocate a process table
         **/
#if i386 || u370
        /* These machines store a pointer to the process and session table */
        if(lseek(kmem, (long)Sess, 0) == ERR) {
                prerror("seek error of session table address");
                exit(1);
        }
        if(read(kmem, (char *)&Sess, sizeof(Sess)) == ERR) {
                prerror("read error of session table address ");
                exit(1);
        }
        if(lseek(kmem, (long)Proc, 0) == ERR) {
                prerror("seek error of process table address");
                exit(1);
        }
        if(read(kmem, (char *)&Proc, sizeof(Proc)) == ERR) {
                prerror("read error of process table address ");
                exit(1);
        }
        size = ((unsigned)v.ve_proc - Proc) / sizeof(struct proc);

	/**
	 * It's dangerous to overlay the proc structure
	 **/
        if((uproc = (struct uproc *)malloc(sizeof(struct uproc)*size)) == NULL) {
                prerror("malloc error of user process table");
                exit(1);
        }
#else /* i386 */

        if(sizeof(struct proc) < sizeof(struct uproc)) {
                error("size error: uproc larger than proc");
                exit(1);
        }

        size = ((unsigned)v.ve_proc - PROC.n_value) / sizeof(struct proc);
#endif /* i386 */

        if((pp = (struct proc *)malloc(sizeof(struct proc)*size)) == NULL) {
                prerror("malloc error of process table");
                exit(1);
        }
        if(lseek(kmem, (long)Proc, 0) == ERR) {
                prerror("seek error of process table");
                exit(1);
        }
        if(read(kmem, (char *)pp, (int)(sizeof(struct proc)*size)) == ERR) {
                prerror("read error of process table");
                exit(1);
        }
        ssize = (unsigned)v.v_proc;
        if((ss = (struct session *)malloc(sizeof(struct session)*ssize)) == NULL) {
                prerror("malloc error of session table");
                exit(1);
        }
        if(lseek(kmem, (long)Sess, 0) == ERR) {
                prerror("seek error of session table");
                exit(1);
        }
        if(read(kmem, (char *)ss, (int)(sizeof(struct session)*ssize)) == ERR) {
                prerror("read error of session table");
                exit(1);
        }

        /**
         * gather uarea and plug in applicable data
         * into the process table slot mask
         */
#ifdef i386
        for(p = pp, up = uproc; p < &pp[size]; ++p, ++up) {
#else
        for(p = pp; p < (struct proc *)&pp[size]; ++p) {
#endif /* i386 */
#ifdef i286
                if(! p->p_stat || p->p_stat==SZOMB)
#else
                if(! p->p_stat || p->p_stat==SZOMB || p->p_stat==SIDL)
#endif
                        continue;
		s = &ss[p->p_session - (struct session *)Sess];
                if((u = getuarea(p)) == NULL) {
                        /* process has disappeared */
                        p->p_stat = 0;
                        continue;
                }
		if (s->s_vp) {
			ssize = sizeof(ttyd);
			(void) lseek(kmem, (long)&s->s_vp->v_rdev, 0);
			read(kmem, (char *) &ttyd, ssize);
		} else
			ttyd = 0;
#ifndef i386
                up = (struct uproc *)p;
#endif /* ! i386 */
#ifdef  pdp11
                /* reassign overlay values to remove unwanted
                 * fields and make the uproc structure the
                 * same size as the proc structure.  Do not
                 * change the order of these assignments.
                 */
                up->p_uid = p->p_uid;
                up->p_suid = p->p_suid;
                up->p_pgrp = p->p_pgrp;
                up->p_pid = p->p_pid;
                up->p_ppid = p->p_ppid;
#endif
		if (ttyd) {
			up->p_ttyd = ttyd;
		}
                up->p_time = u->u_utime + u->u_stime;
                strncpy(up->p_comm, u->u_comm, MAXCOMLEN);
        }
        close(kmem);
        close(memfd);

        /**
         * loop through utmp file, printing process info
         * about each logged in user
         **/
        for(; ut < (struct utmp *)utmpend; ut++) {
                if(ut->ut_type != USER_PROCESS)
                        continue;
                tm = localtime(&ut->ut_time);
                printf("\n%-11.14s %-8.8s %2.1d:%2.2d\n",
                        ut->ut_line, ut->ut_name, tm->tm_hour, tm->tm_min);
                uid = getpwnam(ut->ut_name)->pw_uid;
                sproc(ut->ut_pid);
        }
}

sproc(pid)
register short pid;
{
#ifdef  pdp11
        register struct uproc   *p;
#else
        register struct proc    *p;
#endif
        register struct uproc   *up;
        char                    *getty();
        register int            found = 0;

#ifdef  pdp11
        for(p = (struct uproc *)pp; p < &pp[size]; p++) {
#else
#ifdef i386
        for(p = pp, up = uproc; p < &pp[size]; p++, up++) {
#else
        for(p = pp; p < &pp[size]; p++) {
#endif /* i386 */
#endif
                if(! p->p_stat)
                        continue;

                if(pid == p->p_ppid || pid == p->p_pgrp)
                        found++;

                if(found || pid==p->p_pid) {
#ifndef i386
                        up = (struct uproc *)p;
#endif /* ! i386 */
#ifdef i286
                        if(p->p_stat == SZOMB)
#else
                        if(p->p_stat == SZOMB || p->p_stat == SIDL)
#endif
                                printf("    %-11.14s %5d %4.1ld:%2.2ld %s\n",
                                        "  ?", p->p_pid, 0L, 0L, "<defunct>");
                        else {
                                printf("    %-11.14s %5d %4.1ld:%2.2ld %s\n",
                                        getty(up->p_ttyd), p->p_pid,
                                        up->p_time/60L, up->p_time%60L,
                                        up->p_comm);
                        }
                        p->p_stat =  0;
                }
                if(found) {
                        found = 0;
                        sproc(p->p_pid);
                }
        }
}

struct user *
getuarea(p)
register struct proc *p;
{
        register long   addr;
        register int    c;
#ifdef i386
	register int	size;
	char *pp, *malloc();
#endif /* i386 */
#ifdef  vax
        int     ww, spte;
        int     upte[128];
#endif
#ifdef u3b2
	int	i;
#endif
#ifdef i286
	struct memseg memseg;
	struct dskseg dskseg;
	long seekval;
#endif
        fd = memfd;

	/*
	 *	Pages in the user page table are contiguous on a 3b5 
	 *	but not on a 3b2.
	 */	

#ifdef u3b2

	if(sys3b(RDUBLK, p->p_pid, &uarea, sizeof(uarea)) == -1)
		return(NULL);
#endif /* u3b2 */

#ifdef u3b15
	addr = (p->p_addr << BPCSHIFT) - 0x800000;
	lseek(fd,addr,0);
	if (read(fd, (char *)&uarea, sizeof(uarea)) != sizeof(uarea)) {
		perror("read error on ublock");
		return(NULL);
	}
#endif /* u3b15 */

#ifdef  u3b
        addr = p->p_uptbl[0] & PG_ADDR;
        if(lseek(fd, addr, 0) == ERR)
                perror("cannot seek to uarea");
        if(read(fd, &uarea, sizeof(uarea)) != sizeof(uarea)) {
                perror("read error on ublock");
                return(NULL);
        }
#endif /* u3b */

#ifdef  vax
        ww = (int)(p->p_spt + (p->p_nspt-1)*128);
        ww = sbrpte + ((ww&0x3fffffff)>>9)*4;
        lseek(fd,(long)ww&0x3fffffff,0);
        read(fd,&spte, 4);
        if(spte == 0)
                return(NULL);
        lseek(fd, (spte&0x1fffff)<<9,0);
        read(fd, upte, sizeof upte);
        for(c=0; c<USIZE; c++) {
                lseek(fd, (upte[128-USIZE+c]&0x1fffff)<<9, 0);
                     /*
                      * get u page
                      */
                if(read(fd,(char *)(((int *)&uarea)+128*c),512) != 512)
                        return(NULL);
                if(sizeof(struct user) - (c + 1) * 512 <= 0)
                        break;
        }
#endif

#ifdef pdp11
        if (p->p_flag&SLOAD)
                addr = ctob((long)p->p_addr);
        else {
                addr = (p->p_addr+swaplo)<<9;
                fd = swap;
        }
        lseek(fd, addr, 0);
        if (read(fd, (char *)&uarea, sizeof(uarea)) != sizeof(uarea))
                return(NULL);
#endif

#ifdef i286
	seekval = lseek(kmem, 0L, 1);
	if (p->p_umemseg) {
		l_lseek(kmem, (long)(p->p_umemseg), 0);
		r_read(kmem, (char *)&memseg, sizeof(memseg));
		addr = (((long)(memseg.ms_addr))<<9);
	} else if (p->p_udskseg) {
		l_lseek(kmem, (long)(p->p_udskseg), 0);
		r_read(kmem, (char *)&dskseg, sizeof(dskseg));
		addr = (swaplo + dskseg.ds_addr) * (long)512;
	} else return(NULL);
	l_lseek(kmem, seekval, 0);
	l_lseek(fd, addr, 0);
	if (r_read(fd, (char *)&uarea, sizeof(uarea)) != sizeof(uarea))
			return(NULL);
#endif
#ifdef i386
	pp = malloc(ctob(UPAGES) + 511);
	ublock = (char *)(((int)pp + 511) &~ 511);
	uarea = UBTOUSER(ublock);
	if (p->p_flag & SLOAD) {	/* proc is in core */
		size = sizeof(struct user);
		(void) lseek(kmem, (long)p->p_uarea, 0);
		if (read(kmem, (char *) uarea, size) != size)
			return(NULL);
	} else {	/* proc is swapped out */
		size = ctob(UPAGES);
		(void) lseek(swap, (long)dtob(p->p_swaddr), 0);
		if (read(swap, ublock, size) != size)
			return(NULL);
	}
	return(uarea);
#else
	return(&uarea);
#endif /* i386 */
}

char *
getty(dev)
register dev_t dev;
{
        register struct psdata *ps;
	register struct devlst *devlstp;
	register char	*p;
	struct stat	tsb;
	extern char	*dev_to_ttyname();

#ifndef OLD_TTYS
	/* Search the dynamic device list for match */
	for (devlstp = devlst;
			devlstp != (struct devlst *)NULL;
			devlstp = devlstp->dnext) {
		if (dev == devlstp->adev.dev)
			return(devlstp->adev.device);
	}
#endif /* OLD_TTYS */

	/* Search the cached top level /dev entries for match */
        for(ps = psptr; ps < &psptr[ndevs]; ps++) {
                if(ps->dev == dev)
                        return(ps->device);
        }

#ifndef OLD_TTYS
	/* Well, then see if it exists and add it to the dynamic list */
	p = dev_to_ttyname(dev);
	if (stat(p, &tsb) == 0
	&& tsb.st_rdev == dev
	&& (tsb.st_mode&S_IFMT) == S_IFCHR) {
		devlstp = (struct devlst *)calloc(1, sizeof(struct devlst));
		if (devlstp == NULL)
			return("?");

		/* attach to front of list */
		if (devlst != NULL)
			devlstp->dnext = devlst;
		devlst = devlstp;

		devlstp->adev.dev = dev;
		strcpy(devlstp->adev.device, p+5);
		return(p + 5); /* remove the '/dev/' */
	}
#endif /* OLD_TTYS */
	/* Give up */
        return("  ?  ");
}

openswap()
{
        int     swp;

        if((swp = open("/dev/swap", O_RDONLY)) == ERR) {
                prerror("open error of /dev/swap");
                exit(1);
        }
        return(swp);
}

#ifndef i386
initswaplo()
{
        /**
         * read in base of swap
         **/
        if(lseek(kmem, swaplo, 0) == ERR) {
                prerror("seek error of swplo symbol");
                exit(1);
        }
        if(read(kmem, (char *)&swaplo, sizeof(swaplo)) == ERR) {
                prerror("read error of swplo value");
                exit(1);
        }
}
#endif /* ! i386 */

#ifdef vax
initsbrpte()
{
        /**
         * read to find system page tables
         **/
        if(lseek(kmem, (long)SBR.n_value, 0) == ERR) {
                prerror("seek error of sbrpte symbol");
                exit(1);
        }
        if(read(kmem, (char *)&sbrpte, sizeof(sbrpte)) == ERR) {
                prerror("read error of sbrpte value");
                exit(1);
        }
        sbrpte &= 0x3fffffff;
}
#endif

#if i286
l_lseek(ffdd, offsett, whence)
short ffdd, whence;
long offsett;
{
	if (lseek(ffdd, offsett, whence) == -1) {
		fprintf((&_iob[2]), "whodo: l_lseek() error on lseek, %s\n",
				sys_errlist[errno]);
		done(1);
	}
}

r_read(rrdd, rbuf, nbytes)
short rrdd, nbytes;
char *rbuf;
{
	short rbytes;
	rbytes = read(rrdd, rbuf, (int)nbytes);
	if (rbytes != nbytes) {
		fprintf((&_iob[2]),
                      "whodo: r_read() error on read, rbytes=%d, nbytes=%d\n",
                       rbytes, nbytes);
		/* fprintf((&_iob[2]), "whodo: read() error on read %s\n",
				sys_errlist[errno]);     /* */
		done(1);
	}
}

done(exitno)
short exitno;
{
	exit(exitno);
}
#endif
