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