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