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