2 * Copyright (c) 2000-2007,2011-2013 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // machserver - C++ shell for writing Mach 3 servers
28 #include "machserver.h"
29 #include <servers/bootstrap.h>
30 #include <mach/kern_return.h>
31 #include <mach/message.h>
32 #include <mach/mig_errors.h>
33 #include "mach_notifyServer.h"
34 #include <security_utilities/debugging.h>
35 #include <malloc/malloc.h>
37 #if defined(USECFCURRENTTIME)
38 # include <CoreFoundation/CFDate.h>
40 # include <sys/time.h>
43 #define SEC_MACH_AUDIT_TOKEN_PID (5)
46 namespace MachPlusPlus
{
50 // Global per-thread information
52 ModuleNexus
< ThreadNexus
<MachServer::PerThread
> > MachServer::thread
;
56 // Create a server object.
57 // The resulting object is not "active", and any number of server objects
58 // can be in this "prepared" state at the same time.
60 MachServer::MachServer()
61 { setup("(anonymous)"); }
63 MachServer::MachServer(const char *name
)
64 : mServerPort(name
, bootstrap
)
67 MachServer::MachServer(const char *name
, const Bootstrap
&boot
)
68 : bootstrap(boot
), mServerPort(name
, bootstrap
)
71 void MachServer::setup(const char *name
)
73 workerTimeout
= 60 * 2; // 2 minutes default timeout
74 maxWorkerCount
= 100; // sanity check limit
75 useFloatingThread
= false; // tight thread management
77 mPortSet
+= mServerPort
;
80 MachServer::~MachServer()
82 // The ReceivePort members will clean themselves up.
83 // The bootstrap server will clear us from its map when our receive port dies.
88 // Add and remove extra listening ports.
89 // Messages directed to those ports are dispatched through the main handler.
90 // To get automatic call-out to another handler, use the Handler class.
92 void MachServer::add(Port receiver
)
94 secinfo("machserver", "port add: %d", receiver
.port());
98 void MachServer::remove(Port receiver
)
100 secinfo("machserver", "port remove: %d", receiver
.port());
101 mPortSet
-= receiver
;
106 // Register for mach port notifications
108 void MachServer::notifyIfDead(Port port
, bool doNotify
) const
111 port
.requestNotify(mServerPort
);
116 void MachServer::notifyIfUnused(Port port
, bool doNotify
) const
119 port
.requestNotify(port
, MACH_NOTIFY_NO_SENDERS
, true);
121 port
.cancelNotify(MACH_NOTIFY_NO_SENDERS
);
127 // This call will take control of the current thread and use it to service
128 // incoming requests. The thread will not be released until an error happens, which
129 // will cause an exception to be thrown. In other words, this never returns normally.
130 // We may also be creating additional threads to service concurrent requests
132 // @@@ Msg-errors in additional threads are not acted upon.
134 void MachServer::run(mach_msg_size_t maxSize
, mach_msg_options_t options
)
136 // establish server-global (thread-shared) parameters
138 mMsgOptions
= options
;
140 // establish the thread pool state
141 // (don't need managerLock since we're the only thread as of yet)
142 idleCount
= workerCount
= 1;
143 nextCheckTime
= Time::now() + workerTimeout
;
144 leastIdleWorkers
= 1;
145 highestWorkerCount
= 1;
147 // run server loop in initial (immortal) thread
148 secinfo("machserver", "start thread");
149 runServerThread(false);
150 secinfo("machserver", "end thread");
152 // primary server thread exited somehow (not currently possible)
158 // This is the core of a server thread at work. It takes over the thread until
159 // (a) an error occurs, throwing an exception
160 // (b) low-load timeout happens, causing a normal return (doTimeout only)
161 // This code was once based on mach_msg_server.c, but it is getting harder to notice
162 // the lingering resemblance.
164 extern "C" boolean_t
cdsa_notify_server(mach_msg_header_t
*in
, mach_msg_header_t
*out
);
166 void MachServer::runServerThread(bool doTimeout
)
168 // allocate request/reply buffers
169 Message
bufRequest(mMaxSize
);
170 Message
bufReply(mMaxSize
);
172 // all exits from runServerThread are through exceptions
174 // register as a worker thread
175 perThread().server
= this;
181 // process all pending timers
182 while (processTimer()) {}
184 // check for worker idle timeout
185 { StLock
<Mutex
> _(managerLock
);
186 // record idle thread low-water mark in scan interval
187 if (idleCount
< leastIdleWorkers
)
188 leastIdleWorkers
= idleCount
;
190 // perform self-timeout processing
192 if (workerCount
> maxWorkerCount
) // someone reduced maxWorkerCount recently...
193 break; // ... so release this thread immediately
194 Time::Absolute rightNow
= Time::now();
195 if (rightNow
>= nextCheckTime
) { // reaping period complete; process
196 UInt32 idlers
= leastIdleWorkers
;
197 secinfo("machserver", "reaping workers: %d %d", (uint32_t) workerCount
, (uint32_t) idlers
);
198 nextCheckTime
= rightNow
+ workerTimeout
;
199 leastIdleWorkers
= INT_MAX
;
200 if (idlers
> 1) // multiple idle threads throughout measuring interval...
201 break; // ... so release this thread now
206 // determine next timeout (if any)
207 bool indefinite
= false;
208 Time::Interval timeout
= workerTimeout
;
209 { StLock
<Mutex
> _(managerLock
);
210 if (timers
.empty()) {
211 indefinite
= !doTimeout
;
213 timeout
= max(Time::Interval(0), timers
.next() - Time::now());
214 if (doTimeout
&& workerTimeout
< timeout
)
215 timeout
= workerTimeout
;
219 // receive next IPC request (or wait for timeout)
220 mach_msg_return_t mr
= indefinite
?
221 mach_msg_overwrite(bufRequest
,
222 MACH_RCV_MSG
| mMsgOptions
,
223 0, mMaxSize
, mPortSet
,
224 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
,
225 (mach_msg_header_t
*) 0, 0)
227 mach_msg_overwrite(bufRequest
,
228 MACH_RCV_MSG
| MACH_RCV_TIMEOUT
| MACH_RCV_INTERRUPT
| mMsgOptions
,
229 0, mMaxSize
, mPortSet
,
230 mach_msg_timeout_t(timeout
.mSeconds()), MACH_PORT_NULL
,
231 (mach_msg_header_t
*) 0, 0);
234 case MACH_MSG_SUCCESS
:
235 // process received request message below
238 secinfo("machserver", "received error: %d", mr
);
242 // reset the buffer each time, handlers don't consistently set out params
243 bufReply
.clearBuffer();
245 // process received message
246 if (bufRequest
.msgId() >= MACH_NOTIFY_FIRST
&&
247 bufRequest
.msgId() <= MACH_NOTIFY_LAST
) {
248 // mach kernel notification message
249 // we assume this is quick, so no thread arbitration here
250 mach_msg_audit_trailer_t
*tlr
= bufRequest
.auditTrailer();
251 if (tlr
== NULL
|| tlr
->msgh_audit
.val
[SEC_MACH_AUDIT_TOKEN_PID
] != 0) {
252 secnotice("machserver", "ignoring invalid notify message");
255 cdsa_notify_server(bufRequest
, bufReply
);
257 // normal request message
258 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this);
259 secinfo("machserver", "begin request: %d, %d", bufRequest
.localPort().port(), bufRequest
.msgId());
261 // try subsidiary handlers first
262 bool handled
= false;
263 for (HandlerSet::const_iterator it
= mHandlers
.begin();
264 it
!= mHandlers
.end(); it
++)
265 if (bufRequest
.localPort() == (*it
)->port()) {
266 (*it
)->handle(bufRequest
, bufReply
);
270 // unclaimed, send to main handler
271 handle(bufRequest
, bufReply
);
274 secinfo("machserver", "end request");
277 // process reply generated by handler
278 if (!(bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
) &&
279 bufReply
.returnCode() != KERN_SUCCESS
) {
280 if (bufReply
.returnCode() == MIG_NO_REPLY
)
282 // don't destroy the reply port right, so we can send an error message
283 bufRequest
.remotePort(MACH_PORT_NULL
);
284 mach_msg_destroy(bufRequest
);
287 if (bufReply
.remotePort() == MACH_PORT_NULL
) {
288 // no reply port, so destroy the reply
289 if (bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
)
295 * We don't want to block indefinitely because the client
296 * isn't receiving messages from the reply port.
297 * If we have a send-once right for the reply port, then
298 * this isn't a concern because the send won't block.
299 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
300 * To avoid falling off the kernel's fast RPC path unnecessarily,
301 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
303 mr
= mach_msg_overwrite(bufReply
,
304 (MACH_MSGH_BITS_REMOTE(bufReply
.bits()) ==
305 MACH_MSG_TYPE_MOVE_SEND_ONCE
) ?
306 MACH_SEND_MSG
| mMsgOptions
:
307 MACH_SEND_MSG
| MACH_SEND_TIMEOUT
| mMsgOptions
,
308 bufReply
.length(), 0, MACH_PORT_NULL
,
309 0, MACH_PORT_NULL
, NULL
, 0);
311 case MACH_MSG_SUCCESS
:
313 case MACH_SEND_INVALID_DEST
:
314 case MACH_SEND_TIMED_OUT
:
315 secinfo("machserver", "send error: %d %d", mr
, bufReply
.remotePort().port());
319 secinfo("machserver", "send error: %d %d", mr
, bufReply
.remotePort().port());
324 // clean up after the transaction
325 releaseDeferredAllocations();
327 perThread().server
= NULL
;
330 perThread().server
= NULL
;
337 // Manage subsidiary port handlers
339 void MachServer::add(Handler
&handler
)
341 assert(mHandlers
.find(&handler
) == mHandlers
.end());
342 assert(handler
.port() != MACH_PORT_NULL
);
343 mHandlers
.insert(&handler
);
344 mPortSet
+= handler
.port();
347 void MachServer::remove(Handler
&handler
)
349 assert(mHandlers
.find(&handler
) != mHandlers
.end());
350 mHandlers
.erase(&handler
);
351 mPortSet
-= handler
.port();
356 // Abstract auxiliary message handlers
358 MachServer::Handler::~Handler()
363 // Implement a Handler that sends no reply
365 boolean_t
MachServer::NoReplyHandler::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
367 // set up reply message to be valid (enough) and read "do not send reply"
369 out
->msgh_remote_port
= MACH_PORT_NULL
;
370 out
->msgh_size
= sizeof(mig_reply_error_t
);
371 ((mig_reply_error_t
*)out
)->RetCode
= MIG_NO_REPLY
;
373 // call input-only handler
379 // Register a memory block for deferred release.
381 void MachServer::releaseWhenDone(Allocator
&alloc
, void *memory
)
384 set
<Allocation
> &releaseSet
= perThread().deferredAllocations
;
385 assert(releaseSet
.find(Allocation(memory
, alloc
)) == releaseSet
.end());
386 secinfo("machserver", "allocing register %p with alloc %p", memory
, &alloc
);
387 releaseSet
.insert(Allocation(memory
, alloc
));
393 // Run through the accumulated deferred allocations and release them.
394 // This is done automatically on every pass through the server loop;
395 // it must be called by subclasses that implement their loop in some
397 // @@@X Needs to be thread local
399 void MachServer::releaseDeferredAllocations()
401 set
<Allocation
> &releaseSet
= perThread().deferredAllocations
;
402 for (set
<Allocation
>::iterator it
= releaseSet
.begin(); it
!= releaseSet
.end(); it
++) {
403 secinfo("machserver", "releasing alloc at %p with %p", it
->addr
, it
->allocator
);
405 // before we release the deferred allocation, zap it so that secrets aren't left in memory
406 size_t memSize
= malloc_size(it
->addr
);
407 bzero(it
->addr
, memSize
);
408 it
->allocator
->free(it
->addr
);
410 releaseSet
.erase(releaseSet
.begin(), releaseSet
.end());
415 // The handler function calls this if it realizes that it might be blocked
416 // (or doing something that takes a long time). We respond by ensuring that
417 // at least one more thread is ready to serve requests.
418 // Calls the threadLimitReached callback in the server object if the thread
419 // limit has been exceeded and a needed new thread was not created.
421 void MachServer::longTermActivity()
423 if (!useFloatingThread
) {
424 StLock
<Mutex
> _(managerLock
);
429 void MachServer::busy()
431 StLock
<Mutex
> _(managerLock
);
433 if (useFloatingThread
)
437 void MachServer::idle()
439 StLock
<Mutex
> _(managerLock
);
444 void MachServer::ensureReadyThread()
446 if (idleCount
== 0) {
447 if (workerCount
>= maxWorkerCount
) {
448 this->threadLimitReached(workerCount
); // call remedial handler
450 if (workerCount
< maxWorkerCount
) { // threadLimit() may have raised maxWorkerCount
451 (new LoadThread(*this))->run();
458 // The callback hook for our subclasses.
459 // The default does nothing, thereby denying further thread creation.
460 // You could do something like maxThreads(limit+1) here to grant an variance;
461 // or throw an exception to avoid possible deadlocks (this would abort the current
462 // request but not otherwise impact the server's operation).
464 void MachServer::threadLimitReached(UInt32 limit
)
470 // What our (non-primary) load threads do
472 void MachServer::LoadThread::action()
474 //@@@ race condition?! can server exit before helpers thread gets here?
476 // register the worker thread and go
477 server
.addThread(this);
479 secinfo("machserver", "start thread");
480 server
.runServerThread(true);
481 secinfo("machserver", "end thread");
483 // fell out of server loop by error. Let the thread go quietly
484 secinfo("machserver", "end thread (due to error)");
486 server
.removeThread(this);
493 void MachServer::addThread(Thread
*thread
)
495 StLock
<Mutex
> _(managerLock
);
498 workers
.insert(thread
);
501 void MachServer::removeThread(Thread
*thread
)
503 StLock
<Mutex
> _(managerLock
);
506 workers
.erase(thread
);
513 MachServer::Timer::~Timer()
516 void MachServer::Timer::select()
519 void MachServer::Timer::unselect()
522 bool MachServer::processTimer()
525 { StLock
<Mutex
> _(managerLock
); // could have multiple threads trying this
526 if (!(top
= static_cast<Timer
*>(timers
.pop(Time::now()))))
527 return false; // nothing (more) to be done now
528 } // drop lock; work has been retrieved
530 secinfo("machserver", "timer start: %p, %d, %f", top
, top
->longTerm(), Time::now().internalForm());
531 StLock
<MachServer::Timer
,
532 &MachServer::Timer::select
, &MachServer::Timer::unselect
> _t(*top
);
533 if (top
->longTerm()) {
534 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this);
539 secinfo("machserver", "timer end (false)");
541 secinfo("machserver", "timer end (true)");
546 void MachServer::setTimer(Timer
*timer
, Time::Absolute when
)
548 StLock
<Mutex
> _(managerLock
);
549 timers
.schedule(timer
, when
);
552 void MachServer::clearTimer(Timer
*timer
)
554 StLock
<Mutex
> _(managerLock
);
555 if (timer
->scheduled())
556 timers
.unschedule(timer
);
561 // Notification hooks and shims. Defaults do nothing.
563 kern_return_t
cdsa_mach_notify_dead_name(mach_port_t
, mach_port_name_t port
)
566 MachServer::active().notifyDeadName(port
);
569 // the act of receiving a dead name notification allocates a dead-name
570 // right that must be deallocated
571 mach_port_deallocate(mach_task_self(), port
);
575 void MachServer::notifyDeadName(Port
) { }
577 kern_return_t
cdsa_mach_notify_port_deleted(mach_port_t
, mach_port_name_t port
)
580 MachServer::active().notifyPortDeleted(port
);
586 void MachServer::notifyPortDeleted(Port
) { }
588 kern_return_t
cdsa_mach_notify_port_destroyed(mach_port_t
, mach_port_name_t port
)
591 MachServer::active().notifyPortDestroyed(port
);
597 void MachServer::notifyPortDestroyed(Port
) { }
599 kern_return_t
cdsa_mach_notify_send_once(mach_port_t port
)
602 MachServer::active().notifySendOnce(port
);
608 void MachServer::notifySendOnce(Port
) { }
610 kern_return_t
cdsa_mach_notify_no_senders(mach_port_t port
, mach_port_mscount_t count
)
613 MachServer::active().notifyNoSenders(port
, count
);
619 void MachServer::notifyNoSenders(Port
, mach_port_mscount_t
) { }
621 void MachServer::eventDone() { }
624 } // end namespace MachPlusPlus
626 } // end namespace Security