2 * Copyright (c) 2000-2008, 2010-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 <mach/mach.h>
35 #include <mach/mach_time.h>
36 #include <mach/mach_error.h>
37 #include <servers/bootstrap.h>
41 #define OS_LOG_PACK_SPI
43 #include <os/log_private.h>
45 #include "SCDynamicStoreInternal.h"
47 #include "config.h" /* MiG generated file */
49 #define INSTALL_ENVIRONMENT "__OSINSTALL_ENVIRONMENT"
51 /* framework variables */
52 int _sc_debug
= FALSE
; /* non-zero if debugging enabled */
53 int _sc_verbose
= FALSE
; /* non-zero if verbose logging enabled */
54 _SCLogDestination _sc_log
= kSCLogDestinationDefault
;
58 #pragma mark Thread specific data
61 static pthread_once_t tsKeyInitialized
= PTHREAD_ONCE_INIT
;
62 static pthread_key_t tsDataKey
;
66 __SCThreadSpecificDataFinalize(void *arg
)
68 __SCThreadSpecificDataRef tsd
= (__SCThreadSpecificDataRef
)arg
;
71 if (tsd
->_sc_store
!= NULL
) CFRelease(tsd
->_sc_store
);
72 CFAllocatorDeallocate(kCFAllocatorSystemDefault
, tsd
);
79 __SCThreadSpecificKeyInitialize()
81 pthread_key_create(&tsDataKey
, __SCThreadSpecificDataFinalize
);
87 __SCThreadSpecificDataRef
88 __SCGetThreadSpecificData()
90 __SCThreadSpecificDataRef tsd
;
91 pthread_once(&tsKeyInitialized
, __SCThreadSpecificKeyInitialize
);
93 tsd
= pthread_getspecific(tsDataKey
);
95 tsd
= CFAllocatorAllocate(kCFAllocatorSystemDefault
, sizeof(__SCThreadSpecificData
), 0);
96 tsd
->_sc_error
= kSCStatusOK
;
97 tsd
->_sc_store
= NULL
;
98 pthread_setspecific(tsDataKey
, tsd
);
109 #define ENABLE_SC_FORMATTING
110 #ifdef ENABLE_SC_FORMATTING
111 // from <CoreFoundation/ForFoundationOnly.h>
112 extern CFStringRef
_CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc
, CFStringRef (*copyDescFunc
)(CFTypeRef
, CFDictionaryRef
), CFDictionaryRef formatOptions
, CFStringRef format
, va_list arguments
);
113 #endif /* ENABLE_SC_FORMATTING */
117 _SCCopyDescription(CFTypeRef cf
, CFDictionaryRef formatOptions
)
119 #ifdef ENABLE_SC_FORMATTING
120 CFMutableDictionaryRef nFormatOptions
;
123 CFTypeID type
= CFGetTypeID(cf
);
125 if (!formatOptions
||
126 !CFDictionaryGetValueIfPresent(formatOptions
, CFSTR("PREFIX1"), (const void **)&prefix1
)) {
130 if (type
== CFStringGetTypeID()) {
131 return CFStringCreateWithFormat(NULL
,
138 if (type
== CFBooleanGetTypeID()) {
139 return CFStringCreateWithFormat(NULL
,
143 CFBooleanGetValue(cf
) ? "TRUE" : "FALSE");
146 if (type
== CFDataGetTypeID()) {
150 CFMutableStringRef str
;
152 str
= CFStringCreateMutable(NULL
, 0);
153 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<data> 0x"), prefix1
);
155 data
= CFDataGetBytePtr(cf
);
156 dataLen
= CFDataGetLength(cf
);
157 for (i
= 0; i
< dataLen
; i
++) {
158 CFStringAppendFormat(str
, NULL
, CFSTR("%02x"), data
[i
]);
164 if (type
== CFNumberGetTypeID()) {
165 return CFStringCreateWithFormat(NULL
,
172 if (type
== CFDateGetTypeID()) {
173 CFCalendarRef calendar
;
176 int MM
, DD
, YYYY
, hh
, mm
, ss
;
178 calendar
= CFCalendarCreateWithIdentifier(NULL
, kCFGregorianCalendar
);
179 tz
= CFTimeZoneCopySystem();
180 CFCalendarSetTimeZone(calendar
, tz
);
182 CFCalendarDecomposeAbsoluteTime(calendar
,
183 CFDateGetAbsoluteTime(cf
),
185 &MM
, &DD
, &YYYY
, &hh
, &mm
, &ss
);
188 str
= CFStringCreateWithFormat(NULL
,
190 CFSTR("%@%02d/%02d/%04d %02d:%02d:%02d"),
192 MM
, DD
, YYYY
, hh
, mm
, ss
);
196 if ((formatOptions
== NULL
) ||
197 !CFDictionaryGetValueIfPresent(formatOptions
, CFSTR("PREFIX2"), (const void **)&prefix2
)) {
201 if (formatOptions
!= NULL
) {
202 nFormatOptions
= CFDictionaryCreateMutableCopy(NULL
, 0, formatOptions
);
204 nFormatOptions
= CFDictionaryCreateMutable(NULL
,
206 &kCFTypeDictionaryKeyCallBacks
,
207 &kCFTypeDictionaryValueCallBacks
);
209 assert(nFormatOptions
!= NULL
);
213 if (type
== CFArrayGetTypeID()) {
214 const void * elements_q
[N_QUICK
];
215 const void ** elements
= elements_q
;
218 CFMutableStringRef str
;
220 str
= CFStringCreateMutable(NULL
, 0);
221 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<array> {"), prefix1
);
223 nElements
= CFArrayGetCount(cf
);
225 if (nElements
> (CFIndex
)(sizeof(elements_q
)/sizeof(CFTypeRef
)))
226 elements
= CFAllocatorAllocate(NULL
, nElements
* sizeof(CFTypeRef
), 0);
227 CFArrayGetValues(cf
, CFRangeMake(0, nElements
), elements
);
228 for (i
= 0; i
< nElements
; i
++) {
229 CFMutableStringRef nPrefix1
;
230 CFMutableStringRef nPrefix2
;
234 nStr
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%ld"), i
);
236 nPrefix1
= CFStringCreateMutable(NULL
, 0);
237 CFStringAppendFormat(nPrefix1
,
242 nPrefix2
= CFStringCreateMutable(NULL
, 0);
243 CFStringAppendFormat(nPrefix2
,
248 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX1"), nPrefix1
);
249 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX2"), nPrefix2
);
254 vStr
= _SCCopyDescription((CFTypeRef
)elements
[i
], nFormatOptions
);
255 CFStringAppendFormat(str
,
261 if (elements
!= elements_q
) CFAllocatorDeallocate(NULL
, elements
);
263 CFStringAppendFormat(str
, formatOptions
, CFSTR("\n%@}"), prefix2
);
265 CFRelease(nFormatOptions
);
269 if (type
== CFDictionaryGetTypeID()) {
270 const void * keys_q
[N_QUICK
];
271 const void ** keys
= keys_q
;
274 CFMutableStringRef nPrefix1
;
275 CFMutableStringRef nPrefix2
;
276 CFMutableStringRef str
;
278 str
= CFStringCreateMutable(NULL
, 0);
279 CFStringAppendFormat(str
, formatOptions
, CFSTR("%@<dictionary> {"), prefix1
);
281 nElements
= CFDictionaryGetCount(cf
);
283 CFComparatorFunction compFunc
= NULL
;
284 CFMutableArrayRef sortedKeys
;
286 if (nElements
> (CFIndex
)(sizeof(keys_q
) / sizeof(CFTypeRef
))) {
287 keys
= CFAllocatorAllocate(NULL
, nElements
* sizeof(CFTypeRef
), 0);
289 CFDictionaryGetKeysAndValues(cf
, keys
, NULL
);
291 sortedKeys
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
292 for (i
= 0; i
< nElements
; i
++) {
293 CFArrayAppendValue(sortedKeys
, (CFStringRef
)keys
[i
]);
296 if (isA_CFString(keys
[0])) {
297 compFunc
= (CFComparatorFunction
)CFStringCompare
;
299 else if (isA_CFNumber(keys
[0])) {
300 compFunc
= (CFComparatorFunction
)CFNumberCompare
;
302 else if (isA_CFDate(keys
[0])) {
303 compFunc
= (CFComparatorFunction
)CFDateCompare
;
306 if (compFunc
!= NULL
) {
307 CFArraySortValues(sortedKeys
,
308 CFRangeMake(0, nElements
),
313 for (i
= 0; i
< nElements
; i
++) {
319 key
= CFArrayGetValueAtIndex(sortedKeys
, i
);
320 kStr
= _SCCopyDescription((CFTypeRef
)key
, NULL
);
322 nPrefix1
= CFStringCreateMutable(NULL
, 0);
323 CFStringAppendFormat(nPrefix1
,
328 nPrefix2
= CFStringCreateMutable(NULL
, 0);
329 CFStringAppendFormat(nPrefix2
,
334 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX1"), nPrefix1
);
335 CFDictionarySetValue(nFormatOptions
, CFSTR("PREFIX2"), nPrefix2
);
340 val
= CFDictionaryGetValue(cf
, key
);
341 vStr
= _SCCopyDescription((CFTypeRef
)val
, nFormatOptions
);
342 CFStringAppendFormat(str
,
349 CFRelease(sortedKeys
);
351 if (keys
!= keys_q
) {
352 CFAllocatorDeallocate(NULL
, keys
);
355 CFStringAppendFormat(str
, formatOptions
, CFSTR("\n%@}"), prefix2
);
357 CFRelease(nFormatOptions
);
361 CFRelease(nFormatOptions
);
362 #endif /* ENABLE_SC_FORMATTING */
364 return CFStringCreateWithFormat(NULL
,
372 static pthread_mutex_t lock
= PTHREAD_MUTEX_INITIALIZER
;
375 _SC_isInstallEnvironment() {
376 static dispatch_once_t once
;
377 static Boolean is_install
;
379 dispatch_once(&once
, ^{
380 is_install
= (getenv(INSTALL_ENVIRONMENT
) != NULL
);
388 _SC_LOG_DEFAULT(void)
390 static os_log_t log
= NULL
;
393 log
= os_log_create("com.apple.SystemConfiguration", "");
401 _SC_syslog_os_log_mapping(int level
)
411 return OS_LOG_TYPE_ERROR
;
416 return OS_LOG_TYPE_DEFAULT
;
419 return OS_LOG_TYPE_INFO
;
422 return OS_LOG_TYPE_DEBUG
;
425 return OS_LOG_TYPE_DEFAULT
;
429 __SCLog(void *ret_addr
, os_log_type_t type
, const char *formatString
, va_list formatArguments
)
431 os_log_with_args(_SC_LOG_DEFAULT(),
441 __SCPrint(FILE *stream
, CFStringRef formatString
, va_list formatArguments
, Boolean addTime
, Boolean addNL
)
447 #ifdef ENABLE_SC_FORMATTING
448 str
= _CFStringCreateWithFormatAndArgumentsAux(NULL
,
453 #else /* ENABLE_SC_FORMATTING */
454 str
= CFStringCreateWithFormatAndArguments (NULL
,
458 #endif /* !ENABLE_SC_FORMATTING */
460 line
=_SC_cfstring_to_cstring_ext(str
,
463 kCFStringEncodingUTF8
,
471 pthread_mutex_lock(&lock
);
474 struct timeval tv_now
;
476 (void)gettimeofday(&tv_now
, NULL
);
477 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
478 (void)fprintf(stream
, "%2d:%02d:%02d.%03d ",
479 tm_now
.tm_hour
, tm_now
.tm_min
, tm_now
.tm_sec
, tv_now
.tv_usec
/ 1000);
481 (void)fwrite((const void *)line
, usedBufLen
, 1, stream
);
483 (void)fputc('\n', stream
);
486 pthread_mutex_unlock(&lock
);
487 CFAllocatorDeallocate(NULL
, line
);
494 * NOTE: We need to keep this function in place (for a least a while) to ensure
495 * that any [old] code that was using an earlier version of SC_log() will
496 * have the needed support code to perform the actual logging. Newly
497 * compiled code uses the new/replacement _SC_log_send() function.
500 __SC_Log(int level
, CFStringRef format_CF
, os_log_t log
, os_log_type_t type
, const char *format
, ...)
502 #pragma unused(level)
503 Boolean do_log
= FALSE
;
504 Boolean do_print
= FALSE
;
508 if (_sc_log
> kSCLogDestinationFile
) {
509 do_log
= TRUE
; // log requested
510 va_start(args_log
, format
);
512 if (_sc_log
>= kSCLogDestinationBoth
) {
513 do_print
= TRUE
; // log AND print requested
514 va_copy(args_print
, args_log
);
517 do_print
= TRUE
; // print requested
518 va_start(args_print
, format
);
522 os_log_with_args(log
,
526 __builtin_return_address(0));
534 (_sc_log
== kSCLogDestinationBoth
), // trace
535 TRUE
); // add newline
544 __SC_log_enabled(int level
, os_log_t log
, os_log_type_t type
)
546 if (os_log_type_enabled(log
, type
)) {
550 if (_sc_log
!= kSCLogDestinationDefault
) {
551 // if os_log'ing not enabled and the messages is targeted to stdout/stderr
552 if (level
< LOG_INFO
) {
553 // if not LOG_INFO/LOG_DEBUG message, print
555 } else if ((level
== LOG_INFO
) && _sc_verbose
) {
556 // if LOG_INFO and _sc_verbose, print
558 } else if (_sc_debug
) {
559 // if _sc_debug, print
564 if (_SC_isInstallEnvironment()) {
565 // if OSInstaller environment
566 if (level
< LOG_INFO
) {
567 // if not LOG_INFO/LOG_DEBUG message, syslog
569 } else if ((level
== LOG_INFO
) && _SC_isAppleInternal()) {
570 // if LOG_INFO and internal, syslog
572 } else if (_sc_debug
) {
573 // if _sc_debug, syslog
583 __SC_log_send(int level
, os_log_t log
, os_log_type_t type
, os_log_pack_t pack
)
585 Boolean addTime
= (_sc_log
== kSCLogDestinationBoth
);
587 const char *buffer_ptr
= buffer
;
588 char *composed
= NULL
;
589 Boolean do_log
= FALSE
;
590 Boolean do_print
= FALSE
;
591 Boolean do_syslog
= FALSE
;
593 if (_sc_log
> kSCLogDestinationFile
) {
596 if (_SC_isInstallEnvironment()) {
598 * os_log(3) messages are not persisted in the
599 * install environment. But, the installer does
600 * capture syslog(3) messages.
605 if (_sc_log
>= kSCLogDestinationBoth
) {
606 do_print
= TRUE
; // log AND print requested
609 do_print
= TRUE
; // print requested
613 if (!do_print
&& !do_syslog
) {
614 // if only os_log requested
615 os_log_pack_send(pack
, log
, type
);
617 // if os_log and print (or syslog) requested
618 composed
= os_log_pack_send_and_compose(pack
, log
, type
, buffer
, sizeof(buffer
));
621 // if print-only requested
622 mach_get_times(NULL
, &pack
->olp_continuous_time
, &pack
->olp_wall_time
);
623 composed
= os_log_pack_compose(pack
, log
, type
, buffer
, sizeof(buffer
));
628 (level
< LOG_INFO
) || // print most messages
629 ((level
== LOG_INFO
) && _sc_verbose
) || // with _sc_verbose, include LOG_INFO
630 _sc_debug
// with _sc_debug, include LOG_DEBUG
634 pthread_mutex_lock(&lock
);
637 struct timeval tv_now
;
639 tv_now
.tv_sec
= (time_t)&pack
->olp_wall_time
.tv_sec
;
640 tv_now
.tv_usec
= (suseconds_t
)((uint64_t)&pack
->olp_wall_time
.tv_nsec
/ NSEC_PER_USEC
);
641 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
642 (void)fprintf(stdout
, "%2d:%02d:%02d.%03d ",
643 tm_now
.tm_hour
, tm_now
.tm_min
, tm_now
.tm_sec
, tv_now
.tv_usec
/ 1000);
645 (void)fprintf(stdout
, "%s\n", composed
);
647 pthread_mutex_unlock(&lock
);
652 (level
< LOG_INFO
) ||
653 ((level
== LOG_INFO
) && _SC_isAppleInternal()) ||
657 // if [install/upgrade] syslog'ing
658 syslog(level
| LOG_INSTALL
, "%s", composed
);
661 if (composed
!= buffer_ptr
) {
670 SCLog(Boolean condition
, int level
, CFStringRef formatString
, ...)
672 va_list formatArguments
;
673 va_list formatArguments_print
;
675 Boolean print
= FALSE
;
682 * Note: The following are the expected values for _sc_log
684 * 0 if SC messages should be written to stdout/stderr
685 * 1 if SC messages should be logged w/os_log(3)
686 * 2 if SC messages should be written to stdout/stderr AND logged
687 * 3 if SC messages should be logged AND written to stdout/stderr (w/o timestamp)
690 if (_sc_log
> kSCLogDestinationFile
) {
691 log
= TRUE
; // log requested
692 va_start(formatArguments
, formatString
);
694 if (_sc_log
>= kSCLogDestinationBoth
) {
695 print
= TRUE
; // log AND print requested
696 va_copy(formatArguments_print
, formatArguments
);
699 print
= TRUE
; // print requested
700 va_start(formatArguments_print
, formatString
);
704 const char *__format
;
706 __format
= CFStringGetCStringPtr(formatString
, kCFStringEncodingUTF8
);
707 if (__format
!= NULL
) {
708 os_log_type_t __type
;
710 __type
= _SC_syslog_os_log_mapping(level
);
711 __SCLog(__builtin_return_address(0), __type
, __format
, formatArguments
);
713 va_end(formatArguments
);
717 __SCPrint((LOG_PRI(level
) > LOG_NOTICE
) ? stderr
: stdout
,
719 formatArguments_print
,
720 (_sc_log
== kSCLogDestinationBoth
), // trace
721 TRUE
); // add newline
722 va_end(formatArguments_print
);
730 SCPrint(Boolean condition
, FILE *stream
, CFStringRef formatString
, ...)
732 va_list formatArguments
;
738 va_start(formatArguments
, formatString
);
739 __SCPrint(stream
, formatString
, formatArguments
, FALSE
, FALSE
);
740 va_end(formatArguments
);
747 #pragma mark SC error handling / logging
750 const CFStringRef kCFErrorDomainSystemConfiguration
= CFSTR("com.apple.SystemConfiguration");
753 static const struct sc_errmsg
{
757 { kSCStatusAccessError
, "Permission denied" },
758 { kSCStatusConnectionIgnore
, "Network connection information not available at this time" },
759 { kSCStatusConnectionNoService
, "Network service for connection not available" },
760 { kSCStatusFailed
, "Failed!" },
761 { kSCStatusInvalidArgument
, "Invalid argument" },
762 { kSCStatusKeyExists
, "Key already defined" },
763 { kSCStatusLocked
, "Lock already held" },
764 { kSCStatusMaxLink
, "Maximum link count exceeded" },
765 { kSCStatusNeedLock
, "Lock required for this operation" },
766 { kSCStatusNoStoreServer
, "Configuration daemon not (no longer) available" },
767 { kSCStatusNoStoreSession
, "Configuration daemon session not active" },
768 { kSCStatusNoConfigFile
, "Configuration file not found" },
769 { kSCStatusNoKey
, "No such key" },
770 { kSCStatusNoLink
, "No such link" },
771 { kSCStatusNoPrefsSession
, "Preference session not active" },
772 { kSCStatusNotifierActive
, "Notifier is currently active" },
773 { kSCStatusOK
, "Success!" },
774 { kSCStatusPrefsBusy
, "Preferences update currently in progress" },
775 { kSCStatusReachabilityUnknown
, "Network reachability cannot be determined" },
776 { kSCStatusStale
, "Write attempted on stale version of object" },
778 #define nSC_ERRMSGS (sizeof(sc_errmsgs)/sizeof(struct sc_errmsg))
781 _SCErrorSet(int error
)
783 __SCThreadSpecificDataRef tsd
;
785 tsd
= __SCGetThreadSpecificData();
786 tsd
->_sc_error
= error
;
792 SCCopyLastError(void)
798 __SCThreadSpecificDataRef tsd
;
799 CFMutableDictionaryRef userInfo
= NULL
;
801 tsd
= __SCGetThreadSpecificData();
802 code
=tsd
->_sc_error
;
804 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
805 if (sc_errmsgs
[i
].status
== code
) {
808 domain
= kCFErrorDomainSystemConfiguration
;
809 userInfo
= CFDictionaryCreateMutable(NULL
,
811 &kCFCopyStringDictionaryKeyCallBacks
,
812 &kCFTypeDictionaryValueCallBacks
);
813 str
= CFStringCreateWithCString(NULL
,
814 sc_errmsgs
[i
].message
,
815 kCFStringEncodingASCII
);
816 CFDictionarySetValue(userInfo
, kCFErrorDescriptionKey
, str
);
822 if ((code
> 0) && (code
<= ELAST
)) {
823 domain
= kCFErrorDomainPOSIX
;
827 domain
= kCFErrorDomainMach
;
831 error
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
832 if (userInfo
!= NULL
) CFRelease(userInfo
);
840 __SCThreadSpecificDataRef tsd
;
842 tsd
= __SCGetThreadSpecificData();
843 return tsd
->_sc_error
;
848 SCErrorString(int status
)
852 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
853 if (sc_errmsgs
[i
].status
== status
) {
854 return sc_errmsgs
[i
].message
;
858 if ((status
> 0) && (status
<= ELAST
)) {
859 return strerror(status
);
862 if ((status
>= BOOTSTRAP_SUCCESS
) && (status
<= BOOTSTRAP_NO_MEMORY
)) {
863 return bootstrap_strerror(status
);
866 return mach_error_string(status
);