2 * Copyright (c) 2000-2005, 2008-2012 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 */
48 notifyMPCopyDescription(const void *info
)
50 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
52 return CFStringCreateWithFormat(NULL
,
54 CFSTR("<SCDynamicStore notification MP> {store = %p}"),
60 rlsCallback(CFMachPortRef port
, void *msg
, CFIndex size
, void *info
)
62 mach_no_senders_notification_t
*buf
= msg
;
63 mach_msg_id_t msgid
= buf
->not_header
.msgh_id
;
64 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
65 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
67 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
68 /* the server died, disable additional callbacks */
70 SCLog(_sc_verbose
, LOG_INFO
, CFSTR(" rlsCallback(), notifier port closed"));
74 if (port
!= storePrivate
->rlsNotifyPort
) {
75 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR("rlsCallback(), why is port != rlsNotifyPort?"));
79 /* re-establish notification and inform the client */
80 (void)__SCDynamicStoreReconnectNotifications(store
);
83 /* signal the real runloop source */
84 if (storePrivate
->rls
!= NULL
) {
85 CFRunLoopSourceSignal(storePrivate
->rls
);
92 portInvalidate(CFMachPortRef port
, void *info
) {
93 mach_port_t mp
= CFMachPortGetPort(port
);
95 __MACH_PORT_DEBUG(TRUE
, "*** portInvalidate", mp
);
96 /* remove our receive right */
97 (void)mach_port_mod_refs(mach_task_self(), mp
, MACH_PORT_RIGHT_RECEIVE
, -1);
102 rlsSchedule(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
104 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
105 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
108 SCLog(_sc_verbose
, LOG_DEBUG
,
109 CFSTR("schedule notifications for mode %@"),
110 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
113 if (storePrivate
->rlList
== NULL
) {
114 CFMachPortContext context
= { 0
118 , notifyMPCopyDescription
120 mach_port_t oldNotify
;
123 kern_return_t status
;
126 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" activate callback runloop source"));
129 /* Allocating port (for server response) */
130 status
= mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE
, &port
);
131 if (status
!= KERN_SUCCESS
) {
132 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule mach_port_allocate(): %s"), mach_error_string(status
));
136 status
= mach_port_insert_right(mach_task_self(),
139 MACH_MSG_TYPE_MAKE_SEND
);
140 if (status
!= KERN_SUCCESS
) {
142 * We can't insert a send right into our own port! This should
143 * only happen if someone stomped on OUR port (so let's leave
146 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule mach_port_insert_right(): %s"), mach_error_string(status
));
150 /* Request a notification when/if the server dies */
151 status
= mach_port_request_notification(mach_task_self(),
153 MACH_NOTIFY_NO_SENDERS
,
156 MACH_MSG_TYPE_MAKE_SEND_ONCE
,
158 if (status
!= KERN_SUCCESS
) {
160 * We can't request a notification for our own port! This should
161 * only happen if someone stomped on OUR port (so let's leave
164 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule mach_port_request_notification(): %s"), mach_error_string(status
));
168 if (oldNotify
!= MACH_PORT_NULL
) {
169 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsSchedule(): oldNotify != MACH_PORT_NULL"));
174 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule", port
);
175 status
= notifyviaport(storePrivate
->server
, port
, 0, (int *)&sc_status
);
177 if (__SCDynamicStoreCheckRetryAndHandleError(store
,
180 "rlsSchedule notifyviaport()")) {
184 if (status
!= KERN_SUCCESS
) {
185 if ((status
== MACH_SEND_INVALID_DEST
) || (status
== MIG_SERVER_DIED
)) {
186 /* remove the send right that we tried (but failed) to pass to the server */
187 (void) mach_port_deallocate(mach_task_self(), port
);
190 /* remove our receive right */
191 (void) mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
195 if (sc_status
!= kSCStatusOK
) {
196 /* something [else] didn't work, remove our receive right */
197 (void) mach_port_mod_refs(mach_task_self(), port
, MACH_PORT_RIGHT_RECEIVE
, -1);
201 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after notifyviaport)", port
);
202 storePrivate
->rlsNotifyPort
= _SC_CFMachPortCreateWithPort("SCDynamicStore",
206 CFMachPortSetInvalidationCallBack(storePrivate
->rlsNotifyPort
, portInvalidate
);
207 storePrivate
->rlsNotifyRLS
= CFMachPortCreateRunLoopSource(NULL
, storePrivate
->rlsNotifyPort
, 0);
209 storePrivate
->rlList
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
212 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
213 if (!_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
215 * if we are not already scheduled with this runLoop / runLoopMode
217 CFRunLoopAddSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
218 __MACH_PORT_DEBUG(TRUE
, "*** rlsSchedule (after CFRunLoopAddSource)", CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
221 _SC_schedule(store
, rl
, mode
, storePrivate
->rlList
);
229 rlsCancel(void *info
, CFRunLoopRef rl
, CFStringRef mode
)
232 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
233 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
236 SCLog(_sc_verbose
, LOG_DEBUG
,
237 CFSTR("cancel notifications for mode %@"),
238 (rl
!= NULL
) ? mode
: CFSTR("libdispatch"));
241 if ((rl
!= NULL
) && (storePrivate
->rlsNotifyRLS
!= NULL
)) {
242 if (_SC_unschedule(store
, rl
, mode
, storePrivate
->rlList
, FALSE
)) {
244 * if currently scheduled on this runLoop / runLoopMode
246 n
= CFArrayGetCount(storePrivate
->rlList
);
247 if (n
== 0 || !_SC_isScheduled(store
, rl
, mode
, storePrivate
->rlList
)) {
249 * if we are no longer scheduled to receive notifications for
250 * this runLoop / runLoopMode
252 CFRunLoopRemoveSource(rl
, storePrivate
->rlsNotifyRLS
, mode
);
259 kern_return_t status
;
262 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" cancel callback runloop source"));
264 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
266 CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
268 if (storePrivate
->rlList
!= NULL
) {
269 CFRelease(storePrivate
->rlList
);
270 storePrivate
->rlList
= NULL
;
273 if (storePrivate
->rlsNotifyRLS
!= NULL
) {
274 /* invalidate & remove the run loop source */
275 CFRunLoopSourceInvalidate(storePrivate
->rlsNotifyRLS
);
276 CFRelease(storePrivate
->rlsNotifyRLS
);
277 storePrivate
->rlsNotifyRLS
= NULL
;
280 if (storePrivate
->rlsNotifyPort
!= NULL
) {
281 /* invalidate port */
282 __MACH_PORT_DEBUG((storePrivate
->rlsNotifyPort
!= NULL
),
283 "*** rlsCancel (before invalidating CFMachPort)",
284 CFMachPortGetPort(storePrivate
->rlsNotifyPort
));
285 CFMachPortInvalidate(storePrivate
->rlsNotifyPort
);
286 CFRelease(storePrivate
->rlsNotifyPort
);
287 storePrivate
->rlsNotifyPort
= NULL
;
290 if (storePrivate
->server
!= MACH_PORT_NULL
) {
291 status
= notifycancel(storePrivate
->server
, (int *)&sc_status
);
293 (void) __SCDynamicStoreCheckRetryAndHandleError(store
,
296 "rlsCancel notifycancel()");
298 if (status
!= KERN_SUCCESS
) {
309 rlsPerform(void *info
)
311 CFArrayRef changedKeys
;
313 void (*context_release
)(const void *);
314 SCDynamicStoreCallBack rlsFunction
;
315 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
316 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
319 SCLog(_sc_verbose
, LOG_DEBUG
, CFSTR(" executing notification function"));
322 changedKeys
= SCDynamicStoreCopyNotifiedKeys(store
);
323 if (storePrivate
->disconnectForceCallBack
) {
324 storePrivate
->disconnectForceCallBack
= FALSE
;
325 if (changedKeys
== NULL
) {
326 changedKeys
= CFArrayCreate(NULL
, NULL
, 0, &kCFTypeArrayCallBacks
);
329 if (changedKeys
== NULL
) {
330 /* if no changes or something happened to the server */
332 } else if (CFArrayGetCount(changedKeys
) == 0) {
337 rlsFunction
= storePrivate
->rlsFunction
;
339 if (storePrivate
->rlsContext
.retain
!= NULL
) {
340 context_info
= (void *)storePrivate
->rlsContext
.retain(storePrivate
->rlsContext
.info
);
341 context_release
= storePrivate
->rlsContext
.release
;
343 context_info
= storePrivate
->rlsContext
.info
;
344 context_release
= NULL
;
346 (*rlsFunction
)(store
, changedKeys
, context_info
);
347 if (context_release
) {
348 context_release(context_info
);
353 CFRelease(changedKeys
);
359 rlsRetain(CFTypeRef cf
)
361 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
362 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
364 switch (storePrivate
->notifyStatus
) {
365 case NotifierNotRegistered
:
366 /* mark RLS active */
367 storePrivate
->notifyStatus
= Using_NotifierInformViaRunLoop
;
368 /* keep a reference to the store */
371 case Using_NotifierInformViaRunLoop
:
374 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsRetain() error: notifyStatus=%d"), storePrivate
->notifyStatus
);
383 rlsRelease(CFTypeRef cf
)
385 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
386 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
388 switch (storePrivate
->notifyStatus
) {
389 case NotifierNotRegistered
:
391 case Using_NotifierInformViaRunLoop
:
392 /* mark RLS inactive */
393 storePrivate
->notifyStatus
= NotifierNotRegistered
;
394 storePrivate
->rls
= NULL
;
396 /* release our reference to the store */
400 SCLog(TRUE
, LOG_ERR
, CFSTR("rlsRelease() error: notifyStatus=%d"), storePrivate
->notifyStatus
);
409 rlsCopyDescription(const void *info
)
411 CFMutableStringRef result
;
412 SCDynamicStoreRef store
= (SCDynamicStoreRef
)info
;
413 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
415 result
= CFStringCreateMutable(NULL
, 0);
416 CFStringAppendFormat(result
, NULL
, CFSTR("<SCDynamicStore RLS> {"));
417 CFStringAppendFormat(result
, NULL
, CFSTR("store = %p"), store
);
418 if (storePrivate
->notifyStatus
== Using_NotifierInformViaRunLoop
) {
419 CFStringRef description
= NULL
;
421 CFStringAppendFormat(result
, NULL
, CFSTR(", callout = %p"), storePrivate
->rlsFunction
);
423 if ((storePrivate
->rlsContext
.info
!= NULL
) && (storePrivate
->rlsContext
.copyDescription
!= NULL
)) {
424 description
= (*storePrivate
->rlsContext
.copyDescription
)(storePrivate
->rlsContext
.info
);
426 if (description
== NULL
) {
427 description
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("<SCDynamicStore context %p>"), storePrivate
->rlsContext
.info
);
429 if (description
== NULL
) {
430 description
= CFRetain(CFSTR("<no description>"));
432 CFStringAppendFormat(result
, NULL
, CFSTR(", context = %@"), description
);
433 CFRelease(description
);
435 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
442 SCDynamicStoreCreateRunLoopSource(CFAllocatorRef allocator
,
443 SCDynamicStoreRef store
,
446 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
449 /* sorry, you must provide a session */
450 _SCErrorSet(kSCStatusNoStoreSession
);
454 if (storePrivate
->server
== MACH_PORT_NULL
) {
455 /* sorry, you must have an open session to play */
456 _SCErrorSet(kSCStatusNoStoreServer
);
460 switch (storePrivate
->notifyStatus
) {
461 case NotifierNotRegistered
:
462 case Using_NotifierInformViaRunLoop
:
463 /* OK to enable runloop notification */
466 /* sorry, you can only have one notification registered at once */
467 _SCErrorSet(kSCStatusNotifierActive
);
471 if (storePrivate
->rls
!= NULL
) {
472 CFRetain(storePrivate
->rls
);
474 CFRunLoopSourceContext context
= { 0 // version
475 , (void *)store
// info
476 , rlsRetain
// retain
477 , rlsRelease
// release
478 , rlsCopyDescription
// copyDescription
481 , rlsSchedule
// schedule
482 , rlsCancel
// cancel
483 , rlsPerform
// perform
486 storePrivate
->rls
= CFRunLoopSourceCreate(allocator
, order
, &context
);
487 if (storePrivate
->rls
== NULL
) {
488 _SCErrorSet(kSCStatusFailed
);
492 return storePrivate
->rls
;
497 SCDynamicStoreSetDispatchQueue(SCDynamicStoreRef store
, dispatch_queue_t queue
)
499 dispatch_group_t drainGroup
= NULL
;
500 dispatch_queue_t drainQueue
= NULL
;
503 dispatch_source_t source
;
504 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
507 // sorry, you must provide a session
508 _SCErrorSet(kSCStatusNoStoreSession
);
513 if (storePrivate
->dispatchQueue
== NULL
) {
514 _SCErrorSet(kSCStatusInvalidArgument
);
522 if (storePrivate
->server
== MACH_PORT_NULL
) {
523 // sorry, you must have an open session to play
524 _SCErrorSet(kSCStatusNoStoreServer
);
528 if ((storePrivate
->dispatchQueue
!= NULL
) || (storePrivate
->rls
!= NULL
)) {
529 _SCErrorSet(kSCStatusInvalidArgument
);
533 if (storePrivate
->notifyStatus
!= NotifierNotRegistered
) {
534 // sorry, you can only have one notification registered at once...
535 _SCErrorSet(kSCStatusNotifierActive
);
540 * mark our using of the SCDynamicStore notifications, create and schedule
541 * the notification port (storePrivate->rlsNotifyPort), and a bunch of other
544 storePrivate
->notifyStatus
= Using_NotifierInformViaDispatch
;
545 rlsSchedule((void*)store
, NULL
, NULL
);
546 if (storePrivate
->rlsNotifyPort
== NULL
) {
547 /* if we could not schedule the notification */
548 _SCErrorSet(kSCStatusFailed
);
552 // retain the dispatch queue
553 storePrivate
->dispatchQueue
= queue
;
554 dispatch_retain(storePrivate
->dispatchQueue
);
557 // We've taken a reference to the callers dispatch_queue and we
558 // want to hold on to that reference until we've processed any/all
559 // notifications. To facilitate this we create a group, dispatch
560 // any notification blocks to via that group, and when the caller
561 // has told us to stop the notifications (unschedule) we wait for
562 // the group to empty and use the group's finalizer to release
563 // our reference to the SCDynamicStore.
565 storePrivate
->dispatchGroup
= dispatch_group_create();
567 dispatch_set_context(storePrivate
->dispatchGroup
, (void *)store
);
568 dispatch_set_finalizer_f(storePrivate
->dispatchGroup
, (dispatch_function_t
)CFRelease
);
570 // create a dispatch source for the mach notifications
571 mp
= CFMachPortGetPort(storePrivate
->rlsNotifyPort
);
572 source
= dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV
, mp
, 0, queue
);
573 if (source
== NULL
) {
574 SCLog(TRUE
, LOG_ERR
, CFSTR("SCDynamicStore dispatch_source_create() failed"));
575 _SCErrorSet(kSCStatusFailed
);
579 dispatch_source_set_event_handler(source
, ^{
583 u_int8_t buf
[sizeof(mach_msg_empty_t
) + MAX_TRAILER_SIZE
];
584 mach_msg_empty_rcv_t msg
;
585 mach_no_senders_notification_t no_senders
;
588 kr
= mach_msg(¬ify_msg
.msg
.header
, // msg
589 MACH_RCV_MSG
, // options
591 sizeof(notify_msg
), // rcv_size
593 MACH_MSG_TIMEOUT_NONE
, // timeout
594 MACH_PORT_NULL
); // notify
595 if (kr
!= KERN_SUCCESS
) {
597 CFSTR("SCDynamicStore notification handler, kr=0x%x"),
602 msgid
= notify_msg
.msg
.header
.msgh_id
;
605 dispatch_group_async(storePrivate
->dispatchGroup
, storePrivate
->dispatchQueue
, ^{
606 if (msgid
== MACH_NOTIFY_NO_SENDERS
) {
607 // re-establish notification and inform the client
608 (void)__SCDynamicStoreReconnectNotifications(store
);
610 rlsPerform(storePrivate
);
615 dispatch_source_set_cancel_handler(source
, ^{
616 dispatch_release(source
);
619 storePrivate
->dispatchSource
= source
;
620 dispatch_resume(source
);
628 if (storePrivate
->dispatchSource
!= NULL
) {
629 dispatch_source_cancel(storePrivate
->dispatchSource
);
630 storePrivate
->dispatchSource
= NULL
;
632 drainGroup
= storePrivate
->dispatchGroup
;
633 storePrivate
->dispatchGroup
= NULL
;
634 drainQueue
= storePrivate
->dispatchQueue
;
635 storePrivate
->dispatchQueue
= NULL
;
637 rlsCancel((void*)store
, NULL
, NULL
);
639 if (drainGroup
!= NULL
) {
640 dispatch_group_notify(drainGroup
, drainQueue
, ^{
641 // release group/queue references
642 dispatch_release(drainQueue
);
643 dispatch_release(drainGroup
); // releases our store reference
647 storePrivate
->notifyStatus
= NotifierNotRegistered
;