Minix1.5/commands/ps.c

Compare this file to the similar file:
Show the results in this format:

/* ps - print status			Author: Peter Valkenburg */

/* ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990.
 *
 * This is a V7 ps(1) look-alike for MINIX 1.5.0.  It can use a database with
 * information on system addresses as an extra, and has some additional fields.
 * It does not support the 'k' option (i.e. cannot read memory from core file).
 * If you want to compile this for non-IBM PC architectures, the header files
 * require that you have your CHIP, MACHINE etc. defined.
 *
 * VERY IMPORTANT NOTE:
 *	To use ps, kernel/kernel, fs/fs, and mm/mm must be in the /usr/src
 *	(or the #includes below must be changed).  Furthermore, all of
 *	must contain symbol tables.  This can be arranged using the -s flag
 *	and the ast program.  For example in fs, one would have
 *
 *	asld -s -i -o fs $l/head.s $(obj) $l/libc.a $l/end.s >symbol.out
 *	ast -X fs		# include symbol.out in fs
 *
 */

/*
 * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are
 * absent, RECV which replaces WCHAN, and RUID and PGRP that are extras.
 * The info is obtained from the following fields of proc, mproc and fproc:
 * F	- kernel status field, p_flags
 * S	- kernel status field, p_flags; mm status field, mp_flags (R if p_flags
 * 	  is 0; Z if mp_flags == HANGING; T if mp_flags == STOPPED; else W).
 * UID	- mm eff uid field, mp_effuid
 * RUID	- mm real uid field, mp_realuid
 * PID	- mm pid field, mp_pid
 * PPID	- mm parent process index field, mp_parent (used as index in proc).
 * PGRP - mm process group id mp_procgrp
 * ADDR	- kernel physical text address, p_map[T].mem_phys
 * SZ	- kernel physical stack address + stack size - physical text address,
 * 	  p_map[S].mem_phys + p_map[S].mem_len - p_map[T].mem_phys
 * RECV	- kernel process index field for message receiving, p_getfrom
 *	  If sleeping, mm's mp_flags, or fs's fp_task are used for more info.
 * TTY	- fs controlling tty device field, fs_tty.
 * TIME	- kernel user + system times fields, user_time + sys_time
 * CMD	- system process index (converted to mnemonic name obtained by reading
 *	  tasktab array from kmem), or user process argument list (obtained by
 *	  reading reading stack frame; the resulting address is used to get
 *	  the argument vector from user space and converted into a concatenated
 *	  argument list).
 */  	  
 
#include <minix/config.h>
#include <limits.h>
#include <sys/types.h>

#include <minix/const.h>
#undef EXTERN				/* <minix/const.h> defined this */
#define EXTERN				/* so we get proc, mproc and fproc */
#include <minix/type.h>

#include "../kernel/const.h"
#include "../kernel/type.h"
#include "../kernel/proc.h"
#undef printf				/* kernel's const.h defined this */

#include "../mm/mproc.h"
#include "../fs/fproc.h"
#include "../fs/const.h"
#undef printf				/* fs's const.h defined this */


/*----- ps's local stuff below this line ------*/

#include <minix/com.h>
#include <fcntl.h>
#include <a.out.h>
#include <stdio.h>

#define mindev(dev)	(((dev)>>MINOR) & 0377)	/* yield minor device */
#define majdev(dev)	(((dev)>>MAJOR) & 0377)	/* yield major device */

#define	TTY_MAJ		4			/* fixed tty major device */

/* macro to convert memory offsets to rounded kilo-units */
#define	off_to_k(off)	((unsigned) (((off) + 512) / 1024))

/* what we think the relevant identifiers in the namelists are */
#define	ID_PROC		"_proc"		/* from kernel namelist */
#define	ID_MPROC	"_mproc"	/* from mm namelist */
#define	ID_FPROC	"_fproc"	/* from fs namelist */
#define	ID_TASKTAB	"_tasktab"	/* from kernel namelist */

/*
 * Structure for system address info (also layout of ps's database).
 */
typedef struct {
	struct nlist ke_proc[2], ke_tasktab[2];
	struct nlist mm_mproc[2];
	struct nlist fs_fproc[2];
} sysinfo_t;

sysinfo_t sysinfo;			/* sysinfo holds actual system info */

