2 * Copyright (c) 2000-2003 Apple Computer, 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 * October 30, 2003 Allan Nathanson <ajn@apple.com>
28 * - add plugin "stop()" function support
30 * June 11, 2001 Allan Nathanson <ajn@apple.com>
31 * - start using CFBundle code
33 * June 1, 2001 Allan Nathanson <ajn@apple.com>
34 * - public API conversion
36 * May 26, 2000 Allan Nathanson <ajn@apple.com>
40 #include <mach-o/dyld.h>
41 #include <sys/types.h>
43 #include <sys/param.h>
47 #include <NSSystemDirectories.h>
50 #include "configd_server.h"
51 #include <SystemConfiguration/SCDPlugin.h>
52 void _SCDPluginExecInit();
56 * path components, extensions, entry points, ...
58 #define BUNDLE_DIRECTORY "/SystemConfiguration" /* [/System/Library]/... */
59 #define BUNDLE_DIR_EXTENSION ".bundle"
67 SCDynamicStoreBundleLoadFunction load
;
68 SCDynamicStoreBundleStartFunction start
;
69 SCDynamicStoreBundlePrimeFunction prime
;
70 SCDynamicStoreBundleStopFunction stop
;
75 static CFMutableArrayRef allBundles
= NULL
;
78 static CFMutableDictionaryRef exiting
= NULL
;
80 // plugin CFRunLoopRef
81 static CFRunLoopRef plugin_runLoop
= NULL
;
85 //extern SCDynamicStoreBundleLoadFunction load_ATconfig;
86 //extern SCDynamicStoreBundleStopFunction stop_ATconfig;
88 extern SCDynamicStoreBundleLoadFunction load_IPMonitor
;
89 extern SCDynamicStoreBundlePrimeFunction prime_IPMonitor
;
90 extern SCDynamicStoreBundleLoadFunction load_InterfaceNamer
;
91 extern SCDynamicStoreBundleLoadFunction load_KernelEventMonitor
;
92 extern SCDynamicStoreBundlePrimeFunction prime_KernelEventMonitor
;
93 extern SCDynamicStoreBundleLoadFunction load_Kicker
;
94 extern SCDynamicStoreBundleLoadFunction load_LinkConfiguration
;
95 extern SCDynamicStoreBundleLoadFunction load_PreferencesMonitor
;
96 extern SCDynamicStoreBundlePrimeFunction prime_PreferencesMonitor
;
97 extern SCDynamicStoreBundleStopFunction stop_PreferencesMonitor
;
101 const CFStringRef bundleID
;
102 const void *load
; // SCDynamicStoreBundleLoadFunction
103 const void *start
; // SCDynamicStoreBundleStartFunction
104 const void *prime
; // SCDynamicStoreBundlePrimeFunction
105 const void *stop
; // SCDynamicStoreBundleStopFunction
106 } builtin
, *builtinRef
;
109 static const builtin builtin_plugins
[] = {
112 // CFSTR("com.apple.SystemConfiguration.ATconfig"),
120 CFSTR("com.apple.SystemConfiguration.IPMonitor"),
127 CFSTR("com.apple.SystemConfiguration.InterfaceNamer"),
128 &load_InterfaceNamer
,
134 CFSTR("com.apple.SystemConfiguration.KernelEventMonitor"),
135 &load_KernelEventMonitor
,
137 &prime_KernelEventMonitor
,
141 CFSTR("com.apple.SystemConfiguration.Kicker"),
148 CFSTR("com.apple.SystemConfiguration.LinkConfiguration"),
149 &load_LinkConfiguration
,
155 CFSTR("com.apple.SystemConfiguration.PreferencesMonitor"),
156 &load_PreferencesMonitor
,
158 &prime_PreferencesMonitor
,
159 &stop_PreferencesMonitor
165 addBundle(CFBundleRef bundle
)
167 CFDictionaryRef bundleDict
;
168 bundleInfoRef bundleInfo
;
170 bundleInfo
= CFAllocatorAllocate(NULL
, sizeof(*bundleInfo
), 0);
171 bundleInfo
->bundle
= (CFBundleRef
)CFRetain(bundle
);
172 bundleInfo
->loaded
= FALSE
;
173 bundleInfo
->builtin
= FALSE
;
174 bundleInfo
->verbose
= FALSE
;
175 bundleInfo
->load
= NULL
;
176 bundleInfo
->start
= NULL
;
177 bundleInfo
->prime
= NULL
;
178 bundleInfo
->stop
= NULL
;
180 bundleDict
= CFBundleGetInfoDictionary(bundle
);
181 if (isA_CFDictionary(bundleDict
)) {
184 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleIsBuiltinKey
);
185 if (isA_CFBoolean(bVal
) && CFBooleanGetValue(bVal
)) {
186 bundleInfo
->builtin
= TRUE
;
189 bVal
= CFDictionaryGetValue(bundleDict
, kSCBundleVerboseKey
);
190 if (isA_CFBoolean(bVal
) && CFBooleanGetValue(bVal
)) {
191 bundleInfo
->verbose
= TRUE
;
195 CFArrayAppendValue(allBundles
, bundleInfo
);
201 shortBundleIdentifier(CFStringRef bundleID
)
203 CFIndex len
= CFStringGetLength(bundleID
);
205 CFStringRef shortID
= NULL
;
207 if (CFStringFindWithOptions(bundleID
,
212 range
.location
= range
.location
+ range
.length
;
213 range
.length
= len
- range
.location
;
214 shortID
= CFStringCreateWithSubstring(NULL
, bundleID
, range
);
222 getBundleSymbol(CFBundleRef bundle
, CFStringRef functionName
, CFStringRef shortID
)
226 // search for load(), start(), prime(), stop(), ...
227 func
= CFBundleGetFunctionPointerForName(bundle
, functionName
);
232 if (shortID
!= NULL
) {
233 CFStringRef altFunctionName
;
235 // search for load_XXX(), ...
236 altFunctionName
= CFStringCreateWithFormat(NULL
,
241 func
= CFBundleGetFunctionPointerForName(bundle
, altFunctionName
);
242 CFRelease(altFunctionName
);
250 loadBundle(const void *value
, void *context
) {
251 CFStringRef bundleID
;
252 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
253 Boolean bundleExclude
;
254 CFIndex
*nLoaded
= (CFIndex
*)context
;
257 bundleID
= CFBundleGetIdentifier(bundleInfo
->bundle
);
258 if (bundleID
== NULL
) {
259 // sorry, no bundles without a bundle identifier
260 SCLog(TRUE
, LOG_DEBUG
, CFSTR("skipped %@"), bundleInfo
->bundle
);
264 shortID
= shortBundleIdentifier(bundleID
);
266 bundleExclude
= CFSetContainsValue(_plugins_exclude
, bundleID
);
268 if (shortID
!= NULL
) {
269 bundleExclude
= CFSetContainsValue(_plugins_exclude
, shortID
);
274 // sorry, this bundle has been excluded
275 SCLog(TRUE
, LOG_DEBUG
, CFSTR("excluded %@"), bundleID
);
279 if (!bundleInfo
->verbose
) {
280 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, bundleID
);
281 if (!bundleInfo
->verbose
) {
282 if (shortID
!= NULL
) {
283 bundleInfo
->verbose
= CFSetContainsValue(_plugins_verbose
, shortID
);
288 if (bundleInfo
->builtin
) {
291 SCLog(TRUE
, LOG_DEBUG
, CFSTR("adding %@"), bundleID
);
293 for (i
= 0; i
< sizeof(builtin_plugins
)/sizeof(builtin_plugins
[0]); i
++) {
294 if (CFEqual(bundleID
, builtin_plugins
[i
].bundleID
)) {
295 bundleInfo
->load
= builtin_plugins
[i
].load
;
296 bundleInfo
->start
= builtin_plugins
[i
].start
;
297 bundleInfo
->prime
= builtin_plugins
[i
].prime
;
298 bundleInfo
->stop
= builtin_plugins
[i
].stop
;
303 SCLog(TRUE
, LOG_DEBUG
, CFSTR("loading %@"), bundleID
);
305 if (!CFBundleLoadExecutable(bundleInfo
->bundle
)) {
306 SCLog(TRUE
, LOG_NOTICE
, CFSTR("%@ load failed"), bundleID
);
310 // get bundle entry points
311 bundleInfo
->load
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("load" ), shortID
);
312 bundleInfo
->start
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("start"), shortID
);
313 bundleInfo
->prime
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("prime"), shortID
);
314 bundleInfo
->stop
= getBundleSymbol(bundleInfo
->bundle
, CFSTR("stop" ), shortID
);
317 /* mark this bundle as having been loaded */
318 bundleInfo
->loaded
= TRUE
;
320 /* bump the count of loaded bundles */
321 *nLoaded
= *nLoaded
+ 1;
325 if (shortID
!= NULL
) CFRelease(shortID
);
331 callLoadFunction(const void *value
, void *context
) {
332 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
334 if (!bundleInfo
->loaded
) {
338 if (bundleInfo
->load
== NULL
) {
339 // if no load() function
343 (*bundleInfo
->load
)(bundleInfo
->bundle
, bundleInfo
->verbose
);
349 callStartFunction(const void *value
, void *context
) {
350 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
352 char bundleName
[MAXNAMLEN
+ 1];
353 char bundlePath
[MAXPATHLEN
];
358 if (!bundleInfo
->loaded
) {
362 if (bundleInfo
->start
== NULL
) {
363 // if no start() function
367 bundleURL
= CFBundleCopyBundleURL(bundleInfo
->bundle
);
368 if (bundleURL
== NULL
) {
372 ok
= CFURLGetFileSystemRepresentation(bundleURL
,
374 (UInt8
*)&bundlePath
,
376 CFRelease(bundleURL
);
381 cp
= strrchr(bundlePath
, '/');
388 /* check if this directory entry is a valid bundle name */
390 if (len
<= (int)sizeof(BUNDLE_DIR_EXTENSION
)) {
391 /* if entry name isn't long enough */
395 len
-= sizeof(BUNDLE_DIR_EXTENSION
) - 1;
396 if (strcmp(&cp
[len
], BUNDLE_DIR_EXTENSION
) != 0) {
397 /* if entry name doesn end with ".bundle" */
401 /* get (just) the bundle's name */
402 bundleName
[0] = '\0';
403 (void) strncat(bundleName
, cp
, len
);
405 (*bundleInfo
->start
)(bundleName
, bundlePath
);
411 callPrimeFunction(const void *value
, void *context
) {
412 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
414 if (!bundleInfo
->loaded
) {
418 if (bundleInfo
->prime
== NULL
) {
419 // if no prime() function
423 (*bundleInfo
->prime
)();
429 stopComplete(void *info
)
431 CFBundleRef bundle
= (CFBundleRef
)info
;
432 CFStringRef bundleID
= CFBundleGetIdentifier(bundle
);
433 CFRunLoopSourceRef stopRls
;
435 SCLog(TRUE
, LOG_DEBUG
, CFSTR("** %@ complete (%f)"), bundleID
, CFAbsoluteTimeGetCurrent());
437 stopRls
= (CFRunLoopSourceRef
)CFDictionaryGetValue(exiting
, bundle
);
438 CFRunLoopSourceInvalidate(stopRls
);
440 CFDictionaryRemoveValue(exiting
, bundle
);
442 if (CFDictionaryGetCount(exiting
) == 0) {
445 // if all of the plugins are happy
446 status
= server_shutdown();
447 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
456 stopDelayed(CFRunLoopTimerRef timer
, void *info
)
463 SCLog(TRUE
, LOG_ERR
, CFSTR("server shutdown was delayed, unresponsive plugins:"));
466 * we've asked our plugins to shutdown but someone
469 n
= CFDictionaryGetCount(exiting
);
470 keys
= CFAllocatorAllocate(NULL
, n
* sizeof(CFTypeRef
), 0);
471 CFDictionaryGetKeysAndValues(exiting
, keys
, NULL
);
472 for (i
= 0; i
< n
; i
++) {
474 CFStringRef bundleID
;
476 bundle
= (CFBundleRef
)keys
[i
];
477 bundleID
= CFBundleGetIdentifier(bundle
);
478 SCLog(TRUE
, LOG_ERR
, CFSTR("** %@"), bundleID
);
480 CFAllocatorDeallocate(NULL
, keys
);
482 status
= server_shutdown();
487 stopBundle(const void *value
, void *context
) {
488 bundleInfoRef bundleInfo
= (bundleInfoRef
)value
;
489 CFRunLoopSourceRef stopRls
;
490 CFRunLoopSourceContext stopContext
= { 0 // version
491 , bundleInfo
->bundle
// info
493 , CFRelease
// release
494 , CFCopyDescription
// copyDescription
499 , stopComplete
// perform
502 if (!bundleInfo
->loaded
) {
506 if (bundleInfo
->stop
== NULL
) {
507 // if no stop() function
511 stopRls
= CFRunLoopSourceCreate(NULL
, 0, &stopContext
);
512 CFRunLoopAddSource(CFRunLoopGetCurrent(), stopRls
, kCFRunLoopDefaultMode
);
513 CFDictionaryAddValue(exiting
, bundleInfo
->bundle
, stopRls
);
516 (*bundleInfo
->stop
)(stopRls
);
526 * If defined, call each bundles stop() function. This function is
527 * called when configd has been asked to shut down (via a SIGTERM). The
528 * function should signal the provided run loop source when it is "ready"
529 * for the shut down to proceeed.
531 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle stop() functions"));
532 CFArrayApplyFunction(allBundles
,
533 CFRangeMake(0, CFArrayGetCount(allBundles
)),
537 if (CFDictionaryGetCount(exiting
) == 0) {
540 // if all of the plugins are happy
541 status
= server_shutdown();
542 SCLog(TRUE
, LOG_DEBUG
, CFSTR("server shutdown complete (%f)"), CFAbsoluteTimeGetCurrent());
545 CFRunLoopTimerRef timer
;
547 /* sorry, we're not going to wait longer than 20 seconds */
548 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
549 CFAbsoluteTimeGetCurrent() + 20.0, /* fireDate (in 20 seconds) */
550 0.0, /* interval (== one-shot) */
553 stopDelayed
, /* callout */
555 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
565 plugin_term(int *status
)
567 CFRunLoopSourceRef stopRls
;
568 CFRunLoopSourceContext stopContext
= { 0 // version
572 , NULL
// copyDescription
577 , stopBundles
// perform
580 if (plugin_runLoop
== NULL
) {
583 return FALSE
; // don't delay shutdown
586 if (exiting
!= NULL
) {
587 // if shutdown already active
591 SCLog(TRUE
, LOG_DEBUG
, CFSTR("starting server shutdown (%f)"), CFAbsoluteTimeGetCurrent());
593 exiting
= CFDictionaryCreateMutable(NULL
,
595 &kCFTypeDictionaryKeyCallBacks
,
596 &kCFTypeDictionaryValueCallBacks
);
598 stopRls
= CFRunLoopSourceCreate(NULL
, 0, &stopContext
);
599 CFRunLoopAddSource(plugin_runLoop
, stopRls
, kCFRunLoopDefaultMode
);
600 CFRunLoopSourceSignal(stopRls
);
602 CFRunLoopWakeUp(plugin_runLoop
);
611 timerCallback(CFRunLoopTimerRef timer
, void *info
)
613 SCLog(_configd_verbose
,
615 CFSTR("the CFRunLoop is waiting for something to happen...."));
623 sortBundles(CFMutableArrayRef orig
)
625 CFMutableArrayRef
new;
627 new = CFArrayCreateMutable(NULL
, 0, NULL
);
628 while (CFArrayGetCount(orig
) > 0) {
630 Boolean inserted
= FALSE
;
631 int nOrig
= CFArrayGetCount(orig
);
633 for (i
= 0; i
< nOrig
; i
++) {
634 bundleInfoRef bundleInfo1
= (bundleInfoRef
)CFArrayGetValueAtIndex(orig
, i
);
635 CFStringRef bundleID1
= CFBundleGetIdentifier(bundleInfo1
->bundle
);
637 CFDictionaryRef dict
;
640 CFArrayRef
requires = NULL
;
642 dict
= isA_CFDictionary(CFBundleGetInfoDictionary(bundleInfo1
->bundle
));
644 requires = CFDictionaryGetValue(dict
, kSCBundleRequiresKey
);
645 requires = isA_CFArray(requires);
647 if (bundleID1
== NULL
|| requires == NULL
) {
648 CFArrayInsertValueAtIndex(new, 0, bundleInfo1
);
649 CFArrayRemoveValueAtIndex(orig
, i
);
653 count
= nRequires
= CFArrayGetCount(requires);
654 for (j
= 0; j
< nRequires
; j
++) {
657 CFStringRef r
= CFArrayGetValueAtIndex(requires, j
);
659 nNew
= CFArrayGetCount(new);
660 for (k
= 0; k
< nNew
; k
++) {
661 bundleInfoRef bundleInfo2
= (bundleInfoRef
)CFArrayGetValueAtIndex(new, k
);
662 CFStringRef bundleID2
= CFBundleGetIdentifier(bundleInfo2
->bundle
);
664 if (bundleID2
&& CFEqual(bundleID2
, r
)) {
670 /* all dependencies are met, append */
671 CFArrayAppendValue(new, bundleInfo1
);
672 CFArrayRemoveValueAtIndex(orig
, i
);
678 if (inserted
== FALSE
) {
679 SCLog(TRUE
, LOG_NOTICE
, CFSTR("Bundles have circular dependency!!!"));
683 if (CFArrayGetCount(orig
) > 0) {
684 /* we have a circular dependency, append remaining items on new array */
685 CFArrayAppendArray(new, orig
, CFRangeMake(0, CFArrayGetCount(orig
)));
688 /* new one is a sorted version of original */
691 CFArrayRemoveAllValues(orig
);
692 CFArrayAppendArray(orig
, new, CFRangeMake(0, CFArrayGetCount(new)));
700 plugin_exec(void *arg
)
704 /* keep track of bundles */
705 allBundles
= CFArrayCreateMutable(NULL
, 0, NULL
);
707 /* allow plug-ins to exec child/helper processes */
708 _SCDPluginExecInit();
711 char path
[MAXPATHLEN
];
712 NSSearchPathEnumerationState state
;
715 * identify and load all bundles
717 state
= NSStartSearchPathEnumeration(NSLibraryDirectory
,
719 while ((state
= NSGetNextSearchPathEnumeration(state
, path
))) {
723 /* load any available bundle */
724 strcat(path
, BUNDLE_DIRECTORY
);
725 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("searching for bundles in \".\""));
726 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
730 bundles
= CFBundleCreateBundlesFromDirectory(NULL
, url
, CFSTR(".bundle"));
733 if (bundles
!= NULL
) {
737 n
= CFArrayGetCount(bundles
);
738 for (i
= 0; i
< n
; i
++) {
741 bundle
= (CFBundleRef
)CFArrayGetValueAtIndex(bundles
, i
);
748 sortBundles(allBundles
);
754 * load (only) the specified bundle
756 url
= CFURLCreateFromFileSystemRepresentation(NULL
,
760 bundle
= CFBundleCreate(NULL
, url
);
761 if (bundle
!= NULL
) {
771 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("loading bundles"));
772 CFArrayApplyFunction(allBundles
,
773 CFRangeMake(0, CFArrayGetCount(allBundles
)),
778 * If defined, call each bundles load() function. This function (or
779 * the start() function) should initialize any variables, open any
780 * sessions with "configd", and register any needed notifications.
782 * Note: Establishing initial information in the store should be
783 * deferred until the prime() initialization function so that
784 * any bundles which want to receive a notification that the
785 * data has changed will have an opportunity to install a
786 * notification handler.
788 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle load() functions"));
789 CFArrayApplyFunction(allBundles
,
790 CFRangeMake(0, CFArrayGetCount(allBundles
)),
795 * If defined, call each bundles start() function. This function is
796 * called after the bundle has been loaded and its load() function has
797 * been called. It should initialize any variables, open any sessions
798 * with "configd", and register any needed notifications.
800 * Note: Establishing initial information in the store should be
801 * deferred until the prime() initialization function so that
802 * any bundles which want to receive a notification that the
803 * data has changed will have an opportunity to install a
804 * notification handler.
806 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle start() functions"));
807 CFArrayApplyFunction(allBundles
,
808 CFRangeMake(0, CFArrayGetCount(allBundles
)),
813 * If defined, call each bundles prime() function. This function is
814 * called after the bundle has been loaded and its load() and start()
815 * functions have been called. It should initialize any configuration
816 * information and/or state in the store.
818 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("calling bundle prime() functions"));
819 CFArrayApplyFunction(allBundles
,
820 CFRangeMake(0, CFArrayGetCount(allBundles
)),
825 if (arg
== NULL
&& (nLoaded
> 0)) {
826 CFRunLoopTimerRef timer
;
828 /* allocate a periodic event (to help show we're not blocking) */
829 timer
= CFRunLoopTimerCreate(NULL
, /* allocator */
830 CFAbsoluteTimeGetCurrent() + 1.0, /* fireDate */
834 timerCallback
, /* callout */
836 CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer
, kCFRunLoopDefaultMode
);
842 * The assumption is that each loaded plugin will establish CFMachPortRef,
843 * CFSocketRef, and CFRunLoopTimerRef input sources to handle any events
844 * and register these sources with this threads run loop. If the plugin
845 * needs to wait and/or block at any time it should do so only in its a
848 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("starting plugin CFRunLoop"));
849 plugin_runLoop
= CFRunLoopGetCurrent();
852 SCLog(_configd_verbose
, LOG_INFO
, CFSTR("No more work for the \"configd\" plugins"));
853 plugin_runLoop
= NULL
;
862 pthread_attr_t tattr
;
865 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR("Starting thread for plug-ins..."));
866 pthread_attr_init(&tattr
);
867 pthread_attr_setscope(&tattr
, PTHREAD_SCOPE_SYSTEM
);
868 pthread_attr_setdetachstate(&tattr
, PTHREAD_CREATE_DETACHED
);
869 // pthread_attr_setstacksize(&tattr, 96 * 1024); // each thread gets a 96K stack
870 pthread_create(&tid
, &tattr
, plugin_exec
, NULL
);
871 pthread_attr_destroy(&tattr
);
872 SCLog(_configd_verbose
, LOG_DEBUG
, CFSTR(" thread id=0x%08x"), tid
);