]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCD.c
c9dfe7d060b172eb558db3264f55f815bba22575
[apple/configd.git] / SystemConfiguration.fproj / SCD.c
1 /*
2 * Copyright (c) 2000-2008, 2010-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 /*
25 * Modification History
26 *
27 * June 1, 2001 Allan Nathanson <ajn@apple.com>
28 * - public API conversion
29 *
30 * March 24, 2000 Allan Nathanson <ajn@apple.com>
31 * - initial revision
32 */
33
34 #include <mach/mach.h>
35 #include <mach/mach_error.h>
36 #include <servers/bootstrap.h>
37 #include <asl.h>
38 #include <pthread.h>
39 #include <sys/time.h>
40 #include <os/log.h>
41 #include <os/log_private.h>
42
43 #include "SCDynamicStoreInternal.h"
44 #include "SCD.h"
45 #include "config.h" /* MiG generated file */
46
47 // asl logging
48 #define INSTALL_FACILITY "install"
49 #define INSTALL_ENVIRONMENT "__OSINSTALL_ENVIRONMENT"
50
51 // LIBASL SPI
52 extern asl_object_t _asl_server_control_query(void);
53
54 /* framework variables */
55 int _sc_debug = FALSE; /* non-zero if debugging enabled */
56 int _sc_verbose = FALSE; /* non-zero if verbose logging enabled */
57 int _sc_log = TRUE; /* 0 if SC messages should be written to stdout/stderr,
58 1 if SC messages should be logged w/asl(3),
59 2 if SC messages should be written to stdout/stderr AND logged */
60
61
62 #pragma mark -
63 #pragma mark Thread specific data
64
65
66 static pthread_once_t tsKeyInitialized = PTHREAD_ONCE_INIT;
67 static pthread_key_t tsDataKey;
68
69
70 static void
71 __SCThreadSpecificDataFinalize(void *arg)
72 {
73 __SCThreadSpecificDataRef tsd = (__SCThreadSpecificDataRef)arg;
74
75 if (tsd != NULL) {
76 if (tsd->_asl != NULL) asl_release(tsd->_asl);
77 if (tsd->_sc_store != NULL) CFRelease(tsd->_sc_store);
78 CFAllocatorDeallocate(kCFAllocatorSystemDefault, tsd);
79 }
80 return;
81 }
82
83
84 static void
85 __SCThreadSpecificKeyInitialize()
86 {
87 pthread_key_create(&tsDataKey, __SCThreadSpecificDataFinalize);
88 return;
89 }
90
91
92 __private_extern__
93 __SCThreadSpecificDataRef
94 __SCGetThreadSpecificData()
95 {
96 __SCThreadSpecificDataRef tsd;
97 pthread_once(&tsKeyInitialized, __SCThreadSpecificKeyInitialize);
98
99 tsd = pthread_getspecific(tsDataKey);
100 if (tsd == NULL) {
101 tsd = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__SCThreadSpecificData), 0);
102 tsd->_asl = NULL;
103 tsd->_sc_error = kSCStatusOK;
104 tsd->_sc_store = NULL;
105 pthread_setspecific(tsDataKey, tsd);
106 }
107
108 return tsd;
109 }
110
111
112 #pragma mark -
113 #pragma mark Logging
114
115
116 #define kASLModule "ASLModule"
117 #define kASLOption "ASLOption"
118 #define kLoggerID "LoggerID"
119
120 #define ENABLE_SC_FORMATTING
121 #ifdef ENABLE_SC_FORMATTING
122 // from <CoreFoundation/ForFoundationOnly.h>
123 extern CFStringRef _CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(CFTypeRef, CFDictionaryRef), CFDictionaryRef formatOptions, CFStringRef format, va_list arguments);
124 #endif /* ENABLE_SC_FORMATTING */
125
126
127 CFStringRef
128 _SCCopyDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
129 {
130 #ifdef ENABLE_SC_FORMATTING
131 CFMutableDictionaryRef nFormatOptions;
132 CFStringRef prefix1;
133 CFStringRef prefix2;
134 CFTypeID type = CFGetTypeID(cf);
135
136 if (!formatOptions ||
137 !CFDictionaryGetValueIfPresent(formatOptions, CFSTR("PREFIX1"), (const void **)&prefix1)) {
138 prefix1 = CFSTR("");
139 }
140
141 if (type == CFStringGetTypeID()) {
142 return CFStringCreateWithFormat(NULL,
143 formatOptions,
144 CFSTR("%@%@"),
145 prefix1,
146 cf);
147 }
148
149 if (type == CFBooleanGetTypeID()) {
150 return CFStringCreateWithFormat(NULL,
151 formatOptions,
152 CFSTR("%@%s"),
153 prefix1,
154 CFBooleanGetValue(cf) ? "TRUE" : "FALSE");
155 }
156
157 if (type == CFDataGetTypeID()) {
158 const uint8_t *data;
159 CFIndex dataLen;
160 CFIndex i;
161 CFMutableStringRef str;
162
163 str = CFStringCreateMutable(NULL, 0);
164 CFStringAppendFormat(str, formatOptions, CFSTR("%@<data> 0x"), prefix1);
165
166 data = CFDataGetBytePtr(cf);
167 dataLen = CFDataGetLength(cf);
168 for (i = 0; i < dataLen; i++) {
169 CFStringAppendFormat(str, NULL, CFSTR("%02x"), data[i]);
170 }
171
172 return str;
173 }
174
175 if (type == CFNumberGetTypeID()) {
176 return CFStringCreateWithFormat(NULL,
177 formatOptions,
178 CFSTR("%@%@"),
179 prefix1,
180 cf);
181 }
182
183 if (type == CFDateGetTypeID()) {
184 CFCalendarRef calendar;
185 CFStringRef str;
186 CFTimeZoneRef tz;
187 int MM, DD, YYYY, hh, mm, ss;
188
189 calendar = CFCalendarCreateWithIdentifier(NULL, kCFGregorianCalendar);
190 tz = CFTimeZoneCopySystem();
191 CFCalendarSetTimeZone(calendar, tz);
192 CFRelease(tz);
193 CFCalendarDecomposeAbsoluteTime(calendar,
194 CFDateGetAbsoluteTime(cf),
195 "MdyHms",
196 &MM, &DD, &YYYY, &hh, &mm, &ss);
197 CFRelease(calendar);
198
199 str = CFStringCreateWithFormat(NULL,
200 formatOptions,
201 CFSTR("%@%02d/%02d/%04d %02d:%02d:%02d"),
202 prefix1,
203 MM, DD, YYYY, hh, mm, ss);
204 return str;
205 }
206
207 if ((formatOptions == NULL) ||
208 !CFDictionaryGetValueIfPresent(formatOptions, CFSTR("PREFIX2"), (const void **)&prefix2)) {
209 prefix2 = prefix1;
210 }
211
212 if (formatOptions != NULL) {
213 nFormatOptions = CFDictionaryCreateMutableCopy(NULL, 0, formatOptions);
214 } else {
215 nFormatOptions = CFDictionaryCreateMutable(NULL,
216 0,
217 &kCFTypeDictionaryKeyCallBacks,
218 &kCFTypeDictionaryValueCallBacks);
219 }
220 assert(nFormatOptions != NULL);
221
222 #define N_QUICK 32
223
224 if (type == CFArrayGetTypeID()) {
225 const void * elements_q[N_QUICK];
226 const void ** elements = elements_q;
227 CFIndex i;
228 CFIndex nElements;
229 CFMutableStringRef str;
230
231 str = CFStringCreateMutable(NULL, 0);
232 CFStringAppendFormat(str, formatOptions, CFSTR("%@<array> {"), prefix1);
233
234 nElements = CFArrayGetCount(cf);
235 if (nElements > 0) {
236 if (nElements > (CFIndex)(sizeof(elements_q)/sizeof(CFTypeRef)))
237 elements = CFAllocatorAllocate(NULL, nElements * sizeof(CFTypeRef), 0);
238 CFArrayGetValues(cf, CFRangeMake(0, nElements), elements);
239 for (i = 0; i < nElements; i++) {
240 CFMutableStringRef nPrefix1;
241 CFMutableStringRef nPrefix2;
242 CFStringRef nStr;
243 CFStringRef vStr;
244
245 nStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), i);
246
247 nPrefix1 = CFStringCreateMutable(NULL, 0);
248 CFStringAppendFormat(nPrefix1,
249 formatOptions,
250 CFSTR("%@ %@ : "),
251 prefix2,
252 nStr);
253 nPrefix2 = CFStringCreateMutable(NULL, 0);
254 CFStringAppendFormat(nPrefix2,
255 formatOptions,
256 CFSTR("%@ "),
257 prefix2);
258
259 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX1"), nPrefix1);
260 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX2"), nPrefix2);
261 CFRelease(nPrefix1);
262 CFRelease(nPrefix2);
263 CFRelease(nStr);
264
265 vStr = _SCCopyDescription((CFTypeRef)elements[i], nFormatOptions);
266 CFStringAppendFormat(str,
267 formatOptions,
268 CFSTR("\n%@"),
269 vStr);
270 CFRelease(vStr);
271 }
272 if (elements != elements_q) CFAllocatorDeallocate(NULL, elements);
273 }
274 CFStringAppendFormat(str, formatOptions, CFSTR("\n%@}"), prefix2);
275
276 CFRelease(nFormatOptions);
277 return str;
278 }
279
280 if (type == CFDictionaryGetTypeID()) {
281 const void * keys_q[N_QUICK];
282 const void ** keys = keys_q;
283 CFIndex i;
284 CFIndex nElements;
285 CFMutableStringRef nPrefix1;
286 CFMutableStringRef nPrefix2;
287 CFMutableStringRef str;
288
289 str = CFStringCreateMutable(NULL, 0);
290 CFStringAppendFormat(str, formatOptions, CFSTR("%@<dictionary> {"), prefix1);
291
292 nElements = CFDictionaryGetCount(cf);
293 if (nElements > 0) {
294 CFComparatorFunction compFunc = NULL;
295 CFMutableArrayRef sortedKeys;
296
297 if (nElements > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
298 keys = CFAllocatorAllocate(NULL, nElements * sizeof(CFTypeRef), 0);
299 }
300 CFDictionaryGetKeysAndValues(cf, keys, NULL);
301
302 sortedKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
303 for (i = 0; i < nElements; i++) {
304 CFArrayAppendValue(sortedKeys, (CFStringRef)keys[i]);
305 }
306
307 if (isA_CFString(keys[0])) {
308 compFunc = (CFComparatorFunction)CFStringCompare;
309 }
310 else if (isA_CFNumber(keys[0])) {
311 compFunc = (CFComparatorFunction)CFNumberCompare;
312 }
313 else if (isA_CFDate(keys[0])) {
314 compFunc = (CFComparatorFunction)CFDateCompare;
315 }
316
317 if (compFunc != NULL) {
318 CFArraySortValues(sortedKeys,
319 CFRangeMake(0, nElements),
320 compFunc,
321 NULL);
322 }
323
324 for (i = 0; i < nElements; i++) {
325 CFStringRef key;
326 CFStringRef kStr;
327 CFTypeRef val;
328 CFStringRef vStr;
329
330 key = CFArrayGetValueAtIndex(sortedKeys, i);
331 kStr = _SCCopyDescription((CFTypeRef)key, NULL);
332
333 nPrefix1 = CFStringCreateMutable(NULL, 0);
334 CFStringAppendFormat(nPrefix1,
335 formatOptions,
336 CFSTR("%@ %@ : "),
337 prefix2,
338 kStr);
339 nPrefix2 = CFStringCreateMutable(NULL, 0);
340 CFStringAppendFormat(nPrefix2,
341 formatOptions,
342 CFSTR("%@ "),
343 prefix2);
344
345 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX1"), nPrefix1);
346 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX2"), nPrefix2);
347 CFRelease(nPrefix1);
348 CFRelease(nPrefix2);
349 CFRelease(kStr);
350
351 val = CFDictionaryGetValue(cf, key);
352 vStr = _SCCopyDescription((CFTypeRef)val, nFormatOptions);
353 CFStringAppendFormat(str,
354 formatOptions,
355 CFSTR("\n%@"),
356 vStr);
357 CFRelease(vStr);
358 }
359
360 CFRelease(sortedKeys);
361
362 if (keys != keys_q) {
363 CFAllocatorDeallocate(NULL, keys);
364 }
365 }
366 CFStringAppendFormat(str, formatOptions, CFSTR("\n%@}"), prefix2);
367
368 CFRelease(nFormatOptions);
369 return str;
370 }
371
372 CFRelease(nFormatOptions);
373 #endif /* ENABLE_SC_FORMATTING */
374
375 return CFStringCreateWithFormat(NULL,
376 formatOptions,
377 CFSTR("%@%@"),
378 prefix1,
379 cf);
380 }
381
382
383 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
384
385 Boolean
386 _SC_isInstallEnvironment() {
387 static dispatch_once_t once;
388 static Boolean is_install;
389
390 dispatch_once(&once, ^{
391 is_install = (getenv(INSTALL_ENVIRONMENT) != NULL);
392 });
393
394 return is_install;
395 }
396
397
398 os_log_t
399 _SC_LOG_DEFAULT()
400 {
401 static os_log_t log = NULL;
402
403 if (log == NULL) {
404 log = os_log_create("com.apple.SystemConfiguration", "");
405 }
406
407 return log;
408 }
409
410
411 os_log_type_t
412 _SC_syslog_os_log_mapping(int level)
413 {
414 if (level < 0) {
415 level = ~level;
416 }
417
418 switch (level) {
419 case LOG_EMERG :
420 case LOG_ALERT :
421 case LOG_CRIT :
422 return OS_LOG_TYPE_ERROR;
423
424 case LOG_ERR :
425 case LOG_WARNING :
426 case LOG_NOTICE :
427 return OS_LOG_TYPE_DEFAULT;
428
429 case LOG_INFO :
430 return OS_LOG_TYPE_INFO;
431
432 case LOG_DEBUG :
433 return OS_LOG_TYPE_DEBUG;
434 }
435
436 return OS_LOG_TYPE_DEFAULT;
437 };
438
439 static void
440 __SCLog(asl_object_t asl, asl_object_t msg, int level, void *ret_addr, CFStringRef formatString, va_list formatArguments)
441 {
442 char *line;
443 CFArrayRef lines;
444 CFStringRef str;
445
446 if ((asl == NULL) && (level >= 0)) {
447 const char *__format;
448
449 __format = CFStringGetCStringPtr(formatString, kCFStringEncodingUTF8);
450 if (__format != NULL) {
451 os_log_type_t __type;
452
453 __type = _SC_syslog_os_log_mapping(level);
454 os_log_with_args(_SC_LOG_DEFAULT(),
455 __type,
456 __format,
457 formatArguments,
458 ret_addr);
459 return;
460 }
461 }
462
463 if (asl == NULL) {
464 __SCThreadSpecificDataRef tsd;
465
466 tsd = __SCGetThreadSpecificData();
467 if (tsd->_asl == NULL) {
468 tsd->_asl = asl_open(NULL, (_SC_isInstallEnvironment() ? INSTALL_FACILITY : NULL), 0);
469 asl_set_filter(tsd->_asl, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
470 }
471 asl = tsd->_asl;
472 }
473
474 #ifdef ENABLE_SC_FORMATTING
475 str = _CFStringCreateWithFormatAndArgumentsAux(NULL,
476 _SCCopyDescription,
477 NULL,
478 formatString,
479 formatArguments);
480 #else /* ENABLE_SC_FORMATTING */
481 str = CFStringCreateWithFormatAndArguments (NULL,
482 NULL,
483 formatString,
484 formatArguments);
485 #endif /* !ENABLE_SC_FORMATTING */
486
487 if (level >= 0) {
488 lines = CFStringCreateArrayBySeparatingStrings(NULL, str, CFSTR("\n"));
489 if (lines != NULL) {
490 CFIndex i;
491 CFIndex n = CFArrayGetCount(lines);
492
493 for (i = 0; i < n; i++) {
494 line =_SC_cfstring_to_cstring_ext(CFArrayGetValueAtIndex(lines, i),
495 NULL,
496 0,
497 kCFStringEncodingUTF8,
498 (UInt8)'?',
499 NULL);
500 asl_log(asl, msg, level, "%s", line);
501 CFAllocatorDeallocate(NULL, line);
502 }
503 CFRelease(lines);
504 }
505 } else {
506 line =_SC_cfstring_to_cstring_ext(str,
507 NULL,
508 0,
509 kCFStringEncodingUTF8,
510 (UInt8)'?',
511 NULL);
512 asl_log(asl, msg, ~level, "%s", line);
513 CFAllocatorDeallocate(NULL, line);
514 }
515 CFRelease(str);
516 return;
517 }
518
519
520 static void
521 __SCPrint(FILE *stream, CFStringRef formatString, va_list formatArguments, Boolean trace, Boolean addNL)
522 {
523 char *line;
524 CFStringRef str;
525 CFIndex usedBufLen;
526
527 #ifdef ENABLE_SC_FORMATTING
528 str = _CFStringCreateWithFormatAndArgumentsAux(NULL,
529 _SCCopyDescription,
530 NULL,
531 formatString,
532 formatArguments);
533 #else /* ENABLE_SC_FORMATTING */
534 str = CFStringCreateWithFormatAndArguments (NULL,
535 NULL,
536 formatString,
537 formatArguments);
538 #endif /* !ENABLE_SC_FORMATTING */
539
540 line =_SC_cfstring_to_cstring_ext(str,
541 NULL,
542 0,
543 kCFStringEncodingUTF8,
544 (UInt8)'?',
545 &usedBufLen);
546 CFRelease(str);
547 if (!line) {
548 return;
549 }
550
551 pthread_mutex_lock(&lock);
552 if (trace) {
553 struct tm tm_now;
554 struct timeval tv_now;
555
556 (void)gettimeofday(&tv_now, NULL);
557 (void)localtime_r(&tv_now.tv_sec, &tm_now);
558 (void)fprintf(stream, "%2d:%02d:%02d.%03d ",
559 tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, tv_now.tv_usec / 1000);
560 }
561 (void)fwrite((const void *)line, usedBufLen, 1, stream);
562 if (addNL) {
563 (void)fputc('\n', stream);
564 }
565 fflush (stream);
566 pthread_mutex_unlock(&lock);
567 CFAllocatorDeallocate(NULL, line);
568
569 return;
570 }
571
572
573 void
574 SCLog(Boolean condition, int level, CFStringRef formatString, ...)
575 {
576 va_list formatArguments;
577 va_list formatArguments_print;
578 Boolean log = FALSE;
579 Boolean print = FALSE;
580
581 if (!condition) {
582 return;
583 }
584
585 /*
586 * Note: The following are the expected values for _sc_log
587 *
588 * 0 if SC messages should be written to stdout/stderr
589 * 1 if SC messages should be logged w/asl(3)
590 * 2 if SC messages should be written to stdout/stderr AND logged
591 */
592
593 if (_sc_log > 0) {
594 log = TRUE; // log requested
595 va_start(formatArguments, formatString);
596
597 if (_sc_log > 1) {
598 print = TRUE; // log AND print requested
599 va_copy(formatArguments_print, formatArguments);
600 }
601 } else {
602 print = TRUE; // print requested
603 va_start(formatArguments_print, formatString);
604 }
605
606 if (log) {
607 __SCLog(NULL, NULL, level, __builtin_return_address(0), formatString, formatArguments);
608 va_end(formatArguments);
609 }
610
611 if (print) {
612 __SCPrint((LOG_PRI(level) > LOG_NOTICE) ? stderr : stdout,
613 formatString,
614 formatArguments_print,
615 (_sc_log > 0), // trace
616 TRUE); // add newline
617 va_end(formatArguments_print);
618 }
619
620 return;
621 }
622
623
624 void
625 __SC_Log(int level, CFStringRef format_CF, os_log_t log, os_log_type_t type, const char *format, ...)
626 {
627 Boolean do_log = FALSE;
628 Boolean do_print = FALSE;
629 va_list args_log;
630 va_list args_print;
631
632 /*
633 * Note: The following are the expected values for _sc_log
634 *
635 * 0 if SC messages should be written to stdout/stderr
636 * 1 if SC messages should be logged w/asl(3)
637 * 2 if SC messages should be written to stdout/stderr AND logged
638 */
639
640 if (_sc_log > 0) {
641 log = TRUE; // log requested
642 va_start(args_log, format);
643
644 if (_sc_log > 1) {
645 do_print = TRUE; // log AND print requested
646 va_copy(args_print, args_log);
647 }
648 } else {
649 do_print = TRUE; // print requested
650 va_start(args_print, format);
651 }
652
653 if (do_log) {
654 if (level >= 0) {
655 os_log_with_args(log,
656 type,
657 format,
658 args_log,
659 __builtin_return_address(0));
660 } else {
661 // if we need to break apart a multi-line message
662 __SCLog(NULL,
663 NULL,
664 level,
665 __builtin_return_address(0),
666 format_CF,
667 args_log);
668 }
669 va_end(args_log);
670 }
671
672 if (do_print) {
673 __SCPrint(stdout,
674 format_CF,
675 args_print,
676 (_sc_log > 0), // trace
677 TRUE); // add newline
678 va_end(args_print);
679 }
680
681 return;
682
683 }
684
685
686 void
687 SCLOG(asl_object_t asl, asl_object_t msg, int level, CFStringRef formatString, ...)
688 {
689 va_list formatArguments;
690 va_list formatArguments_print;
691 Boolean log = FALSE;
692 Boolean print = FALSE;
693
694 /*
695 * Note: The following are the expected values for _sc_log
696 *
697 * 0 if SC messages should be written to stdout/stderr
698 * 1 if SC messages should be logged w/asl(3)
699 * 2 if SC messages should be written to stdout/stderr AND logged
700 */
701
702 if (_sc_log > 0) {
703 log = TRUE; // log requested
704 va_start(formatArguments, formatString);
705
706 if (_sc_log > 1) {
707 print = TRUE; // log AND print requested
708 va_copy(formatArguments_print, formatArguments);
709 }
710 } else {
711 print = TRUE; // print requested
712 va_start(formatArguments_print, formatString);
713 }
714
715 if (log) {
716 __SCLog(asl, msg, level, __builtin_return_address(0), formatString, formatArguments);
717 va_end(formatArguments);
718 }
719
720 if (print) {
721 if (level < 0) {
722 level = ~level;
723 }
724 __SCPrint((level > ASL_LEVEL_NOTICE) ? stderr : stdout,
725 formatString,
726 formatArguments_print,
727 (_sc_log > 0), // trace
728 TRUE); // add newline
729 va_end(formatArguments_print);
730 }
731
732 return;
733 }
734
735
736 void
737 SCPrint(Boolean condition, FILE *stream, CFStringRef formatString, ...)
738 {
739 va_list formatArguments;
740
741 if (!condition) {
742 return;
743 }
744
745 va_start(formatArguments, formatString);
746 __SCPrint(stream, formatString, formatArguments, FALSE, FALSE);
747 va_end(formatArguments);
748
749 return;
750 }
751
752
753 #pragma mark -
754 #pragma mark ASL Functions
755
756
757 static CFTypeID __kSCLoggerTypeID = _kCFRuntimeNotATypeID;
758
759 typedef enum {
760 kModuleStatusEnabled,
761 kModuleStatusDisabled,
762 kModuleStatusDoesNotExist
763 } ModuleStatus;
764
765 struct SCLogger
766 {
767 CFRuntimeBase cf_base;
768
769 char * loggerID; // LoggerID
770 SCLoggerFlags flags;
771 asl_object_t aslc;
772 asl_object_t aslm;
773 ModuleStatus module_status;
774 pthread_mutex_t lock;
775 };
776
777
778 static void __SCLoggerDeallocate(CFTypeRef cf);
779 static const CFRuntimeClass __SCLoggerClass = {
780 0, /* version */
781 "SCLogger", /* className */
782 NULL, /* init */
783 NULL, /* copy */
784 __SCLoggerDeallocate, /* deallocate */
785 NULL, /* equal */
786 NULL, /* hash */
787 NULL, /* copyFormattingDesc */
788 NULL /* copyDebugDesc */
789 };
790
791
792 #define DATETIMEBUFFERSIZE 32
793
794
795 static pthread_once_t registerLoggerOnce = PTHREAD_ONCE_INIT;
796 static pthread_once_t defaultLoggerOnce = PTHREAD_ONCE_INIT;
797
798 typedef enum {
799 kLoggerASLControlEnableModule,
800 kLoggerASLControlDisableModule,
801 kLoggerASLControlLogFileCheckpoint
802 } LoggerASLControl;
803
804 static SCLoggerRef defaultLogger = NULL;
805 static SCLoggerRef __SCLoggerCreate(void);
806 static void __SCLoggerDefaultLoggerInit();
807 static SCLoggerRef SCLoggerGetDefaultLogger();
808 static void SCLoggerSetLoggerID(SCLoggerRef logger, CFStringRef loggerID);
809 static void SCLoggerSendMessageToModuleOnly(SCLoggerRef logger, Boolean isPrivate);
810 static void SCLoggerSendASLControl(SCLoggerRef logger, LoggerASLControl control);
811 static ModuleStatus GetModuleStatus(const char * loggerID);
812
813 static void
814 __SCLoggerRegisterClass(void)
815 {
816 if (__kSCLoggerTypeID == _kCFRuntimeNotATypeID) {
817 __kSCLoggerTypeID = _CFRuntimeRegisterClass(&__SCLoggerClass);
818 }
819 return;
820 }
821
822 static SCLoggerRef
823 __SCLoggerAllocate(CFAllocatorRef allocator)
824 {
825 SCLoggerRef state;
826 int size;
827
828 pthread_once(&registerLoggerOnce, __SCLoggerRegisterClass);
829
830 size = sizeof(*state) - sizeof(CFRuntimeBase);
831 state = (SCLoggerRef) _CFRuntimeCreateInstance(allocator,
832 __kSCLoggerTypeID,
833 size,
834 NULL);
835 return (state);
836 }
837
838 static void
839 __SCLoggerDeallocate(CFTypeRef cf)
840 {
841 SCLoggerRef logger = (SCLoggerRef)cf;
842
843 if (logger != NULL) {
844 // Rotate on close behavior
845 if (logger->module_status != kModuleStatusDoesNotExist) {
846 SCLoggerSendASLControl(logger,
847 kLoggerASLControlLogFileCheckpoint);
848 }
849 if (logger->loggerID != NULL) {
850 CFAllocatorDeallocate(NULL, logger->loggerID);
851 logger->loggerID = NULL;
852 }
853 if (logger->aslm != NULL) {
854 asl_release(logger->aslm);
855 logger->aslm = NULL;
856 }
857 if (logger->aslc != NULL) {
858 asl_release(logger->aslc);
859 logger->aslc = NULL;
860 }
861 }
862 }
863
864 static SCLoggerRef
865 __SCLoggerCreate(void)
866 {
867 SCLoggerRef tempLogger = NULL;
868
869 tempLogger = __SCLoggerAllocate(kCFAllocatorDefault);
870 tempLogger->loggerID = NULL;
871 tempLogger->flags = kSCLoggerFlagsDefault;
872 tempLogger->aslc = asl_open(NULL, (_SC_isInstallEnvironment() ? INSTALL_FACILITY : NULL), ASL_OPT_NO_DELAY);
873 tempLogger->aslm = asl_new(ASL_TYPE_MSG);
874 pthread_mutex_init(&(tempLogger->lock), NULL);
875 tempLogger->module_status = kModuleStatusDoesNotExist;
876
877 return tempLogger;
878 }
879
880 SCLoggerFlags
881 SCLoggerGetFlags(SCLoggerRef logger)
882 {
883 return logger->flags;
884 }
885
886 void
887 SCLoggerSetFlags(SCLoggerRef logger, SCLoggerFlags flags)
888 {
889 if (logger == defaultLogger) {
890 return;
891 }
892 pthread_mutex_lock(&(logger->lock));
893 if (flags != kSCLoggerFlagsNone) {
894 logger->module_status = GetModuleStatus(logger->loggerID);
895 if (logger->module_status == kModuleStatusDoesNotExist) {
896 goto done;
897 }
898 if ((flags & kSCLoggerFlagsFile) != 0) {
899 if ((logger->flags & kSCLoggerFlagsFile) == 0) {
900 // Enable the module if disabled
901 if (logger->module_status == kModuleStatusDisabled) {
902 SCLoggerSendASLControl(logger, kLoggerASLControlEnableModule);
903 }
904 // Setting ASL Filter level to debug
905 asl_set_filter(logger->aslc, ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG));
906 if (logger->loggerID != NULL) {
907 asl_set(logger->aslm, kLoggerID,
908 logger->loggerID);
909 }
910 }
911 }
912 else if ((logger->flags & kSCLoggerFlagsFile) != 0) {
913 asl_unset(logger->aslm, kLoggerID);
914 asl_set_filter(logger->aslc, ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE));
915 SCLoggerSendMessageToModuleOnly(logger, false);
916 }
917 if ((flags & kSCLoggerFlagsDefault) != 0) {
918 if ((logger->flags & kSCLoggerFlagsDefault) == 0) {
919 SCLoggerSendMessageToModuleOnly(logger, false);
920 }
921 }
922 else if ((logger->flags & kSCLoggerFlagsDefault) != 0) {
923 SCLoggerSendMessageToModuleOnly(logger, true);
924 }
925 }
926 logger->flags = flags;
927 done:
928 pthread_mutex_unlock(&(logger->lock));
929 }
930
931
932 static void
933 SCLoggerSetLoggerID(SCLoggerRef logger, CFStringRef loggerID)
934 {
935 logger->loggerID
936 = _SC_cfstring_to_cstring(loggerID, NULL, 0,
937 kCFStringEncodingUTF8);
938 // Enable the module if disabled
939 logger->module_status = GetModuleStatus(logger->loggerID);
940 if (logger->module_status == kModuleStatusDisabled) {
941 SCLoggerSendASLControl(logger, kLoggerASLControlEnableModule);
942 }
943 }
944
945 static ModuleStatus
946 GetModuleStatus(const char * loggerID)
947 {
948 ModuleStatus moduleStatus = kModuleStatusDoesNotExist;
949 asl_object_t response = NULL;
950 const char* value = NULL;
951
952 if (loggerID != NULL) {
953 response = _asl_server_control_query();
954 if (response == NULL) {
955 goto done;
956 }
957 value = asl_get(response, loggerID);
958 if (value == NULL) {
959 moduleStatus = kModuleStatusDoesNotExist;
960 goto done;
961 }
962
963 if (strcmp(value, "enabled") == 0) {
964 moduleStatus = kModuleStatusEnabled;
965 }
966 else {
967 moduleStatus = kModuleStatusDisabled;
968 }
969 }
970 done:
971 asl_release(response);
972
973 return moduleStatus;
974 }
975
976 static void
977 SCLoggerSendMessageToModuleOnly(SCLoggerRef logger, Boolean isPrivate)
978 {
979 if (isPrivate) {
980 asl_set(logger->aslm, kASLModule, logger->loggerID);
981 }
982 else {
983 if (asl_get(logger->aslm, kASLModule) != NULL) {
984 asl_unset(logger->aslm, kASLModule);
985 }
986 }
987 }
988
989 static void
990 SCLoggerSendASLControl(SCLoggerRef logger, LoggerASLControl control)
991 {
992 SCLoggerRef defLogger = SCLoggerGetDefaultLogger();
993 pthread_mutex_lock(&(defLogger->lock));
994
995 // this next line turns the asl_log()'s that follow into control messages
996 asl_set(defLogger->aslm, kASLOption, "control");
997
998 switch (control) {
999 case kLoggerASLControlEnableModule:
1000 asl_log(defLogger->aslc, defLogger->aslm,
1001 ASL_LEVEL_NOTICE, "@ %s enable 1",
1002 logger->loggerID);
1003 break;
1004 case kLoggerASLControlDisableModule:
1005 asl_log(defLogger->aslc, defLogger->aslm,
1006 ASL_LEVEL_NOTICE, "@ %s enable 0",
1007 logger->loggerID);
1008 break;
1009 case kLoggerASLControlLogFileCheckpoint:
1010 asl_log(defLogger->aslc, defLogger->aslm,
1011 ASL_LEVEL_NOTICE, "@ %s checkpoint",
1012 logger->loggerID);
1013 break;
1014 default:
1015 break;
1016 }
1017
1018 // turn off control mode
1019 asl_unset(defLogger->aslm, kASLOption);
1020 pthread_mutex_unlock(&defLogger->lock);
1021 return;
1022 }
1023
1024 SCLoggerRef
1025 SCLoggerCreate(CFStringRef loggerID)
1026 {
1027 SCLoggerRef logger = NULL;
1028
1029 logger = __SCLoggerCreate();
1030 if (loggerID != NULL) {
1031 SCLoggerSetLoggerID(logger, loggerID);
1032 }
1033 SCLoggerSetFlags(logger, kSCLoggerFlagsDefault);
1034 return logger;
1035 }
1036
1037 static void
1038 __SCLoggerDefaultLoggerInit()
1039 {
1040 if (defaultLogger == NULL) {
1041 defaultLogger = __SCLoggerCreate();
1042 defaultLogger->flags = kSCLoggerFlagsDefault;
1043 }
1044 }
1045
1046 static SCLoggerRef
1047 SCLoggerGetDefaultLogger()
1048 {
1049 pthread_once(&defaultLoggerOnce, __SCLoggerDefaultLoggerInit);
1050 return defaultLogger;
1051 }
1052
1053 static void
1054 SCLoggerVLogInternal(SCLoggerRef logger, int loglevel, void *ret_addr,
1055 CFStringRef formatString, va_list args)
1056 {
1057 asl_object_t aslc;
1058 asl_object_t aslm;
1059
1060 if (logger == NULL
1061 || logger->module_status == kModuleStatusDoesNotExist) {
1062 logger = SCLoggerGetDefaultLogger();
1063 }
1064 pthread_mutex_lock(&(logger->lock));
1065 if (logger->flags == kSCLoggerFlagsNone) {
1066 pthread_mutex_unlock(&(logger->lock));
1067 return;
1068 }
1069 aslc = logger->aslc;
1070 aslm = logger->aslm;
1071 __SCLog(aslc, aslm, loglevel, ret_addr, formatString, args);
1072 pthread_mutex_unlock(&(logger->lock));
1073 return;
1074 }
1075
1076 void
1077 SCLoggerLog(SCLoggerRef logger, int loglevel, CFStringRef formatString, ...)
1078 {
1079 va_list args;
1080
1081 va_start(args, formatString);
1082 SCLoggerVLogInternal(logger, loglevel, __builtin_return_address(0), formatString, args);
1083 va_end(args);
1084
1085 return;
1086 }
1087
1088 void
1089 SCLoggerVLog(SCLoggerRef logger, int loglevel, CFStringRef formatString, va_list args)
1090 {
1091 SCLoggerVLogInternal(logger, loglevel, __builtin_return_address(0), formatString, args);
1092 return;
1093 }
1094
1095
1096 #pragma mark -
1097 #pragma mark SC error handling / logging
1098
1099
1100 const CFStringRef kCFErrorDomainSystemConfiguration = CFSTR("com.apple.SystemConfiguration");
1101
1102
1103 static const struct sc_errmsg {
1104 int status;
1105 char *message;
1106 } sc_errmsgs[] = {
1107 { kSCStatusAccessError, "Permission denied" },
1108 { kSCStatusConnectionIgnore, "Network connection information not available at this time" },
1109 { kSCStatusConnectionNoService, "Network service for connection not available" },
1110 { kSCStatusFailed, "Failed!" },
1111 { kSCStatusInvalidArgument, "Invalid argument" },
1112 { kSCStatusKeyExists, "Key already defined" },
1113 { kSCStatusLocked, "Lock already held" },
1114 { kSCStatusMaxLink, "Maximum link count exceeded" },
1115 { kSCStatusNeedLock, "Lock required for this operation" },
1116 { kSCStatusNoStoreServer, "Configuration daemon not (no longer) available" },
1117 { kSCStatusNoStoreSession, "Configuration daemon session not active" },
1118 { kSCStatusNoConfigFile, "Configuration file not found" },
1119 { kSCStatusNoKey, "No such key" },
1120 { kSCStatusNoLink, "No such link" },
1121 { kSCStatusNoPrefsSession, "Preference session not active" },
1122 { kSCStatusNotifierActive, "Notifier is currently active" },
1123 { kSCStatusOK, "Success!" },
1124 { kSCStatusPrefsBusy, "Preferences update currently in progress" },
1125 { kSCStatusReachabilityUnknown, "Network reachability cannot be determined" },
1126 { kSCStatusStale, "Write attempted on stale version of object" },
1127 };
1128 #define nSC_ERRMSGS (sizeof(sc_errmsgs)/sizeof(struct sc_errmsg))
1129
1130 void
1131 _SCErrorSet(int error)
1132 {
1133 __SCThreadSpecificDataRef tsd;
1134
1135 tsd = __SCGetThreadSpecificData();
1136 tsd->_sc_error = error;
1137 return;
1138 }
1139
1140
1141 CFErrorRef
1142 SCCopyLastError(void)
1143 {
1144 CFStringRef domain;
1145 CFErrorRef error;
1146 int i;
1147 int code;
1148 __SCThreadSpecificDataRef tsd;
1149 CFMutableDictionaryRef userInfo = NULL;
1150
1151 tsd = __SCGetThreadSpecificData();
1152 code =tsd->_sc_error;
1153
1154 for (i = 0; i < (int)nSC_ERRMSGS; i++) {
1155 if (sc_errmsgs[i].status == code) {
1156 CFStringRef str;
1157
1158 domain = kCFErrorDomainSystemConfiguration;
1159 userInfo = CFDictionaryCreateMutable(NULL,
1160 0,
1161 &kCFCopyStringDictionaryKeyCallBacks,
1162 &kCFTypeDictionaryValueCallBacks);
1163 str = CFStringCreateWithCString(NULL,
1164 sc_errmsgs[i].message,
1165 kCFStringEncodingASCII);
1166 CFDictionarySetValue(userInfo, kCFErrorDescriptionKey, str);
1167 CFRelease(str);
1168 goto done;
1169 }
1170 }
1171
1172 if ((code > 0) && (code <= ELAST)) {
1173 domain = kCFErrorDomainPOSIX;
1174 goto done;
1175 }
1176
1177 domain = kCFErrorDomainMach;
1178
1179 done :
1180
1181 error = CFErrorCreate(NULL, domain, code, userInfo);
1182 if (userInfo != NULL) CFRelease(userInfo);
1183 return error;
1184 }
1185
1186
1187 int
1188 SCError(void)
1189 {
1190 __SCThreadSpecificDataRef tsd;
1191
1192 tsd = __SCGetThreadSpecificData();
1193 return tsd->_sc_error;
1194 }
1195
1196
1197 const char *
1198 SCErrorString(int status)
1199 {
1200 int i;
1201
1202 for (i = 0; i < (int)nSC_ERRMSGS; i++) {
1203 if (sc_errmsgs[i].status == status) {
1204 return sc_errmsgs[i].message;
1205 }
1206 }
1207
1208 if ((status > 0) && (status <= ELAST)) {
1209 return strerror(status);
1210 }
1211
1212 if ((status >= BOOTSTRAP_SUCCESS) && (status <= BOOTSTRAP_NO_MEMORY)) {
1213 return bootstrap_strerror(status);
1214 }
1215
1216 return mach_error_string(status);
1217 }