#define	NAME_SIZ	(sizeof(sysinfo.ke_proc[0].n_name))	/* 8 chars */

/* what we think the identfiers of the imported variables in this program are */
#define	PROC	proc
#define	MPROC	mproc
#define	FPROC	fproc
#define	TASKTAB	tasktab

/* default paths for system binaries */
#if (CHIP == M68000)
#define KERNEL_PATH	"/usr/src/kernel/kernel.mix"
#define MM_PATH		"/usr/src/mm/mm.mix"
#define FS_PATH		"/usr/src/fs/fs.mix"
#else
#define KERNEL_PATH	"/usr/src/kernel/kernel"
#define MM_PATH		"/usr/src/mm/mm"
#define FS_PATH		"/usr/src/fs/fs"
# endif

#define	KMEM_PATH	"/dev/kmem"	/* opened for kernel proc table */
#define	MEM_PATH	"/dev/mem"	/* opened for mm/fs + user processes */

int kmemfd, memfd;			/* file descriptors of [k]mem */

#define DBASE_PATH	"/etc/psdatabase"	/* path of ps's database */
#define DBASE_MODE	0644			/* mode for ps's database */

/* paths for system binaries (not relevant if database is used) */
char *kpath = KERNEL_PATH;
char *mpath = MM_PATH;
char *fpath = FS_PATH;

struct tasktab tasktab[NR_TASKS + INIT_PROC_NR + 1];	/* task table */

/*
 * Short and long listing formats:
 *
 *   PID TTY  TIME CMD
 * ppppp  ttmmm:ss ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc
 * 
 *   F S UID   PID  PPID  PGRP ADDR  SZ        RECV TTY  TIME CMD
 * fff s uuu ppppp ppppp ppppp aaaa sss rrrrrrrrrrr  ttmmm:ss cccccccccccccccccccc
 * (RAMDSK) FS
 * or
 * (PAUSE) MM
 */
#define S_HEADER "  PID TTY  TIME CMD\n"
#define S_FORMAT "%5d  %3s%3ld:%02ld %.62s\n"
#define L_HEADER "  F S UID   PID  PPID  PGRP ADDR  SZ        RECV TTY  TIME CMD\n"
#define L_FORMAT "%3o %c %3d %5d %5d %5d %4d %3d %11s  %3s%3ld:%02ld %.19s\n"

struct pstat {				/* structure filled by pstat() */
	dev_t ps_dev;			/* major/minor of controlling tty */
	uid_t ps_ruid;			/* real uid */
	uid_t ps_euid;			/* effective uid */
	pid_t ps_pid;			/* process id */
	pid_t ps_ppid;			/* parent process id */
	int ps_pgrp;			/* parent process id */
	int ps_flags;			/* kernel flags */
	int ps_mflags;			/* mm flags */
	int ps_ftask;			/* (possibly pseudo) fs suspend task */
	char ps_state;			/* process state */
	size_t ps_tsize;		/* text size (in bytes) */
	size_t ps_dsize;		/* data size (in bytes) */
	size_t ps_ssize;		/* stack size (in bytes) */
	off_t ps_text;			/* physical text offset */
	off_t ps_data;			/* physical data offset */
	off_t ps_stack;			/* physical stack offset */
	int ps_recv;			/* process number to receive from */
	time_t ps_utime;		/* accumulated user time */
	time_t ps_stime;		/* accumulated system time */
	char *ps_args;			/* concatenated argument string */
};

/* ps_state field values in pstat struct above */
#define	Z_STATE		'Z'		/* Zombie */
#define	W_STATE		'W'		/* Waiting */
#define	S_STATE		'S'		/* Sleeping */
#define	R_STATE		'R'		/* Runnable */
#define	T_STATE		'T'		/* stopped (Trace) */

/*
 * Tname returns mnemonic string for dev_nr.  This is "?" for unknown maj/min
 * pairs.  It is utterly rigid in this implementation...
 */
char *tname(dev_nr)
{
	static char buf[4];
	
	if (majdev(dev_nr) != TTY_MAJ ||	/* yuchhh! */
	    mindev(dev_nr) < 0 || mindev(dev_nr) >= 100)
		return "?  ";
	if (mindev(dev_nr) == 0)
		return "co ";

	sprintf(buf, "t%-2d", mindev(dev_nr));
	return buf;
}

