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