2 * Copyright (c) 2000-2006, 2008 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>
36 #include <sys/types.h>
37 #include <mach/mach.h>
38 #include <mach/mach_error.h>
39 #include <servers/bootstrap.h>
41 #include <SystemConfiguration/SystemConfiguration.h>
42 #include <SystemConfiguration/SCPrivate.h>
43 #include "SCDynamicStoreInternal.h"
44 #include "config.h" /* MiG generated file */
47 static CFStringRef _sc_bundleID
= NULL
;
48 static pthread_mutex_t _sc_lock
= PTHREAD_MUTEX_INITIALIZER
;
49 static mach_port_t _sc_server
= MACH_PORT_NULL
;
53 __SCDynamicStoreCopyDescription(CFTypeRef cf
) {
54 CFAllocatorRef allocator
= CFGetAllocator(cf
);
55 CFMutableStringRef result
;
56 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)cf
;
58 result
= CFStringCreateMutable(allocator
, 0);
59 CFStringAppendFormat(result
, NULL
, CFSTR("<SCDynamicStore %p [%p]> {"), cf
, allocator
);
60 if (storePrivate
->server
!= MACH_PORT_NULL
) {
61 CFStringAppendFormat(result
, NULL
, CFSTR("server port = %p"), storePrivate
->server
);
63 CFStringAppendFormat(result
, NULL
, CFSTR("server not (no longer) available"));
65 if (storePrivate
->locked
) {
66 CFStringAppendFormat(result
, NULL
, CFSTR(", locked"));
68 switch (storePrivate
->notifyStatus
) {
69 case Using_NotifierWait
:
70 CFStringAppendFormat(result
, NULL
, CFSTR(", waiting for a notification"));
72 case Using_NotifierInformViaMachPort
:
73 CFStringAppendFormat(result
, NULL
, CFSTR(", mach port notifications"));
75 case Using_NotifierInformViaFD
:
76 CFStringAppendFormat(result
, NULL
, CFSTR(", FD notifications"));
78 case Using_NotifierInformViaSignal
:
79 CFStringAppendFormat(result
, NULL
, CFSTR(", BSD signal notifications"));
81 case Using_NotifierInformViaRunLoop
:
82 case Using_NotifierInformViaCallback
:
83 if (storePrivate
->notifyStatus
== Using_NotifierInformViaRunLoop
) {
84 CFStringAppendFormat(result
, NULL
, CFSTR(", runloop notifications"));
85 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->rlsFunction
);
86 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->rlsContext
.info
);
87 CFStringAppendFormat(result
, NULL
, CFSTR(", rls = %p"), storePrivate
->rls
);
88 CFStringAppendFormat(result
, NULL
, CFSTR(", refs = %d"), storePrivate
->rlsRefs
);
90 CFStringAppendFormat(result
, NULL
, CFSTR(", mach port/callback notifications"));
91 CFStringAppendFormat(result
, NULL
, CFSTR(" {callout = %p"), storePrivate
->callbackFunction
);
92 CFStringAppendFormat(result
, NULL
, CFSTR(", info = %p"), storePrivate
->callbackArgument
);
94 if (storePrivate
->callbackRLS
!= NULL
) {
95 CFStringAppendFormat(result
, NULL
, CFSTR(", notify rls = %@" ), storePrivate
->callbackRLS
);
97 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
100 CFStringAppendFormat(result
, NULL
, CFSTR(", notification delivery not requested%s"),
101 storePrivate
->rlsFunction
? " (yet)" : "");
104 CFStringAppendFormat(result
, NULL
, CFSTR("}"));
111 __SCDynamicStoreDeallocate(CFTypeRef cf
)
115 SCDynamicStoreRef store
= (SCDynamicStoreRef
)cf
;
116 SCDynamicStorePrivateRef storePrivate
= (SCDynamicStorePrivateRef
)store
;
118 (void) pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED
, &oldThreadState
);
120 /* Remove/cancel any outstanding notification requests. */
121 (void) SCDynamicStoreNotifyCancel(store
);
123 if ((storePrivate
->server
!= MACH_PORT_NULL
) && storePrivate
->locked
) {
124 (void) SCDynamicStoreUnlock(store
); /* release the lock */
127 if (storePrivate
->server
!= MACH_PORT_NULL
) {
128 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreDeallocate", storePrivate
->server
);
129 (void) configclose(storePrivate
->server
, (int *)&sc_status
);
130 __MACH_PORT_DEBUG(TRUE
, "*** __SCDynamicStoreDeallocate (after configclose)", storePrivate
->server
);
133 * the above call to configclose() should result in the SCDynamicStore
134 * server code deallocating it's receive right. That, in turn, should
135 * result in our send becoming a dead name. We could explicitly remove
136 * the dead name right with a call to mach_port_mod_refs() but, to be
137 * sure, we use mach_port_deallocate() since that will get rid of a
138 * send, send_once, or dead name right.
140 (void) mach_port_deallocate(mach_task_self(), storePrivate
->server
);
141 storePrivate
->server
= MACH_PORT_NULL
;
144 (void) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS
, &oldThreadState
);
145 pthread_testcancel();
147 /* release any callback context info */
148 if (storePrivate
->rlsContext
.release
!= NULL
) {
149 (*storePrivate
->rlsContext
.release
)(storePrivate
->rlsContext
.info
);
152 /* release any keys being watched */
153 CFRelease(storePrivate
->keys
);
154 CFRelease(storePrivate
->patterns
);
160 static CFTypeID __kSCDynamicStoreTypeID
= _kCFRuntimeNotATypeID
;
163 static const CFRuntimeClass __SCDynamicStoreClass
= {
165 "SCDynamicStore", // className
168 __SCDynamicStoreDeallocate
, // dealloc
171 NULL
, // copyFormattingDesc
172 __SCDynamicStoreCopyDescription
// copyDebugDesc
179 /* the process has forked (and we are the child process) */
181 _sc_server
= MACH_PORT_NULL
;
186 static pthread_once_t initialized
= PTHREAD_ONCE_INIT
;
189 __SCDynamicStoreInitialize(void)
193 /* register with CoreFoundation */
194 __kSCDynamicStoreTypeID
= _CFRuntimeRegisterClass(&__SCDynamicStoreClass
);
196 /* add handler to cleanup after fork() */
197 (void) pthread_atfork(NULL
, NULL
, childForkHandler
);
199 /* get the application/executable/bundle name */
200 bundle
= CFBundleGetMainBundle();
201 if (bundle
!= NULL
) {
202 _sc_bundleID
= CFBundleGetIdentifier(bundle
);
203 if (_sc_bundleID
!= NULL
) {
204 CFRetain(_sc_bundleID
);
208 url
= CFBundleCopyExecutableURL(bundle
);
210 _sc_bundleID
= CFURLCopyPath(url
);
215 if (_sc_bundleID
!= NULL
) {
216 if (CFEqual(_sc_bundleID
, CFSTR("/"))) {
217 CFRelease(_sc_bundleID
);
218 _sc_bundleID
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("(%d)"), getpid());
228 __SCDynamicStoreServerPort(kern_return_t
*status
)
230 mach_port_t server
= MACH_PORT_NULL
;
233 server_name
= getenv("SCD_SERVER");
235 server_name
= SCD_SERVER
;
238 *status
= bootstrap_look_up(bootstrap_port
, server_name
, &server
);
240 case BOOTSTRAP_SUCCESS
:
241 /* service currently registered, "a good thing" (tm) */
243 case BOOTSTRAP_UNKNOWN_SERVICE
:
244 /* service not currently registered, try again later */
248 SCLog(_sc_verbose
, LOG_DEBUG
,
249 CFSTR("SCDynamicStoreCreate[WithOptions] bootstrap_look_up() failed: status=%s"),
250 bootstrap_strerror(*status
));
255 return MACH_PORT_NULL
;
259 SCDynamicStorePrivateRef
260 __SCDynamicStoreCreatePrivate(CFAllocatorRef allocator
,
261 const CFStringRef name
,
262 SCDynamicStoreCallBack callout
,
263 SCDynamicStoreContext
*context
)
265 int sc_status
= kSCStatusOK
;
267 SCDynamicStorePrivateRef storePrivate
;
269 /* initialize runtime */
270 pthread_once(&initialized
, __SCDynamicStoreInitialize
);
273 /* allocate session */
274 size
= sizeof(SCDynamicStorePrivate
) - sizeof(CFRuntimeBase
);
275 storePrivate
= (SCDynamicStorePrivateRef
)_CFRuntimeCreateInstance(allocator
,
276 __kSCDynamicStoreTypeID
,
279 if (storePrivate
== NULL
) {
280 _SCErrorSet(kSCStatusFailed
);
284 /* server side of the "configd" session */
285 storePrivate
->server
= MACH_PORT_NULL
;
288 storePrivate
->locked
= FALSE
;
289 storePrivate
->useSessionKeys
= FALSE
;
291 /* Notification status */
292 storePrivate
->notifyStatus
= NotifierNotRegistered
;
294 /* "client" information associated with SCDynamicStoreCreateRunLoopSource() */
295 storePrivate
->rlsRefs
= 0;
296 storePrivate
->rls
= NULL
;
297 storePrivate
->rlsFunction
= callout
;
298 storePrivate
->rlsContext
.info
= NULL
;
299 storePrivate
->rlsContext
.retain
= NULL
;
300 storePrivate
->rlsContext
.release
= NULL
;
301 storePrivate
->rlsContext
.copyDescription
= NULL
;
303 bcopy(context
, &storePrivate
->rlsContext
, sizeof(SCDynamicStoreContext
));
304 if (context
->retain
!= NULL
) {
305 storePrivate
->rlsContext
.info
= (void *)(*context
->retain
)(context
->info
);
309 /* "client" information associated with SCDynamicStoreNotifyCallback() */
310 storePrivate
->callbackFunction
= NULL
;
311 storePrivate
->callbackArgument
= NULL
;
312 storePrivate
->callbackPort
= NULL
;
313 storePrivate
->callbackRLS
= NULL
;
315 /* "server" information associated with SCDynamicStoreSetNotificationKeys() */
316 storePrivate
->keys
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
317 storePrivate
->patterns
= CFSetCreateMutable(NULL
, 0, &kCFTypeSetCallBacks
);
319 /* "server" information associated with SCDynamicStoreNotifyMachPort(); */
320 storePrivate
->notifyPort
= MACH_PORT_NULL
;
321 storePrivate
->notifyPortIdentifier
= 0;
323 /* "server" information associated with SCDynamicStoreNotifyFileDescriptor(); */
324 storePrivate
->notifyFile
= -1;
325 storePrivate
->notifyFileIdentifier
= 0;
327 /* "server" information associated with SCDynamicStoreNotifySignal(); */
328 storePrivate
->notifySignal
= 0;
329 storePrivate
->notifySignalTask
= TASK_NULL
;
331 if (sc_status
!= kSCStatusOK
) {
332 _SCErrorSet(sc_status
);
333 CFRelease(storePrivate
);
341 const CFStringRef kSCDynamicStoreUseSessionKeys
= CFSTR("UseSessionKeys"); /* CFBoolean */
345 SCDynamicStoreCreateWithOptions(CFAllocatorRef allocator
,
347 CFDictionaryRef storeOptions
,
348 SCDynamicStoreCallBack callout
,
349 SCDynamicStoreContext
*context
)
351 int sc_status
= kSCStatusFailed
;
353 kern_return_t status
= KERN_SUCCESS
;
354 SCDynamicStorePrivateRef storePrivate
;
355 CFDataRef utfName
; /* serialized name */
358 CFDataRef xmlOptions
= NULL
; /* serialized options */
359 xmlData_t myOptionsRef
= NULL
;
360 CFIndex myOptionsLen
= 0;
363 * allocate and initialize a new session
365 storePrivate
= __SCDynamicStoreCreatePrivate(allocator
, name
, callout
, context
);
366 if (storePrivate
== NULL
) {
370 if (_sc_bundleID
!= NULL
) {
371 CFStringRef fullName
;
373 fullName
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%@:%@"), _sc_bundleID
, name
);
379 if (!_SCSerializeString(name
, &utfName
, (void **)&myNameRef
, &myNameLen
)) {
385 /* serialize the options */
386 if (storeOptions
!= NULL
) {
387 if (!_SCSerialize(storeOptions
, &xmlOptions
, (void **)&myOptionsRef
, &myOptionsLen
)) {
393 /* open a new session with the server */
396 if (server
!= MACH_PORT_NULL
) {
397 status
= configopen(server
,
402 &storePrivate
->server
,
404 if (status
== KERN_SUCCESS
) {
408 // our [cached] server port is not valid
409 if (status
!= MACH_SEND_INVALID_DEST
) {
410 // if we got an unexpected error, don't retry
416 pthread_mutex_lock(&_sc_lock
);
417 if (_sc_server
!= MACH_PORT_NULL
) {
418 if (server
== _sc_server
) {
419 // if the server we tried returned the error
420 (void)mach_port_deallocate(mach_task_self(), _sc_server
);
421 _sc_server
= __SCDynamicStoreServerPort(&sc_status
);
423 // another thread has refreshed the SCDynamicStore server port
426 _sc_server
= __SCDynamicStoreServerPort(&sc_status
);
429 pthread_mutex_unlock(&_sc_lock
);
431 if (server
== MACH_PORT_NULL
) {
432 // if SCDynamicStore server not available
436 __MACH_PORT_DEBUG(TRUE
, "*** SCDynamicStoreCreate[WithOptions]", storePrivate
->server
);
440 if (xmlOptions
) CFRelease(xmlOptions
);
444 if (sc_status
!= kSCStatusOK
) {
446 (status
== KERN_SUCCESS
) ? LOG_DEBUG
: LOG_ERR
,
447 CFSTR("SCDynamicStoreCreate[WithOptions] configopen(): %s"),
448 SCErrorString(sc_status
));
449 _SCErrorSet(sc_status
);
450 CFRelease(storePrivate
);
454 return (SCDynamicStoreRef
)storePrivate
;
459 SCDynamicStoreCreate(CFAllocatorRef allocator
,
461 SCDynamicStoreCallBack callout
,
462 SCDynamicStoreContext
*context
)
464 return SCDynamicStoreCreateWithOptions(allocator
, name
, NULL
, callout
, context
);
469 SCDynamicStoreGetTypeID(void) {
470 pthread_once(&initialized
, __SCDynamicStoreInitialize
); /* initialize runtime */
471 return __kSCDynamicStoreTypeID
;