Minix1.5/commands/ps.c
/* 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);
}