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