2 * Copyright (c) 2000-2006, 2008-2020 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 24, 2000 Allan Nathanson <ajn@apple.com>
34 #include <TargetConditionals.h>
37 #include <os/state_private.h>
38 #include <sys/types.h>
39 #include <mach/mach.h>
40 #include <mach/mach_error.h>
41 #include <servers/bootstrap.h>
42 #include <bootstrap_priv.h>
44 #include "SCDynamicStoreInternal.h"
45 #include "config.h" /* MiG generated file */
48 #define N_SESSIONS_WARN_DEFAULT 50 // complain if SCDynamicStore session count exceeds this [soft-]limit
49 #define N_SESSIONS_WARN_MAX 5000 // stop complaining when # sessions exceeds this [hard-]limit
51 static pthread_mutex_t _sc_lock
= PTHREAD_MUTEX_INITIALIZER
;
52 static mach_port_t _sc_server
= MACH_PORT_NULL
;
53 static CFIndex _sc_store_advise
= N_SESSIONS_WARN_DEFAULT
/ 2; // when snapshots should log
54 static CFIndex _sc_store_max
= N_SESSIONS_WARN_DEFAULT
;
55 static CFMutableSetRef _sc_store_sessions
= NULL
; // sync w/storeQueue()
57 static dispatch_queue_t
60 static dispatch_once_t once
;
61 static dispatch_queue_t q
;
63 dispatch_once(&once
, ^{
64 // allocate mapping between [client] sessions and session info
65 _sc_store_sessions
= CFSetCreateMutable(NULL
, 0, NULL
);
67 // and a queue to synchronize access to the mapping
68 q
= dispatch_queue_create("SCDynamicStore/client sessions", NULL
);
74 static const char *notifyType
[] = {
86 #pragma mark SCDynamicStore state handler
90 addSessionReference(const void *value
, void *context
)
92 CFMutableDictionaryRef dict
= (CFMutableDictionaryRef
)context
;
93 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)value
;
95 if (!storePrivate
->serverNullSession
&&
96 (storePrivate
->name
!= NULL
)) {
98 CFMutableStringRef key
;
102 // create [key] signature
103 key
= CFStringCreateMutableCopy(NULL
, 0, storePrivate
->name
);
104 n
= (storePrivate
->keys
!= NULL
) ? CFArrayGetCount(storePrivate
->keys
) : 0;
106 CFStringAppendFormat(key
, NULL
, CFSTR(":k[0/%ld]=%@"),
108 CFArrayGetValueAtIndex(storePrivate
->keys
, 0));
110 n
= (storePrivate
->patterns
!= NULL
) ? CFArrayGetCount(storePrivate
->patterns
) : 0;
112 CFStringAppendFormat(key
, NULL
, CFSTR(":p[0/%ld]=%@"),
114 CFArrayGetValueAtIndex(storePrivate
->patterns
, 0));
118 if (!CFDictionaryGetValueIfPresent(dict
,
120 (const void **)&num
) ||
121 !CFNumberGetValue(num
, kCFNumberIntType
, &cnt
)) {
126 num
= CFNumberCreate(NULL
, kCFNumberIntType
, &cnt
);
127 CFDictionarySetValue(dict
, key
, num
); // do we want to include name+keys[0]/patterns[0] ?
140 os_state_block_t state_block
;
142 state_block
= ^os_state_data_t(os_state_hints_t hints
) {
143 #pragma unused(hints)
144 CFDataRef data
= NULL
;
145 CFMutableDictionaryRef dict
;
148 os_state_data_t state_data
;
149 size_t state_data_size
;
152 n
= CFSetGetCount(_sc_store_sessions
);
153 if ((_sc_store_max
<= 0) || (n
== 0) || (n
< _sc_store_advise
)) {
157 dict
= CFDictionaryCreateMutable(NULL
,
159 &kCFTypeDictionaryKeyCallBacks
,
160 &kCFTypeDictionaryValueCallBacks
);
161 CFSetApplyFunction(_sc_store_sessions
, addSessionReference
, dict
);
162 if (CFDictionaryGetCount(dict
) == 0) {
166 ok
= _SCSerialize(dict
, &data
, NULL
, NULL
);
169 state_len
= (ok
&& (data
!= NULL
)) ? CFDataGetLength(data
) : 0;
170 state_data_size
= OS_STATE_DATA_SIZE_NEEDED(state_len
);
171 if (state_data_size
> MAX_STATEDUMP_SIZE
) {
172 SC_log(LOG_ERR
, "SCDynamicStore/client sessions : state data too large (%zd > %zd)",
174 (size_t)MAX_STATEDUMP_SIZE
);
175 if (data
!= NULL
) CFRelease(data
);
179 state_data
= calloc(1, state_data_size
);
180 if (state_data
== NULL
) {
181 SC_log(LOG_ERR
, "SCDynamicStore/client sessions: could not allocate state data");
182 if (data
!= NULL
) CFRelease(data
);
186 state_data
->osd_type
= OS_STATE_DATA_SERIALIZED_NSCF_OBJECT
;
187 state_data
->osd_data_size
= (uint32_t)state_len
;
188 strlcpy(state_data
->osd_title
, "SCDynamicStore/client sessions", sizeof(state_data
->osd_title
));
190 memcpy(state_data
->osd_data
, CFDataGetBytePtr(data
), state_len
);
192 if (data
!= NULL
) CFRelease(data
);
197 (void) os_state_add_handler(storeQueue(), state_block
);
203 #pragma mark SCDynamicStore APIs
207 _SCDynamicStoreSetSessionWatchLimit(unsigned int limit
)
209 _sc_store_max
= limit
;
210 _sc_store_advise
= limit
;
215 __private_extern__ os_log_t
216 __log_SCDynamicStore(void)
218 static os_log_t log
= NULL
;
221 log
= os_log_create("com.apple.SystemConfiguration", "SCDynamicStore");
229 __SCDynamicStoreCopyDescription(CFTypeRef cf
) {
230 CFAllocatorRef allocator
= CFGetAllocator(cf
);
231 CFMutableStringRef result
;
232 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)cf
;
234 result
= CFStringCreateMutable(allocator
, 0);
235 CFStringAppendFormat(result
, NULL
, CFSTR("<SCDynamicStore %p [%p]> {"), cf
, allocator
);
236 if (storePrivate
->server
!= MACH_PORT_NULL
) {
237 CFStringAppendFormat(result
, NULL
, CFSTR("server port = 0x%x"), storePrivate
->server
);
239 CFStringAppendFormat(result
, NULL
, CFSTR("server not (no longer) available"));
241 if (storePrivate
->disconnectFunction
!= NULL
) {
242 CFStringAppendFormat(result
, NULL
, CFSTR(", disconnect = %p"), storePrivate
->disconnectFunction
);
244 switch (storePrivate
->notifyStatus
) {
245 case Using_NotifierWait
:
246 CFStringAppendFormat(result
, NULL
, CFSTR(", waiting for a notification"));
248 case Using_NotifierInformViaMachPort
:
249 CFStringAppendFormat(result
, NULL
, CFSTR(", mach port notifications"));
251 case Using_NotifierInformViaFD
:
252 CFStringAppendFormat(result
, NULL
, CFSTR(", FD notifications"));
254 case Using_NotifierInformViaRunLoop
:
255 case Using_NotifierInformViaDispatch
:
256 if (storePrivate
->notifyStatus
== Using_NotifierInformViaRunLoop
) {
257 CFStringAppendFormat(result
, NULL
, CFSTR(", runloop notifications"));
258 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->rlsFunction
);
259 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->rlsContext
.info
);
260 CFStringAppendFormat(result
, NULL
, CFSTR(", rls = %p"), storePrivate
->rls
);
261 CFStringAppendFormat(result
, NULL
, CFSTR(", notify rls = %@" ), storePrivate
->rlsNotifyRLS
);
262 } else if (storePrivate
->notifyStatus
== Using_NotifierInformViaDispatch
) {
263 CFStringAppendFormat(result
, NULL
, CFSTR(", dispatch notifications"));
264 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->rlsFunction
);
265 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->rlsContext
.info
);
266 CFStringAppendFormat(result
, NULL
, CFSTR(", queue = %p"), storePrivate
->dispatchQueue
);
267 CFStringAppendFormat(result
, NULL
, CFSTR(", source = %p"), storePrivate
->dispatchSource
);
269 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
272 CFStringAppendFormat(result
, NULL
, CFSTR(", notification delivery not requested%s"),
273 storePrivate
->rlsFunction
? " (yet)" : "");
276 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
283 __SCDynamicStoreDeallocate(CFTypeRef cf
)
286 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
287 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
289 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED
, &oldThreadState
);
291 dispatch_sync(storeQueue(), ^{
292 // remove session tracking
293 CFSetRemoveValue(_sc_store_sessions
, storePrivate
);
296 /* Remove/cancel any outstanding notification requests. */
297 (void) SCDynamicStoreNotifyCancel(store
);
299 if (storePrivate
->server
!= MACH_PORT_NULL
) {
301 * Remove our send right to the SCDynamicStore server.
303 * In the case of a "real" session this will result in our
304 * session being closed.
306 * In the case of a "NULL" session, we just remove the
307 * the send right reference we are holding.
309 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreDeallocate", storePrivate
->server
);
310 (void) mach_port_deallocate(mach_task_self(), storePrivate
->server
);
311 storePrivate
->server
= MACH_PORT_NULL
;
314 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, &oldThreadState
);
315 pthread_testcancel();
317 /* release any callback context info */
318 if (storePrivate
->rlsContext
.release
!= NULL
) {
319 (*storePrivate
->rlsContext
.release
)(storePrivate
->rlsContext
.info
);
322 /* release any keys being watched */
323 if (storePrivate
->keys
!= NULL
) CFRelease(storePrivate
->keys
);
324 if (storePrivate
->patterns
!= NULL
) CFRelease(storePrivate
->patterns
);
326 /* release any client info */
327 if (storePrivate
->name
!= NULL
) CFRelease(storePrivate
->name
);
328 if (storePrivate
->options
!= NULL
) CFRelease(storePrivate
->options
);
330 /* release any cached content */
331 if (storePrivate
->cache_active
) {
332 _SCDynamicStoreCacheClose(store
);
339 static CFTypeID __kSCDynamicStoreTypeID
= _kCFRuntimeNotATypeID
;
342 static const CFRuntimeClass __SCDynamicStoreClass
= {
344 "SCDynamicStore", // className
347 __SCDynamicStoreDeallocate
, // dealloc
350 NULL
, // copyFormattingDesc
351 __SCDynamicStoreCopyDescription
// copyDebugDesc
358 /* the process has forked (and we are the child process) */
360 _sc_server
= MACH_PORT_NULL
;
365 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
368 __SCDynamicStoreInitialize(void)
370 /* register with CoreFoundation */
371 __kSCDynamicStoreTypeID
= _CFRuntimeRegisterClass(&__SCDynamicStoreClass
);
373 /* add handler to cleanup after fork() */
374 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
383 __SCDynamicStoreServerPort(SCDynamicStorePrivateRef storePrivate
, kern_return_t
*status
)
385 #pragma unused(storePrivate)
386 mach_port_t server
= MACH_PORT_NULL
;
389 server_name
= getenv("SCD_SERVER");
393 * only allow the SCDynamicStore server bootstrap name to be changed with
394 * DEBUG builds. For RELEASE builds, assume that no server is available.
396 if (server_name
!= NULL
) {
397 *status
= BOOTSTRAP_UNKNOWN_SERVICE
;
398 return MACH_PORT_NULL
;
403 if (server_name
== NULL
) {
404 server_name
= SCD_SERVER
;
407 #if defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
408 *status
= bootstrap_look_up2(bootstrap_port
,
412 BOOTSTRAP_PRIVILEGED_SERVER
);
413 #else // defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
414 *status
= bootstrap_look_up(bootstrap_port
, server_name
, &server
);
415 #endif // defined(BOOTSTRAP_PRIVILEGED_SERVER) && !TARGET_OS_SIMULATOR
418 case BOOTSTRAP_SUCCESS
:
419 /* service currently registered, "a good thing" (tm) */
421 case BOOTSTRAP_NOT_PRIVILEGED
:
422 /* the service is not privileged */
424 case BOOTSTRAP_UNKNOWN_SERVICE
:
425 /* service not currently registered, try again later */
429 SC_log(LOG_INFO
, "bootstrap_look_up() failed: status=%s (%d)",
430 bootstrap_strerror(*status
),
436 return MACH_PORT_NULL
;
441 logSessionReference(const void *key
, const void *value
, void *context
)
443 #pragma unused(context)
444 CFNumberRef cnt
= (CFNumberRef
)value
;
445 CFStringRef name
= (CFStringRef
)key
;
447 SC_log(LOG_ERR
, " %@ sessions w/name = \"%@\"", cnt
, name
);
452 SCDynamicStorePrivateRef
453 __SCDynamicStoreCreatePrivate(CFAllocatorRef allocator
,
454 const CFStringRef name
,
455 SCDynamicStoreCallBack callout
,
456 SCDynamicStoreContext
*context
)
459 SCDynamicStorePrivateRef storePrivate
;
460 __block Boolean tooManySessions
= FALSE
;
462 /* initialize runtime */
463 pthread_once(&initialized
, __SCDynamicStoreInitialize
);
465 /* allocate session */
466 size
= sizeof(SCDynamicStorePrivate
) - sizeof(CFRuntimeBase
);
467 storePrivate
= (SCDynamicStorePrivateRef
)_CFRuntimeCreateInstance(allocator
,
468 __kSCDynamicStoreTypeID
,
471 if (storePrivate
== NULL
) {
472 _SCErrorSet(kSCStatusFailed
);
476 /* initialize non-zero/NULL members */
478 /* client side of the "configd" session */
479 storePrivate
->name
= (name
!= NULL
) ? CFRetain(name
) : NULL
;
481 /* Notification status */
482 storePrivate
->notifyStatus
= NotifierNotRegistered
;
484 /* "client" information associated with SCDynamicStoreCreateRunLoopSource() */
485 storePrivate
->rlsFunction
= callout
;
486 if (context
!= NULL
) {
487 memcpy(&storePrivate
->rlsContext
, context
, sizeof(SCDynamicStoreContext
));
488 if (context
->retain
!= NULL
) {
489 storePrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
493 /* "server" information associated with SCDynamicStoreNotifyFileDescriptor(); */
494 storePrivate
->notifyFile
= -1;
496 /* watch for excessive SCDynamicStore usage */
497 dispatch_sync(storeQueue(), ^{
501 CFSetAddValue(_sc_store_sessions
, storePrivate
);
502 n
= CFSetGetCount(_sc_store_sessions
);
503 if (n
> _sc_store_max
) {
504 if (_sc_store_max
> 0) {
505 CFMutableDictionaryRef dict
;
507 dict
= CFDictionaryCreateMutable(NULL
,
509 &kCFTypeDictionaryKeyCallBacks
,
510 &kCFTypeDictionaryValueCallBacks
);
511 CFSetApplyFunction(_sc_store_sessions
, addSessionReference
, dict
);
513 "SCDynamicStoreCreate(): number of SCDynamicStore sessions %sexceeds %ld",
514 (n
!= N_SESSIONS_WARN_DEFAULT
) ? "now " : "",
516 CFDictionaryApplyFunction(dict
, logSessionReference
, NULL
);
519 // yes, we should complain
520 tooManySessions
= TRUE
;
522 // bump the threshold before we complain again
523 _sc_store_max
= (_sc_store_max
< N_SESSIONS_WARN_MAX
) ? (_sc_store_max
* 2) : 0;
528 if (tooManySessions
) {
529 _SC_crash_once("Excessive number of SCDynamicStore sessions", NULL
, NULL
);
537 updateServerPort(SCDynamicStorePrivateRef storePrivate
, mach_port_t
*server
, int *sc_status_p
)
539 mach_port_t old_port
;
541 pthread_mutex_lock(&_sc_lock
);
542 old_port
= _sc_server
;
543 if (_sc_server
!= MACH_PORT_NULL
) {
544 if (*server
== _sc_server
) {
545 // if the server we tried returned the error, save the old port,
546 // [re-]lookup the name to the server, and deallocate the original
547 // send [or dead name] right
548 _sc_server
= __SCDynamicStoreServerPort(storePrivate
, sc_status_p
);
549 (void)mach_port_deallocate(mach_task_self(), old_port
);
551 // another thread has refreshed the [main] SCDynamicStore server port
554 _sc_server
= __SCDynamicStoreServerPort(storePrivate
, sc_status_p
);
557 *server
= _sc_server
;
558 pthread_mutex_unlock(&_sc_lock
);
561 SC_log(LOG_DEBUG
, "updateServerPort (%@): 0x%x (%d) --> 0x%x (%d)",
562 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"),
572 __SCDynamicStoreAddSession(SCDynamicStorePrivateRef storePrivate
)
574 kern_return_t kr
= KERN_SUCCESS
;
575 CFDataRef myName
; /* serialized name */
578 CFDataRef myOptions
= NULL
; /* serialized options */
579 xmlData_t myOptionsRef
= NULL
;
580 CFIndex myOptionsLen
= 0;
581 int sc_status
= kSCStatusFailed
;
584 if (!_SCSerializeString(storePrivate
->name
, &myName
, (void **)&myNameRef
, &myNameLen
)) {
588 /* serialize the options */
589 if (storePrivate
->options
!= NULL
) {
590 if (!_SCSerialize(storePrivate
->options
, &myOptions
, (void **)&myOptionsRef
, &myOptionsLen
)) {
596 /* open a new session with the server */
597 server
= MACH_PORT_NULL
;
600 updateServerPort(storePrivate
, &server
, &sc_status
);
603 while (server
!= MACH_PORT_NULL
) {
604 // if SCDynamicStore server available
606 if (!storePrivate
->serverNullSession
) {
607 // if SCDynamicStore session
608 kr
= configopen(server
,
610 (mach_msg_type_number_t
)myNameLen
,
612 (mach_msg_type_number_t
)myOptionsLen
,
613 &storePrivate
->server
,
617 if (storePrivate
->server
== MACH_PORT_NULL
) {
618 // use the [main] SCDynamicStore server port
619 kr
= mach_port_mod_refs(mach_task_self(), server
, MACH_PORT_RIGHT_SEND
, +1);
620 if (kr
== KERN_SUCCESS
) {
621 storePrivate
->server
= server
;
622 sc_status
= kSCStatusOK
;
624 if (kr
== KERN_INVALID_RIGHT
) {
625 // We can get KERN_INVALID_RIGHT if the server dies and we try to
626 // add a send right to a stale (now dead) port name
627 kr
= MACH_SEND_INVALID_DEST
;
629 storePrivate
->server
= MACH_PORT_NULL
;
632 // if the server port we used returned an error
633 storePrivate
->server
= MACH_PORT_NULL
;
634 kr
= MACH_SEND_INVALID_DEST
;
638 if (kr
== KERN_SUCCESS
) {
642 // our [cached] server port is not valid
643 if ((kr
!= MACH_SEND_INVALID_DEST
) && (kr
!= MIG_SERVER_DIED
)) {
644 // if we got an unexpected error, don't retry
650 updateServerPort(storePrivate
, &server
, &sc_status
);
652 __MACH_PORT_DEBUG(TRUE
, "*** SCDynamicStoreAddSession", storePrivate
->server
);
656 if (myOptions
!= NULL
) CFRelease(myOptions
);
663 case BOOTSTRAP_UNKNOWN_SERVICE
:
664 SC_log((kr
== KERN_SUCCESS
) ? LOG_INFO
: LOG_ERR
, "SCDynamicStore server not available");
665 sc_status
= kSCStatusNoStoreServer
;
668 SC_log((kr
== KERN_SUCCESS
) ? LOG_INFO
: LOG_ERR
, "configopen() failed: %d: %s",
670 SCErrorString(sc_status
));
674 _SCErrorSet(sc_status
);
681 __SCDynamicStoreNullSession(void)
683 SCDynamicStorePrivateRef storePrivate
;
685 __SCThreadSpecificDataRef tsd
;
687 tsd
= __SCGetThreadSpecificData();
688 if (tsd
->_sc_store
== NULL
) {
689 storePrivate
= __SCDynamicStoreCreatePrivate(NULL
,
690 CFSTR("NULL session"),
693 assert(storePrivate
!= NULL
);
694 storePrivate
->serverNullSession
= TRUE
;
695 tsd
->_sc_store
= (SCDynamicStoreRef
)storePrivate
;
698 storePrivate
= (SCDynamicStorePrivateRef
)tsd
->_sc_store
;
699 if (storePrivate
->server
== MACH_PORT_NULL
) {
700 ok
= __SCDynamicStoreAddSession(storePrivate
);
703 return ok
? tsd
->_sc_store
: NULL
;
708 __SCDynamicStoreReconnect(SCDynamicStoreRef store
)
711 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
713 ok
= __SCDynamicStoreAddSession(storePrivate
);
720 __SCDynamicStoreCheckRetryAndHandleError(SCDynamicStoreRef store
,
721 kern_return_t status
,
725 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
727 if (status
== KERN_SUCCESS
) {
733 case MACH_SEND_INVALID_DEST
:
734 case MACH_SEND_INVALID_RIGHT
:
735 case MIG_SERVER_DIED
:
737 * the server's gone, remove the session's send (or dead name) right
740 SC_log(LOG_DEBUG
, "__SCDynamicStoreCheckRetryAndHandleError(%s): %@: 0x%x (%d) --> 0x%x (%d)",
742 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"),
743 storePrivate
->server
, storePrivate
->server
,
744 MACH_PORT_NULL
, MACH_PORT_NULL
);
746 (void) mach_port_deallocate(mach_task_self(), storePrivate
->server
);
747 storePrivate
->server
= MACH_PORT_NULL
;
750 if (__SCDynamicStoreReconnect(store
)) {
760 * an unexpected error, leave the [session] port alone
763 SC_log(LOG_DEBUG
, "__SCDynamicStoreCheckRetryAndHandleError(%s): %@: unexpected status=%s (0x%x)",
765 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"),
766 mach_error_string(status
),
770 SC_log(LOG_NOTICE
, "%s: %s", log_str
, mach_error_string(status
));
771 storePrivate
->server
= MACH_PORT_NULL
;
781 pushDisconnect(SCDynamicStoreRef store
)
784 void (*context_release
)(const void *);
785 SCDynamicStoreDisconnectCallBack disconnectFunction
;
786 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
788 disconnectFunction
= storePrivate
->disconnectFunction
;
789 if (disconnectFunction
== NULL
) {
790 // if no reconnect callout, push empty notification
791 storePrivate
->disconnectForceCallBack
= TRUE
;
795 if (storePrivate
->rlsContext
.retain
!= NULL
) {
796 context_info
= (void *)storePrivate
->rlsContext
.retain(storePrivate
->rlsContext
.info
);
797 context_release
= storePrivate
->rlsContext
.release
;
799 context_info
= storePrivate
->rlsContext
.info
;
800 context_release
= NULL
;
802 SC_log(LOG_DEBUG
, "exec SCDynamicStore disconnect callout");
803 (*disconnectFunction
)(store
, context_info
);
804 if (context_release
) {
805 context_release(context_info
);
814 __SCDynamicStoreReconnectNotifications(SCDynamicStoreRef store
)
816 dispatch_queue_t dispatchQueue
= NULL
;
817 __SCDynamicStoreNotificationStatus notifyStatus
;
819 CFArrayRef rlList
= NULL
;
820 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
823 SC_log(LOG_DEBUG
, "SCDynamicStore: reconnect notifications (%@)",
824 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"));
827 // save old SCDynamicStore [notification] state
828 notifyStatus
= storePrivate
->notifyStatus
;
830 // before tearing down our [old] notifications, make sure we've
831 // retained any information that will be lost when we cancel the
832 // current no-longer-valid handler
833 switch (notifyStatus
) {
834 case Using_NotifierInformViaRunLoop
:
835 if (storePrivate
->rlList
!= NULL
) {
836 rlList
= CFArrayCreateCopy(NULL
, storePrivate
->rlList
);
839 case Using_NotifierInformViaDispatch
:
840 dispatchQueue
= storePrivate
->dispatchQueue
;
841 if (dispatchQueue
!= NULL
) dispatch_retain(dispatchQueue
);
847 // cancel [old] notifications
848 if (!SCDynamicStoreNotifyCancel(store
)) {
849 // if we could not cancel / reconnect
850 SC_log(LOG_NOTICE
, "SCDynamicStoreNotifyCancel() failed: %s", SCErrorString(SCError()));
853 // set notification keys & patterns
854 if ((storePrivate
->keys
!= NULL
) || (storePrivate
->patterns
)) {
855 ok
= SCDynamicStoreSetNotificationKeys(store
,
857 storePrivate
->patterns
);
859 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
860 SC_log(LOG_NOTICE
, "SCDynamicStoreSetNotificationKeys() failed");
866 switch (notifyStatus
) {
867 case Using_NotifierInformViaRunLoop
: {
870 CFRunLoopSourceRef rls
;
873 SC_log(LOG_DEBUG
, "SCDynamicStore: reconnecting w/CFRunLoop (%@)",
874 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"));
877 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
879 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
880 SC_log(LOG_NOTICE
, "SCDynamicStoreCreateRunLoopSource() failed");
886 n
= (rlList
!= NULL
) ? CFArrayGetCount(rlList
) : 0;
887 for (i
= 0; i
< n
; i
+= 3) {
888 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
889 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
891 CFRunLoopAddSource(rl
, rls
, rlMode
);
897 case Using_NotifierInformViaDispatch
:
900 SC_log(LOG_DEBUG
, "SCDynamicStore: reconnecting w/dispatch queue (%@)",
901 (storePrivate
->name
!= NULL
) ? storePrivate
->name
: CFSTR("?"));
904 ok
= SCDynamicStoreSetDispatchQueue(store
, dispatchQueue
);
906 if (SCError() != BOOTSTRAP_UNKNOWN_SERVICE
) {
907 SC_log(LOG_NOTICE
, "SCDynamicStoreSetDispatchQueue() failed");
914 _SCErrorSet(kSCStatusFailed
);
922 switch (notifyStatus
) {
923 case Using_NotifierInformViaRunLoop
:
924 if (rlList
!= NULL
) CFRelease(rlList
);
926 case Using_NotifierInformViaDispatch
:
927 if (dispatchQueue
!= NULL
) dispatch_release(dispatchQueue
);
934 SC_log(LOG_NOTICE
, "SCDynamicStore server %s, notification (%s) not restored",
935 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed",
936 notifyType
[notifyStatus
]);
940 pushDisconnect(store
);
946 const CFStringRef kSCDynamicStoreUseSessionKeys
= CFSTR("UseSessionKeys"); /* CFBoolean */
951 SCDynamicStoreCreateWithOptions(CFAllocatorRef allocator
,
953 CFDictionaryRef storeOptions
,
954 SCDynamicStoreCallBack callout
,
955 SCDynamicStoreContext
*context
)
958 SCDynamicStorePrivateRef storePrivate
;
960 // allocate and initialize a new session
961 storePrivate
= __SCDynamicStoreCreatePrivate(allocator
, NULL
, callout
, context
);
962 if (storePrivate
== NULL
) {
967 storePrivate
->name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:%@"), _SC_getApplicationBundleID(), name
);
971 if (storeOptions
!= NULL
) {
972 storePrivate
->options
= CFRetain(storeOptions
);
975 // establish SCDynamicStore session
976 ok
= __SCDynamicStoreAddSession(storePrivate
);
978 CFRelease(storePrivate
);
982 return (SCDynamicStoreRef
)storePrivate
;
987 SCDynamicStoreCreate(CFAllocatorRef allocator
,
989 SCDynamicStoreCallBack callout
,
990 SCDynamicStoreContext
*context
)
992 return SCDynamicStoreCreateWithOptions(allocator
, name
, NULL
, callout
, context
);
997 SCDynamicStoreGetTypeID(void) {
998 pthread_once(&initialized
, __SCDynamicStoreInitialize
); /* initialize runtime */
999 return __kSCDynamicStoreTypeID
;
1004 SCDynamicStoreSetDisconnectCallBack(SCDynamicStoreRef store
,
1005 SCDynamicStoreDisconnectCallBack callout
)
1007 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
1009 if (store
== NULL
) {
1010 /* sorry, you must provide a session */
1011 _SCErrorSet(kSCStatusNoStoreSession
);
1015 storePrivate
->disconnectFunction
= callout
;