/* return canonical task name of task p_nr; overwritten on each call */
char *taskname(p_nr)
{
	char *cp;
	
	if (p_nr < -NR_TASKS || p_nr > INIT_PROC_NR)
		return "?";
	
	/* strip trailing blanks for right-adjusted output */
	for (cp = tasktab[p_nr + NR_TASKS].name; *cp != '\0'; cp++)
		if (*cp == ' ')
			break;
	*cp = '\0';		
	
	return tasktab[p_nr + NR_TASKS].name;
}

/*
 * Prrecv prints the RECV field for process with pstat buffer pointer bufp.
 * This is either "ANY", "taskname", or "(blockreason) taskname".
 */
char *prrecv(bufp)
struct pstat *bufp;
{
	char *blkstr, *task;		/* reason for blocking and task */
	static char recvstr[20];

	if (bufp->ps_recv == ANY)
		return "ANY";

	task = taskname(bufp->ps_recv);
	if (bufp->ps_state != S_STATE)
		return task;
	
	blkstr = "?";
	if (bufp->ps_recv == MM_PROC_NR) {
		if (bufp->ps_mflags & PAUSED)
			blkstr = "pause";
		else if (bufp->ps_mflags & WAITING)
			blkstr = "wait";
	}
	else if (bufp->ps_recv == FS_PROC_NR) {
		if (-bufp->ps_ftask == XOPEN)
			blkstr = "xopen";
		else if (-bufp->ps_ftask == XPIPE)
			blkstr = "xpipe";
		else
			blkstr = taskname(-bufp->ps_ftask);	
	}
	
	(void) sprintf(recvstr, "(%s) %s", blkstr, task);
	return recvstr;
}

/*
 * Main interprets arguments, gets system addresses, opens [k]mem, reads in
 * process tables from kernel/mm/fs and calls pstat() for relevant entries.
 */
