1 /* ps - print status Author: Peter Valkenburg */
2 /* Modified for ProcFS by Alen Stojanov and David van Moolenbroek */
4 /* Ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990.
6 * This is a V7 ps(1) look-alike for MINIX >= 1.5.0.
7 * It does not support the 'k' option (i.e. cannot read memory from core file).
8 * If you want to compile this for non-IBM PC architectures, the header files
9 * require that you have your CHIP, MACHINE etc. defined.
12 * Option `a' gives all processes, `l' for detailed info, `x' includes even
13 * processes without a terminal.
14 * The `f' and `e' options were added by Kees Bot for the convenience of
15 * Solaris users accustomed to these options. The `e' option is equivalent to
16 * `a' and `f' is equivalent to -l. These do not appear in the usage message.
19 /* Some technical comments on this implementation:
21 * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are
22 * absent, RECV which replaces WCHAN, and PGRP that is an extra.
23 * The info is obtained from the following fields of proc, mproc and fproc:
24 * ST - kernel status field, p_rts_flags; pm status field, mp_flags (R if
25 * p_rts_flags is 0; Z if mp_flags == ZOMBIE; T if mp_flags == STOPPED;
27 * UID - pm eff uid field, mp_effuid
28 * PID - pm pid field, mp_pid
29 * PPID - pm parent process index field, mp_parent (used as index in proc).
30 * PGRP - pm process group field, mp_procgrp
31 * SZ - memory size, including common and shared memory
32 * RECV - kernel process index field for message receiving, p_getfrom
33 * If sleeping, pm's mp_flags, or fs's fp_task are used for more info.
34 * TTY - fs controlling tty device field, fp_tty.
35 * TIME - kernel user + system times fields, user_time + sys_time
36 * CMD - system process index (converted to mnemonic name by using the p_name
37 * field), or user process argument list (obtained by reading the stack
38 * frame; the resulting address is used to get the argument vector from
39 * user space and converted into a concatenated argument list).
42 #include <minix/config.h>
43 #include <minix/endpoint.h>
45 #include <minix/procfs.h>
47 #include <sys/types.h>
49 #include <minix/const.h>
50 #include <minix/type.h>
51 #include <minix/dmap.h>
63 /*----- ps's local stuff below this line ------*/
65 /* Structure for tty name info. */
67 char tty_name
[NAME_MAX
+ 1]; /* file name in /dev */
68 dev_t tty_dev
; /* major/minor pair */
71 ttyinfo_t
*ttyinfo
; /* ttyinfo holds actual tty info */
72 size_t n_ttyinfo
; /* Number of tty info slots */
74 u32_t system_hz
; /* system clock frequency */
75 unsigned int nr_procs
; /* maximum number of processes */
76 unsigned int nr_tasks
; /* maximum number of tasks */
78 struct pstat
*ptable
; /* table with process information */
80 /* Macro to convert endpoints to slots into ptable */
81 #define SLOT_NR(e) (_ENDPOINT_P(e) + nr_tasks)
83 /* Macro to convert memory offsets to rounded kilo-units */
84 #define off_to_k(off) ((unsigned) (((off) + 512) / 1024))
87 /* Short and long listing formats:
90 * ppppp tttmmm:ss cccccccccc...
92 * ST UID PID PPID PGRP SZ RECV TTY TIME CMD
93 * s uuu ppppp ppppp ppppp ssss rrrrrrrrrr tttmmm:ss cccccccc...
95 #define S_HEADER " PID TTY TIME CMD\n"
96 #define S_FORMAT "%5s %3s %s %s\n"
97 #define L_HEADER "ST UID PID PPID PGRP SZ RECV TTY TIME CMD\n"
98 #define L_FORMAT " %c %3d %5s %5d %5d %6d %12s %3s %s %s\n"
101 struct pstat
{ /* structure filled by pstat() */
102 struct pstat
*ps_next
; /* next in process list */
103 int ps_task
; /* is this process a task or not? */
104 int ps_endpt
; /* process endpoint (NONE means unused slot) */
105 dev_t ps_dev
; /* major/minor of controlling tty */
106 uid_t ps_ruid
; /* real uid */
107 uid_t ps_euid
; /* effective uid */
108 pid_t ps_pid
; /* process id */
109 pid_t ps_ppid
; /* parent process id */
110 int ps_pgrp
; /* process group id */
111 char ps_state
; /* process state */
112 char ps_pstate
; /* sleep state */
113 char ps_fstate
; /* VFS block state */
114 int ps_ftask
; /* VFS suspend task (endpoint) */
115 vir_bytes ps_memory
; /* memory usage */
116 int ps_recv
; /* process number to receive from (endpoint) */
117 time_t ps_utime
; /* accumulated user time */
118 time_t ps_stime
; /* accumulated system time */
119 char ps_name
[PROC_NAME_LEN
+1];/* process name */
120 char *ps_args
; /* concatenated argument string */
123 int main(int argc
, char *argv
[]);
125 int addrread(int fd
, phys_clicks base
, vir_bytes addr
, char *buf
, int
127 void usage(const char *pname
);
128 void err(const char *s
);
129 int gettynames(void);
133 * Tname returns mnemonic string for dev_nr. This is "?" for maj/min pairs that
134 * are not found. It uses the ttyinfo array (prepared by gettynames).
135 * Tname assumes that the first three letters of the tty's name can be omitted
136 * and returns the rest (except for the console, which yields "co").
138 static char *tname(dev_t dev_nr
)
142 if (major(dev_nr
) == TTY_MAJOR
&& minor(dev_nr
) == 0) return "co";
144 for (i
= 0; i
< n_ttyinfo
&& ttyinfo
[i
].tty_name
[0] != '\0'; i
++)
145 if (ttyinfo
[i
].tty_dev
== dev_nr
)
146 return ttyinfo
[i
].tty_name
+ 3;
151 /* Find a task by its endpoint. */
152 static struct pstat
*findtask(endpoint_t endpt
)
157 slot
= SLOT_NR(endpt
);
159 if (slot
>= nr_tasks
+ nr_procs
)
164 if (ps
!= NULL
&& ps
->ps_endpt
== (int) endpt
)
170 /* Return canonical task name of the given endpoint. */
171 static char *taskname(endpoint_t endpt
)
175 ps
= findtask(endpt
);
177 return ps
? ps
->ps_name
: "???";
180 /* Prrecv prints the RECV field for process with pstat buffer pointer ps.
181 * This is either "ANY", "taskname", or "(blockreason) taskname".
183 static char *prrecv(struct pstat
*ps
)
185 char *blkstr
, *task
; /* reason for blocking and task */
186 static char recvstr
[20];
188 if (ps
->ps_recv
== ANY
) return "ANY";
190 task
= taskname(ps
->ps_recv
);
191 if (ps
->ps_state
!= STATE_SLEEP
) return task
;
194 if (ps
->ps_recv
== PM_PROC_NR
) {
195 switch (ps
->ps_pstate
) {
196 case PSTATE_PAUSED
: blkstr
= "pause"; break;
197 case PSTATE_WAITING
: blkstr
= "wait"; break;
198 case PSTATE_SIGSUSP
: blkstr
= "sigsusp"; break;
200 } else if (ps
->ps_recv
== VFS_PROC_NR
) {
201 switch (ps
->ps_fstate
) {
202 case FSTATE_PIPE
: blkstr
= "pipe"; break;
203 case FSTATE_LOCK
: blkstr
= "flock"; break;
204 case FSTATE_POPEN
: blkstr
= "popen"; break;
205 case FSTATE_SELECT
: blkstr
= "select"; break;
206 case FSTATE_DOPEN
: blkstr
= "dopen"; break;
207 case FSTATE_TASK
: blkstr
= taskname(ps
->ps_ftask
); break;
208 default: blkstr
= "??"; break;
211 (void) sprintf(recvstr
, "(%s) %s", blkstr
, task
);
215 static void getkinfo(void)
219 if ((fp
= fopen("kinfo", "r")) == NULL
)
220 err("Unable to open " _PATH_PROC
"kinfo");
222 if (fscanf(fp
, "%u %u", &nr_procs
, &nr_tasks
) != 2)
223 err("Unable to read from " _PATH_PROC
"kinfo");
228 /* Main interprets arguments, gathers information, and prints a process list.
237 int uid
= getuid(); /* real uid of caller */
239 int opt_all
= FALSE
; /* -a */
240 int opt_long
= FALSE
; /* -l */
241 int opt_notty
= FALSE
; /* -x */
242 int opt_endpoint
= FALSE
; /* -E */
243 char pid
[2 + sizeof(pid_t
) * 3];
244 unsigned long ustime
;
245 char cpu
[sizeof(clock_t) * 3 + 1 + 2];
247 /* Parse arguments; a '-' need not be present (V7/BSD compatability) */
248 for (i
= 1; i
< argc
; i
++) {
250 if (opt
[0] == '-') opt
++;
251 while (*opt
!= 0) switch (*opt
++) {
252 case 'a': opt_all
= TRUE
; break;
253 case 'E': opt_endpoint
= TRUE
; break;
254 case 'e': opt_all
= opt_notty
= TRUE
; break;
256 case 'l': opt_long
= TRUE
; break;
257 case 'x': opt_notty
= TRUE
; break;
258 default: usage(argv
[0]);
262 if (gettynames() == -1) err("Can't get tty names");
264 if (chdir(_PATH_PROC
) != 0) err("Can't chdir to /proc");
266 /* Get information from the proc file system */
267 system_hz
= (u32_t
) sysconf(_SC_CLK_TCK
);
273 /* Now loop through process table and handle each entry */
274 printf("%s", opt_long
? L_HEADER
: S_HEADER
);
275 for (n
= 0; n
< nr_procs
+ nr_tasks
; n
++) {
277 if (ps
->ps_endpt
== NONE
)
280 if ((opt_all
|| ps
->ps_euid
== uid
|| ps
->ps_ruid
== uid
) &&
281 (opt_notty
|| major(ps
->ps_dev
) == TTY_MAJOR
)) {
283 sprintf(pid
, "(%d)", ps
->ps_pid
);
286 opt_endpoint
? ps
->ps_endpt
: ps
->ps_pid
);
289 ustime
= (ps
->ps_utime
+ ps
->ps_stime
) / system_hz
;
290 if (ustime
< 60 * 60) {
291 sprintf(cpu
, "%2lu:%02lu", ustime
/ 60, ustime
% 60);
293 if (ustime
< 100L * 60 * 60) {
295 sprintf(cpu
, "%2luh%02lu", ustime
/ 60, ustime
% 60);
297 sprintf(cpu
, "%4luh", ustime
/ 3600);
300 if (opt_long
) printf(L_FORMAT
,
302 ps
->ps_euid
, pid
, ps
->ps_ppid
,
304 off_to_k(ps
->ps_memory
),
305 (ps
->ps_recv
!= NONE
? prrecv(ps
) : ""),
306 tname((dev_t
) ps
->ps_dev
),
308 ps
->ps_args
!= NULL
? ps
->ps_args
: ps
->ps_name
312 pid
, tname((dev_t
) ps
->ps_dev
),
314 ps
->ps_args
!= NULL
? ps
->ps_args
: ps
->ps_name
321 /* Get_args obtains the command line of a process. */
322 char *get_args(struct pstat
*ps
)
324 char path
[PATH_MAX
], buf
[4096];
328 /* Get a reasonable subset of the contents of the 'cmdline' file from procfs.
329 * It contains all arguments, separated and terminated by null characters.
331 sprintf(path
, "%d/cmdline", ps
->ps_pid
);
333 fd
= open(path
, O_RDONLY
);
334 if (fd
< 0) return NULL
;
336 n
= read(fd
, buf
, sizeof(buf
));
345 /* Replace all argument separating null characters with spaces. */
346 for (i
= 0; i
< n
-1; i
++)
350 /* The last character should already be null, except if it got cut off. */
356 /* Pstat obtains the actual information for the given process, and stores it
357 * in the pstat structure. The outside world may change while we are doing
358 * this, so nothing is reported in case any of the calls fail.
360 int pstat(struct pstat
*ps
, pid_t pid
)
363 int version
, ruid
, euid
, dev
;
364 char type
, path
[PATH_MAX
], name
[256];
369 sprintf(path
, "%d/psinfo", pid
);
371 if ((fp
= fopen(path
, "r")) == NULL
)
374 if (fscanf(fp
, "%d", &version
) != 1) {
379 /* The psinfo file's version must match what we expect. */
380 if (version
!= PSINFO_VERSION
) {
381 fputs("procfs version mismatch!\n", stderr
);
385 if (fscanf(fp
, " %c %d %255s %c %d %*d %lu %lu %*u %*u",
386 &type
, &ps
->ps_endpt
, name
, &ps
->ps_state
,
387 &ps
->ps_recv
, &ps
->ps_utime
, &ps
->ps_stime
) != 7) {
393 strncpy(ps
->ps_name
, name
, sizeof(ps
->ps_name
)-1);
394 ps
->ps_name
[sizeof(ps
->ps_name
)-1] = 0;
396 ps
->ps_task
= type
== TYPE_TASK
;
399 if (fscanf(fp
, " %lu %*u %*u %c %d %u %u %u %*d %c %d %u",
400 &ps
->ps_memory
, &ps
->ps_pstate
, &ps
->ps_ppid
,
401 &ruid
, &euid
, &ps
->ps_pgrp
, &ps
->ps_fstate
,
402 &ps
->ps_ftask
, &dev
) != 9) {
413 ps
->ps_pstate
= PSTATE_NONE
;
418 ps
->ps_fstate
= FSTATE_NONE
;
425 if (ps
->ps_state
== STATE_ZOMBIE
)
426 ps
->ps_args
= "<defunct>";
427 else if (!ps
->ps_task
)
428 ps
->ps_args
= get_args(ps
);
435 /* Plist creates a list of processes with status information. */
439 struct dirent
*p_ent
;
445 /* Allocate a table for process information. Initialize all slots' endpoints
446 * to NONE, indicating those slots are not used.
448 if ((ptable
= malloc((nr_tasks
+ nr_procs
) * sizeof(struct pstat
))) == NULL
)
449 err("Out of memory!");
451 for (slot
= 0; slot
< nr_tasks
+ nr_procs
; slot
++)
452 ptable
[slot
].ps_endpt
= NONE
;
454 /* Fill in the table slots for all existing processes, by retrieving all PID
455 * entries from the /proc directory.
457 p_dir
= opendir(".");
459 if (p_dir
== NULL
) err("Can't open " _PATH_PROC
);
461 p_ent
= readdir(p_dir
);
462 while (p_ent
!= NULL
) {
463 pid
= strtol(p_ent
->d_name
, &end
, 10);
465 if (!end
[0] && pid
!= 0 && !pstat(&pbuf
, pid
)) {
466 slot
= SLOT_NR(pbuf
.ps_endpt
);
468 if (slot
< nr_tasks
+ nr_procs
)
469 memcpy(&ptable
[slot
], &pbuf
, sizeof(pbuf
));
472 p_ent
= readdir(p_dir
);
478 void usage(const char *pname
)
480 fprintf(stderr
, "Usage: %s [-][aeflx]\n", pname
);
484 void err(const char *s
)
489 fprintf(stderr
, "ps: %s\n", s
);
491 fprintf(stderr
, "ps: %s: %s\n", s
, strerror(errno
));
496 /* Fill ttyinfo by fstatting character specials in /dev. */
499 static char dev_path
[] = "/dev/";
501 static char path
[sizeof(dev_path
) + NAME_MAX
];
506 while ((ttyp
= getttyent()) != NULL
) {
507 strcpy(path
, dev_path
);
508 strcat(path
, ttyp
->ty_name
);
509 if (stat(path
, &statbuf
) == -1 || !S_ISCHR(statbuf
.st_mode
))
511 if (index
>= n_ttyinfo
) {
512 n_ttyinfo
= (index
+16) * 2;
513 ttyinfo
= realloc(ttyinfo
, n_ttyinfo
* sizeof(ttyinfo
[0]));
514 if (ttyinfo
== NULL
) err("Out of memory");
516 ttyinfo
[index
].tty_dev
= statbuf
.st_rdev
;
517 strcpy(ttyinfo
[index
].tty_name
, ttyp
->ty_name
);
521 while (index
< n_ttyinfo
) ttyinfo
[index
++].tty_dev
= 0;