]> git.saurik.com Git - apple/configd.git/blob - SystemConfiguration.fproj/SCD.c
configd-1061.101.1.tar.gz
[apple/configd.git] / SystemConfiguration.fproj / SCD.c
1 /*
2 * Copyright (c) 2000-2008, 2010-2020 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 <pthread.h>
38 #include <sys/time.h>
39 #include <os/log.h>
40 #include <os/log_private.h>
41
42 #include "SCDynamicStoreInternal.h"
43 #include "SCD.h"
44 #include "config.h" /* MiG generated file */
45
46 #define INSTALL_ENVIRONMENT "__OSINSTALL_ENVIRONMENT"
47
48 /* framework variables */
49 int _sc_debug = FALSE; /* non-zero if debugging enabled */
50 int _sc_verbose = FALSE; /* non-zero if verbose logging enabled */
51 int _sc_log = 1; /* 0 if SC messages should be written to stdout/stderr,
52 1 if SC messages should be logged w/os_log(3),
53 2 if SC messages should be logged AND written to stdout/stderr
54 3 if SC messages should be logged AND written to stdout/stderr (w/o timestamp) */
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->_sc_store != NULL) CFRelease(tsd->_sc_store);
72 CFAllocatorDeallocate(kCFAllocatorSystemDefault, tsd);
73 }
74 return;
75 }
76
77
78 static void
79 __SCThreadSpecificKeyInitialize()
80 {
81 pthread_key_create(&tsDataKey, __SCThreadSpecificDataFinalize);
82 return;
83 }
84
85
86 __private_extern__
87 __SCThreadSpecificDataRef
88 __SCGetThreadSpecificData()
89 {
90 __SCThreadSpecificDataRef tsd;
91 pthread_once(&tsKeyInitialized, __SCThreadSpecificKeyInitialize);
92
93 tsd = pthread_getspecific(tsDataKey);
94 if (tsd == NULL) {
95 tsd = CFAllocatorAllocate(kCFAllocatorSystemDefault, sizeof(__SCThreadSpecificData), 0);
96 tsd->_sc_error = kSCStatusOK;
97 tsd->_sc_store = NULL;
98 pthread_setspecific(tsDataKey, tsd);
99 }
100
101 return tsd;
102 }
103
104
105 #pragma mark -
106 #pragma mark Logging
107
108
109 #define ENABLE_SC_FORMATTING
110 #ifdef ENABLE_SC_FORMATTING
111 // from <CoreFoundation/ForFoundationOnly.h>
112 extern CFStringRef _CFStringCreateWithFormatAndArgumentsAux(CFAllocatorRef alloc, CFStringRef (*copyDescFunc)(CFTypeRef, CFDictionaryRef), CFDictionaryRef formatOptions, CFStringRef format, va_list arguments);
113 #endif /* ENABLE_SC_FORMATTING */
114
115
116 CFStringRef
117 _SCCopyDescription(CFTypeRef cf, CFDictionaryRef formatOptions)
118 {
119 #ifdef ENABLE_SC_FORMATTING
120 CFMutableDictionaryRef nFormatOptions;
121 CFStringRef prefix1;
122 CFStringRef prefix2;
123 CFTypeID type = CFGetTypeID(cf);
124
125 if (!formatOptions ||
126 !CFDictionaryGetValueIfPresent(formatOptions, CFSTR("PREFIX1"), (const void **)&prefix1)) {
127 prefix1 = CFSTR("");
128 }
129
130 if (type == CFStringGetTypeID()) {
131 return CFStringCreateWithFormat(NULL,
132 formatOptions,
133 CFSTR("%@%@"),
134 prefix1,
135 cf);
136 }
137
138 if (type == CFBooleanGetTypeID()) {
139 return CFStringCreateWithFormat(NULL,
140 formatOptions,
141 CFSTR("%@%s"),
142 prefix1,
143 CFBooleanGetValue(cf) ? "TRUE" : "FALSE");
144 }
145
146 if (type == CFDataGetTypeID()) {
147 const uint8_t *data;
148 CFIndex dataLen;
149 CFIndex i;
150 CFMutableStringRef str;
151
152 str = CFStringCreateMutable(NULL, 0);
153 CFStringAppendFormat(str, formatOptions, CFSTR("%@<data> 0x"), prefix1);
154
155 data = CFDataGetBytePtr(cf);
156 dataLen = CFDataGetLength(cf);
157 for (i = 0; i < dataLen; i++) {
158 CFStringAppendFormat(str, NULL, CFSTR("%02x"), data[i]);
159 }
160
161 return str;
162 }
163
164 if (type == CFNumberGetTypeID()) {
165 return CFStringCreateWithFormat(NULL,
166 formatOptions,
167 CFSTR("%@%@"),
168 prefix1,
169 cf);
170 }
171
172 if (type == CFDateGetTypeID()) {
173 CFCalendarRef calendar;
174 CFStringRef str;
175 CFTimeZoneRef tz;
176 int MM, DD, YYYY, hh, mm, ss;
177
178 calendar = CFCalendarCreateWithIdentifier(NULL, kCFGregorianCalendar);
179 tz = CFTimeZoneCopySystem();
180 CFCalendarSetTimeZone(calendar, tz);
181 CFRelease(tz);
182 CFCalendarDecomposeAbsoluteTime(calendar,
183 CFDateGetAbsoluteTime(cf),
184 "MdyHms",
185 &MM, &DD, &YYYY, &hh, &mm, &ss);
186 CFRelease(calendar);
187
188 str = CFStringCreateWithFormat(NULL,
189 formatOptions,
190 CFSTR("%@%02d/%02d/%04d %02d:%02d:%02d"),
191 prefix1,
192 MM, DD, YYYY, hh, mm, ss);
193 return str;
194 }
195
196 if ((formatOptions == NULL) ||
197 !CFDictionaryGetValueIfPresent(formatOptions, CFSTR("PREFIX2"), (const void **)&prefix2)) {
198 prefix2 = prefix1;
199 }
200
201 if (formatOptions != NULL) {
202 nFormatOptions = CFDictionaryCreateMutableCopy(NULL, 0, formatOptions);
203 } else {
204 nFormatOptions = CFDictionaryCreateMutable(NULL,
205 0,
206 &kCFTypeDictionaryKeyCallBacks,
207 &kCFTypeDictionaryValueCallBacks);
208 }
209 assert(nFormatOptions != NULL);
210
211 #define N_QUICK 32
212
213 if (type == CFArrayGetTypeID()) {
214 const void * elements_q[N_QUICK];
215 const void ** elements = elements_q;
216 CFIndex i;
217 CFIndex nElements;
218 CFMutableStringRef str;
219
220 str = CFStringCreateMutable(NULL, 0);
221 CFStringAppendFormat(str, formatOptions, CFSTR("%@<array> {"), prefix1);
222
223 nElements = CFArrayGetCount(cf);
224 if (nElements > 0) {
225 if (nElements > (CFIndex)(sizeof(elements_q)/sizeof(CFTypeRef)))
226 elements = CFAllocatorAllocate(NULL, nElements * sizeof(CFTypeRef), 0);
227 CFArrayGetValues(cf, CFRangeMake(0, nElements), elements);
228 for (i = 0; i < nElements; i++) {
229 CFMutableStringRef nPrefix1;
230 CFMutableStringRef nPrefix2;
231 CFStringRef nStr;
232 CFStringRef vStr;
233
234 nStr = CFStringCreateWithFormat(NULL, NULL, CFSTR("%ld"), i);
235
236 nPrefix1 = CFStringCreateMutable(NULL, 0);
237 CFStringAppendFormat(nPrefix1,
238 formatOptions,
239 CFSTR("%@ %@ : "),
240 prefix2,
241 nStr);
242 nPrefix2 = CFStringCreateMutable(NULL, 0);
243 CFStringAppendFormat(nPrefix2,
244 formatOptions,
245 CFSTR("%@ "),
246 prefix2);
247
248 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX1"), nPrefix1);
249 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX2"), nPrefix2);
250 CFRelease(nPrefix1);
251 CFRelease(nPrefix2);
252 CFRelease(nStr);
253
254 vStr = _SCCopyDescription((CFTypeRef)elements[i], nFormatOptions);
255 CFStringAppendFormat(str,
256 formatOptions,
257 CFSTR("\n%@"),
258 vStr);
259 CFRelease(vStr);
260 }
261 if (elements != elements_q) CFAllocatorDeallocate(NULL, elements);
262 }
263 CFStringAppendFormat(str, formatOptions, CFSTR("\n%@}"), prefix2);
264
265 CFRelease(nFormatOptions);
266 return str;
267 }
268
269 if (type == CFDictionaryGetTypeID()) {
270 const void * keys_q[N_QUICK];
271 const void ** keys = keys_q;
272 CFIndex i;
273 CFIndex nElements;
274 CFMutableStringRef nPrefix1;
275 CFMutableStringRef nPrefix2;
276 CFMutableStringRef str;
277
278 str = CFStringCreateMutable(NULL, 0);
279 CFStringAppendFormat(str, formatOptions, CFSTR("%@<dictionary> {"), prefix1);
280
281 nElements = CFDictionaryGetCount(cf);
282 if (nElements > 0) {
283 CFComparatorFunction compFunc = NULL;
284 CFMutableArrayRef sortedKeys;
285
286 if (nElements > (CFIndex)(sizeof(keys_q) / sizeof(CFTypeRef))) {
287 keys = CFAllocatorAllocate(NULL, nElements * sizeof(CFTypeRef), 0);
288 }
289 CFDictionaryGetKeysAndValues(cf, keys, NULL);
290
291 sortedKeys = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
292 for (i = 0; i < nElements; i++) {
293 CFArrayAppendValue(sortedKeys, (CFStringRef)keys[i]);
294 }
295
296 if (isA_CFString(keys[0])) {
297 compFunc = (CFComparatorFunction)CFStringCompare;
298 }
299 else if (isA_CFNumber(keys[0])) {
300 compFunc = (CFComparatorFunction)CFNumberCompare;
301 }
302 else if (isA_CFDate(keys[0])) {
303 compFunc = (CFComparatorFunction)CFDateCompare;
304 }
305
306 if (compFunc != NULL) {
307 CFArraySortValues(sortedKeys,
308 CFRangeMake(0, nElements),
309 compFunc,
310 NULL);
311 }
312
313 for (i = 0; i < nElements; i++) {
314 CFStringRef key;
315 CFStringRef kStr;
316 CFTypeRef val;
317 CFStringRef vStr;
318
319 key = CFArrayGetValueAtIndex(sortedKeys, i);
320 kStr = _SCCopyDescription((CFTypeRef)key, NULL);
321
322 nPrefix1 = CFStringCreateMutable(NULL, 0);
323 CFStringAppendFormat(nPrefix1,
324 formatOptions,
325 CFSTR("%@ %@ : "),
326 prefix2,
327 kStr);
328 nPrefix2 = CFStringCreateMutable(NULL, 0);
329 CFStringAppendFormat(nPrefix2,
330 formatOptions,
331 CFSTR("%@ "),
332 prefix2);
333
334 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX1"), nPrefix1);
335 CFDictionarySetValue(nFormatOptions, CFSTR("PREFIX2"), nPrefix2);
336 CFRelease(nPrefix1);
337 CFRelease(nPrefix2);
338 CFRelease(kStr);
339
340 val = CFDictionaryGetValue(cf, key);
341 vStr = _SCCopyDescription((CFTypeRef)val, nFormatOptions);
342 CFStringAppendFormat(str,
343 formatOptions,
344 CFSTR("\n%@"),
345 vStr);
346 CFRelease(vStr);
347 }
348
349 CFRelease(sortedKeys);
350
351 if (keys != keys_q) {
352 CFAllocatorDeallocate(NULL, keys);
353 }
354 }
355 CFStringAppendFormat(str, formatOptions, CFSTR("\n%@}"), prefix2);
356
357 CFRelease(nFormatOptions);
358 return str;
359 }
360
361 CFRelease(nFormatOptions);
362 #endif /* ENABLE_SC_FORMATTING */
363
364 return CFStringCreateWithFormat(NULL,
365 formatOptions,
366 CFSTR("%@%@"),
367 prefix1,
368 cf);
369 }
370
371
372 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
373
374 Boolean
375 _SC_isInstallEnvironment() {
376 static dispatch_once_t once;
377 static Boolean is_install;
378
379 dispatch_once(&once, ^{
380 is_install = (getenv(INSTALL_ENVIRONMENT) != NULL);
381 });
382
383 return is_install;
384 }
385
386
387 os_log_t
388 _SC_LOG_DEFAULT(void)
389 {
390 static os_log_t log = NULL;
391
392 if (log == NULL) {
393 log = os_log_create("com.apple.SystemConfiguration", "");
394 }
395
396 return log;
397 }
398
399
400 os_log_type_t
401 _SC_syslog_os_log_mapping(int level)
402 {
403 if (level < 0) {
404 level = ~level;
405 }
406
407 switch (level) {
408 case LOG_EMERG :
409 case LOG_ALERT :
410 case LOG_CRIT :
411 return OS_LOG_TYPE_ERROR;
412
413 case LOG_ERR :
414 case LOG_WARNING :
415 case LOG_NOTICE :
416 return OS_LOG_TYPE_DEFAULT;
417
418 case LOG_INFO :
419 return OS_LOG_TYPE_INFO;
420
421 case LOG_DEBUG :
422 return OS_LOG_TYPE_DEBUG;
423 }
424
425 return OS_LOG_TYPE_DEFAULT;
426 };
427
428 static void
429 __SCLog(void *ret_addr, os_log_type_t type, const char *formatString, va_list formatArguments)
430 {
431 os_log_with_args(_SC_LOG_DEFAULT(),
432 type,
433 formatString,
434 formatArguments,
435 ret_addr);
436 return;
437 }
438
439
440 static void
441 __SCPrint(FILE *stream, CFStringRef formatString, va_list formatArguments, Boolean trace, Boolean addNL)
442 {
443 char *line;
444 CFStringRef str;
445 CFIndex usedBufLen;
446
447 #ifdef ENABLE_SC_FORMATTING
448 str = _CFStringCreateWithFormatAndArgumentsAux(NULL,
449 _SCCopyDescription,
450 NULL,
451 formatString,
452 formatArguments);
453 #else /* ENABLE_SC_FORMATTING */
454 str = CFStringCreateWithFormatAndArguments (NULL,
455 NULL,
456 formatString,
457 formatArguments);
458 #endif /* !ENABLE_SC_FORMATTING */
459
460 line =_SC_cfstring_to_cstring_ext(str,
461 NULL,
462 0,
463 kCFStringEncodingUTF8,
464 (UInt8)'?',
465 &usedBufLen);
466 CFRelease(str);
467 if (!line) {
468 return;
469 }
470
471 pthread_mutex_lock(&lock);
472 if (trace) {
473 struct tm tm_now;
474 struct timeval tv_now;
475
476 (void)gettimeofday(&tv_now, NULL);
477 (void)localtime_r(&tv_now.tv_sec, &tm_now);
478 (void)fprintf(stream, "%2d:%02d:%02d.%03d ",
479 tm_now.tm_hour, tm_now.tm_min, tm_now.tm_sec, tv_now.tv_usec / 1000);
480 }
481 (void)fwrite((const void *)line, usedBufLen, 1, stream);
482 if (addNL) {
483 (void)fputc('\n', stream);
484 }
485 fflush (stream);
486 pthread_mutex_unlock(&lock);
487 CFAllocatorDeallocate(NULL, line);
488
489 return;
490 }
491
492
493 void
494 __SC_Log(int level, CFStringRef format_CF, os_log_t log, os_log_type_t type, const char *format, ...)
495 {
496 #pragma unused(level)
497 Boolean do_log = FALSE;
498 Boolean do_print = FALSE;
499 va_list args_log;
500 va_list args_print;
501
502 /*
503 * Note: The following are the expected values for _sc_log
504 *
505 * 0 if SC messages should be written to stdout/stderr
506 * 1 if SC messages should be logged w/os_log(3)
507 * 2 if SC messages should be written to stdout/stderr AND logged
508 * 3 if SC messages should be logged AND written to stdout/stderr (w/o timestamp)
509 */
510
511 if (_sc_log > 0) {
512 do_log = TRUE; // log requested
513 va_start(args_log, format);
514
515 if (_sc_log > 1) {
516 do_print = TRUE; // log AND print requested
517 va_copy(args_print, args_log);
518 }
519 } else {
520 do_print = TRUE; // print requested
521 va_start(args_print, format);
522 }
523
524 if (do_log) {
525 os_log_with_args(log,
526 type,
527 format,
528 args_log,
529 __builtin_return_address(0));
530 va_end(args_log);
531 }
532
533 if (do_print) {
534 __SCPrint(stdout,
535 format_CF,
536 args_print,
537 (_sc_log == 2), // trace
538 TRUE); // add newline
539 va_end(args_print);
540 }
541
542 return;
543 }
544
545
546 void
547 SCLog(Boolean condition, int level, CFStringRef formatString, ...)
548 {
549 va_list formatArguments;
550 va_list formatArguments_print;
551 Boolean log = FALSE;
552 Boolean print = FALSE;
553
554 if (!condition) {
555 return;
556 }
557
558 /*
559 * Note: The following are the expected values for _sc_log
560 *
561 * 0 if SC messages should be written to stdout/stderr
562 * 1 if SC messages should be logged w/os_log(3)
563 * 2 if SC messages should be written to stdout/stderr AND logged
564 * 3 if SC messages should be logged AND written to stdout/stderr (w/o timestamp)
565 */
566
567 if (_sc_log > 0) {
568 log = TRUE; // log requested
569 va_start(formatArguments, formatString);
570
571 if (_sc_log > 1) {
572 print = TRUE; // log AND print requested
573 va_copy(formatArguments_print, formatArguments);
574 }
575 } else {
576 print = TRUE; // print requested
577 va_start(formatArguments_print, formatString);
578 }
579
580 if (log) {
581 const char *__format;
582
583 __format = CFStringGetCStringPtr(formatString, kCFStringEncodingUTF8);
584 if (__format != NULL) {
585 os_log_type_t __type;
586
587 __type = _SC_syslog_os_log_mapping(level);
588 __SCLog(__builtin_return_address(0), __type, __format, formatArguments);
589 }
590 va_end(formatArguments);
591 }
592
593 if (print) {
594 __SCPrint((LOG_PRI(level) > LOG_NOTICE) ? stderr : stdout,
595 formatString,
596 formatArguments_print,
597 (_sc_log == 2), // trace
598 TRUE); // add newline
599 va_end(formatArguments_print);
600 }
601
602 return;
603 }
604
605
606 void
607 SCPrint(Boolean condition, FILE *stream, CFStringRef formatString, ...)
608 {
609 va_list formatArguments;
610
611 if (!condition) {
612 return;
613 }
614
615 va_start(formatArguments, formatString);
616 __SCPrint(stream, formatString, formatArguments, FALSE, FALSE);
617 va_end(formatArguments);
618
619 return;
620 }
621
622
623 #pragma mark -
624 #pragma mark SC error handling / logging
625
626
627 const CFStringRef kCFErrorDomainSystemConfiguration = CFSTR("com.apple.SystemConfiguration");
628
629
630 static const struct sc_errmsg {
631 int status;
632 char *message;
633 } sc_errmsgs[] = {
634 { kSCStatusAccessError, "Permission denied" },
635 { kSCStatusConnectionIgnore, "Network connection information not available at this time" },
636 { kSCStatusConnectionNoService, "Network service for connection not available" },
637 { kSCStatusFailed, "Failed!" },
638 { kSCStatusInvalidArgument, "Invalid argument" },
639 { kSCStatusKeyExists, "Key already defined" },
640 { kSCStatusLocked, "Lock already held" },
641 { kSCStatusMaxLink, "Maximum link count exceeded" },
642 { kSCStatusNeedLock, "Lock required for this operation" },
643 { kSCStatusNoStoreServer, "Configuration daemon not (no longer) available" },
644 { kSCStatusNoStoreSession, "Configuration daemon session not active" },
645 { kSCStatusNoConfigFile, "Configuration file not found" },
646 { kSCStatusNoKey, "No such key" },
647 { kSCStatusNoLink, "No such link" },
648 { kSCStatusNoPrefsSession, "Preference session not active" },
649 { kSCStatusNotifierActive, "Notifier is currently active" },
650 { kSCStatusOK, "Success!" },
651 { kSCStatusPrefsBusy, "Preferences update currently in progress" },
652 { kSCStatusReachabilityUnknown, "Network reachability cannot be determined" },
653 { kSCStatusStale, "Write attempted on stale version of object" },
654 };
655 #define nSC_ERRMSGS (sizeof(sc_errmsgs)/sizeof(struct sc_errmsg))
656
657 void
658 _SCErrorSet(int error)
659 {
660 __SCThreadSpecificDataRef tsd;
661
662 tsd = __SCGetThreadSpecificData();
663 tsd->_sc_error = error;
664 return;
665 }
666
667
668 CFErrorRef
669 SCCopyLastError(void)
670 {
671 CFStringRef domain;
672 CFErrorRef error;
673 int i;
674 int code;
675 __SCThreadSpecificDataRef tsd;
676 CFMutableDictionaryRef userInfo = NULL;
677
678 tsd = __SCGetThreadSpecificData();
679 code =tsd->_sc_error;
680
681 for (i = 0; i < (int)nSC_ERRMSGS; i++) {
682 if (sc_errmsgs[i].status == code) {
683 CFStringRef str;
684
685 domain = kCFErrorDomainSystemConfiguration;
686 userInfo = CFDictionaryCreateMutable(NULL,
687 0,
688 &kCFCopyStringDictionaryKeyCallBacks,
689 &kCFTypeDictionaryValueCallBacks);
690 str = CFStringCreateWithCString(NULL,
691 sc_errmsgs[i].message,
692 kCFStringEncodingASCII);
693 CFDictionarySetValue(userInfo, kCFErrorDescriptionKey, str);
694 CFRelease(str);
695 goto done;
696 }
697 }
698
699 if ((code > 0) && (code <= ELAST)) {
700 domain = kCFErrorDomainPOSIX;
701 goto done;
702 }
703
704 domain = kCFErrorDomainMach;
705
706 done :
707
708 error = CFErrorCreate(NULL, domain, code, userInfo);
709 if (userInfo != NULL) CFRelease(userInfo);
710 return error;
711 }
712
713
714 int
715 SCError(void)
716 {
717 __SCThreadSpecificDataRef tsd;
718
719 tsd = __SCGetThreadSpecificData();
720 return tsd->_sc_error;
721 }
722
723
724 const char *
725 SCErrorString(int status)
726 {
727 int i;
728
729 for (i = 0; i < (int)nSC_ERRMSGS; i++) {
730 if (sc_errmsgs[i].status == status) {
731 return sc_errmsgs[i].message;
732 }
733 }
734
735 if ((status > 0) && (status <= ELAST)) {
736 return strerror(status);
737 }
738
739 if ((status >= BOOTSTRAP_SUCCESS) && (status <= BOOTSTRAP_NO_MEMORY)) {
740 return bootstrap_strerror(status);
741 }
742
743 return mach_error_string(status);
744 }