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