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