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 # # #