2 * Copyright (c) 2009 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@
24 Copyright (c) 1998-2009, Apple Inc. All rights reserved.
25 Responsibility: Christopher Kane
28 #include <CoreFoundation/CFMessagePort.h>
29 #include <CoreFoundation/CFRunLoop.h>
30 #include <CoreFoundation/CFMachPort.h>
31 #include <CoreFoundation/CFDictionary.h>
32 #include <CoreFoundation/CFByteOrder.h>
34 #include "CFInternal.h"
35 #include <mach/mach.h>
36 #include <mach/message.h>
37 #include <mach/mach_error.h>
38 #include <bootstrap_priv.h>
40 #include <mach/mach_time.h>
42 #include <dispatch/dispatch.h>
44 extern pid_t
getpid(void);
47 #define __kCFMessagePortMaxNameLengthMax 255
49 #if defined(BOOTSTRAP_MAX_NAME_LEN)
50 #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN
52 #define __kCFMessagePortMaxNameLength 128
55 #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength
56 #undef __kCFMessagePortMaxNameLength
57 #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax
60 #define __CFMessagePortMaxDataSize 0x60000000L
63 DISPATCH_HELPER_FUNCTIONS(mport
, CFMessagePort
)
66 static CFSpinLock_t __CFAllMessagePortsLock
= CFSpinLockInit
;
67 static CFMutableDictionaryRef __CFAllLocalMessagePorts
= NULL
;
68 static CFMutableDictionaryRef __CFAllRemoteMessagePorts
= NULL
;
70 struct __CFMessagePort
{
74 CFMachPortRef _port
; /* immutable; invalidated */
75 CFMutableDictionaryRef _replies
;
77 int32_t _perPID
; /* zero if not per-pid, else pid */
78 CFMachPortRef _replyPort
; /* only used by remote port; immutable once created; invalidated */
79 CFRunLoopSourceRef _source
; /* only used by local port; immutable once created; invalidated */
80 dispatch_source_t _dispatchSource
; /* only used by local port; invalidated */
81 dispatch_queue_t _dispatchQ
; /* only used by local port */
82 CFMessagePortInvalidationCallBack _icallout
;
83 CFMessagePortCallBack _callout
; /* only used by local port; immutable */
84 CFMessagePortContext _context
; /* not part of remote port; immutable; invalidated */
87 /* Bit 0 in the base reserved bits is used for invalid state */
88 /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */
89 /* Bit 2 of the base reserved bits is used for is-remote state */
90 /* Bit 3 in the base reserved bits is used for is-deallocing state */
92 CF_INLINE Boolean
__CFMessagePortIsValid(CFMessagePortRef ms
) {
93 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0);
96 CF_INLINE
void __CFMessagePortSetValid(CFMessagePortRef ms
) {
97 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 1);
100 CF_INLINE
void __CFMessagePortUnsetValid(CFMessagePortRef ms
) {
101 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 0, 0, 0);
104 CF_INLINE Boolean
__CFMessagePortExtraMachRef(CFMessagePortRef ms
) {
105 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1);
108 CF_INLINE
void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms
) {
109 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 1);
112 CF_INLINE
void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms
) {
113 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 1, 1, 0);
116 CF_INLINE Boolean
__CFMessagePortIsRemote(CFMessagePortRef ms
) {
117 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2);
120 CF_INLINE
void __CFMessagePortSetRemote(CFMessagePortRef ms
) {
121 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 1);
124 CF_INLINE
void __CFMessagePortUnsetRemote(CFMessagePortRef ms
) {
125 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 2, 2, 0);
128 CF_INLINE Boolean
__CFMessagePortIsDeallocing(CFMessagePortRef ms
) {
129 return (Boolean
)__CFBitfieldGetValue(((const CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3);
132 CF_INLINE
void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms
) {
133 __CFBitfieldSetValue(((CFRuntimeBase
*)ms
)->_cfinfo
[CF_INFO_BITS
], 3, 3, 1);
136 CF_INLINE
void __CFMessagePortLock(CFMessagePortRef ms
) {
137 __CFSpinLock(&(ms
->_lock
));
140 CF_INLINE
void __CFMessagePortUnlock(CFMessagePortRef ms
) {
141 __CFSpinUnlock(&(ms
->_lock
));
145 #define __CFMessagePortMaxInlineBytes 4096*10
147 struct __CFMessagePortMachMessage
{
148 mach_msg_header_t head
;
149 mach_msg_body_t body
;
151 mach_msg_ool_descriptor32_t _0
;
152 mach_msg_ool_descriptor64_t _1
;
153 mach_msg_ool_descriptor_t out_of_line
;
154 } desc
; // ignored for inline bytes messages
160 static struct __CFMessagePortMachMessage
*__CFMessagePortCreateMessage(bool reply
, mach_port_t port
, mach_port_t replyPort
, int32_t convid
, int32_t msgid
, const uint8_t *bytes
, int32_t byteslen
) {
161 if (__CFMessagePortMaxDataSize
< byteslen
) return NULL
;
162 struct __CFMessagePortMachMessage
*msg
;
163 int32_t size
= sizeof(struct __CFMessagePortMachMessage
);
164 int32_t rounded_byteslen
= ((byteslen
+ 3) & ~0x3);
165 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
166 size
+= rounded_byteslen
;
168 msg
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, size
, 0);
169 if (!msg
) return NULL
;
170 memset(msg
, 0, size
);
171 msg
->head
.msgh_id
= convid
;
172 msg
->head
.msgh_size
= size
;
173 msg
->head
.msgh_remote_port
= port
;
174 msg
->head
.msgh_local_port
= replyPort
;
175 msg
->head
.msgh_reserved
= 0;
176 // msg->head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, (replyPort ? MACH_MSG_TYPE_MAKE_SEND : 0));
177 msg
->head
.msgh_bits
= MACH_MSGH_BITS((reply
? MACH_MSG_TYPE_MOVE_SEND_ONCE
: MACH_MSG_TYPE_COPY_SEND
), (MACH_PORT_NULL
!= replyPort
? MACH_MSG_TYPE_MAKE_SEND_ONCE
: 0));
178 msg
->msgid
= CFSwapInt32HostToLittle(msgid
);
179 msg
->byteslen
= CFSwapInt32HostToLittle(byteslen
);
180 if (rounded_byteslen
<= __CFMessagePortMaxInlineBytes
) {
181 msg
->body
.msgh_descriptor_count
= 0;
182 if (NULL
!= bytes
&& 0 < byteslen
) {
183 memmove(msg
->bytes
, bytes
, byteslen
);
186 msg
->head
.msgh_bits
|= MACH_MSGH_BITS_COMPLEX
;
187 msg
->body
.msgh_descriptor_count
= 1;
188 msg
->desc
.out_of_line
.deallocate
= false;
189 msg
->desc
.out_of_line
.copy
= MACH_MSG_VIRTUAL_COPY
;
190 msg
->desc
.out_of_line
.address
= (void *)bytes
;
191 msg
->desc
.out_of_line
.size
= byteslen
;
192 msg
->desc
.out_of_line
.type
= MACH_MSG_OOL_DESCRIPTOR
;
197 static CFStringRef
__CFMessagePortCopyDescription(CFTypeRef cf
) {
198 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
201 CFStringRef contextDesc
= NULL
;
202 locked
= ms
->_lock
? "Yes" : "No";
203 if (__CFMessagePortIsRemote(ms
)) {
204 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
);
206 if (NULL
!= ms
->_context
.info
&& NULL
!= ms
->_context
.copyDescription
) {
207 contextDesc
= ms
->_context
.copyDescription(ms
->_context
.info
);
209 if (NULL
== contextDesc
) {
210 contextDesc
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort context %p>"), ms
->_context
.info
);
212 void *addr
= ms
->_callout
;
214 const char *name
= (dladdr(addr
, &info
) && info
.dli_saddr
== addr
&& info
.dli_sname
) ? info
.dli_sname
: "???";
215 result
= CFStringCreateWithFormat(kCFAllocatorSystemDefault
, NULL
, CFSTR("<CFMessagePort %p [%p]>{locked = %s, valid = %s, remote = %s, name = %@, source = %p, callout = %s (%p), context = %@}"), cf
, CFGetAllocator(ms
), locked
, (__CFMessagePortIsValid(ms
) ? "Yes" : "No"), (__CFMessagePortIsRemote(ms
) ? "Yes" : "No"), ms
->_name
, ms
->_source
, name
, addr
, (NULL
!= contextDesc
? contextDesc
: CFSTR("<no description>")));
217 if (NULL
!= contextDesc
) {
218 CFRelease(contextDesc
);
223 static void __CFMessagePortDeallocate(CFTypeRef cf
) {
224 CFMessagePortRef ms
= (CFMessagePortRef
)cf
;
225 __CFMessagePortSetIsDeallocing(ms
);
226 CFMessagePortInvalidate(ms
);
227 // Delay cleanup of _replies until here so that invalidation during
228 // SendRequest does not cause _replies to disappear out from under that function.
229 if (NULL
!= ms
->_replies
) {
230 CFRelease(ms
->_replies
);
232 if (NULL
!= ms
->_name
) {
233 CFRelease(ms
->_name
);
235 if (NULL
!= ms
->_port
) {
236 if (__CFMessagePortExtraMachRef(ms
)) {
237 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_SEND
, -1);
238 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms
->_port
), MACH_PORT_RIGHT_RECEIVE
, -1);
240 CFMachPortInvalidate(ms
->_port
);
241 CFRelease(ms
->_port
);
244 // A remote message port for a local message port in the same process will get the
245 // same mach port, and the remote port will keep the mach port from being torn down,
246 // thus keeping the remote port from getting any sort of death notification and
247 // auto-invalidating; so we manually implement the 'auto-invalidation' here by
248 // tickling each remote port to check its state after any message port is destroyed,
249 // but most importantly after local message ports are destroyed.
250 __CFSpinLock(&__CFAllMessagePortsLock
);
251 CFMessagePortRef
*remotePorts
= NULL
;
253 if (NULL
!= __CFAllRemoteMessagePorts
) {
254 cnt
= CFDictionaryGetCount(__CFAllRemoteMessagePorts
);
255 remotePorts
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, cnt
* sizeof(CFMessagePortRef
), __kCFAllocatorGCScannedMemory
);
256 CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts
, NULL
, (const void **)remotePorts
);
257 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
258 CFRetain(remotePorts
[idx
]);
261 __CFSpinUnlock(&__CFAllMessagePortsLock
);
263 for (CFIndex idx
= 0; idx
< cnt
; idx
++) {
264 // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid
265 CFMessagePortIsValid(remotePorts
[idx
]);
266 CFRelease(remotePorts
[idx
]);
268 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, remotePorts
);
272 static CFTypeID __kCFMessagePortTypeID
= _kCFRuntimeNotATypeID
;
274 static const CFRuntimeClass __CFMessagePortClass
= {
279 __CFMessagePortDeallocate
,
283 __CFMessagePortCopyDescription
286 __private_extern__
void __CFMessagePortInitialize(void) {
287 __kCFMessagePortTypeID
= _CFRuntimeRegisterClass(&__CFMessagePortClass
);
290 CFTypeID
CFMessagePortGetTypeID(void) {
291 return __kCFMessagePortTypeID
;
294 static CFStringRef
__CFMessagePortSanitizeStringName(CFStringRef name
, uint8_t **utfnamep
, CFIndex
*utfnamelenp
) {
298 utfname
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, __kCFMessagePortMaxNameLength
+ 1, 0);
299 CFStringGetBytes(name
, CFRangeMake(0, CFStringGetLength(name
)), kCFStringEncodingUTF8
, 0, false, utfname
, __kCFMessagePortMaxNameLength
, &utflen
);
300 utfname
[utflen
] = '\0';
301 /* A new string is created, because the original string may have been
302 truncated to the max length, and we want the string name to definitely
303 match the raw UTF-8 chunk that has been created. Also, this is useful
304 to get a constant string in case the original name string was mutable. */
305 result
= CFStringCreateWithBytes(kCFAllocatorSystemDefault
, utfname
, utflen
, kCFStringEncodingUTF8
, false);
306 if (NULL
!= utfnamep
) {
309 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
311 if (NULL
!= utfnamelenp
) {
312 *utfnamelenp
= utflen
;
317 static void __CFMessagePortDummyCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
318 // not supposed to be implemented
321 static void __CFMessagePortInvalidationCallBack(CFMachPortRef port
, void *info
) {
322 // info has been setup as the CFMessagePort owning the CFMachPort
323 if (info
) CFMessagePortInvalidate(info
);
326 static CFMessagePortRef
__CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
, Boolean perPID
) {
327 CFMessagePortRef memory
;
328 uint8_t *utfname
= NULL
;
330 if (shouldFreeInfo
) *shouldFreeInfo
= true;
332 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
334 __CFSpinLock(&__CFAllMessagePortsLock
);
335 if (!perPID
&& NULL
!= name
) {
336 CFMessagePortRef existing
;
337 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
339 __CFSpinUnlock(&__CFAllMessagePortsLock
);
341 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
342 return (CFMessagePortRef
)(existing
);
345 __CFSpinUnlock(&__CFAllMessagePortsLock
);
346 CFIndex size
= sizeof(struct __CFMessagePort
) - sizeof(CFRuntimeBase
);
347 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
348 if (NULL
== memory
) {
352 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
355 __CFMessagePortUnsetValid(memory
);
356 __CFMessagePortUnsetExtraMachRef(memory
);
357 __CFMessagePortUnsetRemote(memory
);
358 memory
->_lock
= CFSpinLockInit
;
359 memory
->_name
= name
;
360 memory
->_port
= NULL
;
361 memory
->_replies
= NULL
;
362 memory
->_convCounter
= 0;
363 memory
->_perPID
= perPID
? getpid() : 0; // actual value not terribly useful for local ports
364 memory
->_replyPort
= NULL
;
365 memory
->_source
= NULL
;
366 memory
->_dispatchSource
= NULL
;
367 memory
->_dispatchQ
= NULL
;
368 memory
->_icallout
= NULL
;
369 memory
->_callout
= callout
;
370 memory
->_context
.info
= NULL
;
371 memory
->_context
.retain
= NULL
;
372 memory
->_context
.release
= NULL
;
373 memory
->_context
.copyDescription
= NULL
;
376 CFMachPortRef native
= NULL
;
379 task_get_bootstrap_port(mach_task_self(), &bs
);
381 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
382 if (ret
== KERN_SUCCESS
) {
383 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
384 if (KERN_SUCCESS
== ret
) {
385 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
386 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
387 __CFMessagePortSetExtraMachRef(memory
);
389 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: mach_port_insert_member() after bootstrap_check_in(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'"), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
390 mach_port_destroy(mach_task_self(), mp
);
391 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
392 // name is released by deallocation
399 CFMachPortContext ctx
= {0, memory
, NULL
, NULL
, NULL
};
400 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
402 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
403 // name is released by deallocation
407 mp
= CFMachPortGetPort(native
);
408 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
409 if (ret
!= KERN_SUCCESS
) {
410 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
411 CFMachPortInvalidate(native
);
413 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
414 // name is released by deallocation
419 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
420 memory
->_port
= native
;
423 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
424 __CFMessagePortSetValid(memory
);
425 if (NULL
!= context
) {
426 memmove(&memory
->_context
, context
, sizeof(CFMessagePortContext
));
427 memory
->_context
.info
= context
->retain
? (void *)context
->retain(context
->info
) : context
->info
;
429 __CFSpinLock(&__CFAllMessagePortsLock
);
430 if (!perPID
&& NULL
!= name
) {
431 CFMessagePortRef existing
;
432 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
434 __CFSpinUnlock(&__CFAllMessagePortsLock
);
436 return (CFMessagePortRef
)(existing
);
438 if (NULL
== __CFAllLocalMessagePorts
) {
439 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
441 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, memory
);
443 __CFSpinUnlock(&__CFAllMessagePortsLock
);
444 if (shouldFreeInfo
) *shouldFreeInfo
= false;
448 CFMessagePortRef
CFMessagePortCreateLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
449 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, false);
452 CFMessagePortRef
CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator
, CFStringRef name
, CFMessagePortCallBack callout
, CFMessagePortContext
*context
, Boolean
*shouldFreeInfo
) {
453 return __CFMessagePortCreateLocal(allocator
, name
, callout
, context
, shouldFreeInfo
, true);
456 static CFMessagePortRef
__CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
, Boolean perPID
, CFIndex pid
) {
457 CFMessagePortRef memory
;
458 CFMachPortRef native
;
459 CFMachPortContext ctx
;
460 uint8_t *utfname
= NULL
;
462 mach_port_t bp
, port
;
465 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
469 __CFSpinLock(&__CFAllMessagePortsLock
);
470 if (!perPID
&& NULL
!= name
) {
471 CFMessagePortRef existing
;
472 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
474 __CFSpinUnlock(&__CFAllMessagePortsLock
);
476 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
477 return (CFMessagePortRef
)(existing
);
480 __CFSpinUnlock(&__CFAllMessagePortsLock
);
481 size
= sizeof(struct __CFMessagePort
) - sizeof(CFMessagePortContext
) - sizeof(CFRuntimeBase
);
482 memory
= (CFMessagePortRef
)_CFRuntimeCreateInstance(allocator
, __kCFMessagePortTypeID
, size
, NULL
);
483 if (NULL
== memory
) {
487 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
490 __CFMessagePortUnsetValid(memory
);
491 __CFMessagePortUnsetExtraMachRef(memory
);
492 __CFMessagePortSetRemote(memory
);
493 memory
->_lock
= CFSpinLockInit
;
494 memory
->_name
= name
;
495 memory
->_port
= NULL
;
496 memory
->_replies
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, NULL
, NULL
);
497 memory
->_convCounter
= 0;
498 memory
->_perPID
= perPID
? pid
: 0;
499 memory
->_replyPort
= NULL
;
500 memory
->_source
= NULL
;
501 memory
->_dispatchSource
= NULL
;
502 memory
->_dispatchQ
= NULL
;
503 memory
->_icallout
= NULL
;
504 memory
->_callout
= NULL
;
509 ctx
.copyDescription
= NULL
;
510 task_get_bootstrap_port(mach_task_self(), &bp
);
511 ret
= bootstrap_look_up2(bp
, (char *)utfname
, &port
, perPID
? (pid_t
)pid
: 0, perPID
? BOOTSTRAP_PER_PID_SERVICE
: 0);
512 native
= (KERN_SUCCESS
== ret
) ? CFMachPortCreateWithPort(allocator
, port
, __CFMessagePortDummyCallback
, &ctx
, NULL
) : NULL
;
513 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
514 if (NULL
== native
) {
515 // name is released by deallocation
519 memory
->_port
= native
;
520 __CFMessagePortSetValid(memory
);
521 __CFSpinLock(&__CFAllMessagePortsLock
);
522 if (!perPID
&& NULL
!= name
) {
523 CFMessagePortRef existing
;
524 if (NULL
!= __CFAllRemoteMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts
, name
, (const void **)&existing
)) {
526 __CFSpinUnlock(&__CFAllMessagePortsLock
);
528 return (CFMessagePortRef
)(existing
);
530 if (NULL
== __CFAllRemoteMessagePorts
) {
531 __CFAllRemoteMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
533 CFDictionaryAddValue(__CFAllRemoteMessagePorts
, name
, memory
);
535 __CFSpinUnlock(&__CFAllMessagePortsLock
);
536 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
537 // that set-invalidation-callback might have called back into us
538 // if the CFMachPort is already bad, but that was a no-op since
539 // there was no callback setup at the (previous) time the CFMachPort
540 // went invalid; so check for validity manually and react
541 if (!CFMachPortIsValid(native
)) {
542 CFRelease(memory
); // does the invalidate
545 return (CFMessagePortRef
)memory
;
548 CFMessagePortRef
CFMessagePortCreateRemote(CFAllocatorRef allocator
, CFStringRef name
) {
549 return __CFMessagePortCreateRemote(allocator
, name
, false, 0);
552 CFMessagePortRef
CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator
, CFStringRef name
, CFIndex pid
) {
553 return __CFMessagePortCreateRemote(allocator
, name
, true, pid
);
556 Boolean
CFMessagePortIsRemote(CFMessagePortRef ms
) {
557 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
558 return __CFMessagePortIsRemote(ms
);
561 CFStringRef
CFMessagePortGetName(CFMessagePortRef ms
) {
562 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
566 Boolean
CFMessagePortSetName(CFMessagePortRef ms
, CFStringRef name
) {
567 CFAllocatorRef allocator
= CFGetAllocator(ms
);
568 uint8_t *utfname
= NULL
;
570 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
571 if (ms
->_perPID
|| __CFMessagePortIsRemote(ms
)) return false;
572 name
= __CFMessagePortSanitizeStringName(name
, &utfname
, NULL
);
576 __CFSpinLock(&__CFAllMessagePortsLock
);
578 CFMessagePortRef existing
;
579 if (NULL
!= __CFAllLocalMessagePorts
&& CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts
, name
, (const void **)&existing
)) {
580 __CFSpinUnlock(&__CFAllMessagePortsLock
);
582 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
586 __CFSpinUnlock(&__CFAllMessagePortsLock
);
588 if (NULL
!= name
&& (NULL
== ms
->_name
|| !CFEqual(ms
->_name
, name
))) {
589 CFMachPortRef oldPort
= ms
->_port
;
590 CFMachPortRef native
= NULL
;
593 task_get_bootstrap_port(mach_task_self(), &bs
);
594 ret
= bootstrap_check_in(bs
, (char *)utfname
, &mp
); /* If we're started by launchd or the old mach_init */
595 if (ret
== KERN_SUCCESS
) {
596 ret
= mach_port_insert_right(mach_task_self(), mp
, mp
, MACH_MSG_TYPE_MAKE_SEND
);
597 if (KERN_SUCCESS
== ret
) {
598 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
599 native
= CFMachPortCreateWithPort(allocator
, mp
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
600 __CFMessagePortSetExtraMachRef(ms
);
602 mach_port_destroy(mach_task_self(), mp
);
603 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
609 CFMachPortContext ctx
= {0, ms
, NULL
, NULL
, NULL
};
610 native
= CFMachPortCreate(allocator
, __CFMessagePortDummyCallback
, &ctx
, NULL
);
612 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
616 mp
= CFMachPortGetPort(native
);
617 ret
= bootstrap_register2(bs
, (char *)utfname
, mp
, 0);
618 if (ret
!= KERN_SUCCESS
) {
619 CFLog(kCFLogLevelDebug
, CFSTR("*** CFMessagePort: bootstrap_register(): failed %d (0x%x) '%s', port = 0x%x, name = '%s'\nSee /usr/include/servers/bootstrap_defs.h for the error codes."), ret
, ret
, bootstrap_strerror(ret
), mp
, utfname
);
620 CFMachPortInvalidate(native
);
622 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
627 CFMachPortSetInvalidationCallBack(native
, __CFMessagePortInvalidationCallBack
);
629 if (NULL
!= oldPort
&& oldPort
!= native
) {
630 if (__CFMessagePortExtraMachRef(ms
)) {
631 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_SEND
, -1);
632 mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort
), MACH_PORT_RIGHT_RECEIVE
, -1);
634 CFMachPortInvalidate(oldPort
);
637 __CFSpinLock(&__CFAllMessagePortsLock
);
638 // This relocking without checking to see if something else has grabbed
639 // that name in the cache is rather suspect, but what would that even
640 // mean has happened? We'd expect the bootstrap_* calls above to have
641 // failed for this one and not gotten this far, or failed for all of the
642 // other simultaneous attempts to get the name (and having succeeded for
643 // this one, gotten here). So we're not going to try very hard here
644 // with the thread-safety.
645 if (NULL
== __CFAllLocalMessagePorts
) {
646 __CFAllLocalMessagePorts
= CFDictionaryCreateMutable(kCFAllocatorSystemDefault
, 0, &kCFTypeDictionaryKeyCallBacks
, NULL
);
648 if (NULL
!= ms
->_name
) {
649 CFDictionaryRemoveValue(__CFAllLocalMessagePorts
, ms
->_name
);
650 CFRelease(ms
->_name
);
653 CFDictionaryAddValue(__CFAllLocalMessagePorts
, name
, ms
);
654 __CFSpinUnlock(&__CFAllMessagePortsLock
);
657 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, utfname
);
661 void CFMessagePortGetContext(CFMessagePortRef ms
, CFMessagePortContext
*context
) {
662 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
663 //#warning CF: assert that this is a local port
664 CFAssert1(0 == context
->version
, __kCFLogAssertion
, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__
);
665 memmove(context
, &ms
->_context
, sizeof(CFMessagePortContext
));
668 void CFMessagePortInvalidate(CFMessagePortRef ms
) {
669 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
670 if (!__CFMessagePortIsDeallocing(ms
)) {
673 __CFMessagePortLock(ms
);
674 if (__CFMessagePortIsValid(ms
)) {
675 if (ms
->_dispatchSource
) {
676 dispatch_cancel(ms
->_dispatchSource
);
677 ms
->_dispatchSource
= NULL
;
678 ms
->_dispatchQ
= NULL
;
681 CFMessagePortInvalidationCallBack callout
= ms
->_icallout
;
682 CFRunLoopSourceRef source
= ms
->_source
;
683 CFMachPortRef replyPort
= ms
->_replyPort
;
684 CFMachPortRef port
= ms
->_port
;
685 CFStringRef name
= ms
->_name
;
688 __CFMessagePortUnsetValid(ms
);
689 if (!__CFMessagePortIsRemote(ms
)) {
690 info
= ms
->_context
.info
;
691 ms
->_context
.info
= NULL
;
694 ms
->_replyPort
= NULL
;
695 __CFMessagePortUnlock(ms
);
697 __CFSpinLock(&__CFAllMessagePortsLock
);
698 if (0 == ms
->_perPID
&& NULL
!= (__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
)) {
699 CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms
) ? __CFAllRemoteMessagePorts
: __CFAllLocalMessagePorts
, name
);
701 __CFSpinUnlock(&__CFAllMessagePortsLock
);
702 if (NULL
!= callout
) {
705 // We already know we're going invalid, don't need this callback
706 // anymore; plus, this solves a reentrancy deadlock; also, this
707 // must be done before the deallocate of the Mach port, to
708 // avoid a race between the notification message which could be
709 // handled in another thread, and this NULL'ing out.
710 CFMachPortSetInvalidationCallBack(port
, NULL
);
711 // For hashing and equality purposes, cannot get rid of _port here
712 if (!__CFMessagePortIsRemote(ms
) && NULL
!= ms
->_context
.release
) {
713 ms
->_context
.release(info
);
715 if (NULL
!= source
) {
716 CFRunLoopSourceInvalidate(source
);
719 if (NULL
!= replyPort
) {
720 CFMachPortInvalidate(replyPort
);
721 CFRelease(replyPort
);
723 if (__CFMessagePortIsRemote(ms
)) {
724 // Get rid of our extra ref on the Mach port gotten from bs server
725 mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port
));
728 __CFMessagePortUnlock(ms
);
730 if (!__CFMessagePortIsDeallocing(ms
)) {
735 Boolean
CFMessagePortIsValid(CFMessagePortRef ms
) {
736 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
737 if (!__CFMessagePortIsValid(ms
)) return false;
738 if (NULL
!= ms
->_port
&& !CFMachPortIsValid(ms
->_port
)) {
739 CFMessagePortInvalidate(ms
);
742 if (NULL
!= ms
->_replyPort
&& !CFMachPortIsValid(ms
->_replyPort
)) {
743 CFMessagePortInvalidate(ms
);
746 if (NULL
!= ms
->_source
&& !CFRunLoopSourceIsValid(ms
->_source
)) {
747 CFMessagePortInvalidate(ms
);
753 CFMessagePortInvalidationCallBack
CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms
) {
754 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
755 return ms
->_icallout
;
758 void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms
, CFMessagePortInvalidationCallBack callout
) {
759 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
760 if (!__CFMessagePortIsValid(ms
) && NULL
!= callout
) {
761 callout(ms
, ms
->_context
.info
);
763 ms
->_icallout
= callout
;
767 static void __CFMessagePortReplyCallBack(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
) {
768 CFMessagePortRef ms
= info
;
769 struct __CFMessagePortMachMessage
*msgp
= msg
;
770 struct __CFMessagePortMachMessage
*replymsg
;
771 __CFMessagePortLock(ms
);
772 if (!__CFMessagePortIsValid(ms
)) {
773 __CFMessagePortUnlock(ms
);
777 int32_t byteslen
= 0;
779 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
780 invalidComplex
= invalidComplex
|| ((msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
781 Boolean wayTooBig
= sizeof(struct __CFMessagePortMachMessage
) + __CFMessagePortMaxInlineBytes
< msgp
->head
.msgh_size
;
782 Boolean wayTooSmall
= msgp
->head
.msgh_size
< sizeof(struct __CFMessagePortMachMessage
);
783 Boolean wrongSize
= false;
784 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
785 byteslen
= CFSwapInt32LittleToHost(msgp
->byteslen
);
786 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
787 if (msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
788 wrongSize
= wrongSize
|| (msgp
->desc
.out_of_line
.size
!= byteslen
);
790 wrongSize
= wrongSize
|| (msgp
->head
.msgh_size
- sizeof(struct __CFMessagePortMachMessage
) < byteslen
);
793 Boolean invalidMsgID
= (0 <= msgp
->head
.msgh_id
) && (msgp
->head
.msgh_id
<= INT32_MAX
); // conversation id
794 if (invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
795 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d)"), invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
796 mach_msg_destroy((mach_msg_header_t
*)msgp
);
797 __CFMessagePortUnlock(ms
);
801 if (CFDictionaryContainsKey(ms
->_replies
, (void *)(uintptr_t)msgp
->head
.msgh_id
)) {
802 CFDataRef reply
= NULL
;
803 replymsg
= (struct __CFMessagePortMachMessage
*)msg
;
804 if (0 == replymsg
->body
.msgh_descriptor_count
) {
805 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->head
.msgh_size
);
806 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(replymsg
->bytes
) + byteslen
);
807 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
808 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->bytes
, byteslen
);
810 reply
= (void *)~0; // means NULL data
813 //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here
814 reply
= CFDataCreate(kCFAllocatorSystemDefault
, replymsg
->desc
.out_of_line
.address
, replymsg
->desc
.out_of_line
.size
);
815 vm_deallocate(mach_task_self(), (vm_address_t
)replymsg
->desc
.out_of_line
.address
, replymsg
->desc
.out_of_line
.size
);
817 CFDictionarySetValue(ms
->_replies
, (void *)(uintptr_t)msgp
->head
.msgh_id
, (void *)reply
);
818 } else { /* discard message */
819 if (1 == msgp
->body
.msgh_descriptor_count
) {
820 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->desc
.out_of_line
.address
, msgp
->desc
.out_of_line
.size
);
823 __CFMessagePortUnlock(ms
);
826 SInt32
CFMessagePortSendRequest(CFMessagePortRef remote
, SInt32 msgid
, CFDataRef data
, CFTimeInterval sendTimeout
, CFTimeInterval rcvTimeout
, CFStringRef replyMode
, CFDataRef
*returnDatap
) {
827 struct __CFMessagePortMachMessage
*sendmsg
;
828 CFRunLoopRef currentRL
= CFRunLoopGetCurrent();
829 CFRunLoopSourceRef source
= NULL
;
830 CFDataRef reply
= NULL
;
832 uint32_t sendOpts
= 0, sendTimeOut
= 0;
833 int32_t desiredReply
;
834 Boolean didRegister
= false;
837 //#warning CF: This should be an assert
838 // if (!__CFMessagePortIsRemote(remote)) return -999;
839 if (data
&& __CFMessagePortMaxDataSize
< CFDataGetLength(data
)) {
840 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
841 return kCFMessagePortTransportError
;
843 __CFMessagePortLock(remote
);
844 if (!__CFMessagePortIsValid(remote
)) {
845 __CFMessagePortUnlock(remote
);
846 return kCFMessagePortIsInvalid
;
848 CFRetain(remote
); // retain during run loop to avoid invalidation causing freeing
849 if (NULL
== remote
->_replyPort
) {
850 CFMachPortContext context
;
852 context
.info
= remote
;
853 context
.retain
= (const void *(*)(const void *))CFRetain
;
854 context
.release
= (void (*)(const void *))CFRelease
;
855 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
856 remote
->_replyPort
= CFMachPortCreate(CFGetAllocator(remote
), __CFMessagePortReplyCallBack
, &context
, NULL
);
858 remote
->_convCounter
++;
859 desiredReply
= -remote
->_convCounter
;
860 sendmsg
= __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote
->_port
), (replyMode
!= NULL
? CFMachPortGetPort(remote
->_replyPort
) : MACH_PORT_NULL
), -desiredReply
, msgid
, (data
? CFDataGetBytePtr(data
) : NULL
), (data
? CFDataGetLength(data
) : 0));
862 __CFMessagePortUnlock(remote
);
864 return kCFMessagePortTransportError
;
866 if (replyMode
!= NULL
) {
867 CFDictionarySetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
, NULL
);
868 source
= CFMachPortCreateRunLoopSource(CFGetAllocator(remote
), remote
->_replyPort
, -100);
869 didRegister
= !CFRunLoopContainsSource(currentRL
, source
, replyMode
);
871 CFRunLoopAddSource(currentRL
, source
, replyMode
);
874 if (sendTimeout
< 10.0*86400) {
875 // anything more than 10 days is no timeout!
876 sendOpts
= MACH_SEND_TIMEOUT
;
877 sendTimeout
*= 1000.0;
878 if (sendTimeout
< 1.0) sendTimeout
= 0.0;
879 sendTimeOut
= floor(sendTimeout
);
881 ret
= mach_msg((mach_msg_header_t
*)sendmsg
, MACH_SEND_MSG
|sendOpts
, sendmsg
->head
.msgh_size
, 0, MACH_PORT_NULL
, sendTimeOut
, MACH_PORT_NULL
);
882 if (KERN_SUCCESS
!= ret
) {
883 // need to deallocate the send-once right that might have been created
884 if (replyMode
!= NULL
) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t
*)sendmsg
)->msgh_local_port
);
886 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
888 if (source
) CFRelease(source
);
889 __CFMessagePortUnlock(remote
);
890 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
892 return (MACH_SEND_TIMED_OUT
== ret
) ? kCFMessagePortSendTimeout
: kCFMessagePortTransportError
;
894 __CFMessagePortUnlock(remote
);
895 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, sendmsg
);
896 if (replyMode
== NULL
) {
898 return kCFMessagePortSuccess
;
900 _CFMachPortInstallNotifyPort(currentRL
, replyMode
);
901 termTSR
= mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout
);
903 CFRunLoopRunInMode(replyMode
, __CFTSRToTimeInterval(termTSR
- mach_absolute_time()), true);
904 // warning: what, if anything, should be done if remote is now invalid?
905 reply
= CFDictionaryGetValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
906 if (NULL
!= reply
|| termTSR
< (int64_t)mach_absolute_time()) {
909 if (!CFMessagePortIsValid(remote
)) {
910 // no reason that reply port alone should go invalid so we don't check for that
914 // Should we uninstall the notify port? A complex question...
916 CFRunLoopRemoveSource(currentRL
, source
, replyMode
);
918 if (source
) CFRelease(source
);
920 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
922 return CFMessagePortIsValid(remote
) ? kCFMessagePortReceiveTimeout
: kCFMessagePortBecameInvalidError
;
924 if (NULL
!= returnDatap
) {
925 *returnDatap
= ((void *)~0 == reply
) ? NULL
: reply
;
926 } else if ((void *)~0 != reply
) {
929 CFDictionaryRemoveValue(remote
->_replies
, (void *)(uintptr_t)desiredReply
);
931 return kCFMessagePortSuccess
;
934 static mach_port_t
__CFMessagePortGetPort(void *info
) {
935 CFMessagePortRef ms
= info
;
936 if (!ms
->_port
) CFLog(kCFLogLevelWarning
, CFSTR("*** Warning: A local CFMessagePort (%p) is being put in a run loop or dispatch queue, but it has not been named yet, so this will be a no-op and no messages are going to be received, even if named later."), info
);
937 return ms
->_port
? CFMachPortGetPort(ms
->_port
) : MACH_PORT_NULL
;
940 static void *__CFMessagePortPerform(void *msg
, CFIndex size
, CFAllocatorRef allocator
, void *info
) {
941 CFMessagePortRef ms
= info
;
942 struct __CFMessagePortMachMessage
*msgp
= msg
;
943 struct __CFMessagePortMachMessage
*replymsg
;
945 void (*context_release
)(const void *);
946 CFDataRef returnData
, data
= NULL
;
947 void *return_bytes
= NULL
;
948 CFIndex return_len
= 0;
951 __CFMessagePortLock(ms
);
952 if (!__CFMessagePortIsValid(ms
)) {
953 __CFMessagePortUnlock(ms
);
956 if (NULL
!= ms
->_context
.retain
) {
957 context_info
= (void *)ms
->_context
.retain(ms
->_context
.info
);
958 context_release
= ms
->_context
.release
;
960 context_info
= ms
->_context
.info
;
961 context_release
= NULL
;
963 __CFMessagePortUnlock(ms
);
965 int32_t byteslen
= 0;
967 Boolean invalidComplex
= (0 != msgp
->body
.msgh_descriptor_count
) && !(msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
);
968 invalidComplex
= invalidComplex
|| ((msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) && (0 == msgp
->body
.msgh_descriptor_count
));
969 Boolean wayTooBig
= sizeof(struct __CFMessagePortMachMessage
) + __CFMessagePortMaxInlineBytes
< msgp
->head
.msgh_size
;
970 Boolean wayTooSmall
= msgp
->head
.msgh_size
< sizeof(struct __CFMessagePortMachMessage
);
971 Boolean wrongSize
= false;
972 if (!(invalidComplex
|| wayTooBig
|| wayTooSmall
)) {
973 byteslen
= CFSwapInt32LittleToHost(msgp
->byteslen
);
974 wrongSize
= (byteslen
< 0) || (__CFMessagePortMaxDataSize
< byteslen
);
975 if (msgp
->head
.msgh_bits
& MACH_MSGH_BITS_COMPLEX
) {
976 wrongSize
= wrongSize
|| (msgp
->desc
.out_of_line
.size
!= byteslen
);
978 wrongSize
= wrongSize
|| (msgp
->head
.msgh_size
- sizeof(struct __CFMessagePortMachMessage
) < byteslen
);
981 Boolean invalidMsgID
= (msgp
->head
.msgh_id
<= 0) || (INT32_MAX
< msgp
->head
.msgh_id
); // conversation id
982 if (invalidComplex
|| wayTooBig
|| wayTooSmall
|| wrongSize
|| invalidMsgID
) {
983 mach_msg_security_trailer_t
*trailer
= (void *)msgp
+ msgp
->head
.msgh_size
;
984 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d)"), invalidComplex
, wayTooBig
, wayTooSmall
, wrongSize
, invalidMsgID
);
985 mach_msg_destroy((mach_msg_header_t
*)msgp
);
989 /* Create no-copy, no-free-bytes wrapper CFData */
990 if (0 == msgp
->body
.msgh_descriptor_count
) {
991 uintptr_t msgp_extent
= (uintptr_t)((uint8_t *)msgp
+ msgp
->head
.msgh_size
);
992 uintptr_t data_extent
= (uintptr_t)((uint8_t *)&(msgp
->bytes
) + byteslen
);
993 msgid
= CFSwapInt32LittleToHost(msgp
->msgid
);
994 if (0 <= byteslen
&& data_extent
<= msgp_extent
) {
995 data
= CFDataCreateWithBytesNoCopy(allocator
, msgp
->bytes
, byteslen
, kCFAllocatorNull
);
998 msgid
= CFSwapInt32LittleToHost(msgp
->msgid
);
999 data
= CFDataCreateWithBytesNoCopy(allocator
, msgp
->desc
.out_of_line
.address
, msgp
->desc
.out_of_line
.size
, kCFAllocatorNull
);
1001 returnData
= ms
->_callout(ms
, msgid
, data
, context_info
);
1002 /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE,
1003 (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE,
1004 (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return
1005 bytes inline in the Mach message, so can release the returnData object
1006 here. In cases (3) and (5), we'll send the data out-of-line, we need to
1007 create a copy of the memory, which we'll have the kernel autodeallocate
1008 for us on send. In case (4) also, the bytes in the return data may be part
1009 of the bytes in "data" that we sent into the callout, so if the incoming
1010 data was received out of line, we wouldn't be able to clean up the out-of-line
1011 wad until the message was sent either, if we didn't make the copy. */
1012 if (NULL
!= returnData
) {
1013 return_len
= CFDataGetLength(returnData
);
1014 if (__CFMessagePortMaxDataSize
< return_len
) {
1015 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize
);
1019 if (returnData
&& return_len
< __CFMessagePortMaxInlineBytes
) {
1020 return_bytes
= (void *)CFDataGetBytePtr(returnData
);
1021 } else if (returnData
) {
1022 return_bytes
= NULL
;
1023 vm_allocate(mach_task_self(), (vm_address_t
*)&return_bytes
, return_len
, VM_FLAGS_ANYWHERE
| VM_MAKE_TAG(VM_MEMORY_MACH_MSG
));
1024 /* vm_copy would only be a win here if the source address
1025 is page aligned; it is a lose in all other cases, since
1026 the kernel will just do the memmove for us (but not in
1027 as simple a way). */
1028 memmove(return_bytes
, CFDataGetBytePtr(returnData
), return_len
);
1031 replymsg
= __CFMessagePortCreateMessage(true, msgp
->head
.msgh_remote_port
, MACH_PORT_NULL
, -1 * (int32_t)msgp
->head
.msgh_id
, msgid
, return_bytes
, return_len
);
1032 if (1 == replymsg
->body
.msgh_descriptor_count
) {
1033 replymsg
->desc
.out_of_line
.deallocate
= true;
1035 if (data
) CFRelease(data
);
1036 if (1 == msgp
->body
.msgh_descriptor_count
) {
1037 vm_deallocate(mach_task_self(), (vm_address_t
)msgp
->desc
.out_of_line
.address
, msgp
->desc
.out_of_line
.size
);
1039 if (returnData
) CFRelease(returnData
);
1040 if (context_release
) {
1041 context_release(context_info
);
1046 CFRunLoopSourceRef
CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator
, CFMessagePortRef ms
, CFIndex order
) {
1047 CFRunLoopSourceRef result
= NULL
;
1048 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1049 //#warning CF: This should be an assert
1050 // if (__CFMessagePortIsRemote(ms)) return NULL;
1051 __CFMessagePortLock(ms
);
1052 if (NULL
== ms
->_source
&& NULL
== ms
->_dispatchSource
&& __CFMessagePortIsValid(ms
)) {
1053 CFRunLoopSourceContext1 context
;
1054 context
.version
= 1;
1055 context
.info
= (void *)ms
;
1056 context
.retain
= (const void *(*)(const void *))CFRetain
;
1057 context
.release
= (void (*)(const void *))CFRelease
;
1058 context
.copyDescription
= (CFStringRef (*)(const void *))__CFMessagePortCopyDescription
;
1059 context
.equal
= NULL
;
1060 context
.hash
= NULL
;
1061 context
.getPort
= __CFMessagePortGetPort
;
1062 context
.perform
= __CFMessagePortPerform
;
1063 ms
->_source
= CFRunLoopSourceCreate(allocator
, order
, (CFRunLoopSourceContext
*)&context
);
1065 if (NULL
!= ms
->_source
) {
1066 result
= (CFRunLoopSourceRef
)CFRetain(ms
->_source
);
1068 __CFMessagePortUnlock(ms
);
1072 void CFMessagePortSetDispatchQueue(CFMessagePortRef ms
, dispatch_queue_t queue
) {
1073 __CFGenericValidateType(ms
, __kCFMessagePortTypeID
);
1074 __CFMessagePortLock(ms
);
1075 if (!__CFMessagePortIsValid(ms
)) {
1076 __CFMessagePortUnlock(ms
);
1077 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid"));
1080 if (__CFMessagePortIsRemote(ms
)) {
1081 __CFMessagePortUnlock(ms
);
1082 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set"));
1085 if (NULL
!= ms
->_source
) {
1086 __CFMessagePortUnlock(ms
);
1087 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set"));
1091 if (ms
->_dispatchSource
) {
1092 dispatch_cancel(ms
->_dispatchSource
);
1093 ms
->_dispatchSource
= NULL
;
1094 ms
->_dispatchQ
= NULL
;
1098 mach_port_t port
= __CFMessagePortGetPort(ms
);
1099 if (MACH_PORT_NULL
!= port
) {
1100 ms
->_dispatchSource
= dispatch_source_machport_create(port
, DISPATCH_MACHPORT_RECV
, DISPATCH_SOURCE_CREATE_SUSPENDED
, __mportQueue(), ^(dispatch_source_t source
) {
1101 long e
= 0, d
= dispatch_source_get_error(source
, &e
);
1102 if (DISPATCH_ERROR_DOMAIN_POSIX
== d
&& ECANCELED
== e
) {
1103 dispatch_release(queue
);
1104 dispatch_release(source
);
1107 if (DISPATCH_ERROR_DOMAIN_NO_ERROR
!= d
) {
1112 mach_port_t port
= dispatch_source_get_handle(source
);
1113 mach_msg_header_t
*msg
= (mach_msg_header_t
*)CFAllocatorAllocate(kCFAllocatorSystemDefault
, 2048, 0);
1114 msg
->msgh_size
= 2048;
1118 msg
->msgh_local_port
= port
;
1119 msg
->msgh_remote_port
= MACH_PORT_NULL
;
1122 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
);
1123 if (MACH_MSG_SUCCESS
== ret
) break;
1124 if (MACH_RCV_TOO_LARGE
!= ret
) HALT
;
1126 uint32_t newSize
= round_msg(msg
->msgh_size
+ MAX_TRAILER_SIZE
);
1127 msg
= CFAllocatorReallocate(kCFAllocatorSystemDefault
, msg
, newSize
, 0);
1128 msg
->msgh_size
= newSize
;
1131 dispatch_async(queue
, ^{
1132 mach_msg_header_t
*reply
= __CFMessagePortPerform(msg
, msg
->msgh_size
, kCFAllocatorSystemDefault
, ms
);
1133 if (NULL
!= reply
) {
1134 kern_return_t ret
= mach_msg(reply
, MACH_SEND_MSG
, reply
->msgh_size
, 0, MACH_PORT_NULL
, 0, MACH_PORT_NULL
);
1135 if (KERN_SUCCESS
!= ret
) mach_msg_destroy(reply
);
1136 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, reply
);
1138 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, msg
);
1143 if (ms
->_dispatchSource
) {
1144 dispatch_retain(queue
);
1145 ms
->_dispatchQ
= queue
;
1146 dispatch_resume(ms
->_dispatchSource
);
1148 CFLog(kCFLogLevelWarning
, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created"));
1151 __CFMessagePortUnlock(ms
);