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                         // process received message 
 241                         if (bufRequest
.msgId() >= MACH_NOTIFY_FIRST 
&& 
 242                                 bufRequest
.msgId() <= MACH_NOTIFY_LAST
) { 
 243                                 // mach kernel notification message 
 244                                 // we assume this is quick, so no thread arbitration here 
 245                                 cdsa_notify_server(bufRequest
, bufReply
); 
 247                                 // normal request message 
 248                                 StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this); 
 249                 secinfo("machserver", "begin request: %d, %d", bufRequest
.localPort().port(), bufRequest
.msgId()); 
 251                                 // try subsidiary handlers first 
 252                                 bool handled 
= false; 
 253                                 for (HandlerSet::const_iterator it 
= mHandlers
.begin(); 
 254                                                 it 
!= mHandlers
.end(); it
++) 
 255                                         if (bufRequest
.localPort() == (*it
)->port()) { 
 256                                                 (*it
)->handle(bufRequest
, bufReply
); 
 260                                         // unclaimed, send to main handler 
 261                     handle(bufRequest
, bufReply
); 
 264                 secinfo("machserver", "end request"); 
 267                         // process reply generated by handler 
 268             if (!(bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
) && 
 269                 bufReply
.returnCode() != KERN_SUCCESS
) { 
 270                     if (bufReply
.returnCode() == MIG_NO_REPLY
) 
 272                     // don't destroy the reply port right, so we can send an error message 
 273                     bufRequest
.remotePort(MACH_PORT_NULL
); 
 274                     mach_msg_destroy(bufRequest
); 
 277             if (bufReply
.remotePort() == MACH_PORT_NULL
) { 
 278                 // no reply port, so destroy the reply 
 279                 if (bufReply
.bits() & MACH_MSGH_BITS_COMPLEX
) 
 285              *  We don't want to block indefinitely because the client 
 286              *  isn't receiving messages from the reply port. 
 287              *  If we have a send-once right for the reply port, then 
 288              *  this isn't a concern because the send won't block. 
 289              *  If we have a send right, we need to use MACH_SEND_TIMEOUT. 
 290              *  To avoid falling off the kernel's fast RPC path unnecessarily, 
 291              *  we only supply MACH_SEND_TIMEOUT when absolutely necessary. 
 293                         mr 
= mach_msg_overwrite(bufReply
, 
 294                           (MACH_MSGH_BITS_REMOTE(bufReply
.bits()) == 
 295                                                 MACH_MSG_TYPE_MOVE_SEND_ONCE
) ? 
 296                           MACH_SEND_MSG 
| mMsgOptions 
: 
 297                           MACH_SEND_MSG 
| MACH_SEND_TIMEOUT 
| mMsgOptions
, 
 298                           bufReply
.length(), 0, MACH_PORT_NULL
, 
 299                           0, MACH_PORT_NULL
, NULL
, 0); 
 301                         case MACH_MSG_SUCCESS
: 
 304                 secinfo("machserver", "send error: %d %d", mr
, bufReply
.remotePort().port()); 
 310             // clean up after the transaction 
 311             releaseDeferredAllocations(); 
 313                 perThread().server 
= NULL
; 
 316                 perThread().server 
= NULL
; 
 323 // Manage subsidiary port handlers 
 325 void MachServer::add(Handler 
&handler
) 
 327     assert(mHandlers
.find(&handler
) == mHandlers
.end()); 
 328     assert(handler
.port() != MACH_PORT_NULL
); 
 329     mHandlers
.insert(&handler
); 
 330     mPortSet 
+= handler
.port(); 
 333 void MachServer::remove(Handler 
&handler
) 
 335     assert(mHandlers
.find(&handler
) != mHandlers
.end()); 
 336     mHandlers
.erase(&handler
); 
 337     mPortSet 
-= handler
.port(); 
 342 // Abstract auxiliary message handlers 
 344 MachServer::Handler::~Handler() 
 349 // Implement a Handler that sends no reply 
 351 boolean_t 
MachServer::NoReplyHandler::handle(mach_msg_header_t 
*in
, mach_msg_header_t 
*out
) 
 353     // set up reply message to be valid (enough) and read "do not send reply" 
 355     out
->msgh_remote_port 
= MACH_PORT_NULL
; 
 356     out
->msgh_size 
= sizeof(mig_reply_error_t
); 
 357     ((mig_reply_error_t 
*)out
)->RetCode 
= MIG_NO_REPLY
; 
 359     // call input-only handler 
 365 // Register a memory block for deferred release. 
 367 void MachServer::releaseWhenDone(Allocator 
&alloc
, void *memory
) 
 370         set
<Allocation
> &releaseSet 
= perThread().deferredAllocations
; 
 371         assert(releaseSet
.find(Allocation(memory
, alloc
)) == releaseSet
.end()); 
 372         secinfo("machserver", "allocing register %p with alloc %p", memory
, &alloc
); 
 373         releaseSet
.insert(Allocation(memory
, alloc
)); 
 379 // Run through the accumulated deferred allocations and release them. 
 380 // This is done automatically on every pass through the server loop; 
 381 // it must be called by subclasses that implement their loop in some 
 383 // @@@X Needs to be thread local 
 385 void MachServer::releaseDeferredAllocations() 
 387     set
<Allocation
> &releaseSet 
= perThread().deferredAllocations
; 
 388         for (set
<Allocation
>::iterator it 
= releaseSet
.begin(); it 
!= releaseSet
.end(); it
++) { 
 389         secinfo("machserver", "releasing alloc at %p with %p", it
->addr
, it
->allocator
); 
 391         // before we release the deferred allocation, zap it so that secrets aren't left in memory 
 392         size_t memSize 
= malloc_size(it
->addr
); 
 393         bzero(it
->addr
, memSize
); 
 394                 it
->allocator
->free(it
->addr
); 
 396         releaseSet
.erase(releaseSet
.begin(), releaseSet
.end()); 
 401 // The handler function calls this if it realizes that it might be blocked 
 402 // (or doing something that takes a long time). We respond by ensuring that 
 403 // at least one more thread is ready to serve requests. 
 404 // Calls the threadLimitReached callback in the server object if the thread 
 405 // limit has been exceeded and a needed new thread was not created. 
 407 void MachServer::longTermActivity() 
 409         if (!useFloatingThread
) { 
 410                 StLock
<Mutex
> _(managerLock
); 
 415 void MachServer::busy() 
 417         StLock
<Mutex
> _(managerLock
); 
 419         if (useFloatingThread
) 
 423 void MachServer::idle() 
 425         StLock
<Mutex
> _(managerLock
); 
 430 void MachServer::ensureReadyThread() 
 432         if (idleCount 
== 0) { 
 433                 if (workerCount 
>= maxWorkerCount
) { 
 434                         this->threadLimitReached(workerCount
);  // call remedial handler 
 436                 if (workerCount 
< maxWorkerCount
) { // threadLimit() may have raised maxWorkerCount 
 437                         (new LoadThread(*this))->run(); 
 444 // The callback hook for our subclasses. 
 445 // The default does nothing, thereby denying further thread creation. 
 446 // You could do something like maxThreads(limit+1) here to grant an variance; 
 447 // or throw an exception to avoid possible deadlocks (this would abort the current 
 448 // request but not otherwise impact the server's operation). 
 450 void MachServer::threadLimitReached(UInt32 limit
) 
 456 // What our (non-primary) load threads do 
 458 void MachServer::LoadThread::action() 
 460         //@@@ race condition?! can server exit before helpers thread gets here? 
 462         // register the worker thread and go 
 463         server
.addThread(this); 
 465         secinfo("machserver", "start thread"); 
 466                 server
.runServerThread(true); 
 467         secinfo("machserver", "end thread"); 
 469                 // fell out of server loop by error. Let the thread go quietly 
 470         secinfo("machserver", "end thread (due to error)"); 
 472         server
.removeThread(this); 
 479 void MachServer::addThread(Thread 
*thread
) 
 481         StLock
<Mutex
> _(managerLock
); 
 484         workers
.insert(thread
); 
 487 void MachServer::removeThread(Thread 
*thread
) 
 489         StLock
<Mutex
> _(managerLock
); 
 492         workers
.erase(thread
); 
 499 MachServer::Timer::~Timer() 
 502 void MachServer::Timer::select() 
 505 void MachServer::Timer::unselect() 
 508 bool MachServer::processTimer() 
 511         {       StLock
<Mutex
> _(managerLock
);   // could have multiple threads trying this 
 512                 if (!(top 
= static_cast<Timer 
*>(timers
.pop(Time::now())))) 
 513                         return false;                           // nothing (more) to be done now 
 514         }       // drop lock; work has been retrieved 
 516         secinfo("machserver", "timer start: %p, %d, %f", top
, top
->longTerm(), Time::now().internalForm()); 
 517                 StLock
<MachServer::Timer
, 
 518                         &MachServer::Timer::select
, &MachServer::Timer::unselect
> _t(*top
); 
 519                 if (top
->longTerm()) { 
 520                         StLock
<MachServer
, &MachServer::busy
, &MachServer::idle
> _(*this); 
 525         secinfo("machserver", "timer end (false)"); 
 527         secinfo("machserver", "timer end (true)"); 
 532 void MachServer::setTimer(Timer 
*timer
, Time::Absolute when
) 
 534         StLock
<Mutex
> _(managerLock
); 
 535         timers
.schedule(timer
, when
);  
 538 void MachServer::clearTimer(Timer 
*timer
) 
 540         StLock
<Mutex
> _(managerLock
);  
 541         if (timer
->scheduled()) 
 542                 timers
.unschedule(timer
);  
 547 // Notification hooks and shims. Defaults do nothing. 
 549 void cdsa_mach_notify_dead_name(mach_port_t
, mach_port_name_t port
) 
 552                 MachServer::active().notifyDeadName(port
); 
 557 void MachServer::notifyDeadName(Port
) { } 
 559 void cdsa_mach_notify_port_deleted(mach_port_t
, mach_port_name_t port
) 
 562                 MachServer::active().notifyPortDeleted(port
); 
 567 void MachServer::notifyPortDeleted(Port
) { } 
 569 void cdsa_mach_notify_port_destroyed(mach_port_t
, mach_port_name_t port
) 
 572                 MachServer::active().notifyPortDestroyed(port
); 
 577 void MachServer::notifyPortDestroyed(Port
) { } 
 579 void cdsa_mach_notify_send_once(mach_port_t port
) 
 582                 MachServer::active().notifySendOnce(port
); 
 587 void MachServer::notifySendOnce(Port
) { } 
 589 void cdsa_mach_notify_no_senders(mach_port_t port
, mach_port_mscount_t count
) 
 592                 MachServer::active().notifyNoSenders(port
, count
); 
 597 void MachServer::notifyNoSenders(Port
, mach_port_mscount_t
) { } 
 599 void MachServer::eventDone() { } 
 602 } // end namespace MachPlusPlus 
 604 } // end namespace Security