1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1989-2011 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Eclipse Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.eclipse.org/org/documents/epl-v10.html *
11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * ps -- list process status
26 *
27 * fall back to /bin/ps if no support -- and you better match their args!
28 */
29
30 #define FIELDS_default "pid,tty,time,comm"
31 #define FIELDS_c "pid,class,pri,tty,time,comm"
32 #define FIELDS_f "user,pid,ppid,start,tty,time,cmd"
33 #define FIELDS_j "pid,pgrp,sid,tty,time,comm"
34 #define FIELDS_l "flags,state,user,pid,ppid,pri,nice,size,rss,wchan,tty,time,cmd"
35
36 static const char usage[] =
37 "[-1o?\n@(#)$Id: ps (AT&T Research) 2011-12-13 $\n]"
38 USAGE_LICENSE
39 "[+NAME?ps - report process status]"
40 "[+DESCRIPTION?\bps\b lists process information subject to the appropriate"
41 " privilege. If \apid\a arguments are specified then only those"
42 " processes are listed, otherwise all processes with the effective"
43 " user id and controlling terminal of the caller are listed. The options"
44 " may alter this default behavior.]"
45 "[+?The listings are sorted by <\bUID,START,PID\b>. Options taking list"
46 " arguments accept either space or comma separators.]"
47
48 "[a:interactive?List all processes associated with terminals.]"
49 "[B!:branch?Print tree branch prefixes for \bcommand\b and \bargs\b."
50 " Implied by \b--children\b, \b--parents\b, and \b--tree\b.]"
51 "[c:class?Equivalent to \b--fields=" FIELDS_c "\b.]"
52 "[C:children?Display the process tree hierarchy, including the children"
53 " of all selected processes, in the \bCMD\b field list.]"
54 "[d:no-session?List all processes except session leaders.]"
55 "[D:define?Define \akey\a with optional \avalue\a. \avalue\a will be expanded"
56 " when \b%(\b\akey\a\b)\b is specified in \b--format\b. \akey\a may"
57 " override internal \b--format\b identifiers.]:[key[=value]]]"
58 "[e|A:all?List all processes.]"
59 "[E!:escape?Escape non-printing characters in \bcommand\b and \bargs\b.]"
60 "[f:full?Equivalent to \b--fields=" FIELDS_f "\b.]"
61 "[F:format?Append to the listing format string (if \b--format\b is specified"
62 " then \b--fields\b and all options that modify \b--fields\b are"
63 " ignored.) The \bdf\b(1), \bls\b(1) and \bpax\b(1) commands also have"
64 " \b--format\b options in this same style. \aformat\a follows"
65 " \bprintf\b(3) conventions, except that \bsfio\b(3) inline ids are used"
66 " instead of arguments:"
67 " %[#-+]][\awidth\a[.\aprecis\a[.\abase\a]]]]]](\aid\a[:\aheading\a]])\achar\a."
68 " If \b#\b is specified then the internal width and precision are used."
69 " If \achar\a is \bs\b then the string form of the item is listed,"
70 " otherwise the corresponding numeric form is listed. If \achar\a is"
71 " \bq\b then the string form of the item is $'...' quoted if it contains"
72 " space or non-printing characters. If \awidth\a is omitted then the"
73 " default width is assumed. \aheading\a overrides the default"
74 " heading for \aid\a. Supported \aid\as"
75 " are:]:[format]{\fformats\f}"
76 "[g:pgrps|process-groups?List processes with group leaders in the \apgrp\a"
77 " list.]:[pgrp...]"
78 "[G:groups?List processes with real group id names or numbers in the \agroup\a"
79 " list.]:[group...]"
80 "[h!:heading?Output a heading line.]"
81 "[j:jobs?Equivalent to \b--fields=" FIELDS_j "\b.]"
82 "[l:long?Equivalent to \b--fields=" FIELDS_l "\b.]"
83 "[L:leaders?List session leaders.]"
84 "[n:namelist?Specifies an alternate system namelist \afile\a. Ignored by"
85 " this implementation.]"
86 "[N:default?Equivalent to \b--fields=" FIELDS_default "\b. This is the"
87 " format when \b--fields\b is not specified.]"
88 "[o:fields?(\b--format\b is more general.) List information according to"
89 " \akey\a. Multiple \b--fields\b options may be specified; the"
90 " resulting format is a left-right ordered list with duplicate entries"
91 " deleted from the right. The default width can be overriden by"
92 " appending \a+width\a to \akey\a, and the default \alabel\a can be"
93 " overridden by appending \a=label\a to \akey\a. The keys, labels and"
94 " widths are listed under \b--format\b.]:[key[+width]][=label]]...]"
95 "[p:pids?List processes in the \apid\a list.]:[pid...]"
96 "[P:parents?Display the process tree hierarchy, including the parents"
97 " of all selected processes, in the \bCMD\b field list.]"
98 "[r|R:recursive?Recursively list the children of all selected processes.]"
99 "[s:sessions?List processes with session leaders in the \asid\a list.]:[sid...]"
100 "[t:terminals|ttys?List processes with controlling terminals in the \atty\a"
101 " list.]:[tty...]"
102 "[T:tree|forest?Display the process tree hierarchy, including the parents and"
103 " children of all selected processes, in the \bCMD\b field list.]"
104 "[u|U:users?List processes with real user id names or numbers in the \auser\a"
105 " list.]:[user...]"
106 "[v:verbose?List verbose error messages for inaccessible processes.]"
107 "[w:wide?Ignored by this implementation.]"
108 "[x:detached?List all processes not associated with terminals.]"
109 "[X:hex?List numeric entries in hexadecimal notation.]"
110
111 "\n"
112 "\n[ pid ... ]\n"
113 "\n"
114
115 "[+SEE ALSO?\bdf\b(1), \bkill\b(1), \bls\b(1), \bnice\b(1), \bpax\b(1),"
116 " \bps\b(1), \bsh\b(1), \btop\b(1)]"
117 ;
118
119 #include <ast.h>
120 #include <ast_dir.h>
121 #include <cdt.h>
122 #include <ctype.h>
123 #include <dirent.h>
124 #include <error.h>
125 #include <ls.h>
126 #include <pss.h>
127 #include <sfdisc.h>
128 #include <tm.h>
129
130 #if !_mem_st_rdev_stat
131 #define st_rdev st_dev
132 #endif
133
134 #define KEY_environ (-1)
135 #define KEY_alias 0
136
137 #define KEY_addr 1
138 #define KEY_class 2
139 #define KEY_cmd 3
140 #define KEY_comm 4
141 #define KEY_cpu 5
142 #define KEY_etime 6
143 #define KEY_flags 7
144 #define KEY_gid 8
145 #define KEY_group 9
146 #define KEY_job 10
147 #define KEY_nice 11
148 #define KEY_npid 12
149 #define KEY_pgrp 13
150 #define KEY_pid 14
151 #define KEY_ppid 15
152 #define KEY_pri 16
153 #define KEY_proc 17
154 #define KEY_refcount 18
155 #define KEY_rss 19
156 #define KEY_sid 20
157 #define KEY_size 21
158 #define KEY_start 22
159 #define KEY_state 23
160 #define KEY_tgrp 24
161 #define KEY_time 25
162 #define KEY_tty 26
163 #define KEY_uid 27
164 #define KEY_user 28
165 #define KEY_wchan 29
166
167 typedef struct Key_s /* format key */
168 {
169 char* name; /* key name */
170 char* head; /* heading name */
171 char* desc; /* description */
172 unsigned long field; /* pss field */
173 short index; /* index */
174 short width; /* field width */
175 unsigned long maxval; /* max value if !=0 */
176 unsigned char hex; /* optional hex output */
177 unsigned char already; /* already specified */
178 short cancel; /* cancel this if specified */
179 short prec; /* field precision */
180 short disable; /* macro being expanded */
181 unsigned char skip; /* skip this */
182 char* macro; /* macro value */
183 const char* sep; /* next field separator */
184 Dtlink_t hashed; /* hash link */
185 struct Key_s* next; /* format link */
186 } Key_t;
187
188 typedef struct List_s /* pid list */
189 {
190 struct List_s* next; /* next in list */
191 char** argv; /* , separated string vector */
192 int argc; /* elementsof(argv) */
193 } List_t;
194
195 typedef struct Ps_s /* process state */
196 {
197 Dtlink_t hashed; /* pid hash link */
198 Dtlink_t sorted; /* sorted link */
199 Pssent_t* ps; /* ps info */
200 struct Ps_s* children; /* child list */
201 struct Ps_s* lastchild; /* end of children */
202 struct Ps_s* sibling; /* sibling list */
203 struct Ps_s* root; /* (partial) root list */
204 char* user; /* user name */
205 Pss_id_t pid; /* pid */
206 int level; /* process tree level */
207 int seen; /* already seen on chain */
208 int shown; /* list state */
209 } Ps_t;
210
211 typedef struct State_s /* program state */
212 {
213 int children; /* recursively list all children*/
214 int escape; /* escape { command args } */
215 int heading; /* output heading */
216 int parents; /* recursively list all parents */
217 int tree; /* list proc tree */
218 int hex; /* output optional hex key form */
219 int width; /* output width */
220 unsigned long now; /* current time */
221 Key_t* fields; /* format field list */
222 List_t* pids; /* pid vectors */
223 char* format; /* sfkeyprintf() format */
224 Key_t* lastfield; /* end of format list */
225 Dt_t* keys; /* format keys */
226 Dt_t* bypid; /* procs by pid */
227 Dt_t* byorder; /* procs by pid */
228 Ps_t* pp; /* next proc info slot */
229 Pss_t* pss; /* ps stream */
230 Pssdisc_t pssdisc; /* ps stream discipline */
231 Sfio_t* mac; /* temporary string stream */
232 Sfio_t* tmp; /* temporary string stream */
233 Sfio_t* wrk; /* temporary string stream */
234 char branch[1024]; /* process tree branch */
235 char buf[1024]; /* work buffer */
236 } State_t;
237
238 static Key_t keys[] =
239 {
240 {
241 0
242 },
243 {
244 "addr",
245 "ADDR",
246 "Physical address.",
247 PSS_addr,
248 KEY_addr,
249 8
250 },
251 {
252 "class",
253 "CLS",
254 "Scheduling class.",
255 PSS_sched,
256 KEY_class,
257 3,
258 },
259 {
260 "cmd",
261 "CMD",
262 "Command path with arguments.",
263 PSS_args,
264 KEY_cmd,
265 -32, 0,
266 0,0,0,
267 KEY_comm
268 },
269 {
270 "comm",
271 "COMMAND",
272 "Command file base name.",
273 PSS_command,
274 KEY_comm,
275 -24, 0,
276 0,0,0,
277 KEY_cmd
278 },
279 {
280 "cpu",
281 "%CPU",
282 "Cpu percent usage.",
283 PSS_cpu,
284 KEY_cpu,
285 4
286 },
287 {
288 "etime",
289 "ELAPSED",
290 "Elapsed time since start.",
291 0,
292 KEY_etime,
293 7
294 },
295 {
296 "flags",
297 "F",
298 "State flags (octal and additive).",
299 PSS_flags,
300 KEY_flags,
301 3
302 },
303 {
304 "gid",
305 "GROUP",
306 "Numeric group id.",
307 PSS_gid,
308 KEY_gid,
309 8, 0,
310 0,0,0,
311 KEY_group
312 },
313 {
314 "group",
315 "GROUP",
316 "Group id name.",
317 PSS_gid,
318 KEY_group,
319 8, 0,
320 0,0,0,
321 KEY_gid
322 },
323 {
324 "job",
325 "JOB",
326 "Job id.",
327 PSS_job,
328 KEY_job,
329 5, PID_MAX,
330 1,0,0
331 },
332 {
333 "nice",
334 "NI",
335 "Adjusted scheduling priority.",
336 PSS_nice,
337 KEY_nice,
338 4
339 },
340 {
341 "npid",
342 "NPID",
343 "Native process id.",
344 PSS_npid,
345 KEY_npid,
346 5, 0,
347 1,
348 },
349 {
350 "pgrp",
351 "PGRP",
352 "Process group id.",
353 PSS_pgrp,
354 KEY_pgrp,
355 5, PID_MAX,
356 1,0,0
357 },
358 {
359 "pid",
360 "PID",
361 "Process id.",
362 PSS_pid,
363 KEY_pid,
364 5, PID_MAX,
365 1,0,0
366 },
367 {
368 "ppid",
369 "PPID",
370 "Parent process id.",
371 PSS_ppid,
372 KEY_ppid,
373 5, PID_MAX,
374 1
375 },
376 {
377 "pri",
378 "PRI",
379 "Scheduling priority.",
380 PSS_pri,
381 KEY_pri,
382 3
383 },
384 {
385 "processor",
386 "PROC",
387 "Assigned processor.",
388 PSS_proc,
389 KEY_proc,
390 3
391 },
392 {
393 "refcount",
394 "REFS",
395 "Reference count.",
396 PSS_refcount,
397 KEY_refcount,
398 4, 0,
399 1,
400 },
401 {
402 "rss",
403 "RSS",
404 "Resident page set size in kilobytes.",
405 PSS_rss,
406 KEY_rss,
407 5
408 },
409 {
410 "sid",
411 "SID",
412 "Session id.",
413 PSS_sid,
414 KEY_sid,
415 5, PID_MAX,
416 1
417 },
418 {
419 "size",
420 "SIZE",
421 "Virtual memory size in kilobytes.",
422 PSS_size,
423 KEY_size,
424 6
425 },
426 {
427 "start",
428 "START",
429 "Start time.",
430 PSS_start,
431 KEY_start,
432 8
433 },
434 {
435 "state",
436 "S",
437 "Basic state.",
438 PSS_state,
439 KEY_state,
440 1
441 },
442 {
443 "tgrp",
444 "TGRP",
445 "Terminal group id.",
446 PSS_tgrp,
447 KEY_tgrp,
448 5, PID_MAX,
449 1,
450 },
451 {
452 "time",
453 "TIME",
454 "usr+sys time.",
455 PSS_time,
456 KEY_time,
457 6
458 },
459 {
460 "tty",
461 "TT",
462 "Controlling terminal base name.",
463 PSS_tty,
464 KEY_tty,
465 -7,
466 },
467 {
468 "uid",
469 "USER",
470 "Numeric user id.",
471 PSS_uid,
472 KEY_uid,
473 8, 0,
474 0,0,0,
475 KEY_user
476 },
477 {
478 "user",
479 "USER",
480 "User id name.",
481 PSS_uid,
482 KEY_user,
483 8, 0,
484 0,0,0,
485 KEY_uid
486 },
487 {
488 "wchan",
489 "WCHAN",
490 "Wait address.",
491 PSS_wchan,
492 KEY_wchan,
493 8
494 },
495
496 /* aliases after this point */
497
498 { "args", 0, 0, 0, KEY_cmd },
499 { "command", 0, 0, 0, KEY_cmd },
500 { "f", 0, 0, 0, KEY_flags },
501 { "jid", 0, 0, 0, KEY_job },
502 { "ntpid", 0, 0, 0, KEY_npid },
503 { "pcpu", 0, 0, 0, KEY_cpu },
504 { "pgid", 0, 0, 0, KEY_pgrp },
505 { "proc", 0, 0, 0, KEY_proc },
506 { "psr", 0, 0, 0, KEY_proc },
507 { "rgroup", 0, 0, 0, KEY_group },
508 { "ruser", 0, 0, 0, KEY_user },
509 { "s", 0, 0, 0, KEY_state },
510 { "sess", 0, 0, 0, KEY_sid },
511 { "stime", 0, 0, 0, KEY_start },
512 { "tid", 0, 0, 0, KEY_tgrp },
513 { "vsz", 0, 0, 0, KEY_size },
514
515 };
516
517 static const char fields_default[] = FIELDS_default;
518 static const char newline[] = "\n";
519 static const char space[] = " ";
520
521 static State_t state;
522
523 /*
524 * optget() info discipline function
525 */
526
527 static int
optinfo(Opt_t * op,Sfio_t * sp,const char * s,Optdisc_t * dp)528 optinfo(Opt_t* op, Sfio_t* sp, const char* s, Optdisc_t* dp)
529 {
530 register int i;
531
532 if (streq(s, "formats"))
533 for (i = 1; i < elementsof(keys); i++)
534 {
535 sfprintf(sp, "[+%s?", keys[i].name);
536 if (keys[i].head)
537 sfprintf(sp, "%s The title string is \b%s\b and the default width is %d.%s]", keys[i].desc, keys[i].head, keys[i].width, (!keys[i].field || (state.pss->meth->fields & keys[i].field)) ? "" : " Not available on this system.");
538 else
539 sfprintf(sp, "Equivalent to \b%s\b.]", keys[keys[i].index].name);
540 }
541 return 0;
542 }
543
544 /*
545 * return device id given tty base name
546 */
547
548 static int
ttyid(const char * name)549 ttyid(const char* name)
550 {
551 return pssttydev(state.pss, name);
552 }
553
554 /*
555 * sfkeyprintf() lookup
556 * handle==0 for heading
557 */
558
559 static int
key(void * handle,register Sffmt_t * fp,const char * arg,char ** ps,Sflong_t * pn)560 key(void* handle, register Sffmt_t* fp, const char* arg, char** ps, Sflong_t* pn)
561 {
562 register Ps_t* pp = (Ps_t*)handle;
563 register char* s = 0;
564 register Sflong_t n = 0;
565 register Key_t* kp;
566 register int i;
567 int j;
568 unsigned long u;
569
570 static char sbuf[2];
571
572 if (!fp->t_str)
573 return 0;
574 if (!(kp = (Key_t*)dtmatch(state.keys, fp->t_str)))
575 {
576 if (*fp->t_str != '$')
577 {
578 error(3, "%s: unknown format key", fp->t_str);
579 return 0;
580 }
581 if (!(kp = newof(0, Key_t, 1, strlen(fp->t_str) + 1)))
582 error(ERROR_SYSTEM|3, "out of space");
583 kp->name = strcpy((char*)(kp + 1), fp->t_str);
584 kp->macro = getenv(fp->t_str + 1);
585 kp->index = KEY_environ;
586 kp->disable = 1;
587 dtinsert(state.keys, kp);
588 }
589 if (!kp->head && (state.pss->meth->fields & keys[kp->index].field))
590 kp = &keys[kp->index];
591 if (kp->macro && !kp->disable)
592 {
593 kp->disable = 1;
594 sfkeyprintf(state.mac, handle, kp->macro, key, NiL);
595 if (!(s = sfstruse(state.mac)))
596 error(ERROR_SYSTEM|3, "out of space");
597 kp->disable = 0;
598 }
599 else if (!pp)
600 {
601 if (!(state.pss->meth->fields & kp->field))
602 {
603 error(1, "%s: not available on this system", kp->name);
604 return -1;
605 }
606 state.pssdisc.fields |= kp->field;
607 if (fp->flags & SFFMT_ALTER)
608 {
609 if (kp->maxval)
610 {
611 for (i = 1; kp->maxval /= 10; i++);
612 if (kp->width < 0)
613 {
614 i = -i;
615 if (kp->width > i)
616 kp->width = i;
617 }
618 else if (kp->width < i)
619 kp->width = i;
620 }
621 if ((fp->width = kp->width) < 0)
622 {
623 fp->width = -fp->width;
624 fp->flags |= SFFMT_LEFT;
625 }
626 fp->precis = fp->width;
627 }
628 kp->width = fp->width;
629 if (fp->flags & SFFMT_LEFT)
630 kp->width = -kp->width;
631 fp->fmt = 's';
632 *ps = arg && (arg = (const char*)strdup(arg)) ? (char*)arg : kp->head;
633 }
634 else
635 {
636 if ((fp->flags & SFFMT_ALTER) && (fp->width = kp->width) < 0)
637 {
638 fp->width = -fp->width;
639 fp->flags |= SFFMT_LEFT;
640 }
641 switch (kp->index)
642 {
643 case KEY_addr:
644 if (pp->ps->state == PSS_ZOMBIE)
645 goto zombie;
646 n = (long)pp->ps->addr;
647 goto number;
648 case KEY_class:
649 if (pp->ps->state == PSS_ZOMBIE)
650 goto zombie;
651 s = pp->ps->sched;
652 break;
653 case KEY_cmd:
654 s = pp->ps->args;
655 goto branch;
656 case KEY_comm:
657 s = pp->ps->command;
658 branch:
659 if (!s)
660 s = "<defunct>";
661 if ((j = pp->level) > 0)
662 {
663 for (i = 0, j--; i < j; i++)
664 sfputr(state.wrk, state.branch[i] ? " | " : " ", -1);
665 sfputr(state.wrk, " \\_ ", -1);
666 i = sfstrtell(state.wrk);
667 sfputr(state.wrk, s, -1);
668 if (!(s = sfstruse(state.wrk)))
669 error(ERROR_SYSTEM|3, "out of space");
670 if (state.escape)
671 fmtesc(s + i);
672 }
673 break;
674 case KEY_cpu:
675 if (pp->ps->state == PSS_ZOMBIE)
676 goto zombie;
677 n = pp->ps->cpu;
678 goto percent;
679 case KEY_etime:
680 if (fp->fmt == 's')
681 s = fmtelapsed(state.now - (unsigned long)pp->ps->start, 1);
682 else
683 n = pp->ps->start;
684 break;
685 case KEY_flags:
686 n = pp->ps->flags & PSS_FLAGS;
687 goto number;
688 case KEY_group:
689 if (fp->fmt == 's')
690 {
691 s = fmtgid(pp->ps->gid);
692 break;
693 }
694 /*FALLTHROUGH*/
695 case KEY_gid:
696 n = pp->ps->gid;
697 goto number;
698 case KEY_nice:
699 if (pp->ps->state == PSS_ZOMBIE)
700 goto zombie;
701 n = pp->ps->nice;
702 goto number;
703 case KEY_npid:
704 n = pp->ps->npid;
705 goto number;
706 case KEY_pgrp:
707 n = pp->ps->pgrp;
708 goto number;
709 case KEY_pid:
710 n = pp->ps->pid;
711 goto number;
712 case KEY_ppid:
713 n = pp->ps->ppid;
714 break;
715 case KEY_pri:
716 if (pp->ps->state == PSS_ZOMBIE)
717 goto zombie;
718 n = pp->ps->pri;
719 goto number;
720 case KEY_refcount:
721 n = pp->ps->refcount;
722 goto number;
723 case KEY_rss:
724 if (pp->ps->state == PSS_ZOMBIE)
725 goto zombie;
726 n = pp->ps->rss;
727 goto number;
728 case KEY_sid:
729 n = pp->ps->sid;
730 goto number;
731 case KEY_size:
732 if (pp->ps->state == PSS_ZOMBIE)
733 goto zombie;
734 n = pp->ps->size;
735 goto number;
736 case KEY_start:
737 if (pp->ps->state == PSS_ZOMBIE)
738 goto zombie;
739 if (fp->fmt == 's')
740 {
741 u = pp->ps->start;
742 s = fmttime((state.now - u) >= (24 * 60 * 60) ? "%y-%m-%d" : "%H:%M:%S", u);
743 }
744 else
745 n = pp->ps->start;
746 break;
747 case KEY_state:
748 *(s = sbuf) = pp->ps->state;
749 *(s + 1) = 0;
750 break;
751 case KEY_tgrp:
752 n = pp->ps->tgrp;
753 goto number;
754 case KEY_time:
755 if (fp->fmt == 's')
756 s = fmtelapsed(pp->ps->time, 1);
757 else
758 n = pp->ps->time;
759 break;
760 case KEY_tty:
761 if (pp->ps->state == PSS_ZOMBIE)
762 goto zombie;
763 s = pssttyname(state.pss, pp->ps);
764 if (kp->prec && (i = strlen(s) - kp->prec) > 0)
765 {
766 if (s[0] == 'p' && s[1] == 't')
767 {
768 if (s[2] == 'y')
769 s += 3;
770 else
771 s += 2;
772 }
773 else if (s[0] == 't' && s[1] == 't' && s[2] == 'y')
774 s += 3;
775 else
776 s += i;
777 }
778 break;
779 case KEY_user:
780 if (fp->fmt == 's')
781 {
782 s = pp->user;
783 break;
784 }
785 /*FALLTHROUGH*/
786 case KEY_uid:
787 n = pp->ps->uid;
788 goto number;
789 case KEY_wchan:
790 if (pp->ps->state == PSS_ZOMBIE)
791 goto zombie;
792 n = (long)pp->ps->wchan;
793 goto number;
794 default:
795 return 0;
796 zombie:
797 s = "";
798 break;
799 percent:
800 sfprintf(state.tmp, "%%%I*d", sizeof(n), n);
801 if (!(s = sfstruse(state.tmp)))
802 error(ERROR_SYSTEM|3, "out of space");
803 break;
804 number:
805 if (state.hex)
806 {
807 fp->fmt = 'x';
808 if (!kp->hex)
809 fp->flags |= SFFMT_ZERO;
810 }
811 break;
812 }
813 if (s)
814 *ps = s;
815 else
816 *pn = n;
817 }
818 return 1;
819 }
820
821 /*
822 * ps a single proc
823 */
824
825 static void
ps(Ps_t * pp)826 ps(Ps_t* pp)
827 {
828 register Key_t* kp;
829 register Pssent_t* pr;
830 register char* s;
831 register int i;
832 register long n;
833 unsigned long u;
834 int j;
835 char sbuf[2];
836
837 pp->shown = 1;
838 if (state.format)
839 {
840 sfkeyprintf(sfstdout, pp, state.format, key, NiL);
841 return;
842 }
843 pr = pp->ps;
844 for (kp = state.fields; kp; kp = kp->next)
845 {
846 switch (kp->index)
847 {
848 case KEY_addr:
849 if (pr->state == PSS_ZOMBIE)
850 goto zombie;
851 n = (long)pr->addr;
852 goto hex;
853 case KEY_class:
854 if (pr->state == PSS_ZOMBIE)
855 goto zombie;
856 s = pr->sched;
857 goto string;
858 case KEY_cmd:
859 s = pr->args;
860 goto branch;
861 case KEY_comm:
862 s = pr->command;
863 branch:
864 if (!s)
865 s = "<defunct>";
866 if ((j = pp->level) > 0)
867 {
868 for (i = 0, j--; i < j; i++)
869 sfputr(sfstdout, state.branch[i] ? " | " : " ", -1);
870 sfputr(sfstdout, " \\_ ", -1);
871 }
872 if (state.escape)
873 s = fmtesc(s);
874 goto string;
875 case KEY_cpu:
876 if (pr->state == PSS_ZOMBIE)
877 goto zombie;
878 n = pr->cpu;
879 goto percent;
880 case KEY_etime:
881 s = fmtelapsed(state.now - (unsigned long)pr->start, 1);
882 goto string;
883 case KEY_flags:
884 n = pr->flags & PSS_FLAGS;
885 goto octal;
886 case KEY_gid:
887 n = pr->gid;
888 goto number;
889 case KEY_group:
890 s = fmtgid(pr->gid);
891 goto string;
892 case KEY_nice:
893 if (pr->state == PSS_ZOMBIE)
894 goto zombie;
895 n = pr->nice;
896 goto number;
897 case KEY_npid:
898 n = pr->npid;
899 goto number;
900 case KEY_pgrp:
901 n = pr->pgrp;
902 goto number;
903 case KEY_pid:
904 n = pr->pid;
905 goto number;
906 case KEY_ppid:
907 n = pr->ppid;
908 goto number;
909 case KEY_pri:
910 if (pr->state == PSS_ZOMBIE)
911 goto zombie;
912 n = pr->pri;
913 goto number;
914 case KEY_refcount:
915 n = pr->refcount;
916 goto number;
917 case KEY_rss:
918 if (pr->state == PSS_ZOMBIE)
919 goto zombie;
920 n = pr->rss;
921 goto number;
922 case KEY_sid:
923 n = pr->sid;
924 goto number;
925 case KEY_size:
926 if (pr->state == PSS_ZOMBIE)
927 goto zombie;
928 n = pr->size;
929 goto number;
930 case KEY_start:
931 if (pr->state == PSS_ZOMBIE)
932 goto zombie;
933 u = pr->start;
934 s = fmttime((state.now - u) >= (24 * 60 * 60) ? "%y-%m-%d" : "%H:%M:%S", u);
935 goto string;
936 case KEY_state:
937 *(s = sbuf) = pr->state;
938 *(s + 1) = 0;
939 goto string;
940 case KEY_tgrp:
941 n = pr->tgrp;
942 goto number;
943 case KEY_time:
944 s = fmtelapsed(pr->time, 1);
945 goto string;
946 case KEY_tty:
947 if (pr->state == PSS_ZOMBIE)
948 goto zombie;
949 s = pssttyname(state.pss, pr);
950 if (kp->prec && (i = strlen(s) - kp->prec) > 0)
951 {
952 if (s[0] == 'p' && s[1] == 't')
953 {
954 if (s[2] == 'y')
955 s += 3;
956 else
957 s += 2;
958 }
959 else if (s[0] == 't' && s[1] == 't' && s[2] == 'y')
960 s += 3;
961 else
962 s += i;
963 }
964 goto string;
965 case KEY_uid:
966 n = pr->uid;
967 goto number;
968 case KEY_user:
969 s = pp->user;
970 goto string;
971 case KEY_wchan:
972 if (pr->state == PSS_ZOMBIE)
973 goto zombie;
974 n = (long)pr->wchan;
975 goto hex;
976 }
977 s = "????";
978 string:
979 if (kp->width == kp->prec)
980 sfprintf(sfstdout, "%0*s%s", kp->width, s, kp->sep);
981 else
982 sfprintf(sfstdout, "%*.*s%s", kp->width, kp->prec, s, kp->sep);
983 continue;
984 zombie:
985 s = "-";
986 goto string;
987 percent:
988 sfprintf(state.tmp, "%%%ld", n);
989 if (!(s = sfstruse(state.tmp)))
990 error(ERROR_SYSTEM|3, "out of space");
991 goto string;
992 number:
993 if (!state.hex || !kp->hex)
994 {
995 sfprintf(sfstdout, "%*ld%s", kp->width, n, kp->sep);
996 continue;
997 }
998 hex:
999 if (kp->hex)
1000 sfprintf(sfstdout, "%*lx%s", kp->width, n, kp->sep);
1001 else
1002 sfprintf(sfstdout, "%0*lx%s", kp->width, n, kp->sep);
1003 continue;
1004 octal:
1005 sfprintf(sfstdout, "%*lo%s", kp->width, n, kp->sep);
1006 continue;
1007 }
1008 }
1009
1010 /*
1011 * ps() a process and its children
1012 */
1013
1014 static void
kids(register Ps_t * pp,int level)1015 kids(register Ps_t* pp, int level)
1016 {
1017 if (state.tree)
1018 {
1019 pp->level = level;
1020 ps(pp);
1021 if (level > 0)
1022 state.branch[level - 1] = pp->sibling != 0;
1023 if (level < elementsof(state.branch) - 1)
1024 level++;
1025 }
1026 else
1027 ps(pp);
1028 for (pp = pp->children; pp; pp = pp->sibling)
1029 kids(pp, level);
1030 }
1031
1032 /*
1033 * ps() the selected procs
1034 */
1035
1036 static void
list(void)1037 list(void)
1038 {
1039 register Ps_t* pp;
1040 register Ps_t* xp;
1041 register Ps_t* zp;
1042 Ps_t* rp;
1043
1044 if (state.children || state.parents)
1045 {
1046 /*
1047 * list the child/parent branches of selected processes
1048 */
1049
1050 if (state.parents)
1051 for (pp = (Ps_t*)dtfirst(state.byorder); pp; pp = (Ps_t*)dtnext(state.byorder, pp))
1052 if (pp->ps->pss & (PSS_EXPLICIT|PSS_MATCHED))
1053 {
1054 xp = pp;
1055 do
1056 {
1057 xp->ps->pss |= PSS_PARENT;
1058 } while ((xp = (Ps_t*)dtmatch(state.bypid, &xp->ps->ppid)) && !xp->ps->pss);
1059 }
1060 if (state.children)
1061 for (pp = (Ps_t*)dtfirst(state.byorder); pp; pp = (Ps_t*)dtnext(state.byorder, pp))
1062 if (!(pp->ps->pss & (PSS_EXPLICIT|PSS_MATCHED|PSS_PARENT)) && !pp->seen)
1063 {
1064 xp = pp;
1065 do
1066 {
1067 xp->seen = 1;
1068 if (xp->ps->pss & (PSS_ANCESTOR|PSS_EXPLICIT|PSS_MATCHED))
1069 {
1070 xp->ps->pss |= PSS_ANCESTOR;
1071 xp = pp;
1072 do
1073 {
1074 xp->ps->pss |= PSS_PARENT;
1075 } while ((xp = (Ps_t*)dtmatch(state.bypid, &xp->ps->ppid)) && !xp->ps->pss);
1076 break;
1077 }
1078 } while (xp->ps->ppid != xp->ps->pid && (xp = (Ps_t*)dtmatch(state.bypid, &xp->ps->ppid)));
1079 }
1080 rp = zp = 0;
1081 for (pp = (Ps_t*)dtfirst(state.byorder); pp; pp = (Ps_t*)dtnext(state.byorder, pp))
1082 if (pp->ps->pss)
1083 {
1084 if (pp->ps->ppid != pp->ps->pid && (xp = (Ps_t*)dtmatch(state.bypid, &pp->ps->ppid)) && (xp->ps->pss & (PSS_EXPLICIT|PSS_MATCHED|PSS_PARENT)))
1085 {
1086 xp->ps->pss |= PSS_CHILD;
1087 if (xp->lastchild)
1088 xp->lastchild = xp->lastchild->sibling = pp;
1089 else
1090 xp->children = xp->lastchild = pp;
1091 }
1092 else if (zp)
1093 zp = zp->root = pp;
1094 else
1095 rp = zp = pp;
1096 }
1097 for (pp = rp; pp; pp = pp->root)
1098 kids(pp, 0);
1099 }
1100 else
1101 {
1102 /*
1103 * list by order
1104 */
1105
1106 for (pp = (Ps_t*)dtfirst(state.byorder); pp; pp = (Ps_t*)dtnext(state.byorder, pp))
1107 if (!pp->shown)
1108 {
1109 pp->shown = 1;
1110 pp->level = 0;
1111 ps(pp);
1112 }
1113 }
1114 }
1115
1116 /*
1117 * finalize the field formats and optionally list the heading
1118 */
1119
1120 static void
head(void)1121 head(void)
1122 {
1123 register int n;
1124 register Key_t* kp;
1125
1126 if (state.fields)
1127 {
1128 while (state.fields->skip)
1129 state.fields = state.fields->next;
1130 kp = state.fields;
1131 while (kp->next)
1132 {
1133 if (!kp->next->skip)
1134 kp = kp->next;
1135 else if (!(kp->next = kp->next->next))
1136 {
1137 state.lastfield = kp;
1138 break;
1139 }
1140 }
1141 n = 0;
1142 for (kp = state.fields; kp; kp = kp->next)
1143 {
1144 if ((kp->prec = kp->width) < 0)
1145 kp->prec = -kp->prec;
1146 if (*kp->head)
1147 n = 1;
1148 }
1149 kp = state.lastfield;
1150 if (kp->width < 0)
1151 kp->width = 0;
1152 if (kp->index == KEY_cmd)
1153 kp->prec = 80;
1154 if (n && state.heading)
1155 {
1156 for (kp = state.fields; kp; kp = kp->next)
1157 sfprintf(sfstdout, "%*s%s", kp->width, kp->head, kp->sep);
1158 sfputc(sfstdout, '\n');
1159 }
1160 }
1161 else
1162 {
1163 sfkeyprintf(state.heading ? sfstdout : state.wrk, NiL, state.format, key, NiL);
1164 sfstrseek(state.wrk, 0, SEEK_SET);
1165 }
1166 }
1167
1168 /*
1169 * order procs by <uid,start,pid>
1170 */
1171
1172 static int
byorder(Dt_t * dt,void * a,void * b,Dtdisc_t * disc)1173 byorder(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
1174 {
1175 register Ps_t* pa = (Ps_t*)a;
1176 register Ps_t* pb = (Ps_t*)b;
1177 register int i;
1178
1179 NoP(dt);
1180 NoP(disc);
1181 if (i = strcmp(pa->user, pb->user))
1182 return i;
1183 if (pa->ps->pgrp < pb->ps->pgrp)
1184 return -1;
1185 if (pa->ps->pgrp > pb->ps->pgrp)
1186 return 1;
1187 if (i = (pa->ps->pgrp == pa->ps->pid) - (pb->ps->pgrp == pb->ps->pid))
1188 return i;
1189 if (pa->ps->start < pb->ps->start)
1190 return -1;
1191 if (pa->ps->start > pb->ps->start)
1192 return 1;
1193 if (pa->ps->pid < pb->ps->pid)
1194 return -1;
1195 if (pa->ps->pid > pb->ps->pid)
1196 return 1;
1197 return 0;
1198 }
1199
1200 /*
1201 * add the procs in the pid list
1202 */
1203
1204 static void
addpid(Pssent_t * pe,register char * s)1205 addpid(Pssent_t* pe, register char* s)
1206 {
1207 register char* t;
1208 register Ps_t* pp;
1209 register int c;
1210 char* e;
1211 Pss_id_t pid;
1212 long n;
1213
1214 do
1215 {
1216 if (s)
1217 {
1218 for (; isspace(*s) || *s == ','; s++);
1219 for (t = s; *s && !isspace(*s) && *s != ','; s++);
1220 c = *s;
1221 *s = 0;
1222 if (!*t)
1223 break;
1224 errno = 0;
1225 n = strtol(t, &e, 0);
1226 if (errno || n <= 0 || n > PID_MAX || *e)
1227 {
1228 error(2, "%s: invalid pid", t);
1229 continue;
1230 }
1231 pid = n;
1232 pe = pssread(state.pss, pid);
1233 }
1234 if (pe)
1235 {
1236 if (!(pp = state.pp) && !(state.pp = pp = newof(0, Ps_t, 1, 0)))
1237 error(ERROR_SYSTEM|3, "out of space");
1238 pp->user = fmtuid(pe->uid);
1239 pp->pid = pe->pid;
1240 pp->ps = pe;
1241 if (!dtsearch(state.byorder, pp))
1242 {
1243 if (!(pp->ps = psssave(state.pss, pe)))
1244 break;
1245 dtinsert(state.byorder, pp);
1246 state.pp = 0;
1247 }
1248 }
1249 } while (s && (*s++ = c));
1250 }
1251
1252 /*
1253 * add the ids in s into state.pssdisc.match
1254 * getid!=0 translates alnum to id number
1255 */
1256
1257 static void
addid(register char * s,int index,int (* getid)(const char *))1258 addid(register char* s, int index, int (*getid)(const char*))
1259 {
1260 register char* t;
1261 register int c;
1262 char* e;
1263 long n;
1264 unsigned long field;
1265 Pssmatch_t* mp;
1266 Pssdata_t* dp;
1267
1268 if (!((field = keys[index].field) & PSS_match))
1269 error(3, "%s: cannot match on this field", keys[index].name);
1270 for (mp = state.pssdisc.match; mp && mp->field != field; mp = mp->next);
1271 if (!mp)
1272 {
1273 if (!(mp = newof(0, Pssmatch_t, 1, 0)))
1274 error(ERROR_SYSTEM|3, "out of space");
1275 mp->next = state.pssdisc.match;
1276 mp->field = field;
1277 state.pssdisc.match = mp;
1278 }
1279 do
1280 {
1281 for (; isspace(*s) || *s == ','; s++);
1282 for (t = s; *s && !isspace(*s) && *s != ','; s++);
1283 if ((c = s - t) >= sizeof(state.buf))
1284 c = sizeof(state.buf) - 1;
1285 memcpy(state.buf, t, c);
1286 (t = state.buf)[c] = 0;
1287 if (!*t)
1288 break;
1289 if (isdigit(*t))
1290 {
1291 n = strtol(t, &e, 10);
1292 if (*e)
1293 {
1294 error(1, "%s: invalid %s", t, keys[index].name);
1295 continue;
1296 }
1297 }
1298 else if (!getid || (n = (*getid)(t)) < 0)
1299 {
1300 error(1, "%s: invalid %s", t, keys[index].name);
1301 continue;
1302 }
1303 if (!(dp = newof(0, Pssdata_t, 1, 0)))
1304 error(ERROR_SYSTEM|3, "out of space");
1305 dp->next = mp->data;
1306 mp->data = dp;
1307 dp->data = n;
1308 } while (*s++);
1309 }
1310
1311 /*
1312 * add the format key in s into state.fields
1313 */
1314
1315 static void
addkey(const char * k,int ignore)1316 addkey(const char* k, int ignore)
1317 {
1318 register char* s = (char*)k;
1319 register char* t;
1320 register int c;
1321 register Key_t* kp;
1322 register Key_t* ap;
1323 char* e;
1324 int w;
1325
1326 if (streq(s, "?"))
1327 {
1328 sfprintf(sfstdout, "%-8s %-8s %s\n", "KEY", "HEADING", "DESCRIPTION");
1329 for (kp = keys + 1; kp < keys + elementsof(keys); kp++)
1330 {
1331 ap = kp->head ? kp : (keys + kp->index);
1332 sfprintf(sfstdout, "%-8s %-8s %s%s%s\n", kp->name, ap->head, ap->desc, ap == kp ? "" : " [alias]", (!ap->field || (state.pss->meth->fields & ap->field)) ? "" : " [not available]");
1333 }
1334 exit(0);
1335 }
1336 do
1337 {
1338 for (; isspace(*s) || *s == ','; s++);
1339 for (t = s; *s && !isspace(*s) && *s != ',' && *s != '=' && *s != ':' && *s != '+'; s++);
1340 if ((c = s - t) >= sizeof(state.buf))
1341 c = sizeof(state.buf) - 1;
1342 memcpy(state.buf, t, c);
1343 (t = state.buf)[c] = 0;
1344 if (!*t)
1345 break;
1346 if (*s == ':' || *s == '+')
1347 {
1348 c = (int)strtol(s + 1, &e, 10);
1349 s = e;
1350 }
1351 else
1352 c = 0;
1353 if (!(kp = (Key_t*)dtmatch(state.keys, t)))
1354 {
1355 error(2, "%s: unknown format key", t);
1356 continue;
1357 }
1358
1359 /*
1360 * aliases have Key_t.head == 0
1361 */
1362
1363 if (!kp->head)
1364 kp = keys + kp->index;
1365
1366 /*
1367 * adjust the width field
1368 */
1369
1370 if (*s == '=')
1371 {
1372 for (t = ++s; *s && !isspace(*s) && *s != ','; s++);
1373 w = s - t;
1374 if (!(kp->head = newof(0, char, w, 1)))
1375 error(ERROR_SYSTEM|3, "out of space");
1376 memcpy(kp->head, t, w);
1377 if (w < c)
1378 w = c;
1379 if (w > kp->width)
1380 kp->width = w;
1381 }
1382 else if (c >= strlen(kp->head))
1383 kp->width = c;
1384 if (!(state.pss->meth->fields & kp->field))
1385 {
1386 if (!ignore)
1387 error(1, "%s: not available on this system", kp->name);
1388 continue;
1389 }
1390
1391 /*
1392 * except for width and head adjustments
1393 * we ignore keys already specified to let
1394 * shell aliases work with least suprise
1395 */
1396
1397 if (!kp->already)
1398 {
1399 kp->already = keys[kp->cancel].already = keys[kp->cancel].skip = 1;
1400 if (state.lastfield)
1401 state.lastfield = state.lastfield->next = kp;
1402 else
1403 state.fields = state.lastfield = kp;
1404 kp->sep = space;
1405 if (kp->maxval)
1406 {
1407 for (c = 1; kp->maxval /= 10; c++);
1408 if (c >= kp->width)
1409 kp->width = c;
1410 }
1411 state.pssdisc.fields |= kp->field;
1412 }
1413 } while (*s != '=' && *s++);
1414 }
1415
1416 /*
1417 * add pid vector for subsequent poppids()
1418 */
1419
1420 static void
pushpids(void * argv,int argc)1421 pushpids(void* argv, int argc)
1422 {
1423 register List_t* p;
1424
1425 if (!(p = newof(0, List_t, 1, 0)))
1426 error(ERROR_SYSTEM|3, "out of space");
1427 p->argv = (char**)argv;
1428 p->argc = argc;
1429 p->next = state.pids;
1430 state.pids = p;
1431 }
1432
1433 /*
1434 * pop state.pids by calling addpid()
1435 */
1436
1437 static void
poppids(void)1438 poppids(void)
1439 {
1440 register List_t* p;
1441 register int i;
1442 unsigned long flags;
1443
1444 flags = state.pssdisc.flags;
1445 state.pssdisc.flags |= PSS_VERBOSE;
1446 while (p = state.pids)
1447 {
1448 state.pids = p->next;
1449 if (i = p->argc)
1450 while (--i >= 0)
1451 addpid(NiL, p->argv[i]);
1452 else
1453 addpid(NiL, (char*)p->argv);
1454 free(p);
1455 }
1456 state.pssdisc.flags = flags;
1457 }
1458
1459 int
main(int argc,register char ** argv)1460 main(int argc, register char** argv)
1461 {
1462 register int n;
1463 register char* s;
1464 Sfio_t* fmt;
1465 Key_t* kp;
1466 Ps_t* pp;
1467 Pssent_t* pe;
1468 Dtdisc_t kd;
1469 Dtdisc_t nd;
1470 Dtdisc_t sd;
1471 Optdisc_t od;
1472 struct stat st;
1473
1474 NoP(argc);
1475 error_info.id = "ps";
1476 setlocale(LC_ALL, "");
1477 state.now = time((time_t*)0);
1478 state.escape = 1;
1479 state.heading = 1;
1480 if (!(fmt = sfstropen()) || !(state.tmp = sfstropen()) || !(state.wrk = sfstropen()))
1481 error(ERROR_SYSTEM|3, "out of space");
1482
1483 /*
1484 * set up the disciplines
1485 */
1486
1487 pssinit(&state.pssdisc, argv[0], errorf);
1488 optinit(&od, optinfo);
1489 memset(&kd, 0, sizeof(kd));
1490 kd.key = offsetof(Key_t, name);
1491 kd.size = -1;
1492 kd.link = offsetof(Key_t, hashed);
1493 memset(&nd, 0, sizeof(nd));
1494 nd.key = offsetof(Ps_t, pid);
1495 nd.size = sizeof(Pss_id_t);
1496 nd.link = offsetof(Ps_t, hashed);
1497 memset(&sd, 0, sizeof(sd));
1498 sd.link = offsetof(Ps_t, sorted);
1499 sd.comparf = byorder;
1500
1501 /*
1502 * open the ps stream
1503 */
1504
1505 if (!(state.pss = pssopen(&state.pssdisc)) || !state.pss->meth->fields)
1506 {
1507 s = "/bin/ps";
1508 if (!streq(argv[0], s) && !eaccess(s, X_OK))
1509 {
1510 argv[0] = s;
1511 error(1, "falling back to %s", s);
1512 execv(s, argv);
1513 exit(EXIT_NOTFOUND);
1514 }
1515 if (!state.pss)
1516 {
1517 state.pssdisc.flags |= PSS_VERBOSE;
1518 pssopen(&state.pssdisc);
1519 }
1520 error(3, "process status access error");
1521 }
1522
1523 /*
1524 * initialize the format key table
1525 */
1526
1527 if (!(state.keys = dtopen(&kd, Dtset)) || !(state.bypid = dtopen(&nd, Dtset)) || !(state.byorder = dtopen(&sd, Dtset)))
1528 error(ERROR_SYSTEM|3, "out of space");
1529 for (n = 1; n < elementsof(keys); n++)
1530 dtinsert(state.keys, keys + n);
1531
1532 /*
1533 * grab the options
1534 */
1535
1536 n = 0;
1537 for (;;)
1538 {
1539 switch (optget(argv, usage))
1540 {
1541 case 'a':
1542 state.pssdisc.flags |= PSS_ATTACHED|PSS_NOLEADER;
1543 continue;
1544 case 'c':
1545 addkey(FIELDS_c, 1);
1546 continue;
1547 case 'd':
1548 state.pssdisc.flags |= PSS_NOLEADER;
1549 continue;
1550 case 'e':
1551 case 'A':
1552 state.pssdisc.flags |= PSS_ALL;
1553 continue;
1554 case 'f':
1555 addkey(FIELDS_f, 1);
1556 continue;
1557 case 'h':
1558 state.heading = opt_info.num;
1559 continue;
1560 case 'g':
1561 addid(opt_info.arg, KEY_pgrp, NiL);
1562 continue;
1563 case 'j':
1564 addkey(FIELDS_j, 1);
1565 continue;
1566 case 'l':
1567 addkey(FIELDS_l, 1);
1568 continue;
1569 case 'n':
1570 continue;
1571 case 'o':
1572 addkey(opt_info.arg, 0);
1573 continue;
1574 case 'p':
1575 pushpids(opt_info.arg, 0);
1576 continue;
1577 case 'r':
1578 case 'R':
1579 state.children = opt_info.num;
1580 continue;
1581 case 's':
1582 addid(opt_info.arg, KEY_sid, NiL);
1583 continue;
1584 case 't':
1585 addid(opt_info.arg, KEY_tty, ttyid);
1586 continue;
1587 case 'u':
1588 addid(opt_info.arg, KEY_uid, struid);
1589 continue;
1590 case 'v':
1591 state.pssdisc.flags |= PSS_VERBOSE;
1592 continue;
1593 case 'w':
1594 /* ignored by this implementation */
1595 continue;
1596 case 'x':
1597 state.pssdisc.flags |= PSS_DETACHED;
1598 continue;
1599 case 'B':
1600 n = !opt_info.num;
1601 continue;
1602 case 'C':
1603 state.tree = state.children = opt_info.num;
1604 continue;
1605 case 'D':
1606 if (s = strchr(opt_info.arg, '='))
1607 *s++ = 0;
1608 if (*opt_info.arg == 'n' && *(opt_info.arg + 1) == 'o')
1609 {
1610 opt_info.arg += 2;
1611 s = 0;
1612 }
1613 if (!(kp = (Key_t*)dtmatch(state.keys, opt_info.arg)))
1614 {
1615 if (!s)
1616 continue;
1617 if (!(kp = newof(0, Key_t, 1, strlen(opt_info.arg) + 1)))
1618 error(ERROR_SYSTEM|3, "out of space");
1619 kp->name = strcpy((char*)(kp + 1), opt_info.arg);
1620 dtinsert(state.keys, kp);
1621 }
1622 if (kp->macro = s)
1623 stresc(s);
1624 continue;
1625 case 'E':
1626 state.escape = opt_info.num;
1627 continue;
1628 case 'F':
1629 if (sfstrtell(fmt))
1630 sfputc(fmt, ' ');
1631 sfputr(fmt, opt_info.arg, -1);
1632 continue;
1633 case 'G':
1634 addid(opt_info.arg, KEY_group, strgid);
1635 continue;
1636 case 'L':
1637 state.pssdisc.flags |= PSS_LEADER;
1638 continue;
1639 case 'N':
1640 addkey(fields_default, 1);
1641 continue;
1642 case 'P':
1643 state.tree = state.parents = opt_info.num;
1644 continue;
1645 case 'T':
1646 state.tree = state.parents = state.children = opt_info.num;
1647 continue;
1648 case 'X':
1649 state.hex = !state.hex;
1650 continue;
1651 case '?':
1652 error(ERROR_USAGE|4, "%s", opt_info.arg);
1653 break;
1654 case ':':
1655 error(2, "%s", opt_info.arg);
1656 break;
1657 }
1658 break;
1659 }
1660 argv += opt_info.index;
1661 argc -= opt_info.index;
1662 if (error_info.errors)
1663 error(ERROR_USAGE|4, "%s", optusage(NiL));
1664 if (n)
1665 state.tree = 0;
1666 if (sfstrtell(fmt))
1667 {
1668 sfputc(fmt, '\n');
1669 if (!(state.format = sfstruse(fmt)))
1670 error(ERROR_SYSTEM|3, "out of space");
1671 }
1672 else
1673 {
1674 sfclose(fmt);
1675 fmt = 0;
1676 if (!state.fields)
1677 addkey(fields_default, 1);
1678 }
1679 head();
1680
1681 /*
1682 * add each proc by name
1683 */
1684
1685 if (*argv)
1686 pushpids(argv, argc);
1687 if (state.children || state.parents || !state.pids)
1688 {
1689 if (state.children || state.parents)
1690 state.pssdisc.flags |= PSS_UNMATCHED;
1691 if (!state.pids && !state.pssdisc.match && !(state.pssdisc.flags & (PSS_ALL|PSS_ATTACHED|PSS_DETACHED|PSS_LEADER|PSS_NOLEADER)))
1692 {
1693 state.pssdisc.flags |= PSS_UID;
1694 state.pssdisc.uid = geteuid();
1695 for (n = 0; n <= 2; n++)
1696 if (isatty(n) && !fstat(n, &st))
1697 {
1698 state.pssdisc.flags |= PSS_TTY;
1699 state.pssdisc.tty = st.st_rdev;
1700 break;
1701 }
1702 }
1703 poppids();
1704 while (pe = pssread(state.pss, PSS_SCAN))
1705 addpid(pe, NiL);
1706 if (state.children || state.parents)
1707 for (pp = (Ps_t*)dtfirst(state.byorder); pp; pp = (Ps_t*)dtnext(state.byorder, pp))
1708 dtinsert(state.bypid, pp);
1709 }
1710 else
1711 poppids();
1712
1713 /*
1714 * list the procs
1715 */
1716
1717 if (state.fields)
1718 state.lastfield->sep = newline;
1719 list();
1720 pssclose(state.pss);
1721 return error_info.errors != 0;
1722 }
1723