xref: /openbsd/sys/kern/kern_proc.c (revision 5a38ef86)
1 /*	$OpenBSD: kern_proc.c,v 1.91 2021/10/24 00:02:25 jsg Exp $	*/
2 /*	$NetBSD: kern_proc.c,v 1.14 1996/02/09 18:59:41 christos Exp $	*/
3 
4 /*
5  * Copyright (c) 1982, 1986, 1989, 1991, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *	@(#)kern_proc.c	8.4 (Berkeley) 1/4/94
33  */
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/proc.h>
39 #include <sys/buf.h>
40 #include <sys/acct.h>
41 #include <sys/wait.h>
42 #include <sys/rwlock.h>
43 #include <ufs/ufs/quota.h>
44 #include <sys/uio.h>
45 #include <sys/malloc.h>
46 #include <sys/mbuf.h>
47 #include <sys/ioctl.h>
48 #include <sys/tty.h>
49 #include <sys/signalvar.h>
50 #include <sys/pool.h>
51 #include <sys/vnode.h>
52 
53 struct rwlock uidinfolk;
54 #define	UIHASH(uid)	(&uihashtbl[(uid) & uihash])
55 LIST_HEAD(uihashhead, uidinfo) *uihashtbl;
56 u_long uihash;		/* size of hash table - 1 */
57 
58 /*
59  * Other process lists
60  */
61 struct tidhashhead *tidhashtbl;
62 u_long tidhash;
63 struct pidhashhead *pidhashtbl;
64 u_long pidhash;
65 struct pgrphashhead *pgrphashtbl;
66 u_long pgrphash;
67 struct processlist allprocess;
68 struct processlist zombprocess;
69 struct proclist allproc;
70 
71 struct pool proc_pool;
72 struct pool process_pool;
73 struct pool rusage_pool;
74 struct pool ucred_pool;
75 struct pool pgrp_pool;
76 struct pool session_pool;
77 
78 void	pgdelete(struct pgrp *);
79 void	fixjobc(struct process *, struct pgrp *, int);
80 
81 static void orphanpg(struct pgrp *);
82 #ifdef DEBUG
83 void pgrpdump(void);
84 #endif
85 
86 /*
87  * Initialize global process hashing structures.
88  */
89 void
90 procinit(void)
91 {
92 	LIST_INIT(&allprocess);
93 	LIST_INIT(&zombprocess);
94 	LIST_INIT(&allproc);
95 
96 	rw_init(&uidinfolk, "uidinfo");
97 
98 	tidhashtbl = hashinit(maxthread / 4, M_PROC, M_NOWAIT, &tidhash);
99 	pidhashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pidhash);
100 	pgrphashtbl = hashinit(maxprocess / 4, M_PROC, M_NOWAIT, &pgrphash);
101 	uihashtbl = hashinit(maxprocess / 16, M_PROC, M_NOWAIT, &uihash);
102 	if (!tidhashtbl || !pidhashtbl || !pgrphashtbl || !uihashtbl)
103 		panic("procinit: malloc");
104 
105 	pool_init(&proc_pool, sizeof(struct proc), 0, IPL_NONE,
106 	    PR_WAITOK, "procpl", NULL);
107 	pool_init(&process_pool, sizeof(struct process), 0, IPL_NONE,
108 	    PR_WAITOK, "processpl", NULL);
109 	pool_init(&rusage_pool, sizeof(struct rusage), 0, IPL_NONE,
110 	    PR_WAITOK, "zombiepl", NULL);
111 	pool_init(&ucred_pool, sizeof(struct ucred), 0, IPL_MPFLOOR,
112 	    0, "ucredpl", NULL);
113 	pool_init(&pgrp_pool, sizeof(struct pgrp), 0, IPL_NONE,
114 	    PR_WAITOK, "pgrppl", NULL);
115 	pool_init(&session_pool, sizeof(struct session), 0, IPL_NONE,
116 	    PR_WAITOK, "sessionpl", NULL);
117 }
118 
119 /*
120  * This returns with `uidinfolk' held: caller must call uid_release()
121  * after making whatever change they needed.
122  */
123 struct uidinfo *
124 uid_find(uid_t uid)
125 {
126 	struct uidinfo *uip, *nuip;
127 	struct uihashhead *uipp;
128 
129 	uipp = UIHASH(uid);
130 	rw_enter_write(&uidinfolk);
131 	LIST_FOREACH(uip, uipp, ui_hash)
132 		if (uip->ui_uid == uid)
133 			break;
134 	if (uip)
135 		return (uip);
136 	rw_exit_write(&uidinfolk);
137 	nuip = malloc(sizeof(*nuip), M_PROC, M_WAITOK|M_ZERO);
138 	rw_enter_write(&uidinfolk);
139 	LIST_FOREACH(uip, uipp, ui_hash)
140 		if (uip->ui_uid == uid)
141 			break;
142 	if (uip) {
143 		free(nuip, M_PROC, sizeof(*nuip));
144 		return (uip);
145 	}
146 	nuip->ui_uid = uid;
147 	LIST_INSERT_HEAD(uipp, nuip, ui_hash);
148 
149 	return (nuip);
150 }
151 
152 void
153 uid_release(struct uidinfo *uip)
154 {
155 	rw_exit_write(&uidinfolk);
156 }
157 
158 /*
159  * Change the count associated with number of threads
160  * a given user is using.
161  */
162 int
163 chgproccnt(uid_t uid, int diff)
164 {
165 	struct uidinfo *uip;
166 	long count;
167 
168 	uip = uid_find(uid);
169 	count = (uip->ui_proccnt += diff);
170 	uid_release(uip);
171 	if (count < 0)
172 		panic("chgproccnt: procs < 0");
173 	return count;
174 }
175 
176 /*
177  * Is pr an inferior of parent?
178  */
179 int
180 inferior(struct process *pr, struct process *parent)
181 {
182 
183 	for (; pr != parent; pr = pr->ps_pptr)
184 		if (pr->ps_pid == 0 || pr->ps_pid == 1)
185 			return (0);
186 	return (1);
187 }
188 
189 /*
190  * Locate a proc (thread) by number
191  */
192 struct proc *
193 tfind(pid_t tid)
194 {
195 	struct proc *p;
196 
197 	LIST_FOREACH(p, TIDHASH(tid), p_hash)
198 		if (p->p_tid == tid)
199 			return (p);
200 	return (NULL);
201 }
202 
203 /*
204  * Locate a process by number
205  */
206 struct process *
207 prfind(pid_t pid)
208 {
209 	struct process *pr;
210 
211 	LIST_FOREACH(pr, PIDHASH(pid), ps_hash)
212 		if (pr->ps_pid == pid)
213 			return (pr);
214 	return (NULL);
215 }
216 
217 /*
218  * Locate a process group by number
219  */
220 struct pgrp *
221 pgfind(pid_t pgid)
222 {
223 	struct pgrp *pgrp;
224 
225 	LIST_FOREACH(pgrp, PGRPHASH(pgid), pg_hash)
226 		if (pgrp->pg_id == pgid)
227 			return (pgrp);
228 	return (NULL);
229 }
230 
231 /*
232  * Locate a zombie process
233  */
234 struct process *
235 zombiefind(pid_t pid)
236 {
237 	struct process *pr;
238 
239 	LIST_FOREACH(pr, &zombprocess, ps_list)
240 		if (pr->ps_pid == pid)
241 			return (pr);
242 	return (NULL);
243 }
244 
245 /*
246  * Move process to a new process group.  If a session is provided
247  * then it's a new session to contain this process group; otherwise
248  * the process is staying within its existing session.
249  */
250 void
251 enternewpgrp(struct process *pr, struct pgrp *pgrp, struct session *newsess)
252 {
253 #ifdef DIAGNOSTIC
254 	if (SESS_LEADER(pr))
255 		panic("%s: session leader attempted setpgrp", __func__);
256 #endif
257 
258 	if (newsess != NULL) {
259 		/*
260 		 * New session.  Initialize it completely
261 		 */
262 		timeout_set(&newsess->s_verauthto, zapverauth, newsess);
263 		newsess->s_leader = pr;
264 		newsess->s_count = 1;
265 		newsess->s_ttyvp = NULL;
266 		newsess->s_ttyp = NULL;
267 		memcpy(newsess->s_login, pr->ps_session->s_login,
268 		    sizeof(newsess->s_login));
269 		atomic_clearbits_int(&pr->ps_flags, PS_CONTROLT);
270 		pgrp->pg_session = newsess;
271 #ifdef DIAGNOSTIC
272 		if (pr != curproc->p_p)
273 			panic("%s: mksession but not curproc", __func__);
274 #endif
275 	} else {
276 		pgrp->pg_session = pr->ps_session;
277 		pgrp->pg_session->s_count++;
278 	}
279 	pgrp->pg_id = pr->ps_pid;
280 	LIST_INIT(&pgrp->pg_members);
281 	LIST_INIT(&pgrp->pg_sigiolst);
282 	LIST_INSERT_HEAD(PGRPHASH(pr->ps_pid), pgrp, pg_hash);
283 	pgrp->pg_jobc = 0;
284 
285 	enterthispgrp(pr, pgrp);
286 }
287 
288 /*
289  * move process to an existing process group
290  */
291 void
292 enterthispgrp(struct process *pr, struct pgrp *pgrp)
293 {
294 	struct pgrp *savepgrp = pr->ps_pgrp;
295 
296 	/*
297 	 * Adjust eligibility of affected pgrps to participate in job control.
298 	 * Increment eligibility counts before decrementing, otherwise we
299 	 * could reach 0 spuriously during the first call.
300 	 */
301 	fixjobc(pr, pgrp, 1);
302 	fixjobc(pr, savepgrp, 0);
303 
304 	LIST_REMOVE(pr, ps_pglist);
305 	pr->ps_pgrp = pgrp;
306 	LIST_INSERT_HEAD(&pgrp->pg_members, pr, ps_pglist);
307 	if (LIST_EMPTY(&savepgrp->pg_members))
308 		pgdelete(savepgrp);
309 }
310 
311 /*
312  * remove process from process group
313  */
314 void
315 leavepgrp(struct process *pr)
316 {
317 
318 	if (pr->ps_session->s_verauthppid == pr->ps_pid)
319 		zapverauth(pr->ps_session);
320 	LIST_REMOVE(pr, ps_pglist);
321 	if (LIST_EMPTY(&pr->ps_pgrp->pg_members))
322 		pgdelete(pr->ps_pgrp);
323 	pr->ps_pgrp = NULL;
324 }
325 
326 /*
327  * delete a process group
328  */
329 void
330 pgdelete(struct pgrp *pgrp)
331 {
332 	sigio_freelist(&pgrp->pg_sigiolst);
333 
334 	if (pgrp->pg_session->s_ttyp != NULL &&
335 	    pgrp->pg_session->s_ttyp->t_pgrp == pgrp)
336 		pgrp->pg_session->s_ttyp->t_pgrp = NULL;
337 	LIST_REMOVE(pgrp, pg_hash);
338 	SESSRELE(pgrp->pg_session);
339 	pool_put(&pgrp_pool, pgrp);
340 }
341 
342 void
343 zapverauth(void *v)
344 {
345 	struct session *sess = v;
346 	sess->s_verauthuid = 0;
347 	sess->s_verauthppid = 0;
348 }
349 
350 /*
351  * Adjust pgrp jobc counters when specified process changes process group.
352  * We count the number of processes in each process group that "qualify"
353  * the group for terminal job control (those with a parent in a different
354  * process group of the same session).  If that count reaches zero, the
355  * process group becomes orphaned.  Check both the specified process'
356  * process group and that of its children.
357  * entering == 0 => pr is leaving specified group.
358  * entering == 1 => pr is entering specified group.
359  * XXX need proctree lock
360  */
361 void
362 fixjobc(struct process *pr, struct pgrp *pgrp, int entering)
363 {
364 	struct pgrp *hispgrp;
365 	struct session *mysession = pgrp->pg_session;
366 
367 	/*
368 	 * Check pr's parent to see whether pr qualifies its own process
369 	 * group; if so, adjust count for pr's process group.
370 	 */
371 	if ((hispgrp = pr->ps_pptr->ps_pgrp) != pgrp &&
372 	    hispgrp->pg_session == mysession) {
373 		if (entering)
374 			pgrp->pg_jobc++;
375 		else if (--pgrp->pg_jobc == 0)
376 			orphanpg(pgrp);
377 	}
378 
379 	/*
380 	 * Check this process' children to see whether they qualify
381 	 * their process groups; if so, adjust counts for children's
382 	 * process groups.
383 	 */
384 	LIST_FOREACH(pr, &pr->ps_children, ps_sibling)
385 		if ((hispgrp = pr->ps_pgrp) != pgrp &&
386 		    hispgrp->pg_session == mysession &&
387 		    (pr->ps_flags & PS_ZOMBIE) == 0) {
388 			if (entering)
389 				hispgrp->pg_jobc++;
390 			else if (--hispgrp->pg_jobc == 0)
391 				orphanpg(hispgrp);
392 		}
393 }
394 
395 void
396 killjobc(struct process *pr)
397 {
398 	if (SESS_LEADER(pr)) {
399 		struct session *sp = pr->ps_session;
400 
401 		if (sp->s_ttyvp) {
402 			struct vnode *ovp;
403 
404 			/*
405 			 * Controlling process.
406 			 * Signal foreground pgrp,
407 			 * drain controlling terminal
408 			 * and revoke access to controlling terminal.
409 			 */
410 			if (sp->s_ttyp->t_session == sp) {
411 				if (sp->s_ttyp->t_pgrp)
412 					pgsignal(sp->s_ttyp->t_pgrp, SIGHUP, 1);
413 				ttywait(sp->s_ttyp);
414 				/*
415 				 * The tty could have been revoked
416 				 * if we blocked.
417 				 */
418 				if (sp->s_ttyvp)
419 					VOP_REVOKE(sp->s_ttyvp, REVOKEALL);
420 			}
421 			ovp = sp->s_ttyvp;
422 			sp->s_ttyvp = NULL;
423 			if (ovp)
424 				vrele(ovp);
425 			/*
426 			 * s_ttyp is not zero'd; we use this to
427 			 * indicate that the session once had a
428 			 * controlling terminal.  (for logging and
429 			 * informational purposes)
430 			 */
431 		}
432 		sp->s_leader = NULL;
433 	}
434 	fixjobc(pr, pr->ps_pgrp, 0);
435 }
436 
437 /*
438  * A process group has become orphaned;
439  * if there are any stopped processes in the group,
440  * hang-up all process in that group.
441  */
442 static void
443 orphanpg(struct pgrp *pg)
444 {
445 	struct process *pr;
446 
447 	LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
448 		if (pr->ps_mainproc->p_stat == SSTOP) {
449 			LIST_FOREACH(pr, &pg->pg_members, ps_pglist) {
450 				prsignal(pr, SIGHUP);
451 				prsignal(pr, SIGCONT);
452 			}
453 			return;
454 		}
455 	}
456 }
457 
458 #ifdef DDB
459 void
460 proc_printit(struct proc *p, const char *modif,
461     int (*pr)(const char *, ...) __attribute__((__format__(__kprintf__,1,2))))
462 {
463 	static const char *const pstat[] = {
464 		"idle", "run", "sleep", "stop", "zombie", "dead", "onproc"
465 	};
466 	char pstbuf[5];
467 	const char *pst = pstbuf;
468 
469 
470 	if (p->p_stat < 1 || p->p_stat > sizeof(pstat) / sizeof(pstat[0]))
471 		snprintf(pstbuf, sizeof(pstbuf), "%d", p->p_stat);
472 	else
473 		pst = pstat[(int)p->p_stat - 1];
474 
475 	(*pr)("PROC (%s) pid=%d stat=%s\n", p->p_p->ps_comm, p->p_tid, pst);
476 	(*pr)("    flags process=%b proc=%b\n",
477 	    p->p_p->ps_flags, PS_BITS, p->p_flag, P_BITS);
478 	(*pr)("    pri=%u, usrpri=%u, nice=%d\n",
479 	    p->p_runpri, p->p_usrpri, p->p_p->ps_nice);
480 	(*pr)("    forw=%p, list=%p,%p\n",
481 	    TAILQ_NEXT(p, p_runq), p->p_list.le_next, p->p_list.le_prev);
482 	(*pr)("    process=%p user=%p, vmspace=%p\n",
483 	    p->p_p, p->p_addr, p->p_vmspace);
484 	(*pr)("    estcpu=%u, cpticks=%d, pctcpu=%u.%u\n",
485 	    p->p_estcpu, p->p_cpticks, p->p_pctcpu / 100, p->p_pctcpu % 100);
486 	(*pr)("    user=%u, sys=%u, intr=%u\n",
487 	    p->p_uticks, p->p_sticks, p->p_iticks);
488 }
489 #include <machine/db_machdep.h>
490 
491 #include <ddb/db_output.h>
492 
493 void
494 db_kill_cmd(db_expr_t addr, int have_addr, db_expr_t count, char *modif)
495 {
496 	struct process *pr;
497 	struct proc *p;
498 
499 	pr = prfind(addr);
500 	if (pr == NULL) {
501 		db_printf("%ld: No such process", addr);
502 		return;
503 	}
504 
505 	p = TAILQ_FIRST(&pr->ps_threads);
506 
507 	/* Send uncatchable SIGABRT for coredump */
508 	sigabort(p);
509 }
510 
511 void
512 db_show_all_procs(db_expr_t addr, int haddr, db_expr_t count, char *modif)
513 {
514 	char *mode;
515 	int skipzomb = 0;
516 	int has_kernel_lock = 0;
517 	struct proc *p;
518 	struct process *pr, *ppr;
519 
520 	if (modif[0] == 0)
521 		modif[0] = 'n';			/* default == normal mode */
522 
523 	mode = "mawno";
524 	while (*mode && *mode != modif[0])
525 		mode++;
526 	if (*mode == 0 || *mode == 'm') {
527 		db_printf("usage: show all procs [/a] [/n] [/w]\n");
528 		db_printf("\t/a == show process address info\n");
529 		db_printf("\t/n == show normal process info [default]\n");
530 		db_printf("\t/w == show process pgrp/wait info\n");
531 		db_printf("\t/o == show normal info for non-idle SONPROC\n");
532 		return;
533 	}
534 
535 	pr = LIST_FIRST(&allprocess);
536 
537 	switch (*mode) {
538 
539 	case 'a':
540 		db_printf("    TID  %-9s  %18s  %18s  %18s\n",
541 		    "COMMAND", "STRUCT PROC *", "UAREA *", "VMSPACE/VM_MAP");
542 		break;
543 	case 'n':
544 		db_printf("   PID  %6s  %5s  %5s  S  %10s  %-12s  %-15s\n",
545 		    "TID", "PPID", "UID", "FLAGS", "WAIT", "COMMAND");
546 		break;
547 	case 'w':
548 		db_printf("    TID  %-15s  %-5s  %18s  %s\n",
549 		    "COMMAND", "PGRP", "WAIT-CHANNEL", "WAIT-MSG");
550 		break;
551 	case 'o':
552 		skipzomb = 1;
553 		db_printf("    TID  %5s  %5s  %10s %10s  %3s  %-30s\n",
554 		    "PID", "UID", "PRFLAGS", "PFLAGS", "CPU", "COMMAND");
555 		break;
556 	}
557 
558 	while (pr != NULL) {
559 		ppr = pr->ps_pptr;
560 
561 		TAILQ_FOREACH(p, &pr->ps_threads, p_thr_link) {
562 #ifdef MULTIPROCESSOR
563 			if (__mp_lock_held(&kernel_lock, p->p_cpu))
564 				has_kernel_lock = 1;
565 			else
566 				has_kernel_lock = 0;
567 #endif
568 			if (p->p_stat) {
569 				if (*mode == 'o') {
570 					if (p->p_stat != SONPROC)
571 						continue;
572 					if (p->p_cpu != NULL && p->p_cpu->
573 					    ci_schedstate.spc_idleproc == p)
574 						continue;
575 				}
576 
577 				if (*mode == 'n') {
578 					db_printf("%c%5d  ", (p == curproc ?
579 					    '*' : ' '), pr->ps_pid);
580 				} else {
581 					db_printf("%c%6d  ", (p == curproc ?
582 					    '*' : ' '), p->p_tid);
583 				}
584 
585 				switch (*mode) {
586 
587 				case 'a':
588 					db_printf("%-9.9s  %18p  %18p  %18p\n",
589 					    pr->ps_comm, p, p->p_addr, p->p_vmspace);
590 					break;
591 
592 				case 'n':
593 					db_printf("%6d  %5d  %5d  %d  %#10x  "
594 					    "%-12.12s  %-15s\n",
595 					    p->p_tid, ppr ? ppr->ps_pid : -1,
596 					    pr->ps_ucred->cr_ruid, p->p_stat,
597 					    p->p_flag | pr->ps_flags,
598 					    (p->p_wchan && p->p_wmesg) ?
599 						p->p_wmesg : "", pr->ps_comm);
600 					break;
601 
602 				case 'w':
603 					db_printf("%-15s  %-5d  %18p  %s\n",
604 					    pr->ps_comm, (pr->ps_pgrp ?
605 						pr->ps_pgrp->pg_id : -1),
606 					    p->p_wchan,
607 					    (p->p_wchan && p->p_wmesg) ?
608 						p->p_wmesg : "");
609 					break;
610 
611 				case 'o':
612 					db_printf("%5d  %5d  %#10x %#10x  %3d"
613 					    "%c %-31s\n",
614 					    pr->ps_pid, pr->ps_ucred->cr_ruid,
615 					    pr->ps_flags, p->p_flag,
616 					    CPU_INFO_UNIT(p->p_cpu),
617 					    has_kernel_lock ? 'K' : ' ',
618 					    pr->ps_comm);
619 					break;
620 
621 				}
622 			}
623 		}
624 		pr = LIST_NEXT(pr, ps_list);
625 		if (pr == NULL && skipzomb == 0) {
626 			skipzomb = 1;
627 			pr = LIST_FIRST(&zombprocess);
628 		}
629 	}
630 }
631 #endif
632 
633 #ifdef DEBUG
634 void
635 pgrpdump(void)
636 {
637 	struct pgrp *pgrp;
638 	struct process *pr;
639 	int i;
640 
641 	for (i = 0; i <= pgrphash; i++) {
642 		if (!LIST_EMPTY(&pgrphashtbl[i])) {
643 			printf("\tindx %d\n", i);
644 			LIST_FOREACH(pgrp, &pgrphashtbl[i], pg_hash) {
645 				printf("\tpgrp %p, pgid %d, sess %p, sesscnt %d, mem %p\n",
646 				    pgrp, pgrp->pg_id, pgrp->pg_session,
647 				    pgrp->pg_session->s_count,
648 				    LIST_FIRST(&pgrp->pg_members));
649 				LIST_FOREACH(pr, &pgrp->pg_members, ps_pglist) {
650 					printf("\t\tpid %d addr %p pgrp %p\n",
651 					    pr->ps_pid, pr, pr->ps_pgrp);
652 				}
653 			}
654 		}
655 	}
656 }
657 #endif /* DEBUG */
658