xref: /minix3/minix/servers/pm/forkexit.c (revision 637f688f0d4ccff0fa8a6ddafcd1d89fdfbc462d)
1433d6423SLionel Sambuc /* This file deals with creating processes (via FORK) and deleting them (via
229346ab0SDavid van Moolenbroek  * EXIT/WAIT4).  When a process forks, a new slot in the 'mproc' table is
3433d6423SLionel Sambuc  * allocated for it, and a copy of the parent's core image is made for the
4433d6423SLionel Sambuc  * child.  Then the kernel and file system are informed.  A process is removed
5433d6423SLionel Sambuc  * from the 'mproc' table when two events have occurred: (1) it has exited or
629346ab0SDavid van Moolenbroek  * been killed by a signal, and (2) the parent has done a WAIT4.  If the
7433d6423SLionel Sambuc  * process exits first, it continues to occupy a slot until the parent does a
829346ab0SDavid van Moolenbroek  * WAIT4.
9433d6423SLionel Sambuc  *
10433d6423SLionel Sambuc  * The entry points into this file are:
11433d6423SLionel Sambuc  *   do_fork:		perform the FORK system call
12433d6423SLionel Sambuc  *   do_srv_fork:	special FORK, used by RS to create sys services
13433d6423SLionel Sambuc  *   do_exit:		perform the EXIT system call (by calling exit_proc())
14433d6423SLionel Sambuc  *   exit_proc:		actually do the exiting, and tell VFS about it
15433d6423SLionel Sambuc  *   exit_restart:	continue exiting a process after VFS has replied
1629346ab0SDavid van Moolenbroek  *   do_wait4:		perform the WAIT4 system call
17433d6423SLionel Sambuc  *   wait_test:		check whether a parent is waiting for a child
18433d6423SLionel Sambuc  */
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc #include "pm.h"
21433d6423SLionel Sambuc #include <sys/wait.h>
22433d6423SLionel Sambuc #include <assert.h>
23433d6423SLionel Sambuc #include <minix/callnr.h>
24433d6423SLionel Sambuc #include <minix/com.h>
25433d6423SLionel Sambuc #include <minix/sched.h>
26433d6423SLionel Sambuc #include <minix/vm.h>
27433d6423SLionel Sambuc #include <sys/ptrace.h>
28433d6423SLionel Sambuc #include <sys/resource.h>
29433d6423SLionel Sambuc #include <signal.h>
30433d6423SLionel Sambuc #include "mproc.h"
31433d6423SLionel Sambuc 
32433d6423SLionel Sambuc #define LAST_FEW            2	/* last few slots reserved for superuser */
33433d6423SLionel Sambuc 
34433d6423SLionel Sambuc static void zombify(struct mproc *rmp);
35433d6423SLionel Sambuc static void check_parent(struct mproc *child, int try_cleanup);
3629346ab0SDavid van Moolenbroek static int tell_parent(struct mproc *child, vir_bytes addr);
37433d6423SLionel Sambuc static void tell_tracer(struct mproc *child);
38433d6423SLionel Sambuc static void tracer_died(struct mproc *child);
39433d6423SLionel Sambuc static void cleanup(register struct mproc *rmp);
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc /*===========================================================================*
42433d6423SLionel Sambuc  *				do_fork					     *
43433d6423SLionel Sambuc  *===========================================================================*/
44*637f688fSRichard Sailer int
do_fork(void)45*637f688fSRichard Sailer do_fork(void)
46433d6423SLionel Sambuc {
47433d6423SLionel Sambuc /* The process pointed to by 'mp' has forked.  Create a child process. */
48433d6423SLionel Sambuc   register struct mproc *rmp;	/* pointer to parent */
49433d6423SLionel Sambuc   register struct mproc *rmc;	/* pointer to child */
50433d6423SLionel Sambuc   pid_t new_pid;
51433d6423SLionel Sambuc   static unsigned int next_child = 0;
52433d6423SLionel Sambuc   int i, n = 0, s;
53433d6423SLionel Sambuc   endpoint_t child_ep;
54433d6423SLionel Sambuc   message m;
55433d6423SLionel Sambuc 
56433d6423SLionel Sambuc  /* If tables might fill up during FORK, don't even start since recovery half
57433d6423SLionel Sambuc   * way through is such a nuisance.
58433d6423SLionel Sambuc   */
59433d6423SLionel Sambuc   rmp = mp;
60433d6423SLionel Sambuc   if ((procs_in_use == NR_PROCS) ||
61433d6423SLionel Sambuc   		(procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
62433d6423SLionel Sambuc   {
63433d6423SLionel Sambuc   	printf("PM: warning, process table is full!\n");
64433d6423SLionel Sambuc   	return(EAGAIN);
65433d6423SLionel Sambuc   }
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc   /* Find a slot in 'mproc' for the child process.  A slot must exist. */
68433d6423SLionel Sambuc   do {
69433d6423SLionel Sambuc         next_child = (next_child+1) % NR_PROCS;
70433d6423SLionel Sambuc 	n++;
71433d6423SLionel Sambuc   } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
72433d6423SLionel Sambuc   if(n > NR_PROCS)
73433d6423SLionel Sambuc 	panic("do_fork can't find child slot");
74433d6423SLionel Sambuc   if(next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE))
75433d6423SLionel Sambuc 	panic("do_fork finds wrong child slot: %d", next_child);
76433d6423SLionel Sambuc 
77433d6423SLionel Sambuc   /* Memory part of the forking. */
78433d6423SLionel Sambuc   if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) {
79433d6423SLionel Sambuc 	return s;
80433d6423SLionel Sambuc   }
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc   /* PM may not fail fork after call to vm_fork(), as VM calls sys_fork(). */
83433d6423SLionel Sambuc 
84433d6423SLionel Sambuc   rmc = &mproc[next_child];
85433d6423SLionel Sambuc   /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
86433d6423SLionel Sambuc   procs_in_use++;
87433d6423SLionel Sambuc   *rmc = *rmp;			/* copy parent's process slot to child's */
886ad322a9SDavid van Moolenbroek   rmc->mp_sigact = mpsigact[next_child];	/* restore mp_sigact ptr */
896ad322a9SDavid van Moolenbroek   memcpy(rmc->mp_sigact, rmp->mp_sigact, sizeof(mpsigact[next_child]));
90433d6423SLionel Sambuc   rmc->mp_parent = who_p;			/* record child's parent */
91433d6423SLionel Sambuc   if (!(rmc->mp_trace_flags & TO_TRACEFORK)) {
92433d6423SLionel Sambuc 	rmc->mp_tracer = NO_TRACER;		/* no tracer attached */
93433d6423SLionel Sambuc 	rmc->mp_trace_flags = 0;
94433d6423SLionel Sambuc 	(void) sigemptyset(&rmc->mp_sigtrace);
95433d6423SLionel Sambuc   }
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc   /* Some system servers like to call regular fork, such as RS spawning
98433d6423SLionel Sambuc    * recovery scripts; in this case PM will take care of their scheduling
99433d6423SLionel Sambuc    * because RS cannot do so for non-system processes */
100433d6423SLionel Sambuc   if (rmc->mp_flags & PRIV_PROC) {
101433d6423SLionel Sambuc 	assert(rmc->mp_scheduler == NONE);
102433d6423SLionel Sambuc 	rmc->mp_scheduler = SCHED_PROC_NR;
103433d6423SLionel Sambuc   }
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc   /* Inherit only these flags. In normal fork(), PRIV_PROC is not inherited. */
106433d6423SLionel Sambuc   rmc->mp_flags &= (IN_USE|DELAY_CALL|TAINTED);
107433d6423SLionel Sambuc   rmc->mp_child_utime = 0;		/* reset administration */
108433d6423SLionel Sambuc   rmc->mp_child_stime = 0;		/* reset administration */
109433d6423SLionel Sambuc   rmc->mp_exitstatus = 0;
110433d6423SLionel Sambuc   rmc->mp_sigstatus = 0;
111433d6423SLionel Sambuc   rmc->mp_endpoint = child_ep;		/* passed back by VM */
112433d6423SLionel Sambuc   for (i = 0; i < NR_ITIMERS; i++)
113433d6423SLionel Sambuc 	rmc->mp_interval[i] = 0;	/* reset timer intervals */
11458be976dSDavid van Moolenbroek   rmc->mp_started = getticks();		/* remember start time, for ps(1) */
115433d6423SLionel Sambuc 
116910831cbSDavid van Moolenbroek   assert(rmc->mp_eventsub == NO_EVENTSUB);
117910831cbSDavid van Moolenbroek 
118433d6423SLionel Sambuc   /* Find a free pid for the child and put it in the table. */
119433d6423SLionel Sambuc   new_pid = get_free_pid();
120433d6423SLionel Sambuc   rmc->mp_pid = new_pid;	/* assign pid to child */
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc   memset(&m, 0, sizeof(m));
123433d6423SLionel Sambuc   m.m_type = VFS_PM_FORK;
124433d6423SLionel Sambuc   m.VFS_PM_ENDPT = rmc->mp_endpoint;
125433d6423SLionel Sambuc   m.VFS_PM_PENDPT = rmp->mp_endpoint;
126433d6423SLionel Sambuc   m.VFS_PM_CPID = rmc->mp_pid;
127433d6423SLionel Sambuc   m.VFS_PM_REUID = -1;	/* Not used by VFS_PM_FORK */
128433d6423SLionel Sambuc   m.VFS_PM_REGID = -1;	/* Not used by VFS_PM_FORK */
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc   tell_vfs(rmc, &m);
131433d6423SLionel Sambuc 
132433d6423SLionel Sambuc   /* Tell the tracer, if any, about the new child */
133433d6423SLionel Sambuc   if (rmc->mp_tracer != NO_TRACER)
134433d6423SLionel Sambuc 	sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
135433d6423SLionel Sambuc 
136433d6423SLionel Sambuc   /* Do not reply until VFS is ready to process the fork
137433d6423SLionel Sambuc   * request
138433d6423SLionel Sambuc   */
139433d6423SLionel Sambuc   return SUSPEND;
140433d6423SLionel Sambuc }
141433d6423SLionel Sambuc 
142433d6423SLionel Sambuc /*===========================================================================*
143433d6423SLionel Sambuc  *				do_srv_fork				     *
144433d6423SLionel Sambuc  *===========================================================================*/
145*637f688fSRichard Sailer int
do_srv_fork(void)146*637f688fSRichard Sailer do_srv_fork(void)
147433d6423SLionel Sambuc {
148433d6423SLionel Sambuc /* The process pointed to by 'mp' has forked.  Create a child process. */
149433d6423SLionel Sambuc   register struct mproc *rmp;	/* pointer to parent */
150433d6423SLionel Sambuc   register struct mproc *rmc;	/* pointer to child */
151433d6423SLionel Sambuc   int s;
152433d6423SLionel Sambuc   pid_t new_pid;
153433d6423SLionel Sambuc   static unsigned int next_child = 0;
154433d6423SLionel Sambuc   int i, n = 0;
155433d6423SLionel Sambuc   endpoint_t child_ep;
156433d6423SLionel Sambuc   message m;
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc   /* Only RS is allowed to use srv_fork. */
159433d6423SLionel Sambuc   if (mp->mp_endpoint != RS_PROC_NR)
160433d6423SLionel Sambuc 	return EPERM;
161433d6423SLionel Sambuc 
162433d6423SLionel Sambuc  /* If tables might fill up during FORK, don't even start since recovery half
163433d6423SLionel Sambuc   * way through is such a nuisance.
164433d6423SLionel Sambuc   */
165433d6423SLionel Sambuc   rmp = mp;
166433d6423SLionel Sambuc   if ((procs_in_use == NR_PROCS) ||
167433d6423SLionel Sambuc   		(procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0))
168433d6423SLionel Sambuc   {
169433d6423SLionel Sambuc   	printf("PM: warning, process table is full!\n");
170433d6423SLionel Sambuc   	return(EAGAIN);
171433d6423SLionel Sambuc   }
172433d6423SLionel Sambuc 
173433d6423SLionel Sambuc   /* Find a slot in 'mproc' for the child process.  A slot must exist. */
174433d6423SLionel Sambuc   do {
175433d6423SLionel Sambuc         next_child = (next_child+1) % NR_PROCS;
176433d6423SLionel Sambuc 	n++;
177433d6423SLionel Sambuc   } while((mproc[next_child].mp_flags & IN_USE) && n <= NR_PROCS);
178433d6423SLionel Sambuc   if(n > NR_PROCS)
179433d6423SLionel Sambuc 	panic("do_fork can't find child slot");
180433d6423SLionel Sambuc   if(next_child >= NR_PROCS || (mproc[next_child].mp_flags & IN_USE))
181433d6423SLionel Sambuc 	panic("do_fork finds wrong child slot: %d", next_child);
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc   if((s=vm_fork(rmp->mp_endpoint, next_child, &child_ep)) != OK) {
184433d6423SLionel Sambuc 	return s;
185433d6423SLionel Sambuc   }
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc   rmc = &mproc[next_child];
188433d6423SLionel Sambuc   /* Set up the child and its memory map; copy its 'mproc' slot from parent. */
189433d6423SLionel Sambuc   procs_in_use++;
190433d6423SLionel Sambuc   *rmc = *rmp;			/* copy parent's process slot to child's */
1916ad322a9SDavid van Moolenbroek   rmc->mp_sigact = mpsigact[next_child];	/* restore mp_sigact ptr */
1926ad322a9SDavid van Moolenbroek   memcpy(rmc->mp_sigact, rmp->mp_sigact, sizeof(mpsigact[next_child]));
193433d6423SLionel Sambuc   rmc->mp_parent = who_p;			/* record child's parent */
194433d6423SLionel Sambuc   if (!(rmc->mp_trace_flags & TO_TRACEFORK)) {
195433d6423SLionel Sambuc 	rmc->mp_tracer = NO_TRACER;		/* no tracer attached */
196433d6423SLionel Sambuc 	rmc->mp_trace_flags = 0;
197433d6423SLionel Sambuc 	(void) sigemptyset(&rmc->mp_sigtrace);
198433d6423SLionel Sambuc   }
199433d6423SLionel Sambuc   /* inherit only these flags */
200433d6423SLionel Sambuc   rmc->mp_flags &= (IN_USE|PRIV_PROC|DELAY_CALL);
201433d6423SLionel Sambuc   rmc->mp_child_utime = 0;		/* reset administration */
202433d6423SLionel Sambuc   rmc->mp_child_stime = 0;		/* reset administration */
203433d6423SLionel Sambuc   rmc->mp_exitstatus = 0;
204433d6423SLionel Sambuc   rmc->mp_sigstatus = 0;
205433d6423SLionel Sambuc   rmc->mp_endpoint = child_ep;		/* passed back by VM */
206433d6423SLionel Sambuc   rmc->mp_realuid = m_in.m_lsys_pm_srv_fork.uid;
207433d6423SLionel Sambuc   rmc->mp_effuid = m_in.m_lsys_pm_srv_fork.uid;
2081122b286SDavid van Moolenbroek   rmc->mp_svuid = m_in.m_lsys_pm_srv_fork.uid;
209433d6423SLionel Sambuc   rmc->mp_realgid = m_in.m_lsys_pm_srv_fork.gid;
210433d6423SLionel Sambuc   rmc->mp_effgid = m_in.m_lsys_pm_srv_fork.gid;
2111122b286SDavid van Moolenbroek   rmc->mp_svgid = m_in.m_lsys_pm_srv_fork.gid;
212433d6423SLionel Sambuc   for (i = 0; i < NR_ITIMERS; i++)
213433d6423SLionel Sambuc 	rmc->mp_interval[i] = 0;	/* reset timer intervals */
21458be976dSDavid van Moolenbroek   rmc->mp_started = getticks();		/* remember start time, for ps(1) */
215433d6423SLionel Sambuc 
216910831cbSDavid van Moolenbroek   assert(rmc->mp_eventsub == NO_EVENTSUB);
217910831cbSDavid van Moolenbroek 
218433d6423SLionel Sambuc   /* Find a free pid for the child and put it in the table. */
219433d6423SLionel Sambuc   new_pid = get_free_pid();
220433d6423SLionel Sambuc   rmc->mp_pid = new_pid;	/* assign pid to child */
221433d6423SLionel Sambuc 
222433d6423SLionel Sambuc   memset(&m, 0, sizeof(m));
223433d6423SLionel Sambuc   m.m_type = VFS_PM_SRV_FORK;
224433d6423SLionel Sambuc   m.VFS_PM_ENDPT = rmc->mp_endpoint;
225433d6423SLionel Sambuc   m.VFS_PM_PENDPT = rmp->mp_endpoint;
226433d6423SLionel Sambuc   m.VFS_PM_CPID = rmc->mp_pid;
227433d6423SLionel Sambuc   m.VFS_PM_REUID = m_in.m_lsys_pm_srv_fork.uid;
228433d6423SLionel Sambuc   m.VFS_PM_REGID = m_in.m_lsys_pm_srv_fork.gid;
229433d6423SLionel Sambuc 
230433d6423SLionel Sambuc   tell_vfs(rmc, &m);
231433d6423SLionel Sambuc 
232433d6423SLionel Sambuc   /* Tell the tracer, if any, about the new child */
233433d6423SLionel Sambuc   if (rmc->mp_tracer != NO_TRACER)
234433d6423SLionel Sambuc 	sig_proc(rmc, SIGSTOP, TRUE /*trace*/, FALSE /* ksig */);
235433d6423SLionel Sambuc 
236433d6423SLionel Sambuc   /* Wakeup the newly created process */
237433d6423SLionel Sambuc   reply(rmc-mproc, OK);
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc   return rmc->mp_pid;
240433d6423SLionel Sambuc }
241433d6423SLionel Sambuc 
242433d6423SLionel Sambuc /*===========================================================================*
243433d6423SLionel Sambuc  *				do_exit					     *
244433d6423SLionel Sambuc  *===========================================================================*/
245*637f688fSRichard Sailer int
do_exit(void)246*637f688fSRichard Sailer do_exit(void)
247433d6423SLionel Sambuc {
248433d6423SLionel Sambuc  /* Perform the exit(status) system call. The real work is done by exit_proc(),
249433d6423SLionel Sambuc   * which is also called when a process is killed by a signal. System processes
250433d6423SLionel Sambuc   * do not use PM's exit() to terminate. If they try to, we warn the user
251433d6423SLionel Sambuc   * and send a SIGKILL signal to the system process.
252433d6423SLionel Sambuc   */
253433d6423SLionel Sambuc   if(mp->mp_flags & PRIV_PROC) {
254433d6423SLionel Sambuc       printf("PM: system process %d (%s) tries to exit(), sending SIGKILL\n",
255433d6423SLionel Sambuc           mp->mp_endpoint, mp->mp_name);
256433d6423SLionel Sambuc       sys_kill(mp->mp_endpoint, SIGKILL);
257433d6423SLionel Sambuc   }
258433d6423SLionel Sambuc   else {
259433d6423SLionel Sambuc       exit_proc(mp, m_in.m_lc_pm_exit.status, FALSE /*dump_core*/);
260433d6423SLionel Sambuc   }
261433d6423SLionel Sambuc   return(SUSPEND);		/* can't communicate from beyond the grave */
262433d6423SLionel Sambuc }
263433d6423SLionel Sambuc 
264433d6423SLionel Sambuc /*===========================================================================*
265433d6423SLionel Sambuc  *				exit_proc				     *
266433d6423SLionel Sambuc  *===========================================================================*/
267*637f688fSRichard Sailer void
exit_proc(register struct mproc * rmp,int exit_status,int dump_core)268*637f688fSRichard Sailer exit_proc(
269*637f688fSRichard Sailer 	register struct mproc *rmp,	/* pointer to the process to be terminated */
270*637f688fSRichard Sailer 	int exit_status,		/* the process' exit status (for parent) */
271*637f688fSRichard Sailer 	int dump_core			/* flag indicating whether to dump core */
272*637f688fSRichard Sailer )
273433d6423SLionel Sambuc {
274433d6423SLionel Sambuc /* A process is done.  Release most of the process' possessions.  If its
275433d6423SLionel Sambuc  * parent is waiting, release the rest, else keep the process slot and
276433d6423SLionel Sambuc  * become a zombie.
277433d6423SLionel Sambuc  */
278433d6423SLionel Sambuc   register int proc_nr, proc_nr_e;
279433d6423SLionel Sambuc   int r;
280433d6423SLionel Sambuc   pid_t procgrp;
281433d6423SLionel Sambuc   clock_t user_time, sys_time;
282433d6423SLionel Sambuc   message m;
283433d6423SLionel Sambuc 
284433d6423SLionel Sambuc   /* Do not create core files for set uid execution */
285433d6423SLionel Sambuc   if (dump_core && rmp->mp_realuid != rmp->mp_effuid)
286433d6423SLionel Sambuc 	dump_core = FALSE;
287433d6423SLionel Sambuc 
288433d6423SLionel Sambuc   /* System processes are destroyed before informing VFS, meaning that VFS can
289433d6423SLionel Sambuc    * not get their CPU state, so we can't generate a coredump for them either.
290433d6423SLionel Sambuc    */
291433d6423SLionel Sambuc   if (dump_core && (rmp->mp_flags & PRIV_PROC))
292433d6423SLionel Sambuc 	dump_core = FALSE;
293433d6423SLionel Sambuc 
294433d6423SLionel Sambuc   proc_nr = (int) (rmp - mproc);	/* get process slot number */
295433d6423SLionel Sambuc   proc_nr_e = rmp->mp_endpoint;
296433d6423SLionel Sambuc 
297433d6423SLionel Sambuc   /* Remember a session leader's process group. */
298433d6423SLionel Sambuc   procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0;
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc   /* If the exited process has a timer pending, kill it. */
301433d6423SLionel Sambuc   if (rmp->mp_flags & ALARM_ON) set_alarm(rmp, (clock_t) 0);
302433d6423SLionel Sambuc 
3030f5c95a0SDavid van Moolenbroek   /* Do accounting: fetch usage times and save with dead child process.
3040f5c95a0SDavid van Moolenbroek    * POSIX forbids accumulation at parent until child has been waited for.
3050f5c95a0SDavid van Moolenbroek    */
306433d6423SLionel Sambuc   if((r=sys_times(proc_nr_e, &user_time, &sys_time, NULL, NULL)) != OK)
307433d6423SLionel Sambuc   	panic("exit_proc: sys_times failed: %d", r);
3080f5c95a0SDavid van Moolenbroek   rmp->mp_child_utime += user_time;		/* add user time */
3090f5c95a0SDavid van Moolenbroek   rmp->mp_child_stime += sys_time;		/* add system time */
310433d6423SLionel Sambuc 
311433d6423SLionel Sambuc   /* Tell the kernel the process is no longer runnable to prevent it from
312433d6423SLionel Sambuc    * being scheduled in between the following steps. Then tell VFS that it
313433d6423SLionel Sambuc    * the process has exited and finally, clean up the process at the kernel.
314433d6423SLionel Sambuc    * This order is important so that VFS can tell drivers to cancel requests
315433d6423SLionel Sambuc    * such as copying to/ from the exiting process, before it is gone.
316433d6423SLionel Sambuc    */
317433d6423SLionel Sambuc   /* If the process is not yet stopped, we force a stop here. This means that
318433d6423SLionel Sambuc    * the process may still have a delay call pending. For this reason, the main
319433d6423SLionel Sambuc    * message loop discards requests from exiting processes.
320910831cbSDavid van Moolenbroek    *
321910831cbSDavid van Moolenbroek    * TODO: make the kernel discard delayed calls upon forced stops for exits,
322910831cbSDavid van Moolenbroek    * so that no service needs to deal with this.  Right now it appears that the
323910831cbSDavid van Moolenbroek    * only thing preventing problems with other services is the fact that
324910831cbSDavid van Moolenbroek    * regular messages are prioritized over asynchronous messages.
325433d6423SLionel Sambuc    */
326433d6423SLionel Sambuc   if (!(rmp->mp_flags & PROC_STOPPED)) {
327433d6423SLionel Sambuc 	if ((r = sys_stop(proc_nr_e)) != OK)		/* stop the process */
328433d6423SLionel Sambuc 		panic("sys_stop failed: %d", r);
329433d6423SLionel Sambuc 	rmp->mp_flags |= PROC_STOPPED;
330433d6423SLionel Sambuc   }
331433d6423SLionel Sambuc 
332433d6423SLionel Sambuc   if((r=vm_willexit(proc_nr_e)) != OK) {
333433d6423SLionel Sambuc 	panic("exit_proc: vm_willexit failed: %d", r);
334433d6423SLionel Sambuc   }
335910831cbSDavid van Moolenbroek 
336433d6423SLionel Sambuc   if (proc_nr_e == INIT_PROC_NR)
337433d6423SLionel Sambuc   {
338cd34841dSBen Gras 	printf("PM: INIT died with exit status %d; showing stacktrace\n", exit_status);
339cd34841dSBen Gras 	sys_diagctl_stacktrace(proc_nr_e);
340433d6423SLionel Sambuc 	return;
341433d6423SLionel Sambuc   }
342433d6423SLionel Sambuc   if (proc_nr_e == VFS_PROC_NR)
343433d6423SLionel Sambuc   {
344433d6423SLionel Sambuc 	panic("exit_proc: VFS died: %d", r);
345433d6423SLionel Sambuc   }
346433d6423SLionel Sambuc 
347910831cbSDavid van Moolenbroek   /* Tell VFS, and after that any matching process event subscribers, about the
348910831cbSDavid van Moolenbroek    * exiting process.
349910831cbSDavid van Moolenbroek    */
350433d6423SLionel Sambuc   memset(&m, 0, sizeof(m));
351433d6423SLionel Sambuc   m.m_type = dump_core ? VFS_PM_DUMPCORE : VFS_PM_EXIT;
352433d6423SLionel Sambuc   m.VFS_PM_ENDPT = rmp->mp_endpoint;
353433d6423SLionel Sambuc 
354433d6423SLionel Sambuc   if (dump_core) {
355433d6423SLionel Sambuc 	m.VFS_PM_TERM_SIG = rmp->mp_sigstatus;
356433d6423SLionel Sambuc 	m.VFS_PM_PATH = rmp->mp_name;
357433d6423SLionel Sambuc   }
358433d6423SLionel Sambuc 
359433d6423SLionel Sambuc   tell_vfs(rmp, &m);
360433d6423SLionel Sambuc 
361433d6423SLionel Sambuc   if (rmp->mp_flags & PRIV_PROC)
362433d6423SLionel Sambuc   {
363433d6423SLionel Sambuc 	/* Destroy system processes without waiting for VFS. This is
364433d6423SLionel Sambuc 	 * needed because the system process might be a block device
365433d6423SLionel Sambuc 	 * driver that VFS is blocked waiting on.
366433d6423SLionel Sambuc 	 */
367433d6423SLionel Sambuc 	if((r= sys_clear(rmp->mp_endpoint)) != OK)
368433d6423SLionel Sambuc 		panic("exit_proc: sys_clear failed: %d", r);
369433d6423SLionel Sambuc   }
370433d6423SLionel Sambuc 
371433d6423SLionel Sambuc   /* Clean up most of the flags describing the process's state before the exit,
372433d6423SLionel Sambuc    * and mark it as exiting.
373433d6423SLionel Sambuc    */
374433d6423SLionel Sambuc   rmp->mp_flags &= (IN_USE|VFS_CALL|PRIV_PROC|TRACE_EXIT|PROC_STOPPED);
375433d6423SLionel Sambuc   rmp->mp_flags |= EXITING;
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc   /* Keep the process around until VFS is finished with it. */
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc   rmp->mp_exitstatus = (char) exit_status;
380433d6423SLionel Sambuc 
381433d6423SLionel Sambuc   /* For normal exits, try to notify the parent as soon as possible.
382433d6423SLionel Sambuc    * For core dumps, notify the parent only once the core dump has been made.
383433d6423SLionel Sambuc    */
384433d6423SLionel Sambuc   if (!dump_core)
385433d6423SLionel Sambuc 	zombify(rmp);
386433d6423SLionel Sambuc 
387433d6423SLionel Sambuc   /* If the process has children, disinherit them.  INIT is the new parent. */
388433d6423SLionel Sambuc   for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) {
389433d6423SLionel Sambuc 	if (!(rmp->mp_flags & IN_USE)) continue;
390433d6423SLionel Sambuc 	if (rmp->mp_tracer == proc_nr) {
391433d6423SLionel Sambuc 		/* This child's tracer died. Do something sensible. */
392433d6423SLionel Sambuc 		tracer_died(rmp);
393433d6423SLionel Sambuc 	}
394433d6423SLionel Sambuc 	if (rmp->mp_parent == proc_nr) {
395433d6423SLionel Sambuc 		/* 'rmp' now points to a child to be disinherited. */
396433d6423SLionel Sambuc 		rmp->mp_parent = INIT_PROC_NR;
397433d6423SLionel Sambuc 
398433d6423SLionel Sambuc 		/* If the process is making a VFS call, remember that we set
399433d6423SLionel Sambuc 		 * a new parent. This prevents FORK from replying to the wrong
400433d6423SLionel Sambuc 		 * parent upon completion.
401433d6423SLionel Sambuc 		 */
402433d6423SLionel Sambuc 		if (rmp->mp_flags & VFS_CALL)
403433d6423SLionel Sambuc 			rmp->mp_flags |= NEW_PARENT;
404433d6423SLionel Sambuc 
405433d6423SLionel Sambuc 		/* Notify new parent. */
406433d6423SLionel Sambuc 		if (rmp->mp_flags & ZOMBIE)
407433d6423SLionel Sambuc 			check_parent(rmp, TRUE /*try_cleanup*/);
408433d6423SLionel Sambuc 	}
409433d6423SLionel Sambuc   }
410433d6423SLionel Sambuc 
411433d6423SLionel Sambuc   /* Send a hangup to the process' process group if it was a session leader. */
412433d6423SLionel Sambuc   if (procgrp != 0) check_sig(-procgrp, SIGHUP, FALSE /* ksig */);
413433d6423SLionel Sambuc }
414433d6423SLionel Sambuc 
415433d6423SLionel Sambuc /*===========================================================================*
416433d6423SLionel Sambuc  *				exit_restart				     *
417433d6423SLionel Sambuc  *===========================================================================*/
exit_restart(struct mproc * rmp)418910831cbSDavid van Moolenbroek void exit_restart(struct mproc *rmp)
419433d6423SLionel Sambuc {
420433d6423SLionel Sambuc /* VFS replied to our exit or coredump request. Perform the second half of the
421433d6423SLionel Sambuc  * exit code.
422433d6423SLionel Sambuc  */
423433d6423SLionel Sambuc   int r;
424433d6423SLionel Sambuc 
425433d6423SLionel Sambuc   if((r = sched_stop(rmp->mp_scheduler, rmp->mp_endpoint)) != OK) {
426433d6423SLionel Sambuc  	/* If the scheduler refuses to give up scheduling, there is
427433d6423SLionel Sambuc 	 * little we can do, except report it. This may cause problems
428433d6423SLionel Sambuc 	 * later on, if this scheduler is asked to schedule another proc
429433d6423SLionel Sambuc 	 * that has an endpoint->schedproc mapping identical to the proc
430433d6423SLionel Sambuc 	 * we just tried to stop scheduling.
431433d6423SLionel Sambuc 	*/
432433d6423SLionel Sambuc 	printf("PM: The scheduler did not want to give up "
433433d6423SLionel Sambuc 		"scheduling %s, ret=%d.\n", rmp->mp_name, r);
434433d6423SLionel Sambuc   }
435433d6423SLionel Sambuc 
436433d6423SLionel Sambuc   /* sched_stop is either called when the process is exiting or it is
437433d6423SLionel Sambuc    * being moved between schedulers. If it is being moved between
438433d6423SLionel Sambuc    * schedulers, we need to set the mp_scheduler to NONE so that PM
439433d6423SLionel Sambuc    * doesn't forward messages to the process' scheduler while being moved
440433d6423SLionel Sambuc    * (such as sched_nice). */
441433d6423SLionel Sambuc   rmp->mp_scheduler = NONE;
442433d6423SLionel Sambuc 
443433d6423SLionel Sambuc   /* For core dumps, now is the right time to try to contact the parent. */
444910831cbSDavid van Moolenbroek   if (!(rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE | TOLD_PARENT)))
445433d6423SLionel Sambuc 	zombify(rmp);
446433d6423SLionel Sambuc 
447433d6423SLionel Sambuc   if (!(rmp->mp_flags & PRIV_PROC))
448433d6423SLionel Sambuc   {
449433d6423SLionel Sambuc 	/* destroy the (user) process */
450433d6423SLionel Sambuc 	if((r=sys_clear(rmp->mp_endpoint)) != OK)
451433d6423SLionel Sambuc 		panic("exit_restart: sys_clear failed: %d", r);
452433d6423SLionel Sambuc   }
453433d6423SLionel Sambuc 
454433d6423SLionel Sambuc   /* Release the memory occupied by the child. */
455433d6423SLionel Sambuc   if((r=vm_exit(rmp->mp_endpoint)) != OK) {
456433d6423SLionel Sambuc   	panic("exit_restart: vm_exit failed: %d", r);
457433d6423SLionel Sambuc   }
458433d6423SLionel Sambuc 
459433d6423SLionel Sambuc   if (rmp->mp_flags & TRACE_EXIT)
460433d6423SLionel Sambuc   {
461433d6423SLionel Sambuc 	/* Wake up the tracer, completing the ptrace(T_EXIT) call */
462433d6423SLionel Sambuc 	mproc[rmp->mp_tracer].mp_reply.m_pm_lc_ptrace.data = 0;
463433d6423SLionel Sambuc 	reply(rmp->mp_tracer, OK);
464433d6423SLionel Sambuc   }
465433d6423SLionel Sambuc 
466433d6423SLionel Sambuc   /* Clean up if the parent has collected the exit status */
467433d6423SLionel Sambuc   if (rmp->mp_flags & TOLD_PARENT)
468433d6423SLionel Sambuc 	cleanup(rmp);
469433d6423SLionel Sambuc }
470433d6423SLionel Sambuc 
471433d6423SLionel Sambuc /*===========================================================================*
47229346ab0SDavid van Moolenbroek  *				do_wait4				     *
473433d6423SLionel Sambuc  *===========================================================================*/
474*637f688fSRichard Sailer int
do_wait4(void)475*637f688fSRichard Sailer do_wait4(void)
476433d6423SLionel Sambuc {
477433d6423SLionel Sambuc /* A process wants to wait for a child to terminate. If a child is already
47829346ab0SDavid van Moolenbroek  * waiting, go clean it up and let this WAIT4 call terminate.  Otherwise,
479433d6423SLionel Sambuc  * really wait.
48029346ab0SDavid van Moolenbroek  * A process calling WAIT4 never gets a reply in the usual way at the end
481433d6423SLionel Sambuc  * of the main loop (unless WNOHANG is set or no qualifying child exists).
482433d6423SLionel Sambuc  * If a child has already exited, the routine tell_parent() sends the reply
483433d6423SLionel Sambuc  * to awaken the caller.
484433d6423SLionel Sambuc  */
485433d6423SLionel Sambuc   register struct mproc *rp;
48629346ab0SDavid van Moolenbroek   vir_bytes addr;
48729346ab0SDavid van Moolenbroek   int i, pidarg, options, children, waited_for;
488433d6423SLionel Sambuc 
489433d6423SLionel Sambuc   /* Set internal variables. */
49029346ab0SDavid van Moolenbroek   pidarg  = m_in.m_lc_pm_wait4.pid;		/* 1st param */
49129346ab0SDavid van Moolenbroek   options = m_in.m_lc_pm_wait4.options;		/* 3rd param */
49229346ab0SDavid van Moolenbroek   addr    = m_in.m_lc_pm_wait4.addr;		/* 4th param */
493433d6423SLionel Sambuc   if (pidarg == 0) pidarg = -mp->mp_procgrp;	/* pidarg < 0 ==> proc grp */
494433d6423SLionel Sambuc 
495433d6423SLionel Sambuc   /* Is there a child waiting to be collected? At this point, pidarg != 0:
496433d6423SLionel Sambuc    *	pidarg  >  0 means pidarg is pid of a specific process to wait for
497433d6423SLionel Sambuc    *	pidarg == -1 means wait for any child
498433d6423SLionel Sambuc    *	pidarg  < -1 means wait for any child whose process group = -pidarg
499433d6423SLionel Sambuc    */
500433d6423SLionel Sambuc   children = 0;
501433d6423SLionel Sambuc   for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) {
502433d6423SLionel Sambuc 	if ((rp->mp_flags & (IN_USE | TOLD_PARENT)) != IN_USE) continue;
503433d6423SLionel Sambuc 	if (rp->mp_parent != who_p && rp->mp_tracer != who_p) continue;
504433d6423SLionel Sambuc 	if (rp->mp_parent != who_p && (rp->mp_flags & ZOMBIE)) continue;
505433d6423SLionel Sambuc 
506433d6423SLionel Sambuc 	/* The value of pidarg determines which children qualify. */
507433d6423SLionel Sambuc 	if (pidarg  > 0 && pidarg != rp->mp_pid) continue;
508433d6423SLionel Sambuc 	if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue;
509433d6423SLionel Sambuc 
510433d6423SLionel Sambuc 	children++;			/* this child is acceptable */
511433d6423SLionel Sambuc 
512433d6423SLionel Sambuc 	if (rp->mp_tracer == who_p) {
513433d6423SLionel Sambuc 		if (rp->mp_flags & TRACE_ZOMBIE) {
514433d6423SLionel Sambuc 			/* Traced child meets the pid test and has exited. */
515433d6423SLionel Sambuc 			tell_tracer(rp);
516433d6423SLionel Sambuc 			check_parent(rp, TRUE /*try_cleanup*/);
517433d6423SLionel Sambuc 			return(SUSPEND);
518433d6423SLionel Sambuc 		}
519433d6423SLionel Sambuc 		if (rp->mp_flags & TRACE_STOPPED) {
520433d6423SLionel Sambuc 			/* This child meets the pid test and is being traced.
521433d6423SLionel Sambuc 			 * Deliver a signal to the tracer, if any.
522433d6423SLionel Sambuc 			 */
523433d6423SLionel Sambuc 			for (i = 1; i < _NSIG; i++) {
524433d6423SLionel Sambuc 				if (sigismember(&rp->mp_sigtrace, i)) {
52529346ab0SDavid van Moolenbroek 					/* TODO: rusage support */
52629346ab0SDavid van Moolenbroek 
527433d6423SLionel Sambuc 					sigdelset(&rp->mp_sigtrace, i);
528433d6423SLionel Sambuc 
52929346ab0SDavid van Moolenbroek 					mp->mp_reply.m_pm_lc_wait4.status =
53029346ab0SDavid van Moolenbroek 					    W_STOPCODE(i);
531433d6423SLionel Sambuc 					return(rp->mp_pid);
532433d6423SLionel Sambuc 				}
533433d6423SLionel Sambuc 			}
534433d6423SLionel Sambuc 		}
535433d6423SLionel Sambuc 	}
536433d6423SLionel Sambuc 
537433d6423SLionel Sambuc 	if (rp->mp_parent == who_p) {
538433d6423SLionel Sambuc 		if (rp->mp_flags & ZOMBIE) {
539433d6423SLionel Sambuc 			/* This child meets the pid test and has exited. */
54029346ab0SDavid van Moolenbroek 			waited_for = tell_parent(rp, addr);
54129346ab0SDavid van Moolenbroek 
542910831cbSDavid van Moolenbroek 			if (waited_for &&
543910831cbSDavid van Moolenbroek 			    !(rp->mp_flags & (VFS_CALL | EVENT_CALL)))
544433d6423SLionel Sambuc 				cleanup(rp);
545433d6423SLionel Sambuc 			return(SUSPEND);
546433d6423SLionel Sambuc 		}
547433d6423SLionel Sambuc 	}
548433d6423SLionel Sambuc   }
549433d6423SLionel Sambuc 
550433d6423SLionel Sambuc   /* No qualifying child has exited.  Wait for one, unless none exists. */
551433d6423SLionel Sambuc   if (children > 0) {
552433d6423SLionel Sambuc 	/* At least 1 child meets the pid test exists, but has not exited. */
553433d6423SLionel Sambuc 	if (options & WNOHANG) {
554433d6423SLionel Sambuc 		return(0);    /* parent does not want to wait */
555433d6423SLionel Sambuc 	}
556433d6423SLionel Sambuc 	mp->mp_flags |= WAITING;	     /* parent wants to wait */
557433d6423SLionel Sambuc 	mp->mp_wpid = (pid_t) pidarg;	     /* save pid for later */
55829346ab0SDavid van Moolenbroek 	mp->mp_waddr = addr;		     /* save rusage addr for later */
559433d6423SLionel Sambuc 	return(SUSPEND);		     /* do not reply, let it wait */
560433d6423SLionel Sambuc   } else {
561433d6423SLionel Sambuc 	/* No child even meets the pid test.  Return error immediately. */
562433d6423SLionel Sambuc 	return(ECHILD);			     /* no - parent has no children */
563433d6423SLionel Sambuc   }
564433d6423SLionel Sambuc }
565433d6423SLionel Sambuc 
566433d6423SLionel Sambuc /*===========================================================================*
567433d6423SLionel Sambuc  *				wait_test				     *
568433d6423SLionel Sambuc  *===========================================================================*/
569*637f688fSRichard Sailer int
wait_test(struct mproc * rmp,struct mproc * child)570*637f688fSRichard Sailer wait_test(
571*637f688fSRichard Sailer 	struct mproc *rmp,			/* process that may be waiting */
572*637f688fSRichard Sailer 	struct mproc *child			/* process that may be waited for */
573*637f688fSRichard Sailer )
574433d6423SLionel Sambuc {
575433d6423SLionel Sambuc /* See if a parent or tracer process is waiting for a child process.
576433d6423SLionel Sambuc  * A tracer is considered to be a pseudo-parent.
577433d6423SLionel Sambuc  */
578433d6423SLionel Sambuc   int parent_waiting, right_child;
579433d6423SLionel Sambuc   pid_t pidarg;
580433d6423SLionel Sambuc 
581433d6423SLionel Sambuc   pidarg = rmp->mp_wpid;		/* who's being waited for? */
582433d6423SLionel Sambuc   parent_waiting = rmp->mp_flags & WAITING;
583433d6423SLionel Sambuc   right_child =				/* child meets one of the 3 tests? */
584433d6423SLionel Sambuc   	(pidarg == -1 || pidarg == child->mp_pid ||
585433d6423SLionel Sambuc   	 -pidarg == child->mp_procgrp);
586433d6423SLionel Sambuc 
587433d6423SLionel Sambuc   return (parent_waiting && right_child);
588433d6423SLionel Sambuc }
589433d6423SLionel Sambuc 
590433d6423SLionel Sambuc /*===========================================================================*
591433d6423SLionel Sambuc  *				zombify					     *
592433d6423SLionel Sambuc  *===========================================================================*/
593*637f688fSRichard Sailer static void
zombify(struct mproc * rmp)594*637f688fSRichard Sailer zombify(struct mproc *rmp)
595433d6423SLionel Sambuc {
596433d6423SLionel Sambuc /* Zombify a process. First check if the exiting process is traced by a process
597433d6423SLionel Sambuc  * other than its parent; if so, the tracer must be notified about the exit
598433d6423SLionel Sambuc  * first. Once that is done, the real parent may be notified about the exit of
599433d6423SLionel Sambuc  * its child.
600433d6423SLionel Sambuc  */
601433d6423SLionel Sambuc   struct mproc *t_mp;
602433d6423SLionel Sambuc 
603433d6423SLionel Sambuc   if (rmp->mp_flags & (TRACE_ZOMBIE | ZOMBIE))
604433d6423SLionel Sambuc 	panic("zombify: process was already a zombie");
605433d6423SLionel Sambuc 
606433d6423SLionel Sambuc   /* See if we have to notify a tracer process first. */
607433d6423SLionel Sambuc   if (rmp->mp_tracer != NO_TRACER && rmp->mp_tracer != rmp->mp_parent) {
608433d6423SLionel Sambuc 	rmp->mp_flags |= TRACE_ZOMBIE;
609433d6423SLionel Sambuc 
610433d6423SLionel Sambuc 	t_mp = &mproc[rmp->mp_tracer];
611433d6423SLionel Sambuc 
612433d6423SLionel Sambuc 	/* Do not bother sending SIGCHLD signals to tracers. */
613433d6423SLionel Sambuc 	if (!wait_test(t_mp, rmp))
614433d6423SLionel Sambuc 		return;
615433d6423SLionel Sambuc 
616433d6423SLionel Sambuc 	tell_tracer(rmp);
617433d6423SLionel Sambuc   }
618433d6423SLionel Sambuc   else {
619433d6423SLionel Sambuc 	rmp->mp_flags |= ZOMBIE;
620433d6423SLionel Sambuc   }
621433d6423SLionel Sambuc 
622433d6423SLionel Sambuc   /* No tracer, or tracer is parent, or tracer has now been notified. */
623433d6423SLionel Sambuc   check_parent(rmp, FALSE /*try_cleanup*/);
624433d6423SLionel Sambuc }
625433d6423SLionel Sambuc 
626433d6423SLionel Sambuc /*===========================================================================*
627433d6423SLionel Sambuc  *				check_parent				     *
628433d6423SLionel Sambuc  *===========================================================================*/
629*637f688fSRichard Sailer static void
check_parent(struct mproc * child,int try_cleanup)630*637f688fSRichard Sailer check_parent(
631*637f688fSRichard Sailer 	struct mproc *child,			/* tells which process is exiting */
632*637f688fSRichard Sailer 	int try_cleanup			/* clean up the child when done? */
633*637f688fSRichard Sailer )
634433d6423SLionel Sambuc {
635433d6423SLionel Sambuc /* We would like to inform the parent of an exiting child about the child's
636433d6423SLionel Sambuc  * death. If the parent is waiting for the child, tell it immediately;
637433d6423SLionel Sambuc  * otherwise, send it a SIGCHLD signal.
638433d6423SLionel Sambuc  *
639433d6423SLionel Sambuc  * Note that we may call this function twice on a single child; first with
640433d6423SLionel Sambuc  * its original parent, later (if the parent died) with INIT as its parent.
641433d6423SLionel Sambuc  */
642433d6423SLionel Sambuc   struct mproc *p_mp;
643433d6423SLionel Sambuc 
644433d6423SLionel Sambuc   p_mp = &mproc[child->mp_parent];
645433d6423SLionel Sambuc 
646433d6423SLionel Sambuc   if (p_mp->mp_flags & EXITING) {
647433d6423SLionel Sambuc 	/* This may trigger if the child of a dead parent dies. The child will
648433d6423SLionel Sambuc 	 * be assigned to INIT and rechecked shortly after. Do nothing.
649433d6423SLionel Sambuc 	 */
650433d6423SLionel Sambuc   }
651433d6423SLionel Sambuc   else if (wait_test(p_mp, child)) {
65229346ab0SDavid van Moolenbroek 	if (!tell_parent(child, p_mp->mp_waddr))
65329346ab0SDavid van Moolenbroek 		try_cleanup = FALSE; /* child is still there */
654433d6423SLionel Sambuc 
655433d6423SLionel Sambuc 	/* The 'try_cleanup' flag merely saves us from having to be really
656433d6423SLionel Sambuc 	 * careful with statement ordering in exit_proc() and exit_restart().
657433d6423SLionel Sambuc 	 */
658910831cbSDavid van Moolenbroek 	if (try_cleanup && !(child->mp_flags & (VFS_CALL | EVENT_CALL)))
659433d6423SLionel Sambuc 		cleanup(child);
660433d6423SLionel Sambuc   }
661433d6423SLionel Sambuc   else {
662433d6423SLionel Sambuc 	/* Parent is not waiting. */
663433d6423SLionel Sambuc 	sig_proc(p_mp, SIGCHLD, TRUE /*trace*/, FALSE /* ksig */);
664433d6423SLionel Sambuc   }
665433d6423SLionel Sambuc }
666433d6423SLionel Sambuc 
667433d6423SLionel Sambuc /*===========================================================================*
668433d6423SLionel Sambuc  *				tell_parent				     *
669433d6423SLionel Sambuc  *===========================================================================*/
tell_parent(struct mproc * child,vir_bytes addr)67029346ab0SDavid van Moolenbroek static int tell_parent(struct mproc *child, vir_bytes addr)
671433d6423SLionel Sambuc {
67229346ab0SDavid van Moolenbroek /* Tell the parent of the given process that it has terminated, by satisfying
67329346ab0SDavid van Moolenbroek  * the parent's ongoing wait4() call.  If the parent has requested the child
67429346ab0SDavid van Moolenbroek  * tree's resource usage, copy that information out first.  The copy may fail;
67529346ab0SDavid van Moolenbroek  * in that case, the parent's wait4() call will return with an error, but the
67629346ab0SDavid van Moolenbroek  * child will remain a zombie.  Return TRUE if the child is cleaned up, or
67729346ab0SDavid van Moolenbroek  * FALSE if the child is still a zombie.
67829346ab0SDavid van Moolenbroek  */
67929346ab0SDavid van Moolenbroek   struct rusage r_usage;
680433d6423SLionel Sambuc   int mp_parent;
681433d6423SLionel Sambuc   struct mproc *parent;
68229346ab0SDavid van Moolenbroek   int r;
683433d6423SLionel Sambuc 
684433d6423SLionel Sambuc   mp_parent= child->mp_parent;
685433d6423SLionel Sambuc   if (mp_parent <= 0)
686433d6423SLionel Sambuc 	panic("tell_parent: bad value in mp_parent: %d", mp_parent);
687433d6423SLionel Sambuc   if(!(child->mp_flags & ZOMBIE))
688433d6423SLionel Sambuc   	panic("tell_parent: child not a zombie");
689433d6423SLionel Sambuc   if(child->mp_flags & TOLD_PARENT)
690433d6423SLionel Sambuc 	panic("tell_parent: telling parent again");
691433d6423SLionel Sambuc   parent = &mproc[mp_parent];
692433d6423SLionel Sambuc 
69329346ab0SDavid van Moolenbroek   /* See if we need to report resource usage to the parent. */
69429346ab0SDavid van Moolenbroek   if (addr) {
69529346ab0SDavid van Moolenbroek 	/* We report only user and system times for now. TODO: support other
69629346ab0SDavid van Moolenbroek 	 * fields, although this is tricky since the child process is already
69729346ab0SDavid van Moolenbroek 	 * gone as far as the kernel and other services are concerned..
69829346ab0SDavid van Moolenbroek 	 */
69929346ab0SDavid van Moolenbroek 	memset(&r_usage, 0, sizeof(r_usage));
70029346ab0SDavid van Moolenbroek 	set_rusage_times(&r_usage, child->mp_child_utime,
70129346ab0SDavid van Moolenbroek 	    child->mp_child_stime);
70229346ab0SDavid van Moolenbroek 
70329346ab0SDavid van Moolenbroek 	if ((r = sys_datacopy(SELF, (vir_bytes)&r_usage, parent->mp_endpoint,
70429346ab0SDavid van Moolenbroek 	    addr, sizeof(r_usage))) != OK) {
70529346ab0SDavid van Moolenbroek 		reply(child->mp_parent, r);
70629346ab0SDavid van Moolenbroek 
70729346ab0SDavid van Moolenbroek 		return FALSE; /* copy error - the child is still there */
70829346ab0SDavid van Moolenbroek 	}
70929346ab0SDavid van Moolenbroek   }
71029346ab0SDavid van Moolenbroek 
711433d6423SLionel Sambuc   /* Wake up the parent by sending the reply message. */
71229346ab0SDavid van Moolenbroek   parent->mp_reply.m_pm_lc_wait4.status =
713433d6423SLionel Sambuc 	W_EXITCODE(child->mp_exitstatus, child->mp_sigstatus);
714433d6423SLionel Sambuc   reply(child->mp_parent, child->mp_pid);
715433d6423SLionel Sambuc   parent->mp_flags &= ~WAITING;		/* parent no longer waiting */
716433d6423SLionel Sambuc   child->mp_flags &= ~ZOMBIE;		/* child no longer a zombie */
717433d6423SLionel Sambuc   child->mp_flags |= TOLD_PARENT;	/* avoid informing parent twice */
7180f5c95a0SDavid van Moolenbroek 
7190f5c95a0SDavid van Moolenbroek   /* Now that the child has been waited for, accumulate the times of the
7200f5c95a0SDavid van Moolenbroek    * terminated child process at the parent.
7210f5c95a0SDavid van Moolenbroek    */
7220f5c95a0SDavid van Moolenbroek   parent->mp_child_utime += child->mp_child_utime;
7230f5c95a0SDavid van Moolenbroek   parent->mp_child_stime += child->mp_child_stime;
72429346ab0SDavid van Moolenbroek 
72529346ab0SDavid van Moolenbroek   return TRUE; /* child has been waited for */
726433d6423SLionel Sambuc }
727433d6423SLionel Sambuc 
728433d6423SLionel Sambuc /*===========================================================================*
729433d6423SLionel Sambuc  *				tell_tracer				     *
730433d6423SLionel Sambuc  *===========================================================================*/
731*637f688fSRichard Sailer static void
tell_tracer(struct mproc * child)732*637f688fSRichard Sailer tell_tracer(
733*637f688fSRichard Sailer 	struct mproc *child			/* tells which process is exiting */
734*637f688fSRichard Sailer )
735433d6423SLionel Sambuc {
736433d6423SLionel Sambuc   int mp_tracer;
737433d6423SLionel Sambuc   struct mproc *tracer;
738433d6423SLionel Sambuc 
739433d6423SLionel Sambuc   mp_tracer = child->mp_tracer;
740433d6423SLionel Sambuc   if (mp_tracer <= 0)
741433d6423SLionel Sambuc 	panic("tell_tracer: bad value in mp_tracer: %d", mp_tracer);
742433d6423SLionel Sambuc   if(!(child->mp_flags & TRACE_ZOMBIE))
743433d6423SLionel Sambuc   	panic("tell_tracer: child not a zombie");
744433d6423SLionel Sambuc   tracer = &mproc[mp_tracer];
745433d6423SLionel Sambuc 
74629346ab0SDavid van Moolenbroek   /* TODO: rusage support */
74729346ab0SDavid van Moolenbroek 
74829346ab0SDavid van Moolenbroek   tracer->mp_reply.m_pm_lc_wait4.status =
749433d6423SLionel Sambuc 	W_EXITCODE(child->mp_exitstatus, (child->mp_sigstatus & 0377));
750433d6423SLionel Sambuc   reply(child->mp_tracer, child->mp_pid);
751433d6423SLionel Sambuc   tracer->mp_flags &= ~WAITING;		/* tracer no longer waiting */
752433d6423SLionel Sambuc   child->mp_flags &= ~TRACE_ZOMBIE;	/* child no longer zombie to tracer */
753433d6423SLionel Sambuc   child->mp_flags |= ZOMBIE;		/* child is now zombie to parent */
754433d6423SLionel Sambuc }
755433d6423SLionel Sambuc 
756433d6423SLionel Sambuc /*===========================================================================*
757433d6423SLionel Sambuc  *				tracer_died				     *
758433d6423SLionel Sambuc  *===========================================================================*/
759*637f688fSRichard Sailer static void
tracer_died(struct mproc * child)760*637f688fSRichard Sailer tracer_died(
761*637f688fSRichard Sailer 	struct mproc *child			/* process being traced */
762*637f688fSRichard Sailer )
763433d6423SLionel Sambuc {
764433d6423SLionel Sambuc /* The process that was tracing the given child, has died for some reason.
765433d6423SLionel Sambuc  * This is really the tracer's fault, but we can't let INIT deal with this.
766433d6423SLionel Sambuc  */
767433d6423SLionel Sambuc 
768433d6423SLionel Sambuc   child->mp_tracer = NO_TRACER;
769433d6423SLionel Sambuc   child->mp_flags &= ~TRACE_EXIT;
770433d6423SLionel Sambuc 
771433d6423SLionel Sambuc   /* If the tracer died while the child was running or stopped, we have no
772433d6423SLionel Sambuc    * idea what state the child is in. Avoid a trainwreck, by killing the child.
773433d6423SLionel Sambuc    * Note that this may cause cascading exits.
774433d6423SLionel Sambuc    */
775433d6423SLionel Sambuc   if (!(child->mp_flags & EXITING)) {
776433d6423SLionel Sambuc 	sig_proc(child, SIGKILL, TRUE /*trace*/, FALSE /* ksig */);
777433d6423SLionel Sambuc 
778433d6423SLionel Sambuc 	return;
779433d6423SLionel Sambuc   }
780433d6423SLionel Sambuc 
781433d6423SLionel Sambuc   /* If the tracer died while the child was telling it about its own death,
782433d6423SLionel Sambuc    * forget about the tracer and notify the real parent instead.
783433d6423SLionel Sambuc    */
784433d6423SLionel Sambuc   if (child->mp_flags & TRACE_ZOMBIE) {
785433d6423SLionel Sambuc 	child->mp_flags &= ~TRACE_ZOMBIE;
786433d6423SLionel Sambuc 	child->mp_flags |= ZOMBIE;
787433d6423SLionel Sambuc 
788433d6423SLionel Sambuc 	check_parent(child, TRUE /*try_cleanup*/);
789433d6423SLionel Sambuc   }
790433d6423SLionel Sambuc }
791433d6423SLionel Sambuc 
792433d6423SLionel Sambuc /*===========================================================================*
793433d6423SLionel Sambuc  *				cleanup					     *
794433d6423SLionel Sambuc  *===========================================================================*/
795*637f688fSRichard Sailer static void
cleanup(register struct mproc * rmp)796*637f688fSRichard Sailer cleanup(
797*637f688fSRichard Sailer 	register struct mproc *rmp	/* tells which process is exiting */
798*637f688fSRichard Sailer )
799433d6423SLionel Sambuc {
800433d6423SLionel Sambuc   /* Release the process table entry and reinitialize some field. */
801433d6423SLionel Sambuc   rmp->mp_pid = 0;
802433d6423SLionel Sambuc   rmp->mp_flags = 0;
803433d6423SLionel Sambuc   rmp->mp_child_utime = 0;
804433d6423SLionel Sambuc   rmp->mp_child_stime = 0;
805433d6423SLionel Sambuc   procs_in_use--;
806433d6423SLionel Sambuc }
807433d6423SLionel Sambuc 
808