X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/securityd/src/child.cpp diff --git a/securityd/src/child.cpp b/securityd/src/child.cpp new file mode 100644 index 00000000..120bf4cd --- /dev/null +++ b/securityd/src/child.cpp @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2004,2007 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + + +// +// child - track a single child process and its belongings +// +#include "child.h" +#include "dtrace.h" +#include + + +// +// We use a static Mutex to coordinate checkin +// +Mutex ServerChild::mCheckinLock; + + +// +// Make and break ServerChildren +// +ServerChild::ServerChild() + : mCheckinCond(mCheckinLock) +{ +} + + +// +// If the ServerChild is destroyed, kill its process, nice or hard. +// +// In case you wonder about the tango below, it's making sure we +// get to "It's dead, Jim" with the minimum number of checkChildren() +// calls while still working correctly if this is the only thread alive. +// +//@@@ We *could* define a "soft shutdown" MIG message to send to all +//@@@ ServerChildren in this situation. +// +ServerChild::~ServerChild() +{ + mServicePort.destroy(); + + if (state() == alive) { + this->kill(SIGTERM); // shoot it once + checkChildren(); // check for quick death + if (state() == alive) { + usleep(300000); // give it some grace + if (state() == alive) { // could have been reaped by another thread + checkChildren(); // check again + if (state() == alive) { // it... just... won't... die... + this->kill(SIGKILL); // take THAT! + checkChildren(); + if (state() == alive) // stuck zombie + abandon(); // leave the body behind + } + } + } + } +} + + +// +// Parent action during fork: wait until ready or dead, then return +// +void ServerChild::parentAction() +{ + // wait for either checkin or (premature) death + secdebug("serverchild", "%p (pid %d) waiting for checkin", this, pid()); + StLock _(mCheckinLock); + while (!ready() && state() == alive) + mCheckinCond.wait(); + + // so what happened? + if (state() == dead) { + // our child died + secdebug("serverchild", "%p (pid %d) died before checking in", this, pid()); + SECURITYD_CHILD_STILLBORN(this->pid()); + } else if (ready()) { + // child has checked in and is ready for service + secdebug("serverchild", "%p (pid %d) ready for service on port %d", + this, pid(), mServicePort.port()); + SECURITYD_CHILD_READY(this->pid()); + } else + assert(false); // how did we ever get here?! +} + + +// +// Death action during fork: release the waiting creator thread, if any +// +void ServerChild::dying() +{ + SECURITYD_CHILD_DYING(this->pid()); + secdebug("serverchild", "%p is dead; resuming parent thread (if any)", this); + mCheckinCond.signal(); +} + + +void ServerChild::checkIn(Port servicePort, pid_t pid) +{ + if (ServerChild *child = Child::find(pid)) { + // Child was alive when last seen. Store service port and signal parent thread + { + StLock _(mCheckinLock); + child->mServicePort = servicePort; + servicePort.modRefs(MACH_PORT_RIGHT_SEND, +1); // retain send right + secdebug("serverchild", "%p (pid %d) checking in; resuming parent thread", + child, pid); + } + SECURITYD_CHILD_CHECKIN(pid, servicePort); + child->mCheckinCond.signal(); + } else { + // Child has died; is wrong kind; or spurious checkin. + // If it was a proper child, death notifications will wake up the parent thread + secdebug("serverchild", "pid %d not in child set; checkin ignored", pid); + SECURITYD_CHILD_CHECKIN(pid, 0); + } +}