1/* 2 * Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// child - track a single child process and its belongings 27// 28#include "child.h" 29#include "dtrace.h" 30#include <security_utilities/debugging.h> 31 32 33// 34// We use a static Mutex to coordinate checkin 35// 36Mutex ServerChild::mCheckinLock; 37 38 39// 40// Make and break ServerChildren 41// 42ServerChild::ServerChild() 43 : mCheckinCond(mCheckinLock) 44{ 45} 46 47 48// 49// If the ServerChild is destroyed, kill its process, nice or hard. 50// 51// In case you wonder about the tango below, it's making sure we 52// get to "It's dead, Jim" with the minimum number of checkChildren() 53// calls while still working correctly if this is the only thread alive. 54// 55//@@@ We *could* define a "soft shutdown" MIG message to send to all 56//@@@ ServerChildren in this situation. 57// 58ServerChild::~ServerChild() 59{ 60 mServicePort.destroy(); 61 62 if (state() == alive) { 63 this->kill(SIGTERM); // shoot it once 64 checkChildren(); // check for quick death 65 if (state() == alive) { 66 usleep(300000); // give it some grace 67 if (state() == alive) { // could have been reaped by another thread 68 checkChildren(); // check again 69 if (state() == alive) { // it... just... won't... die... 70 this->kill(SIGKILL); // take THAT! 71 checkChildren(); 72 if (state() == alive) // stuck zombie 73 abandon(); // leave the body behind 74 } 75 } 76 } 77 } 78} 79 80 81// 82// Parent action during fork: wait until ready or dead, then return 83// 84void ServerChild::parentAction() 85{ 86 // wait for either checkin or (premature) death 87 secdebug("serverchild", "%p (pid %d) waiting for checkin", this, pid()); 88 StLock<Mutex> _(mCheckinLock); 89 while (!ready() && state() == alive) 90 mCheckinCond.wait(); 91 92 // so what happened? 93 if (state() == dead) { 94 // our child died 95 secdebug("serverchild", "%p (pid %d) died before checking in", this, pid()); 96 SECURITYD_CHILD_STILLBORN(this->pid()); 97 } else if (ready()) { 98 // child has checked in and is ready for service 99 secdebug("serverchild", "%p (pid %d) ready for service on port %d", 100 this, pid(), mServicePort.port()); 101 SECURITYD_CHILD_READY(this->pid()); 102 } else 103 assert(false); // how did we ever get here?! 104} 105 106 107// 108// Death action during fork: release the waiting creator thread, if any 109// 110void ServerChild::dying() 111{ 112 SECURITYD_CHILD_DYING(this->pid()); 113 secdebug("serverchild", "%p is dead; resuming parent thread (if any)", this); 114 mCheckinCond.signal(); 115} 116 117 118void ServerChild::checkIn(Port servicePort, pid_t pid) 119{ 120 if (ServerChild *child = Child::find<ServerChild>(pid)) { 121 // Child was alive when last seen. Store service port and signal parent thread 122 { 123 StLock<Mutex> _(mCheckinLock); 124 child->mServicePort = servicePort; 125 servicePort.modRefs(MACH_PORT_RIGHT_SEND, +1); // retain send right 126 secdebug("serverchild", "%p (pid %d) checking in; resuming parent thread", 127 child, pid); 128 } 129 SECURITYD_CHILD_CHECKIN(pid, servicePort); 130 child->mCheckinCond.signal(); 131 } else { 132 // Child has died; is wrong kind; or spurious checkin. 133 // If it was a proper child, death notifications will wake up the parent thread 134 secdebug("serverchild", "pid %d not in child set; checkin ignored", pid); 135 SECURITYD_CHILD_CHECKIN(pid, 0); 136 } 137} 138