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_notify.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>
44 namespace MachPlusPlus
{
48 // Global per-thread information
50 ModuleNexus
< ThreadNexus
<MachServer::PerThread
> > MachServer::thread
;
54 // Create a server object.
55 // The resulting object is not "active", and any number of server objects
56 // can be in this "prepared" state at the same time.
58 MachServer::MachServer()
59 { setup("(anonymous)"); }
61 MachServer::MachServer(const char *name
)
62 : mServerPort(name
, bootstrap
)
65 MachServer::MachServer(const char *name
, const Bootstrap
&boot
)
66 : bootstrap(boot
), mServerPort(name
, bootstrap
)
69 void MachServer::setup(const char *name
)
71 workerTimeout
= 60 * 2; // 2 minutes default timeout
72 maxWorkerCount
= 100; // sanity check limit
73 useFloatingThread
= false; // tight thread management
75 mPortSet
+= mServerPort
;
78 MachServer::~MachServer()
80 // The ReceivePort members will clean themselves up.
81 // The bootstrap server will clear us from its map when our receive port dies.
86 // Add and remove extra listening ports.
87 // Messages directed to those ports are dispatched through the main handler.
88 // To get automatic call-out to another handler, use the Handler class.
90 void MachServer::add(Port receiver
)
92 secinfo("machserver", "port add: %d", receiver
.port());
96 void MachServer::remove(Port receiver
)
98 secinfo("machserver", "port remove: %d", receiver
.port());
104 // Register for mach port notifications
106 void MachServer::notifyIfDead(Port port
, bool doNotify
) const
109 port
.requestNotify(mServerPort
);
114 void MachServer::notifyIfUnused(Port port
, bool doNotify
) const
117 port
.requestNotify(port
, MACH_NOTIFY_NO_SENDERS
, true);
119 port
.cancelNotify(MACH_NOTIFY_NO_SENDERS
);
125 // This call will take control of the current thread and use it to service
126 // incoming requests. The thread will not be released until an error happens, which
127 // will cause an exception to be thrown. In other words, this never returns normally.
128 // We may also be creating additional threads to service concurrent requests
130 // @@@ Msg-errors in additional threads are not acted upon.
132 void MachServer::run(mach_msg_size_t maxSize
, mach_msg_options_t options
)
134 // establish server-global (thread-shared) parameters
136 mMsgOptions
= options
;
138 // establish the thread pool state
139 // (don't need managerLock since we're the only thread as of yet)
140 idleCount
= workerCount
= 1;
141 nextCheckTime
= Time::now() + workerTimeout
;
142 leastIdleWorkers
= 1;
143 highestWorkerCount
= 1;
145 // run server loop in initial (immortal) thread
146 secinfo("machserver", "start thread");
147 runServerThread(false);
148 secinfo("machserver", "end thread");
150 // primary server thread exited somehow (not currently possible)
156 // This is the core of a server thread at work. It takes over the thread until
157 // (a) an error occurs, throwing an exception
158 // (b) low-load timeout happens, causing a normal return (doTimeout only)
159 // This code was once based on mach_msg_server.c, but it is getting harder to notice
160 // the lingering resemblance.
162 extern "C" boolean_t
cdsa_notify_server(mach_msg_header_t
*in
, mach_msg_header_t
*out
);
164 void MachServer::runServerThread(bool doTimeout
)
166 // allocate request/reply buffers
167 Message
bufRequest(mMaxSize
);
168 Message
bufReply(mMaxSize
);
170 // all exits from runServerThread are through exceptions
172 // register as a worker thread
173 perThread().server
= this;
179 // process all pending timers
180 while (processTimer()) {}
182 // check for worker idle timeout
183 { StLock
<Mutex
> _(managerLock
);
184 // record idle thread low-water mark in scan interval
185 if (idleCount
< leastIdleWorkers
)
186 leastIdleWorkers
= idleCount
;
188 // perform self-timeout processing
190 if (workerCount
> maxWorkerCount
) // someone reduced maxWorkerCount recently...
191 break; // ... so release this thread immediately
192 Time::Absolute rightNow
= Time::now();
193 if (rightNow
>= nextCheckTime
) { // reaping period complete; process
194 UInt32 idlers
= leastIdleWorkers
;
195 secinfo("machserver", "reaping workers: %d %d", (uint32_t) workerCount
, (uint32_t) idlers
);
196 nextCheckTime
= rightNow
+ workerTimeout
;
197 leastIdleWorkers
= INT_MAX
;
198 if (idlers
> 1) // multiple idle threads throughout measuring interval...
199 break; // ... so release this thread now
204 // determine next timeout (if any)
205 bool indefinite
= false;
206 Time::Interval timeout
= workerTimeout
;
207 { StLock
<Mutex
> _(managerLock
);
208 if (timers
.empty()) {
209 indefinite
= !doTimeout
;
211 timeout
= max(Time::Interval(0), timers
.next() - Time::now());
212 if (doTimeout
&& workerTimeout
< timeout
)
213 timeout
= workerTimeout
;
217 // receive next IPC request (or wait for timeout)
218 mach_msg_return_t mr
= indefinite
?
219 mach_msg_overwrite(bufRequest
,
220 MACH_RCV_MSG
| mMsgOptions
,
221 0, mMaxSize
, mPortSet
,
222 MACH_MSG_TIMEOUT_NONE
, MACH_PORT_NULL
,
223 (mach_msg_header_t
*) 0, 0)
225 mach_msg_overwrite(bufRequest
,
226 MACH_RCV_MSG
| MACH_RCV_TIMEOUT
| MACH_RCV_INTERRUPT
| mMsgOptions
,
227 0, mMaxSize
, mPortSet
,
228 mach_msg_timeout_t(timeout
.mSeconds()), MACH_PORT_NULL
,
229 (mach_msg_header_t
*) 0, 0);
232 case MACH_MSG_SUCCESS
:
233 // process received request message below
236 secinfo("machserver", "received error: %d", mr
);
240 // reset the buffer each time, handlers don't consistently set out params
241 bufReply
.clearBuffer();
243 // process received message
244 if (bufRequest
.msgId() >= MACH_NOTIFY_FIRST
&&
245 bufRequest
.msgId() <= MACH_NOTIFY_LAST
) {
246 // mach kernel notification message
247 // we assume this is quick, so no thread arbitration here
248 cdsa_notify_server(bufRequest
, bufReply
);
250 // normal request message
251 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this);
252 secinfo("machserver", "begin request: %d, %d", bufRequest
.localPort().port(), bufRequest
.msgId());
254 // try subsidiary handlers first
255 bool handled
= false;
256 for (HandlerSet::const_iterator it
= mHandlers
.begin();
257 it
!= mHandlers
.end(); it
++)
258 if (bufRequest
.localPort() == (*it
)->port()) {
259 (*it
)->handle(bufRequest
, bufReply
);
263 // unclaimed, send to main handler
264 handle(bufRequest
, bufReply
);
267 secinfo("machserver", "end request");
270 // process reply generated by handler
271 if (!(bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
) &&
272 bufReply
.returnCode() != KERN_SUCCESS
) {
273 if (bufReply
.returnCode() == MIG_NO_REPLY
)
275 // don't destroy the reply port right, so we can send an error message
276 bufRequest
.remotePort(MACH_PORT_NULL
);
277 mach_msg_destroy(bufRequest
);
280 if (bufReply
.remotePort() == MACH_PORT_NULL
) {
281 // no reply port, so destroy the reply
282 if (bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
)
288 * We don't want to block indefinitely because the client
289 * isn't receiving messages from the reply port.
290 * If we have a send-once right for the reply port, then
291 * this isn't a concern because the send won't block.
292 * If we have a send right, we need to use MACH_SEND_TIMEOUT.
293 * To avoid falling off the kernel's fast RPC path unnecessarily,
294 * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
296 mr
= mach_msg_overwrite(bufReply
,
297 (MACH_MSGH_BITS_REMOTE(bufReply
.bits()) ==
298 MACH_MSG_TYPE_MOVE_SEND_ONCE
) ?
299 MACH_SEND_MSG
| mMsgOptions
:
300 MACH_SEND_MSG
| MACH_SEND_TIMEOUT
| mMsgOptions
,
301 bufReply
.length(), 0, MACH_PORT_NULL
,
302 0, MACH_PORT_NULL
, NULL
, 0);
304 case MACH_MSG_SUCCESS
:
307 secinfo("machserver", "send error: %d %d", mr
, bufReply
.remotePort().port());
313 // clean up after the transaction
314 releaseDeferredAllocations();
316 perThread().server
= NULL
;
319 perThread().server
= NULL
;
326 // Manage subsidiary port handlers
328 void MachServer::add(Handler
&handler
)
330 assert(mHandlers
.find(&handler
) == mHandlers
.end());
331 assert(handler
.port() != MACH_PORT_NULL
);
332 mHandlers
.insert(&handler
);
333 mPortSet
+= handler
.port();
336 void MachServer::remove(Handler
&handler
)
338 assert(mHandlers
.find(&handler
) != mHandlers
.end());
339 mHandlers
.erase(&handler
);
340 mPortSet
-= handler
.port();
345 // Abstract auxiliary message handlers
347 MachServer::Handler::~Handler()
352 // Implement a Handler that sends no reply
354 boolean_t
MachServer::NoReplyHandler::handle(mach_msg_header_t
*in
, mach_msg_header_t
*out
)
356 // set up reply message to be valid (enough) and read "do not send reply"
358 out
->msgh_remote_port
= MACH_PORT_NULL
;
359 out
->msgh_size
= sizeof(mig_reply_error_t
);
360 ((mig_reply_error_t
*)out
)->RetCode
= MIG_NO_REPLY
;
362 // call input-only handler
368 // Register a memory block for deferred release.
370 void MachServer::releaseWhenDone(Allocator
&alloc
, void *memory
)
373 set
<Allocation
> &releaseSet
= perThread().deferredAllocations
;
374 assert(releaseSet
.find(Allocation(memory
, alloc
)) == releaseSet
.end());
375 secinfo("machserver", "allocing register %p with alloc %p", memory
, &alloc
);
376 releaseSet
.insert(Allocation(memory
, alloc
));
382 // Run through the accumulated deferred allocations and release them.
383 // This is done automatically on every pass through the server loop;
384 // it must be called by subclasses that implement their loop in some
386 // @@@X Needs to be thread local
388 void MachServer::releaseDeferredAllocations()
390 set
<Allocation
> &releaseSet
= perThread().deferredAllocations
;
391 for (set
<Allocation
>::iterator it
= releaseSet
.begin(); it
!= releaseSet
.end(); it
++) {
392 secinfo("machserver", "releasing alloc at %p with %p", it
->addr
, it
->allocator
);
394 // before we release the deferred allocation, zap it so that secrets aren't left in memory
395 size_t memSize
= malloc_size(it
->addr
);
396 bzero(it
->addr
, memSize
);
397 it
->allocator
->free(it
->addr
);
399 releaseSet
.erase(releaseSet
.begin(), releaseSet
.end());
404 // The handler function calls this if it realizes that it might be blocked
405 // (or doing something that takes a long time). We respond by ensuring that
406 // at least one more thread is ready to serve requests.
407 // Calls the threadLimitReached callback in the server object if the thread
408 // limit has been exceeded and a needed new thread was not created.
410 void MachServer::longTermActivity()
412 if (!useFloatingThread
) {
413 StLock
<Mutex
> _(managerLock
);
418 void MachServer::busy()
420 StLock
<Mutex
> _(managerLock
);
422 if (useFloatingThread
)
426 void MachServer::idle()
428 StLock
<Mutex
> _(managerLock
);
433 void MachServer::ensureReadyThread()
435 if (idleCount
== 0) {
436 if (workerCount
>= maxWorkerCount
) {
437 this->threadLimitReached(workerCount
); // call remedial handler
439 if (workerCount
< maxWorkerCount
) { // threadLimit() may have raised maxWorkerCount
440 (new LoadThread(*this))->run();
447 // The callback hook for our subclasses.
448 // The default does nothing, thereby denying further thread creation.
449 // You could do something like maxThreads(limit+1) here to grant an variance;
450 // or throw an exception to avoid possible deadlocks (this would abort the current
451 // request but not otherwise impact the server's operation).
453 void MachServer::threadLimitReached(UInt32 limit
)
459 // What our (non-primary) load threads do
461 void MachServer::LoadThread::action()
463 //@@@ race condition?! can server exit before helpers thread gets here?
465 // register the worker thread and go
466 server
.addThread(this);
468 secinfo("machserver", "start thread");
469 server
.runServerThread(true);
470 secinfo("machserver", "end thread");
472 // fell out of server loop by error. Let the thread go quietly
473 secinfo("machserver", "end thread (due to error)");
475 server
.removeThread(this);
482 void MachServer::addThread(Thread
*thread
)
484 StLock
<Mutex
> _(managerLock
);
487 workers
.insert(thread
);
490 void MachServer::removeThread(Thread
*thread
)
492 StLock
<Mutex
> _(managerLock
);
495 workers
.erase(thread
);
502 MachServer::Timer::~Timer()
505 void MachServer::Timer::select()
508 void MachServer::Timer::unselect()
511 bool MachServer::processTimer()
514 { StLock
<Mutex
> _(managerLock
); // could have multiple threads trying this
515 if (!(top
= static_cast<Timer
*>(timers
.pop(Time::now()))))
516 return false; // nothing (more) to be done now
517 } // drop lock; work has been retrieved
519 secinfo("machserver", "timer start: %p, %d, %f", top
, top
->longTerm(), Time::now().internalForm());
520 StLock
<MachServer::Timer
,
521 &MachServer::Timer::select
, &MachServer::Timer::unselect
> _t(*top
);
522 if (top
->longTerm()) {
523 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this);
528 secinfo("machserver", "timer end (false)");
530 secinfo("machserver", "timer end (true)");
535 void MachServer::setTimer(Timer
*timer
, Time::Absolute when
)
537 StLock
<Mutex
> _(managerLock
);
538 timers
.schedule(timer
, when
);
541 void MachServer::clearTimer(Timer
*timer
)
543 StLock
<Mutex
> _(managerLock
);
544 if (timer
->scheduled())
545 timers
.unschedule(timer
);
550 // Notification hooks and shims. Defaults do nothing.
552 kern_return_t
cdsa_mach_notify_dead_name(mach_port_t
, mach_port_name_t port
)
555 MachServer::active().notifyDeadName(port
);
561 void MachServer::notifyDeadName(Port
) { }
563 kern_return_t
cdsa_mach_notify_port_deleted(mach_port_t
, mach_port_name_t port
)
566 MachServer::active().notifyPortDeleted(port
);
572 void MachServer::notifyPortDeleted(Port
) { }
574 kern_return_t
cdsa_mach_notify_port_destroyed(mach_port_t
, mach_port_name_t port
)
577 MachServer::active().notifyPortDestroyed(port
);
583 void MachServer::notifyPortDestroyed(Port
) { }
585 kern_return_t
cdsa_mach_notify_send_once(mach_port_t port
)
588 MachServer::active().notifySendOnce(port
);
594 void MachServer::notifySendOnce(Port
) { }
596 kern_return_t
cdsa_mach_notify_no_senders(mach_port_t port
, mach_port_mscount_t count
)
599 MachServer::active().notifyNoSenders(port
, count
);
605 void MachServer::notifyNoSenders(Port
, mach_port_mscount_t
) { }
607 void MachServer::eventDone() { }
610 } // end namespace MachPlusPlus
612 } // end namespace Security