]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCDNotifierInformViaCallback.c
4c8812ba83cda01f8a70d8fbe3861e05e4b6a923
[apple/configd.git] / SystemConfiguration.fproj / SCDNotifierInformViaCallback.c
1 /*
2 * Copyright (c) 2000-2005, 2008-2017 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 CFStringRef
44 notifyMPCopyDescription(const void *info)
45 {
46 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
47
48 return CFStringCreateWithFormat(NULL,
49 NULL,
50 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
51 store);
52 }
53
54
55 static void
56 rlsCallback(CFMachPortRef port, void *msg, CFIndex size, void *info)
57 {
58 #pragma unused(size)
59 mach_no_senders_notification_t *buf = msg;
60 mach_msg_id_t msgid = buf->not_header.msgh_id;
61 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
62 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
63
64 if (msgid == MACH_NOTIFY_NO_SENDERS) {
65 /* the server died, disable additional callbacks */
66 #ifdef DEBUG
67 SC_log(LOG_DEBUG, " notifier port closed");
68 #endif /* DEBUG */
69
70 #ifdef DEBUG
71 if (port != storePrivate->rlsNotifyPort) {
72 SC_log(LOG_DEBUG, "why is port != rlsNotifyPort?");
73 }
74 #endif /* DEBUG */
75
76 /* re-establish notification and inform the client */
77 (void)__SCDynamicStoreReconnectNotifications(store);
78 }
79
80 #ifdef DEBUG
81 SC_log(LOG_DEBUG, "mach port callback, signal RLS");
82 #endif /* DEBUG */
83
84 /* signal the real runloop source */
85 if (storePrivate->rls != NULL) {
86 CFRunLoopSourceSignal(storePrivate->rls);
87 }
88
89 return;
90 }
91
92
93 static void
94 rlsSchedule(void *info, CFRunLoopRef rl, CFStringRef mode)
95 {
96 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
97 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
98
99 #ifdef DEBUG
100 SC_log(LOG_DEBUG, "schedule notifications for mode %@",
101 (rl != NULL) ? mode : CFSTR("libdispatch"));
102 #endif /* DEBUG */
103
104 if (storePrivate->rlsNotifyPort == NULL) {
105 CFMachPortContext context = { 0
106 , (void *)store
107 , CFRetain
108 , CFRelease
109 , notifyMPCopyDescription
110 };
111 kern_return_t kr;
112 mach_port_t oldNotify;
113 #ifdef HAVE_MACHPORT_GUARDS
114 mach_port_options_t opts;
115 #endif // HAVE_MACHPORT_GUARDS
116 mach_port_t port;
117 int sc_status;
118
119 #ifdef DEBUG
120 SC_log(LOG_DEBUG, " activate callback runloop source");
121 #endif /* DEBUG */
122
123 /* allocate a mach port for the SCDynamicStore notifications */
124
125 retry_allocate :
126
127 #ifdef HAVE_MACHPORT_GUARDS
128 bzero(&opts, sizeof(opts));
129 opts.flags = MPO_CONTEXT_AS_GUARD|MPO_INSERT_SEND_RIGHT;
130
131 kr = mach_port_construct(mach_task_self(), &opts, (mach_port_context_t)store, &port);
132 #else // HAVE_MACHPORT_GUARDS
133 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
134 #endif // HAVE_MACHPORT_GUARDS
135
136 if (kr != KERN_SUCCESS) {
137 SC_log(LOG_NOTICE, "could not allocate mach port: %s", mach_error_string(kr));
138 if ((kr == KERN_NO_SPACE) || (kr == KERN_RESOURCE_SHORTAGE)) {
139 sleep(1);
140 goto retry_allocate;
141 } else {
142 return;
143 }
144 }
145
146 #ifndef HAVE_MACHPORT_GUARDS
147 kr = mach_port_insert_right(mach_task_self(),
148 port,
149 port,
150 MACH_MSG_TYPE_MAKE_SEND);
151 if (kr != KERN_SUCCESS) {
152 /*
153 * We can't insert a send right into our own port! This should
154 * only happen if someone stomped on OUR port (so let's leave
155 * the port alone).
156 */
157 SC_log(LOG_NOTICE, "mach_port_insert_right() failed: %s", mach_error_string(kr));
158 return;
159 }
160 #endif // HAVE_MACHPORT_GUARDS
161
162 /* Request a notification when/if the server dies */
163 kr = mach_port_request_notification(mach_task_self(),
164 port,
165 MACH_NOTIFY_NO_SENDERS,
166 1,
167 port,
168 MACH_MSG_TYPE_MAKE_SEND_ONCE,
169 &oldNotify);
170 if (kr != KERN_SUCCESS) {
171 /*
172 * We can't request a notification for our own port! This should
173 * only happen if someone stomped on OUR port (so let's leave
174 * the port alone).
175 */
176 SC_log(LOG_NOTICE, "mach_port_request_notification() failed: %s", mach_error_string(kr));
177 return;
178 }
179
180 if (oldNotify != MACH_PORT_NULL) {
181 SC_log(LOG_NOTICE, "oldNotify != MACH_PORT_NULL");
182 }
183
184 #ifdef DEBUG
185 SC_log(LOG_DEBUG, " establish notification request with SCDynamicStore server");
186 #endif /* DEBUG */
187
188 #ifdef VERBOSE_ACTIVITY_LOGGING
189 os_activity_scope(storePrivate->activity);
190 #endif // VERBOSE_ACTIVITY_LOGGING
191
192 retry :
193
194 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule", port);
195 kr = notifyviaport(storePrivate->server, port, 0, (int *)&sc_status);
196
197 if (__SCDynamicStoreCheckRetryAndHandleError(store,
198 kr,
199 &sc_status,
200 "rlsSchedule notifyviaport()")) {
201 goto retry;
202 }
203
204 if (kr != KERN_SUCCESS) {
205 if ((kr == MACH_SEND_INVALID_DEST) || (kr == MIG_SERVER_DIED)) {
206 /* remove the send right that we tried (but failed) to pass to the server */
207 (void) mach_port_deallocate(mach_task_self(), port);
208 }
209
210 /* remove our receive right */
211 #ifdef HAVE_MACHPORT_GUARDS
212 (void) mach_port_destruct(mach_task_self(), port, 0, (mach_port_context_t)store);
213 #else // HAVE_MACHPORT_GUARDS
214 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
215 #endif // HAVE_MACHPORT_GUARDS
216 return;
217 }
218
219 if (sc_status != kSCStatusOK) {
220 /* something [else] didn't work, remove our receive right */
221 #ifdef HAVE_MACHPORT_GUARDS
222 (void) mach_port_destruct(mach_task_self(), port, 0, (mach_port_context_t)store);
223 #else // HAVE_MACHPORT_GUARDS
224 (void) mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1);
225 #endif // HAVE_MACHPORT_GUARDS
226 return;
227 }
228
229 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after notifyviaport)", port);
230 storePrivate->rlsNotifyPort = _SC_CFMachPortCreateWithPort("SCDynamicStore",
231 port,
232 rlsCallback,
233 &context);
234
235 if (rl != NULL) {
236 storePrivate->rlsNotifyRLS = CFMachPortCreateRunLoopSource(NULL, storePrivate->rlsNotifyPort, 0);
237 storePrivate->rlList = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
238 }
239 }
240
241 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
242 /* set notifier active */
243 storePrivate->notifyStatus = Using_NotifierInformViaRunLoop;
244
245 if (!_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
246 /*
247 * if we are not already scheduled with this runLoop / runLoopMode
248 */
249 CFRunLoopAddSource(rl, storePrivate->rlsNotifyRLS, mode);
250 __MACH_PORT_DEBUG(TRUE, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate->rlsNotifyPort));
251 }
252
253 _SC_schedule(store, rl, mode, storePrivate->rlList);
254 }
255
256 return;
257 }
258
259
260 static void
261 rlsCancel(void *info, CFRunLoopRef rl, CFStringRef mode)
262 {
263 CFIndex n = 0;
264 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
265 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
266
267 #ifdef DEBUG
268 SC_log(LOG_DEBUG, "cancel notifications for mode %@",
269 (rl != NULL) ? mode : CFSTR("libdispatch"));
270 #endif /* DEBUG */
271
272 if ((rl != NULL) && (storePrivate->rlsNotifyRLS != NULL)) {
273 if (_SC_unschedule(store, rl, mode, storePrivate->rlList, FALSE)) {
274 /*
275 * if currently scheduled on this runLoop / runLoopMode
276 */
277 n = CFArrayGetCount(storePrivate->rlList);
278 if (n == 0 || !_SC_isScheduled(store, rl, mode, storePrivate->rlList)) {
279 /*
280 * if we are no longer scheduled to receive notifications for
281 * this runLoop / runLoopMode
282 */
283 CFRunLoopRemoveSource(rl, storePrivate->rlsNotifyRLS, mode);
284 }
285 }
286 }
287
288 if (n == 0) {
289 int sc_status;
290 kern_return_t kr;
291
292 #ifdef DEBUG
293 SC_log(LOG_DEBUG, " cancel callback runloop source");
294 #endif /* DEBUG */
295 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
296 "*** rlsCancel",
297 CFMachPortGetPort(storePrivate->rlsNotifyPort));
298
299 if (storePrivate->rls != NULL) {
300 // Remove the reference we took on the rls. We do not invalidate
301 // the runloop source and let the client do it when appropriate.
302 CFRelease(storePrivate->rls);
303 storePrivate->rls = NULL;
304 }
305
306 if (storePrivate->rlList != NULL) {
307 CFRelease(storePrivate->rlList);
308 storePrivate->rlList = NULL;
309 }
310
311 if (storePrivate->rlsNotifyRLS != NULL) {
312 /* invalidate & remove the run loop source */
313 CFRunLoopSourceInvalidate(storePrivate->rlsNotifyRLS);
314 CFRelease(storePrivate->rlsNotifyRLS);
315 storePrivate->rlsNotifyRLS = NULL;
316 }
317
318 if (storePrivate->rlsNotifyPort != NULL) {
319 mach_port_t mp;
320
321 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
322 __MACH_PORT_DEBUG((storePrivate->rlsNotifyPort != NULL),
323 "*** rlsCancel (before invalidating/releasing CFMachPort)",
324 mp);
325
326 /* invalidate and release port */
327 CFMachPortInvalidate(storePrivate->rlsNotifyPort);
328 CFRelease(storePrivate->rlsNotifyPort);
329 storePrivate->rlsNotifyPort = NULL;
330
331 /* and, finally, remove our receive right */
332 #ifdef HAVE_MACHPORT_GUARDS
333 (void) mach_port_destruct(mach_task_self(), mp, 0, (mach_port_context_t)store);
334 #else // HAVE_MACHPORT_GUARDS
335 (void) mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1);
336 #endif // HAVE_MACHPORT_GUARDS
337 }
338
339 #ifdef DEBUG
340 SC_log(LOG_DEBUG, " cancel notification request with SCDynamicStore server");
341 #endif /* DEBUG */
342
343 #ifdef VERBOSE_ACTIVITY_LOGGING
344 os_activity_scope(storePrivate->activity);
345 #endif // VERBOSE_ACTIVITY_LOGGING
346
347 if (storePrivate->server != MACH_PORT_NULL) {
348 kr = notifycancel(storePrivate->server, (int *)&sc_status);
349
350 (void) __SCDynamicStoreCheckRetryAndHandleError(store,
351 kr,
352 &sc_status,
353 "rlsCancel notifycancel()");
354
355 if (kr != KERN_SUCCESS) {
356 return;
357 }
358 }
359
360 /* set notifier inactive */
361 storePrivate->notifyStatus = NotifierNotRegistered;
362 }
363
364 return;
365 }
366
367
368 static void
369 rlsPerform(void *info)
370 {
371 CFArrayRef changedKeys = NULL;
372 void *context_info;
373 void (*context_release)(const void *);
374 SCDynamicStoreCallBack rlsFunction;
375 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
376 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
377
378 #ifdef DEBUG
379 SC_log(LOG_DEBUG, " executing notification function");
380 #endif /* DEBUG */
381
382 changedKeys = SCDynamicStoreCopyNotifiedKeys(store);
383 if (storePrivate->disconnectForceCallBack) {
384 storePrivate->disconnectForceCallBack = FALSE;
385 if (changedKeys == NULL) {
386 changedKeys = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks);
387 }
388 } else if ((changedKeys == NULL) || (CFArrayGetCount(changedKeys) == 0)) {
389 /* if no changes or something happened to the server */
390 goto done;
391 }
392
393 rlsFunction = storePrivate->rlsFunction;
394
395 if (storePrivate->rlsContext.retain != NULL) {
396 context_info = (void *)storePrivate->rlsContext.retain(storePrivate->rlsContext.info);
397 context_release = storePrivate->rlsContext.release;
398 } else {
399 context_info = storePrivate->rlsContext.info;
400 context_release = NULL;
401 }
402 if (rlsFunction != NULL) {
403 SC_log(LOG_DEBUG, "exec SCDynamicStore callout");
404 (*rlsFunction)(store, changedKeys, context_info);
405 }
406 if (context_release != NULL) {
407 context_release(context_info);
408 }
409
410 done :
411
412 #ifdef DEBUG
413 SC_log(LOG_DEBUG, " done!");
414 #endif /* DEBUG */
415
416 if (changedKeys != NULL) {
417 CFRelease(changedKeys);
418 }
419
420 return;
421 }
422
423
424 static CFStringRef
425 rlsCopyDescription(const void *info)
426 {
427 CFMutableStringRef result;
428 SCDynamicStoreRef store = (SCDynamicStoreRef)info;
429 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
430
431 result = CFStringCreateMutable(NULL, 0);
432 CFStringAppendFormat(result, NULL, CFSTR("<SCDynamicStore RLS> {"));
433 CFStringAppendFormat(result, NULL, CFSTR("store = %p"), store);
434 if (storePrivate->notifyStatus == Using_NotifierInformViaRunLoop) {
435 CFStringRef description = NULL;
436
437 CFStringAppendFormat(result, NULL, CFSTR(", callout = %p"), storePrivate->rlsFunction);
438
439 if ((storePrivate->rlsContext.info != NULL) && (storePrivate->rlsContext.copyDescription != NULL)) {
440 description = (*storePrivate->rlsContext.copyDescription)(storePrivate->rlsContext.info);
441 }
442 if (description == NULL) {
443 description = CFStringCreateWithFormat(NULL, NULL, CFSTR("<SCDynamicStore context %p>"), storePrivate->rlsContext.info);
444 }
445 if (description == NULL) {
446 description = CFRetain(CFSTR("<no description>"));
447 }
448 CFStringAppendFormat(result, NULL, CFSTR(", context = %@"), description);
449 CFRelease(description);
450 }
451 CFStringAppendFormat(result, NULL, CFSTR("}"));
452
453 return result;
454 }
455
456
457 CFRunLoopSourceRef
458 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator,
459 SCDynamicStoreRef store,
460 CFIndex order)
461 {
462 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
463
464 if (store == NULL) {
465 /* sorry, you must provide a session */
466 _SCErrorSet(kSCStatusNoStoreSession);
467 return NULL;
468 }
469
470 if (storePrivate->server == MACH_PORT_NULL) {
471 /* sorry, you must have an open session to play */
472 _SCErrorSet(kSCStatusNoStoreServer);
473 return NULL;
474 }
475
476 switch (storePrivate->notifyStatus) {
477 case NotifierNotRegistered :
478 case Using_NotifierInformViaRunLoop :
479 /* OK to enable runloop notification */
480 break;
481 default :
482 /* sorry, you can only have one notification registered at once */
483 _SCErrorSet(kSCStatusNotifierActive);
484 return NULL;
485 }
486
487 if (storePrivate->rls == NULL) {
488 CFRunLoopSourceContext context = { 0 // version
489 , (void *)store // info
490 , CFRetain // retain
491 , CFRelease // release
492 , rlsCopyDescription // copyDescription
493 , CFEqual // equal
494 , CFHash // hash
495 , rlsSchedule // schedule
496 , rlsCancel // cancel
497 , rlsPerform // perform
498 };
499
500 storePrivate->rls = CFRunLoopSourceCreate(allocator, order, &context);
501 if (storePrivate->rls == NULL) {
502 _SCErrorSet(kSCStatusFailed);
503 }
504 }
505
506 if (storePrivate->rls != NULL) {
507 CFRetain(storePrivate->rls);
508 }
509
510 return storePrivate->rls;
511 }
512
513
514 Boolean
515 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store, dispatch_queue_t queue)
516 {
517 dispatch_group_t drainGroup = NULL;
518 dispatch_queue_t drainQueue = NULL;
519 dispatch_group_t group = NULL;
520 mach_port_t mp;
521 Boolean ok = FALSE;
522 dispatch_source_t source;
523 SCDynamicStorePrivateRef storePrivate = (SCDynamicStorePrivateRef)store;
524
525 if (store == NULL) {
526 // sorry, you must provide a session
527 _SCErrorSet(kSCStatusNoStoreSession);
528 return FALSE;
529 }
530
531 if (queue == NULL) {
532 if (storePrivate->dispatchQueue == NULL) {
533 _SCErrorSet(kSCStatusInvalidArgument);
534 return FALSE;
535 }
536
537 ok = TRUE;
538 goto cleanup;
539 }
540
541 if (storePrivate->server == MACH_PORT_NULL) {
542 // sorry, you must have an open session to play
543 _SCErrorSet(kSCStatusNoStoreServer);
544 return FALSE;
545 }
546
547 if ((storePrivate->dispatchQueue != NULL) || (storePrivate->rls != NULL)) {
548 _SCErrorSet(kSCStatusInvalidArgument);
549 return FALSE;
550 }
551
552 if (storePrivate->notifyStatus != NotifierNotRegistered) {
553 // sorry, you can only have one notification registered at once...
554 _SCErrorSet(kSCStatusNotifierActive);
555 return FALSE;
556 }
557
558 /*
559 * mark our using of the SCDynamicStore notifications, create and schedule
560 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
561 * "setup"
562 */
563 storePrivate->notifyStatus = Using_NotifierInformViaDispatch;
564 rlsSchedule((void*)store, NULL, NULL);
565 if (storePrivate->rlsNotifyPort == NULL) {
566 /* if we could not schedule the notification */
567 _SCErrorSet(kSCStatusFailed);
568 goto cleanup;
569 }
570
571 // retain the dispatch queue
572 storePrivate->dispatchQueue = queue;
573 dispatch_retain(storePrivate->dispatchQueue);
574
575 //
576 // We've taken a reference to the callers dispatch_queue and we
577 // want to hold on to that reference until we've processed any/all
578 // notifications. To facilitate this we create a group, dispatch
579 // any notification blocks to via that group, and when the caller
580 // has told us to stop the notifications (unschedule) we wait for
581 // the group to empty and use the group's finalizer to release
582 // our reference to the SCDynamicStore.
583 //
584 group = dispatch_group_create();
585 storePrivate->dispatchGroup = group;
586 CFRetain(store);
587 dispatch_set_context(storePrivate->dispatchGroup, (void *)store);
588 dispatch_set_finalizer_f(storePrivate->dispatchGroup, (dispatch_function_t)CFRelease);
589
590 // create a dispatch source for the mach notifications
591 mp = CFMachPortGetPort(storePrivate->rlsNotifyPort);
592 source = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, mp, 0, queue);
593 if (source == NULL) {
594 SC_log(LOG_NOTICE, "dispatch_source_create() failed");
595 _SCErrorSet(kSCStatusFailed);
596 goto cleanup;
597 }
598
599 dispatch_source_set_event_handler(source, ^{
600 kern_return_t kr;
601 mach_msg_id_t msgid;
602 union {
603 u_int8_t buf[sizeof(mach_msg_empty_t) + MAX_TRAILER_SIZE];
604 mach_msg_empty_rcv_t msg;
605 mach_no_senders_notification_t no_senders;
606 } notify_msg;
607
608 kr = mach_msg(&notify_msg.msg.header, // msg
609 MACH_RCV_MSG, // options
610 0, // send_size
611 sizeof(notify_msg), // rcv_size
612 mp, // rcv_name
613 MACH_MSG_TIMEOUT_NONE, // timeout
614 MACH_PORT_NULL); // notify
615 if (kr != KERN_SUCCESS) {
616 SC_log(LOG_NOTICE, "mach_msg() failed, kr=0x%x", kr);
617 return;
618 }
619
620 msgid = notify_msg.msg.header.msgh_id;
621
622 #ifdef DEBUG
623 SC_log(LOG_DEBUG, "dispatch source callback, queue rlsPerform");
624 #endif /* DEBUG */
625
626 CFRetain(store);
627 dispatch_group_async(group, queue, ^{
628 if (msgid == MACH_NOTIFY_NO_SENDERS) {
629 // re-establish notification and inform the client
630 (void)__SCDynamicStoreReconnectNotifications(store);
631 }
632 rlsPerform(storePrivate);
633 CFRelease(store);
634 });
635 });
636
637 dispatch_source_set_cancel_handler(source, ^{
638 dispatch_release(source);
639 });
640
641 storePrivate->dispatchSource = source;
642 dispatch_resume(source);
643
644 return TRUE;
645
646 cleanup :
647
648 CFRetain(store);
649
650 if (storePrivate->dispatchSource != NULL) {
651 dispatch_source_cancel(storePrivate->dispatchSource);
652 storePrivate->dispatchSource = NULL;
653 }
654 drainGroup = storePrivate->dispatchGroup;
655 storePrivate->dispatchGroup = NULL;
656 drainQueue = storePrivate->dispatchQueue;
657 storePrivate->dispatchQueue = NULL;
658
659 rlsCancel((void*)store, NULL, NULL);
660
661 if ((drainGroup != NULL) && (drainQueue != NULL)) {
662 dispatch_group_notify(drainGroup, drainQueue, ^{
663 // release group/queue references
664 dispatch_release(drainQueue);
665 dispatch_release(drainGroup); // releases our store reference
666 });
667 }
668
669 storePrivate->notifyStatus = NotifierNotRegistered;
670
671 CFRelease(store);
672
673 return ok;
674 }