2 * Copyright (c) 2000-2006, 2008-2011 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 <sys/types.h>
38 #include <mach/mach.h>
39 #include <mach/mach_error.h>
40 #include <servers/bootstrap.h>
41 #include <bootstrap_priv.h>
43 #include <SystemConfiguration/SystemConfiguration.h>
44 #include <SystemConfiguration/SCPrivate.h>
45 #include "SCDynamicStoreInternal.h"
46 #include "config.h" /* MiG generated file */
49 static CFStringRef _sc_bundleID
= NULL
;
50 static pthread_mutex_t _sc_lock
= PTHREAD_MUTEX_INITIALIZER
;
51 static mach_port_t _sc_server
= MACH_PORT_NULL
;
54 static const char *notifyType
[] = {
67 __SCDynamicStoreCopyDescription(CFTypeRef cf
) {
68 CFAllocatorRef allocator
= CFGetAllocator(cf
);
69 CFMutableStringRef result
;
70 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)cf
;
72 result
= CFStringCreateMutable(allocator
, 0);
73 CFStringAppendFormat(result
, NULL
, CFSTR("<SCDynamicStore %p [%p]> {"), cf
, allocator
);
74 if (storePrivate
->server
!= MACH_PORT_NULL
) {
75 CFStringAppendFormat(result
, NULL
, CFSTR("server port = %p"), storePrivate
->server
);
77 CFStringAppendFormat(result
, NULL
, CFSTR("server not (no longer) available"));
79 if (storePrivate
->locked
) {
80 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
82 if (storePrivate
->disconnectFunction
!= NULL
) {
83 CFStringAppendFormat(result
, NULL
, CFSTR(", disconnect = %p"), storePrivate
->disconnectFunction
);
85 switch (storePrivate
->notifyStatus
) {
86 case Using_NotifierWait
:
87 CFStringAppendFormat(result
, NULL
, CFSTR(", waiting for a notification"));
89 case Using_NotifierInformViaMachPort
:
90 CFStringAppendFormat(result
, NULL
, CFSTR(", mach port notifications"));
92 case Using_NotifierInformViaFD
:
93 CFStringAppendFormat(result
, NULL
, CFSTR(", FD notifications"));
95 case Using_NotifierInformViaSignal
:
96 CFStringAppendFormat(result
, NULL
, CFSTR(", BSD signal notifications"));
98 case Using_NotifierInformViaRunLoop
:
99 case Using_NotifierInformViaCallback
:
100 if (storePrivate
->notifyStatus
== Using_NotifierInformViaRunLoop
) {
101 CFStringAppendFormat(result
, NULL
, CFSTR(", runloop notifications"));
102 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->rlsFunction
);
103 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->rlsContext
.info
);
104 CFStringAppendFormat(result
, NULL
, CFSTR(", rls = %p"), storePrivate
->rls
);
106 CFStringAppendFormat(result
, NULL
, CFSTR(", mach port/callback notifications"));
107 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->callbackFunction
);
108 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->callbackArgument
);
110 if (storePrivate
->callbackRLS
!= NULL
) {
111 CFStringAppendFormat(result
, NULL
, CFSTR(", notify rls = %@" ), storePrivate
->callbackRLS
);
113 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
116 CFStringAppendFormat(result
, NULL
, CFSTR(", notification delivery not requested%s"),
117 storePrivate
->rlsFunction
? " (yet)" : "");
120 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
127 __SCDynamicStoreDeallocate(CFTypeRef cf
)
131 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
132 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
134 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED
, &oldThreadState
);
136 /* Remove/cancel any outstanding notification requests. */
137 (void) SCDynamicStoreNotifyCancel(store
);
139 if ((storePrivate
->server
!= MACH_PORT_NULL
) && storePrivate
->locked
) {
140 (void) SCDynamicStoreUnlock(store
); /* release the lock */
143 if (storePrivate
->server
!= MACH_PORT_NULL
) {
144 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreDeallocate", storePrivate
->server
);
145 (void) configclose(storePrivate
->server
, (int *)&sc_status
);
146 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreDeallocate (after configclose)", storePrivate
->server
);
149 * the above call to configclose() should result in the SCDynamicStore
150 * server code deallocating it's receive right. That, in turn, should
151 * result in our send becoming a dead name. We could explicitly remove
152 * the dead name right with a call to mach_port_mod_refs() but, to be
153 * sure, we use mach_port_deallocate() since that will get rid of a
154 * send, send_once, or dead name right.
156 (void) mach_port_deallocate(mach_task_self(), storePrivate
->server
);
157 storePrivate
->server
= MACH_PORT_NULL
;
160 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, &oldThreadState
);
161 pthread_testcancel();
163 /* release any callback context info */
164 if (storePrivate
->rlsContext
.release
!= NULL
) {
165 (*storePrivate
->rlsContext
.release
)(storePrivate
->rlsContext
.info
);
168 /* release any keys being watched */
169 if (storePrivate
->keys
!= NULL
) CFRelease(storePrivate
->keys
);
170 if (storePrivate
->patterns
!= NULL
) CFRelease(storePrivate
->patterns
);
172 /* release any client info */
173 if (storePrivate
->name
!= NULL
) CFRelease(storePrivate
->name
);
174 if (storePrivate
->options
!= NULL
) CFRelease(storePrivate
->options
);
180 static CFTypeID __kSCDynamicStoreTypeID
= _kCFRuntimeNotATypeID
;
183 static const CFRuntimeClass __SCDynamicStoreClass
= {
185 "SCDynamicStore", // className
188 __SCDynamicStoreDeallocate
, // dealloc
191 NULL
, // copyFormattingDesc
192 __SCDynamicStoreCopyDescription
// copyDebugDesc
199 /* the process has forked (and we are the child process) */
201 _sc_server
= MACH_PORT_NULL
;
206 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
209 __SCDynamicStoreInitialize(void)
213 /* register with CoreFoundation */
214 __kSCDynamicStoreTypeID
= _CFRuntimeRegisterClass(&__SCDynamicStoreClass
);
216 /* add handler to cleanup after fork() */
217 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
219 /* get the application/executable/bundle name */
220 bundle
= CFBundleGetMainBundle();
221 if (bundle
!= NULL
) {
222 _sc_bundleID
= CFBundleGetIdentifier(bundle
);
223 if (_sc_bundleID
!= NULL
) {
224 CFRetain(_sc_bundleID
);
228 url
= CFBundleCopyExecutableURL(bundle
);
230 _sc_bundleID
= CFURLCopyPath(url
);
235 if (_sc_bundleID
!= NULL
) {
236 if (CFEqual(_sc_bundleID
, CFSTR("/"))) {
237 CFRelease(_sc_bundleID
);
238 _sc_bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("(%d)"), getpid());
248 __SCDynamicStoreServerPort(kern_return_t
*status
)
250 mach_port_t server
= MACH_PORT_NULL
;
253 server_name
= getenv("SCD_SERVER");
255 server_name
= SCD_SERVER
;
258 #ifdef BOOTSTRAP_PRIVILEGED_SERVER
259 *status
= bootstrap_look_up2(bootstrap_port
,
263 BOOTSTRAP_PRIVILEGED_SERVER
);
264 #else // BOOTSTRAP_PRIVILEGED_SERVER
265 *status
= bootstrap_look_up(bootstrap_port
, server_name
, &server
);
266 #endif // BOOTSTRAP_PRIVILEGED_SERVER
269 case BOOTSTRAP_SUCCESS
:
270 /* service currently registered, "a good thing" (tm) */
272 case BOOTSTRAP_NOT_PRIVILEGED
:
273 /* the service is not privileged */
275 case BOOTSTRAP_UNKNOWN_SERVICE
:
276 /* service not currently registered, try again later */
280 SCLog(_sc_verbose
, LOG_DEBUG
,
281 CFSTR("SCDynamicStoreCreate[WithOptions] bootstrap_look_up() failed: status=%s"),
282 bootstrap_strerror(*status
));
287 return MACH_PORT_NULL
;
291 SCDynamicStorePrivateRef
292 __SCDynamicStoreCreatePrivate(CFAllocatorRef allocator
,
293 const CFStringRef name
,
294 SCDynamicStoreCallBack callout
,
295 SCDynamicStoreContext
*context
)
298 SCDynamicStorePrivateRef storePrivate
;
300 /* initialize runtime */
301 pthread_once(&initialized
, __SCDynamicStoreInitialize
);
304 /* allocate session */
305 size
= sizeof(SCDynamicStorePrivate
) - sizeof(CFRuntimeBase
);
306 storePrivate
= (SCDynamicStorePrivateRef
)_CFRuntimeCreateInstance(allocator
,
307 __kSCDynamicStoreTypeID
,
310 if (storePrivate
== NULL
) {
311 _SCErrorSet(kSCStatusFailed
);
315 /* client side of the "configd" session */
316 storePrivate
->name
= NULL
;
317 storePrivate
->options
= NULL
;
319 /* server side of the "configd" session */
320 storePrivate
->server
= MACH_PORT_NULL
;
323 storePrivate
->locked
= FALSE
;
324 storePrivate
->useSessionKeys
= FALSE
;
326 /* Notification status */
327 storePrivate
->notifyStatus
= NotifierNotRegistered
;
329 /* "client" information associated with SCDynamicStoreCreateRunLoopSource() */
330 storePrivate
->rlList
= NULL
;
331 storePrivate
->rls
= NULL
;
332 storePrivate
->rlsFunction
= callout
;
333 storePrivate
->rlsContext
.info
= NULL
;
334 storePrivate
->rlsContext
.retain
= NULL
;
335 storePrivate
->rlsContext
.release
= NULL
;
336 storePrivate
->rlsContext
.copyDescription
= NULL
;
338 bcopy(context
, &storePrivate
->rlsContext
, sizeof(SCDynamicStoreContext
));
339 if (context
->retain
!= NULL
) {
340 storePrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
344 /* "client" information associated with SCDynamicStoreNotifyCallback() */
345 storePrivate
->callbackFunction
= NULL
;
346 storePrivate
->callbackArgument
= NULL
;
347 storePrivate
->callbackPort
= NULL
;
348 storePrivate
->callbackRLS
= NULL
;
350 /* "client" information associated with SCDynamicStoreSetDispatchQueue() */
351 storePrivate
->dispatchQueue
= NULL
;
352 storePrivate
->callbackSource
= NULL
;
353 storePrivate
->callbackQueue
= NULL
;
355 /* "client" information associated with SCDynamicStoreSetDisconnectCallBack() */
356 storePrivate
->disconnectFunction
= NULL
;
357 storePrivate
->disconnectForceCallBack
= FALSE
;
359 /* "server" information associated with SCDynamicStoreSetNotificationKeys() */
360 storePrivate
->keys
= NULL
;
361 storePrivate
->patterns
= NULL
;
363 /* "server" information associated with SCDynamicStoreNotifyMachPort(); */
364 storePrivate
->notifyPort
= MACH_PORT_NULL
;
365 storePrivate
->notifyPortIdentifier
= 0;
367 /* "server" information associated with SCDynamicStoreNotifyFileDescriptor(); */
368 storePrivate
->notifyFile
= -1;
369 storePrivate
->notifyFileIdentifier
= 0;
371 /* "server" information associated with SCDynamicStoreNotifySignal(); */
372 storePrivate
->notifySignal
= 0;
373 storePrivate
->notifySignalTask
= TASK_NULL
;
380 __SCDynamicStoreAddSession(SCDynamicStorePrivateRef storePrivate
)
382 CFDataRef myName
; /* serialized name */
385 CFDataRef myOptions
= NULL
; /* serialized options */
386 xmlData_t myOptionsRef
= NULL
;
387 CFIndex myOptionsLen
= 0;
388 int sc_status
= kSCStatusFailed
;
390 kern_return_t status
= KERN_SUCCESS
;
392 if (!_SCSerializeString(storePrivate
->name
, &myName
, (void **)&myNameRef
, &myNameLen
)) {
396 /* serialize the options */
397 if (storePrivate
->options
!= NULL
) {
398 if (!_SCSerialize(storePrivate
->options
, &myOptions
, (void **)&myOptionsRef
, &myOptionsLen
)) {
404 /* open a new session with the server */
407 if (server
!= MACH_PORT_NULL
) {
408 status
= configopen(server
,
413 &storePrivate
->server
,
415 if (status
== KERN_SUCCESS
) {
419 // our [cached] server port is not valid
420 if ((status
!= MACH_SEND_INVALID_DEST
) && (status
!= MIG_SERVER_DIED
)) {
421 // if we got an unexpected error, don't retry
427 pthread_mutex_lock(&_sc_lock
);
428 if (_sc_server
!= MACH_PORT_NULL
) {
429 if (server
== _sc_server
) {
430 // if the server we tried returned the error
431 (void)mach_port_deallocate(mach_task_self(), _sc_server
);
432 _sc_server
= __SCDynamicStoreServerPort(&sc_status
);
434 // another thread has refreshed the SCDynamicStore server port
437 _sc_server
= __SCDynamicStoreServerPort(&sc_status
);
440 pthread_mutex_unlock(&_sc_lock
);
442 if (server
== MACH_PORT_NULL
) {
443 // if SCDynamicStore server not available
447 __MACH_PORT_DEBUG(TRUE
, "*** SCDynamicStoreAddSession", storePrivate
->server
);
451 if (myOptions
!= NULL
) CFRelease(myOptions
);
458 case BOOTSTRAP_UNKNOWN_SERVICE
:
460 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
461 CFSTR("SCDynamicStore server not available"));
465 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
466 CFSTR("SCDynamicStoreCreateAddSession configopen(): %s"),
467 SCErrorString(sc_status
));
471 _SCErrorSet(sc_status
);
478 __SCDynamicStoreReconnect(SCDynamicStoreRef store
)
481 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
483 ok
= __SCDynamicStoreAddSession(storePrivate
);
489 pushDisconnect(SCDynamicStoreRef store
)
492 void (*context_release
)(const void *);
493 SCDynamicStoreDisconnectCallBack disconnectFunction
;
494 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
496 disconnectFunction
= storePrivate
->disconnectFunction
;
497 if (disconnectFunction
== NULL
) {
498 // if no reconnect callout, push empty notification
499 storePrivate
->disconnectForceCallBack
= TRUE
;
503 if (storePrivate
->rlsContext
.retain
!= NULL
) {
504 context_info
= (void *)storePrivate
->rlsContext
.retain(storePrivate
->rlsContext
.info
);
505 context_release
= storePrivate
->rlsContext
.release
;
507 context_info
= storePrivate
->rlsContext
.info
;
508 context_release
= NULL
;
510 (*disconnectFunction
)(store
, context_info
);
511 if (context_release
) {
512 context_release(context_info
);
521 __SCDynamicStoreReconnectNotifications(SCDynamicStoreRef store
)
523 dispatch_queue_t dispatchQueue
= NULL
;
524 __SCDynamicStoreNotificationStatus notifyStatus
;
526 CFArrayRef rlList
= NULL
;
527 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
529 // save old SCDynamicStore [notification] state
530 notifyStatus
= storePrivate
->notifyStatus
;
532 // before tearing down our [old] notifications, make sure we've
533 // retained any information that will be lost when we cancel the
534 // current no-longer-valid handler
535 switch (notifyStatus
) {
536 case Using_NotifierInformViaRunLoop
:
537 if (storePrivate
->rlList
!= NULL
) {
538 rlList
= CFArrayCreateCopy(NULL
, storePrivate
->rlList
);
540 case Using_NotifierInformViaDispatch
:
541 dispatchQueue
= storePrivate
->dispatchQueue
;
542 if (dispatchQueue
!= NULL
) dispatch_retain(dispatchQueue
);
549 // invalidate the run loop source(s)
550 if (storePrivate
->callbackRLS
!= NULL
) {
551 CFRunLoopSourceInvalidate(storePrivate
->callbackRLS
);
552 CFRelease(storePrivate
->callbackRLS
);
553 storePrivate
->callbackRLS
= NULL
;
557 if (storePrivate
->callbackPort
!= NULL
) {
558 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreReconnectNotifications w/MACH_NOTIFY_NO_SENDERS", CFMachPortGetPort(storePrivate
->callbackPort
));
559 CFMachPortInvalidate(storePrivate
->callbackPort
);
560 CFRelease(storePrivate
->callbackPort
);
561 storePrivate
->callbackPort
= NULL
;
565 // cancel [old] notifications
566 SCDynamicStoreNotifyCancel(store
);
568 // set notification keys & patterns
569 if ((storePrivate
->keys
!= NULL
) || (storePrivate
->patterns
)) {
570 ok
= SCDynamicStoreSetNotificationKeys(store
,
572 storePrivate
->patterns
);
574 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
576 CFSTR("__SCDynamicStoreReconnectNotifications: SCDynamicStoreSetNotificationKeys() failed"));
581 switch (notifyStatus
) {
582 case Using_NotifierInformViaRunLoop
: {
585 CFRunLoopSourceRef rls
;
587 rls
= SCDynamicStoreCreateRunLoopSource(NULL
, store
, 0);
589 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
591 CFSTR("__SCDynamicStoreReconnectNotifications: SCDynamicStoreCreateRunLoopSource() failed"));
596 n
= (rlList
!= NULL
) ? CFArrayGetCount(rlList
) : 0;
597 for (i
= 0; i
< n
; i
+= 3) {
598 CFRunLoopRef rl
= (CFRunLoopRef
)CFArrayGetValueAtIndex(rlList
, i
+1);
599 CFStringRef rlMode
= (CFStringRef
) CFArrayGetValueAtIndex(rlList
, i
+2);
601 CFRunLoopAddSource(rl
, rls
, rlMode
);
607 case Using_NotifierInformViaDispatch
:
608 ok
= SCDynamicStoreSetDispatchQueue(store
, dispatchQueue
);
610 SCLog((SCError() != BOOTSTRAP_UNKNOWN_SERVICE
),
612 CFSTR("__SCDynamicStoreReconnectNotifications: SCDynamicStoreSetDispatchQueue() failed"));
618 _SCErrorSet(kSCStatusFailed
);
626 switch (notifyStatus
) {
627 case Using_NotifierInformViaRunLoop
:
628 if (rlList
!= NULL
) CFRelease(rlList
);
630 case Using_NotifierInformViaDispatch
:
631 if (dispatchQueue
!= NULL
) dispatch_release(dispatchQueue
);
639 CFSTR("SCDynamicStore server %s, notification (%s) not restored"),
640 (SCError() == BOOTSTRAP_UNKNOWN_SERVICE
) ? "shutdown" : "failed",
641 notifyType
[notifyStatus
]);
645 pushDisconnect(store
);
651 const CFStringRef kSCDynamicStoreUseSessionKeys
= CFSTR("UseSessionKeys"); /* CFBoolean */
655 SCDynamicStoreCreateWithOptions(CFAllocatorRef allocator
,
657 CFDictionaryRef storeOptions
,
658 SCDynamicStoreCallBack callout
,
659 SCDynamicStoreContext
*context
)
662 SCDynamicStorePrivateRef storePrivate
;
664 // allocate and initialize a new session
665 storePrivate
= __SCDynamicStoreCreatePrivate(allocator
, name
, callout
, context
);
666 if (storePrivate
== NULL
) {
671 if (_sc_bundleID
!= NULL
) {
672 storePrivate
->name
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:%@"), _sc_bundleID
, name
);
674 storePrivate
->name
= CFRetain(name
);
678 storePrivate
->options
= (storeOptions
!= NULL
) ? CFRetain(storeOptions
) : NULL
;
680 // establish SCDynamicStore session
681 ok
= __SCDynamicStoreAddSession(storePrivate
);
683 CFRelease(storePrivate
);
687 return (SCDynamicStoreRef
)storePrivate
;
692 SCDynamicStoreCreate(CFAllocatorRef allocator
,
694 SCDynamicStoreCallBack callout
,
695 SCDynamicStoreContext
*context
)
697 return SCDynamicStoreCreateWithOptions(allocator
, name
, NULL
, callout
, context
);
702 SCDynamicStoreGetTypeID(void) {
703 pthread_once(&initialized
, __SCDynamicStoreInitialize
); /* initialize runtime */
704 return __kSCDynamicStoreTypeID
;
708 SCDynamicStoreSetDisconnectCallBack(SCDynamicStoreRef store
,
709 SCDynamicStoreDisconnectCallBack callout
)
711 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
714 /* sorry, you must provide a session */
715 _SCErrorSet(kSCStatusNoStoreSession
);
719 storePrivate
->disconnectFunction
= callout
;