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