[comp.os.minix] VC-changes for ps

root@relay.eu.net (05/26/90)

While using the newly posted VirtCons diffs, I noticed that
PS(1) did not understand a thing about the new devices.  Since
I _always_ use PS(1) instead of hitting the MAP key, this
bothered me a lot.

I therefore modified ps.c to use the VC-devices as well.  All
changes to Ps.c have been marked with #if MUG_VC.  I also changed
the name of the database to /etc/ps_data, since we also used 
that on out V7 and BSD (2.8 and 2.9) systems.

Regards,
	Fred van Kempen
	MINIX User Group Holland.
-------------------------------------------------------------------------
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
#		"End of archive"
# Contents:
#	ps.c          
#
# Wrapped by root@minixug on Thu May 24 13:33:29 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ps.c' -a "${1}" != "-c"
then
   echo "$0: Will not overwrite existing file: 'ps.c'"
else
   echo "x - ps.c (19540 characters)"
sed 's/^X//' <<\END_OF_SHAR >ps.c
X/* ps - print status			Author: Peter Valkenburg */
X
X/* ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990.
X *
X * This is a V7 ps(1) look-alike for MINIX 1.5.0.  It can use a database with
X * information on system addresses as an extra, and has some additional fields.
X * It does not support the 'k' option (i.e. cannot read memory from core file).
X * If you want to compile this for non-IBM PC architectures, the header files
X * require that you have your CHIP, MACHINE etc. defined.
X *
X * VERY IMPORTANT NOTE:
X *	To use ps, kernel/kernel, fs/fs, and mm/mm must be in the /usr/src
X *	(or the #includes below must be changed).  Furthermore, all of
X *	must contain symbol tables.  This can be arranged using the -s flag
X *	and the ast program.  For example in fs, one would have
X *
X *	asld -s -i -o fs $l/head.s $(obj) $l/libc.a $l/end.s >symbol.out
X *	ast -X fs		# include symbol.out in fs
X *
X */
X
X/*
X * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are
X * absent, RECV which replaces WCHAN, and RUID and PGRP that are extras.
X * The info is obtained from the following fields of proc, mproc and fproc:
X * F	- kernel status field, p_flags
X * S	- kernel status field, p_flags; mm status field, mp_flags (R if p_flags
X * 	  is 0; Z if mp_flags == HANGING; T if mp_flags == STOPPED; else W).
X * UID	- mm eff uid field, mp_effuid
X * RUID	- mm real uid field, mp_realuid
X * PID	- mm pid field, mp_pid
X * PPID	- mm parent process index field, mp_parent (used as index in proc).
X * PGRP - mm process group id mp_procgrp
X * ADDR	- kernel physical text address, p_map[T].mem_phys
X * SZ	- kernel physical stack address + stack size - physical text address,
X * 	  p_map[S].mem_phys + p_map[S].mem_len - p_map[T].mem_phys
X * RECV	- kernel process index field for message receiving, p_getfrom
X *	  If sleeping, mm's mp_flags, or fs's fp_task are used for more info.
X * TTY	- fs controlling tty device field, fs_tty.
X * TIME	- kernel user + system times fields, user_time + sys_time
X * CMD	- system process index (converted to mnemonic name obtained by reading
X *	  tasktab array from kmem), or user process argument list (obtained by
X *	  reading reading stack frame; the resulting address is used to get
X *	  the argument vector from user space and converted into a concatenated
X *	  argument list).
X */  	  
X 
X#include <minix/config.h>
X#include <limits.h>
X#include <sys/types.h>
X
X#include <minix/const.h>
X#undef EXTERN				/* <minix/const.h> defined this */
X#define EXTERN				/* so we get proc, mproc and fproc */
X#include <minix/type.h>
X
X#include "/usr/src/kernel/const.h"
X#include "/usr/src/kernel/type.h"
X#include "/usr/src/kernel/proc.h"
X#undef printf				/* kernel's const.h defined this */
X
X#include "/usr/src/mm/mproc.h"
X#include "/usr/src/fs/fproc.h"
X#include "/usr/src/fs/const.h"
X#undef printf				/* fs's const.h defined this */
X
X
X/*----- ps's local stuff below this line ------*/
X
X#include <minix/com.h>
X#include <fcntl.h>
X#include <a.out.h>
X#include <stdio.h>
X
X#define mindev(dev)	(((dev)>>MINOR) & 0377)	/* yield minor device */
X#define majdev(dev)	(((dev)>>MAJOR) & 0377)	/* yield major device */
X
X#define	TTY_MAJ		4			/* fixed tty major device */
X
X/* macro to convert memory offsets to rounded kilo-units */
X#define	off_to_k(off)	((unsigned) (((off) + 512) / 1024))
X
X/* what we think the relevant identifiers in the namelists are */
X#define	ID_PROC		"_proc"		/* from kernel namelist */
X#define	ID_MPROC	"_mproc"	/* from mm namelist */
X#define	ID_FPROC	"_fproc"	/* from fs namelist */
X#define	ID_TASKTAB	"_tasktab"	/* from kernel namelist */
X
X/*
X * Structure for system address info (also layout of ps's database).
X */
Xtypedef struct {
X	struct nlist ke_proc[2], ke_tasktab[2];
X	struct nlist mm_mproc[2];
X	struct nlist fs_fproc[2];
X} sysinfo_t;
X
Xsysinfo_t sysinfo;			/* sysinfo holds actual system info */
X
X#define	NAME_SIZ	(sizeof(sysinfo.ke_proc[0].n_name))	/* 8 chars */
X
X/* what we think the identfiers of the imported variables in this program are */
X#define	PROC	proc
X#define	MPROC	mproc
X#define	FPROC	fproc
X#define	TASKTAB	tasktab
X
X/* default paths for system binaries */
X#if (CHIP == M68000)
X#define KERNEL_PATH	"/usr/src/kernel/kernel.mix"
X#define MM_PATH		"/usr/src/mm/mm.mix"
X#define FS_PATH		"/usr/src/fs/fs.mix"
X#else
X#define KERNEL_PATH	"/usr/src/kernel/kernel"
X#define MM_PATH		"/usr/src/mm/mm"
X#define FS_PATH		"/usr/src/fs/fs"
X# endif
X
X#define	KMEM_PATH	"/dev/kmem"	/* opened for kernel proc table */
X#define	MEM_PATH	"/dev/mem"	/* opened for mm/fs + user processes */
X
Xint kmemfd, memfd;			/* file descriptors of [k]mem */
X
X#if MUG
X#define DBASE_PATH	"/etc/ps_data"	/* path of ps's database */
X#else
X#define DBASE_PATH	"/etc/psdatabase"	/* path of ps's database */
X#endif
X#define DBASE_MODE	0644			/* mode for ps's database */
X
X/* paths for system binaries (not relevant if database is used) */
Xchar *kpath = KERNEL_PATH;
Xchar *mpath = MM_PATH;
Xchar *fpath = FS_PATH;
X
Xstruct tasktab tasktab[NR_TASKS + INIT_PROC_NR + 1];	/* task table */
X
X/*
X * Short and long listing formats:
X *
X *   PID TTY  TIME CMD
X * ppppp  ttmmm:ss ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
X * 
X *   F S UID   PID  PPID  PGRP ADDR  SZ        RECV TTY  TIME CMD
X * fff s uuu ppppp ppppp ppppp aaaa sss rrrrrrrrrrr  ttmmm:ss cccccccccccccccccccc
X * (RAMDSK) FS
X * or
X * (PAUSE) MM
X */
X#define S_HEADER "  PID TTY  TIME CMD\n"
X#define S_FORMAT "%5d  %2s%3D:%02D %.63s\n"
X#define L_HEADER "  F S UID   PID  PPID  PGRP ADDR  SZ        RECV TTY  TIME CMD\n"
X#define L_FORMAT "%3o %c %3d %5d %5d %5d %4d %3d %11s  %2s%3D:%02D %.20s\n"
X
Xstruct pstat {				/* structure filled by pstat() */
X	dev_t ps_dev;			/* major/minor of controlling tty */
X	uid_t ps_ruid;			/* real uid */
X	uid_t ps_euid;			/* effective uid */
X	pid_t ps_pid;			/* process id */
X	pid_t ps_ppid;			/* parent process id */
X	int ps_pgrp;			/* parent process id */
X	int ps_flags;			/* kernel flags */
X	int ps_mflags;			/* mm flags */
X	int ps_ftask;			/* (possibly pseudo) fs suspend task */
X	char ps_state;			/* process state */
X	size_t ps_tsize;		/* text size (in bytes) */
X	size_t ps_dsize;		/* data size (in bytes) */
X	size_t ps_ssize;		/* stack size (in bytes) */
X	off_t ps_text;			/* physical text offset */
X	off_t ps_data;			/* physical data offset */
X	off_t ps_stack;			/* physical stack offset */
X	int ps_recv;			/* process number to receive from */
X	time_t ps_utime;		/* accumulated user time */
X	time_t ps_stime;		/* accumulated system time */
X	char *ps_args;			/* concatenated argument string */
X};
X
X/* ps_state field values in pstat struct above */
X#define	Z_STATE		'Z'		/* Zombie */
X#define	W_STATE		'W'		/* Waiting */
X#define	S_STATE		'S'		/* Sleeping */
X#define	R_STATE		'R'		/* Runnable */
X#define	T_STATE		'T'		/* stopped (Trace) */
X
X/*
X * Tname returns mnemonic string for dev_nr.  This is "?" for unknown maj/min
X * pairs.  It is utterly rigid in this implementation...
X */
Xchar *tname(dev_nr)
X{
X#if MUG_VC
X	static char *ttys[] = {"co", "t1", "t2", "t3", "t4"};	/* consoles */
X	static char *sttys[] = {"t5", "t6"};		/* serial lines */
X	int min;
X#else
X	static char *ttys[] = {"co", "t1", "t2", "t3"};
X#endif
X
X#if MUG_VC
X	min = mindev(dev_nr);
X	if (min < 0 ||
X	    min > 64 + (sizeof(sttys) / sizeof(char *)) ||
X	    majdev(dev_nr) != TTY_MAJ)	/* yuchhh! */
X#else
X	if (majdev(dev_nr) != TTY_MAJ ||		/* yuchhh! */
X	    mindev(dev_nr) < 0 ||
X	    mindev(dev_nr) >= sizeof(ttys) / sizeof(char *))
X#endif
X		return "? ";
X
X#if MUG_VC
X	if (min >= 64) return(sttys[min - 64]);
X	  else return(ttys[min]);
X#else	
X	return ttys[mindev(dev_nr)];	
X#endif
X}
X
X/* return canonical task name of task p_nr; overwritten on each call */
Xchar *taskname(p_nr)
X{
X	char *cp;
X	
X	if (p_nr < -NR_TASKS || p_nr > INIT_PROC_NR)
X		return "?";
X	
X	/* strip trailing blanks for right-adjusted output */
X	for (cp = tasktab[p_nr + NR_TASKS].name; *cp != '\0'; cp++)
X		if (*cp == ' ')
X			break;
X	*cp = '\0';		
X	
X	return tasktab[p_nr + NR_TASKS].name;
X}
X
X/*
X * Prrecv prints the RECV field for process with pstat buffer pointer bufp.
X * This is either "ANY", "taskname", or "(blockreason) taskname".
X */
Xchar *prrecv(bufp)
Xstruct pstat *bufp;
X{
X	char *blkstr, *task;		/* reason for blocking and task */
X	static char recvstr[20];
X
X	if (bufp->ps_recv == ANY)
X		return "ANY";
X
X	task = taskname(bufp->ps_recv);
X	if (bufp->ps_state != S_STATE)
X		return task;
X	
X	blkstr = "?";
X	if (bufp->ps_recv == MM_PROC_NR) {
X		if (bufp->ps_mflags & PAUSED)
X			blkstr = "pause";
X		else if (bufp->ps_mflags & WAITING)
X			blkstr = "wait";
X	}
X	else if (bufp->ps_recv == FS_PROC_NR) {
X		if (-bufp->ps_ftask == XOPEN)
X			blkstr = "xopen";
X		else if (-bufp->ps_ftask == XPIPE)
X			blkstr = "xpipe";
X		else
X			blkstr = taskname(-bufp->ps_ftask);	
X	}
X	
X	(void) sprintf(recvstr, "(%s) %s", blkstr, task);
X	return recvstr;
X}
X
X/*
X * Main interprets arguments, gets system addresses, opens [k]mem, reads in
X * process tables from kernel/mm/fs and calls pstat() for relevant entries.
X */
Xmain(argc, argv)
Xchar *argv[];
X{
X	int i;
X	struct pstat buf;
X	int db_fd;
X	int uid = getuid();		/* real uid of caller */
X	int opt_all = FALSE;		/* -a */
X	int opt_long = FALSE;		/* -l */
X	int opt_notty = FALSE;		/* -x */
X	int opt_update = FALSE;		/* -U */
X
X	/* parse arguments; a '-' need not be present (V7/BSD compatability) */
X	switch (argc) {
X	case 1:		/* plain ps */
X		break;
X	case 2:		/* ps <[-][alxU]> */
X	case 5:		/* ps <[-][alxU]> <kernel mm fs> */
X		for (i = (argv[1][0] == '-' ? 1 : 0); argv[1][i] != '\0'; i++)
X			switch (argv[1][i]) {
X			case 'a':
X				opt_all = TRUE;
X				break;
X			case 'l':
X				opt_long = TRUE;
X				break;
X			case 'x':
X				opt_notty = TRUE;
X				break;
X			case 'U':
X				opt_update = TRUE;
X				break;
X			default:
X				usage(argv[0]);
X			}	
X		break;
X	case 4:		/* ps <kernel mm fs> */
X		if (argv[1][0] != '-')
X			break;
X	default:
X		usage(argv[0]);
X	}
X	
X	if (argc >= 4) {	/* ps [-][alxU] <kernel mm fs> */
X		kpath = argv[argc - 3];
X		mpath = argv[argc - 2];
X		fpath = argv[argc - 1];
X	}
X	
X	/* fill the sysinfo struct with system address information */
X	if (opt_update || (db_fd = open(DBASE_PATH, O_RDONLY)) == -1) {
X		strncpy(sysinfo.ke_proc[0].n_name, ID_PROC, NAME_SIZ);
X		strncpy(sysinfo.ke_tasktab[0].n_name, ID_TASKTAB, NAME_SIZ);
X		if (nlist(kpath, sysinfo.ke_proc) != 0 ||
X		    nlist(kpath, sysinfo.ke_tasktab) != 0)
X			err("Can't read kernel namelist");
X		strncpy(sysinfo.mm_mproc[0].n_name, ID_MPROC, NAME_SIZ);
X		if (nlist(mpath, sysinfo.mm_mproc) != 0)
X			err("Can't read mm namelist");
X		strncpy(sysinfo.fs_fproc[0].n_name, ID_FPROC, NAME_SIZ);
X		if (nlist(fpath, sysinfo.fs_fproc) != 0)
X			err("Can't read fs namelist");
X		if (opt_update) {
X			if ((db_fd = creat(DBASE_PATH, DBASE_MODE)) == -1)
X				err("Can't creat psdatabase");
X			if (write(db_fd, (char *) &sysinfo,
X				  sizeof(sysinfo_t)) != sizeof(sysinfo_t))
X				err("Can't write psdatabase");
X		}		
X	}
X	else {
X		if (read(db_fd, (char *) &sysinfo,
X			 sizeof(sysinfo_t)) != sizeof(sysinfo_t))
X			err("Can't read psdatabase");	 
X	}
X	(void) close (db_fd);
X	    	
X	/* get kernel tables */    	
X	if ((kmemfd = open(KMEM_PATH, O_RDONLY)) == -1)
X		err(KMEM_PATH);
X	if (addrread(kmemfd, (phys_clicks) 0,
X		     (vir_bytes) sysinfo.ke_proc[0].n_value,
X		     (char *) PROC, sizeof(PROC)) != sizeof(PROC))
X		err("Can't get kernel proc table from /dev/kmem");
X	if (addrread(kmemfd, (phys_clicks) 0,
X		     (vir_bytes) sysinfo.ke_tasktab[0].n_value,
X		     (char *) TASKTAB, sizeof(TASKTAB)) != sizeof(TASKTAB))
X		err("Can't get kernel task table from /dev/kmem");
X
X	/* get mm/fs tables */
X	if ((memfd = open(MEM_PATH, O_RDONLY)) == -1)
X		err(MEM_PATH);
X	if (addrread(memfd, PROC[NR_TASKS + MM_PROC_NR].p_map[D].mem_phys,
X		     (vir_bytes) sysinfo.mm_mproc[0].n_value,
X		     (char *) MPROC, sizeof(MPROC)) != sizeof(MPROC))
X		err("Can't get mm proc table from /dev/mem");
X	if (addrread(memfd, PROC[NR_TASKS + FS_PROC_NR].p_map[D].mem_phys,
X		     (vir_bytes) sysinfo.fs_fproc[0].n_value,
X		     (char *) FPROC, sizeof(FPROC)) != sizeof(FPROC))
X		err("Can't get fs proc table from /dev/mem");
X		
X	/* now loop through process table and handle each entry */
X	printf("%s", opt_long ? L_HEADER : S_HEADER);
X	for (i = -NR_TASKS; i < NR_PROCS; i++) {
X		if (pstat(i, &buf) != -1 &&
X		    (opt_all || buf.ps_euid == uid || buf.ps_ruid == uid) &&
X		    (opt_notty || majdev(buf.ps_dev) == TTY_MAJ))
X			if (opt_long)
X				printf(L_FORMAT,
X				       buf.ps_flags, buf.ps_state,
X				       buf.ps_euid, buf.ps_pid, buf.ps_ppid,
X				       buf.ps_pgrp,
X				       off_to_k(buf.ps_text),
X				       off_to_k((buf.ps_stack + buf.ps_ssize
X				       			- buf.ps_text)),
X				       (buf.ps_flags & RECEIVING ?
X						prrecv(&buf) :
X				       		""),
X				       tname(buf.ps_dev),
X				       (buf.ps_utime + buf.ps_stime) / HZ / 60,
X				       (buf.ps_utime + buf.ps_stime) / HZ % 60,
X				       i <= INIT_PROC_NR ? taskname(i) :
X						(buf.ps_args == NULL ? "" :
X					   		buf.ps_args));
X			else
X				printf(S_FORMAT,
X				       buf.ps_pid, tname(buf.ps_dev),
X				       (buf.ps_utime + buf.ps_stime) / HZ / 60,
X				       (buf.ps_utime + buf.ps_stime) / HZ % 60,
X				       i <= INIT_PROC_NR ? taskname(i) :
X						(buf.ps_args == NULL ? "" :
X					   		buf.ps_args));
X	}
X}
X
X/*
X * Get_args inspects /dev/mem, using bufp, and tries to locate the initial
X * stack frame pointer, i.e. the place where the stack started at exec time.
X * It is assumed that the end of the stack frame looks as follows:
X *	argc	<-- initial stack frame starts here
X *	argv[0]
X *	...
X *	NULL	(*)
X *	envp[0]
X *	...
X *	NULL	(**)
X *	argv[0][0] ... '\0'
X *	...
X *	argv[argc - 1][0] ... '\0'
X *	envp[0][0] ... '\0'
X *	...
X *	[trailing '\0']
X * Where the total space occupied by this original stack frame <= ARG_MAX.
X * Get_args reads in the last ARG_MAX bytes of the process' data, and
X * searches back for two NULL ptrs (hopefully the (*) & (**) above).
X * If it finds such a portion, it continues backwards, counting ptrs until:
X * a) either a word is found that has as its value the count (supposedly argc),
X * b) another NULL word is found, in which case the algorithm is reiterated, or
X * c) we wind up before the start of the buffer and fail.
X * Upon success, get_args returns a pointer to the conactenated arg list.
X * Warning: this routine is inherently unreliable and probably doesn't work if
X * ptrs and ints have different sizes.
X */
Xchar *get_args(bufp)
Xstruct pstat *bufp;
X{
X	union {
X#if (CHIP == M68000)
X		long stk_i;
X#else
X		int stk_i;
X#endif
X		char *stk_cp;
X		char stk_c;
X	} stk[ARG_MAX / sizeof(char *)], *sp;
X	enum {INITIAL, DONE, FAIL, NULL1, NULL2} state;
X	int nargv;		/* guessed # of (non-NULL) argv pointers seen */
X	int cnt;		/* # of bytes read from stack frame */
X	int neos;		/* # of '\0's seen in argv string space */
X	off_t l;
X	char *cp, *args;
X	
X	
X	if (bufp->ps_ssize < ARG_MAX)
X		cnt = bufp->ps_ssize;
X	else
X		cnt = ARG_MAX;
X	/* get last cnt bytes from user stack */
X	if (addrread(memfd, (phys_clicks) (bufp->ps_stack >> CLICK_SHIFT),
X		     (vir_bytes) (bufp->ps_ssize - cnt),
X		     (char *) stk, cnt) != cnt)
X		return NULL;
X	
X	state = INITIAL;
X	sp = &stk[cnt / sizeof(char *)];
X	while (state != DONE && state != FAIL) {
X		if (--sp < &stk[0])
X			state = FAIL;	/* wound up before start of buffer */
X		switch(state) {
X		case INITIAL:	/* no NULL seen yet */
X			if (sp[0].stk_cp == NULL)
X				state = NULL1;
X			break;
X		case NULL1:	/* one NULL seen */	
X			if (sp[0].stk_cp == NULL) {
X				nargv = 0;	/* start counting argv ptrs */
X				state = NULL2;
X			}	
X			/*
X			 * What follows is a dirty patch to recognize sh's 
X			 * stack frame when it has assigned argv[0] to argv[1],
X			 * and has thus blown away its NULL pointer there.
X			 */
X			else if (sp > &stk[0] && sp[0].stk_cp == sp[-1].stk_cp){
X				nargv = 0;
X				state = NULL2;
X			}
X			break;
X		case NULL2:	/* two NULLs seen */
X			if (sp[0].stk_cp == NULL)
X				nargv = 0;	/* restart counting */
X			else if (sp[0].stk_i == nargv)
X				state = DONE;	/* think i got it */
X			/* next is same ugly patch as above */	
X			else if (sp > &stk[0] && sp[0].stk_cp == sp[-1].stk_cp)
X				nargv = 0;
X			else
X				nargv++;	/* ? some argv pointer ? */
X			break;
X		default:	/* FAIL or DONE */
X			break;
X		}	
X	}
X	
X	if (state != DONE)
X		return NULL;
X
X	/* get a local version of argv[0]; l is offset back from end of stack */
X	l = bufp->ps_stack + bufp->ps_ssize -
X#if (CHIP == INTEL)
X		bufp->ps_data - 
X#endif
X		(vir_bytes) sp[1].stk_cp;
X	if (l < 0 || l > cnt)
X		return NULL;
X	args = &((char *) stk)[cnt - (int) l];
X	neos = 0;
X	for (cp = args; cp < &((char *) stk)[cnt]; cp++)
X		if (*cp == '\0')
X			if (++neos >= sp[0].stk_i)
X				break;
X			else
X				*cp = ' ';	
X	if (neos != sp[0].stk_i)
X		return NULL;			
X
X	return args;
X}
X
X/*
X * Pstat collects info on process number p_nr and returns it in buf.
X * It is assumed that tasks do not have entries in fproc/mproc.
X */
Xint pstat(p_nr, bufp)
Xstruct pstat *bufp;
X{
X	int p_ki = p_nr + NR_TASKS;	/* kernel proc index */
X	
X	if (p_nr < -NR_TASKS || p_nr >= NR_PROCS)
X		return -1;
X	
X	if ((PROC[p_ki].p_flags & P_SLOT_FREE) &&
X	    !(MPROC[p_nr].mp_flags & IN_USE))
X		return -1;
X
X	bufp->ps_flags = PROC[p_ki].p_flags;
X	
X	if (p_nr >= 0) {
X		bufp->ps_dev = FPROC[p_nr].fs_tty;
X		bufp->ps_ftask = FPROC[p_nr].fp_task;
X	}	
X	else {
X		bufp->ps_dev = 0;
X		bufp->ps_ftask = 0;
X	}
X
X	if (p_nr >= 0) {
X		bufp->ps_ruid = MPROC[p_nr].mp_realuid;	
X		bufp->ps_euid = MPROC[p_nr].mp_effuid;	
X		bufp->ps_pid = MPROC[p_nr].mp_pid;
X		bufp->ps_ppid = MPROC[MPROC[p_nr].mp_parent].mp_pid;
X		bufp->ps_pgrp = MPROC[p_nr].mp_procgrp;
X		bufp->ps_mflags = MPROC[p_nr].mp_flags;
X	}
X	else {
X		bufp->ps_pid = bufp->ps_ppid = 0;
X		bufp->ps_ruid = bufp->ps_euid = 0;
X		bufp->ps_pgrp = 0;
X		bufp->ps_mflags = 0;
X	}	
X	
X	/* state is interpretation of combined kernel/mm flags for non-tasks */
X	if (p_nr >= 0) {				/* non-tasks */
X		if (MPROC[p_nr].mp_flags & HANGING)
X			bufp->ps_state = Z_STATE;	/* zombie */
X		else if (MPROC[p_nr].mp_flags & STOPPED)
X			bufp->ps_state = T_STATE;	/* stopped (traced) */
X		else if (PROC[p_ki].p_flags == 0)
X			bufp->ps_state = R_STATE;	/* in run-queue */
X		else if (MPROC[p_nr].mp_flags & (WAITING | PAUSED) ||
X			 FPROC[p_nr].fp_suspended == SUSPENDED)
X			bufp->ps_state = S_STATE;	/* sleeping */
X		else	
X			bufp->ps_state = W_STATE;	/* a short wait */
X	}
X	else {						/* tasks are simple */
X		if (PROC[p_ki].p_flags == 0)
X			bufp->ps_state = R_STATE;	/* in run-queue */
X		else
X			bufp->ps_state = W_STATE;	/* other i.e. waiting */
X	}		
X		
X	bufp->ps_tsize = (size_t) PROC[p_ki].p_map[T].mem_len << CLICK_SHIFT;
X	bufp->ps_dsize = (size_t) PROC[p_ki].p_map[D].mem_len << CLICK_SHIFT;
X	bufp->ps_ssize = (size_t) PROC[p_ki].p_map[S].mem_len << CLICK_SHIFT;
X	bufp->ps_text = (off_t) PROC[p_ki].p_map[T].mem_phys << CLICK_SHIFT;
X	bufp->ps_data = (off_t) PROC[p_ki].p_map[D].mem_phys << CLICK_SHIFT;
X	bufp->ps_stack = (off_t) PROC[p_ki].p_map[S].mem_phys << CLICK_SHIFT;
X	
X	bufp->ps_recv = PROC[p_ki].p_getfrom;
X	
X	bufp->ps_utime = PROC[p_ki].user_time;
X	bufp->ps_stime = PROC[p_ki].sys_time;
X	
X	if (bufp->ps_state == Z_STATE)
X		bufp->ps_args = "<defunct>";
X	else if (p_nr > INIT_PROC_NR)
X		bufp->ps_args = get_args(bufp);
X	
X	return 0;
X}
X
X/*
X * Addrread reads nbytes from offset addr to click base of fd into buf.
X */
Xint addrread(fd, base, addr, buf, nbytes)
Xphys_clicks base;
Xvir_bytes addr;
Xchar *buf;
X{
X	extern long lseek();
X    
X	if (lseek(fd, ((long) base << CLICK_SHIFT) + (long) addr, 0) < 0)
X		return -1;
X
X	return read(fd, buf, nbytes);
X}
X
Xusage(pname)
Xchar *pname;
X{
X	fprintf(stderr, "Usage: %s [-][alxU] [kernel mm fs]\n", pname);
X	exit(1);
X}
X
Xerr(s)
Xchar *s;
X{
X	perror(s);
X	exit(2);
X}	
END_OF_SHAR
   if test 19540 -ne `wc -c <'ps.c'`
   then
      echo "$0: unpacked with wrong size: ps.c"
   fi
fi
echo "		End of archive"
exit 0
#
#
#