xref: /minix/minix/servers/pm/forkexit.c (revision fb9c64b2)
1 /* This file deals with creating processes (via FORK) and deleting them (via
2  * EXIT/WAIT4).  When a process forks, a new slot in the 'mproc' table is
3  * allocated for it, and a copy of the parent's core image is made for the
4  * child.  Then the kernel and file system are informed.  A process is removed
5  * from the 'mproc' table when two events have occurred: (1) it has exited or
6  * been killed by a signal, and (2) the parent has done a WAIT4.  If the
7  * process exits first, it continues to occupy a slot until the parent does a
8  * WAIT4.
9  *
10  * The entry points into this file are:
11  *   do_fork:		perform the FORK system call
12  *   do_srv_fork:	special FORK, used by RS to create sys services
13  *   do_exit:		perform the EXIT system call (by calling exit_proc())
14  *   exit_proc:		actually do the exiting, and tell VFS about it
15  *   exit_restart:	continue exiting a process after VFS has replied
16  *   do_wait4:		perform the WAIT4 system call
17  *   wait_test:		check whether a parent is waiting for a child
18  */
19 
20 #include "pm.h"
21 #include <sys/wait.h>
22 #include <assert.h>
23 #include <minix/callnr.h>
24 #include <minix/com.h>
25 #include <minix/sched.h>
26 #include <minix/vm.h>
27 #include <sys/ptrace.h>
28 #include <sys/resource.h>
29 #include <signal.h>
30 #include "mproc.h"
31 
32 #define LAST_FEW            2	/* last few slots reserved for superuser */
33 
34 static void zombify(struct mproc *rmp);
35 static void check_parent(struct mproc *child, int try_cleanup);
36 static int tell_parent(struct mproc *child, vir_bytes addr);
37 static void tell_tracer(struct mproc *child);
38 static void tracer_died(struct mproc *child);
39 static void cleanup(register struct mproc *rmp);
40 
41 /*===========================================================================*
42  *				do_fork					     *
43  *===========================================================================*/
44 int
45 do_fork(void)
46 {
47 /* The process pointed to by 'mp' has forked.  Create a child process. */
48   register struct mproc *rmp;	/* pointer to parent */
49   register struct mproc *rmc;	/* pointer to child */
50   pid_t new_pid;
51   static unsigned int next_child = 0;
52   int i, n = 0, s;
53   endpoint_t child_ep;
54   message m;
55 
56  /* If tables might fill up during FORK, don't even start since recovery half
57   * way through is such a nuisance.
58   */
59   rmp = mp;
60   if ((procs_in_use == NR_PROCS) ||
61   		(procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
62   {
63   	printf("PM: warning, process table is full!\n");
64   	return(EAGAIN);
65   }
66 
67   /* Find a slot in 'mproc' for the child process.  A slot must exist. */
68   do {
69         next_child = (next_child+1) % NR_PROCS;
70 	n++;
71   } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
72   if(n > NR_PROCS)
73 	panic("do_fork can't find child slot");
74   if(next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE))
75 	panic("do_fork finds wrong child slot: %d", next_child);
76 
77   /* Memory part of the forking. */
78   if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) {
79 	return s;
80   }
81 
82   /* PM may not fail fork after call to vm_fork(), as VM calls sys_fork(). */
83 
84   rmc = &mproc[next_child];
85   /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
86   procs_in_use++;
87   *rmc = *rmp;			/* copy parent's process slot to child's */
88   rmc->mp_sigact = mpsigact[next_child];	/* restore mp_sigact ptr */
89   memcpy(rmc->mp_sigact, rmp->mp_sigact, sizeof(mpsigact[next_child]));
90   rmc->mp_parent = who_p;			/* record child's parent */
91   if (!(rmc->mp_trace_flags & TO_TRACEFORK)) {
92 	rmc->mp_tracer = NO_TRACER;		/* no tracer attached */
93 	rmc->mp_trace_flags = 0;
94 	(void) sigemptyset(&rmc->mp_sigtrace);
95   }
96 
97   /* Some system servers like to call regular fork, such as RS spawning
98    * recovery scripts; in this case PM will take care of their scheduling
99    * because RS cannot do so for non-system processes */
100   if (rmc->mp_flags & PRIV_PROC) {
101 	assert(rmc->mp_scheduler == NONE);
102 	rmc->mp_scheduler = SCHED_PROC_NR;
103   }
104 
105   /* Inherit only these flags. In normal fork(), PRIV_PROC is not inherited. */
106   rmc->mp_flags &= (IN_USE|DELAY_CALL|TAINTED);
107   rmc->mp_child_utime = 0;		/* reset administration */
108   rmc->mp_child_stime = 0;		/* reset administration */
109   rmc->mp_exitstatus = 0;
110   rmc->mp_sigstatus = 0;
111   rmc->mp_endpoint = child_ep;		/* passed back by VM */
112   for (i = 0; i < NR_ITIMERS; i++)
113 	rmc->mp_interval[i] = 0;	/* reset timer intervals */
114   rmc->mp_started = getticks();		/* remember start time, for ps(1) */
115 
116   assert(rmc->mp_eventsub == NO_EVENTSUB);
117 
118   /* Find a free pid for the child and put it in the table. */
119   new_pid = get_free_pid();
120   rmc->mp_pid = new_pid;	/* assign pid to child */
121 
122   memset(&m, 0, sizeof(m));
123   m.m_type = VFS_PM_FORK;
124   m.VFS_PM_ENDPT = rmc->mp_endpoint;
125   m.VFS_PM_PENDPT = rmp->mp_endpoint;
126   m.VFS_PM_CPID = rmc->mp_pid;
127   m.VFS_PM_REUID = -1;	/* Not used by VFS_PM_FORK */
128   m.VFS_PM_REGID = -1;	/* Not used by VFS_PM_FORK */
129 
130   tell_vfs(rmc, &m);
131 
132   /* Tell the tracer, if any, about the new child */
133   if (rmc->mp_tracer != NO_TRACER)
134 	sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
135 
136   /* Do not reply until VFS is ready to process the fork
137   * request
138   */
139   return SUSPEND;
140 }
141 
142 /*===========================================================================*
143  *				do_srv_fork				     *
144  *===========================================================================*/
145 int
146 do_srv_fork(void)
147 {
148 /* The process pointed to by 'mp' has forked.  Create a child process. */
149   register struct mproc *rmp;	/* pointer to parent */
150   register struct mproc *rmc;	/* pointer to child */
151   int s;
152   pid_t new_pid;
153   static unsigned int next_child = 0;
154   int i, n = 0;
155   endpoint_t child_ep;
156   message m;
157 
158   /* Only RS is allowed to use srv_fork. */
159   if (mp->mp_endpoint != RS_PROC_NR)
160 	return EPERM;
161 
162  /* If tables might fill up during FORK, don't even start since recovery half
163   * way through is such a nuisance.
164   */
165   rmp = mp;
166   if ((procs_in_use == NR_PROCS) ||
167   		(procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
168   {
169   	printf("PM: warning, process table is full!\n");
170   	return(EAGAIN);
171   }
172 
173   /* Find a slot in 'mproc' for the child process.  A slot must exist. */
174   do {
175         next_child = (next_child+1) % NR_PROCS;
176 	n++;
177   } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
178   if(n > NR_PROCS)
179 	panic("do_fork can't find child slot");
180   if(next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE))
181 	panic("do_fork finds wrong child slot: %d", next_child);
182 
183   if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) {
184 	return s;
185   }
186 
187   rmc = &mproc[next_child];
188   /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
189   procs_in_use++;
190   *rmc = *rmp;			/* copy parent's process slot to child's */
191   rmc->mp_sigact = mpsigact[next_child];	/* restore mp_sigact ptr */
192   memcpy(rmc->mp_sigact, rmp->mp_sigact, sizeof(mpsigact[next_child]));
193   rmc->mp_parent = who_p;			/* record child's parent */
194   if (!(rmc->mp_trace_flags & TO_TRACEFORK)) {
195 	rmc->mp_tracer = NO_TRACER;		/* no tracer attached */
196 	rmc->mp_trace_flags = 0;
197 	(void) sigemptyset(&rmc->mp_sigtrace);
198   }
199   /* inherit only these flags */
200   rmc->mp_flags &= (IN_USE|PRIV_PROC|DELAY_CALL);
201   rmc->mp_child_utime = 0;		/* reset administration */
202   rmc->mp_child_stime = 0;		/* reset administration */
203   rmc->mp_exitstatus = 0;
204   rmc->mp_sigstatus = 0;
205   rmc->mp_endpoint = child_ep;		/* passed back by VM */
206   rmc->mp_realuid = m_in.m_lsys_pm_srv_fork.uid;
207   rmc->mp_effuid = m_in.m_lsys_pm_srv_fork.uid;
208   rmc->mp_svuid = m_in.m_lsys_pm_srv_fork.uid;
209   rmc->mp_realgid = m_in.m_lsys_pm_srv_fork.gid;
210   rmc->mp_effgid = m_in.m_lsys_pm_srv_fork.gid;
211   rmc->mp_svgid = m_in.m_lsys_pm_srv_fork.gid;
212   for (i = 0; i < NR_ITIMERS; i++)
213 	rmc->mp_interval[i] = 0;	/* reset timer intervals */
214   rmc->mp_started = getticks();		/* remember start time, for ps(1) */
215 
216   assert(rmc->mp_eventsub == NO_EVENTSUB);
217 
218   /* Find a free pid for the child and put it in the table. */
219   new_pid = get_free_pid();
220   rmc->mp_pid = new_pid;	/* assign pid to child */
221 
222   memset(&m, 0, sizeof(m));
223   m.m_type = VFS_PM_SRV_FORK;
224   m.VFS_PM_ENDPT = rmc->mp_endpoint;
225   m.VFS_PM_PENDPT = rmp->mp_endpoint;
226   m.VFS_PM_CPID = rmc->mp_pid;
227   m.VFS_PM_REUID = m_in.m_lsys_pm_srv_fork.uid;
228   m.VFS_PM_REGID = m_in.m_lsys_pm_srv_fork.gid;
229 
230   tell_vfs(rmc, &m);
231 
232   /* Tell the tracer, if any, about the new child */
233   if (rmc->mp_tracer != NO_TRACER)
234 	sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
235 
236   /* Wakeup the newly created process */
237   reply(rmc-mproc, OK);
238 
239   return rmc->mp_pid;
240 }
241 
242 /*===========================================================================*
243  *				do_exit					     *
244  *===========================================================================*/
245 int
246 do_exit(void)
247 {
248  /* Perform the exit(status) system call. The real work is done by exit_proc(),
249   * which is also called when a process is killed by a signal. System processes
250   * do not use PM's exit() to terminate. If they try to, we warn the user
251   * and send a SIGKILL signal to the system process.
252   */
253   if(mp->mp_flags & PRIV_PROC) {
254       printf("PM: system process %d (%s) tries to exit(), sending SIGKILL\n",
255           mp->mp_endpoint, mp->mp_name);
256       sys_kill(mp->mp_endpoint, SIGKILL);
257   }
258   else {
259       exit_proc(mp, m_in.m_lc_pm_exit.status, FALSE /*dump_core*/);
260   }
261   return(SUSPEND);		/* can't communicate from beyond the grave */
262 }
263 
264 /*===========================================================================*
265  *				exit_proc				     *
266  *===========================================================================*/
267 void
268 exit_proc(
269 	register struct mproc *rmp,	/* pointer to the process to be terminated */
270 	int exit_status,		/* the process' exit status (for parent) */
271 	int dump_core			/* flag indicating whether to dump core */
272 )
273 {
274 /* A process is done.  Release most of the process' possessions.  If its
275  * parent is waiting, release the rest, else keep the process slot and
276  * become a zombie.
277  */
278   register int proc_nr, proc_nr_e;
279   int r;
280   pid_t procgrp;
281   clock_t user_time, sys_time;
282   message m;
283 
284   /* Do not create core files for set uid execution */
285   if (dump_core && rmp->mp_realuid != rmp->mp_effuid)
286 	dump_core = FALSE;
287 
288   /* System processes are destroyed before informing VFS, meaning that VFS can
289    * not get their CPU state, so we can't generate a coredump for them either.
290    */
291   if (dump_core && (rmp->mp_flags & PRIV_PROC))
292 	dump_core = FALSE;
293 
294   proc_nr = (int) (rmp - mproc);	/* get process slot number */
295   proc_nr_e = rmp->mp_endpoint;
296 
297   /* Remember a session leader's process group. */
298   procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;
299 
300   /* If the exited process has a timer pending, kill it. */
301   if (rmp->mp_flags & ALARM_ON) set_alarm(rmp, (clock_t) 0);
302 
303   /* Do accounting: fetch usage times and save with dead child process.
304    * POSIX forbids accumulation at parent until child has been waited for.
305    */
306   if((r=sys_times(proc_nr_e, &user_time, &sys_time, NULL, NULL)) != OK)
307   	panic("exit_proc: sys_times failed: %d", r);
308   rmp->mp_child_utime += user_time;		/* add user time */
309   rmp->mp_child_stime += sys_time;		/* add system time */
310 
311   /* Tell the kernel the process is no longer runnable to prevent it from
312    * being scheduled in between the following steps. Then tell VFS that it
313    * the process has exited and finally, clean up the process at the kernel.
314    * This order is important so that VFS can tell drivers to cancel requests
315    * such as copying to/ from the exiting process, before it is gone.
316    */
317   /* If the process is not yet stopped, we force a stop here. This means that
318    * the process may still have a delay call pending. For this reason, the main
319    * message loop discards requests from exiting processes.
320    *
321    * TODO: make the kernel discard delayed calls upon forced stops for exits,
322    * so that no service needs to deal with this.  Right now it appears that the
323    * only thing preventing problems with other services is the fact that
324    * regular messages are prioritized over asynchronous messages.
325    */
326   if (!(rmp->mp_flags & PROC_STOPPED)) {
327 	if ((r = sys_stop(proc_nr_e)) != OK)		/* stop the process */
328 		panic("sys_stop failed: %d", r);
329 	rmp->mp_flags |= PROC_STOPPED;
330   }
331 
332   if((r=vm_willexit(proc_nr_e)) != OK) {
333 	panic("exit_proc: vm_willexit failed: %d", r);
334   }
335 
336   if (proc_nr_e == INIT_PROC_NR)
337   {
338 	printf("PM: INIT died with exit status %d; showing stacktrace\n", exit_status);
339 	sys_diagctl_stacktrace(proc_nr_e);
340 	return;
341   }
342   if (proc_nr_e == VFS_PROC_NR)
343   {
344 	panic("exit_proc: VFS died: %d", r);
345   }
346 
347   /* Tell VFS, and after that any matching process event subscribers, about the
348    * exiting process.
349    */
350   memset(&m, 0, sizeof(m));
351   m.m_type = dump_core ? VFS_PM_DUMPCORE : VFS_PM_EXIT;
352   m.VFS_PM_ENDPT = rmp->mp_endpoint;
353 
354   if (dump_core) {
355 	m.VFS_PM_TERM_SIG = rmp->mp_sigstatus;
356 	m.VFS_PM_PATH = rmp->mp_name;
357   }
358 
359   tell_vfs(rmp, &m);
360 
361   if (rmp->mp_flags & PRIV_PROC)
362   {
363 	/* Destroy system processes without waiting for VFS. This is
364 	 * needed because the system process might be a block device
365 	 * driver that VFS is blocked waiting on.
366 	 */
367 	if((r= sys_clear(rmp->mp_endpoint)) != OK)
368 		panic("exit_proc: sys_clear failed: %d", r);
369   }
370 
371   /* Clean up most of the flags describing the process's state before the exit,
372    * and mark it as exiting.
373    */
374   rmp->mp_flags &= (IN_USE|VFS_CALL|PRIV_PROC|TRACE_EXIT|PROC_STOPPED);
375   rmp->mp_flags |= EXITING;
376 
377   /* Keep the process around until VFS is finished with it. */
378 
379   rmp->mp_exitstatus = (char) exit_status;
380 
381   /* For normal exits, try to notify the parent as soon as possible.
382    * For core dumps, notify the parent only once the core dump has been made.
383    */
384   if (!dump_core)
385 	zombify(rmp);
386 
387   /* If the process has children, disinherit them.  INIT is the new parent. */
388   for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
389 	if (!(rmp->mp_flags & IN_USE)) continue;
390 	if (rmp->mp_tracer == proc_nr) {
391 		/* This child's tracer died. Do something sensible. */
392 		tracer_died(rmp);
393 	}
394 	if (rmp->mp_parent == proc_nr) {
395 		/* 'rmp' now points to a child to be disinherited. */
396 		rmp->mp_parent = INIT_PROC_NR;
397 
398 		/* If the process is making a VFS call, remember that we set
399 		 * a new parent. This prevents FORK from replying to the wrong
400 		 * parent upon completion.
401 		 */
402 		if (rmp->mp_flags & VFS_CALL)
403 			rmp->mp_flags |= NEW_PARENT;
404 
405 		/* Notify new parent. */
406 		if (rmp->mp_flags & ZOMBIE)
407 			check_parent(rmp, TRUE /*try_cleanup*/);
408 	}
409   }
410 
411   /* Send a hangup to the process' process group if it was a session leader. */
412   if (procgrp != 0) check_sig(-procgrp, SIGHUP, FALSE /* ksig */);
413 }
414 
415 /*===========================================================================*
416  *				exit_restart				     *
417  *===========================================================================*/
418 void exit_restart(struct mproc *rmp)
419 {
420 /* VFS replied to our exit or coredump request. Perform the second half of the
421  * exit code.
422  */
423   int r;
424 
425   if((r = sched_stop(rmp->mp_scheduler, rmp->mp_endpoint)) != OK) {
426  	/* If the scheduler refuses to give up scheduling, there is
427 	 * little we can do, except report it. This may cause problems
428 	 * later on, if this scheduler is asked to schedule another proc
429 	 * that has an endpoint->schedproc mapping identical to the proc
430 	 * we just tried to stop scheduling.
431 	*/
432 	printf("PM: The scheduler did not want to give up "
433 		"scheduling %s, ret=%d.\n", rmp->mp_name, r);
434   }
435 
436   /* sched_stop is either called when the process is exiting or it is
437    * being moved between schedulers. If it is being moved between
438    * schedulers, we need to set the mp_scheduler to NONE so that PM
439    * doesn't forward messages to the process' scheduler while being moved
440    * (such as sched_nice). */
441   rmp->mp_scheduler = NONE;
442 
443   /* For core dumps, now is the right time to try to contact the parent. */
444   if (!(rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE | TOLD_PARENT)))
445 	zombify(rmp);
446 
447   if (!(rmp->mp_flags & PRIV_PROC))
448   {
449 	/* destroy the (user) process */
450 	if((r=sys_clear(rmp->mp_endpoint)) != OK)
451 		panic("exit_restart: sys_clear failed: %d", r);
452   }
453 
454   /* Release the memory occupied by the child. */
455   if((r=vm_exit(rmp->mp_endpoint)) != OK) {
456   	panic("exit_restart: vm_exit failed: %d", r);
457   }
458 
459   if (rmp->mp_flags & TRACE_EXIT)
460   {
461 	/* Wake up the tracer, completing the ptrace(T_EXIT) call */
462 	mproc[rmp->mp_tracer].mp_reply.m_pm_lc_ptrace.data = 0;
463 	reply(rmp->mp_tracer, OK);
464   }
465 
466   /* Clean up if the parent has collected the exit status */
467   if (rmp->mp_flags & TOLD_PARENT)
468 	cleanup(rmp);
469 }
470 
471 /*===========================================================================*
472  *				do_wait4				     *
473  *===========================================================================*/
474 int
475 do_wait4(void)
476 {
477 /* A process wants to wait for a child to terminate. If a child is already
478  * waiting, go clean it up and let this WAIT4 call terminate.  Otherwise,
479  * really wait.
480  * A process calling WAIT4 never gets a reply in the usual way at the end
481  * of the main loop (unless WNOHANG is set or no qualifying child exists).
482  * If a child has already exited, the routine tell_parent() sends the reply
483  * to awaken the caller.
484  */
485   register struct mproc *rp;
486   vir_bytes addr;
487   int i, pidarg, options, children, waited_for;
488 
489   /* Set internal variables. */
490   pidarg  = m_in.m_lc_pm_wait4.pid;		/* 1st param */
491   options = m_in.m_lc_pm_wait4.options;		/* 3rd param */
492   addr    = m_in.m_lc_pm_wait4.addr;		/* 4th param */
493   if (pidarg == 0) pidarg = -mp->mp_procgrp;	/* pidarg < 0 ==> proc grp */
494 
495   /* Is there a child waiting to be collected? At this point, pidarg != 0:
496    *	pidarg  >  0 means pidarg is pid of a specific process to wait for
497    *	pidarg == -1 means wait for any child
498    *	pidarg  < -1 means wait for any child whose process group = -pidarg
499    */
500   children = 0;
501   for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
502 	if ((rp->mp_flags & (IN_USE | TOLD_PARENT)) != IN_USE) continue;
503 	if (rp->mp_parent != who_p && rp->mp_tracer != who_p) continue;
504 	if (rp->mp_parent != who_p && (rp->mp_flags & ZOMBIE)) continue;
505 
506 	/* The value of pidarg determines which children qualify. */
507 	if (pidarg  > 0 && pidarg != rp->mp_pid) continue;
508 	if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue;
509 
510 	children++;			/* this child is acceptable */
511 
512 	if (rp->mp_tracer == who_p) {
513 		if (rp->mp_flags & TRACE_ZOMBIE) {
514 			/* Traced child meets the pid test and has exited. */
515 			tell_tracer(rp);
516 			check_parent(rp, TRUE /*try_cleanup*/);
517 			return(SUSPEND);
518 		}
519 		if (rp->mp_flags & TRACE_STOPPED) {
520 			/* This child meets the pid test and is being traced.
521 			 * Deliver a signal to the tracer, if any.
522 			 */
523 			for (i = 1; i < _NSIG; i++) {
524 				if (sigismember(&rp->mp_sigtrace, i)) {
525 					/* TODO: rusage support */
526 
527 					sigdelset(&rp->mp_sigtrace, i);
528 
529 					mp->mp_reply.m_pm_lc_wait4.status =
530 					    W_STOPCODE(i);
531 					return(rp->mp_pid);
532 				}
533 			}
534 		}
535 	}
536 
537 	if (rp->mp_parent == who_p) {
538 		if (rp->mp_flags & ZOMBIE) {
539 			/* This child meets the pid test and has exited. */
540 			waited_for = tell_parent(rp, addr);
541 
542 			if (waited_for &&
543 			    !(rp->mp_flags & (VFS_CALL | EVENT_CALL)))
544 				cleanup(rp);
545 			return(SUSPEND);
546 		}
547 	}
548   }
549 
550   /* No qualifying child has exited.  Wait for one, unless none exists. */
551   if (children > 0) {
552 	/* At least 1 child meets the pid test exists, but has not exited. */
553 	if (options & WNOHANG) {
554 		return(0);    /* parent does not want to wait */
555 	}
556 	mp->mp_flags |= WAITING;	     /* parent wants to wait */
557 	mp->mp_wpid = (pid_t) pidarg;	     /* save pid for later */
558 	mp->mp_waddr = addr;		     /* save rusage addr for later */
559 	return(SUSPEND);		     /* do not reply, let it wait */
560   } else {
561 	/* No child even meets the pid test.  Return error immediately. */
562 	return(ECHILD);			     /* no - parent has no children */
563   }
564 }
565 
566 /*===========================================================================*
567  *				wait_test				     *
568  *===========================================================================*/
569 int
570 wait_test(
571 	struct mproc *rmp,			/* process that may be waiting */
572 	struct mproc *child			/* process that may be waited for */
573 )
574 {
575 /* See if a parent or tracer process is waiting for a child process.
576  * A tracer is considered to be a pseudo-parent.
577  */
578   int parent_waiting, right_child;
579   pid_t pidarg;
580 
581   pidarg = rmp->mp_wpid;		/* who's being waited for? */
582   parent_waiting = rmp->mp_flags & WAITING;
583   right_child =				/* child meets one of the 3 tests? */
584   	(pidarg == -1 || pidarg == child->mp_pid ||
585   	 -pidarg == child->mp_procgrp);
586 
587   return (parent_waiting && right_child);
588 }
589 
590 /*===========================================================================*
591  *				zombify					     *
592  *===========================================================================*/
593 static void
594 zombify(struct mproc *rmp)
595 {
596 /* Zombify a process. First check if the exiting process is traced by a process
597  * other than its parent; if so, the tracer must be notified about the exit
598  * first. Once that is done, the real parent may be notified about the exit of
599  * its child.
600  */
601   struct mproc *t_mp;
602 
603   if (rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE))
604 	panic("zombify: process was already a zombie");
605 
606   /* See if we have to notify a tracer process first. */
607   if (rmp->mp_tracer != NO_TRACER && rmp->mp_tracer != rmp->mp_parent) {
608 	rmp->mp_flags |= TRACE_ZOMBIE;
609 
610 	t_mp = &mproc[rmp->mp_tracer];
611 
612 	/* Do not bother sending SIGCHLD signals to tracers. */
613 	if (!wait_test(t_mp, rmp))
614 		return;
615 
616 	tell_tracer(rmp);
617   }
618   else {
619 	rmp->mp_flags |= ZOMBIE;
620   }
621 
622   /* No tracer, or tracer is parent, or tracer has now been notified. */
623   check_parent(rmp, FALSE /*try_cleanup*/);
624 }
625 
626 /*===========================================================================*
627  *				check_parent				     *
628  *===========================================================================*/
629 static void
630 check_parent(
631 	struct mproc *child,			/* tells which process is exiting */
632 	int try_cleanup			/* clean up the child when done? */
633 )
634 {
635 /* We would like to inform the parent of an exiting child about the child's
636  * death. If the parent is waiting for the child, tell it immediately;
637  * otherwise, send it a SIGCHLD signal.
638  *
639  * Note that we may call this function twice on a single child; first with
640  * its original parent, later (if the parent died) with INIT as its parent.
641  */
642   struct mproc *p_mp;
643 
644   p_mp = &mproc[child->mp_parent];
645 
646   if (p_mp->mp_flags & EXITING) {
647 	/* This may trigger if the child of a dead parent dies. The child will
648 	 * be assigned to INIT and rechecked shortly after. Do nothing.
649 	 */
650   }
651   else if (wait_test(p_mp, child)) {
652 	if (!tell_parent(child, p_mp->mp_waddr))
653 		try_cleanup = FALSE; /* child is still there */
654 
655 	/* The 'try_cleanup' flag merely saves us from having to be really
656 	 * careful with statement ordering in exit_proc() and exit_restart().
657 	 */
658 	if (try_cleanup && !(child->mp_flags & (VFS_CALL | EVENT_CALL)))
659 		cleanup(child);
660   }
661   else {
662 	/* Parent is not waiting. */
663 	sig_proc(p_mp, SIGCHLD, TRUE /*trace*/, FALSE /* ksig */);
664   }
665 }
666 
667 /*===========================================================================*
668  *				tell_parent				     *
669  *===========================================================================*/
670 static int tell_parent(struct mproc *child, vir_bytes addr)
671 {
672 /* Tell the parent of the given process that it has terminated, by satisfying
673  * the parent's ongoing wait4() call.  If the parent has requested the child
674  * tree's resource usage, copy that information out first.  The copy may fail;
675  * in that case, the parent's wait4() call will return with an error, but the
676  * child will remain a zombie.  Return TRUE if the child is cleaned up, or
677  * FALSE if the child is still a zombie.
678  */
679   struct rusage r_usage;
680   int mp_parent;
681   struct mproc *parent;
682   int r;
683 
684   mp_parent= child->mp_parent;
685   if (mp_parent <= 0)
686 	panic("tell_parent: bad value in mp_parent: %d", mp_parent);
687   if(!(child->mp_flags & ZOMBIE))
688   	panic("tell_parent: child not a zombie");
689   if(child->mp_flags & TOLD_PARENT)
690 	panic("tell_parent: telling parent again");
691   parent = &mproc[mp_parent];
692 
693   /* See if we need to report resource usage to the parent. */
694   if (addr) {
695 	/* We report only user and system times for now. TODO: support other
696 	 * fields, although this is tricky since the child process is already
697 	 * gone as far as the kernel and other services are concerned..
698 	 */
699 	memset(&r_usage, 0, sizeof(r_usage));
700 	set_rusage_times(&r_usage, child->mp_child_utime,
701 	    child->mp_child_stime);
702 
703 	if ((r = sys_datacopy(SELF, (vir_bytes)&r_usage, parent->mp_endpoint,
704 	    addr, sizeof(r_usage))) != OK) {
705 		reply(child->mp_parent, r);
706 
707 		return FALSE; /* copy error - the child is still there */
708 	}
709   }
710 
711   /* Wake up the parent by sending the reply message. */
712   parent->mp_reply.m_pm_lc_wait4.status =
713 	W_EXITCODE(child->mp_exitstatus, child->mp_sigstatus);
714   reply(child->mp_parent, child->mp_pid);
715   parent->mp_flags &= ~WAITING;		/* parent no longer waiting */
716   child->mp_flags &= ~ZOMBIE;		/* child no longer a zombie */
717   child->mp_flags |= TOLD_PARENT;	/* avoid informing parent twice */
718 
719   /* Now that the child has been waited for, accumulate the times of the
720    * terminated child process at the parent.
721    */
722   parent->mp_child_utime += child->mp_child_utime;
723   parent->mp_child_stime += child->mp_child_stime;
724 
725   return TRUE; /* child has been waited for */
726 }
727 
728 /*===========================================================================*
729  *				tell_tracer				     *
730  *===========================================================================*/
731 static void
732 tell_tracer(
733 	struct mproc *child			/* tells which process is exiting */
734 )
735 {
736   int mp_tracer;
737   struct mproc *tracer;
738 
739   mp_tracer = child->mp_tracer;
740   if (mp_tracer <= 0)
741 	panic("tell_tracer: bad value in mp_tracer: %d", mp_tracer);
742   if(!(child->mp_flags & TRACE_ZOMBIE))
743   	panic("tell_tracer: child not a zombie");
744   tracer = &mproc[mp_tracer];
745 
746   /* TODO: rusage support */
747 
748   tracer->mp_reply.m_pm_lc_wait4.status =
749 	W_EXITCODE(child->mp_exitstatus, (child->mp_sigstatus & 0377));
750   reply(child->mp_tracer, child->mp_pid);
751   tracer->mp_flags &= ~WAITING;		/* tracer no longer waiting */
752   child->mp_flags &= ~TRACE_ZOMBIE;	/* child no longer zombie to tracer */
753   child->mp_flags |= ZOMBIE;		/* child is now zombie to parent */
754 }
755 
756 /*===========================================================================*
757  *				tracer_died				     *
758  *===========================================================================*/
759 static void
760 tracer_died(
761 	struct mproc *child			/* process being traced */
762 )
763 {
764 /* The process that was tracing the given child, has died for some reason.
765  * This is really the tracer's fault, but we can't let INIT deal with this.
766  */
767 
768   child->mp_tracer = NO_TRACER;
769   child->mp_flags &= ~TRACE_EXIT;
770 
771   /* If the tracer died while the child was running or stopped, we have no
772    * idea what state the child is in. Avoid a trainwreck, by killing the child.
773    * Note that this may cause cascading exits.
774    */
775   if (!(child->mp_flags & EXITING)) {
776 	sig_proc(child, SIGKILL, TRUE /*trace*/, FALSE /* ksig */);
777 
778 	return;
779   }
780 
781   /* If the tracer died while the child was telling it about its own death,
782    * forget about the tracer and notify the real parent instead.
783    */
784   if (child->mp_flags & TRACE_ZOMBIE) {
785 	child->mp_flags &= ~TRACE_ZOMBIE;
786 	child->mp_flags |= ZOMBIE;
787 
788 	check_parent(child, TRUE /*try_cleanup*/);
789   }
790 }
791 
792 /*===========================================================================*
793  *				cleanup					     *
794  *===========================================================================*/
795 static void
796 cleanup(
797 	register struct mproc *rmp	/* tells which process is exiting */
798 )
799 {
800   /* Release the process table entry and reinitialize some field. */
801   rmp->mp_pid = 0;
802   rmp->mp_flags = 0;
803   rmp->mp_child_utime = 0;
804   rmp->mp_child_stime = 0;
805   procs_in_use--;
806 }
807 
808