mkfs: support indirect blocks in directories
[minix.git] / commands / ps / ps.c
blob4d978213c1c99edace088fdf4186a05adf4f0e61
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.
10 * Full syntax:
11 * ps [-][aeflx]
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;
26 * else W).
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>
44 #include <paths.h>
45 #include <minix/procfs.h>
46 #include <limits.h>
47 #include <sys/types.h>
49 #include <minix/const.h>
50 #include <minix/type.h>
51 #include <minix/dmap.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <unistd.h>
56 #include <fcntl.h>
57 #include <dirent.h>
58 #include <sys/stat.h>
59 #include <stdio.h>
60 #include <ttyent.h>
63 /*----- ps's local stuff below this line ------*/
65 /* Structure for tty name info. */
66 typedef struct {
67 char tty_name[NAME_MAX + 1]; /* file name in /dev */
68 dev_t tty_dev; /* major/minor pair */
69 } ttyinfo_t;
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:
89 * PID TTY TIME CMD
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 []);
124 void plist(void);
125 int addrread(int fd, phys_clicks base, vir_bytes addr, char *buf, int
126 nbytes );
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)
140 unsigned int i;
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;
148 return "?";
151 /* Find a task by its endpoint. */
152 static struct pstat *findtask(endpoint_t endpt)
154 struct pstat *ps;
155 unsigned int slot;
157 slot = SLOT_NR(endpt);
159 if (slot >= nr_tasks + nr_procs)
160 return NULL;
162 ps = &ptable[slot];
164 if (ps != NULL && ps->ps_endpt == (int) endpt)
165 return ps;
167 return NULL;
170 /* Return canonical task name of the given endpoint. */
171 static char *taskname(endpoint_t endpt)
173 struct pstat *ps;
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;
193 blkstr = "?";
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);
212 return recvstr;
215 static void getkinfo(void)
217 FILE *fp;
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");
225 fclose(fp);
228 /* Main interprets arguments, gathers information, and prints a process list.
230 int main(argc, argv)
231 int argc;
232 char *argv[];
234 int i;
235 unsigned int n;
236 struct pstat *ps;
237 int uid = getuid(); /* real uid of caller */
238 char *opt;
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++) {
249 opt = argv[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;
255 case 'f':
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);
269 getkinfo();
271 plist();
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++) {
276 ps = &ptable[n];
277 if (ps->ps_endpt == NONE)
278 continue;
280 if ((opt_all || ps->ps_euid == uid || ps->ps_ruid == uid) &&
281 (opt_notty || major(ps->ps_dev) == TTY_MAJOR)) {
282 if (ps->ps_task) {
283 sprintf(pid, "(%d)", ps->ps_pid);
284 } else {
285 sprintf(pid, "%d",
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);
292 } else
293 if (ustime < 100L * 60 * 60) {
294 ustime /= 60;
295 sprintf(cpu, "%2luh%02lu", ustime / 60, ustime % 60);
296 } else {
297 sprintf(cpu, "%4luh", ustime / 3600);
300 if (opt_long) printf(L_FORMAT,
301 ps->ps_state,
302 ps->ps_euid, pid, ps->ps_ppid,
303 ps->ps_pgrp,
304 off_to_k(ps->ps_memory),
305 (ps->ps_recv != NONE ? prrecv(ps) : ""),
306 tname((dev_t) ps->ps_dev),
307 cpu,
308 ps->ps_args != NULL ? ps->ps_args : ps->ps_name
310 else
311 printf(S_FORMAT,
312 pid, tname((dev_t) ps->ps_dev),
313 cpu,
314 ps->ps_args != NULL ? ps->ps_args : ps->ps_name
318 return(0);
321 /* Get_args obtains the command line of a process. */
322 char *get_args(struct pstat *ps)
324 char path[PATH_MAX], buf[4096];
325 ssize_t i, n;
326 int fd;
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));
337 if (n <= 0) {
338 close(fd);
340 return NULL;
343 close(fd);
345 /* Replace all argument separating null characters with spaces. */
346 for (i = 0; i < n-1; i++)
347 if (buf[i] == '\0')
348 buf[i] = ' ';
350 /* The last character should already be null, except if it got cut off. */
351 buf[n-1] = '\0';
353 return strdup(buf);
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)
362 FILE *fp;
363 int version, ruid, euid, dev;
364 char type, path[PATH_MAX], name[256];
366 ps->ps_pid = pid;
367 ps->ps_next = NULL;
369 sprintf(path, "%d/psinfo", pid);
371 if ((fp = fopen(path, "r")) == NULL)
372 return -1;
374 if (fscanf(fp, "%d", &version) != 1) {
375 fclose(fp);
376 return -1;
379 /* The psinfo file's version must match what we expect. */
380 if (version != PSINFO_VERSION) {
381 fputs("procfs version mismatch!\n", stderr);
382 exit(1);
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) {
389 fclose(fp);
390 return -1;
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;
398 if (!ps->ps_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) {
404 fclose(fp);
405 return -1;
408 ps->ps_ruid = ruid;
409 ps->ps_euid = euid;
410 ps->ps_dev = dev;
411 } else {
412 ps->ps_memory = 0L;
413 ps->ps_pstate = PSTATE_NONE;
414 ps->ps_ppid = 0;
415 ps->ps_ruid = 0;
416 ps->ps_euid = 0;
417 ps->ps_pgrp = 0;
418 ps->ps_fstate = FSTATE_NONE;
419 ps->ps_ftask = NONE;
420 ps->ps_dev = NO_DEV;
423 fclose(fp);
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);
429 else
430 ps->ps_args = NULL;
432 return 0;
435 /* Plist creates a list of processes with status information. */
436 void plist(void)
438 DIR *p_dir;
439 struct dirent *p_ent;
440 struct pstat pbuf;
441 pid_t pid;
442 char *end;
443 unsigned int slot;
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);
475 closedir(p_dir);
478 void usage(const char *pname)
480 fprintf(stderr, "Usage: %s [-][aeflx]\n", pname);
481 exit(1);
484 void err(const char *s)
486 extern int errno;
488 if (errno == 0)
489 fprintf(stderr, "ps: %s\n", s);
490 else
491 fprintf(stderr, "ps: %s: %s\n", s, strerror(errno));
493 exit(2);
496 /* Fill ttyinfo by fstatting character specials in /dev. */
497 int gettynames(void)
499 static char dev_path[] = "/dev/";
500 struct stat statbuf;
501 static char path[sizeof(dev_path) + NAME_MAX];
502 unsigned int index;
503 struct ttyent *ttyp;
505 index = 0;
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))
510 continue;
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);
518 index++;
520 endttyent();
521 while (index < n_ttyinfo) ttyinfo[index++].tty_dev= 0;
523 return 0;