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