2 * Copyright (c) 2000-2005, 2008-2015 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 os_activity_t activity_id
;
67 mach_no_senders_notification_t
*buf
= msg
;
68 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
69 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
70 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
72 activity_id
= os_activity_start("processing SCDynamicStore notification",
73 OS_ACTIVITY_FLAG_DEFAULT
);
75 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
76 /* the server died, disable additional callbacks */
78 SC_log(LOG_DEBUG
, " notifier port closed");
82 if (port
!= storePrivate
->rlsNotifyPort
) {
83 SC_log(LOG_DEBUG
, "why is port != rlsNotifyPort?");
87 /* re-establish notification and inform the client */
88 (void)__SCDynamicStoreReconnectNotifications(store
);
92 SC_log(LOG_DEBUG
, "mach port callback, signal RLS");
95 /* signal the real runloop source */
96 if (storePrivate
->rls
!= NULL
) {
97 CFRunLoopSourceSignal(storePrivate
->rls
);
100 os_activity_end(activity_id
);
107 rlsSchedule(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
109 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
110 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
113 SC_log(LOG_DEBUG
, "schedule notifications for mode %@",
114 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
117 if (storePrivate
->rlList
== NULL
) {
118 CFMachPortContext context
= { 0
122 , notifyMPCopyDescription
125 mach_port_t oldNotify
;
126 #ifdef HAVE_MACHPORT_GUARDS
127 mach_port_options_t opts
;
128 #endif // HAVE_MACHPORT_GUARDS
133 SC_log(LOG_DEBUG
, " activate callback runloop source");
136 /* allocate a mach port for the SCDynamicStore notifications */
140 #ifdef HAVE_MACHPORT_GUARDS
141 bzero(&opts
, sizeof(opts
));
142 opts
.flags
= MPO_CONTEXT_AS_GUARD
|MPO_INSERT_SEND_RIGHT
;
144 kr
= mach_port_construct(mach_task_self(), &opts
, store
, &port
);
145 #else // HAVE_MACHPORT_GUARDS
146 kr
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &port
);
147 #endif // HAVE_MACHPORT_GUARDS
149 if (kr
!= KERN_SUCCESS
) {
150 SC_log(LOG_NOTICE
, "could not allocate mach port: %s", mach_error_string(kr
));
151 if ((kr
== KERN_NO_SPACE
) || (kr
== KERN_RESOURCE_SHORTAGE
)) {
159 #ifndef HAVE_MACHPORT_GUARDS
160 kr
= mach_port_insert_right(mach_task_self(),
163 MACH_MSG_TYPE_MAKE_SEND
);
164 if (kr
!= KERN_SUCCESS
) {
166 * We can't insert a send right into our own port! This should
167 * only happen if someone stomped on OUR port (so let's leave
170 SC_log(LOG_NOTICE
, "mach_port_insert_right() failed: %s", mach_error_string(kr
));
173 #endif // HAVE_MACHPORT_GUARDS
175 /* Request a notification when/if the server dies */
176 kr
= mach_port_request_notification(mach_task_self(),
178 MACH_NOTIFY_NO_SENDERS
,
181 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
183 if (kr
!= KERN_SUCCESS
) {
185 * We can't request a notification for our own port! This should
186 * only happen if someone stomped on OUR port (so let's leave
189 SC_log(LOG_NOTICE
, "mach_port_request_notification() failed: %s", mach_error_string(kr
));
193 if (oldNotify
!= MACH_PORT_NULL
) {
194 SC_log(LOG_NOTICE
, "oldNotify != MACH_PORT_NULL");
199 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule", port
);
200 kr
= notifyviaport(storePrivate
->server
, port
, 0, (int *)&sc_status
);
202 if (__SCDynamicStoreCheckRetryAndHandleError(store
,
205 "rlsSchedule notifyviaport()")) {
209 if (kr
!= KERN_SUCCESS
) {
210 if ((kr
== MACH_SEND_INVALID_DEST
) || (kr
== MIG_SERVER_DIED
)) {
211 /* remove the send right that we tried (but failed) to pass to the server */
212 (void) mach_port_deallocate(mach_task_self(), port
);
215 /* 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 if (sc_status
!= kSCStatusOK
) {
225 /* something [else] didn't work, remove our receive right */
226 #ifdef HAVE_MACHPORT_GUARDS
227 (void) mach_port_destruct(mach_task_self(), port
, 0, store
);
228 #else // HAVE_MACHPORT_GUARDS
229 (void) mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
230 #endif // HAVE_MACHPORT_GUARDS
234 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after notifyviaport)", port
);
235 storePrivate
->rlsNotifyPort
= _SC_CFMachPortCreateWithPort("SCDynamicStore",
239 storePrivate
->rlsNotifyRLS
= CFMachPortCreateRunLoopSource(NULL
, storePrivate
->rlsNotifyPort
, 0);
241 storePrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
244 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
245 if (!_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
247 * if we are not already scheduled with this runLoop / runLoopMode
249 CFRunLoopAddSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
250 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
253 _SC_schedule(store
, rl
, mode
, storePrivate
->rlList
);
261 rlsCancel(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
264 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
265 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
268 SC_log(LOG_DEBUG
, "cancel notifications for mode %@",
269 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
272 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
273 if (_SC_unschedule(store
, rl
, mode
, storePrivate
->rlList
, FALSE
)) {
275 * if currently scheduled on this runLoop / runLoopMode
277 n
= CFArrayGetCount(storePrivate
->rlList
);
278 if (n
== 0 || !_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
280 * if we are no longer scheduled to receive notifications for
281 * this runLoop / runLoopMode
283 CFRunLoopRemoveSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
293 SC_log(LOG_DEBUG
, " cancel callback runloop source");
295 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
297 CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
299 if (storePrivate
->rlList
!= NULL
) {
300 CFRelease(storePrivate
->rlList
);
301 storePrivate
->rlList
= NULL
;
304 if (storePrivate
->rlsNotifyRLS
!= NULL
) {
305 /* invalidate & remove the run loop source */
306 CFRunLoopSourceInvalidate(storePrivate
->rlsNotifyRLS
);
307 CFRelease(storePrivate
->rlsNotifyRLS
);
308 storePrivate
->rlsNotifyRLS
= NULL
;
311 if (storePrivate
->rlsNotifyPort
!= NULL
) {
314 mp
= CFMachPortGetPort(storePrivate
->rlsNotifyPort
);
315 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
316 "*** rlsCancel (before invalidating/releasing CFMachPort)",
319 /* invalidate and release port */
320 CFMachPortInvalidate(storePrivate
->rlsNotifyPort
);
321 CFRelease(storePrivate
->rlsNotifyPort
);
322 storePrivate
->rlsNotifyPort
= NULL
;
324 /* and, finally, remove our receive right */
325 #ifdef HAVE_MACHPORT_GUARDS
326 (void) mach_port_destruct(mach_task_self(), mp
, 0, store
);
327 #else // HAVE_MACHPORT_GUARDS
328 (void) mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
329 #endif // HAVE_MACHPORT_GUARDS
332 if (storePrivate
->server
!= MACH_PORT_NULL
) {
333 kr
= notifycancel(storePrivate
->server
, (int *)&sc_status
);
335 (void) __SCDynamicStoreCheckRetryAndHandleError(store
,
338 "rlsCancel notifycancel()");
340 if (kr
!= KERN_SUCCESS
) {
351 rlsPerform(void *info
)
353 os_activity_t activity_id
;
354 CFArrayRef changedKeys
= NULL
;
356 void (*context_release
)(const void *);
357 SCDynamicStoreCallBack rlsFunction
;
358 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
359 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
361 activity_id
= os_activity_start("processing SCDynamicStore notification",
362 OS_ACTIVITY_FLAG_DEFAULT
);
365 SC_log(LOG_DEBUG
, " executing notification function");
368 changedKeys
= SCDynamicStoreCopyNotifiedKeys(store
);
369 if (storePrivate
->disconnectForceCallBack
) {
370 storePrivate
->disconnectForceCallBack
= FALSE
;
371 if (changedKeys
== NULL
) {
372 changedKeys
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
374 } else if ((changedKeys
== NULL
) || (CFArrayGetCount(changedKeys
) == 0)) {
375 /* if no changes or something happened to the server */
379 rlsFunction
= storePrivate
->rlsFunction
;
381 if (storePrivate
->rlsContext
.retain
!= NULL
) {
382 context_info
= (void *)storePrivate
->rlsContext
.retain(storePrivate
->rlsContext
.info
);
383 context_release
= storePrivate
->rlsContext
.release
;
385 context_info
= storePrivate
->rlsContext
.info
;
386 context_release
= NULL
;
388 if (rlsFunction
!= NULL
) {
389 (*rlsFunction
)(store
, changedKeys
, context_info
);
391 if (context_release
!= NULL
) {
392 context_release(context_info
);
397 if (changedKeys
!= NULL
) {
398 CFRelease(changedKeys
);
401 os_activity_end(activity_id
);
408 rlsRetain(CFTypeRef cf
)
410 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
411 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
413 switch (storePrivate
->notifyStatus
) {
414 case NotifierNotRegistered
:
415 /* mark RLS active */
416 storePrivate
->notifyStatus
= Using_NotifierInformViaRunLoop
;
417 /* keep a reference to the store */
420 case Using_NotifierInformViaRunLoop
:
423 SC_log(LOG_NOTICE
, "unexpected notify status=%d", storePrivate
->notifyStatus
);
432 rlsRelease(CFTypeRef cf
)
434 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
435 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
437 switch (storePrivate
->notifyStatus
) {
438 case NotifierNotRegistered
:
440 case Using_NotifierInformViaRunLoop
:
441 /* mark RLS inactive */
442 storePrivate
->notifyStatus
= NotifierNotRegistered
;
443 storePrivate
->rls
= NULL
;
445 /* release our reference to the store */
449 SC_log(LOG_NOTICE
, "unexpected notify status=%d", storePrivate
->notifyStatus
);
458 rlsCopyDescription(const void *info
)
460 CFMutableStringRef result
;
461 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
462 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
464 result
= CFStringCreateMutable(NULL
, 0);
465 CFStringAppendFormat(result
, NULL
, CFSTR("<SCDynamicStore RLS> {"));
466 CFStringAppendFormat(result
, NULL
, CFSTR("store = %p"), store
);
467 if (storePrivate
->notifyStatus
== Using_NotifierInformViaRunLoop
) {
468 CFStringRef description
= NULL
;
470 CFStringAppendFormat(result
, NULL
, CFSTR(", callout = %p"), storePrivate
->rlsFunction
);
472 if ((storePrivate
->rlsContext
.info
!= NULL
) && (storePrivate
->rlsContext
.copyDescription
!= NULL
)) {
473 description
= (*storePrivate
->rlsContext
.copyDescription
)(storePrivate
->rlsContext
.info
);
475 if (description
== NULL
) {
476 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCDynamicStore context %p>"), storePrivate
->rlsContext
.info
);
478 if (description
== NULL
) {
479 description
= CFRetain(CFSTR("<no description>"));
481 CFStringAppendFormat(result
, NULL
, CFSTR(", context = %@"), description
);
482 CFRelease(description
);
484 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
491 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator
,
492 SCDynamicStoreRef store
,
495 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
498 /* sorry, you must provide a session */
499 _SCErrorSet(kSCStatusNoStoreSession
);
503 if (storePrivate
->server
== MACH_PORT_NULL
) {
504 /* sorry, you must have an open session to play */
505 _SCErrorSet(kSCStatusNoStoreServer
);
509 switch (storePrivate
->notifyStatus
) {
510 case NotifierNotRegistered
:
511 case Using_NotifierInformViaRunLoop
:
512 /* OK to enable runloop notification */
515 /* sorry, you can only have one notification registered at once */
516 _SCErrorSet(kSCStatusNotifierActive
);
520 if (storePrivate
->rls
!= NULL
) {
521 CFRetain(storePrivate
->rls
);
523 CFRunLoopSourceContext context
= { 0 // version
524 , (void *)store
// info
525 , rlsRetain
// retain
526 , rlsRelease
// release
527 , rlsCopyDescription
// copyDescription
530 , rlsSchedule
// schedule
531 , rlsCancel
// cancel
532 , rlsPerform
// perform
535 storePrivate
->rls
= CFRunLoopSourceCreate(allocator
, order
, &context
);
536 if (storePrivate
->rls
== NULL
) {
537 _SCErrorSet(kSCStatusFailed
);
541 return storePrivate
->rls
;
546 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store
, dispatch_queue_t queue
)
548 dispatch_group_t drainGroup
= NULL
;
549 dispatch_queue_t drainQueue
= NULL
;
550 dispatch_group_t group
= NULL
;
553 dispatch_source_t source
;
554 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
557 // sorry, you must provide a session
558 _SCErrorSet(kSCStatusNoStoreSession
);
563 if (storePrivate
->dispatchQueue
== NULL
) {
564 _SCErrorSet(kSCStatusInvalidArgument
);
572 if (storePrivate
->server
== MACH_PORT_NULL
) {
573 // sorry, you must have an open session to play
574 _SCErrorSet(kSCStatusNoStoreServer
);
578 if ((storePrivate
->dispatchQueue
!= NULL
) || (storePrivate
->rls
!= NULL
)) {
579 _SCErrorSet(kSCStatusInvalidArgument
);
583 if (storePrivate
->notifyStatus
!= NotifierNotRegistered
) {
584 // sorry, you can only have one notification registered at once...
585 _SCErrorSet(kSCStatusNotifierActive
);
590 * mark our using of the SCDynamicStore notifications, create and schedule
591 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
594 storePrivate
->notifyStatus
= Using_NotifierInformViaDispatch
;
595 rlsSchedule((void*)store
, NULL
, NULL
);
596 if (storePrivate
->rlsNotifyPort
== NULL
) {
597 /* if we could not schedule the notification */
598 _SCErrorSet(kSCStatusFailed
);
602 // retain the dispatch queue
603 storePrivate
->dispatchQueue
= queue
;
604 dispatch_retain(storePrivate
->dispatchQueue
);
607 // We've taken a reference to the callers dispatch_queue and we
608 // want to hold on to that reference until we've processed any/all
609 // notifications. To facilitate this we create a group, dispatch
610 // any notification blocks to via that group, and when the caller
611 // has told us to stop the notifications (unschedule) we wait for
612 // the group to empty and use the group's finalizer to release
613 // our reference to the SCDynamicStore.
615 group
= dispatch_group_create();
616 storePrivate
->dispatchGroup
= group
;
618 dispatch_set_context(storePrivate
->dispatchGroup
, (void *)store
);
619 dispatch_set_finalizer_f(storePrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
621 // create a dispatch source for the mach notifications
622 mp
= CFMachPortGetPort(storePrivate
->rlsNotifyPort
);
623 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
624 if (source
== NULL
) {
625 SC_log(LOG_NOTICE
, "dispatch_source_create() failed");
626 _SCErrorSet(kSCStatusFailed
);
630 dispatch_source_set_event_handler(source
, ^{
634 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
635 mach_msg_empty_rcv_t msg
;
636 mach_no_senders_notification_t no_senders
;
639 kr
= mach_msg(¬ify_msg
.msg
.header
, // msg
640 MACH_RCV_MSG
, // options
642 sizeof(notify_msg
), // rcv_size
644 MACH_MSG_TIMEOUT_NONE
, // timeout
645 MACH_PORT_NULL
); // notify
646 if (kr
!= KERN_SUCCESS
) {
647 SC_log(LOG_NOTICE
, "mach_msg() failed, kr=0x%x", kr
);
651 msgid
= notify_msg
.msg
.header
.msgh_id
;
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
);
659 rlsPerform(storePrivate
);
664 dispatch_source_set_cancel_handler(source
, ^{
665 dispatch_release(source
);
668 storePrivate
->dispatchSource
= source
;
669 dispatch_resume(source
);
677 if (storePrivate
->dispatchSource
!= NULL
) {
678 dispatch_source_cancel(storePrivate
->dispatchSource
);
679 storePrivate
->dispatchSource
= NULL
;
681 drainGroup
= storePrivate
->dispatchGroup
;
682 storePrivate
->dispatchGroup
= NULL
;
683 drainQueue
= storePrivate
->dispatchQueue
;
684 storePrivate
->dispatchQueue
= NULL
;
686 rlsCancel((void*)store
, NULL
, NULL
);
688 if (drainGroup
!= NULL
) {
689 dispatch_group_notify(drainGroup
, drainQueue
, ^{
690 // release group/queue references
691 dispatch_release(drainQueue
);
692 dispatch_release(drainGroup
); // releases our store reference
696 storePrivate
->notifyStatus
= NotifierNotRegistered
;