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