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_print
= FALSE
;
590 Boolean do_syslog
= FALSE
;
592 if (_sc_log
> kSCLogDestinationFile
) {
593 if (_SC_isInstallEnvironment()) {
595 * os_log(3) messages are not persisted in the
596 * install environment. So, we use syslog(3)
602 if (_sc_log
>= kSCLogDestinationBoth
) {
603 do_print
= TRUE
; // log AND print requested
606 do_print
= TRUE
; // print requested
609 if (!do_print
&& !do_syslog
) {
610 // if only os_log requested
611 os_log_pack_send(pack
, log
, type
);
612 } else if (do_print
&& !do_syslog
) {
613 // if os_log and print requested
614 composed
= os_log_pack_send_and_compose(pack
, log
, type
, buffer
, sizeof(buffer
));
616 // if print-only and/or syslog requested
617 mach_get_times(NULL
, &pack
->olp_continuous_time
, &pack
->olp_wall_time
);
618 composed
= os_log_pack_compose(pack
, log
, type
, buffer
, sizeof(buffer
));
623 (level
< LOG_INFO
) || // print most messages
624 ((level
== LOG_INFO
) && _sc_verbose
) || // with _sc_verbose, include LOG_INFO
625 _sc_debug
// with _sc_debug, include LOG_DEBUG
629 pthread_mutex_lock(&lock
);
632 struct timeval tv_now
;
634 tv_now
.tv_sec
= (time_t)&pack
->olp_wall_time
.tv_sec
;
635 tv_now
.tv_usec
= (suseconds_t
)((uint64_t)&pack
->olp_wall_time
.tv_nsec
/ NSEC_PER_USEC
);
636 (void)localtime_r(&tv_now
.tv_sec
, &tm_now
);
637 (void)fprintf(stdout
, "%2d:%02d:%02d.%03d ",
638 tm_now
.tm_hour
, tm_now
.tm_min
, tm_now
.tm_sec
, tv_now
.tv_usec
/ 1000);
640 (void)fprintf(stdout
, "%s\n", composed
);
642 pthread_mutex_unlock(&lock
);
647 (level
< LOG_INFO
) ||
648 ((level
== LOG_INFO
) && _SC_isAppleInternal()) ||
652 // if [install/upgrade] syslog'ing
653 syslog(level
| LOG_INSTALL
, "%s", composed
);
656 if (composed
!= buffer_ptr
) {
665 SCLog(Boolean condition
, int level
, CFStringRef formatString
, ...)
667 va_list formatArguments
;
668 va_list formatArguments_print
;
670 Boolean print
= FALSE
;
677 * Note: The following are the expected values for _sc_log
679 * 0 if SC messages should be written to stdout/stderr
680 * 1 if SC messages should be logged w/os_log(3)
681 * 2 if SC messages should be written to stdout/stderr AND logged
682 * 3 if SC messages should be logged AND written to stdout/stderr (w/o timestamp)
685 if (_sc_log
> kSCLogDestinationFile
) {
686 log
= TRUE
; // log requested
687 va_start(formatArguments
, formatString
);
689 if (_sc_log
>= kSCLogDestinationBoth
) {
690 print
= TRUE
; // log AND print requested
691 va_copy(formatArguments_print
, formatArguments
);
694 print
= TRUE
; // print requested
695 va_start(formatArguments_print
, formatString
);
699 const char *__format
;
701 __format
= CFStringGetCStringPtr(formatString
, kCFStringEncodingUTF8
);
702 if (__format
!= NULL
) {
703 os_log_type_t __type
;
705 __type
= _SC_syslog_os_log_mapping(level
);
706 __SCLog(__builtin_return_address(0), __type
, __format
, formatArguments
);
708 va_end(formatArguments
);
712 __SCPrint((LOG_PRI(level
) > LOG_NOTICE
) ? stderr
: stdout
,
714 formatArguments_print
,
715 (_sc_log
== kSCLogDestinationBoth
), // trace
716 TRUE
); // add newline
717 va_end(formatArguments_print
);
725 SCPrint(Boolean condition
, FILE *stream
, CFStringRef formatString
, ...)
727 va_list formatArguments
;
733 va_start(formatArguments
, formatString
);
734 __SCPrint(stream
, formatString
, formatArguments
, FALSE
, FALSE
);
735 va_end(formatArguments
);
742 #pragma mark SC error handling / logging
745 const CFStringRef kCFErrorDomainSystemConfiguration
= CFSTR("com.apple.SystemConfiguration");
748 static const struct sc_errmsg
{
752 { kSCStatusAccessError
, "Permission denied" },
753 { kSCStatusConnectionIgnore
, "Network connection information not available at this time" },
754 { kSCStatusConnectionNoService
, "Network service for connection not available" },
755 { kSCStatusFailed
, "Failed!" },
756 { kSCStatusInvalidArgument
, "Invalid argument" },
757 { kSCStatusKeyExists
, "Key already defined" },
758 { kSCStatusLocked
, "Lock already held" },
759 { kSCStatusMaxLink
, "Maximum link count exceeded" },
760 { kSCStatusNeedLock
, "Lock required for this operation" },
761 { kSCStatusNoStoreServer
, "Configuration daemon not (no longer) available" },
762 { kSCStatusNoStoreSession
, "Configuration daemon session not active" },
763 { kSCStatusNoConfigFile
, "Configuration file not found" },
764 { kSCStatusNoKey
, "No such key" },
765 { kSCStatusNoLink
, "No such link" },
766 { kSCStatusNoPrefsSession
, "Preference session not active" },
767 { kSCStatusNotifierActive
, "Notifier is currently active" },
768 { kSCStatusOK
, "Success!" },
769 { kSCStatusPrefsBusy
, "Preferences update currently in progress" },
770 { kSCStatusReachabilityUnknown
, "Network reachability cannot be determined" },
771 { kSCStatusStale
, "Write attempted on stale version of object" },
773 #define nSC_ERRMSGS (sizeof(sc_errmsgs)/sizeof(struct sc_errmsg))
776 _SCErrorSet(int error
)
778 __SCThreadSpecificDataRef tsd
;
780 tsd
= __SCGetThreadSpecificData();
781 tsd
->_sc_error
= error
;
787 SCCopyLastError(void)
793 __SCThreadSpecificDataRef tsd
;
794 CFMutableDictionaryRef userInfo
= NULL
;
796 tsd
= __SCGetThreadSpecificData();
797 code
=tsd
->_sc_error
;
799 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
800 if (sc_errmsgs
[i
].status
== code
) {
803 domain
= kCFErrorDomainSystemConfiguration
;
804 userInfo
= CFDictionaryCreateMutable(NULL
,
806 &kCFCopyStringDictionaryKeyCallBacks
,
807 &kCFTypeDictionaryValueCallBacks
);
808 str
= CFStringCreateWithCString(NULL
,
809 sc_errmsgs
[i
].message
,
810 kCFStringEncodingASCII
);
811 CFDictionarySetValue(userInfo
, kCFErrorDescriptionKey
, str
);
817 if ((code
> 0) && (code
<= ELAST
)) {
818 domain
= kCFErrorDomainPOSIX
;
822 domain
= kCFErrorDomainMach
;
826 error
= CFErrorCreate(NULL
, domain
, code
, userInfo
);
827 if (userInfo
!= NULL
) CFRelease(userInfo
);
835 __SCThreadSpecificDataRef tsd
;
837 tsd
= __SCGetThreadSpecificData();
838 return tsd
->_sc_error
;
843 SCErrorString(int status
)
847 for (i
= 0; i
< (int)nSC_ERRMSGS
; i
++) {
848 if (sc_errmsgs
[i
].status
== status
) {
849 return sc_errmsgs
[i
].message
;
853 if ((status
> 0) && (status
<= ELAST
)) {
854 return strerror(status
);
857 if ((status
>= BOOTSTRAP_SUCCESS
) && (status
<= BOOTSTRAP_NO_MEMORY
)) {
858 return bootstrap_strerror(status
);
861 return mach_error_string(status
);