2 * Copyright (c) 2000-2005, 2008-2014 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 * Modification History
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
30 * March 31, 2000 Allan Nathanson <ajn@apple.com>
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>
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCPrivate.h>
43 #include "SCDynamicStoreInternal.h"
44 #include "config.h" /* MiG generated file */
46 #if !TARGET_IPHONE_SIMULATOR || (defined(IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED) && (IPHONE_SIMULATOR_HOST_MIN_VERSION_REQUIRED >= 1090))
47 #define HAVE_MACHPORT_GUARDS
52 notifyMPCopyDescription(const void *info
)
54 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
56 return CFStringCreateWithFormat(NULL
,
58 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
64 rlsCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
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
;
71 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
72 /* the server died, disable additional callbacks */
74 SCLog(_sc_verbose
, LOG_INFO
, CFSTR(" rlsCallback(), notifier port closed"));
78 if (port
!= storePrivate
->rlsNotifyPort
) {
79 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
83 /* re-establish notification and inform the client */
84 (void)__SCDynamicStoreReconnectNotifications(store
);
87 /* signal the real runloop source */
88 if (storePrivate
->rls
!= NULL
) {
89 CFRunLoopSourceSignal(storePrivate
->rls
);
96 rlsSchedule(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
98 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
99 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
102 SCLog(_sc_verbose
, LOG_DEBUG
,
103 CFSTR("schedule notifications for mode %@"),
104 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
107 if (storePrivate
->rlList
== NULL
) {
108 CFMachPortContext context
= { 0
112 , notifyMPCopyDescription
115 mach_port_t oldNotify
;
116 #ifdef HAVE_MACHPORT_GUARDS
117 mach_port_options_t opts
;
118 #endif // HAVE_MACHPORT_GUARDS
123 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" activate callback runloop source"));
126 /* allocate a mach port for the SCDynamicStore notifications */
130 #ifdef HAVE_MACHPORT_GUARDS
131 bzero(&opts
, sizeof(opts
));
132 opts
.flags
= MPO_CONTEXT_AS_GUARD
|MPO_INSERT_SEND_RIGHT
;
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
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
)) {
149 #ifndef HAVE_MACHPORT_GUARDS
150 kr
= mach_port_insert_right(mach_task_self(),
153 MACH_MSG_TYPE_MAKE_SEND
);
154 if (kr
!= KERN_SUCCESS
) {
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
160 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(kr
));
163 #endif // HAVE_MACHPORT_GUARDS
165 /* Request a notification when/if the server dies */
166 kr
= mach_port_request_notification(mach_task_self(),
168 MACH_NOTIFY_NO_SENDERS
,
171 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
173 if (kr
!= KERN_SUCCESS
) {
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
179 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(kr
));
183 if (oldNotify
!= MACH_PORT_NULL
) {
184 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
189 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule", port
);
190 kr
= notifyviaport(storePrivate
->server
, port
, 0, (int *)&sc_status
);
192 if (__SCDynamicStoreCheckRetryAndHandleError(store
,
195 "rlsSchedule notifyviaport()")) {
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
);
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
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
224 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after notifyviaport)", port
);
225 storePrivate
->rlsNotifyPort
= _SC_CFMachPortCreateWithPort("SCDynamicStore",
229 storePrivate
->rlsNotifyRLS
= CFMachPortCreateRunLoopSource(NULL
, storePrivate
->rlsNotifyPort
, 0);
231 storePrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
234 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
235 if (!_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
237 * if we are not already scheduled with this runLoop / runLoopMode
239 CFRunLoopAddSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
240 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
243 _SC_schedule(store
, rl
, mode
, storePrivate
->rlList
);
251 rlsCancel(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
254 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
255 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
258 SCLog(_sc_verbose
, LOG_DEBUG
,
259 CFSTR("cancel notifications for mode %@"),
260 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
263 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
264 if (_SC_unschedule(store
, rl
, mode
, storePrivate
->rlList
, FALSE
)) {
266 * if currently scheduled on this runLoop / runLoopMode
268 n
= CFArrayGetCount(storePrivate
->rlList
);
269 if (n
== 0 || !_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
271 * if we are no longer scheduled to receive notifications for
272 * this runLoop / runLoopMode
274 CFRunLoopRemoveSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
284 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" cancel callback runloop source"));
286 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
288 CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
290 if (storePrivate
->rlList
!= NULL
) {
291 CFRelease(storePrivate
->rlList
);
292 storePrivate
->rlList
= NULL
;
295 if (storePrivate
->rlsNotifyRLS
!= NULL
) {
296 /* invalidate & remove the run loop source */
297 CFRunLoopSourceInvalidate(storePrivate
->rlsNotifyRLS
);
298 CFRelease(storePrivate
->rlsNotifyRLS
);
299 storePrivate
->rlsNotifyRLS
= NULL
;
302 if (storePrivate
->rlsNotifyPort
!= NULL
) {
305 mp
= CFMachPortGetPort(storePrivate
->rlsNotifyPort
);
306 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
307 "*** rlsCancel (before invalidating/releasing CFMachPort)",
310 /* invalidate and release port */
311 CFMachPortInvalidate(storePrivate
->rlsNotifyPort
);
312 CFRelease(storePrivate
->rlsNotifyPort
);
313 storePrivate
->rlsNotifyPort
= NULL
;
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
323 if (storePrivate
->server
!= MACH_PORT_NULL
) {
324 kr
= notifycancel(storePrivate
->server
, (int *)&sc_status
);
326 (void) __SCDynamicStoreCheckRetryAndHandleError(store
,
329 "rlsCancel notifycancel()");
331 if (kr
!= KERN_SUCCESS
) {
342 rlsPerform(void *info
)
344 CFArrayRef changedKeys
;
346 void (*context_release
)(const void *);
347 SCDynamicStoreCallBack rlsFunction
;
348 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
349 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
352 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" executing notification function"));
355 changedKeys
= SCDynamicStoreCopyNotifiedKeys(store
);
356 if (storePrivate
->disconnectForceCallBack
) {
357 storePrivate
->disconnectForceCallBack
= FALSE
;
358 if (changedKeys
== NULL
) {
359 changedKeys
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
362 if (changedKeys
== NULL
) {
363 /* if no changes or something happened to the server */
365 } else if (CFArrayGetCount(changedKeys
) == 0) {
370 rlsFunction
= storePrivate
->rlsFunction
;
372 if (storePrivate
->rlsContext
.retain
!= NULL
) {
373 context_info
= (void *)storePrivate
->rlsContext
.retain(storePrivate
->rlsContext
.info
);
374 context_release
= storePrivate
->rlsContext
.release
;
376 context_info
= storePrivate
->rlsContext
.info
;
377 context_release
= NULL
;
379 if (rlsFunction
!= NULL
) {
380 (*rlsFunction
)(store
, changedKeys
, context_info
);
382 if (context_release
!= NULL
) {
383 context_release(context_info
);
388 CFRelease(changedKeys
);
394 rlsRetain(CFTypeRef cf
)
396 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
397 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
399 switch (storePrivate
->notifyStatus
) {
400 case NotifierNotRegistered
:
401 /* mark RLS active */
402 storePrivate
->notifyStatus
= Using_NotifierInformViaRunLoop
;
403 /* keep a reference to the store */
406 case Using_NotifierInformViaRunLoop
:
409 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate
->notifyStatus
);
418 rlsRelease(CFTypeRef cf
)
420 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
421 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
423 switch (storePrivate
->notifyStatus
) {
424 case NotifierNotRegistered
:
426 case Using_NotifierInformViaRunLoop
:
427 /* mark RLS inactive */
428 storePrivate
->notifyStatus
= NotifierNotRegistered
;
429 storePrivate
->rls
= NULL
;
431 /* release our reference to the store */
435 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate
->notifyStatus
);
444 rlsCopyDescription(const void *info
)
446 CFMutableStringRef result
;
447 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
448 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
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
;
456 CFStringAppendFormat(result
, NULL
, CFSTR(", callout = %p"), storePrivate
->rlsFunction
);
458 if ((storePrivate
->rlsContext
.info
!= NULL
) && (storePrivate
->rlsContext
.copyDescription
!= NULL
)) {
459 description
= (*storePrivate
->rlsContext
.copyDescription
)(storePrivate
->rlsContext
.info
);
461 if (description
== NULL
) {
462 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCDynamicStore context %p>"), storePrivate
->rlsContext
.info
);
464 if (description
== NULL
) {
465 description
= CFRetain(CFSTR("<no description>"));
467 CFStringAppendFormat(result
, NULL
, CFSTR(", context = %@"), description
);
468 CFRelease(description
);
470 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
477 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator
,
478 SCDynamicStoreRef store
,
481 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
484 /* sorry, you must provide a session */
485 _SCErrorSet(kSCStatusNoStoreSession
);
489 if (storePrivate
->server
== MACH_PORT_NULL
) {
490 /* sorry, you must have an open session to play */
491 _SCErrorSet(kSCStatusNoStoreServer
);
495 switch (storePrivate
->notifyStatus
) {
496 case NotifierNotRegistered
:
497 case Using_NotifierInformViaRunLoop
:
498 /* OK to enable runloop notification */
501 /* sorry, you can only have one notification registered at once */
502 _SCErrorSet(kSCStatusNotifierActive
);
506 if (storePrivate
->rls
!= NULL
) {
507 CFRetain(storePrivate
->rls
);
509 CFRunLoopSourceContext context
= { 0 // version
510 , (void *)store
// info
511 , rlsRetain
// retain
512 , rlsRelease
// release
513 , rlsCopyDescription
// copyDescription
516 , rlsSchedule
// schedule
517 , rlsCancel
// cancel
518 , rlsPerform
// perform
521 storePrivate
->rls
= CFRunLoopSourceCreate(allocator
, order
, &context
);
522 if (storePrivate
->rls
== NULL
) {
523 _SCErrorSet(kSCStatusFailed
);
527 return storePrivate
->rls
;
532 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store
, dispatch_queue_t queue
)
534 dispatch_group_t drainGroup
= NULL
;
535 dispatch_queue_t drainQueue
= NULL
;
536 dispatch_group_t group
= NULL
;
539 dispatch_source_t source
;
540 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
543 // sorry, you must provide a session
544 _SCErrorSet(kSCStatusNoStoreSession
);
549 if (storePrivate
->dispatchQueue
== NULL
) {
550 _SCErrorSet(kSCStatusInvalidArgument
);
558 if (storePrivate
->server
== MACH_PORT_NULL
) {
559 // sorry, you must have an open session to play
560 _SCErrorSet(kSCStatusNoStoreServer
);
564 if ((storePrivate
->dispatchQueue
!= NULL
) || (storePrivate
->rls
!= NULL
)) {
565 _SCErrorSet(kSCStatusInvalidArgument
);
569 if (storePrivate
->notifyStatus
!= NotifierNotRegistered
) {
570 // sorry, you can only have one notification registered at once...
571 _SCErrorSet(kSCStatusNotifierActive
);
576 * mark our using of the SCDynamicStore notifications, create and schedule
577 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
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
);
588 // retain the dispatch queue
589 storePrivate
->dispatchQueue
= queue
;
590 dispatch_retain(storePrivate
->dispatchQueue
);
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.
601 group
= dispatch_group_create();
602 storePrivate
->dispatchGroup
= group
;
604 dispatch_set_context(storePrivate
->dispatchGroup
, (void *)store
);
605 dispatch_set_finalizer_f(storePrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
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
);
616 dispatch_source_set_event_handler(source
, ^{
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
;
625 kr
= mach_msg(¬ify_msg
.msg
.header
, // msg
626 MACH_RCV_MSG
, // options
628 sizeof(notify_msg
), // rcv_size
630 MACH_MSG_TIMEOUT_NONE
, // timeout
631 MACH_PORT_NULL
); // notify
632 if (kr
!= KERN_SUCCESS
) {
634 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
639 msgid
= notify_msg
.msg
.header
.msgh_id
;
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
);
647 rlsPerform(storePrivate
);
652 dispatch_source_set_cancel_handler(source
, ^{
653 dispatch_release(source
);
656 storePrivate
->dispatchSource
= source
;
657 dispatch_resume(source
);
665 if (storePrivate
->dispatchSource
!= NULL
) {
666 dispatch_source_cancel(storePrivate
->dispatchSource
);
667 storePrivate
->dispatchSource
= NULL
;
669 drainGroup
= storePrivate
->dispatchGroup
;
670 storePrivate
->dispatchGroup
= NULL
;
671 drainQueue
= storePrivate
->dispatchQueue
;
672 storePrivate
->dispatchQueue
= NULL
;
674 rlsCancel((void*)store
, NULL
, NULL
);
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
684 storePrivate
->notifyStatus
= NotifierNotRegistered
;