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