1/* 2 * Copyright (c) 1998-2013 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#include "DASession.h" 25 26#include "DACallback.h" 27#include "DAServer.h" 28 29#include <mach/mach.h> 30#include <CoreFoundation/CoreFoundation.h> 31#include <CoreFoundation/CFRuntime.h> 32 33struct __DASession 34{ 35 CFRuntimeBase _base; 36 37 AuthorizationRef _authorization; 38 mach_port_t _client; 39 char * _name; 40 pid_t _pid; 41 DASessionOptions _options; 42 CFMutableArrayRef _queue; 43 CFMutableArrayRef _register; 44 CFMachPortRef _server; 45 CFRunLoopSourceRef _source; 46 DASessionState _state; 47}; 48 49typedef struct __DASession __DASession; 50 51static CFStringRef __DASessionCopyDescription( CFTypeRef object ); 52static CFStringRef __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options ); 53static void __DASessionDeallocate( CFTypeRef object ); 54static Boolean __DASessionEqual( CFTypeRef object1, CFTypeRef object2 ); 55static CFHashCode __DASessionHash( CFTypeRef object ); 56 57static const CFRuntimeClass __DASessionClass = 58{ 59 0, 60 "DASession", 61 NULL, 62 NULL, 63 __DASessionDeallocate, 64 __DASessionEqual, 65 __DASessionHash, 66 __DASessionCopyFormattingDescription, 67 __DASessionCopyDescription 68}; 69 70static CFTypeID __kDASessionTypeID = _kCFRuntimeNotATypeID; 71 72static CFStringRef __DASessionCopyDescription( CFTypeRef object ) 73{ 74 DASessionRef session = ( DASessionRef ) object; 75 76 return CFStringCreateWithFormat( CFGetAllocator( object ), 77 NULL, 78 CFSTR( "<DASession %p [%p]>{id = %s [%d]:%d}" ), 79 object, 80 CFGetAllocator( object ), 81 session->_name, 82 session->_pid, 83 CFMachPortGetPort( session->_server ) ); 84} 85 86static CFStringRef __DASessionCopyFormattingDescription( CFTypeRef object, CFDictionaryRef options ) 87{ 88 DASessionRef session = ( DASessionRef ) object; 89 90 return CFStringCreateWithFormat( CFGetAllocator( object ), 91 NULL, 92 CFSTR( "%s [%d]:%d" ), 93 session->_name, 94 session->_pid, 95 CFMachPortGetPort( session->_server ) ); 96} 97 98static DASessionRef __DASessionCreate( CFAllocatorRef allocator ) 99{ 100 __DASession * session; 101 102 session = ( void * ) _CFRuntimeCreateInstance( allocator, __kDASessionTypeID, sizeof( __DASession ) - sizeof( CFRuntimeBase ), NULL ); 103 104 if ( session ) 105 { 106 session->_authorization = NULL; 107 session->_client = MACH_PORT_NULL; 108 session->_name = NULL; 109 session->_pid = 0; 110 session->_options = 0; 111 session->_queue = CFArrayCreateMutable( allocator, 0, &kCFTypeArrayCallBacks ); 112 session->_register = CFArrayCreateMutable( allocator, 0, &kCFTypeArrayCallBacks ); 113 session->_server = NULL; 114 session->_source = NULL; 115 session->_state = 0; 116 117 assert( session->_queue ); 118 assert( session->_register ); 119 } 120 121 return session; 122} 123 124static void __DASessionDeallocate( CFTypeRef object ) 125{ 126 DASessionRef session = ( DASessionRef ) object; 127 128 if ( session->_authorization ) AuthorizationFree( session->_authorization, kAuthorizationFlagDefaults ); 129 if ( session->_client ) mach_port_deallocate( mach_task_self( ), session->_client ); 130 if ( session->_name ) free( session->_name ); 131 if ( session->_queue ) CFRelease( session->_queue ); 132 if ( session->_register ) CFRelease( session->_register ); 133 134 if ( session->_source ) 135 { 136 CFRunLoopSourceInvalidate( session->_source ); 137 138 CFRelease( session->_source ); 139 } 140 141 if ( session->_server ) 142 { 143 mach_port_t serverPort; 144 145 serverPort = CFMachPortGetPort( session->_server ); 146 147 CFMachPortInvalidate( session->_server ); 148 149 CFRelease( session->_server ); 150 151 mach_port_mod_refs( mach_task_self( ), serverPort, MACH_PORT_RIGHT_RECEIVE, -1 ); 152 } 153} 154 155static Boolean __DASessionEqual( CFTypeRef object1, CFTypeRef object2 ) 156{ 157 DASessionRef session1 = ( DASessionRef ) object1; 158 DASessionRef session2 = ( DASessionRef ) object2; 159 160 return ( session1->_server == session2->_server ) ? TRUE : FALSE; 161} 162 163static CFHashCode __DASessionHash( CFTypeRef object ) 164{ 165 DASessionRef session = ( DASessionRef ) object; 166 167 return ( CFHashCode ) CFMachPortGetPort( session->_server ); 168} 169 170///w:start 171const char * _DASessionGetName( DASessionRef session ) 172{ 173 return session->_name; 174} 175///w:stop 176DASessionRef DASessionCreate( CFAllocatorRef allocator, const char * _name, pid_t _pid ) 177{ 178 DASessionRef session; 179 180 /* 181 * Create the session. 182 */ 183 184 session = __DASessionCreate( allocator ); 185 186 if ( session ) 187 { 188 mach_port_t serverPort; 189 kern_return_t status; 190 191 status = mach_port_allocate( mach_task_self( ), MACH_PORT_RIGHT_RECEIVE, &serverPort ); 192 193 if ( status == KERN_SUCCESS ) 194 { 195 CFMachPortRef server; 196 CFMachPortContext serverContext; 197 198 serverContext.version = 0; 199 serverContext.info = session; 200 serverContext.retain = NULL; 201 serverContext.release = NULL; 202 serverContext.copyDescription = NULL; 203 204 /* 205 * Create the session's server port. 206 */ 207 208 server = CFMachPortCreateWithPort( allocator, serverPort, _DAServerCallback, &serverContext, NULL ); 209 210 if ( server ) 211 { 212 CFRunLoopSourceRef source; 213 214 /* 215 * Create the session's server port run loop source. 216 */ 217 218 source = CFMachPortCreateRunLoopSource( allocator, server, 0 ); 219 220 if ( source ) 221 { 222 mach_port_t port; 223 224 /* 225 * Set up the session's server port. 226 */ 227 228 status = mach_port_request_notification( mach_task_self( ), 229 CFMachPortGetPort( server ), 230 MACH_NOTIFY_NO_SENDERS, 231 1, 232 CFMachPortGetPort( server ), 233 MACH_MSG_TYPE_MAKE_SEND_ONCE, 234 &port ); 235 236 if ( status == KERN_SUCCESS ) 237 { 238 assert( port == MACH_PORT_NULL ); 239 240 session->_name = strdup( _name ); 241 session->_pid = _pid; 242 session->_server = server; 243 session->_source = source; 244 245 return session; 246 } 247 248 CFRunLoopSourceInvalidate( source ); 249 250 CFRelease( source ); 251 } 252 253 CFMachPortInvalidate( server ); 254 255 CFRelease( server ); 256 } 257 258 mach_port_mod_refs( mach_task_self( ), serverPort, MACH_PORT_RIGHT_RECEIVE, -1 ); 259 } 260 261 CFRelease( session ); 262 } 263 264 return NULL; 265} 266 267AuthorizationRef DASessionGetAuthorization( DASessionRef session ) 268{ 269 return session->_authorization; 270} 271 272CFMutableArrayRef DASessionGetCallbackQueue( DASessionRef session ) 273{ 274 return session->_queue; 275} 276 277CFMutableArrayRef DASessionGetCallbackRegister( DASessionRef session ) 278{ 279 return session->_register; 280} 281 282mach_port_t DASessionGetID( DASessionRef session ) 283{ 284 return CFMachPortGetPort( session->_server ); 285} 286 287Boolean DASessionGetOption( DASessionRef session, DASessionOption option ) 288{ 289 return ( session->_options & option ) ? TRUE : FALSE; 290} 291 292DASessionOptions DASessionGetOptions( DASessionRef session ) 293{ 294 return session->_options; 295} 296 297mach_port_t DASessionGetServerPort( DASessionRef session ) 298{ 299 return CFMachPortGetPort( session->_server ); 300} 301 302Boolean DASessionGetState( DASessionRef session, DASessionState state ) 303{ 304 return ( session->_state & state ) ? TRUE : FALSE; 305} 306 307CFTypeID DASessionGetTypeID( void ) 308{ 309 return __kDASessionTypeID; 310} 311 312void DASessionInitialize( void ) 313{ 314 __kDASessionTypeID = _CFRuntimeRegisterClass( &__DASessionClass ); 315} 316 317void DASessionQueueCallback( DASessionRef session, DACallbackRef callback ) 318{ 319 session->_state &= ~kDASessionStateIdle; 320 321 CFArrayAppendValue( session->_queue, callback ); 322 323 if ( CFArrayGetCount( session->_queue ) == 1 ) 324 { 325 if ( session->_client ) 326 { 327 mach_msg_header_t message; 328 kern_return_t status; 329 330 message.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, 0 ); 331 message.msgh_id = 0; 332 message.msgh_local_port = MACH_PORT_NULL; 333 message.msgh_remote_port = session->_client; 334 message.msgh_reserved = 0; 335 message.msgh_size = sizeof( message ); 336 337 status = mach_msg( &message, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL ); 338 339 if ( status == MACH_SEND_TIMED_OUT ) 340 { 341 mach_msg_destroy( &message ); 342 } 343 } 344 } 345} 346 347void DASessionRegisterCallback( DASessionRef session, DACallbackRef callback ) 348{ 349 CFArrayAppendValue( session->_register, callback ); 350} 351 352void DASessionScheduleWithRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode ) 353{ 354 CFRunLoopAddSource( runLoop, session->_source, runLoopMode ); 355} 356 357void DASessionSetAuthorization( DASessionRef session, AuthorizationRef authorization ) 358{ 359 if ( session->_authorization ) 360 { 361 AuthorizationFree( session->_authorization, kAuthorizationFlagDefaults ); 362 } 363 364 session->_authorization = authorization; 365} 366 367void DASessionSetClientPort( DASessionRef session, mach_port_t client ) 368{ 369 if ( session->_client ) 370 { 371 mach_port_deallocate( mach_task_self( ), session->_client ); 372 } 373 374 session->_client = client; 375 376 if ( CFArrayGetCount( session->_queue ) ) 377 { 378 if ( session->_client ) 379 { 380 mach_msg_header_t message; 381 kern_return_t status; 382 383 message.msgh_bits = MACH_MSGH_BITS( MACH_MSG_TYPE_COPY_SEND, 0 ); 384 message.msgh_id = 0; 385 message.msgh_local_port = MACH_PORT_NULL; 386 message.msgh_remote_port = session->_client; 387 message.msgh_reserved = 0; 388 message.msgh_size = sizeof( message ); 389 390 status = mach_msg( &message, MACH_SEND_MSG | MACH_SEND_TIMEOUT, message.msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL ); 391 392 if ( status == MACH_SEND_TIMED_OUT ) 393 { 394 mach_msg_destroy( &message ); 395 } 396 } 397 } 398} 399 400void DASessionSetOption( DASessionRef session, DASessionOption option, Boolean value ) 401{ 402 DASessionSetOptions( session, option, value ); 403} 404 405void DASessionSetOptions( DASessionRef session, DASessionOptions options, Boolean value ) 406{ 407 session->_options &= ~options; 408 session->_options |= value ? options : 0; 409} 410 411void DASessionSetState( DASessionRef session, DASessionState state, Boolean value ) 412{ 413 session->_state &= ~state; 414 session->_state |= value ? state : 0; 415} 416 417void DASessionUnregisterCallback( DASessionRef session, DACallbackRef callback ) 418{ 419 CFIndex count; 420 CFIndex index; 421 422 count = CFArrayGetCount( session->_register ); 423 424 for ( index = count - 1; index > -1; index-- ) 425 { 426 DACallbackRef item; 427 428 item = ( void * ) CFArrayGetValueAtIndex( session->_register, index ); 429 430 if ( DACallbackGetAddress( item ) == DACallbackGetAddress( callback ) ) 431 { 432 if ( DACallbackGetContext( item ) == DACallbackGetContext( callback ) ) 433 { 434 CFArrayRemoveValueAtIndex( session->_register, index ); 435 } 436 } 437 } 438 439 count = CFArrayGetCount( session->_queue ); 440 441 for ( index = count - 1; index > -1; index-- ) 442 { 443 DACallbackRef item; 444 445 item = ( void * ) CFArrayGetValueAtIndex( session->_queue, index ); 446 447 if ( DACallbackGetAddress( item ) == DACallbackGetAddress( callback ) ) 448 { 449 if ( DACallbackGetContext( item ) == DACallbackGetContext( callback ) ) 450 { 451 CFArrayRemoveValueAtIndex( session->_queue, index ); 452 } 453 } 454 } 455} 456 457void DASessionUnscheduleFromRunLoop( DASessionRef session, CFRunLoopRef runLoop, CFStringRef runLoopMode ) 458{ 459 CFRunLoopRemoveSource( runLoop, session->_source, runLoopMode ); 460} 461