main(argc, argv)
char *argv[];
{
	int i;
	struct pstat buf;
	int db_fd;
	int uid = getuid();		/* real uid of caller */
	int opt_all = FALSE;		/* -a */
	int opt_long = FALSE;		/* -l */
	int opt_notty = FALSE;		/* -x */
	int opt_update = FALSE;		/* -U */

	/* parse arguments; a '-' need not be present (V7/BSD compatability) */
	switch (argc) {
	case 1:		/* plain ps */
		break;
	case 2:		/* ps <[-][alxU]> */
	case 5:		/* ps <[-][alxU]> <kernel mm fs> */
		for (i = (argv[1][0] == '-' ? 1 : 0); argv[1][i] != '\0'; i++)
			switch (argv[1][i]) {
			case 'a':
				opt_all = TRUE;
				break;
			case 'l':
				opt_long = TRUE;
				break;
			case 'x':
				opt_notty = TRUE;
				break;
			case 'U':
				opt_update = TRUE;
				break;
			default:
				usage(argv[0]);
			}	
		break;
	case 4:		/* ps <kernel mm fs> */
		if (argv[1][0] != '-')
			break;
	default:
		usage(argv[0]);
	}
	
	if (argc >= 4) {	/* ps [-][alxU] <kernel mm fs> */
		kpath = argv[argc - 3];
		mpath = argv[argc - 2];
		fpath = argv[argc - 1];
	}
	
	/* fill the sysinfo struct with system address information */
	if (opt_update || (db_fd = open(DBASE_PATH, O_RDONLY)) == -1) {
		strncpy(sysinfo.ke_proc[0].n_name, ID_PROC, NAME_SIZ);
		strncpy(sysinfo.ke_tasktab[0].n_name, ID_TASKTAB, NAME_SIZ);
		if (nlist(kpath, sysinfo.ke_proc) != 0 ||
		    nlist(kpath, sysinfo.ke_tasktab) != 0)
			err("Can't read kernel namelist");
		strncpy(sysinfo.mm_mproc[0].n_name, ID_MPROC, NAME_SIZ);
		if (nlist(mpath, sysinfo.mm_mproc) != 0)
			err("Can't read mm namelist");
		strncpy(sysinfo.fs_fproc[0].n_name, ID_FPROC, NAME_SIZ);
		if (nlist(fpath, sysinfo.fs_fproc) != 0)
			err("Can't read fs namelist");
		if (opt_update) {
			if ((db_fd = creat(DBASE_PATH, DBASE_MODE)) == -1)
				err("Can't creat psdatabase");
			if (write(db_fd, (char *) &sysinfo,
				  sizeof(sysinfo_t)) != sizeof(sysinfo_t))
				err("Can't write psdatabase");
		}		
	}
	else {
		if (read(db_fd, (char *) &sysinfo,
			 sizeof(sysinfo_t)) != sizeof(sysinfo_t))
			err("Can't read psdatabase");	 
	}
	(void) close (db_fd);
	    	
	/* get kernel tables */    	
	if ((kmemfd = open(KMEM_PATH, O_RDONLY)) == -1)
		err(KMEM_PATH);
	if (addrread(kmemfd, (phys_clicks) 0,
		     (vir_bytes) sysinfo.ke_proc[0].n_value,
		     (char *) PROC, sizeof(PROC)) != sizeof(PROC))
		err("Can't get kernel proc table from /dev/kmem");
	if (addrread(kmemfd, (phys_clicks) 0,
		     (vir_bytes) sysinfo.ke_tasktab[0].n_value,
		     (char *) TASKTAB, sizeof(TASKTAB)) != sizeof(TASKTAB))
		err("Can't get kernel task table from /dev/kmem");

	/* get mm/fs tables */
	if ((memfd = open(MEM_PATH, O_RDONLY)) == -1)
		err(MEM_PATH);
	if (addrread(memfd, PROC[NR_TASKS + MM_PROC_NR].p_map[D].mem_phys,
		     (vir_bytes) sysinfo.mm_mproc[0].n_value,
		     (char *) MPROC, sizeof(MPROC)) != sizeof(MPROC))
		err("Can't get mm proc table from /dev/mem");
	if (addrread(memfd, PROC[NR_TASKS + FS_PROC_NR].p_map[D].mem_phys,
		     (vir_bytes) sysinfo.fs_fproc[0].n_value,
		     (char *) FPROC, sizeof(FPROC)) != sizeof(FPROC))
		err("Can't get fs proc table from /dev/mem");
		
	/* now loop through process table and handle each entry */
	printf("%s", opt_long ? L_HEADER : S_HEADER);
	for (i = -NR_TASKS; i < NR_PROCS; i++) {
		if (pstat(i, &buf) != -1 &&
		    (opt_all || buf.ps_euid == uid || buf.ps_ruid == uid) &&
		    (opt_notty || majdev(buf.ps_dev) == TTY_MAJ))
			if (opt_long)
				printf(L_FORMAT,
				       buf.ps_flags, buf.ps_state,
				       buf.ps_euid, buf.ps_pid, buf.ps_ppid,
				       buf.ps_pgrp,
				       off_to_k(buf.ps_text),
				       off_to_k((buf.ps_stack + buf.ps_ssize
				       			- buf.ps_text)),
				       (buf.ps_flags & RECEIVING ?
						prrecv(&buf) :
				       		""),
				       tname(buf.ps_dev),
				       (buf.ps_utime + buf.ps_stime) / HZ / 60,
				       (buf.ps_utime + buf.ps_stime) / HZ % 60,
				       i <= INIT_PROC_NR ? taskname(i) :
						(buf.ps_args == NULL ? "" :
					   		buf.ps_args));
			else
				printf(S_FORMAT,
				       buf.ps_pid, tname(buf.ps_dev),
				       (buf.ps_utime + buf.ps_stime) / HZ / 60,
				       (buf.ps_utime + buf.ps_stime) / HZ % 60,
				       i <= INIT_PROC_NR ? taskname(i) :
						(buf.ps_args == NULL ? "" :
					   		buf.ps_args));
	}
}

