]>
Commit | Line | Data |
---|---|---|
9ce05555 | 1 | /* |
8ca704e1 | 2 | * Copyright (c) 2011 Apple Inc. All rights reserved. |
9ce05555 A |
3 | * |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
9ce05555 A |
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 | */ | |
f64f9b69 | 23 | |
9ce05555 | 24 | /* CFMessagePort.c |
8ca704e1 | 25 | Copyright (c) 1998-2011, Apple Inc. All rights reserved. |
9ce05555 A |
26 | Responsibility: Christopher Kane |
27 | */ | |
28 | ||
9ce05555 A |
29 | #include <CoreFoundation/CFMessagePort.h> |
30 | #include <CoreFoundation/CFRunLoop.h> | |
31 | #include <CoreFoundation/CFMachPort.h> | |
32 | #include <CoreFoundation/CFDictionary.h> | |
33 | #include <CoreFoundation/CFByteOrder.h> | |
34 | #include <limits.h> | |
35 | #include "CFInternal.h" | |
36 | #include <mach/mach.h> | |
37 | #include <mach/message.h> | |
38 | #include <mach/mach_error.h> | |
bd5b749c | 39 | #include <bootstrap_priv.h> |
9ce05555 A |
40 | #include <math.h> |
41 | #include <mach/mach_time.h> | |
bd5b749c | 42 | #include <dlfcn.h> |
cf7d2af9 A |
43 | #include <dispatch/dispatch.h> |
44 | ||
45 | extern pid_t getpid(void); | |
9ce05555 A |
46 | |
47 | #define __kCFMessagePortMaxNameLengthMax 255 | |
48 | ||
49 | #if defined(BOOTSTRAP_MAX_NAME_LEN) | |
50 | #define __kCFMessagePortMaxNameLength BOOTSTRAP_MAX_NAME_LEN | |
51 | #else | |
52 | #define __kCFMessagePortMaxNameLength 128 | |
53 | #endif | |
54 | ||
55 | #if __kCFMessagePortMaxNameLengthMax < __kCFMessagePortMaxNameLength | |
56 | #undef __kCFMessagePortMaxNameLength | |
57 | #define __kCFMessagePortMaxNameLength __kCFMessagePortMaxNameLengthMax | |
58 | #endif | |
59 | ||
cf7d2af9 A |
60 | #define __CFMessagePortMaxDataSize 0x60000000L |
61 | ||
8ca704e1 A |
62 | //#pragma GCC diagnostic push |
63 | #pragma GCC diagnostic ignored "-Wunused-function" | |
cf7d2af9 | 64 | DISPATCH_HELPER_FUNCTIONS(mport, CFMessagePort) |
8ca704e1 | 65 | //#pragma GCC diagnostic pop |
cf7d2af9 A |
66 | |
67 | ||
bd5b749c | 68 | static CFSpinLock_t __CFAllMessagePortsLock = CFSpinLockInit; |
9ce05555 A |
69 | static CFMutableDictionaryRef __CFAllLocalMessagePorts = NULL; |
70 | static CFMutableDictionaryRef __CFAllRemoteMessagePorts = NULL; | |
71 | ||
72 | struct __CFMessagePort { | |
73 | CFRuntimeBase _base; | |
74 | CFSpinLock_t _lock; | |
75 | CFStringRef _name; | |
76 | CFMachPortRef _port; /* immutable; invalidated */ | |
77 | CFMutableDictionaryRef _replies; | |
78 | int32_t _convCounter; | |
bd5b749c | 79 | int32_t _perPID; /* zero if not per-pid, else pid */ |
9ce05555 A |
80 | CFMachPortRef _replyPort; /* only used by remote port; immutable once created; invalidated */ |
81 | CFRunLoopSourceRef _source; /* only used by local port; immutable once created; invalidated */ | |
cf7d2af9 A |
82 | dispatch_source_t _dispatchSource; /* only used by local port; invalidated */ |
83 | dispatch_queue_t _dispatchQ; /* only used by local port */ | |
9ce05555 A |
84 | CFMessagePortInvalidationCallBack _icallout; |
85 | CFMessagePortCallBack _callout; /* only used by local port; immutable */ | |
8ca704e1 | 86 | CFMessagePortCallBackEx _calloutEx; /* only used by local port; immutable */ |
9ce05555 A |
87 | CFMessagePortContext _context; /* not part of remote port; immutable; invalidated */ |
88 | }; | |
89 | ||
90 | /* Bit 0 in the base reserved bits is used for invalid state */ | |
bd5b749c | 91 | /* Bit 1 of the base reserved bits is used for has-extra-port-refs state */ |
9ce05555 A |
92 | /* Bit 2 of the base reserved bits is used for is-remote state */ |
93 | /* Bit 3 in the base reserved bits is used for is-deallocing state */ | |
94 | ||
95 | CF_INLINE Boolean __CFMessagePortIsValid(CFMessagePortRef ms) { | |
bd5b749c | 96 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0); |
9ce05555 A |
97 | } |
98 | ||
99 | CF_INLINE void __CFMessagePortSetValid(CFMessagePortRef ms) { | |
bd5b749c | 100 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 1); |
9ce05555 A |
101 | } |
102 | ||
103 | CF_INLINE void __CFMessagePortUnsetValid(CFMessagePortRef ms) { | |
bd5b749c A |
104 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 0, 0, 0); |
105 | } | |
106 | ||
107 | CF_INLINE Boolean __CFMessagePortExtraMachRef(CFMessagePortRef ms) { | |
108 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1); | |
109 | } | |
110 | ||
111 | CF_INLINE void __CFMessagePortSetExtraMachRef(CFMessagePortRef ms) { | |
112 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 1); | |
113 | } | |
114 | ||
115 | CF_INLINE void __CFMessagePortUnsetExtraMachRef(CFMessagePortRef ms) { | |
116 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 1, 1, 0); | |
9ce05555 A |
117 | } |
118 | ||
119 | CF_INLINE Boolean __CFMessagePortIsRemote(CFMessagePortRef ms) { | |
bd5b749c | 120 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2); |
9ce05555 A |
121 | } |
122 | ||
123 | CF_INLINE void __CFMessagePortSetRemote(CFMessagePortRef ms) { | |
bd5b749c | 124 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 1); |
9ce05555 A |
125 | } |
126 | ||
127 | CF_INLINE void __CFMessagePortUnsetRemote(CFMessagePortRef ms) { | |
bd5b749c | 128 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 2, 2, 0); |
9ce05555 A |
129 | } |
130 | ||
131 | CF_INLINE Boolean __CFMessagePortIsDeallocing(CFMessagePortRef ms) { | |
bd5b749c | 132 | return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3); |
9ce05555 A |
133 | } |
134 | ||
135 | CF_INLINE void __CFMessagePortSetIsDeallocing(CFMessagePortRef ms) { | |
bd5b749c | 136 | __CFBitfieldSetValue(((CFRuntimeBase *)ms)->_cfinfo[CF_INFO_BITS], 3, 3, 1); |
9ce05555 A |
137 | } |
138 | ||
139 | CF_INLINE void __CFMessagePortLock(CFMessagePortRef ms) { | |
140 | __CFSpinLock(&(ms->_lock)); | |
141 | } | |
142 | ||
143 | CF_INLINE void __CFMessagePortUnlock(CFMessagePortRef ms) { | |
144 | __CFSpinUnlock(&(ms->_lock)); | |
145 | } | |
146 | ||
147 | // Just a heuristic | |
148 | #define __CFMessagePortMaxInlineBytes 4096*10 | |
149 | ||
f64f9b69 A |
150 | struct __CFMessagePortMachMessage0 { |
151 | mach_msg_base_t base; | |
152 | int32_t magic; | |
cf7d2af9 A |
153 | int32_t msgid; |
154 | int32_t byteslen; | |
155 | uint8_t bytes[0]; | |
9ce05555 A |
156 | }; |
157 | ||
f64f9b69 A |
158 | struct __CFMessagePortMachMessage1 { |
159 | mach_msg_base_t base; | |
160 | mach_msg_ool_descriptor_t ool; | |
161 | int32_t magic; | |
162 | int32_t msgid; | |
163 | int32_t byteslen; | |
164 | }; | |
165 | ||
166 | #define MAGIC 0xF1F2F3F4 | |
167 | ||
168 | #define MSGP0_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage0 *)msgp)->ident | |
169 | #define MSGP1_FIELD(msgp, ident) ((struct __CFMessagePortMachMessage1 *)msgp)->ident | |
170 | #define MSGP_GET(msgp, ident) \ | |
171 | ((((mach_msg_base_t *)msgp)->body.msgh_descriptor_count) ? MSGP1_FIELD(msgp, ident) : MSGP0_FIELD(msgp, ident)) | |
172 | ||
173 | static mach_msg_base_t *__CFMessagePortCreateMessage(bool reply, mach_port_t port, mach_port_t replyPort, int32_t convid, int32_t msgid, const uint8_t *bytes, int32_t byteslen) { | |
cf7d2af9 | 174 | if (__CFMessagePortMaxDataSize < byteslen) return NULL; |
cf7d2af9 A |
175 | int32_t rounded_byteslen = ((byteslen + 3) & ~0x3); |
176 | if (rounded_byteslen <= __CFMessagePortMaxInlineBytes) { | |
f64f9b69 A |
177 | int32_t size = sizeof(struct __CFMessagePortMachMessage0) + rounded_byteslen; |
178 | struct __CFMessagePortMachMessage0 *msg = CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); | |
7c97c3e0 A |
179 | if (!msg) return NULL; |
180 | memset(msg, 0, size); | |
f64f9b69 A |
181 | msg->base.header.msgh_id = convid; |
182 | msg->base.header.msgh_size = size; | |
183 | msg->base.header.msgh_remote_port = port; | |
184 | msg->base.header.msgh_local_port = replyPort; | |
185 | msg->base.header.msgh_reserved = 0; | |
186 | msg->base.header.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)); | |
187 | msg->base.body.msgh_descriptor_count = 0; | |
188 | msg->magic = MAGIC; | |
7c97c3e0 A |
189 | msg->msgid = CFSwapInt32HostToLittle(msgid); |
190 | msg->byteslen = CFSwapInt32HostToLittle(byteslen); | |
9ce05555 | 191 | if (NULL != bytes && 0 < byteslen) { |
cf7d2af9 | 192 | memmove(msg->bytes, bytes, byteslen); |
9ce05555 | 193 | } |
f64f9b69 | 194 | return (mach_msg_base_t *)msg; |
9ce05555 | 195 | } else { |
f64f9b69 A |
196 | int32_t size = sizeof(struct __CFMessagePortMachMessage1); |
197 | struct __CFMessagePortMachMessage1 *msg = CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0); | |
198 | if (!msg) return NULL; | |
199 | memset(msg, 0, size); | |
200 | msg->base.header.msgh_id = convid; | |
201 | msg->base.header.msgh_size = size; | |
202 | msg->base.header.msgh_remote_port = port; | |
203 | msg->base.header.msgh_local_port = replyPort; | |
204 | msg->base.header.msgh_reserved = 0; | |
205 | msg->base.header.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)); | |
206 | msg->base.header.msgh_bits |= MACH_MSGH_BITS_COMPLEX; | |
207 | msg->base.body.msgh_descriptor_count = 1; | |
208 | msg->magic = MAGIC; | |
209 | msg->msgid = CFSwapInt32HostToLittle(msgid); | |
210 | msg->byteslen = CFSwapInt32HostToLittle(byteslen); | |
211 | msg->ool.deallocate = false; | |
212 | msg->ool.copy = MACH_MSG_VIRTUAL_COPY; | |
213 | msg->ool.address = (void *)bytes; | |
214 | msg->ool.size = byteslen; | |
215 | msg->ool.type = MACH_MSG_OOL_DESCRIPTOR; | |
216 | return (mach_msg_base_t *)msg; | |
9ce05555 | 217 | } |
9ce05555 A |
218 | } |
219 | ||
9ce05555 A |
220 | static CFStringRef __CFMessagePortCopyDescription(CFTypeRef cf) { |
221 | CFMessagePortRef ms = (CFMessagePortRef)cf; | |
222 | CFStringRef result; | |
223 | const char *locked; | |
224 | CFStringRef contextDesc = NULL; | |
225 | locked = ms->_lock ? "Yes" : "No"; | |
cf7d2af9 A |
226 | if (__CFMessagePortIsRemote(ms)) { |
227 | 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); | |
228 | } else { | |
9ce05555 A |
229 | if (NULL != ms->_context.info && NULL != ms->_context.copyDescription) { |
230 | contextDesc = ms->_context.copyDescription(ms->_context.info); | |
231 | } | |
232 | if (NULL == contextDesc) { | |
bd5b749c | 233 | contextDesc = CFStringCreateWithFormat(kCFAllocatorSystemDefault, NULL, CFSTR("<CFMessagePort context %p>"), ms->_context.info); |
9ce05555 | 234 | } |
8ca704e1 | 235 | void *addr = ms->_callout ? (void *)ms->_callout : (void *)ms->_calloutEx; |
bd5b749c A |
236 | Dl_info info; |
237 | const char *name = (dladdr(addr, &info) && info.dli_saddr == addr && info.dli_sname) ? info.dli_sname : "???"; | |
238 | 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>"))); | |
9ce05555 A |
239 | } |
240 | if (NULL != contextDesc) { | |
241 | CFRelease(contextDesc); | |
242 | } | |
243 | return result; | |
244 | } | |
245 | ||
246 | static void __CFMessagePortDeallocate(CFTypeRef cf) { | |
247 | CFMessagePortRef ms = (CFMessagePortRef)cf; | |
248 | __CFMessagePortSetIsDeallocing(ms); | |
249 | CFMessagePortInvalidate(ms); | |
250 | // Delay cleanup of _replies until here so that invalidation during | |
251 | // SendRequest does not cause _replies to disappear out from under that function. | |
252 | if (NULL != ms->_replies) { | |
253 | CFRelease(ms->_replies); | |
254 | } | |
255 | if (NULL != ms->_name) { | |
256 | CFRelease(ms->_name); | |
257 | } | |
258 | if (NULL != ms->_port) { | |
bd5b749c A |
259 | if (__CFMessagePortExtraMachRef(ms)) { |
260 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_SEND, -1); | |
261 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(ms->_port), MACH_PORT_RIGHT_RECEIVE, -1); | |
262 | } | |
9ce05555 A |
263 | CFMachPortInvalidate(ms->_port); |
264 | CFRelease(ms->_port); | |
265 | } | |
bd5b749c A |
266 | |
267 | // A remote message port for a local message port in the same process will get the | |
268 | // same mach port, and the remote port will keep the mach port from being torn down, | |
269 | // thus keeping the remote port from getting any sort of death notification and | |
270 | // auto-invalidating; so we manually implement the 'auto-invalidation' here by | |
271 | // tickling each remote port to check its state after any message port is destroyed, | |
272 | // but most importantly after local message ports are destroyed. | |
273 | __CFSpinLock(&__CFAllMessagePortsLock); | |
274 | CFMessagePortRef *remotePorts = NULL; | |
275 | CFIndex cnt = 0; | |
276 | if (NULL != __CFAllRemoteMessagePorts) { | |
277 | cnt = CFDictionaryGetCount(__CFAllRemoteMessagePorts); | |
278 | remotePorts = CFAllocatorAllocate(kCFAllocatorSystemDefault, cnt * sizeof(CFMessagePortRef), __kCFAllocatorGCScannedMemory); | |
279 | CFDictionaryGetKeysAndValues(__CFAllRemoteMessagePorts, NULL, (const void **)remotePorts); | |
280 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
281 | CFRetain(remotePorts[idx]); | |
282 | } | |
283 | } | |
284 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
285 | if (remotePorts) { | |
286 | for (CFIndex idx = 0; idx < cnt; idx++) { | |
287 | // as a side-effect, this will auto-invalidate the CFMessagePort if the CFMachPort is invalid | |
288 | CFMessagePortIsValid(remotePorts[idx]); | |
289 | CFRelease(remotePorts[idx]); | |
290 | } | |
291 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, remotePorts); | |
292 | } | |
9ce05555 A |
293 | } |
294 | ||
295 | static CFTypeID __kCFMessagePortTypeID = _kCFRuntimeNotATypeID; | |
296 | ||
297 | static const CFRuntimeClass __CFMessagePortClass = { | |
298 | 0, | |
299 | "CFMessagePort", | |
300 | NULL, // init | |
301 | NULL, // copy | |
302 | __CFMessagePortDeallocate, | |
bd5b749c A |
303 | NULL, |
304 | NULL, | |
9ce05555 A |
305 | NULL, // |
306 | __CFMessagePortCopyDescription | |
307 | }; | |
308 | ||
309 | __private_extern__ void __CFMessagePortInitialize(void) { | |
310 | __kCFMessagePortTypeID = _CFRuntimeRegisterClass(&__CFMessagePortClass); | |
311 | } | |
312 | ||
313 | CFTypeID CFMessagePortGetTypeID(void) { | |
314 | return __kCFMessagePortTypeID; | |
315 | } | |
316 | ||
cf7d2af9 | 317 | static CFStringRef __CFMessagePortSanitizeStringName(CFStringRef name, uint8_t **utfnamep, CFIndex *utfnamelenp) { |
9ce05555 A |
318 | uint8_t *utfname; |
319 | CFIndex utflen; | |
8ca704e1 | 320 | CFStringRef result = NULL; |
cf7d2af9 | 321 | utfname = CFAllocatorAllocate(kCFAllocatorSystemDefault, __kCFMessagePortMaxNameLength + 1, 0); |
9ce05555 A |
322 | CFStringGetBytes(name, CFRangeMake(0, CFStringGetLength(name)), kCFStringEncodingUTF8, 0, false, utfname, __kCFMessagePortMaxNameLength, &utflen); |
323 | utfname[utflen] = '\0'; | |
8ca704e1 A |
324 | if (strlen(utfname) != utflen) { |
325 | /* PCA 9194709: refuse to sanitize a string with an embedded nul character */ | |
326 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); | |
327 | utfname = NULL; | |
328 | utfnamelenp = 0; | |
329 | } else { | |
330 | /* A new string is created, because the original string may have been | |
331 | truncated to the max length, and we want the string name to definitely | |
332 | match the raw UTF-8 chunk that has been created. Also, this is useful | |
333 | to get a constant string in case the original name string was mutable. */ | |
334 | result = CFStringCreateWithBytes(kCFAllocatorSystemDefault, utfname, utflen, kCFStringEncodingUTF8, false); | |
335 | } | |
9ce05555 A |
336 | if (NULL != utfnamep) { |
337 | *utfnamep = utfname; | |
8ca704e1 | 338 | } else if (NULL != utfname) { |
cf7d2af9 | 339 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
340 | } |
341 | if (NULL != utfnamelenp) { | |
342 | *utfnamelenp = utflen; | |
343 | } | |
344 | return result; | |
345 | } | |
346 | ||
347 | static void __CFMessagePortDummyCallback(CFMachPortRef port, void *msg, CFIndex size, void *info) { | |
348 | // not supposed to be implemented | |
349 | } | |
350 | ||
351 | static void __CFMessagePortInvalidationCallBack(CFMachPortRef port, void *info) { | |
352 | // info has been setup as the CFMessagePort owning the CFMachPort | |
cf7d2af9 | 353 | if (info) CFMessagePortInvalidate(info); |
9ce05555 A |
354 | } |
355 | ||
8ca704e1 | 356 | static CFMessagePortRef __CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo, Boolean perPID, CFMessagePortCallBackEx calloutEx) { |
9ce05555 | 357 | CFMessagePortRef memory; |
9ce05555 | 358 | uint8_t *utfname = NULL; |
9ce05555 A |
359 | |
360 | if (shouldFreeInfo) *shouldFreeInfo = true; | |
361 | if (NULL != name) { | |
cf7d2af9 | 362 | name = __CFMessagePortSanitizeStringName(name, &utfname, NULL); |
9ce05555 A |
363 | } |
364 | __CFSpinLock(&__CFAllMessagePortsLock); | |
bd5b749c | 365 | if (!perPID && NULL != name) { |
9ce05555 A |
366 | CFMessagePortRef existing; |
367 | if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { | |
bd5b749c | 368 | CFRetain(existing); |
9ce05555 A |
369 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
370 | CFRelease(name); | |
cf7d2af9 | 371 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
7c97c3e0 A |
372 | if (!CFMessagePortIsValid(existing)) { // must do this outside lock to avoid deadlock |
373 | CFRelease(existing); | |
374 | existing = NULL; | |
375 | } | |
bd5b749c | 376 | return (CFMessagePortRef)(existing); |
9ce05555 A |
377 | } |
378 | } | |
bd5b749c A |
379 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
380 | CFIndex size = sizeof(struct __CFMessagePort) - sizeof(CFRuntimeBase); | |
9ce05555 A |
381 | memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); |
382 | if (NULL == memory) { | |
9ce05555 A |
383 | if (NULL != name) { |
384 | CFRelease(name); | |
385 | } | |
cf7d2af9 | 386 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
387 | return NULL; |
388 | } | |
389 | __CFMessagePortUnsetValid(memory); | |
bd5b749c | 390 | __CFMessagePortUnsetExtraMachRef(memory); |
9ce05555 | 391 | __CFMessagePortUnsetRemote(memory); |
bd5b749c | 392 | memory->_lock = CFSpinLockInit; |
9ce05555 A |
393 | memory->_name = name; |
394 | memory->_port = NULL; | |
395 | memory->_replies = NULL; | |
396 | memory->_convCounter = 0; | |
bd5b749c | 397 | memory->_perPID = perPID ? getpid() : 0; // actual value not terribly useful for local ports |
9ce05555 A |
398 | memory->_replyPort = NULL; |
399 | memory->_source = NULL; | |
cf7d2af9 A |
400 | memory->_dispatchSource = NULL; |
401 | memory->_dispatchQ = NULL; | |
9ce05555 A |
402 | memory->_icallout = NULL; |
403 | memory->_callout = callout; | |
8ca704e1 | 404 | memory->_calloutEx = calloutEx; |
9ce05555 A |
405 | memory->_context.info = NULL; |
406 | memory->_context.retain = NULL; | |
407 | memory->_context.release = NULL; | |
408 | memory->_context.copyDescription = NULL; | |
bd5b749c A |
409 | |
410 | if (NULL != name) { | |
411 | CFMachPortRef native = NULL; | |
412 | kern_return_t ret; | |
413 | mach_port_t bs, mp; | |
414 | task_get_bootstrap_port(mach_task_self(), &bs); | |
415 | if (!perPID) { | |
416 | ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */ | |
417 | if (ret == KERN_SUCCESS) { | |
418 | ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); | |
419 | if (KERN_SUCCESS == ret) { | |
420 | CFMachPortContext ctx = {0, memory, NULL, NULL, NULL}; | |
421 | native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL); | |
422 | __CFMessagePortSetExtraMachRef(memory); | |
423 | } else { | |
cf7d2af9 | 424 | 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); |
bd5b749c | 425 | mach_port_destroy(mach_task_self(), mp); |
cf7d2af9 | 426 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
bd5b749c A |
427 | // name is released by deallocation |
428 | CFRelease(memory); | |
429 | return NULL; | |
430 | } | |
431 | } | |
432 | } | |
433 | if (!native) { | |
434 | CFMachPortContext ctx = {0, memory, NULL, NULL, NULL}; | |
435 | native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); | |
cf7d2af9 A |
436 | if (!native) { |
437 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); | |
438 | // name is released by deallocation | |
439 | CFRelease(memory); | |
440 | return NULL; | |
441 | } | |
bd5b749c A |
442 | mp = CFMachPortGetPort(native); |
443 | ret = bootstrap_register2(bs, (char *)utfname, mp, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0); | |
444 | if (ret != KERN_SUCCESS) { | |
cf7d2af9 | 445 | 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); |
bd5b749c A |
446 | CFMachPortInvalidate(native); |
447 | CFRelease(native); | |
cf7d2af9 | 448 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
bd5b749c A |
449 | // name is released by deallocation |
450 | CFRelease(memory); | |
451 | return NULL; | |
452 | } | |
453 | } | |
454 | CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); | |
455 | memory->_port = native; | |
9ce05555 | 456 | } |
bd5b749c | 457 | |
cf7d2af9 | 458 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
459 | __CFMessagePortSetValid(memory); |
460 | if (NULL != context) { | |
461 | memmove(&memory->_context, context, sizeof(CFMessagePortContext)); | |
462 | memory->_context.info = context->retain ? (void *)context->retain(context->info) : context->info; | |
463 | } | |
bd5b749c A |
464 | __CFSpinLock(&__CFAllMessagePortsLock); |
465 | if (!perPID && NULL != name) { | |
466 | CFMessagePortRef existing; | |
467 | if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { | |
468 | CFRetain(existing); | |
469 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
470 | CFRelease(memory); | |
471 | return (CFMessagePortRef)(existing); | |
472 | } | |
9ce05555 A |
473 | if (NULL == __CFAllLocalMessagePorts) { |
474 | __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); | |
475 | } | |
476 | CFDictionaryAddValue(__CFAllLocalMessagePorts, name, memory); | |
477 | } | |
478 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
479 | if (shouldFreeInfo) *shouldFreeInfo = false; | |
480 | return memory; | |
481 | } | |
482 | ||
bd5b749c | 483 | CFMessagePortRef CFMessagePortCreateLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) { |
8ca704e1 | 484 | return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, false, NULL); |
bd5b749c A |
485 | } |
486 | ||
487 | CFMessagePortRef CFMessagePortCreatePerProcessLocal(CFAllocatorRef allocator, CFStringRef name, CFMessagePortCallBack callout, CFMessagePortContext *context, Boolean *shouldFreeInfo) { | |
8ca704e1 A |
488 | return __CFMessagePortCreateLocal(allocator, name, callout, context, shouldFreeInfo, true, NULL); |
489 | } | |
490 | ||
491 | CFMessagePortRef _CFMessagePortCreateLocalEx(CFAllocatorRef allocator, CFStringRef name, Boolean perPID, uintptr_t unused, CFMessagePortCallBackEx calloutEx, CFMessagePortContext *context, Boolean *shouldFreeInfo) { | |
492 | return __CFMessagePortCreateLocal(allocator, name, NULL, context, shouldFreeInfo, perPID, calloutEx); | |
bd5b749c A |
493 | } |
494 | ||
495 | static CFMessagePortRef __CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name, Boolean perPID, CFIndex pid) { | |
9ce05555 A |
496 | CFMessagePortRef memory; |
497 | CFMachPortRef native; | |
498 | CFMachPortContext ctx; | |
499 | uint8_t *utfname = NULL; | |
500 | CFIndex size; | |
501 | mach_port_t bp, port; | |
502 | kern_return_t ret; | |
503 | ||
cf7d2af9 | 504 | name = __CFMessagePortSanitizeStringName(name, &utfname, NULL); |
9ce05555 A |
505 | if (NULL == name) { |
506 | return NULL; | |
507 | } | |
508 | __CFSpinLock(&__CFAllMessagePortsLock); | |
bd5b749c | 509 | if (!perPID && NULL != name) { |
9ce05555 A |
510 | CFMessagePortRef existing; |
511 | if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) { | |
bd5b749c | 512 | CFRetain(existing); |
9ce05555 A |
513 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
514 | CFRelease(name); | |
cf7d2af9 | 515 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
7c97c3e0 A |
516 | if (!CFMessagePortIsValid(existing)) { // must do this outside lock to avoid deadlock |
517 | CFRelease(existing); | |
518 | existing = NULL; | |
519 | } | |
bd5b749c | 520 | return (CFMessagePortRef)(existing); |
9ce05555 A |
521 | } |
522 | } | |
bd5b749c | 523 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
9ce05555 A |
524 | size = sizeof(struct __CFMessagePort) - sizeof(CFMessagePortContext) - sizeof(CFRuntimeBase); |
525 | memory = (CFMessagePortRef)_CFRuntimeCreateInstance(allocator, __kCFMessagePortTypeID, size, NULL); | |
526 | if (NULL == memory) { | |
9ce05555 A |
527 | if (NULL != name) { |
528 | CFRelease(name); | |
529 | } | |
cf7d2af9 | 530 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
531 | return NULL; |
532 | } | |
533 | __CFMessagePortUnsetValid(memory); | |
bd5b749c | 534 | __CFMessagePortUnsetExtraMachRef(memory); |
9ce05555 | 535 | __CFMessagePortSetRemote(memory); |
bd5b749c | 536 | memory->_lock = CFSpinLockInit; |
9ce05555 A |
537 | memory->_name = name; |
538 | memory->_port = NULL; | |
bd5b749c | 539 | memory->_replies = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL); |
9ce05555 | 540 | memory->_convCounter = 0; |
bd5b749c | 541 | memory->_perPID = perPID ? pid : 0; |
9ce05555 A |
542 | memory->_replyPort = NULL; |
543 | memory->_source = NULL; | |
cf7d2af9 A |
544 | memory->_dispatchSource = NULL; |
545 | memory->_dispatchQ = NULL; | |
9ce05555 A |
546 | memory->_icallout = NULL; |
547 | memory->_callout = NULL; | |
8ca704e1 | 548 | memory->_calloutEx = NULL; |
9ce05555 A |
549 | ctx.version = 0; |
550 | ctx.info = memory; | |
551 | ctx.retain = NULL; | |
552 | ctx.release = NULL; | |
553 | ctx.copyDescription = NULL; | |
554 | task_get_bootstrap_port(mach_task_self(), &bp); | |
bd5b749c | 555 | ret = bootstrap_look_up2(bp, (char *)utfname, &port, perPID ? (pid_t)pid : 0, perPID ? BOOTSTRAP_PER_PID_SERVICE : 0); |
9ce05555 | 556 | native = (KERN_SUCCESS == ret) ? CFMachPortCreateWithPort(allocator, port, __CFMessagePortDummyCallback, &ctx, NULL) : NULL; |
cf7d2af9 | 557 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 | 558 | if (NULL == native) { |
9ce05555 A |
559 | // name is released by deallocation |
560 | CFRelease(memory); | |
561 | return NULL; | |
562 | } | |
563 | memory->_port = native; | |
9ce05555 | 564 | __CFMessagePortSetValid(memory); |
bd5b749c A |
565 | __CFSpinLock(&__CFAllMessagePortsLock); |
566 | if (!perPID && NULL != name) { | |
567 | CFMessagePortRef existing; | |
568 | if (NULL != __CFAllRemoteMessagePorts && CFDictionaryGetValueIfPresent(__CFAllRemoteMessagePorts, name, (const void **)&existing)) { | |
569 | CFRetain(existing); | |
570 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
571 | CFRelease(memory); | |
572 | return (CFMessagePortRef)(existing); | |
573 | } | |
9ce05555 A |
574 | if (NULL == __CFAllRemoteMessagePorts) { |
575 | __CFAllRemoteMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); | |
576 | } | |
577 | CFDictionaryAddValue(__CFAllRemoteMessagePorts, name, memory); | |
578 | } | |
579 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
cf7d2af9 A |
580 | CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); |
581 | // that set-invalidation-callback might have called back into us | |
582 | // if the CFMachPort is already bad, but that was a no-op since | |
583 | // there was no callback setup at the (previous) time the CFMachPort | |
584 | // went invalid; so check for validity manually and react | |
585 | if (!CFMachPortIsValid(native)) { | |
586 | CFRelease(memory); // does the invalidate | |
587 | return NULL; | |
588 | } | |
9ce05555 A |
589 | return (CFMessagePortRef)memory; |
590 | } | |
591 | ||
bd5b749c A |
592 | CFMessagePortRef CFMessagePortCreateRemote(CFAllocatorRef allocator, CFStringRef name) { |
593 | return __CFMessagePortCreateRemote(allocator, name, false, 0); | |
594 | } | |
595 | ||
596 | CFMessagePortRef CFMessagePortCreatePerProcessRemote(CFAllocatorRef allocator, CFStringRef name, CFIndex pid) { | |
597 | return __CFMessagePortCreateRemote(allocator, name, true, pid); | |
598 | } | |
599 | ||
9ce05555 A |
600 | Boolean CFMessagePortIsRemote(CFMessagePortRef ms) { |
601 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
602 | return __CFMessagePortIsRemote(ms); | |
603 | } | |
604 | ||
605 | CFStringRef CFMessagePortGetName(CFMessagePortRef ms) { | |
606 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
607 | return ms->_name; | |
608 | } | |
609 | ||
610 | Boolean CFMessagePortSetName(CFMessagePortRef ms, CFStringRef name) { | |
611 | CFAllocatorRef allocator = CFGetAllocator(ms); | |
612 | uint8_t *utfname = NULL; | |
613 | ||
614 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
bd5b749c | 615 | if (ms->_perPID || __CFMessagePortIsRemote(ms)) return false; |
cf7d2af9 | 616 | name = __CFMessagePortSanitizeStringName(name, &utfname, NULL); |
9ce05555 A |
617 | if (NULL == name) { |
618 | return false; | |
619 | } | |
620 | __CFSpinLock(&__CFAllMessagePortsLock); | |
621 | if (NULL != name) { | |
622 | CFMessagePortRef existing; | |
623 | if (NULL != __CFAllLocalMessagePorts && CFDictionaryGetValueIfPresent(__CFAllLocalMessagePorts, name, (const void **)&existing)) { | |
624 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
625 | CFRelease(name); | |
cf7d2af9 | 626 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
627 | return false; |
628 | } | |
629 | } | |
bd5b749c A |
630 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
631 | ||
9ce05555 | 632 | if (NULL != name && (NULL == ms->_name || !CFEqual(ms->_name, name))) { |
bd5b749c A |
633 | CFMachPortRef oldPort = ms->_port; |
634 | CFMachPortRef native = NULL; | |
635 | kern_return_t ret; | |
636 | mach_port_t bs, mp; | |
637 | task_get_bootstrap_port(mach_task_self(), &bs); | |
638 | ret = bootstrap_check_in(bs, (char *)utfname, &mp); /* If we're started by launchd or the old mach_init */ | |
639 | if (ret == KERN_SUCCESS) { | |
640 | ret = mach_port_insert_right(mach_task_self(), mp, mp, MACH_MSG_TYPE_MAKE_SEND); | |
641 | if (KERN_SUCCESS == ret) { | |
642 | CFMachPortContext ctx = {0, ms, NULL, NULL, NULL}; | |
643 | native = CFMachPortCreateWithPort(allocator, mp, __CFMessagePortDummyCallback, &ctx, NULL); | |
644 | __CFMessagePortSetExtraMachRef(ms); | |
645 | } else { | |
646 | mach_port_destroy(mach_task_self(), mp); | |
cf7d2af9 | 647 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
bd5b749c A |
648 | CFRelease(name); |
649 | return false; | |
650 | } | |
651 | } | |
652 | if (!native) { | |
653 | CFMachPortContext ctx = {0, ms, NULL, NULL, NULL}; | |
654 | native = CFMachPortCreate(allocator, __CFMessagePortDummyCallback, &ctx, NULL); | |
cf7d2af9 A |
655 | if (!native) { |
656 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); | |
657 | CFRelease(name); | |
658 | return false; | |
659 | } | |
bd5b749c A |
660 | mp = CFMachPortGetPort(native); |
661 | ret = bootstrap_register2(bs, (char *)utfname, mp, 0); | |
662 | if (ret != KERN_SUCCESS) { | |
cf7d2af9 | 663 | 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); |
bd5b749c A |
664 | CFMachPortInvalidate(native); |
665 | CFRelease(native); | |
cf7d2af9 | 666 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
bd5b749c A |
667 | CFRelease(name); |
668 | return false; | |
669 | } | |
670 | } | |
671 | CFMachPortSetInvalidationCallBack(native, __CFMessagePortInvalidationCallBack); | |
672 | ms->_port = native; | |
673 | if (NULL != oldPort && oldPort != native) { | |
674 | if (__CFMessagePortExtraMachRef(ms)) { | |
675 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_SEND, -1); | |
676 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(oldPort), MACH_PORT_RIGHT_RECEIVE, -1); | |
677 | } | |
678 | CFMachPortInvalidate(oldPort); | |
679 | CFRelease(oldPort); | |
9ce05555 | 680 | } |
bd5b749c A |
681 | __CFSpinLock(&__CFAllMessagePortsLock); |
682 | // This relocking without checking to see if something else has grabbed | |
683 | // that name in the cache is rather suspect, but what would that even | |
684 | // mean has happened? We'd expect the bootstrap_* calls above to have | |
685 | // failed for this one and not gotten this far, or failed for all of the | |
686 | // other simultaneous attempts to get the name (and having succeeded for | |
687 | // this one, gotten here). So we're not going to try very hard here | |
688 | // with the thread-safety. | |
9ce05555 A |
689 | if (NULL == __CFAllLocalMessagePorts) { |
690 | __CFAllLocalMessagePorts = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeDictionaryKeyCallBacks, NULL); | |
691 | } | |
692 | if (NULL != ms->_name) { | |
693 | CFDictionaryRemoveValue(__CFAllLocalMessagePorts, ms->_name); | |
694 | CFRelease(ms->_name); | |
695 | } | |
696 | ms->_name = name; | |
697 | CFDictionaryAddValue(__CFAllLocalMessagePorts, name, ms); | |
bd5b749c | 698 | __CFSpinUnlock(&__CFAllMessagePortsLock); |
9ce05555 | 699 | } |
bd5b749c | 700 | |
cf7d2af9 | 701 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, utfname); |
9ce05555 A |
702 | return true; |
703 | } | |
704 | ||
705 | void CFMessagePortGetContext(CFMessagePortRef ms, CFMessagePortContext *context) { | |
706 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
707 | //#warning CF: assert that this is a local port | |
708 | CFAssert1(0 == context->version, __kCFLogAssertion, "%s(): context version not initialized to 0", __PRETTY_FUNCTION__); | |
709 | memmove(context, &ms->_context, sizeof(CFMessagePortContext)); | |
710 | } | |
711 | ||
712 | void CFMessagePortInvalidate(CFMessagePortRef ms) { | |
713 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
714 | if (!__CFMessagePortIsDeallocing(ms)) { | |
715 | CFRetain(ms); | |
716 | } | |
717 | __CFMessagePortLock(ms); | |
718 | if (__CFMessagePortIsValid(ms)) { | |
cf7d2af9 | 719 | if (ms->_dispatchSource) { |
7c97c3e0 | 720 | dispatch_source_cancel(ms->_dispatchSource); |
cf7d2af9 A |
721 | ms->_dispatchSource = NULL; |
722 | ms->_dispatchQ = NULL; | |
723 | } | |
724 | ||
9ce05555 A |
725 | CFMessagePortInvalidationCallBack callout = ms->_icallout; |
726 | CFRunLoopSourceRef source = ms->_source; | |
727 | CFMachPortRef replyPort = ms->_replyPort; | |
728 | CFMachPortRef port = ms->_port; | |
729 | CFStringRef name = ms->_name; | |
730 | void *info = NULL; | |
731 | ||
732 | __CFMessagePortUnsetValid(ms); | |
733 | if (!__CFMessagePortIsRemote(ms)) { | |
734 | info = ms->_context.info; | |
735 | ms->_context.info = NULL; | |
736 | } | |
737 | ms->_source = NULL; | |
738 | ms->_replyPort = NULL; | |
8ca704e1 | 739 | ms->_port = NULL; |
9ce05555 A |
740 | __CFMessagePortUnlock(ms); |
741 | ||
742 | __CFSpinLock(&__CFAllMessagePortsLock); | |
bd5b749c | 743 | if (0 == ms->_perPID && NULL != (__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts)) { |
9ce05555 A |
744 | CFDictionaryRemoveValue(__CFMessagePortIsRemote(ms) ? __CFAllRemoteMessagePorts : __CFAllLocalMessagePorts, name); |
745 | } | |
746 | __CFSpinUnlock(&__CFAllMessagePortsLock); | |
747 | if (NULL != callout) { | |
748 | callout(ms, info); | |
749 | } | |
9ce05555 A |
750 | if (!__CFMessagePortIsRemote(ms) && NULL != ms->_context.release) { |
751 | ms->_context.release(info); | |
752 | } | |
753 | if (NULL != source) { | |
754 | CFRunLoopSourceInvalidate(source); | |
755 | CFRelease(source); | |
756 | } | |
757 | if (NULL != replyPort) { | |
758 | CFMachPortInvalidate(replyPort); | |
759 | CFRelease(replyPort); | |
760 | } | |
761 | if (__CFMessagePortIsRemote(ms)) { | |
762 | // Get rid of our extra ref on the Mach port gotten from bs server | |
763 | mach_port_deallocate(mach_task_self(), CFMachPortGetPort(port)); | |
764 | } | |
8ca704e1 A |
765 | if (NULL != port) { |
766 | // We already know we're going invalid, don't need this callback | |
767 | // anymore; plus, this solves a reentrancy deadlock; also, this | |
768 | // must be done before the deallocate of the Mach port, to | |
769 | // avoid a race between the notification message which could be | |
770 | // handled in another thread, and this NULL'ing out. | |
771 | CFMachPortSetInvalidationCallBack(port, NULL); | |
772 | if (__CFMessagePortExtraMachRef(ms)) { | |
773 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port), MACH_PORT_RIGHT_SEND, -1); | |
774 | mach_port_mod_refs(mach_task_self(), CFMachPortGetPort(port), MACH_PORT_RIGHT_RECEIVE, -1); | |
775 | } | |
776 | CFMachPortInvalidate(port); | |
777 | CFRelease(port); | |
778 | } | |
9ce05555 A |
779 | } else { |
780 | __CFMessagePortUnlock(ms); | |
781 | } | |
782 | if (!__CFMessagePortIsDeallocing(ms)) { | |
783 | CFRelease(ms); | |
784 | } | |
785 | } | |
786 | ||
787 | Boolean CFMessagePortIsValid(CFMessagePortRef ms) { | |
788 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
789 | if (!__CFMessagePortIsValid(ms)) return false; | |
790 | if (NULL != ms->_port && !CFMachPortIsValid(ms->_port)) { | |
791 | CFMessagePortInvalidate(ms); | |
792 | return false; | |
793 | } | |
794 | if (NULL != ms->_replyPort && !CFMachPortIsValid(ms->_replyPort)) { | |
795 | CFMessagePortInvalidate(ms); | |
796 | return false; | |
797 | } | |
9ce05555 A |
798 | return true; |
799 | } | |
800 | ||
801 | CFMessagePortInvalidationCallBack CFMessagePortGetInvalidationCallBack(CFMessagePortRef ms) { | |
802 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
803 | return ms->_icallout; | |
804 | } | |
805 | ||
806 | void CFMessagePortSetInvalidationCallBack(CFMessagePortRef ms, CFMessagePortInvalidationCallBack callout) { | |
807 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
808 | if (!__CFMessagePortIsValid(ms) && NULL != callout) { | |
809 | callout(ms, ms->_context.info); | |
810 | } else { | |
811 | ms->_icallout = callout; | |
812 | } | |
813 | } | |
814 | ||
815 | static void __CFMessagePortReplyCallBack(CFMachPortRef port, void *msg, CFIndex size, void *info) { | |
816 | CFMessagePortRef ms = info; | |
f64f9b69 A |
817 | mach_msg_base_t *msgp = msg; |
818 | mach_msg_base_t *replymsg; | |
9ce05555 A |
819 | __CFMessagePortLock(ms); |
820 | if (!__CFMessagePortIsValid(ms)) { | |
821 | __CFMessagePortUnlock(ms); | |
822 | return; | |
823 | } | |
cf7d2af9 A |
824 | |
825 | int32_t byteslen = 0; | |
826 | ||
f64f9b69 A |
827 | Boolean invalidMagic = (MSGP_GET(msgp, magic) != MAGIC) && (CFSwapInt32(MSGP_GET(msgp, magic)) != MAGIC); |
828 | Boolean invalidComplex = (0 != msgp->body.msgh_descriptor_count) && !(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX); | |
829 | invalidComplex = invalidComplex || ((msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (0 == msgp->body.msgh_descriptor_count)); | |
830 | Boolean wayTooBig = ((msgp->body.msgh_descriptor_count) ? sizeof(struct __CFMessagePortMachMessage1) : sizeof(struct __CFMessagePortMachMessage0) + __CFMessagePortMaxInlineBytes) < msgp->header.msgh_size; | |
831 | Boolean wayTooSmall = msgp->header.msgh_size < sizeof(struct __CFMessagePortMachMessage0); | |
cf7d2af9 A |
832 | Boolean wrongSize = false; |
833 | if (!(invalidComplex || wayTooBig || wayTooSmall)) { | |
f64f9b69 | 834 | byteslen = CFSwapInt32LittleToHost(MSGP_GET(msgp, byteslen)); |
cf7d2af9 | 835 | wrongSize = (byteslen < 0) || (__CFMessagePortMaxDataSize < byteslen); |
f64f9b69 A |
836 | if (0 != msgp->body.msgh_descriptor_count) { |
837 | wrongSize = wrongSize || (MSGP1_FIELD(msgp, ool).size != byteslen); | |
cf7d2af9 | 838 | } else { |
f64f9b69 | 839 | wrongSize = wrongSize || (msgp->header.msgh_size - sizeof(struct __CFMessagePortMachMessage0) < byteslen); |
cf7d2af9 A |
840 | } |
841 | } | |
f64f9b69 A |
842 | Boolean invalidMsgID = (0 <= msgp->header.msgh_id) && (msgp->header.msgh_id <= INT32_MAX); // conversation id |
843 | if (invalidMagic || invalidComplex || wayTooBig || wayTooSmall || wrongSize || invalidMsgID) { | |
844 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: dropping corrupt reply Mach message (0b%d%d%d%d%d%d)"), invalidMagic, invalidComplex, wayTooBig, wayTooSmall, wrongSize, invalidMsgID); | |
cf7d2af9 A |
845 | mach_msg_destroy((mach_msg_header_t *)msgp); |
846 | __CFMessagePortUnlock(ms); | |
847 | return; | |
848 | } | |
849 | ||
f64f9b69 | 850 | if (CFDictionaryContainsKey(ms->_replies, (void *)(uintptr_t)msgp->header.msgh_id)) { |
9ce05555 | 851 | CFDataRef reply = NULL; |
f64f9b69 | 852 | replymsg = (mach_msg_base_t *)msg; |
9ce05555 | 853 | if (0 == replymsg->body.msgh_descriptor_count) { |
f64f9b69 A |
854 | uintptr_t msgp_extent = (uintptr_t)((uint8_t *)msgp + msgp->header.msgh_size); |
855 | uintptr_t data_extent = (uintptr_t)((uint8_t *)&(MSGP0_FIELD(replymsg, bytes)) + byteslen); | |
cf7d2af9 | 856 | if (0 <= byteslen && data_extent <= msgp_extent) { |
f64f9b69 | 857 | reply = CFDataCreate(kCFAllocatorSystemDefault, MSGP0_FIELD(replymsg, bytes), byteslen); |
9ce05555 | 858 | } else { |
bd5b749c | 859 | reply = (void *)~0; // means NULL data |
9ce05555 A |
860 | } |
861 | } else { | |
862 | //#warning CF: should create a no-copy data here that has a custom VM-freeing allocator, and not vm_dealloc here | |
f64f9b69 A |
863 | reply = CFDataCreate(kCFAllocatorSystemDefault, MSGP1_FIELD(replymsg, ool).address, MSGP1_FIELD(replymsg, ool).size); |
864 | vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(replymsg, ool).address, MSGP1_FIELD(replymsg, ool).size); | |
9ce05555 | 865 | } |
f64f9b69 | 866 | CFDictionarySetValue(ms->_replies, (void *)(uintptr_t)msgp->header.msgh_id, (void *)reply); |
9ce05555 A |
867 | } else { /* discard message */ |
868 | if (1 == msgp->body.msgh_descriptor_count) { | |
f64f9b69 | 869 | vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size); |
9ce05555 A |
870 | } |
871 | } | |
872 | __CFMessagePortUnlock(ms); | |
873 | } | |
874 | ||
875 | SInt32 CFMessagePortSendRequest(CFMessagePortRef remote, SInt32 msgid, CFDataRef data, CFTimeInterval sendTimeout, CFTimeInterval rcvTimeout, CFStringRef replyMode, CFDataRef *returnDatap) { | |
f64f9b69 | 876 | mach_msg_base_t *sendmsg; |
9ce05555 A |
877 | CFRunLoopRef currentRL = CFRunLoopGetCurrent(); |
878 | CFRunLoopSourceRef source = NULL; | |
879 | CFDataRef reply = NULL; | |
880 | int64_t termTSR; | |
881 | uint32_t sendOpts = 0, sendTimeOut = 0; | |
882 | int32_t desiredReply; | |
883 | Boolean didRegister = false; | |
884 | kern_return_t ret; | |
885 | ||
0ae65c4b | 886 | //#warning CF: This should be an assert |
9ce05555 | 887 | // if (!__CFMessagePortIsRemote(remote)) return -999; |
cf7d2af9 A |
888 | if (data && __CFMessagePortMaxDataSize < CFDataGetLength(data)) { |
889 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSendRequest: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize); | |
890 | return kCFMessagePortTransportError; | |
891 | } | |
9ce05555 | 892 | __CFMessagePortLock(remote); |
cf7d2af9 A |
893 | if (!__CFMessagePortIsValid(remote)) { |
894 | __CFMessagePortUnlock(remote); | |
895 | return kCFMessagePortIsInvalid; | |
896 | } | |
897 | CFRetain(remote); // retain during run loop to avoid invalidation causing freeing | |
9ce05555 A |
898 | if (NULL == remote->_replyPort) { |
899 | CFMachPortContext context; | |
900 | context.version = 0; | |
901 | context.info = remote; | |
902 | context.retain = (const void *(*)(const void *))CFRetain; | |
903 | context.release = (void (*)(const void *))CFRelease; | |
904 | context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; | |
905 | remote->_replyPort = CFMachPortCreate(CFGetAllocator(remote), __CFMessagePortReplyCallBack, &context, NULL); | |
906 | } | |
907 | remote->_convCounter++; | |
908 | desiredReply = -remote->_convCounter; | |
cf7d2af9 A |
909 | sendmsg = __CFMessagePortCreateMessage(false, CFMachPortGetPort(remote->_port), (replyMode != NULL ? CFMachPortGetPort(remote->_replyPort) : MACH_PORT_NULL), -desiredReply, msgid, (data ? CFDataGetBytePtr(data) : NULL), (data ? CFDataGetLength(data) : 0)); |
910 | if (!sendmsg) { | |
911 | __CFMessagePortUnlock(remote); | |
912 | CFRelease(remote); | |
913 | return kCFMessagePortTransportError; | |
914 | } | |
9ce05555 | 915 | if (replyMode != NULL) { |
bd5b749c | 916 | CFDictionarySetValue(remote->_replies, (void *)(uintptr_t)desiredReply, NULL); |
9ce05555 A |
917 | source = CFMachPortCreateRunLoopSource(CFGetAllocator(remote), remote->_replyPort, -100); |
918 | didRegister = !CFRunLoopContainsSource(currentRL, source, replyMode); | |
919 | if (didRegister) { | |
920 | CFRunLoopAddSource(currentRL, source, replyMode); | |
921 | } | |
922 | } | |
923 | if (sendTimeout < 10.0*86400) { | |
924 | // anything more than 10 days is no timeout! | |
925 | sendOpts = MACH_SEND_TIMEOUT; | |
926 | sendTimeout *= 1000.0; | |
927 | if (sendTimeout < 1.0) sendTimeout = 0.0; | |
928 | sendTimeOut = floor(sendTimeout); | |
929 | } | |
f64f9b69 | 930 | ret = mach_msg((mach_msg_header_t *)sendmsg, MACH_SEND_MSG|sendOpts, sendmsg->header.msgh_size, 0, MACH_PORT_NULL, sendTimeOut, MACH_PORT_NULL); |
9ce05555 | 931 | if (KERN_SUCCESS != ret) { |
bd5b749c A |
932 | // need to deallocate the send-once right that might have been created |
933 | if (replyMode != NULL) mach_port_deallocate(mach_task_self(), ((mach_msg_header_t *)sendmsg)->msgh_local_port); | |
9ce05555 A |
934 | if (didRegister) { |
935 | CFRunLoopRemoveSource(currentRL, source, replyMode); | |
9ce05555 | 936 | } |
bd5b749c | 937 | if (source) CFRelease(source); |
cf7d2af9 | 938 | __CFMessagePortUnlock(remote); |
bd5b749c | 939 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg); |
cf7d2af9 | 940 | CFRelease(remote); |
bd5b749c | 941 | return (MACH_SEND_TIMED_OUT == ret) ? kCFMessagePortSendTimeout : kCFMessagePortTransportError; |
9ce05555 | 942 | } |
cf7d2af9 | 943 | __CFMessagePortUnlock(remote); |
bd5b749c | 944 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, sendmsg); |
9ce05555 | 945 | if (replyMode == NULL) { |
cf7d2af9 | 946 | CFRelease(remote); |
9ce05555 A |
947 | return kCFMessagePortSuccess; |
948 | } | |
9ce05555 A |
949 | _CFMachPortInstallNotifyPort(currentRL, replyMode); |
950 | termTSR = mach_absolute_time() + __CFTimeIntervalToTSR(rcvTimeout); | |
951 | for (;;) { | |
952 | CFRunLoopRunInMode(replyMode, __CFTSRToTimeInterval(termTSR - mach_absolute_time()), true); | |
953 | // warning: what, if anything, should be done if remote is now invalid? | |
bd5b749c | 954 | reply = CFDictionaryGetValue(remote->_replies, (void *)(uintptr_t)desiredReply); |
9ce05555 A |
955 | if (NULL != reply || termTSR < (int64_t)mach_absolute_time()) { |
956 | break; | |
957 | } | |
958 | if (!CFMessagePortIsValid(remote)) { | |
959 | // no reason that reply port alone should go invalid so we don't check for that | |
960 | break; | |
961 | } | |
962 | } | |
963 | // Should we uninstall the notify port? A complex question... | |
964 | if (didRegister) { | |
965 | CFRunLoopRemoveSource(currentRL, source, replyMode); | |
9ce05555 | 966 | } |
bd5b749c | 967 | if (source) CFRelease(source); |
9ce05555 | 968 | if (NULL == reply) { |
bd5b749c | 969 | CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply); |
9ce05555 | 970 | CFRelease(remote); |
cf7d2af9 | 971 | return CFMessagePortIsValid(remote) ? kCFMessagePortReceiveTimeout : kCFMessagePortBecameInvalidError; |
9ce05555 A |
972 | } |
973 | if (NULL != returnDatap) { | |
bd5b749c A |
974 | *returnDatap = ((void *)~0 == reply) ? NULL : reply; |
975 | } else if ((void *)~0 != reply) { | |
9ce05555 A |
976 | CFRelease(reply); |
977 | } | |
bd5b749c | 978 | CFDictionaryRemoveValue(remote->_replies, (void *)(uintptr_t)desiredReply); |
9ce05555 A |
979 | CFRelease(remote); |
980 | return kCFMessagePortSuccess; | |
981 | } | |
982 | ||
983 | static mach_port_t __CFMessagePortGetPort(void *info) { | |
984 | CFMessagePortRef ms = info; | |
8ca704e1 | 985 | if (!ms->_port && __CFMessagePortIsValid(ms)) 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); |
bd5b749c | 986 | return ms->_port ? CFMachPortGetPort(ms->_port) : MACH_PORT_NULL; |
9ce05555 A |
987 | } |
988 | ||
989 | static void *__CFMessagePortPerform(void *msg, CFIndex size, CFAllocatorRef allocator, void *info) { | |
990 | CFMessagePortRef ms = info; | |
f64f9b69 A |
991 | mach_msg_base_t *msgp = msg; |
992 | mach_msg_base_t *replymsg; | |
9ce05555 A |
993 | void *context_info; |
994 | void (*context_release)(const void *); | |
995 | CFDataRef returnData, data = NULL; | |
996 | void *return_bytes = NULL; | |
997 | CFIndex return_len = 0; | |
998 | int32_t msgid; | |
999 | ||
1000 | __CFMessagePortLock(ms); | |
1001 | if (!__CFMessagePortIsValid(ms)) { | |
1002 | __CFMessagePortUnlock(ms); | |
1003 | return NULL; | |
1004 | } | |
9ce05555 A |
1005 | if (NULL != ms->_context.retain) { |
1006 | context_info = (void *)ms->_context.retain(ms->_context.info); | |
1007 | context_release = ms->_context.release; | |
1008 | } else { | |
1009 | context_info = ms->_context.info; | |
1010 | context_release = NULL; | |
1011 | } | |
1012 | __CFMessagePortUnlock(ms); | |
cf7d2af9 A |
1013 | |
1014 | int32_t byteslen = 0; | |
1015 | ||
f64f9b69 A |
1016 | Boolean invalidMagic = (MSGP_GET(msgp, magic) != MAGIC) && (CFSwapInt32(MSGP_GET(msgp, magic)) != MAGIC); |
1017 | Boolean invalidComplex = (0 != msgp->body.msgh_descriptor_count) && !(msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX); | |
1018 | invalidComplex = invalidComplex || ((msgp->header.msgh_bits & MACH_MSGH_BITS_COMPLEX) && (0 == msgp->body.msgh_descriptor_count)); | |
1019 | Boolean wayTooBig = ((msgp->body.msgh_descriptor_count) ? sizeof(struct __CFMessagePortMachMessage1) : sizeof(struct __CFMessagePortMachMessage0) + __CFMessagePortMaxInlineBytes) < msgp->header.msgh_size; | |
1020 | Boolean wayTooSmall = msgp->header.msgh_size < sizeof(struct __CFMessagePortMachMessage0); | |
cf7d2af9 A |
1021 | Boolean wrongSize = false; |
1022 | if (!(invalidComplex || wayTooBig || wayTooSmall)) { | |
f64f9b69 | 1023 | byteslen = CFSwapInt32LittleToHost(MSGP_GET(msgp, byteslen)); |
cf7d2af9 | 1024 | wrongSize = (byteslen < 0) || (__CFMessagePortMaxDataSize < byteslen); |
f64f9b69 A |
1025 | if (0 != msgp->body.msgh_descriptor_count) { |
1026 | wrongSize = wrongSize || (MSGP1_FIELD(msgp, ool).size != byteslen); | |
cf7d2af9 | 1027 | } else { |
f64f9b69 | 1028 | wrongSize = wrongSize || (msgp->header.msgh_size - sizeof(struct __CFMessagePortMachMessage0) < byteslen); |
cf7d2af9 A |
1029 | } |
1030 | } | |
f64f9b69 A |
1031 | Boolean invalidMsgID = (msgp->header.msgh_id <= 0) || (INT32_MAX < msgp->header.msgh_id); // conversation id |
1032 | if (invalidMagic || invalidComplex || wayTooBig || wayTooSmall || wrongSize || invalidMsgID) { | |
1033 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort: dropping corrupt request Mach message (0b%d%d%d%d%d%d)"), invalidMagic, invalidComplex, wayTooBig, wayTooSmall, wrongSize, invalidMsgID); | |
cf7d2af9 A |
1034 | mach_msg_destroy((mach_msg_header_t *)msgp); |
1035 | return NULL; | |
1036 | } | |
1037 | ||
9ce05555 A |
1038 | /* Create no-copy, no-free-bytes wrapper CFData */ |
1039 | if (0 == msgp->body.msgh_descriptor_count) { | |
f64f9b69 A |
1040 | uintptr_t msgp_extent = (uintptr_t)((uint8_t *)msgp + msgp->header.msgh_size); |
1041 | uintptr_t data_extent = (uintptr_t)((uint8_t *)&(MSGP0_FIELD(msgp, bytes)) + byteslen); | |
1042 | msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid)); | |
cf7d2af9 | 1043 | if (0 <= byteslen && data_extent <= msgp_extent) { |
f64f9b69 | 1044 | data = CFDataCreateWithBytesNoCopy(allocator, MSGP0_FIELD(msgp, bytes), byteslen, kCFAllocatorNull); |
9ce05555 A |
1045 | } |
1046 | } else { | |
f64f9b69 A |
1047 | msgid = CFSwapInt32LittleToHost(MSGP_GET(msgp, msgid)); |
1048 | data = CFDataCreateWithBytesNoCopy(allocator, MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size, kCFAllocatorNull); | |
9ce05555 | 1049 | } |
8ca704e1 A |
1050 | if (ms->_callout) { |
1051 | returnData = ms->_callout(ms, msgid, data, context_info); | |
1052 | } else { | |
1053 | mach_msg_trailer_t *trailer = (mach_msg_trailer_t *)(((uintptr_t)&(msgp->header) + msgp->header.msgh_size + sizeof(natural_t) - 1) & ~(sizeof(natural_t) - 1)); | |
1054 | returnData = ms->_calloutEx(ms, msgid, data, context_info, trailer, 0); | |
1055 | } | |
9ce05555 A |
1056 | /* Now, returnData could be (1) NULL, (2) an ordinary data < MAX_INLINE, |
1057 | (3) ordinary data >= MAX_INLINE, (4) a no-copy data < MAX_INLINE, | |
1058 | (5) a no-copy data >= MAX_INLINE. In cases (2) and (4), we send the return | |
1059 | bytes inline in the Mach message, so can release the returnData object | |
1060 | here. In cases (3) and (5), we'll send the data out-of-line, we need to | |
1061 | create a copy of the memory, which we'll have the kernel autodeallocate | |
1062 | for us on send. In case (4) also, the bytes in the return data may be part | |
1063 | of the bytes in "data" that we sent into the callout, so if the incoming | |
1064 | data was received out of line, we wouldn't be able to clean up the out-of-line | |
1065 | wad until the message was sent either, if we didn't make the copy. */ | |
1066 | if (NULL != returnData) { | |
1067 | return_len = CFDataGetLength(returnData); | |
cf7d2af9 A |
1068 | if (__CFMessagePortMaxDataSize < return_len) { |
1069 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePort reply: CFMessagePort cannot send more than %lu bytes of data"), __CFMessagePortMaxDataSize); | |
1070 | return_len = 0; | |
1071 | returnData = NULL; | |
1072 | } | |
1073 | if (returnData && return_len < __CFMessagePortMaxInlineBytes) { | |
9ce05555 | 1074 | return_bytes = (void *)CFDataGetBytePtr(returnData); |
cf7d2af9 | 1075 | } else if (returnData) { |
9ce05555 A |
1076 | return_bytes = NULL; |
1077 | vm_allocate(mach_task_self(), (vm_address_t *)&return_bytes, return_len, VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_MACH_MSG)); | |
1078 | /* vm_copy would only be a win here if the source address | |
1079 | is page aligned; it is a lose in all other cases, since | |
1080 | the kernel will just do the memmove for us (but not in | |
1081 | as simple a way). */ | |
1082 | memmove(return_bytes, CFDataGetBytePtr(returnData), return_len); | |
1083 | } | |
1084 | } | |
f64f9b69 | 1085 | replymsg = __CFMessagePortCreateMessage(true, msgp->header.msgh_remote_port, MACH_PORT_NULL, -1 * (int32_t)msgp->header.msgh_id, msgid, return_bytes, return_len); |
9ce05555 | 1086 | if (1 == replymsg->body.msgh_descriptor_count) { |
f64f9b69 | 1087 | MSGP1_FIELD(replymsg, ool).deallocate = true; |
9ce05555 A |
1088 | } |
1089 | if (data) CFRelease(data); | |
1090 | if (1 == msgp->body.msgh_descriptor_count) { | |
f64f9b69 | 1091 | vm_deallocate(mach_task_self(), (vm_address_t)MSGP1_FIELD(msgp, ool).address, MSGP1_FIELD(msgp, ool).size); |
9ce05555 A |
1092 | } |
1093 | if (returnData) CFRelease(returnData); | |
1094 | if (context_release) { | |
1095 | context_release(context_info); | |
1096 | } | |
1097 | return replymsg; | |
1098 | } | |
1099 | ||
1100 | CFRunLoopSourceRef CFMessagePortCreateRunLoopSource(CFAllocatorRef allocator, CFMessagePortRef ms, CFIndex order) { | |
1101 | CFRunLoopSourceRef result = NULL; | |
1102 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
7c97c3e0 A |
1103 | if (!CFMessagePortIsValid(ms)) return NULL; |
1104 | if (__CFMessagePortIsRemote(ms)) return NULL; | |
9ce05555 | 1105 | __CFMessagePortLock(ms); |
7c97c3e0 A |
1106 | if (NULL != ms->_source && !CFRunLoopSourceIsValid(ms->_source)) { |
1107 | CFRelease(ms->_source); | |
1108 | ms->_source = NULL; | |
1109 | } | |
cf7d2af9 | 1110 | if (NULL == ms->_source && NULL == ms->_dispatchSource && __CFMessagePortIsValid(ms)) { |
9ce05555 A |
1111 | CFRunLoopSourceContext1 context; |
1112 | context.version = 1; | |
1113 | context.info = (void *)ms; | |
1114 | context.retain = (const void *(*)(const void *))CFRetain; | |
1115 | context.release = (void (*)(const void *))CFRelease; | |
1116 | context.copyDescription = (CFStringRef (*)(const void *))__CFMessagePortCopyDescription; | |
bd5b749c A |
1117 | context.equal = NULL; |
1118 | context.hash = NULL; | |
9ce05555 A |
1119 | context.getPort = __CFMessagePortGetPort; |
1120 | context.perform = __CFMessagePortPerform; | |
1121 | ms->_source = CFRunLoopSourceCreate(allocator, order, (CFRunLoopSourceContext *)&context); | |
1122 | } | |
1123 | if (NULL != ms->_source) { | |
1124 | result = (CFRunLoopSourceRef)CFRetain(ms->_source); | |
1125 | } | |
1126 | __CFMessagePortUnlock(ms); | |
1127 | return result; | |
1128 | } | |
1129 | ||
cf7d2af9 A |
1130 | void CFMessagePortSetDispatchQueue(CFMessagePortRef ms, dispatch_queue_t queue) { |
1131 | __CFGenericValidateType(ms, __kCFMessagePortTypeID); | |
1132 | __CFMessagePortLock(ms); | |
1133 | if (!__CFMessagePortIsValid(ms)) { | |
1134 | __CFMessagePortUnlock(ms); | |
1135 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is invalid")); | |
1136 | return; | |
1137 | } | |
1138 | if (__CFMessagePortIsRemote(ms)) { | |
1139 | __CFMessagePortUnlock(ms); | |
1140 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort is not a local port, queue cannot be set")); | |
1141 | return; | |
1142 | } | |
1143 | if (NULL != ms->_source) { | |
1144 | __CFMessagePortUnlock(ms); | |
1145 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): CFMessagePort already has a CFRunLoopSourceRef, queue cannot be set")); | |
1146 | return; | |
1147 | } | |
1148 | ||
1149 | if (ms->_dispatchSource) { | |
7c97c3e0 | 1150 | dispatch_source_cancel(ms->_dispatchSource); |
cf7d2af9 A |
1151 | ms->_dispatchSource = NULL; |
1152 | ms->_dispatchQ = NULL; | |
1153 | } | |
1154 | ||
1155 | if (queue) { | |
1156 | mach_port_t port = __CFMessagePortGetPort(ms); | |
1157 | if (MACH_PORT_NULL != port) { | |
7c97c3e0 A |
1158 | dispatch_source_t theSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, port, 0, __mportQueue()); |
1159 | dispatch_source_set_cancel_handler(theSource, ^{ | |
1160 | dispatch_release(queue); | |
1161 | dispatch_release(theSource); | |
1162 | }); | |
1163 | dispatch_source_set_event_handler(theSource, ^{ | |
1164 | CFRetain(ms); | |
1165 | mach_msg_header_t *msg = (mach_msg_header_t *)CFAllocatorAllocate(kCFAllocatorSystemDefault, 2048, 0); | |
1166 | msg->msgh_size = 2048; | |
1167 | ||
1168 | for (;;) { | |
1169 | msg->msgh_bits = 0; | |
1170 | msg->msgh_local_port = port; | |
1171 | msg->msgh_remote_port = MACH_PORT_NULL; | |
1172 | msg->msgh_id = 0; | |
1173 | ||
1174 | 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); | |
1175 | if (MACH_MSG_SUCCESS == ret) break; | |
1176 | if (MACH_RCV_TOO_LARGE != ret) HALT; | |
1177 | ||
1178 | uint32_t newSize = round_msg(msg->msgh_size + MAX_TRAILER_SIZE); | |
1179 | msg = CFAllocatorReallocate(kCFAllocatorSystemDefault, msg, newSize, 0); | |
1180 | msg->msgh_size = newSize; | |
1181 | } | |
1182 | ||
1183 | dispatch_async(queue, ^{ | |
1184 | mach_msg_header_t *reply = __CFMessagePortPerform(msg, msg->msgh_size, kCFAllocatorSystemDefault, ms); | |
1185 | if (NULL != reply) { | |
1186 | kern_return_t ret = mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); | |
1187 | if (KERN_SUCCESS != ret) mach_msg_destroy(reply); | |
1188 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); | |
1189 | } | |
1190 | CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg); | |
1191 | CFRelease(ms); | |
1192 | }); | |
1193 | }); | |
1194 | ms->_dispatchSource = theSource; | |
1195 | } | |
cf7d2af9 A |
1196 | if (ms->_dispatchSource) { |
1197 | dispatch_retain(queue); | |
1198 | ms->_dispatchQ = queue; | |
1199 | dispatch_resume(ms->_dispatchSource); | |
1200 | } else { | |
1201 | CFLog(kCFLogLevelWarning, CFSTR("*** CFMessagePortSetDispatchQueue(): dispatch source could not be created")); | |
1202 | } | |
1203 | } | |
1204 | __CFMessagePortUnlock(ms); | |
1205 | } | |
9ce05555 | 1206 |