/ securityd / src / child.cpp
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  }