/*
 * Get_args inspects /dev/mem, using bufp, and tries to locate the initial
 * stack frame pointer, i.e. the place where the stack started at exec time.
 * It is assumed that the end of the stack frame looks as follows:
 *	argc	<-- initial stack frame starts here
 *	argv[0]
 *	...
 *	NULL	(*)
 *	envp[0]
 *	...
 *	NULL	(**)
 *	argv[0][0] ... '\0'
 *	...
 *	argv[argc - 1][0] ... '\0'
 *	envp[0][0] ... '\0'
 *	...
 *	[trailing '\0']
 * Where the total space occupied by this original stack frame <= ARG_MAX.
 * Get_args reads in the last ARG_MAX bytes of the process' data, and
 * searches back for two NULL ptrs (hopefully the (*) & (**) above).
 * If it finds such a portion, it continues backwards, counting ptrs until:
 * a) either a word is found that has as its value the count (supposedly argc),
 * b) another NULL word is found, in which case the algorithm is reiterated, or
 * c) we wind up before the start of the buffer and fail.
 * Upon success, get_args returns a pointer to the conactenated arg list.
 * Warning: this routine is inherently unreliable and probably doesn't work if
 * ptrs and ints have different sizes.
 */
char *get_args(bufp)
struct pstat *bufp;
{
	union {
#if (CHIP == M68000)
		long stk_i;
#else
		int stk_i;
#endif
		char *stk_cp;
		char stk_c;
	} stk[ARG_MAX / sizeof(char *)], *sp;
	enum {INITIAL, DONE, FAIL, NULL1, NULL2} state;
	int nargv;		/* guessed # of (non-NULL) argv pointers seen */
	int cnt;		/* # of bytes read from stack frame */
	int neos;		/* # of '\0's seen in argv string space */
	off_t l;
	char *cp, *args;
	
	
	if (bufp->ps_ssize < ARG_MAX)
		cnt = bufp->ps_ssize;
	else
		cnt = ARG_MAX;
	/* get last cnt bytes from user stack */
	if (addrread(memfd, (phys_clicks) (bufp->ps_stack >> CLICK_SHIFT),
		     (vir_bytes) (bufp->ps_ssize - cnt),
		     (char *) stk, cnt) != cnt)
		return NULL;
	
	state = INITIAL;
	sp = &stk[cnt / sizeof(char *)];
	while (state != DONE && state != FAIL) {
		if (--sp < &stk[0])
			state = FAIL;	/* wound up before start of buffer */
		switch(state) {
		case INITIAL:	/* no NULL seen yet */
			if (sp[0].stk_cp == NULL)
				state = NULL1;
			break;
		case NULL1:	/* one NULL seen */	
			if (sp[0].stk_cp == NULL) {
				nargv = 0;	/* start counting argv ptrs */
				state = NULL2;
			}	
			/*
			 * What follows is a dirty patch to recognize sh's 
			 * stack frame when it has assigned argv[0] to argv[1],
			 * and has thus blown away its NULL pointer there.
			 */
			else if (sp > &stk[0] && sp[0].stk_cp == sp[-1].stk_cp){
				nargv = 0;
				state = NULL2;
			}
			break;
		case NULL2:	/* two NULLs seen */
			if (sp[0].stk_cp == NULL)
				nargv = 0;	/* restart counting */
			else if (sp[0].stk_i == nargv)
				state = DONE;	/* think i got it */
			/* next is same ugly patch as above */	
			else if (sp > &stk[0] && sp[0].stk_cp == sp[-1].stk_cp)
				nargv = 0;
			else
				nargv++;	/* ? some argv pointer ? */
			break;
		default:	/* FAIL or DONE */
			break;
		}	
	}
	
	if (state != DONE)
		return NULL;

	/* get a local version of argv[0]; l is offset back from end of stack */
	l = bufp->ps_stack + bufp->ps_ssize -
#if (CHIP == INTEL)
		bufp->ps_data - 
#endif
		(vir_bytes) sp[1].stk_cp;
	if (l < 0 || l > cnt)
		return NULL;
	args = &((char *) stk)[cnt - (int) l];
	neos = 0;
	for (cp = args; cp < &((char *) stk)[cnt]; cp++)
		if (*cp == '\0')
			if (++neos >= sp[0].stk_i)
				break;
			else
				*cp = ' ';	
	if (neos != sp[0].stk_i)
		return NULL;			

	return args;
}

/*
 * Pstat collects info on process number p_nr and returns it in buf.
 * It is assumed that tasks do not have entries in fproc/mproc.
 */
int pstat(p_nr, bufp)
struct pstat *bufp;
{
	int p_ki = p_nr + NR_TASKS;	/* kernel proc index */
	
