2 * Copyright (c) 2000-2008, 2010, 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 <mach/mach.h>
35 #include <mach/mach_error.h>
36 #include <servers/bootstrap.h>
40 #include <SystemConfiguration/SystemConfiguration.h>
41 #include <SystemConfiguration/SCPrivate.h>
43 #include "SCDynamicStoreInternal.h"
44 #include "config.h" /* MiG generated file */
47 /* framework variables */
48 int _sc_debug
= FALSE
; /* non-zero if debugging enabled */
49 int _sc_verbose
= FALSE
; /* non-zero if verbose logging enabled */
50 int _sc_log
= TRUE
; /* 0 if SC messages should be written to stdout/stderr,
51 1 if SC messages should be logged w/asl(3),
52 2 if SC messages should be written to stdout/stderr AND logged */
56 #pragma mark Thread specific data
59 static pthread_once_t tsKeyInitialized
= PTHREAD_ONCE_INIT
;
60 static pthread_key_t tsDataKey
;
64 __SCThreadSpecificDataFinalize(void *arg
)
66 __SCThreadSpecificDataRef tsd
= (__SCThreadSpecificDataRef
)arg
;
69 if (tsd
->_asl
!= NULL
) asl_close(tsd
->_asl
);
70 if (tsd
->_sc_store
!= NULL
) CFRelease(tsd
->_sc_store
);
71 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, tsd
);
78 __SCThreadSpecificKeyInitialize()
80 pthread_key_create(&tsDataKey
, __SCThreadSpecificDataFinalize
);
86 __SCThreadSpecificDataRef
87 __SCGetThreadSpecificData()
89 __SCThreadSpecificDataRef tsd
;
91 pthread_once(&tsKeyInitialized
, __SCThreadSpecificKeyInitialize
);
93 tsd
= pthread_getspecific(tsDataKey
);
95 tsd
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__SCThreadSpecificData
), 0);
97 tsd
->_sc_error
= kSCStatusOK
;
98 tsd
->_sc_store
= NULL
;
99 pthread_setspecific(tsDataKey
, tsd
);
110 #define ENABLE_SC_FORMATTING
111 #ifdef ENABLE_SC_FORMATTING
112 // from <CoreFoundation/ForFoundationOnly.h>
113 extern CFStringRef
_CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc
, CFStringRef (*copyDescFunc
)(CFTypeRef
, CFDictionaryRef
), CFDictionaryRef formatOptions
, CFStringRef format
, va_list arguments
);
114 #endif /* ENABLE_SC_FORMATTING */
118 _SCCopyDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
)
120 #ifdef ENABLE_SC_FORMATTING
121 CFMutableDictionaryRef nFormatOptions
;
124 CFTypeID type
= CFGetTypeID(cf
);
126 if (!formatOptions
||
127 !CFDictionaryGetValueIfPresent(formatOptions
, CFSTR("PREFIX1"), (const void **)&prefix1
)) {
131 if (type
== CFStringGetTypeID()) {
132 return CFStringCreateWithFormat(NULL
,
139 if (type
== CFBooleanGetTypeID()) {
140 return CFStringCreateWithFormat(NULL
,
144 CFBooleanGetValue(cf
) ? "TRUE" : "FALSE");
147 if (type
== CFDataGetTypeID()) {
151 CFMutableStringRef str
;
153 str
= CFStringCreateMutable(NULL
, 0);
154 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<data> 0x"), prefix1
);
156 data
= CFDataGetBytePtr(cf
);
157 dataLen
= CFDataGetLength(cf
);
158 for (i
= 0; i
< dataLen
; i
++) {
159 CFStringAppendFormat(str
, NULL
, CFSTR("%02x"), data
[i
]);
165 if (type
== CFNumberGetTypeID()) {
166 return CFStringCreateWithFormat(NULL
,
173 if (type
== CFDateGetTypeID()) {
174 CFGregorianDate gDate
;
178 tZone
= CFTimeZoneCopySystem();
179 gDate
= CFAbsoluteTimeGetGregorianDate(CFDateGetAbsoluteTime(cf
), tZone
);
180 str
= CFStringCreateWithFormat(NULL
,
182 CFSTR("%@%02d/%02d/%04d %02d:%02d:%02.0f %@"),
190 CFTimeZoneGetName(tZone
));
195 if (!formatOptions
||
196 !CFDictionaryGetValueIfPresent(formatOptions
, CFSTR("PREFIX2"), (const void **)&prefix2
)) {
201 nFormatOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, formatOptions
);
203 nFormatOptions
= CFDictionaryCreateMutable(NULL
,
205 &kCFTypeDictionaryKeyCallBacks
,
206 &kCFTypeDictionaryValueCallBacks
);
211 if (type
== CFArrayGetTypeID()) {
212 const void * elements_q
[N_QUICK
];
213 const void ** elements
= elements_q
;
216 CFMutableStringRef str
;
218 str
= CFStringCreateMutable(NULL
, 0);
219 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<array> {"), prefix1
);
221 nElements
= CFArrayGetCount(cf
);
223 if (nElements
> (CFIndex
)(sizeof(elements_q
)/sizeof(CFTypeRef
)))
224 elements
= CFAllocatorAllocate(NULL
, nElements
* sizeof(CFTypeRef
), 0);
225 CFArrayGetValues(cf
, CFRangeMake(0, nElements
), elements
);
226 for (i
= 0; i
< nElements
; i
++) {
227 CFMutableStringRef nPrefix1
;
228 CFMutableStringRef nPrefix2
;
232 nStr
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%u"), i
);
234 nPrefix1
= CFStringCreateMutable(NULL
, 0);
235 CFStringAppendFormat(nPrefix1
,
240 nPrefix2
= CFStringCreateMutable(NULL
, 0);
241 CFStringAppendFormat(nPrefix2
,
246 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX1"), nPrefix1
);
247 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX2"), nPrefix2
);
252 vStr
= _SCCopyDescription((CFTypeRef
)elements
[i
], nFormatOptions
);
253 CFStringAppendFormat(str
,
259 if (elements
!= elements_q
) CFAllocatorDeallocate(NULL
, elements
);
261 CFStringAppendFormat(str
, formatOptions
, CFSTR("\n%@}"), prefix2
);
263 CFRelease(nFormatOptions
);
267 if (type
== CFDictionaryGetTypeID()) {
268 const void * keys_q
[N_QUICK
];
269 const void ** keys
= keys_q
;
272 CFMutableStringRef nPrefix1
;
273 CFMutableStringRef nPrefix2
;
274 CFMutableStringRef str
;
276 str
= CFStringCreateMutable(NULL
, 0);
277 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<dictionary> {"), prefix1
);
279 nElements
= CFDictionaryGetCount(cf
);
281 CFMutableArrayRef sortedKeys
;
283 if (nElements
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
284 keys
= CFAllocatorAllocate(NULL
, nElements
* sizeof(CFTypeRef
), 0);
286 CFDictionaryGetKeysAndValues(cf
, keys
, NULL
);
288 sortedKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
289 for (i
= 0; i
< nElements
; i
++) {
290 CFArrayAppendValue(sortedKeys
, (CFStringRef
)keys
[i
]);
292 CFArraySortValues(sortedKeys
,
293 CFRangeMake(0, nElements
),
294 (CFComparatorFunction
)CFStringCompare
,
297 for (i
= 0; i
< nElements
; i
++) {
303 key
= CFArrayGetValueAtIndex(sortedKeys
, i
);
304 kStr
= _SCCopyDescription((CFTypeRef
)key
, NULL
);
306 nPrefix1
= CFStringCreateMutable(NULL
, 0);
307 CFStringAppendFormat(nPrefix1
,
312 nPrefix2
= CFStringCreateMutable(NULL
, 0);
313 CFStringAppendFormat(nPrefix2
,
318 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX1"), nPrefix1
);
319 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX2"), nPrefix2
);
324 val
= CFDictionaryGetValue(cf
, key
);
325 vStr
= _SCCopyDescription((CFTypeRef
)val
, nFormatOptions
);
326 CFStringAppendFormat(str
,
333 CFRelease(sortedKeys
);
335 if (keys
!= keys_q
) {
336 CFAllocatorDeallocate(NULL
, keys
);
339 CFStringAppendFormat(str
, formatOptions
, CFSTR("\n%@}"), prefix2
);
341 CFRelease(nFormatOptions
);
345 CFRelease(nFormatOptions
);
346 #endif /* ENABLE_SC_FORMATTING */
348 return CFStringCreateWithFormat(NULL
,
356 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
360 __SCLog(aslclient asl
, aslmsg msg
, int level
, CFStringRef formatString
, va_list formatArguments
)
367 __SCThreadSpecificDataRef tsd
;
369 tsd
= __SCGetThreadSpecificData();
370 if (tsd
->_asl
== NULL
) {
371 tsd
->_asl
= asl_open(NULL
, NULL
, 0);
372 asl_set_filter(tsd
->_asl
, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG
));
377 #ifdef ENABLE_SC_FORMATTING
378 str
= _CFStringCreateWithFormatAndArgumentsAux(NULL
,
383 #else /* ENABLE_SC_FORMATTING */
384 str
= CFStringCreateWithFormatAndArguments (NULL
,
388 #endif /* !ENABLE_SC_FORMATTING */
391 lines
= CFStringCreateArrayBySeparatingStrings(NULL
, str
, CFSTR("\n"));
394 int n
= CFArrayGetCount(lines
);
396 for (i
= 0; i
< n
; i
++) {
397 line
= CFStringCreateExternalRepresentation(NULL
,
398 CFArrayGetValueAtIndex(lines
, i
),
399 kCFStringEncodingUTF8
,
402 asl_log(asl
, msg
, level
, "%.*s", (int)CFDataGetLength(line
), CFDataGetBytePtr(line
));
409 line
= CFStringCreateExternalRepresentation(NULL
,
411 kCFStringEncodingUTF8
,
414 asl_log(asl
, msg
, ~level
, "%.*s", (int)CFDataGetLength(line
), CFDataGetBytePtr(line
));
425 __SCPrint(FILE *stream
, CFStringRef formatString
, va_list formatArguments
, Boolean trace
, Boolean addNL
)
430 #ifdef ENABLE_SC_FORMATTING
431 str
= _CFStringCreateWithFormatAndArgumentsAux(NULL
,
436 #else /* ENABLE_SC_FORMATTING */
437 str
= CFStringCreateWithFormatAndArguments (NULL
,
441 #endif /* !ENABLE_SC_FORMATTING */
443 line
= CFStringCreateExternalRepresentation(NULL
,
445 kCFStringEncodingUTF8
,
452 pthread_mutex_lock(&lock
);
455 struct timeval tv_now
;
457 (void)gettimeofday(&tv_now
, NULL
);
458 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
459 (void)fprintf(stream
, "%2d:%02d:%02d.%03d ",
460 tm_now
.tm_hour
, tm_now
.tm_min
, tm_now
.tm_sec
, tv_now
.tv_usec
/ 1000);
462 (void)fwrite((const void *)CFDataGetBytePtr(line
), (size_t)CFDataGetLength(line
), 1, stream
);
464 (void)fputc('\n', stream
);
467 pthread_mutex_unlock(&lock
);
475 SCLog(Boolean condition
, int level
, CFStringRef formatString
, ...)
477 va_list formatArguments
;
478 va_list formatArguments_print
;
480 Boolean print
= FALSE
;
487 * Note: The following are the expected values for _sc_log
489 * 0 if SC messages should be written to stdout/stderr
490 * 1 if SC messages should be logged w/asl(3)
491 * 2 if SC messages should be written to stdout/stderr AND logged
495 log
= TRUE
; // log requested
496 va_start(formatArguments
, formatString
);
499 print
= TRUE
; // log AND print requested
500 va_copy(formatArguments_print
, formatArguments
);
503 print
= TRUE
; // print requested
504 va_start(formatArguments_print
, formatString
);
508 __SCLog(NULL
, NULL
, level
, formatString
, formatArguments
);
509 va_end(formatArguments
);
513 __SCPrint((LOG_PRI(level
) > LOG_NOTICE
) ? stderr
: stdout
,
515 formatArguments_print
,
516 (_sc_log
> 0), // trace
517 TRUE
); // add newline
518 va_end(formatArguments_print
);
526 SCLOG(aslclient asl
, aslmsg msg
, int level
, CFStringRef formatString
, ...)
528 va_list formatArguments
;
529 va_list formatArguments_print
;
531 Boolean print
= FALSE
;
534 * Note: The following are the expected values for _sc_log
536 * 0 if SC messages should be written to stdout/stderr
537 * 1 if SC messages should be logged w/asl(3)
538 * 2 if SC messages should be written to stdout/stderr AND logged
542 log
= TRUE
; // log requested
543 va_start(formatArguments
, formatString
);
546 print
= TRUE
; // log AND print requested
547 va_copy(formatArguments_print
, formatArguments
);
550 print
= TRUE
; // print requested
551 va_start(formatArguments_print
, formatString
);
555 __SCLog(asl
, msg
, level
, formatString
, formatArguments
);
556 va_end(formatArguments
);
563 __SCPrint((level
> ASL_LEVEL_NOTICE
) ? stderr
: stdout
,
565 formatArguments_print
,
566 (_sc_log
> 0), // trace
567 TRUE
); // add newline
568 va_end(formatArguments_print
);
576 SCPrint(Boolean condition
, FILE *stream
, CFStringRef formatString
, ...)
578 va_list formatArguments
;
584 va_start(formatArguments
, formatString
);
585 __SCPrint(stream
, formatString
, formatArguments
, FALSE
, FALSE
);
586 va_end(formatArguments
);
593 SCTrace(Boolean condition
, FILE *stream
, CFStringRef formatString
, ...)
595 va_list formatArguments
;
601 va_start(formatArguments
, formatString
);
602 __SCPrint(stream
, formatString
, formatArguments
, TRUE
, FALSE
);
603 va_end(formatArguments
);
610 #pragma mark SC error handling / logging
613 const CFStringRef kCFErrorDomainSystemConfiguration
= CFSTR("com.apple.SystemConfiguration");
616 static const struct sc_errmsg
{
620 { kSCStatusAccessError
, "Permission denied" },
621 { kSCStatusConnectionNoService
, "Network service for connection not available" },
622 { kSCStatusFailed
, "Failed!" },
623 { kSCStatusInvalidArgument
, "Invalid argument" },
624 { kSCStatusKeyExists
, "Key already defined" },
625 { kSCStatusLocked
, "Lock already held" },
626 { kSCStatusMaxLink
, "Maximum link count exceeded" },
627 { kSCStatusNeedLock
, "Lock required for this operation" },
628 { kSCStatusNoStoreServer
, "Configuration daemon not (no longer) available" },
629 { kSCStatusNoStoreSession
, "Configuration daemon session not active" },
630 { kSCStatusNoConfigFile
, "Configuration file not found" },
631 { kSCStatusNoKey
, "No such key" },
632 { kSCStatusNoLink
, "No such link" },
633 { kSCStatusNoPrefsSession
, "Preference session not active" },
634 { kSCStatusNotifierActive
, "Notifier is currently active" },
635 { kSCStatusOK
, "Success!" },
636 { kSCStatusPrefsBusy
, "Preferences update currently in progress" },
637 { kSCStatusReachabilityUnknown
, "Network reachability cannot be determined" },
638 { kSCStatusStale
, "Write attempted on stale version of object" },
640 #define nSC_ERRMSGS (sizeof(sc_errmsgs)/sizeof(struct sc_errmsg))
644 _SCErrorSet(int error
)
646 __SCThreadSpecificDataRef tsd
;
648 tsd
= __SCGetThreadSpecificData();
649 tsd
->_sc_error
= error
;
655 SCCopyLastError(void)
661 __SCThreadSpecificDataRef tsd
;
662 CFMutableDictionaryRef userInfo
= NULL
;
664 tsd
= __SCGetThreadSpecificData();
665 code
=tsd
->_sc_error
;
667 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
668 if (sc_errmsgs
[i
].status
== code
) {
671 domain
= kCFErrorDomainSystemConfiguration
;
672 userInfo
= CFDictionaryCreateMutable(NULL
,
674 &kCFCopyStringDictionaryKeyCallBacks
,
675 &kCFTypeDictionaryValueCallBacks
);
676 str
= CFStringCreateWithCString(NULL
,
677 sc_errmsgs
[i
].message
,
678 kCFStringEncodingASCII
);
679 CFDictionarySetValue(userInfo
, kCFErrorDescriptionKey
, str
);
685 if ((code
> 0) && (code
<= ELAST
)) {
686 domain
= kCFErrorDomainPOSIX
;
690 domain
= kCFErrorDomainMach
;
694 error
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
695 if (userInfo
!= NULL
) CFRelease(userInfo
);
703 __SCThreadSpecificDataRef tsd
;
705 tsd
= __SCGetThreadSpecificData();
706 return tsd
->_sc_error
;
711 SCErrorString(int status
)
715 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
716 if (sc_errmsgs
[i
].status
== status
) {
717 return sc_errmsgs
[i
].message
;
721 if ((status
> 0) && (status
<= ELAST
)) {
722 return strerror(status
);
725 if ((status
>= BOOTSTRAP_SUCCESS
) && (status
<= BOOTSTRAP_NO_MEMORY
)) {
726 return bootstrap_strerror(status
);
729 return mach_error_string(status
);