child.cpp
1 /* 2 * Copyright (c) 2004,2007 Apple 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 // 36 Mutex ServerChild::mCheckinLock; 37 38 39 // 40 // Make and break ServerChildren 41 // 42 ServerChild::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 // 58 ServerChild::~ServerChild() 59 { 60 mServicePort.deallocate(); 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 // 84 void ServerChild::parentAction() 85 { 86 // wait for either checkin or (premature) death 87 secinfo("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 secinfo("serverchild", "%p (pid %d) died before checking in", this, pid()); 96 } else if (ready()) { 97 // child has checked in and is ready for service 98 secinfo("serverchild", "%p (pid %d) ready for service on port %d", 99 this, pid(), mServicePort.port()); 100 } else 101 assert(false); // how did we ever get here?! 102 } 103 104 105 // 106 // Death action during fork: release the waiting creator thread, if any 107 // 108 void ServerChild::dying() 109 { 110 secinfo("serverchild", "%p [%d] is dead; resuming parent thread (if any)", this, this->pid()); 111 mCheckinCond.signal(); 112 } 113 114 115 void ServerChild::checkIn(Port servicePort, pid_t pid) 116 { 117 if (ServerChild *child = Child::find<ServerChild>(pid)) { 118 // Child was alive when last seen. Store service port and signal parent thread 119 { 120 StLock<Mutex> _(mCheckinLock); 121 child->mServicePort = servicePort; 122 // The macro END_IPCS deallocates this 123 servicePort.modRefs(MACH_PORT_RIGHT_SEND, +1); // retain send right 124 secinfo("serverchild", "%p (pid %d) checking in; resuming parent thread", 125 child, pid); 126 } 127 child->mCheckinCond.signal(); 128 } else { 129 // Child has died; is wrong kind; or spurious checkin. 130 // If it was a proper child, death notifications will wake up the parent thread 131 secinfo("serverchild", "pid %d not in child set; checkin ignored", pid); 132 } 133 }