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