]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
configd-1061.0.2.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005, 2008-2019 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 31, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include "SCDynamicStoreInternal.h"
35 #include "config.h" /* MiG generated file */
36 #include "SCD.h"
37
38 #if !TARGET_OS_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
39 #define HAVE_MACHPORT_GUARDS
40 #endif
41
42
43 static void
44 removeReceiveRight(SCDynamicStoreRef store, mach_port_t port)
45 {
46 /* remove our receive right */
47 #ifdef HAVE_MACHPORT_GUARDS
48 (void) mach_port_destruct(mach_task_self(), port, 0, (mach_port_context_t)store);
49 #else // HAVE_MACHPORT_GUARDS
50 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
51 #endif // HAVE_MACHPORT_GUARDS
52 return;
53 }
54
55
56 #define MAX_INVALID_RIGHT_RETRY 3
57
58 static mach_port_t
59 addNotifyPort(SCDynamicStoreRef store)
60 {
61 kern_return_t kr;
62 mach_port_t oldNotify;
63 #ifdef HAVE_MACHPORT_GUARDS
64 mach_port_options_t opts;
65 #endif // HAVE_MACHPORT_GUARDS
66 mach_port_t port;
67 int sc_status;
68 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
69 int try = 0;
70
71 /* allocate a mach port for the SCDynamicStore notifications */
72
73 retry_allocate :
74
75 #ifdef HAVE_MACHPORT_GUARDS
76 memset(&opts, 0, sizeof(opts));
77 opts.flags = MPO_CONTEXT_AS_GUARD|MPO_INSERT_SEND_RIGHT;
78
79 kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)store, &port);
80 #else // HAVE_MACHPORT_GUARDS
81 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
82 #endif // HAVE_MACHPORT_GUARDS
83
84 if (kr != KERN_SUCCESS) {
85 SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr));
86 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
87 usleep(50 * 1000); // sleep 50ms between attempts
88 goto retry_allocate;
89 }
90 goto fail;
91 }
92
93 #ifndef HAVE_MACHPORT_GUARDS
94 kr = mach_port_insert_right(mach_task_self(),
95 port,
96 port,
97 MACH_MSG_TYPE_MAKE_SEND);
98 if (kr != KERN_SUCCESS) {
99 /*
100 * We can't insert a send right into our own port! This should
101 * only happen if someone stomped on OUR port (so let's leave
102 * the port alone).
103 */
104 SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(kr));
105 goto fail;
106 }
107 #endif // HAVE_MACHPORT_GUARDS
108
109 /* Request a notification when/if the server dies */
110 kr = mach_port_request_notification(mach_task_self(),
111 port,
112 MACH_NOTIFY_NO_SENDERS,
113 1,
114 port,
115 MACH_MSG_TYPE_MAKE_SEND_ONCE,
116 &oldNotify);
117 if (kr != KERN_SUCCESS) {
118 /*
119 * We can't request a notification for our own port! This should
120 * only happen if someone stomped on OUR port (so let's leave
121 * the port alone).
122 */
123 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(kr));
124 goto fail;
125 }
126
127 if (oldNotify != MACH_PORT_NULL) {
128 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
129 }
130
131 #ifdef DEBUG
132 SC_log(LOG_DEBUG, "+ establish notification request w/port=0x%x (%d) with SCDynamicStore server (%@)",
133 port, port,
134 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
135 #endif /* DEBUG */
136
137 retry :
138
139 __MACH_PORT_DEBUG(TRUE, "*** addNotifyPort", port);
140 kr = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
141
142 if (kr == MACH_SEND_INVALID_RIGHT) {
143 /*
144 * the notifyviaport() call was not able to pass/process
145 * the [notify port] send right. This was most likely due
146 * to the [SCDynamicStore] server having restarted. We
147 * handle this issue by removing the remaining receive
148 * right, reallocating the notify port, and retrying
149 * the operation.
150 */
151 SC_log((try++ < MAX_INVALID_RIGHT_RETRY) ? LOG_INFO : LOG_ERR,
152 "SCDynamicStore callback notifyviaport() w/port 0x%x (%d) failed (try %d): %s",
153 port, port,
154 try,
155 mach_error_string(kr));
156
157 removeReceiveRight(store, port);
158 }
159
160 if (__SCDynamicStoreCheckRetryAndHandleError(store,
161 kr,
162 &sc_status,
163 "SCDynamicStore callback notifyviaport()")) {
164 if (kr == MACH_SEND_INVALID_RIGHT) {
165 if (try <= MAX_INVALID_RIGHT_RETRY) {
166 goto retry_allocate;
167 }
168 goto fail;
169 }
170 goto retry;
171 }
172
173 if (try > 0) {
174 SC_log(LOG_INFO,
175 "SCDynamicStore callback notifyviaport() succeeded after %d retr%s w/port 0x%x (%d)",
176 try,
177 (try == 1) ? "y" : "ies",
178 port, port);
179 }
180
181 if (kr != KERN_SUCCESS) {
182 if ((kr == MACH_SEND_INVALID_DEST) || (kr == MIG_SERVER_DIED)) {
183 /* remove the send right that we tried (but failed) to pass to the server */
184 (void) mach_port_deallocate(mach_task_self(), port);
185 }
186 removeReceiveRight(store, port);
187 goto fail;
188 }
189
190 if (sc_status != kSCStatusOK) {
191 /* something [else] didn't work */
192 removeReceiveRight(store, port);
193 kr = sc_status;
194 goto fail;
195 }
196
197 return port;
198
199 fail :
200
201 _SCErrorSet(kr);
202 return MACH_PORT_NULL;
203 }
204
205
206 static CFStringRef
207 notifyMPCopyDescription(const void *info)
208 {
209 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
210
211 return CFStringCreateWithFormat(NULL,
212 NULL,
213 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
214 store);
215 }
216
217
218 static void
219 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
220 {
221 #ifndef DEBUG
222 #pragma unused(port)
223 #endif /* DEBUG */
224 #pragma unused(size)
225 mach_no_senders_notification_t *buf = msg;
226 mach_msg_id_t msgid = buf->not_header.msgh_id;
227 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
228 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
229
230 #ifdef DEBUG
231 SC_log(LOG_DEBUG, "mach port callback, %ssignal RLS(%@)",
232 (msgid == MACH_NOTIFY_NO_SENDERS) ? "reconnect and " : "",
233 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
234 #endif /* DEBUG */
235
236 if (msgid == MACH_NOTIFY_NO_SENDERS) {
237 /* the server died, disable additional callbacks */
238 #ifdef DEBUG
239 SC_log(LOG_DEBUG, " notifier port closed");
240 #endif /* DEBUG */
241
242 #ifdef DEBUG
243 if (port != storePrivate->rlsNotifyPort) {
244 SC_log(LOG_DEBUG, "why is port != rlsNotifyPort?");
245 }
246 #endif /* DEBUG */
247
248 /* re-establish notification and inform the client */
249 (void)__SCDynamicStoreReconnectNotifications(store);
250 }
251
252 /* signal the real runloop source */
253 if (storePrivate->rls != NULL) {
254 CFRunLoopSourceSignal(storePrivate->rls);
255 }
256
257 return;
258 }
259
260
261 static void
262 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
263 {
264 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
265 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
266
267 #ifdef DEBUG
268 SC_log(LOG_DEBUG, "schedule notifications for mode %@", mode);
269 #endif /* DEBUG */
270
271 if (storePrivate->rlsNotifyPort == NULL) {
272 CFMachPortContext context = { 0
273 , (void *)store
274 , CFRetain
275 , CFRelease
276 , notifyMPCopyDescription
277 };
278 mach_port_t port;
279
280 #ifdef DEBUG
281 SC_log(LOG_DEBUG, " activate callback runloop source");
282 #endif /* DEBUG */
283
284 port = addNotifyPort(store);
285 if (port == MACH_PORT_NULL) {
286 return;
287 }
288
289 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after addNotifyPort)", port);
290 storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
291 port,
292 rlsCallback,
293 &context);
294
295 if (rl != NULL) {
296 storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
297 storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
298 }
299 }
300
301 if (storePrivate->rlsNotifyRLS != NULL) {
302 /* set notifier active */
303 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
304
305 if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
306 /*
307 * if we are not already scheduled with this runLoop / runLoopMode
308 */
309 CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
310 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
311 }
312
313 _SC_schedule(store, rl, mode, storePrivate->rlList);
314 }
315
316 return;
317 }
318
319
320 static void
321 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
322 {
323 CFIndex n = 0;
324 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
325 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
326
327 #ifdef DEBUG
328 SC_log(LOG_DEBUG, "cancel notifications for mode %@", mode);
329 #endif /* DEBUG */
330
331 if (storePrivate->rlsNotifyRLS != NULL) {
332 if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
333 /*
334 * if currently scheduled on this runLoop / runLoopMode
335 */
336 n = CFArrayGetCount(storePrivate->rlList);
337 if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
338 /*
339 * if we are no longer scheduled to receive notifications for
340 * this runLoop / runLoopMode
341 */
342 CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
343 }
344 }
345 }
346
347 if (n == 0) {
348 int sc_status;
349 kern_return_t kr;
350
351 #ifdef DEBUG
352 SC_log(LOG_DEBUG, " cancel callback runloop source");
353 #endif /* DEBUG */
354 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
355 "*** rlsCancel",
356 CFMachPortGetPort(storePrivate->rlsNotifyPort));
357
358 if (storePrivate->rls != NULL) {
359 // Remove the reference we took on the rls. We do not invalidate
360 // the runloop source and let the client do it when appropriate.
361 CFRelease(storePrivate->rls);
362 storePrivate->rls = NULL;
363 }
364
365 if (storePrivate->rlList != NULL) {
366 CFRelease(storePrivate->rlList);
367 storePrivate->rlList = NULL;
368 }
369
370 if (storePrivate->rlsNotifyRLS != NULL) {
371 /* invalidate & remove the run loop source */
372 CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
373 CFRelease(storePrivate->rlsNotifyRLS);
374 storePrivate->rlsNotifyRLS = NULL;
375 }
376
377 if (storePrivate->rlsNotifyPort != NULL) {
378 mach_port_t mp;
379
380 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
381 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
382 "*** rlsCancel (before invalidating/releasing CFMachPort)",
383 mp);
384
385 /* invalidate and release port */
386 CFMachPortInvalidate(storePrivate->rlsNotifyPort);
387 CFRelease(storePrivate->rlsNotifyPort);
388 storePrivate->rlsNotifyPort = NULL;
389
390 /* and, finally, remove our receive right */
391 removeReceiveRight(store, mp);
392 }
393
394 #ifdef DEBUG
395 SC_log(LOG_DEBUG, " cancel notification request with SCDynamicStore server");
396 #endif /* DEBUG */
397
398 if (storePrivate->server != MACH_PORT_NULL) {
399 kr = notifycancel(storePrivate->server, (int *)&sc_status);
400
401 (void) __SCDynamicStoreCheckRetryAndHandleError(store,
402 kr,
403 &sc_status,
404 "rlsCancel notifycancel()");
405
406 if (kr != KERN_SUCCESS) {
407 return;
408 }
409 }
410
411 /* set notifier inactive */
412 storePrivate->notifyStatus = NotifierNotRegistered;
413 }
414
415 return;
416 }
417
418
419 static void
420 rlsPerform(void *info)
421 {
422 CFArrayRef changedKeys = NULL;
423 void *context_info;
424 void (*context_release)(const void *);
425 SCDynamicStoreCallBack rlsFunction;
426 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
427 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
428
429 #ifdef DEBUG
430 SC_log(LOG_DEBUG, "handling SCDynamicStore changes (%@)",
431 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
432 #endif /* DEBUG */
433
434 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
435 if (storePrivate->disconnectForceCallBack) {
436 storePrivate->disconnectForceCallBack = FALSE;
437 if (changedKeys == NULL) {
438 changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
439 }
440 } else if ((changedKeys == NULL) || (CFArrayGetCount(changedKeys) == 0)) {
441 /* if no changes or something happened to the server */
442 goto done;
443 }
444
445 rlsFunction = storePrivate->rlsFunction;
446
447 if (storePrivate->rlsContext.retain != NULL) {
448 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
449 context_release = storePrivate->rlsContext.release;
450 } else {
451 context_info = storePrivate->rlsContext.info;
452 context_release = NULL;
453 }
454 if (rlsFunction != NULL) {
455 SC_log(LOG_DEBUG, "+ exec SCDynamicStore callout");
456 (*rlsFunction)(store, changedKeys, context_info);
457 SC_log(LOG_DEBUG, "+ done");
458 }
459 if (context_release != NULL) {
460 context_release(context_info);
461 }
462
463 done :
464
465 #ifdef DEBUG
466 SC_log(LOG_DEBUG, "done");
467 #endif /* DEBUG */
468
469 if (changedKeys != NULL) {
470 CFRelease(changedKeys);
471 }
472
473 return;
474 }
475
476
477 static CFStringRef
478 rlsCopyDescription(const void *info)
479 {
480 CFMutableStringRef result;
481 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
482 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
483
484 result = CFStringCreateMutable(NULL, 0);
485 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
486 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
487 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
488 CFStringRef description = NULL;
489
490 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
491
492 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
493 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
494 }
495 if (description == NULL) {
496 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
497 }
498 if (description == NULL) {
499 description = CFRetain(CFSTR("<no description>"));
500 }
501 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
502 CFRelease(description);
503 }
504 CFStringAppendFormat(result, NULL, CFSTR("}"));
505
506 return result;
507 }
508
509
510 CFRunLoopSourceRef
511 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
512 SCDynamicStoreRef store,
513 CFIndex order)
514 {
515 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
516
517 if (store == NULL) {
518 /* sorry, you must provide a session */
519 _SCErrorSet(kSCStatusNoStoreSession);
520 return NULL;
521 }
522
523 if (storePrivate->server == MACH_PORT_NULL) {
524 /* sorry, you must have an open session to play */
525 _SCErrorSet(kSCStatusNoStoreServer);
526 return NULL;
527 }
528
529 switch (storePrivate->notifyStatus) {
530 case NotifierNotRegistered :
531 case Using_NotifierInformViaRunLoop :
532 /* OK to enable runloop notification */
533 break;
534 default :
535 /* sorry, you can only have one notification registered at once */
536 _SCErrorSet(kSCStatusNotifierActive);
537 return NULL;
538 }
539
540 if (storePrivate->rls == NULL) {
541 CFRunLoopSourceContext context = { 0 // version
542 , (void *)store // info
543 , CFRetain // retain
544 , CFRelease // release
545 , rlsCopyDescription // copyDescription
546 , CFEqual // equal
547 , CFHash // hash
548 , rlsSchedule // schedule
549 , rlsCancel // cancel
550 , rlsPerform // perform
551 };
552
553 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
554 if (storePrivate->rls == NULL) {
555 _SCErrorSet(kSCStatusFailed);
556 }
557 }
558
559 if (storePrivate->rls != NULL) {
560 CFRetain(storePrivate->rls);
561 }
562
563 return storePrivate->rls;
564 }
565
566
567 Boolean
568 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
569 {
570 dispatch_group_t drainGroup = NULL;
571 dispatch_queue_t drainQueue = NULL;
572 dispatch_group_t group = NULL;
573 mach_port_t mp;
574 Boolean ok = FALSE;
575 dispatch_source_t source;
576 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
577
578 if (store == NULL) {
579 // sorry, you must provide a session
580 _SCErrorSet(kSCStatusNoStoreSession);
581 return FALSE;
582 }
583
584 if (queue == NULL) {
585 if (storePrivate->dispatchQueue == NULL) {
586 // if not scheduled
587 _SCErrorSet(kSCStatusInvalidArgument);
588 return FALSE;
589 }
590
591 #ifdef DEBUG
592 SC_log(LOG_DEBUG, "unschedule notifications from dispatch queue (%@)",
593 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
594 #endif /* DEBUG */
595
596 ok = TRUE;
597 goto cleanup;
598 }
599
600 if (storePrivate->server == MACH_PORT_NULL) {
601 // sorry, you must have an open session to play
602 _SCErrorSet(kSCStatusNoStoreServer);
603 return FALSE;
604 }
605
606 if ((storePrivate->dispatchQueue != NULL) ||
607 (storePrivate->rls != NULL) ||
608 (storePrivate->notifyStatus != NotifierNotRegistered)) {
609 // if already scheduled
610 _SCErrorSet(kSCStatusNotifierActive);
611 return FALSE;
612 }
613
614 #ifdef DEBUG
615 SC_log(LOG_DEBUG, "schedule notifications for dispatch queue (%@)",
616 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
617 #endif /* DEBUG */
618
619 //
620 // mark our using of the SCDynamicStore notifications, create and schedule
621 // the notification source/port (storePrivate->dispatchSource), and a bunch
622 // of other "setup"
623 //
624 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
625
626 mp = addNotifyPort(store);
627 if (mp == MACH_PORT_NULL) {
628 // if we could not schedule the notification
629 _SCErrorSet(kSCStatusFailed);
630 #ifdef DEBUG
631 SC_log(LOG_DEBUG, "addNotifyPort() failed (%@)",
632 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
633 #endif /* DEBUG */
634 goto cleanup;
635 }
636
637 // retain the dispatch queue
638 storePrivate->dispatchQueue = queue;
639 dispatch_retain(storePrivate->dispatchQueue);
640
641 //
642 // We've taken a reference to the callers dispatch_queue and we
643 // want to hold on to that reference until we've processed any/all
644 // notifications. To facilitate this we create a group, dispatch
645 // any notification blocks to via that group, and when the caller
646 // has told us to stop the notifications (unschedule) we wait for
647 // the group to empty and use the group's finalizer to release
648 // our reference to the SCDynamicStore.
649 //
650 group = dispatch_group_create();
651 storePrivate->dispatchGroup = group;
652 CFRetain(store);
653 dispatch_set_context(storePrivate->dispatchGroup, (void *)store);
654 dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
655
656 // create a dispatch source for the mach notifications
657 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
658 if (source == NULL) {
659 SC_log(LOG_NOTICE, "dispatch_source_create() failed");
660
661 // remove our receive right
662 removeReceiveRight(store, mp);
663
664 _SCErrorSet(kSCStatusFailed);
665 goto cleanup;
666 }
667
668 dispatch_source_set_event_handler(source, ^{
669 kern_return_t kr;
670 mach_msg_id_t msgid;
671 union {
672 u_int8_t buf1[sizeof(mach_msg_empty_rcv_t) + MAX_TRAILER_SIZE];
673 u_int8_t buf2[sizeof(mach_no_senders_notification_t) + MAX_TRAILER_SIZE];
674 mach_msg_empty_rcv_t msg;
675 mach_no_senders_notification_t no_senders;
676 } notify_msg;
677
678 kr = mach_msg(&notify_msg.msg.header, // msg
679 MACH_RCV_MSG, // options
680 0, // send_size
681 sizeof(notify_msg), // rcv_size
682 mp, // rcv_name
683 MACH_MSG_TIMEOUT_NONE, // timeout
684 MACH_PORT_NULL); // notify
685 if (kr != KERN_SUCCESS) {
686 SC_log(LOG_NOTICE, "mach_msg() failed, kr=0x%x", kr);
687 return;
688 }
689
690 msgid = notify_msg.msg.header.msgh_id;
691 mach_msg_destroy(&notify_msg.msg.header);
692
693 #ifdef DEBUG
694 SC_log(LOG_DEBUG, "dispatch source callback, queue rlsPerform (%@)",
695 (storePrivate->name != NULL) ? storePrivate->name : CFSTR("?"));
696 #endif /* DEBUG */
697
698 CFRetain(store);
699 dispatch_group_async(group, queue, ^{
700 if (msgid == MACH_NOTIFY_NO_SENDERS) {
701 // re-establish notification and inform the client
702 (void)__SCDynamicStoreReconnectNotifications(store);
703 }
704 rlsPerform(storePrivate);
705 CFRelease(store);
706 });
707 });
708
709 dispatch_source_set_cancel_handler(source, ^{
710 __MACH_PORT_DEBUG(TRUE,
711 "*** SCDynamicStoreSetDispatchQueue (before cancel)",
712 mp);
713
714 // remove our receive right
715 removeReceiveRight(store, mp);
716
717 // release source
718 dispatch_release(source);
719 });
720
721 storePrivate->dispatchSource = source;
722 dispatch_resume(source);
723
724 return TRUE;
725
726 cleanup :
727
728 CFRetain(store);
729
730 if (storePrivate->dispatchSource != NULL) {
731 dispatch_source_cancel(storePrivate->dispatchSource);
732 storePrivate->dispatchSource = NULL;
733 }
734 drainGroup = storePrivate->dispatchGroup;
735 storePrivate->dispatchGroup = NULL;
736 drainQueue = storePrivate->dispatchQueue;
737 storePrivate->dispatchQueue = NULL;
738
739 if ((drainGroup != NULL) && (drainQueue != NULL)) {
740 dispatch_group_notify(drainGroup, drainQueue, ^{
741 // release group/queue references
742 dispatch_release(drainQueue);
743 dispatch_release(drainGroup); // releases our store reference
744 });
745 }
746
747 storePrivate->notifyStatus = NotifierNotRegistered;
748
749 CFRelease(store);
750
751 return ok;
752 }