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