1/* 2 * Copyright (c) 2008 Apple Computer, 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#include <TargetConditionals.h> 25#if !TARGET_OS_EMBEDDED 26 #include <bless.h> 27 #include "bootcaches.h" 28#endif // !TARGET_OS_EMBEDDED 29 30#include <libc.h> 31#include <sysexits.h> 32#include <asl.h> 33#include <syslog.h> 34#include <sys/resource.h> 35#include <IOKit/kext/OSKext.h> 36#include <IOKit/kext/OSKextPrivate.h> 37 38#include "kext_tools_util.h" 39 40#if PRAGMA_MARK 41#pragma mark Basic Utility 42#endif /* PRAGMA_MARK */ 43 44/********************************************************************* 45*********************************************************************/ 46char * createUTF8CStringForCFString(CFStringRef aString) 47{ 48 char * result = NULL; 49 CFIndex bufferLength = 0; 50 51 if (!aString) { 52 goto finish; 53 } 54 55 bufferLength = sizeof('\0') + 56 CFStringGetMaximumSizeForEncoding(CFStringGetLength(aString), 57 kCFStringEncodingUTF8); 58 59 result = (char *)malloc(bufferLength * sizeof(char)); 60 if (!result) { 61 goto finish; 62 } 63 if (!CFStringGetCString(aString, result, bufferLength, 64 kCFStringEncodingUTF8)) { 65 66 SAFE_FREE_NULL(result); 67 goto finish; 68 } 69 70finish: 71 return result; 72} 73 74/******************************************************************************* 75* createCFMutableArray() 76*******************************************************************************/ 77Boolean createCFMutableArray(CFMutableArrayRef * array, 78 const CFArrayCallBacks * callbacks) 79{ 80 Boolean result = true; 81 82 *array = CFArrayCreateMutable(kCFAllocatorDefault, 0, 83 callbacks); 84 if (!*array) { 85 result = false; 86 } 87 return result; 88} 89 90/******************************************************************************* 91* createCFMutableDictionary() 92*******************************************************************************/ 93Boolean createCFMutableDictionary(CFMutableDictionaryRef * dict) 94{ 95 Boolean result = true; 96 97 *dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 98 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 99 if (!*dict) { 100 result = false; 101 } 102 return result; 103} 104 105/******************************************************************************* 106* createCFMutableSet() 107*******************************************************************************/ 108Boolean createCFMutableSet(CFMutableSetRef * setOut, 109 const CFSetCallBacks * callbacks) 110{ 111 Boolean result = true; 112 113 *setOut = CFSetCreateMutable(kCFAllocatorDefault, 0, 114 callbacks); 115 if (!*setOut) { 116 result = false; 117 } 118 return result; 119} 120 121/******************************************************************************* 122*******************************************************************************/ 123void addToArrayIfAbsent(CFMutableArrayRef array, const void * value) 124{ 125 if (kCFNotFound == CFArrayGetFirstIndexOfValue(array, RANGE_ALL(array), 126 value)) { 127 128 CFArrayAppendValue(array, value); 129 } 130 return; 131} 132 133/******************************************************************************* 134 * createCFDataFromFile() 135 *******************************************************************************/ 136Boolean createCFDataFromFile(CFDataRef *dataRefOut, 137 const char *filePath) 138{ 139 int fd = -1; 140 Boolean result = false; 141 struct stat statBuf; 142 void *buffer; 143 CFIndex length; 144 145 *dataRefOut = NULL; 146 fd = open(filePath, O_RDONLY, 0); 147 if (fd < 0) { 148 goto finish; 149 } 150 if (fstat(fd, &statBuf) != 0) { 151 goto finish; 152 } 153 if ((statBuf.st_mode & S_IFMT) != S_IFREG) { 154 goto finish; 155 } 156 if (statBuf.st_size == 0) { 157 goto finish; 158 } 159 160 // fill buffer used for CFData passed to caller 161 length = (CFIndex) statBuf.st_size; 162 buffer = CFAllocatorAllocate(kCFAllocatorDefault, length, 0); 163 if (read(fd, buffer, length) < 0) { 164 goto finish; 165 } 166 167 *dataRefOut = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 168 (const UInt8 *)buffer, 169 length, 170 kCFAllocatorDefault); 171 if (*dataRefOut == NULL) { 172 CFAllocatorDeallocate(kCFAllocatorDefault, buffer); 173 goto finish; 174 } 175 result = true; 176finish: 177 if (fd != -1) { 178 close(fd); 179 } 180 if (result == false) { 181 OSKextLog(/* kext */ NULL, 182 kOSKextLogErrorLevel, 183 "%s: failed for '%s'", __func__, filePath); 184 } 185 return result; 186} 187 188 189/******************************************************************************* 190 *******************************************************************************/ 191ExitStatus writeToFile( 192 int fileDescriptor, 193 const UInt8 * data, 194 CFIndex length) 195{ 196 ExitStatus result = EX_OSERR; 197 ssize_t bytesWritten = 0; 198 ssize_t totalBytesWritten = 0; 199 200 while (totalBytesWritten < length) { 201 bytesWritten = write(fileDescriptor, data + totalBytesWritten, 202 length - totalBytesWritten); 203 if (bytesWritten < 0) { 204 OSKextLog(/* kext */ NULL, 205 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 206 "Write failed - %s", strerror(errno)); 207 goto finish; 208 } 209 totalBytesWritten += bytesWritten; 210 } 211 212 result = EX_OK; 213finish: 214 return result; 215} 216 217#if !TARGET_OS_EMBEDDED 218/****************************************************************************** 219 ******************************************************************************/ 220 221void postNoteAboutKexts( CFStringRef theNotificationCenterName, 222 CFMutableDictionaryRef theDict ) 223{ 224 CFNotificationCenterRef myCenter = NULL; 225 226 if (theDict == NULL || theNotificationCenterName == NULL) 227 return; 228 229 myCenter = CFNotificationCenterGetDistributedCenter(); 230 CFRetain(theDict); 231 232 CFNotificationCenterPostNotificationWithOptions( 233 myCenter, 234 theNotificationCenterName, 235 NULL, 236 theDict, 237 kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions ); 238 239 SAFE_RELEASE(theDict); 240 241 return; 242} 243 244/****************************************************************************** 245 * postNoteAboutKextLoadsMT will use CFNotificationCenter to post a notification. 246 * The notification center is named by theNotificationCenterName. This routine 247 * is used to notify kextd about a kext that is getting loaded for message 248 * tracing. 249 ******************************************************************************/ 250 251void postNoteAboutKextLoadsMT(CFStringRef theNotificationCenterName, 252 CFMutableArrayRef theKextPathArray) 253{ 254 CFMutableDictionaryRef myInfoDict = NULL; // must release 255 CFNotificationCenterRef myCenter = NULL; 256 257 if (theKextPathArray == NULL || theNotificationCenterName == NULL) 258 return; 259 260 myCenter = CFNotificationCenterGetDistributedCenter(); 261 myInfoDict = CFDictionaryCreateMutable( 262 kCFAllocatorDefault, 0, 263 &kCFCopyStringDictionaryKeyCallBacks, 264 &kCFTypeDictionaryValueCallBacks); 265 266 if (myInfoDict && myCenter) { 267 CFDictionaryAddValue(myInfoDict, 268 CFSTR("KextArrayKey"), 269 theKextPathArray); 270 271 CFNotificationCenterPostNotificationWithOptions( 272 myCenter, 273 theNotificationCenterName, 274 NULL, 275 myInfoDict, 276 kCFNotificationDeliverImmediately | 277 kCFNotificationPostToAllSessions ); 278 } 279 280 SAFE_RELEASE(myInfoDict); 281 282 return; 283} 284 285/******************************************************************************* 286 ******************************************************************************/ 287void addKextToAlertDict( CFMutableDictionaryRef *theDictPtr, OSKextRef theKext ) 288{ 289 CFStringRef myBundleID; // do NOT release 290 CFStringRef myBundleVersion; // do NOT release 291 CFMutableArrayRef myKextArray; // do NOT release 292 CFURLRef myKextURL = NULL; // must release 293 CFStringRef myKextPath = NULL; // must release 294 CFMutableDictionaryRef myKextInfoDict = NULL; // must release 295 CFMutableDictionaryRef myAlertInfoDict = NULL; // do NOT release 296 CFIndex myCount, i; 297 298 if ( theDictPtr == NULL || theKext == NULL ) { 299 return; 300 } 301 302 myAlertInfoDict = *theDictPtr; 303 if (myAlertInfoDict == NULL) { 304 /* caller wants us to create Alert Info Dictionary */ 305 myAlertInfoDict = CFDictionaryCreateMutable( 306 kCFAllocatorDefault, 0, 307 &kCFCopyStringDictionaryKeyCallBacks, 308 &kCFTypeDictionaryValueCallBacks ); 309 if (myAlertInfoDict == NULL) { 310 return; 311 } 312 *theDictPtr = myAlertInfoDict; 313 } 314 315 myBundleID = OSKextGetIdentifier(theKext); 316 if ( myBundleID == NULL ) { 317 goto finish; 318 } 319 320 /* We never alert about Apple Kexts */ 321 if ( CFStringHasPrefix(myBundleID, __kOSKextApplePrefix) == true) { 322 goto finish; 323 } 324 325 myBundleVersion = OSKextGetValueForInfoDictionaryKey(theKext, 326 kCFBundleVersionKey); 327 if (myBundleVersion == NULL) { 328 goto finish; 329 } 330 331 myKextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext)); 332 if (myKextURL == NULL) { 333 goto finish; 334 } 335 336 myKextPath = CFURLCopyFileSystemPath(myKextURL, kCFURLPOSIXPathStyle); 337 if (myKextPath == NULL) { 338 goto finish; 339 } 340 341 /* add kext info to the Alert Dictionary. 342 * We want BundleID, Version and full path to the kext 343 */ 344 myKextArray = (CFMutableArrayRef) 345 CFDictionaryGetValue(myAlertInfoDict, CFSTR("KextInfoArrayKey")); 346 if (myKextArray == NULL) { 347 /* first kext info so create the kext info array */ 348 myKextArray = CFArrayCreateMutable(kCFAllocatorDefault, 349 0, 350 &kCFTypeArrayCallBacks); 351 if (myKextArray == NULL) { 352 goto finish; 353 } 354 CFDictionarySetValue(myAlertInfoDict, 355 CFSTR("KextInfoArrayKey"), 356 myKextArray); 357 } 358 359 /* check for dup of this kext */ 360 myCount = CFArrayGetCount(myKextArray); 361 if (myCount > 0) { 362 for (i = 0; i < myCount; i++) { 363 CFMutableDictionaryRef myDict; 364 myDict = (CFMutableDictionaryRef) 365 CFArrayGetValueAtIndex(myKextArray, i); 366 if (myDict == NULL) continue; 367 368 if ( !CFDictionaryContainsValue(myDict, myBundleID) ) { 369 continue; 370 } 371 if ( !CFDictionaryContainsValue(myDict, 372 myBundleVersion) ) { 373 continue; 374 } 375 /* already have this one so bail */ 376 goto finish; 377 } 378 } 379 380 /* new kext info to add */ 381 myKextInfoDict = CFDictionaryCreateMutable( 382 kCFAllocatorDefault, 0, 383 &kCFCopyStringDictionaryKeyCallBacks, 384 &kCFTypeDictionaryValueCallBacks); 385 if (myKextInfoDict == NULL) { 386 goto finish; 387 } 388 CFDictionaryAddValue(myKextInfoDict, 389 kCFBundleIdentifierKey, 390 myBundleID); 391 CFDictionaryAddValue(myKextInfoDict, 392 kCFBundleVersionKey, 393 myBundleVersion); 394 CFDictionaryAddValue(myKextInfoDict, 395 CFSTR("KextPathKey"), 396 myKextPath); 397 398 CFArrayAppendValue(myKextArray, 399 myKextInfoDict); 400 401finish: 402 SAFE_RELEASE(myKextURL); 403 SAFE_RELEASE(myKextPath); 404 SAFE_RELEASE(myKextInfoDict); 405 406 return; 407} 408 409 410/******************************************************************************* 411 * isDebugSetInBootargs() - check to see if boot-args has debug set. We cache 412 * the result. 413 *******************************************************************************/ 414Boolean isDebugSetInBootargs(void) 415{ 416 static int didOnce = 0; 417 static Boolean result = false; 418 io_registry_entry_t optionsNode = MACH_PORT_NULL; // must release 419 CFStringRef bootargsEntry = NULL; // must release 420 421 if (didOnce) { 422 return(result); 423 } 424 optionsNode = IORegistryEntryFromPath(kIOMasterPortDefault, 425 "IODeviceTree:/options"); 426 if (optionsNode) { 427 bootargsEntry = (CFStringRef) 428 IORegistryEntryCreateCFProperty(optionsNode, 429 CFSTR("boot-args"), 430 kCFAllocatorDefault, 0); 431 if (bootargsEntry && 432 (CFGetTypeID(bootargsEntry) == CFStringGetTypeID())) { 433 CFRange findRange; 434 findRange = CFStringFind(bootargsEntry, CFSTR("debug"), 0); 435 436 if (findRange.length != 0) { 437 result = true; 438 } 439 } 440 } 441 didOnce++; 442 if (optionsNode) IOObjectRelease(optionsNode); 443 SAFE_RELEASE(bootargsEntry); 444 445 return(result); 446} 447 448#endif // !TARGET_OS_EMBEDDED 449 450#if PRAGMA_MARK 451#pragma mark Path & File 452#endif /* PRAGMA_MARK */ 453/******************************************************************************* 454*******************************************************************************/ 455ExitStatus checkPath( 456 const char * path, 457 const char * suffix, // w/o the dot 458 Boolean directoryRequired, 459 Boolean writableRequired) 460{ 461 Boolean result = EX_USAGE; 462 Boolean nameBad = FALSE; 463 struct stat statBuffer; 464 465 if (!path) { 466 OSKextLog(/* kext */ NULL, 467 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 468 "Internal error - %s - NULL path.", 469 __FUNCTION__); 470 result = EX_SOFTWARE; 471 goto finish; 472 } 473 474 result = EX_USAGE; 475 if (suffix) { 476 size_t pathLength = strlen(path); 477 size_t suffixLength = strlen(suffix); 478 size_t suffixIndex = 0; 479 size_t periodIndex = 0; 480 481 nameBad = TRUE; 482 if (!pathLength || !suffixLength) { 483 OSKextLog(/* kext */ NULL, 484 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 485 "Internal error - %s - empty string.", 486 __FUNCTION__); 487 result = EX_SOFTWARE; 488 goto finish; 489 } 490 491 /* Peel off any trailing '/' characters (silly shell completion), 492 * then advance the length back to point to the character past 493 * the real end (which will be a slash or '\0'). 494 */ 495 while (pathLength-- && path[pathLength] == '/') { 496 /* just scanning for last non-slash */ 497 if (!pathLength) { 498 goto finish; 499 } 500 } 501 pathLength++; 502 503 if (suffixLength >= pathLength) { 504 goto finish; 505 } 506 suffixIndex = pathLength - suffixLength; 507 periodIndex = suffixIndex - 1; 508 if (path[periodIndex] != '.' || 509 strncmp(path + suffixIndex, suffix, suffixLength)) { 510 goto finish; 511 } 512 nameBad = FALSE; 513 } 514 515 result = EX_NOINPUT; 516 if (0 != stat(path, &statBuffer)) { 517 OSKextLog(/* kext */ NULL, 518 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 519 "Can't stat %s - %s.", path, 520 strerror(errno)); 521 goto finish; 522 } 523 524 if (directoryRequired && ((statBuffer.st_mode & S_IFMT) != S_IFDIR) ) { 525 OSKextLog(/* kext */ NULL, 526 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 527 "%s is not a directory.", 528 path); 529 goto finish; 530 } 531 532 result = EX_NOPERM; 533 if (writableRequired && access(path, W_OK) == -1) { 534 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 535 "%s is not writable.", path); 536 goto finish; 537 } 538 539 result = EX_OK; 540 541finish: 542 if (nameBad) { 543 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 544 "%s not of type '%s'.", path, suffix); 545 } 546 return result; 547} 548 549/******************************************************************************* 550*******************************************************************************/ 551void 552saveFile(const void * vKey, const void * vValue, void * vContext) 553{ 554 CFStringRef key = (CFStringRef)vKey; 555 CFDataRef fileData = (CFDataRef)vValue; 556 SaveFileContext * context = (SaveFileContext *)vContext; 557 558 long length; 559 int fd = -1; 560 mode_t mode = 0666; 561 struct stat statBuf; 562 CFURLRef saveURL = NULL; // must release 563 Boolean fileExists = false; 564 char savePath[PATH_MAX]; 565 566 if (context->fatal) { 567 goto finish; 568 } 569 570 saveURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, 571 context->saveDirURL, key, /* isDirectory */ false); 572 if (!saveURL) { 573 context->fatal = true; 574 goto finish; 575 } 576 577 if (!CFURLGetFileSystemRepresentation(saveURL, /* resolveToBase */ true, 578 (u_char *)savePath, sizeof(savePath))) { 579 context->fatal = true; 580 goto finish; 581 } 582 583 if (!context->overwrite) { 584 fileExists = CFURLResourceIsReachable(saveURL, NULL); 585 if (fileExists) { 586 switch (user_approve(/* ask_all */ TRUE, /* default_answer */ REPLY_YES, 587 "%s exists, overwrite", savePath)) { 588 589 case REPLY_YES: 590 // go ahead and overwrite. 591 break; 592 case REPLY_ALL: 593 // go ahead and overwrite this and all following. 594 fprintf(stderr, 595 "Overwriting all symbol files for kexts in dependency graph.\n"); 596 context->overwrite = TRUE; 597 break; 598 case REPLY_NO: 599 goto finish; 600 break; 601 default: 602 context->fatal = true; 603 goto finish; 604 break; 605 } 606 } 607 else { 608 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 609 "%s missing '%s'", __func__, savePath); 610 context->fatal = true; 611 goto finish; 612 } 613 } 614 615 /* Write data. 616 */ 617 length = CFDataGetLength(fileData); 618 if (0 == stat(savePath, &statBuf)) { 619 mode = statBuf.st_mode; 620 } 621 fd = open(savePath, O_WRONLY|O_CREAT|O_TRUNC, mode); 622 if (fd != -1 && length) { 623 ExitStatus result; 624 result = writeToFile(fd, CFDataGetBytePtr(fileData), length); 625 if (result != EX_OK) { 626 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 627 "%s write failed for '%s'", __func__, savePath); 628 } 629 } 630 else { 631 /* Is this fatal to the whole program? I'd rather soldier on. 632 */ 633 OSKextLog(/* kext */ NULL, 634 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 635 "%s Failed to save '%s'", __func__, savePath); 636 } 637 638finish: 639 if (fd != -1) { 640 fsync(fd); 641 close(fd); 642 } 643 SAFE_RELEASE(saveURL); 644 return; 645} 646 647/******************************************************************************* 648*******************************************************************************/ 649CFStringRef copyKextPath(OSKextRef aKext) 650{ 651 CFStringRef result = NULL; 652 CFURLRef absURL = NULL; // must release 653 654 if (!OSKextGetURL(aKext)) { 655 goto finish; 656 } 657 658 absURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext)); 659 if (!absURL) { 660 goto finish; 661 } 662 result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 663finish: 664 SAFE_RELEASE(absURL); 665 return result; 666} 667 668 669/******************************************************************************* 670 * Returns the access and mod times from the file in the given array of 671 * fileURLs with the latest mod time. 672 * 11860417 - support multiple extensions directories 673 *******************************************************************************/ 674ExitStatus 675getLatestTimesFromCFURLArray( 676 CFArrayRef dirURLArray, 677 struct timeval dirTimeVals[2]) 678{ 679 ExitStatus result = EX_SOFTWARE; 680 int i; 681 CFURLRef myURL; 682 struct stat myStatBuf; 683 struct timeval myTempModTime; 684 struct timeval myTempAccessTime; 685 686 if (dirURLArray == NULL) { 687 goto finish; 688 } 689 bzero(dirTimeVals, (sizeof(struct timeval) * 2)); 690 691 for (i = 0; i < CFArrayGetCount(dirURLArray); i++) { 692 myURL = (CFURLRef) CFArrayGetValueAtIndex(dirURLArray, i); 693 if (myURL == NULL) { 694 OSKextLog(NULL, 695 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 696 "%s: NO fileURL at index %d!!!! ", __FUNCTION__, i); 697 goto finish; 698 } 699 700 result = statURL(myURL, &myStatBuf); 701 if (result != EX_OK) { 702 goto finish; 703 } 704 TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec); 705 TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec); 706 707 if (timercmp(&myTempModTime, &dirTimeVals[1], >)) { 708 dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec; 709 dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec; 710 dirTimeVals[1].tv_sec = myTempModTime.tv_sec; 711 dirTimeVals[1].tv_usec = myTempModTime.tv_usec; 712 } 713 } 714 715 result = EX_OK; 716finish: 717 return result; 718} 719 720/******************************************************************************* 721 * Returns the access and mod times from the file in the given directory with 722 * the latest mod time. 723 *******************************************************************************/ 724ExitStatus 725getLatestTimesFromDirURL( 726 CFURLRef dirURL, 727 struct timeval dirTimeVals[2]) 728{ 729 ExitStatus result = EX_SOFTWARE; 730 CFURLEnumeratorRef myEnumerator = NULL; // must release 731 struct stat myStatBuf; 732 struct timeval myTempModTime; 733 struct timeval myTempAccessTime; 734 735 bzero(dirTimeVals, (sizeof(struct timeval) * 2)); 736 737 if (dirURL == NULL) { 738 goto finish; 739 } 740 741 myEnumerator = CFURLEnumeratorCreateForDirectoryURL( 742 NULL, 743 dirURL, 744 kCFURLEnumeratorDefaultBehavior, 745 NULL ); 746 if (myEnumerator == NULL) { 747 OSKextLogMemError(); 748 goto finish; 749 } 750 CFURLRef myURL = NULL; 751 while (CFURLEnumeratorGetNextURL( 752 myEnumerator, 753 &myURL, 754 NULL) == kCFURLEnumeratorSuccess) { 755 if (statURL(myURL, &myStatBuf) != EX_OK) { 756 goto finish; 757 } 758 TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec); 759 TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec); 760 761 if (timercmp(&myTempModTime, &dirTimeVals[1], >)) { 762 dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec; 763 dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec; 764 dirTimeVals[1].tv_sec = myTempModTime.tv_sec; 765 dirTimeVals[1].tv_usec = myTempModTime.tv_usec; 766 } 767 } // while loop... 768 769 result = EX_OK; 770finish: 771 if (myEnumerator) CFRelease(myEnumerator); 772 return result; 773} 774 775/******************************************************************************* 776 * Returns the access and mod times from the file in the given directory with 777 * the latest mod time. 778 *******************************************************************************/ 779ExitStatus 780getLatestTimesFromDirPath( 781 const char * dirPath, 782 struct timeval dirTimeVals[2]) 783{ 784 ExitStatus result = EX_SOFTWARE; 785 CFURLRef kernURL = NULL; // must release 786 787 if (dirPath == NULL) { 788 goto finish; 789 } 790 791 kernURL = CFURLCreateFromFileSystemRepresentation( 792 NULL, 793 (const UInt8 *)dirPath, 794 strlen(dirPath), 795 true ); 796 if (kernURL == NULL) { 797 OSKextLogMemError(); 798 goto finish; 799 } 800 801 result = getLatestTimesFromDirURL(kernURL, dirTimeVals); 802finish: 803 if (kernURL) CFRelease(kernURL); 804 return result; 805} 806 807/******************************************************************************* 808 *******************************************************************************/ 809ExitStatus 810getParentPathTimes( 811 const char * thePath, 812 struct timeval cacheFileTimes[2] ) 813{ 814 ExitStatus result = EX_SOFTWARE; 815 char * lastSlash = NULL; 816 char myTempPath[PATH_MAX]; 817 818 if (thePath == NULL) { 819 goto finish; 820 } 821 822 lastSlash = strrchr(thePath, '/'); 823 // bail if no '/' or if length is < 2 (shortest possible dir path "/a/") 824 if (lastSlash == NULL || (lastSlash - thePath) < 2) { 825 goto finish; 826 } 827 // drop off everything at last '/' and beyond 828 if (strlcpy(myTempPath, 829 thePath, 830 (lastSlash - thePath) + 1) >= PATH_MAX) { 831 goto finish; 832 } 833 834 result = getFilePathTimes(myTempPath, cacheFileTimes); 835finish: 836 return result; 837} 838 839/******************************************************************************* 840 *******************************************************************************/ 841ExitStatus 842getFilePathTimes( 843 const char * filePath, 844 struct timeval cacheFileTimes[2]) 845{ 846 struct stat statBuffer; 847 ExitStatus result = EX_SOFTWARE; 848 849 result = statPath(filePath, &statBuffer); 850 if (result != EX_OK) { 851 goto finish; 852 } 853 854 TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &statBuffer.st_atimespec); 855 TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &statBuffer.st_mtimespec); 856 857 result = EX_OK; 858finish: 859 return result; 860} 861 862 863/******************************************************************************* 864 *******************************************************************************/ 865ExitStatus 866statURL(CFURLRef anURL, struct stat * statBuffer) 867{ 868 ExitStatus result = EX_OSERR; 869 char path[PATH_MAX]; 870 871 if (!CFURLGetFileSystemRepresentation(anURL, /* resolveToBase */ true, 872 (UInt8 *)path, sizeof(path))) 873 { 874 OSKextLogStringError(/* kext */ NULL); 875 goto finish; 876 } 877 878 result = statPath(path, statBuffer); 879 if (!result) { 880 goto finish; 881 } 882 883 result = EX_OK; 884finish: 885 return result; 886} 887 888/******************************************************************************* 889 *******************************************************************************/ 890ExitStatus 891statPath(const char *path, struct stat *statBuffer) 892{ 893 ExitStatus result = EX_OSERR; 894 895 if (stat(path, statBuffer)) { 896 OSKextLog(/* kext */ NULL, 897 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 898 "Can't stat %s - %s.", path, strerror(errno)); 899 goto finish; 900 } 901 902 result = EX_OK; 903 904finish: 905 return result; 906} 907 908/******************************************************************************* 909 *******************************************************************************/ 910ExitStatus 911statParentPath(const char *thePath, struct stat *statBuffer) 912{ 913 ExitStatus result = EX_SOFTWARE; 914 char * lastSlash = NULL; 915 char myTempPath[PATH_MAX]; 916 917 if (thePath == NULL) { 918 goto finish; 919 } 920 921 lastSlash = strrchr(thePath, '/'); 922 // bail if no '/' or if length is < 2 (shortest possible dir path "/a/") 923 if (lastSlash == NULL || (lastSlash - thePath) < 2) { 924 goto finish; 925 } 926 // drop off everything at last '/' and beyond 927 if (strlcpy(myTempPath, 928 thePath, 929 (lastSlash - thePath) + 1) >= PATH_MAX) { 930 goto finish; 931 } 932 933 result = statPath(myTempPath, statBuffer); 934finish: 935 return result; 936} 937 938/******************************************************************************* 939 * caller must free returned pointer 940 *******************************************************************************/ 941char * 942getPathExtension(const char * pathPtr) 943{ 944 char * suffixPtr = NULL; // caller must free 945 CFURLRef pathURL = NULL; // must release 946 CFStringRef tmpCFString = NULL; // must release 947 948 pathURL = CFURLCreateFromFileSystemRepresentation( 949 NULL, 950 (const UInt8 *)pathPtr, 951 strlen(pathPtr), 952 true ); 953 if (pathURL == NULL) { 954 goto finish; 955 } 956 tmpCFString = CFURLCopyPathExtension(pathURL); 957 if (tmpCFString == NULL) { 958 goto finish; 959 } 960 suffixPtr = createUTF8CStringForCFString(tmpCFString); 961 962finish: 963 SAFE_RELEASE(pathURL); 964 SAFE_RELEASE(tmpCFString); 965 966 return suffixPtr; 967} 968 969#if PRAGMA_MARK 970#pragma mark Logging 971#endif /* PRAGMA_MARK */ 972/******************************************************************************* 973*******************************************************************************/ 974OSKextLogSpec _sLogSpecsForVerboseLevels[] = { 975 kOSKextLogErrorLevel | kOSKextLogVerboseFlagsMask, // [0xff1] -v 0 976 kOSKextLogBasicLevel | kOSKextLogVerboseFlagsMask, // [0xff3] -v 1 977 kOSKextLogProgressLevel | kOSKextLogVerboseFlagsMask, // [0xff4] -v 2 978 kOSKextLogStepLevel | kOSKextLogVerboseFlagsMask, // [0xff5] -v 3 979 kOSKextLogDetailLevel | kOSKextLogVerboseFlagsMask, // [0xff6] -v 4 980 kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask, // [0xff7] -v 5 981 kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask | // [0xfff] -v 6 982 kOSKextLogKextOrGlobalMask 983}; 984 985/******************************************************************************* 986* getopt_long_only() doesn't actually handle optional args very well. So, we 987* jump through some hoops here to handle all six possibilities: 988* 989* cmd line optarg argv[optind] 990* ---------------------------------------------- 991* -v (null) (following arg or null) 992* -v arg (null) arg 993* -v=arg (null) -v=arg -- ILLEGAL 994* -verbose (null) (following arg or null) 995* -verbose arg (null) arg 996* -verbose=arg arg (following arg or null) 997* 998* Note that only in the -verbose=arg case does optarg actually get set 999* correctly! 1000* 1001* If we have not optarg but a following argv[optind], we check it to see if 1002* it looks like a legal arg to -v/-verbose; if it matches we increment optind. 1003* -v has never allowed the argument to immediately follow (as in -v2), so 1004* we still don't handle that. 1005*******************************************************************************/ 1006#define kBadVerboseOptPrefix "-v=" 1007 1008ExitStatus setLogFilterForOpt( 1009 int argc, 1010 char * const * argv, 1011 OSKextLogSpec forceOnFlags) 1012{ 1013 ExitStatus result = EX_USAGE; 1014 OSKextLogSpec logFilter = 0; 1015 const char * localOptarg = NULL; 1016 1017 /* Must be a bare -v; just use the extra flags. 1018 */ 1019 if (!optarg && optind >= argc) { 1020 logFilter = _sLogSpecsForVerboseLevels[1]; 1021 1022 } else { 1023 1024 if (optarg) { 1025 localOptarg = optarg; 1026 } else { 1027 localOptarg = argv[optind]; 1028 } 1029 1030 if (!strncmp(localOptarg, kBadVerboseOptPrefix, 1031 sizeof(kBadVerboseOptPrefix) - 1)) { 1032 1033 OSKextLog(/* kext */ NULL, 1034 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1035 "%s - syntax error (don't use = with single-letter option args).", 1036 localOptarg); 1037 goto finish; 1038 } 1039 1040 /* Look for '-v0x####' with no space and advance to the 0x part. 1041 */ 1042 if (localOptarg[0] == '-' && localOptarg[1] == kOptVerbose && 1043 localOptarg[2] == '0' && (localOptarg[3] == 'x' || localOptarg[3] == 'X')) { 1044 1045 localOptarg += 2; 1046 } 1047 1048 /* Look for a 0x#### style verbose arg. 1049 */ 1050 if (localOptarg[0] == '0' && (localOptarg[1] == 'x' || localOptarg[1] == 'X')) { 1051 char * endptr = NULL; 1052 OSKextLogSpec parsedFlags = (unsigned)strtoul(localOptarg, &endptr, 16); 1053 1054 if (endptr[0]) { 1055 OSKextLog(/* kext */ NULL, 1056 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1057 "Can't parse verbose argument %s.", localOptarg); 1058 goto finish; 1059 } 1060 logFilter = parsedFlags; 1061 1062 if (!optarg) { 1063 optind++; 1064 } 1065 1066 /* Now a 0-6 style verbose arg. 1067 */ 1068 } else if (((localOptarg[0] >= '0') || (localOptarg[0] <= '6')) && 1069 (localOptarg[1] == '\0')) { 1070 1071 logFilter = _sLogSpecsForVerboseLevels[localOptarg[0] - '0']; 1072 if (!optarg) { 1073 optind++; 1074 } 1075 1076 /* Must be a -v with command args following; just use the extra flag. 1077 */ 1078 } else { 1079 logFilter = _sLogSpecsForVerboseLevels[1]; 1080 } 1081 } 1082 1083 logFilter = logFilter | forceOnFlags; 1084 1085 OSKextSetLogFilter(logFilter, /* kernel? */ false); 1086 OSKextSetLogFilter(logFilter, /* kernel? */ true); 1087 1088 result = EX_OK; 1089 1090finish: 1091 return result; 1092} 1093 1094/******************************************************************************* 1095*******************************************************************************/ 1096void beQuiet(void) 1097{ 1098 fclose(stdout); 1099 fclose(stderr); 1100 close(1); 1101 close(2); 1102 OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ false); 1103 OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ true); 1104 return; 1105} 1106 1107/******************************************************************************* 1108*******************************************************************************/ 1109FILE * g_log_stream = NULL; 1110aslclient gASLClientHandle = NULL; 1111aslmsg gASLMessage = NULL; // reused 1112// xxx - need to aslclose() 1113 1114void tool_openlog(const char * name) 1115{ 1116 // xxx - do we want separate name & facility? 1117 gASLClientHandle = asl_open(/* ident */ name, /* facility*/ name, 1118 /* options */ 0); 1119 gASLMessage = asl_new(ASL_TYPE_MSG); 1120 return; 1121} 1122 1123#if !TARGET_OS_EMBEDDED 1124/******************************************************************************* 1125 * Check to see if this is an Apple internal build. If apple internel then 1126 * use development kernel if it exists. 1127 * /System/Library/Kernels/kernel.development 1128 *******************************************************************************/ 1129Boolean useDevelopmentKernel(const char * theKernelPath) 1130{ 1131 struct stat statBuf; 1132 char * tempPath = NULL; 1133 size_t length = 0; 1134 Boolean myResult = FALSE; 1135 1136 if (statPath(kAppleInternalPath, &statBuf) != EX_OK) { 1137 return(myResult); 1138 } 1139 tempPath = malloc(PATH_MAX); 1140 1141 while (tempPath) { 1142 length = strlcpy(tempPath, theKernelPath, PATH_MAX); 1143 if (length >= PATH_MAX) break; 1144 length = strlcat(tempPath, 1145 kDefaultKernelSuffix, 1146 PATH_MAX); 1147 if (length >= PATH_MAX) break; 1148 if (statPath(tempPath, &statBuf) == EX_OK) { 1149 // use kernel.development 1150 myResult = TRUE; 1151 } 1152 break; 1153 } // while... 1154 1155 if (tempPath) free(tempPath); 1156 1157 return(myResult); 1158} 1159#endif // !TARGET_OS_EMBEDDED 1160 1161/******************************************************************************* 1162* Basic log function. If any log flags are set, log the message 1163* to syslog/stderr. Note: exported as SPI in bootroot.h. 1164*******************************************************************************/ 1165 1166void tool_log( 1167 OSKextRef aKext __unused, 1168 OSKextLogSpec msgLogSpec, 1169 const char * format, ...) 1170{ 1171 va_list ap; 1172 1173 if (gASLClientHandle) { 1174 int aslLevel = ASL_LEVEL_ERR; 1175 OSKextLogSpec kextLogLevel = msgLogSpec & kOSKextLogLevelMask; 1176 char messageLogSpec[16]; 1177 1178 if (kextLogLevel == kOSKextLogErrorLevel) { 1179 aslLevel = ASL_LEVEL_ERR; 1180 } else if (kextLogLevel == kOSKextLogWarningLevel) { 1181 aslLevel = ASL_LEVEL_WARNING; 1182 } else if (kextLogLevel == kOSKextLogBasicLevel) { 1183 aslLevel = ASL_LEVEL_NOTICE; 1184 } else if (kextLogLevel < kOSKextLogDebugLevel) { 1185 aslLevel = ASL_LEVEL_INFO; 1186 } else { 1187 aslLevel = ASL_LEVEL_DEBUG; 1188 } 1189 1190 snprintf(messageLogSpec, sizeof(messageLogSpec), "0x%x", msgLogSpec); 1191 asl_set(gASLMessage, "OSKextLogSpec", messageLogSpec); 1192 1193 va_start(ap, format); 1194 asl_vlog(gASLClientHandle, gASLMessage, aslLevel, format, ap); 1195 va_end(ap); 1196 1197 } else { 1198 // xxx - change to pick log stream based on log level 1199 // xxx - (0 == stdout, all others stderr) 1200 1201 if (!g_log_stream) { 1202 g_log_stream = stderr; 1203 } 1204 1205 va_start(ap, format); 1206 vfprintf(g_log_stream, format, ap); 1207 va_end(ap); 1208 1209 fprintf(g_log_stream, "\n"); 1210 fflush(g_log_stream); 1211 } 1212 1213 return; 1214} 1215 1216/******************************************************************************* 1217*******************************************************************************/ 1218void log_CFError( 1219 OSKextRef aKext __unused, 1220 OSKextLogSpec msgLogSpec, 1221 CFErrorRef error) 1222{ 1223 CFStringRef errorString = NULL; // must release 1224 char * cstring = NULL; // must release 1225 1226 if (!error) { 1227 return; 1228 } 1229 errorString = CFErrorCopyDescription(error); 1230 if (errorString) { 1231 cstring = createUTF8CStringForCFString(errorString); 1232 OSKextLog(/* kext */ NULL, msgLogSpec, 1233 "CFError descripton: %s.", cstring); 1234 SAFE_RELEASE_NULL(errorString); 1235 SAFE_FREE_NULL(cstring); 1236 } 1237 1238 errorString = CFErrorCopyFailureReason(error); 1239 if (errorString) { 1240 cstring = createUTF8CStringForCFString(errorString); 1241 OSKextLog(/* kext */ NULL, msgLogSpec, 1242 "CFError reason: %s.", cstring); 1243 SAFE_RELEASE_NULL(errorString); 1244 SAFE_FREE_NULL(cstring); 1245 } 1246 1247 return; 1248} 1249 1250#if !TARGET_OS_EMBEDDED 1251// log helper for libbless, exported as SPI via bootroot.h 1252int32_t 1253BRBLLogFunc(void *refcon __unused, int32_t level, const char *string) 1254{ 1255 OSKextLogSpec logSpec = kOSKextLogGeneralFlag; 1256 switch (level) { 1257 case kBLLogLevelVerbose: 1258 logSpec |= kOSKextLogDebugLevel; 1259 break; 1260 case kBLLogLevelError: 1261 logSpec |= kOSKextLogErrorLevel; 1262 break; 1263 default: 1264 logSpec |= kOSKextLogWarningLevel; 1265 } 1266 OSKextLog(NULL, logSpec, "%s", string); 1267 return 0; 1268} 1269 1270/******************************************************************************* 1271 * returns TRUE and fills the Buffer with path to kernel extracted from 1272 * Kernelcache dictionary from /usr/standalone/bootcaches.plist on the 1273 * given volume. If we can't find the path or if it does not exist or if 1274 * theBuffer is too small we return FALSE. 1275 * 1276 * If we find the kernel path in bootcaches.plist, but the file does not exist 1277 * we will copy the path into the buffer. In that case the result will be FALSE 1278 * and strlen on buffer will be > 0. 1279 * 1280 * theVolRootURL == NULL means we want root volume. 1281 *******************************************************************************/ 1282Boolean getKernelPathForURL(CFURLRef theVolRootURL, 1283 char * theBuffer, 1284 int theBufferSize) 1285{ 1286 CFDictionaryRef myDict = NULL; // must release 1287 CFDictionaryRef postBootPathsDict = NULL; // do not release 1288 CFDictionaryRef kernelCacheDict = NULL; // do not release 1289 Boolean myResult = FALSE; 1290 1291 if (theBuffer) { 1292 *theBuffer = 0x00; 1293 1294 myDict = copyBootCachesDictForURL(theVolRootURL); 1295 if (myDict != NULL) { 1296 postBootPathsDict = (CFDictionaryRef) 1297 CFDictionaryGetValue(myDict, kBCPostBootKey); 1298 1299 if (postBootPathsDict && 1300 CFGetTypeID(postBootPathsDict) == CFDictionaryGetTypeID()) { 1301 1302 kernelCacheDict = (CFDictionaryRef) 1303 CFDictionaryGetValue(postBootPathsDict, kBCKernelcacheV3Key); 1304 } 1305 } 1306 } // theBuffer 1307 1308 if (kernelCacheDict && 1309 CFGetTypeID(kernelCacheDict) == CFDictionaryGetTypeID()) { 1310 CFStringRef myTempStr; // do not release 1311 1312 myTempStr = (CFStringRef) CFDictionaryGetValue(kernelCacheDict, 1313 kBCKernelPathKey); 1314 if (myTempStr != NULL && 1315 CFGetTypeID(myTempStr) == CFStringGetTypeID()) { 1316 1317 if (CFStringGetFileSystemRepresentation(myTempStr, 1318 theBuffer, 1319 theBufferSize)) { 1320 struct stat statBuf; 1321 if (statPath(theBuffer, &statBuf) == EX_OK) { 1322 myResult = TRUE; 1323 } 1324 } 1325 } 1326 } // kernelCacheDict 1327 1328 SAFE_RELEASE(myDict); 1329 1330 return(myResult); 1331} 1332 1333/******************************************************************************* 1334 * returns copy of /usr/standalone/bootcaches.plist (as CFDictionary) from 1335 * given volume. 1336 * 1337 * theVolRootURL == NULL means we want root volume. 1338 * 1339 * Caller must release returned CFDictionaryRef. 1340 *******************************************************************************/ 1341CFDictionaryRef copyBootCachesDictForURL(CFURLRef theVolRootURL) 1342{ 1343 CFStringRef myVolRoot = NULL; // must release 1344 CFStringRef myPath = NULL; // must release 1345 CFURLRef myURL = NULL; // must release 1346 CFDictionaryRef myBootCachesPlist = NULL; // do not release 1347 1348 if (theVolRootURL) { 1349 myVolRoot = CFURLCopyFileSystemPath(theVolRootURL, 1350 kCFURLPOSIXPathStyle); 1351 if (myVolRoot == NULL) { 1352 goto finish; 1353 } 1354 myPath = CFStringCreateWithFormat( 1355 kCFAllocatorDefault, 1356 /* formatOptions */ NULL, 1357 CFSTR("%@%s"), 1358 myVolRoot, 1359 "/usr/standalone/bootcaches.plist" ); 1360 } 1361 else { 1362 myPath = CFStringCreateWithCString( 1363 kCFAllocatorDefault, 1364 "/usr/standalone/bootcaches.plist", 1365 kCFStringEncodingUTF8 ); 1366 } 1367 if (myPath == NULL) { 1368 goto finish; 1369 } 1370 1371 myURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, 1372 myPath, 1373 kCFURLPOSIXPathStyle, 1374 false ); 1375 if (myURL && CFURLResourceIsReachable(myURL, NULL)) { 1376 CFReadStreamRef readStream = NULL; // must release 1377 struct stat myStatBuf; 1378 ExitStatus myExitStatus; 1379 1380 myExitStatus = statURL(myURL, &myStatBuf); 1381 if (myExitStatus != EX_OK) { 1382 goto finish; 1383 } 1384 if (myStatBuf.st_uid != 0) { 1385 goto finish; 1386 } 1387 if (myStatBuf.st_mode & S_IWGRP || myStatBuf.st_mode & S_IWOTH) { 1388 goto finish; 1389 } 1390 1391 readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL); 1392 if (readStream) { 1393 if (CFReadStreamOpen(readStream)) { 1394 /* read in contents of bootcaches.plist */ 1395 myBootCachesPlist = CFPropertyListCreateWithStream( 1396 kCFAllocatorDefault, 1397 readStream, 1398 0, 1399 kCFPropertyListMutableContainersAndLeaves, 1400 NULL, NULL); 1401 CFReadStreamClose(readStream); 1402 } 1403 SAFE_RELEASE(readStream); 1404 } 1405 } 1406 1407finish: 1408 SAFE_RELEASE(myURL); 1409 SAFE_RELEASE(myPath); 1410 SAFE_RELEASE(myVolRoot); 1411 1412 return(myBootCachesPlist); 1413} 1414#endif // !TARGET_OS_EMBEDDED 1415 1416/******************************************************************************* 1417* safe_mach_error_string() 1418*******************************************************************************/ 1419const char * safe_mach_error_string(mach_error_t error_code) 1420{ 1421 const char * result = mach_error_string(error_code); 1422 if (!result) { 1423 result = "(unknown)"; 1424 } 1425 return result; 1426} 1427 1428#if PRAGMA_MARK 1429#pragma mark User Input 1430#endif /* PRAGMA_MARK */ 1431/******************************************************************************* 1432* user_approve() 1433* 1434* Ask the user a question and wait for a yes/no answer. 1435*******************************************************************************/ 1436int user_approve(Boolean ask_all, int default_answer, const char * format, ...) 1437{ 1438 int result = REPLY_YES; 1439 va_list ap; 1440 char fake_buffer[2]; 1441 int output_length; 1442 char * output_string; 1443 int c, x; 1444 1445 va_start(ap, format); 1446 output_length = vsnprintf(fake_buffer, 1, format, ap); 1447 va_end(ap); 1448 1449 output_string = (char *)malloc(output_length + 1); 1450 if (!output_string) { 1451 result = REPLY_ERROR; 1452 goto finish; 1453 } 1454 1455 va_start(ap, format); 1456 vsnprintf(output_string, output_length + 1, format, ap); 1457 va_end(ap); 1458 1459 while ( 1 ) { 1460 fprintf(stderr, "%s [%s/%s", output_string, 1461 (default_answer == REPLY_YES) ? "Y" : "y", 1462 (default_answer == REPLY_NO) ? "N" : "n"); 1463 if (ask_all) { 1464 fprintf(stderr, "/%s", 1465 (default_answer == REPLY_ALL) ? "A" : "a"); 1466 } 1467 fprintf(stderr, "]? "); 1468 fflush(stderr); 1469 1470 c = fgetc(stdin); 1471 1472 if (c == EOF) { 1473 result = REPLY_ERROR; 1474 goto finish; 1475 } 1476 1477 /* Make sure we get a newline. 1478 */ 1479 if ( c != '\n' ) { 1480 do { 1481 x = fgetc(stdin); 1482 } while (x != '\n' && x != EOF); 1483 1484 if (x == EOF) { 1485 result = REPLY_ERROR; 1486 goto finish; 1487 } 1488 } 1489 1490 if (c == '\n') { 1491 result = default_answer; 1492 goto finish; 1493 } else if (tolower(c) == 'y') { 1494 result = REPLY_YES; 1495 goto finish; 1496 } else if (tolower(c) == 'n') { 1497 result = REPLY_NO; 1498 goto finish; 1499 } else if (ask_all && tolower(c) == 'a') { 1500 result = REPLY_ALL; 1501 goto finish; 1502 } else { 1503 fprintf(stderr, "Please answer 'y' or 'n'%s.\n", 1504 ask_all ? " or 'a'" : ""); 1505 } 1506 } 1507 1508finish: 1509 if (output_string) free(output_string); 1510 1511 return result; 1512} 1513 1514/******************************************************************************* 1515* user_input() 1516* 1517* Ask the user for input. 1518*******************************************************************************/ 1519const char * user_input(Boolean * eof, const char * format, ...) 1520{ 1521 char * result = NULL; // return value 1522 va_list ap; 1523 char fake_buffer[2]; 1524 int output_length; 1525 char * output_string = NULL; 1526 unsigned index; 1527 size_t size = 80; // more than enough to input a hex address 1528 int c; 1529 1530 if (eof) { 1531 *eof = false; 1532 } 1533 1534 result = (char *)malloc(size); 1535 if (!result) { 1536 goto finish; 1537 } 1538 index = 0; 1539 1540 va_start(ap, format); 1541 output_length = vsnprintf(fake_buffer, 1, format, ap); 1542 va_end(ap); 1543 1544 output_string = (char *)malloc(output_length + 1); 1545 if (!output_string) { 1546 if (result) free(result); 1547 result = NULL; 1548 goto finish; 1549 } 1550 1551 va_start(ap, format); 1552 vsnprintf(output_string, output_length + 1, format, ap); 1553 va_end(ap); 1554 1555 fprintf(stderr, "%s ", output_string); 1556 fflush(stderr); 1557 1558 c = fgetc(stdin); 1559 while (c != '\n' && c != EOF) { 1560 if (index >= (size - 1)) { 1561 fprintf(stderr, "input line too long\n"); 1562 if (result) free(result); 1563 result = NULL; 1564 goto finish; 1565 } 1566 result[index++] = (char)c; 1567 c = fgetc(stdin); 1568 } 1569 1570 result[index] = '\0'; 1571 1572 if (c == EOF) { 1573 if (result) free(result); 1574 result = NULL; 1575 if (eof) { 1576 *eof = true; 1577 } 1578 goto finish; 1579 } 1580 1581finish: 1582 if (output_string) free(output_string); 1583 1584 return result; 1585} 1586 1587#if PRAGMA_MARK 1588#pragma mark Caches 1589#endif /* PRAGMA_MARK */ 1590/******************************************************************************* 1591*******************************************************************************/ 1592Boolean readSystemKextPropertyValues( 1593 CFStringRef propertyKey, 1594 const NXArchInfo * arch, 1595 Boolean forceUpdateFlag, 1596 CFArrayRef * valuesOut) 1597{ 1598 Boolean result = false; 1599 CFArrayRef sysExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs(); 1600 CFMutableArrayRef values = NULL; // must release 1601 CFStringRef cacheBasename = NULL; // must release 1602 CFArrayRef kexts = NULL; // must release 1603 CFMutableDictionaryRef newDict = NULL; // must release 1604 CFStringRef kextPath = NULL; // must release 1605 CFTypeRef value = NULL; // do not release 1606 CFStringRef kextVersion = NULL; // do not release 1607 CFIndex count, i; 1608 1609 cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault, 1610 /* formatOptions */ NULL, CFSTR("%s%@"), 1611 _kKextPropertyValuesCacheBasename, 1612 propertyKey); 1613 if (!cacheBasename) { 1614 OSKextLogMemError(); 1615 goto finish; 1616 } 1617 1618 if (OSKextGetUsesCaches() && !forceUpdateFlag) { 1619 1620 /* See if we have an up-to-date cache containing an array, and return 1621 * that if we have one. 1622 */ 1623 if (_OSKextReadCache(sysExtensionsFolderURLs, cacheBasename, 1624 arch, _kOSKextCacheFormatCFXML, /* parseXML? */ true, 1625 (CFPropertyListRef *)&values)) { 1626 1627 if (values && CFGetTypeID(values) == CFArrayGetTypeID()) { 1628 result = true; 1629 goto finish; 1630 } 1631 } 1632 } 1633 1634 values = CFArrayCreateMutable(kCFAllocatorDefault, /* capacity */ 0, 1635 &kCFTypeArrayCallBacks); 1636 if (!values) { 1637 OSKextLogMemError(); 1638 goto finish; 1639 } 1640 1641 kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 1642 sysExtensionsFolderURLs); 1643 1644 if (!kexts) { 1645 // Create function should log error 1646 goto finish; 1647 } 1648 1649 count = CFArrayGetCount(kexts); 1650 1651 for (i = 0; i < count; i++) { 1652 OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i); 1653 1654 SAFE_RELEASE_NULL(newDict); 1655 SAFE_RELEASE_NULL(kextPath); 1656 // do not release kextVersion 1657 kextVersion = NULL; 1658 1659 if ((OSKextGetSimulatedSafeBoot() || OSKextGetActualSafeBoot()) && 1660 !OSKextIsLoadableInSafeBoot(aKext)) { 1661 1662 continue; 1663 } 1664 //??? if (OSKextGetLoadFailed(aKext)) continue; -- don't have in OSKext 1665 1666 value = OSKextGetValueForInfoDictionaryKey(aKext, propertyKey); 1667 if (!value) { 1668 continue; 1669 } 1670 1671 newDict = CFDictionaryCreateMutable( 1672 kCFAllocatorDefault, 0, 1673 &kCFTypeDictionaryKeyCallBacks, 1674 &kCFTypeDictionaryValueCallBacks); 1675 if (!newDict) { 1676 goto finish; 1677 } 1678 1679 CFDictionarySetValue(newDict, CFSTR("Data"), value); 1680 1681 CFDictionarySetValue(newDict, CFSTR("CFBundleIdentifier"), 1682 OSKextGetIdentifier(aKext)); 1683 1684 kextPath = copyKextPath(aKext); 1685 if (!kextPath) { 1686 goto finish; 1687 } 1688 CFDictionarySetValue(newDict, CFSTR("OSBundlePath"), kextPath); 1689 1690 kextVersion = OSKextGetValueForInfoDictionaryKey(aKext, 1691 CFSTR("CFBundleVersion")); 1692 if (!kextVersion) { 1693 goto finish; 1694 } 1695 CFDictionarySetValue(newDict, CFSTR("CFBundleVersion"), 1696 kextVersion); 1697 1698 CFArrayAppendValue(values, newDict); 1699 } 1700 1701 if (OSKextGetUsesCaches() || forceUpdateFlag) { 1702 _OSKextWriteCache(sysExtensionsFolderURLs, cacheBasename, 1703 arch, _kOSKextCacheFormatCFXML, values); 1704 } 1705 1706 result = true; 1707 1708finish: 1709 if (result && valuesOut && values) { 1710 *valuesOut = (CFArrayRef)CFRetain(values); 1711 } 1712 1713 SAFE_RELEASE(values); 1714 SAFE_RELEASE(cacheBasename); 1715 SAFE_RELEASE(kexts); 1716 SAFE_RELEASE(newDict); 1717 SAFE_RELEASE(kextPath); 1718 1719 return result; 1720} 1721 1722