	if (p_nr < -NR_TASKS || p_nr >= NR_PROCS)
		return -1;
	
	if ((PROC[p_ki].p_flags & P_SLOT_FREE) &&
	    !(MPROC[p_nr].mp_flags & IN_USE))
		return -1;

	bufp->ps_flags = PROC[p_ki].p_flags;
	
	if (p_nr >= 0) {
		bufp->ps_dev = FPROC[p_nr].fs_tty;
		bufp->ps_ftask = FPROC[p_nr].fp_task;
	}	
	else {
		bufp->ps_dev = 0;
		bufp->ps_ftask = 0;
	}

	if (p_nr >= 0) {
		bufp->ps_ruid = MPROC[p_nr].mp_realuid;	
		bufp->ps_euid = MPROC[p_nr].mp_effuid;	
		bufp->ps_pid = MPROC[p_nr].mp_pid;
		bufp->ps_ppid = MPROC[MPROC[p_nr].mp_parent].mp_pid;
		bufp->ps_pgrp = MPROC[p_nr].mp_procgrp;
		bufp->ps_mflags = MPROC[p_nr].mp_flags;
	}
	else {
		bufp->ps_pid = bufp->ps_ppid = 0;
		bufp->ps_ruid = bufp->ps_euid = 0;
		bufp->ps_pgrp = 0;
		bufp->ps_mflags = 0;
	}	
	
	/* state is interpretation of combined kernel/mm flags for non-tasks */
	if (p_nr >= 0) {				/* non-tasks */
		if (MPROC[p_nr].mp_flags & HANGING)
			bufp->ps_state = Z_STATE;	/* zombie */
		else if (MPROC[p_nr].mp_flags & STOPPED)
			bufp->ps_state = T_STATE;	/* stopped (traced) */
		else if (PROC[p_ki].p_flags == 0)
			bufp->ps_state = R_STATE;	/* in run-queue */
		else if (MPROC[p_nr].mp_flags & (WAITING | PAUSED) ||
			 FPROC[p_nr].fp_suspended == SUSPENDED)
			bufp->ps_state = S_STATE;	/* sleeping */
		else	
			bufp->ps_state = W_STATE;	/* a short wait */
	}
	else {						/* tasks are simple */
		if (PROC[p_ki].p_flags == 0)
			bufp->ps_state = R_STATE;	/* in run-queue */
		else
			bufp->ps_state = W_STATE;	/* other i.e. waiting */
	}		
		
	bufp->ps_tsize = (size_t) PROC[p_ki].p_map[T].mem_len << CLICK_SHIFT;
	bufp->ps_dsize = (size_t) PROC[p_ki].p_map[D].mem_len << CLICK_SHIFT;
	bufp->ps_ssize = (size_t) PROC[p_ki].p_map[S].mem_len << CLICK_SHIFT;
	bufp->ps_text = (off_t) PROC[p_ki].p_map[T].mem_phys << CLICK_SHIFT;
	bufp->ps_data = (off_t) PROC[p_ki].p_map[D].mem_phys << CLICK_SHIFT;
	bufp->ps_stack = (off_t) PROC[p_ki].p_map[S].mem_phys << CLICK_SHIFT;
	
	bufp->ps_recv = PROC[p_ki].p_getfrom;
	
	bufp->ps_utime = PROC[p_ki].user_time;
	bufp->ps_stime = PROC[p_ki].sys_time;
	
	if (bufp->ps_state == Z_STATE)
		bufp->ps_args = "<defunct>";
	else if (p_nr > INIT_PROC_NR)
		bufp->ps_args = get_args(bufp);
	
	return 0;
}

/*
 * Addrread reads nbytes from offset addr to click base of fd into buf.
 */
int addrread(fd, base, addr, buf, nbytes)
phys_clicks base;
vir_bytes addr;
char *buf;
{
	extern long lseek();
    
	if (lseek(fd, ((long) base << CLICK_SHIFT) + (long) addr, 0) < 0)
		return -1;

	return read(fd, buf, nbytes);
}

usage(pname)
char *pname;
{
	fprintf(stderr, "Usage: %s [-][alxU] [kernel mm fs]\n", pname);
	exit(1);
}

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