1 /* ps - print status Author: Peter Valkenburg */
3 /* Ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990.
5 * This is a V7 ps(1) look-alike for MINIX >= 1.5.0.
6 * It does not support the 'k' option (i.e. cannot read memory from core file).
7 * If you want to compile this for non-IBM PC architectures, the header files
8 * require that you have your CHIP, MACHINE etc. defined.
11 * Option `a' gives all processes, `l' for detailed info, `x' includes even
12 * processes without a terminal.
13 * The `f' and `e' options were added by Kees Bot for the convenience of
14 * Solaris users accustomed to these options. The `e' option is equivalent to
15 * `a' and `f' is equivalent to -l. These do not appear in the usage message.
17 * VERY IMPORTANT NOTE:
18 * To compile ps, the kernel/, fs/ and pm/ source directories must be in
19 * ../../ relative to the directory where ps is compiled (normally the
20 * tools source directory).
22 * If you want your ps to be useable by anyone, you can arrange the
23 * following access permissions (note the protected memory files and set
25 * -rwxr-sr-x 1 bin kmem 11916 Jul 4 15:31 /bin/ps
26 * crw-r----- 1 bin kmem 1, 1 Jan 1 1970 /dev/mem
27 * crw-r----- 1 bin kmem 1, 2 Jan 1 1970 /dev/kmem
30 /* Some technical comments on this implementation:
32 * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are
33 * absent, RECV which replaces WCHAN, and PGRP that is an extra.
34 * The info is obtained from the following fields of proc, mproc and fproc:
35 * F - kernel status field, p_rts_flags
36 * S - kernel status field, p_rts_flags; mm status field, mp_flags (R if p_rts_flags
37 * is 0; Z if mp_flags == ZOMBIE; T if mp_flags == STOPPED; else W).
38 * UID - mm eff uid field, mp_effuid
39 * PID - mm pid field, mp_pid
40 * PPID - mm parent process index field, mp_parent (used as index in proc).
41 * PGRP - mm process group field, mp_procgrp
42 * SZ - kernel text size + physical stack address - physical data address
44 * p_memmap[T].mem_len + p_memmap[S].mem_phys - p_memmap[D].mem_phys
45 * + p_memmap[S].mem_len
46 * RECV - kernel process index field for message receiving, p_getfrom
47 * If sleeping, mm's mp_flags, or fs's fp_task are used for more info.
48 * TTY - fs controlling tty device field, fp_tty.
49 * TIME - kernel user + system times fields, user_time + sys_time
50 * CMD - system process index (converted to mnemonic name by using the p_name
51 * field), or user process argument list (obtained by reading the stack
52 * frame; the resulting address is used to get the argument vector from
53 * user space and converted into a concatenated argument list).
56 #include <minix/config.h>
57 #include <minix/endpoint.h>
60 #include <sys/types.h>
62 #include <minix/const.h>
63 #include <minix/type.h>
64 #include <minix/ipc.h>
69 #include <minix/com.h>
74 #include <sys/ioctl.h>
79 #include "../../kernel/arch/i386/include/archtypes.h"
80 #include "../../kernel/const.h"
81 #include "../../kernel/type.h"
82 #include "../../kernel/proc.h"
84 #include "../../servers/pm/mproc.h"
85 #include "../../servers/vfs/fproc.h"
86 #include "../../servers/vfs/const.h"
89 /*----- ps's local stuff below this line ------*/
92 #define mindev(dev) (((dev)>>MINOR) & 0377) /* yield minor device */
93 #define majdev(dev) (((dev)>>MAJOR) & 0377) /* yield major device */
95 #define TTY_MAJ 4 /* major device of console */
97 /* Structure for tty name info. */
99 char tty_name
[NAME_MAX
+ 1]; /* file name in /dev */
100 dev_t tty_dev
; /* major/minor pair */
103 ttyinfo_t
*ttyinfo
; /* ttyinfo holds actual tty info */
104 size_t n_ttyinfo
; /* Number of tty info slots */
106 /* Macro to convert memory offsets to rounded kilo-units */
107 #define off_to_k(off) ((unsigned) (((off) + 512) / 1024))
110 /* Number of tasks and processes and addresses of the main process tables. */
111 int nr_tasks
, nr_procs
;
112 vir_bytes proc_addr
, mproc_addr
, fproc_addr
;
115 /* Process tables of the kernel, MM, and FS. */
116 struct proc
*ps_proc
;
117 struct mproc
*ps_mproc
;
118 struct fproc
*ps_fproc
;
122 #define low_user init_proc_nr
124 #define KMEM_PATH "/dev/kmem" /* opened for kernel proc table */
125 #define MEM_PATH "/dev/mem" /* opened for pm/fs + user processes */
127 int kmemfd
, memfd
; /* file descriptors of [k]mem */
129 /* Short and long listing formats:
132 * ppppp tttmmm:ss cccccccccc...
134 * F S UID PID PPID PGRP SZ RECV TTY TIME CMD
135 * fff s uuu ppppp ppppp ppppp ssss rrrrrrrrrr tttmmm:ss cccccccc...
137 #define S_HEADER " PID TTY TIME CMD\n"
138 #define S_FORMAT "%5s %3s %s %s\n"
139 #define L_HEADER " F S UID PID PPID PGRP SZ RECV TTY TIME CMD\n"
140 #define L_FORMAT "%3o %c %3d %5s %5d %5d %6d %12s %3s %s %s\n"
143 struct pstat
{ /* structure filled by pstat() */
144 dev_t ps_dev
; /* major/minor of controlling tty */
145 uid_t ps_ruid
; /* real uid */
146 uid_t ps_euid
; /* effective uid */
147 pid_t ps_pid
; /* process id */
148 pid_t ps_ppid
; /* parent process id */
149 int ps_pgrp
; /* process group id */
150 int ps_flags
; /* kernel flags */
151 int ps_mflags
; /* mm flags */
152 int ps_ftask
; /* (possibly pseudo) fs suspend task */
153 char ps_state
; /* process state */
154 vir_bytes ps_tsize
; /* text size (in bytes) */
155 vir_bytes ps_dsize
; /* data size (in bytes) */
156 vir_bytes ps_ssize
; /* stack size (in bytes) */
157 phys_bytes ps_vtext
; /* virtual text offset */
158 phys_bytes ps_vdata
; /* virtual data offset */
159 phys_bytes ps_vstack
; /* virtual stack offset */
160 phys_bytes ps_text
; /* physical text offset */
161 phys_bytes ps_data
; /* physical data offset */
162 phys_bytes ps_stack
; /* physical stack offset */
163 int ps_recv
; /* process number to receive from */
164 time_t ps_utime
; /* accumulated user time */
165 time_t ps_stime
; /* accumulated system time */
166 char *ps_args
; /* concatenated argument string */
167 vir_bytes ps_procargs
; /* initial stack frame from MM */
170 /* Ps_state field values in pstat struct above */
171 #define Z_STATE 'Z' /* Zombie */
172 #define W_STATE 'W' /* Waiting */
173 #define S_STATE 'S' /* Sleeping */
174 #define R_STATE 'R' /* Runnable */
175 #define T_STATE 'T' /* stopped (Trace) */
177 _PROTOTYPE(char *tname
, (Dev_t dev_nr
));
178 _PROTOTYPE(char *taskname
, (int p_nr
));
179 _PROTOTYPE(char *prrecv
, (struct pstat
*bufp
));
180 _PROTOTYPE(void disaster
, (int sig
));
181 _PROTOTYPE(int main
, (int argc
, char *argv
[]));
182 _PROTOTYPE(char *get_args
, (struct pstat
*bufp
));
183 _PROTOTYPE(int pstat
, (int p_nr
, struct pstat
*bufp
, int Eflag
));
184 _PROTOTYPE(int addrread
, (int fd
, phys_clicks base
, vir_bytes addr
,
185 char *buf
, int nbytes
));
186 _PROTOTYPE(void usage
, (char *pname
));
187 _PROTOTYPE(void err
, (char *s
));
188 _PROTOTYPE(int gettynames
, (void));
192 * Tname returns mnemonic string for dev_nr. This is "?" for maj/min pairs that
193 * are not found. It uses the ttyinfo array (prepared by gettynames).
194 * Tname assumes that the first three letters of the tty's name can be omitted
195 * and returns the rest (except for the console, which yields "co").
202 if (majdev(dev_nr
) == TTY_MAJ
&& mindev(dev_nr
) == 0) return "co";
204 for (i
= 0; i
< n_ttyinfo
&& ttyinfo
[i
].tty_name
[0] != '\0'; i
++)
205 if (ttyinfo
[i
].tty_dev
== dev_nr
)
206 return ttyinfo
[i
].tty_name
+ 3;
211 /* Return canonical task name of task p_nr; overwritten on each call (yucch) */
215 return ps_proc
[_ENDPOINT_P(p_nr
) + nr_tasks
].p_name
;
218 /* Prrecv prints the RECV field for process with pstat buffer pointer bufp.
219 * This is either "ANY", "taskname", or "(blockreason) taskname".
224 char *blkstr
, *task
; /* reason for blocking and task */
225 static char recvstr
[20];
227 if (bufp
->ps_recv
== ANY
) return "ANY";
229 task
= taskname(bufp
->ps_recv
);
230 if (bufp
->ps_state
!= S_STATE
) return task
;
233 if (bufp
->ps_recv
== PM_PROC_NR
) {
234 if (bufp
->ps_mflags
& PAUSED
)
236 else if (bufp
->ps_mflags
& WAITING
)
238 else if (bufp
->ps_mflags
& SIGSUSPENDED
)
240 } else if (bufp
->ps_recv
== FS_PROC_NR
) {
241 if (-bufp
->ps_ftask
== XPIPE
)
243 else if (-bufp
->ps_ftask
== XPOPEN
)
245 else if (-bufp
->ps_ftask
== XDOPEN
)
247 else if (-bufp
->ps_ftask
== XLOCK
)
249 else if(-bufp
->ps_ftask
== XSELECT
)
251 else if(-bufp
->ps_ftask
>= 0)
252 blkstr
= taskname(-bufp
->ps_ftask
);
256 (void) sprintf(recvstr
, "(%s) %s", blkstr
, task
);
260 /* If disaster is called some of the system parameters imported into ps are
261 * probably wrong. This tends to result in memory faults.
266 fprintf(stderr
, "Ooops, got signal %d\n", sig
);
267 fprintf(stderr
, "Was ps recompiled since the last kernel change?\n");
271 /* Main interprets arguments, gets system addresses, opens [k]mem, reads in
272 * process tables from kernel/pm/fs and calls pstat() for relevant entries.
281 int uid
= getuid(); /* real uid of caller */
283 int opt_all
= FALSE
; /* -a */
284 int opt_long
= FALSE
; /* -l */
285 int opt_notty
= FALSE
; /* -x */
286 int opt_endpoint
= FALSE
; /* -E */
287 char *ke_path
; /* paths of kernel, */
288 char *mm_path
; /* mm, */
289 char *fs_path
; /* and fs used in ps -U */
290 char pid
[2 + sizeof(pid_t
) * 3];
291 unsigned long ustime
;
292 char cpu
[sizeof(clock_t) * 3 + 1 + 2];
296 (void) signal(SIGSEGV
, disaster
); /* catch a common crash */
298 /* Parse arguments; a '-' need not be present (V7/BSD compatability) */
299 for (i
= 1; i
< argc
; i
++) {
301 if (opt
[0] == '-') opt
++;
302 while (*opt
!= 0) switch (*opt
++) {
303 case 'a': opt_all
= TRUE
; break;
304 case 'E': opt_endpoint
= TRUE
; break;
305 case 'e': opt_all
= opt_notty
= TRUE
; break;
307 case 'l': opt_long
= TRUE
; break;
308 case 'x': opt_notty
= TRUE
; break;
309 default: usage(argv
[0]);
313 /* Open memory devices and get PS info from the kernel */
314 if ((kmemfd
= open(KMEM_PATH
, O_RDONLY
)) == -1) err(KMEM_PATH
);
315 if ((memfd
= open(MEM_PATH
, O_RDONLY
)) == -1) err(MEM_PATH
);
316 if (gettynames() == -1) err("Can't get tty names");
318 getsysinfo(PM_PROC_NR
, SI_PROC_ADDR
, &mproc_addr
);
319 getsysinfo(FS_PROC_NR
, SI_PROC_ADDR
, &fproc_addr
);
320 getsysinfo(PM_PROC_NR
, SI_KINFO
, &kinfo
);
321 proc_addr
= kinfo
.proc_addr
;
322 nr_tasks
= kinfo
.nr_tasks
;
323 nr_procs
= kinfo
.nr_procs
;
325 /* Allocate memory for process tables */
326 ps_proc
= (struct proc
*) malloc((nr_tasks
+ nr_procs
) * sizeof(ps_proc
[0]));
327 ps_mproc
= (struct mproc
*) malloc(nr_procs
* sizeof(ps_mproc
[0]));
328 ps_fproc
= (struct fproc
*) malloc(nr_procs
* sizeof(ps_fproc
[0]));
329 if (ps_proc
== NULL
|| ps_mproc
== NULL
|| ps_fproc
== NULL
)
330 err("Out of memory");
332 /* Get kernel process table */
333 if (addrread(kmemfd
, (phys_clicks
) 0,
334 proc_addr
, (char *) ps_proc
,
335 (nr_tasks
+ nr_procs
) * sizeof(ps_proc
[0]))
336 != (nr_tasks
+ nr_procs
) * sizeof(ps_proc
[0]))
337 err("Can't get kernel proc table from /dev/kmem");
339 /* Get mm/fs process tables */
340 if (addrread(memfd
, ps_proc
[nr_tasks
+ PM_PROC_NR
].p_memmap
[D
].mem_phys
,
341 mproc_addr
, (char *) ps_mproc
,
342 nr_procs
* sizeof(ps_mproc
[0]))
343 != nr_procs
* sizeof(ps_mproc
[0]))
344 err("Can't get mm proc table from /dev/mem");
345 if (addrread(memfd
, ps_proc
[nr_tasks
+ FS_PROC_NR
].p_memmap
[D
].mem_phys
,
346 fproc_addr
, (char *) ps_fproc
,
347 nr_procs
* sizeof(ps_fproc
[0]))
348 != nr_procs
* sizeof(ps_fproc
[0]))
349 err("Can't get fs proc table from /dev/mem");
351 /* We need to know where INIT hangs out. */
352 for (i
= FS_PROC_NR
; i
< nr_procs
; i
++) {
353 if (strcmp(ps_proc
[nr_tasks
+ i
].p_name
, "init") == 0) break;
357 /* Now loop through process table and handle each entry */
358 printf("%s", opt_long
? L_HEADER
: S_HEADER
);
359 for (i
= -nr_tasks
; i
< nr_procs
; i
++) {
360 if (pstat(i
, &buf
, opt_endpoint
) != -1 &&
361 (opt_all
|| buf
.ps_euid
== uid
|| buf
.ps_ruid
== uid
) &&
362 (opt_notty
|| majdev(buf
.ps_dev
) == TTY_MAJ
)) {
363 if (buf
.ps_pid
== 0 && i
!= PM_PROC_NR
) {
364 sprintf(pid
, "(%d)", i
);
366 sprintf(pid
, "%d", buf
.ps_pid
);
369 ustime
= (buf
.ps_utime
+ buf
.ps_stime
) / HZ
;
370 if (ustime
< 60 * 60) {
371 sprintf(cpu
, "%2lu:%02lu", ustime
/ 60, ustime
% 60);
373 if (ustime
< 100L * 60 * 60) {
375 sprintf(cpu
, "%2luh%02lu", ustime
/ 60, ustime
% 60);
377 sprintf(cpu
, "%4luh", ustime
/ 3600);
380 if (opt_long
) printf(L_FORMAT
,
381 buf
.ps_flags
, buf
.ps_state
,
382 buf
.ps_euid
, pid
, buf
.ps_ppid
,
384 off_to_k((buf
.ps_tsize
385 + buf
.ps_stack
- buf
.ps_data
387 (buf
.ps_flags
& RECEIVING
?
390 tname((Dev_t
) buf
.ps_dev
),
392 i
<= init_proc_nr
|| buf
.ps_args
== NULL
393 ? taskname(i
) : buf
.ps_args
);
396 pid
, tname((Dev_t
) buf
.ps_dev
),
398 i
<= init_proc_nr
|| buf
.ps_args
== NULL
399 ? taskname(i
) : buf
.ps_args
);
409 int cnt
; /* # of bytes read from stack frame */
410 int neos
; /* # of '\0's seen in argv string space */
418 } stk
[ARG_MAX
/ sizeof(char *)];
421 /* Phys address of the original stack frame. */
422 iframe
= bufp
->ps_procargs
- bufp
->ps_vstack
+ bufp
->ps_stack
;
424 /* Calculate the number of bytes to read from user stack */
425 l
= (phys_bytes
) bufp
->ps_ssize
- (iframe
- bufp
->ps_stack
);
426 if (l
> ARG_MAX
) l
= ARG_MAX
;
429 /* Get cnt bytes from user initial stack to local stack buffer */
430 if (lseek(memfd
, (off_t
) iframe
, 0) < 0)
433 if ( read(memfd
, (char *)stk
, cnt
) != cnt
)
437 nargv
= (int) sp
[0].stk_i
; /* number of argv arguments */
439 /* See if argv[0] is with the bytes we read in */
440 l
= (long) sp
[1].stk_cp
- (long) bufp
->ps_procargs
;
442 if ( ( l
< 0 ) || ( l
> cnt
) )
445 /* l is the offset of the argv[0] argument */
446 /* change for concatenation the '\0' to space, for nargv elements */
448 args
= &((char *) stk
)[(int)l
];
450 for (cp
= args
; cp
< &((char *) stk
)[cnt
]; cp
++)
456 if (cp
== args
) return NULL
;
463 /* Pstat collects info on process number p_nr and returns it in buf.
464 * It is assumed that tasks do not have entries in fproc/mproc.
466 int pstat(p_nr
, bufp
, endpoints
)
471 int p_ki
= p_nr
+ nr_tasks
; /* kernel proc index */
473 if (p_nr
< -nr_tasks
|| p_nr
>= nr_procs
) return -1;
475 if ((ps_proc
[p_ki
].p_rts_flags
== SLOT_FREE
)
476 && !(ps_mproc
[p_nr
].mp_flags
& IN_USE
))
479 bufp
->ps_flags
= ps_proc
[p_ki
].p_rts_flags
;
481 if (p_nr
>= low_user
) {
482 bufp
->ps_dev
= ps_fproc
[p_nr
].fp_tty
;
483 bufp
->ps_ftask
= ps_fproc
[p_nr
].fp_task
;
490 bufp
->ps_ruid
= ps_mproc
[p_nr
].mp_realuid
;
491 bufp
->ps_euid
= ps_mproc
[p_nr
].mp_effuid
;
492 if(endpoints
) bufp
->ps_pid
= ps_proc
[p_ki
].p_endpoint
;
493 else bufp
->ps_pid
= ps_mproc
[p_nr
].mp_pid
;
494 bufp
->ps_ppid
= ps_mproc
[ps_mproc
[p_nr
].mp_parent
].mp_pid
;
495 bufp
->ps_pgrp
= ps_mproc
[p_nr
].mp_procgrp
;
496 bufp
->ps_mflags
= ps_mproc
[p_nr
].mp_flags
;
498 if(endpoints
) bufp
->ps_pid
= ps_proc
[p_ki
].p_endpoint
;
499 else bufp
->ps_pid
= 0;
501 bufp
->ps_ruid
= bufp
->ps_euid
= 0;
506 /* State is interpretation of combined kernel/mm flags for non-tasks */
507 if (p_nr
>= low_user
) { /* non-tasks */
508 if (ps_mproc
[p_nr
].mp_flags
& ZOMBIE
)
509 bufp
->ps_state
= Z_STATE
; /* zombie */
510 else if (ps_mproc
[p_nr
].mp_flags
& STOPPED
)
511 bufp
->ps_state
= T_STATE
; /* stopped (traced) */
512 else if (ps_proc
[p_ki
].p_rts_flags
== 0)
513 bufp
->ps_state
= R_STATE
; /* in run-queue */
514 else if (ps_mproc
[p_nr
].mp_flags
& (WAITING
| PAUSED
| SIGSUSPENDED
) ||
515 ps_fproc
[p_nr
].fp_suspended
== SUSPENDED
)
516 bufp
->ps_state
= S_STATE
; /* sleeping */
518 bufp
->ps_state
= W_STATE
; /* a short wait */
519 } else { /* tasks are simple */
520 if (ps_proc
[p_ki
].p_rts_flags
== 0)
521 bufp
->ps_state
= R_STATE
; /* in run-queue */
523 bufp
->ps_state
= W_STATE
; /* other i.e. waiting */
526 bufp
->ps_tsize
= (size_t) ps_proc
[p_ki
].p_memmap
[T
].mem_len
<< CLICK_SHIFT
;
527 bufp
->ps_dsize
= (size_t) ps_proc
[p_ki
].p_memmap
[D
].mem_len
<< CLICK_SHIFT
;
528 bufp
->ps_ssize
= (size_t) ps_proc
[p_ki
].p_memmap
[S
].mem_len
<< CLICK_SHIFT
;
529 bufp
->ps_vtext
= (off_t
) ps_proc
[p_ki
].p_memmap
[T
].mem_vir
<< CLICK_SHIFT
;
530 bufp
->ps_vdata
= (off_t
) ps_proc
[p_ki
].p_memmap
[D
].mem_vir
<< CLICK_SHIFT
;
531 bufp
->ps_vstack
= (off_t
) ps_proc
[p_ki
].p_memmap
[S
].mem_vir
<< CLICK_SHIFT
;
532 bufp
->ps_text
= (off_t
) ps_proc
[p_ki
].p_memmap
[T
].mem_phys
<< CLICK_SHIFT
;
533 bufp
->ps_data
= (off_t
) ps_proc
[p_ki
].p_memmap
[D
].mem_phys
<< CLICK_SHIFT
;
534 bufp
->ps_stack
= (off_t
) ps_proc
[p_ki
].p_memmap
[S
].mem_phys
<< CLICK_SHIFT
;
536 bufp
->ps_recv
= _ENDPOINT_P(ps_proc
[p_ki
].p_getfrom_e
);
538 bufp
->ps_utime
= ps_proc
[p_ki
].p_user_time
;
539 bufp
->ps_stime
= ps_proc
[p_ki
].p_sys_time
;
541 bufp
->ps_procargs
= ps_mproc
[p_nr
].mp_procargs
;
543 if (bufp
->ps_state
== Z_STATE
)
544 bufp
->ps_args
= "<defunct>";
545 else if (p_nr
> init_proc_nr
)
546 bufp
->ps_args
= get_args(bufp
);
551 /* Addrread reads nbytes from offset addr to click base of fd into buf. */
552 int addrread(fd
, base
, addr
, buf
, nbytes
)
559 if (lseek(fd
, ((off_t
) base
<< CLICK_SHIFT
) + addr
, 0) < 0)
562 return read(fd
, buf
, nbytes
);
568 fprintf(stderr
, "Usage: %s [-][aeflx]\n", pname
);
578 fprintf(stderr
, "ps: %s\n", s
);
580 fprintf(stderr
, "ps: %s: %s\n", s
, strerror(errno
));
585 /* Fill ttyinfo by fstatting character specials in /dev. */
588 static char dev_path
[] = "/dev/";
590 static char path
[sizeof(dev_path
) + NAME_MAX
];
595 while ((ttyp
= getttyent()) != NULL
) {
596 strcpy(path
, dev_path
);
597 strcat(path
, ttyp
->ty_name
);
598 if (stat(path
, &statbuf
) == -1 || !S_ISCHR(statbuf
.st_mode
))
600 if (index
>= n_ttyinfo
) {
601 n_ttyinfo
= (index
+16) * 2;
602 ttyinfo
= realloc(ttyinfo
, n_ttyinfo
* sizeof(ttyinfo
[0]));
603 if (ttyinfo
== NULL
) err("Out of memory");
605 ttyinfo
[index
].tty_dev
= statbuf
.st_rdev
;
606 strcpy(ttyinfo
[index
].tty_name
, ttyp
->ty_name
);
610 while (index
< n_ttyinfo
) ttyinfo
[index
++].tty_dev
= 0;