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