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