1/* 2 * Copyright (c) 2009 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 * IOMIGMachPort.c 26 * 27 * Created by Rob Yepez on 1/29/09. 28 * Copyright 2008 Apple Inc.. All rights reserved. 29 * 30 */ 31 32#include <AssertMacros.h> 33#include <CoreFoundation/CFRuntime.h> 34 35#include <pthread.h> 36#include <mach/mach.h> 37#include <mach/mach_time.h> 38#include <servers/bootstrap.h> 39#include "IOMIGMachPort.h" 40 41typedef struct __IOMIGMachPort { 42 CFRuntimeBase cfBase; // base CFType information 43 44 CFRunLoopRef runLoop; 45 CFStringRef runLoopMode; 46 47 dispatch_queue_t dispatchQueue; 48 dispatch_source_t dispatchSource; 49 50 CFMachPortRef port; 51 CFRunLoopSourceRef source; 52 CFIndex maxMessageSize; 53 54 IOMIGMachPortDemuxCallback demuxCallback; 55 void * demuxRefcon; 56 57 IOMIGMachPortTerminationCallback terminationCallback; 58 void * terminationRefcon; 59 60 61} __IOMIGMachPort, *__IOMIGMachPortRef; 62 63 64static void __IOMIGMachPortRelease(CFTypeRef object); 65static void __IOMIGMachPortRegister(void); 66static void __IOMIGMachPortSourceCallback(void * info); 67static void __IOMIGMachPortPortCallback(CFMachPortRef port, void *msg, CFIndex size __unused, void *info); 68static Boolean __NoMoreSenders(mach_msg_header_t *request, mach_msg_header_t *reply); 69 70 71static const CFRuntimeClass __IOMIGMachPortClass = { 72 0, // version 73 "IOMIGMachPort", // className 74 NULL, // init 75 NULL, // copy 76 __IOMIGMachPortRelease, // finalize 77 NULL, // equal 78 NULL, // hash 79 NULL, // copyFormattingDesc 80 NULL, 81 NULL, 82 NULL 83}; 84 85static pthread_once_t __IOMIGMachPortTypeInit = PTHREAD_ONCE_INIT; 86static CFTypeID __IOMIGMachPortTypeID = _kCFRuntimeNotATypeID; 87static CFMutableDictionaryRef __ioPortCache = NULL; 88static pthread_mutex_t __ioPortCacheLock = PTHREAD_MUTEX_INITIALIZER; 89 90//------------------------------------------------------------------------------ 91// __IOMIGMachPortRegister 92//------------------------------------------------------------------------------ 93void __IOMIGMachPortRegister(void) 94{ 95 __ioPortCache = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL); 96 __IOMIGMachPortTypeID = _CFRuntimeRegisterClass(&__IOMIGMachPortClass); 97} 98 99//------------------------------------------------------------------------------ 100// __IOMIGMachPortRelease 101//------------------------------------------------------------------------------ 102void __IOMIGMachPortRelease(CFTypeRef object) 103{ 104 IOMIGMachPortRef migPort = (IOMIGMachPortRef)object; 105 106 if ( migPort->port ) { 107 CFMachPortInvalidate(migPort->port); 108 CFRelease(migPort->port); 109 } 110 111 if ( migPort->source ) { 112 CFRelease(migPort->source); 113 } 114 115 if ( migPort->dispatchSource ) { 116 dispatch_release(migPort->dispatchSource); 117 } 118} 119 120//------------------------------------------------------------------------------ 121// IOMIGMachPortGetTypeID 122//------------------------------------------------------------------------------ 123CFTypeID IOMIGMachPortGetTypeID(void) 124{ 125 if ( _kCFRuntimeNotATypeID == __IOMIGMachPortTypeID ) 126 pthread_once(&__IOMIGMachPortTypeInit, __IOMIGMachPortRegister); 127 128 return __IOMIGMachPortTypeID; 129} 130 131//------------------------------------------------------------------------------ 132// IOMIGMachPortCreate 133//------------------------------------------------------------------------------ 134IOMIGMachPortRef IOMIGMachPortCreate(CFAllocatorRef allocator, CFIndex maxMessageSize, mach_port_t port) 135{ 136 IOMIGMachPortRef migPort = NULL; 137 void * offset = NULL; 138 uint32_t size; 139 140 require(maxMessageSize > 0, exit); 141 142 /* allocate service */ 143 size = sizeof(__IOMIGMachPort) - sizeof(CFRuntimeBase); 144 migPort = ( IOMIGMachPortRef)_CFRuntimeCreateInstance(allocator, IOMIGMachPortGetTypeID(), size, NULL); 145 146 require(migPort, exit); 147 148 offset = migPort; 149 bzero(offset + sizeof(CFRuntimeBase), size); 150 151 CFMachPortContext context = {0, migPort, NULL, NULL, NULL}; 152 migPort->port = (port != MACH_PORT_NULL) ? 153 CFMachPortCreateWithPort(allocator, port, __IOMIGMachPortPortCallback, &context, NULL) : 154 CFMachPortCreate(allocator, __IOMIGMachPortPortCallback, &context, NULL); 155 156 require(migPort->port, exit); 157 158 migPort->maxMessageSize = maxMessageSize; 159 160 return migPort; 161 162exit: 163 if ( migPort ) 164 CFRelease(migPort); 165 166 return NULL; 167} 168 169//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 170// __IOMIGMachPortSourceCallback 171//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 172void __IOMIGMachPortSourceCallback(void * info) 173{ 174 IOMIGMachPortRef migPort = (IOMIGMachPortRef)info; 175 176 CFRetain(migPort); 177 178 mach_port_t port = CFMachPortGetPort(migPort->port); 179 mach_msg_size_t size = migPort->maxMessageSize + MAX_TRAILER_SIZE; 180 mach_msg_header_t * msg = (mach_msg_header_t *)CFAllocatorAllocate(CFGetAllocator(migPort), size, 0); 181 182 msg->msgh_size = size; 183 for (;;) { 184 msg->msgh_bits = 0; 185 msg->msgh_local_port = port; 186 msg->msgh_remote_port = MACH_PORT_NULL; 187 msg->msgh_id = 0; 188 kern_return_t ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, 0, MACH_PORT_NULL); 189 if (MACH_MSG_SUCCESS == ret) break; 190 if (MACH_RCV_TOO_LARGE != ret) goto inner_exit; 191 uint32_t newSize = round_msg(msg->msgh_size + MAX_TRAILER_SIZE); 192 msg = CFAllocatorReallocate(CFGetAllocator(migPort), msg, newSize, 0); 193 msg->msgh_size = newSize; 194 } 195 196 __IOMIGMachPortPortCallback(migPort->port, msg, msg->msgh_size, migPort); 197 198inner_exit: 199 CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); 200 CFRelease(migPort); 201} 202 203 204//------------------------------------------------------------------------------ 205// IOMIGMachPortScheduleWithDispatchQueue 206//------------------------------------------------------------------------------ 207void IOMIGMachPortScheduleWithDispatchQueue(IOMIGMachPortRef migPort, dispatch_queue_t queue) 208{ 209 210 mach_port_t port = CFMachPortGetPort(migPort->port); 211 212 migPort->dispatchQueue = queue; 213 214 require(migPort->dispatchQueue, exit); 215 216 // init the sources 217 if ( !migPort->dispatchSource ) { 218 migPort->dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, port, 0, migPort->dispatchQueue); 219 require(migPort->dispatchSource, exit); 220 221 dispatch_set_context(migPort->dispatchSource, migPort); 222 dispatch_source_set_event_handler_f(migPort->dispatchSource, __IOMIGMachPortSourceCallback); 223 } 224 225 dispatch_resume(migPort->dispatchSource); 226 227exit: 228 return; 229} 230 231//------------------------------------------------------------------------------ 232// IOMIGMachPortUnscheduleFromDispatchQueue 233//------------------------------------------------------------------------------ 234void IOMIGMachPortUnscheduleFromDispatchQueue(IOMIGMachPortRef migPort, dispatch_queue_t queue) 235{ 236 if ( !queue || !migPort->dispatchQueue) 237 return; 238 239 if ( queue != migPort->dispatchQueue ) 240 return; 241 242 migPort->dispatchQueue = NULL; 243 244 if ( migPort->dispatchSource ) { 245 dispatch_release(migPort->dispatchSource); 246 migPort->dispatchSource = NULL; 247 } 248} 249 250//------------------------------------------------------------------------------ 251// IOMIGMachPortScheduleWithRunLoop 252//------------------------------------------------------------------------------ 253void IOMIGMachPortScheduleWithRunLoop(IOMIGMachPortRef migPort, CFRunLoopRef runLoop, CFStringRef runLoopMode) 254{ 255 migPort->runLoop = runLoop; 256 migPort->runLoopMode = runLoopMode; 257 258 require(migPort->runLoop, exit); 259 require(migPort->runLoopMode, exit); 260 261 // init the sources 262 if ( !migPort->source ) { 263 migPort->source = CFMachPortCreateRunLoopSource(CFGetAllocator(migPort), migPort->port, 1); 264 require(migPort->source, exit); 265 } 266 267 CFRunLoopAddSource(runLoop, migPort->source, runLoopMode); 268 269exit: 270 return; 271} 272 273//------------------------------------------------------------------------------ 274// IOMIGMachPortUnscheduleFromRunLoop 275//------------------------------------------------------------------------------ 276void IOMIGMachPortUnscheduleFromRunLoop(IOMIGMachPortRef migPort, CFRunLoopRef runLoop, CFStringRef runLoopMode) 277{ 278 if ( !runLoop || !runLoopMode || !migPort->runLoop || !migPort->runLoopMode) 279 return; 280 281 if ( !CFEqual(runLoop, migPort->runLoop) || !CFEqual(runLoopMode, migPort->runLoopMode) ) 282 return; 283 284 migPort->runLoop = NULL; 285 migPort->runLoopMode = NULL; 286 287 if ( migPort->source ) 288 CFRunLoopRemoveSource(runLoop, migPort->source, runLoopMode); 289} 290 291//------------------------------------------------------------------------------ 292// IOMIGMachPortGetPort 293//------------------------------------------------------------------------------ 294mach_port_t IOMIGMachPortGetPort(IOMIGMachPortRef migPort) 295{ 296 return CFMachPortGetPort(migPort->port); 297} 298 299//------------------------------------------------------------------------------ 300// IOMIGMachPortRegisterDemuxCallback 301//------------------------------------------------------------------------------ 302void IOMIGMachPortRegisterDemuxCallback(IOMIGMachPortRef migPort, IOMIGMachPortDemuxCallback callback, void *refcon) 303{ 304 migPort->demuxCallback = callback; 305 migPort->demuxRefcon = refcon; 306} 307 308//------------------------------------------------------------------------------ 309// IOMIGMachPortRegisterTerminationCallback 310//------------------------------------------------------------------------------ 311void IOMIGMachPortRegisterTerminationCallback(IOMIGMachPortRef migPort, IOMIGMachPortTerminationCallback callback, void *refcon) 312{ 313 migPort->terminationCallback = callback; 314 migPort->terminationRefcon = refcon; 315} 316 317//------------------------------------------------------------------------------ 318// __IOMIGMachPortPortCallback 319//------------------------------------------------------------------------------ 320void __IOMIGMachPortPortCallback(CFMachPortRef port __unused, void *msg, CFIndex size __unused, void *info) 321{ 322 IOMIGMachPortRef migPort = (IOMIGMachPortRef)info; 323 mig_reply_error_t * bufRequest = msg; 324 mig_reply_error_t * bufReply = NULL; 325 mach_msg_return_t mr; 326 int options; 327 328 require(migPort, exit); 329 CFRetain(migPort); 330 331 bufReply = CFAllocatorAllocate(NULL, migPort->maxMessageSize, 0); 332 require(bufReply, exit); 333 334 // let's see if we have no more senders 335 if ( __NoMoreSenders(&bufRequest->Head, &bufReply->Head) ) { 336 if ( migPort->terminationCallback ) 337 (*migPort->terminationCallback)(migPort, migPort->terminationRefcon); 338 else { 339 goto exit; 340 } 341 } else { 342 if ( migPort->demuxCallback ) 343 (*migPort->demuxCallback)(migPort, &bufRequest->Head, &bufReply->Head, migPort->demuxRefcon); 344 else { 345 mach_msg_destroy(&bufRequest->Head); 346 goto exit; 347 } 348 } 349 350 if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) && 351 (bufReply->RetCode != KERN_SUCCESS)) { 352 353 //This return code is a little tricky -- it appears that the 354 //demux routine found an error of some sort, but since that 355 //error would not normally get returned either to the local 356 //user or the remote one, we pretend it's ok. 357 require(bufReply->RetCode != MIG_NO_REPLY, exit); 358 359 // destroy any out-of-line data in the request buffer but don't destroy 360 // the reply port right (since we need that to send an error message). 361 362 bufRequest->Head.msgh_remote_port = MACH_PORT_NULL; 363 mach_msg_destroy(&bufRequest->Head); 364 } 365 366 if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) { 367 //no reply port, so destroy the reply 368 if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { 369 mach_msg_destroy(&bufReply->Head); 370 } 371 372 goto exit; 373 } 374 375 // send reply. 376 // We don't want to block indefinitely because the migPort 377 // isn't receiving messages from the reply port. 378 // If we have a send-once right for the reply port, then 379 // this isn't a concern because the send won't block. 380 // If we have a send right, we need to use MACH_SEND_TIMEOUT. 381 // To avoid falling off the kernel's fast RPC path unnecessarily, 382 // we only supply MACH_SEND_TIMEOUT when absolutely necessary. 383 384 options = MACH_SEND_MSG; 385 if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) { 386 options |= MACH_SEND_TIMEOUT; 387 } 388 389 mr = mach_msg(&bufReply->Head, 390 options, 391 bufReply->Head.msgh_size, 392 0, 393 MACH_PORT_NULL, 394 MACH_MSG_TIMEOUT_NONE, 395 MACH_PORT_NULL); 396 397 398 // Has a message error occurred? 399 switch (mr) { 400 case MACH_SEND_INVALID_DEST: 401 case MACH_SEND_TIMED_OUT: 402 // the reply can't be delivered, so destroy it 403 mach_msg_destroy(&bufReply->Head); 404 break; 405 406 default : 407 // Includes success case. 408 break; 409 } 410 411exit: 412 if ( bufReply ) 413 CFAllocatorDeallocate(NULL, bufReply); 414 415 if ( migPort ) 416 CFRelease(migPort); 417} 418 419//------------------------------------------------------------------------------ 420// __NoMoreSenders 421//------------------------------------------------------------------------------ 422Boolean __NoMoreSenders(mach_msg_header_t *request, mach_msg_header_t *reply) 423{ 424 mach_no_senders_notification_t *Request = (mach_no_senders_notification_t *)request; 425 mig_reply_error_t *Reply = (mig_reply_error_t *)reply; 426 427 reply->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0); 428 reply->msgh_remote_port = request->msgh_remote_port; 429 reply->msgh_size = sizeof(mig_reply_error_t); // Minimal size: update as needed 430 reply->msgh_local_port = MACH_PORT_NULL; 431 reply->msgh_id = request->msgh_id + 100; 432 433 if ((Request->not_header.msgh_id > MACH_NOTIFY_LAST) || 434 (Request->not_header.msgh_id < MACH_NOTIFY_FIRST)) { 435 Reply->NDR = NDR_record; 436 Reply->RetCode = MIG_BAD_ID; 437 return FALSE; // if this is not a notification message 438 } 439 440 switch (Request->not_header.msgh_id) { 441 case MACH_NOTIFY_NO_SENDERS : 442 Reply->Head.msgh_bits = 0; 443 Reply->Head.msgh_remote_port = MACH_PORT_NULL; 444 Reply->RetCode = KERN_SUCCESS; 445 return TRUE; 446 default : 447 break; 448 } 449 450 Reply->NDR = NDR_record; 451 Reply->RetCode = MIG_BAD_ID; 452 return FALSE; // if this is not a notification we are handling 453} 454 455//------------------------------------------------------------------------------ 456//IOMIGMachPortCacheAdd 457//------------------------------------------------------------------------------ 458void IOMIGMachPortCacheAdd(mach_port_t port, CFTypeRef server) 459{ 460 pthread_mutex_lock(&__ioPortCacheLock); 461 462 CFDictionarySetValue(__ioPortCache, (void *)port, server); 463 464 pthread_mutex_unlock(&__ioPortCacheLock); 465} 466 467//------------------------------------------------------------------------------ 468// IOMIGMachPortCacheRemove 469//------------------------------------------------------------------------------ 470void IOMIGMachPortCacheRemove(mach_port_t port) 471{ 472 pthread_mutex_lock(&__ioPortCacheLock); 473 474 CFDictionaryRemoveValue(__ioPortCache, (void *)port); 475 476 pthread_mutex_unlock(&__ioPortCacheLock); 477} 478 479//------------------------------------------------------------------------------ 480// IOMIGMachPortCacheCopy 481//------------------------------------------------------------------------------ 482CFTypeRef IOMIGMachPortCacheCopy(mach_port_t port) 483{ 484 CFTypeRef server; 485 486 pthread_mutex_lock(&__ioPortCacheLock); 487 488 server = (CFTypeRef)CFDictionaryGetValue(__ioPortCache, (void *)port); 489 if ( server ) CFRetain(server); 490 491 pthread_mutex_unlock(&__ioPortCacheLock); 492 493 return server; 494} 495 496