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#endif // !TARGET_OS_EMBEDDED 28 29#include <libc.h> 30#include <sysexits.h> 31#include <asl.h> 32#include <syslog.h> 33#include <sys/resource.h> 34#include <IOKit/kext/OSKext.h> 35#include <IOKit/kext/OSKextPrivate.h> 36 37#include "kext_tools_util.h" 38 39 40#if PRAGMA_MARK 41#pragma mark Basic Utility 42#endif /* PRAGMA_MARK */ 43/********************************************************************* 44*********************************************************************/ 45char * createUTF8CStringForCFString(CFStringRef aString) 46{ 47 char * result = NULL; 48 CFIndex bufferLength = 0; 49 50 if (!aString) { 51 goto finish; 52 } 53 54 bufferLength = sizeof('\0') + 55 CFStringGetMaximumSizeForEncoding(CFStringGetLength(aString), 56 kCFStringEncodingUTF8); 57 58 result = (char *)malloc(bufferLength * sizeof(char)); 59 if (!result) { 60 goto finish; 61 } 62 if (!CFStringGetCString(aString, result, bufferLength, 63 kCFStringEncodingUTF8)) { 64 65 SAFE_FREE_NULL(result); 66 goto finish; 67 } 68 69finish: 70 return result; 71} 72 73/******************************************************************************* 74* createCFMutableArray() 75*******************************************************************************/ 76Boolean createCFMutableArray(CFMutableArrayRef * array, 77 const CFArrayCallBacks * callbacks) 78{ 79 Boolean result = true; 80 81 *array = CFArrayCreateMutable(kCFAllocatorDefault, 0, 82 callbacks); 83 if (!*array) { 84 result = false; 85 } 86 return result; 87} 88 89/******************************************************************************* 90* createCFMutableDictionary() 91*******************************************************************************/ 92Boolean createCFMutableDictionary(CFMutableDictionaryRef * dict) 93{ 94 Boolean result = true; 95 96 *dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, 97 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 98 if (!*dict) { 99 result = false; 100 } 101 return result; 102} 103 104/******************************************************************************* 105* createCFMutableSet() 106*******************************************************************************/ 107Boolean createCFMutableSet(CFMutableSetRef * setOut, 108 const CFSetCallBacks * callbacks) 109{ 110 Boolean result = true; 111 112 *setOut = CFSetCreateMutable(kCFAllocatorDefault, 0, 113 callbacks); 114 if (!*setOut) { 115 result = false; 116 } 117 return result; 118} 119 120/******************************************************************************* 121*******************************************************************************/ 122void addToArrayIfAbsent(CFMutableArrayRef array, const void * value) 123{ 124 if (kCFNotFound == CFArrayGetFirstIndexOfValue(array, RANGE_ALL(array), 125 value)) { 126 127 CFArrayAppendValue(array, value); 128 } 129 return; 130} 131 132/******************************************************************************* 133 * createCFDataFromFile() 134 *******************************************************************************/ 135Boolean createCFDataFromFile(CFDataRef *dataRefOut, 136 const char *filePath) 137{ 138 int fd = -1; 139 Boolean result = false; 140 struct stat statBuf; 141 void *buffer; 142 CFIndex length; 143 144 *dataRefOut = NULL; 145 fd = open(filePath, O_RDONLY, 0); 146 if (fd < 0) { 147 goto finish; 148 } 149 if (fstat(fd, &statBuf) != 0) { 150 goto finish; 151 } 152 if ((statBuf.st_mode & S_IFMT) != S_IFREG) { 153 goto finish; 154 } 155 if (statBuf.st_size == 0) { 156 goto finish; 157 } 158 159 // fill buffer used for CFData passed to caller 160 length = (CFIndex) statBuf.st_size; 161 buffer = CFAllocatorAllocate(kCFAllocatorDefault, length, 0); 162 if (read(fd, buffer, length) < 0) { 163 goto finish; 164 } 165 166 *dataRefOut = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, 167 (const UInt8 *)buffer, 168 length, 169 kCFAllocatorDefault); 170 if (*dataRefOut == NULL) { 171 CFAllocatorDeallocate(kCFAllocatorDefault, buffer); 172 goto finish; 173 } 174 result = true; 175finish: 176 if (fd != -1) { 177 close(fd); 178 } 179 if (result == false) { 180 OSKextLog(/* kext */ NULL, 181 kOSKextLogErrorLevel, 182 "%s: failed for '%s'", __func__, filePath); 183 } 184 return result; 185} 186 187 188/******************************************************************************* 189 *******************************************************************************/ 190ExitStatus writeToFile( 191 int fileDescriptor, 192 const UInt8 * data, 193 CFIndex length) 194{ 195 ExitStatus result = EX_OSERR; 196 ssize_t bytesWritten = 0; 197 ssize_t totalBytesWritten = 0; 198 199 while (totalBytesWritten < length) { 200 bytesWritten = write(fileDescriptor, data + totalBytesWritten, 201 length - totalBytesWritten); 202 if (bytesWritten < 0) { 203 OSKextLog(/* kext */ NULL, 204 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 205 "Write failed - %s", strerror(errno)); 206 goto finish; 207 } 208 totalBytesWritten += bytesWritten; 209 } 210 211 result = EX_OK; 212finish: 213 return result; 214} 215 216#if !TARGET_OS_EMBEDDED 217/****************************************************************************** 218 ******************************************************************************/ 219 220void postNoteAboutKexts( CFStringRef theNotificationCenterName, 221 CFMutableDictionaryRef theDict ) 222{ 223 CFNotificationCenterRef myCenter = NULL; 224 225 if (theDict == NULL || theNotificationCenterName == NULL) 226 return; 227 228 myCenter = CFNotificationCenterGetDistributedCenter(); 229 CFRetain(theDict); 230 231 CFNotificationCenterPostNotificationWithOptions( 232 myCenter, 233 theNotificationCenterName, 234 NULL, 235 theDict, 236 kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions ); 237 238 SAFE_RELEASE(theDict); 239 240 return; 241} 242 243/****************************************************************************** 244 * postNoteAboutKextLoadsMT will use CFNotificationCenter to post a notification. 245 * The notification center is named by theNotificationCenterName. This routine 246 * is used to notify kextd about a kext that is getting loaded for message 247 * tracing. 248 ******************************************************************************/ 249 250void postNoteAboutKextLoadsMT(CFStringRef theNotificationCenterName, 251 CFMutableArrayRef theKextPathArray) 252{ 253 CFMutableDictionaryRef myInfoDict = NULL; // must release 254 CFNotificationCenterRef myCenter = NULL; 255 256 if (theKextPathArray == NULL || theNotificationCenterName == NULL) 257 return; 258 259 myCenter = CFNotificationCenterGetDistributedCenter(); 260 myInfoDict = CFDictionaryCreateMutable( 261 kCFAllocatorDefault, 0, 262 &kCFCopyStringDictionaryKeyCallBacks, 263 &kCFTypeDictionaryValueCallBacks); 264 265 if (myInfoDict && myCenter) { 266 CFDictionaryAddValue(myInfoDict, 267 CFSTR("KextArrayKey"), 268 theKextPathArray); 269 270 CFNotificationCenterPostNotificationWithOptions( 271 myCenter, 272 theNotificationCenterName, 273 NULL, 274 myInfoDict, 275 kCFNotificationDeliverImmediately | 276 kCFNotificationPostToAllSessions ); 277 } 278 279 SAFE_RELEASE(myInfoDict); 280 281 return; 282} 283 284/******************************************************************************* 285 ******************************************************************************/ 286void addKextToAlertDict( CFMutableDictionaryRef *theDictPtr, OSKextRef theKext ) 287{ 288 CFStringRef myBundleID; // do NOT release 289 CFStringRef myBundleVersion; // do NOT release 290 CFMutableArrayRef myKextArray; // do NOT release 291 CFURLRef myKextURL = NULL; // must release 292 CFStringRef myKextPath = NULL; // must release 293 CFMutableDictionaryRef myKextInfoDict = NULL; // must release 294 CFMutableDictionaryRef myAlertInfoDict = NULL; // do NOT release 295 CFIndex myCount, i; 296 297 if ( theDictPtr == NULL || theKext == NULL ) { 298 return; 299 } 300 301 myAlertInfoDict = *theDictPtr; 302 if (myAlertInfoDict == NULL) { 303 /* caller wants us to create Alert Info Dictionary */ 304 myAlertInfoDict = CFDictionaryCreateMutable( 305 kCFAllocatorDefault, 0, 306 &kCFCopyStringDictionaryKeyCallBacks, 307 &kCFTypeDictionaryValueCallBacks ); 308 if (myAlertInfoDict == NULL) { 309 return; 310 } 311 *theDictPtr = myAlertInfoDict; 312 } 313 314 myBundleID = OSKextGetIdentifier(theKext); 315 if ( myBundleID == NULL ) { 316 goto finish; 317 } 318 319 /* We never alert about Apple Kexts */ 320 if ( CFStringHasPrefix(myBundleID, __kOSKextApplePrefix) == true) { 321 goto finish; 322 } 323 324 myBundleVersion = OSKextGetValueForInfoDictionaryKey(theKext, 325 kCFBundleVersionKey); 326 if (myBundleVersion == NULL) { 327 goto finish; 328 } 329 330 myKextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext)); 331 if (myKextURL == NULL) { 332 goto finish; 333 } 334 335 myKextPath = CFURLCopyFileSystemPath(myKextURL, kCFURLPOSIXPathStyle); 336 if (myKextPath == NULL) { 337 goto finish; 338 } 339 340 /* add kext info to the Alert Dictionary. 341 * We want BundleID, Version and full path to the kext 342 */ 343 myKextArray = (CFMutableArrayRef) 344 CFDictionaryGetValue(myAlertInfoDict, CFSTR("KextInfoArrayKey")); 345 if (myKextArray == NULL) { 346 /* first kext info so create the kext info array */ 347 myKextArray = CFArrayCreateMutable(kCFAllocatorDefault, 348 0, 349 &kCFTypeArrayCallBacks); 350 if (myKextArray == NULL) { 351 goto finish; 352 } 353 CFDictionarySetValue(myAlertInfoDict, 354 CFSTR("KextInfoArrayKey"), 355 myKextArray); 356 } 357 358 /* check for dup of this kext */ 359 myCount = CFArrayGetCount(myKextArray); 360 if (myCount > 0) { 361 for (i = 0; i < myCount; i++) { 362 CFMutableDictionaryRef myDict; 363 myDict = (CFMutableDictionaryRef) 364 CFArrayGetValueAtIndex(myKextArray, i); 365 if (myDict == NULL) continue; 366 367 if ( !CFDictionaryContainsValue(myDict, myBundleID) ) { 368 continue; 369 } 370 if ( !CFDictionaryContainsValue(myDict, 371 myBundleVersion) ) { 372 continue; 373 } 374 /* already have this one so bail */ 375 goto finish; 376 } 377 } 378 379 /* new kext info to add */ 380 myKextInfoDict = CFDictionaryCreateMutable( 381 kCFAllocatorDefault, 0, 382 &kCFCopyStringDictionaryKeyCallBacks, 383 &kCFTypeDictionaryValueCallBacks); 384 if (myKextInfoDict == NULL) { 385 goto finish; 386 } 387 CFDictionaryAddValue(myKextInfoDict, 388 kCFBundleIdentifierKey, 389 myBundleID); 390 CFDictionaryAddValue(myKextInfoDict, 391 kCFBundleVersionKey, 392 myBundleVersion); 393 CFDictionaryAddValue(myKextInfoDict, 394 CFSTR("KextPathKey"), 395 myKextPath); 396 397 CFArrayAppendValue(myKextArray, 398 myKextInfoDict); 399 400finish: 401 SAFE_RELEASE(myKextURL); 402 SAFE_RELEASE(myKextPath); 403 SAFE_RELEASE(myKextInfoDict); 404 405 return; 406} 407 408#endif // !TARGET_OS_EMBEDDED 409 410#if PRAGMA_MARK 411#pragma mark Path & File 412#endif /* PRAGMA_MARK */ 413/******************************************************************************* 414*******************************************************************************/ 415ExitStatus checkPath( 416 const char * path, 417 const char * suffix, // w/o the dot 418 Boolean directoryRequired, 419 Boolean writableRequired) 420{ 421 Boolean result = EX_USAGE; 422 Boolean nameBad = FALSE; 423 struct stat statBuffer; 424 425 if (!path) { 426 OSKextLog(/* kext */ NULL, 427 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 428 "Internal error - %s - NULL path.", 429 __FUNCTION__); 430 result = EX_SOFTWARE; 431 goto finish; 432 } 433 434 result = EX_USAGE; 435 if (suffix) { 436 size_t pathLength = strlen(path); 437 size_t suffixLength = strlen(suffix); 438 size_t suffixIndex = 0; 439 size_t periodIndex = 0; 440 441 nameBad = TRUE; 442 if (!pathLength || !suffixLength) { 443 OSKextLog(/* kext */ NULL, 444 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 445 "Internal error - %s - empty string.", 446 __FUNCTION__); 447 result = EX_SOFTWARE; 448 goto finish; 449 } 450 451 /* Peel off any trailing '/' characters (silly shell completion), 452 * then advance the length back to point to the character past 453 * the real end (which will be a slash or '\0'). 454 */ 455 while (pathLength-- && path[pathLength] == '/') { 456 /* just scanning for last non-slash */ 457 if (!pathLength) { 458 goto finish; 459 } 460 } 461 pathLength++; 462 463 if (suffixLength >= pathLength) { 464 goto finish; 465 } 466 suffixIndex = pathLength - suffixLength; 467 periodIndex = suffixIndex - 1; 468 if (path[periodIndex] != '.' || 469 strncmp(path + suffixIndex, suffix, suffixLength)) { 470 goto finish; 471 } 472 nameBad = FALSE; 473 } 474 475 result = EX_NOINPUT; 476 if (0 != stat(path, &statBuffer)) { 477 OSKextLog(/* kext */ NULL, 478 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 479 "Can't stat %s - %s.", path, 480 strerror(errno)); 481 goto finish; 482 } 483 484 if (directoryRequired && ((statBuffer.st_mode & S_IFMT) != S_IFDIR) ) { 485 OSKextLog(/* kext */ NULL, 486 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 487 "%s is not a directory.", 488 path); 489 goto finish; 490 } 491 492 result = EX_NOPERM; 493 if (writableRequired && access(path, W_OK) == -1) { 494 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 495 "%s is not writable.", path); 496 goto finish; 497 } 498 499 result = EX_OK; 500 501finish: 502 if (nameBad) { 503 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 504 "%s not of type '%s'.", path, suffix); 505 } 506 return result; 507} 508 509/******************************************************************************* 510*******************************************************************************/ 511void 512saveFile(const void * vKey, const void * vValue, void * vContext) 513{ 514 CFStringRef key = (CFStringRef)vKey; 515 CFDataRef fileData = (CFDataRef)vValue; 516 SaveFileContext * context = (SaveFileContext *)vContext; 517 518 long length; 519 int fd = -1; 520 mode_t mode = 0666; 521 struct stat statBuf; 522 CFURLRef saveURL = NULL; // must release 523 Boolean fileExists = false; 524 char savePath[PATH_MAX]; 525 526 if (context->fatal) { 527 goto finish; 528 } 529 530 saveURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault, 531 context->saveDirURL, key, /* isDirectory */ false); 532 if (!saveURL) { 533 context->fatal = true; 534 goto finish; 535 } 536 537 if (!CFURLGetFileSystemRepresentation(saveURL, /* resolveToBase */ true, 538 (u_char *)savePath, sizeof(savePath))) { 539 context->fatal = true; 540 goto finish; 541 } 542 543 if (!context->overwrite) { 544 fileExists = CFURLResourceIsReachable(saveURL, NULL); 545 if (fileExists) { 546 switch (user_approve(/* ask_all */ TRUE, /* default_answer */ REPLY_YES, 547 "%s exists, overwrite", savePath)) { 548 549 case REPLY_YES: 550 // go ahead and overwrite. 551 break; 552 case REPLY_ALL: 553 // go ahead and overwrite this and all following. 554 fprintf(stderr, 555 "Overwriting all symbol files for kexts in dependency graph.\n"); 556 context->overwrite = TRUE; 557 break; 558 case REPLY_NO: 559 goto finish; 560 break; 561 default: 562 context->fatal = true; 563 goto finish; 564 break; 565 } 566 } 567 else { 568 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 569 "%s missing '%s'", __func__, savePath); 570 context->fatal = true; 571 goto finish; 572 } 573 } 574 575 /* Write data. 576 */ 577 length = CFDataGetLength(fileData); 578 if (0 == stat(savePath, &statBuf)) { 579 mode = statBuf.st_mode; 580 } 581 fd = open(savePath, O_WRONLY|O_CREAT|O_TRUNC, mode); 582 if (fd != -1 && length) { 583 ExitStatus result; 584 result = writeToFile(fd, CFDataGetBytePtr(fileData), length); 585 if (result != EX_OK) { 586 OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel, 587 "%s write failed for '%s'", __func__, savePath); 588 } 589 } 590 else { 591 /* Is this fatal to the whole program? I'd rather soldier on. 592 */ 593 OSKextLog(/* kext */ NULL, 594 kOSKextLogErrorLevel | kOSKextLogFileAccessFlag, 595 "%s Failed to save '%s'", __func__, savePath); 596 } 597 598finish: 599 if (fd != -1) { 600 fsync(fd); 601 close(fd); 602 } 603 SAFE_RELEASE(saveURL); 604 return; 605} 606 607/******************************************************************************* 608*******************************************************************************/ 609CFStringRef copyKextPath(OSKextRef aKext) 610{ 611 CFStringRef result = NULL; 612 CFURLRef absURL = NULL; // must release 613 614 if (!OSKextGetURL(aKext)) { 615 goto finish; 616 } 617 618 absURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext)); 619 if (!absURL) { 620 goto finish; 621 } 622 result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 623finish: 624 SAFE_RELEASE(absURL); 625 return result; 626} 627 628 629/******************************************************************************* 630 * Returns the access and mod times from the file in the given array of 631 * fileURLs with the latest mod time. 632 * 11860417 - support multiple extensions directories 633 *******************************************************************************/ 634ExitStatus 635getLatestTimesFromCFURLArray( 636 CFArrayRef dirURLArray, 637 struct timeval dirTimeVals[2]) 638{ 639 ExitStatus result = EX_SOFTWARE; 640 int i; 641 CFURLRef myURL; 642 struct stat myStatBuf; 643 struct timeval myTempModTime; 644 struct timeval myTempAccessTime; 645 646 if (dirURLArray == NULL) { 647 goto finish; 648 } 649 bzero(dirTimeVals, (sizeof(struct timeval) * 2)); 650 651 for (i = 0; i < CFArrayGetCount(dirURLArray); i++) { 652 myURL = (CFURLRef) CFArrayGetValueAtIndex(dirURLArray, i); 653 if (myURL == NULL) { 654 OSKextLog(NULL, 655 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 656 "%s: NO fileURL at index %d!!!! ", __FUNCTION__, i); 657 goto finish; 658 } 659 660 result = statURL(myURL, &myStatBuf); 661 if (result != EX_OK) { 662 goto finish; 663 } 664 TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec); 665 TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec); 666 667 if (timercmp(&myTempModTime, &dirTimeVals[1], >)) { 668 dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec; 669 dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec; 670 dirTimeVals[1].tv_sec = myTempModTime.tv_sec; 671 dirTimeVals[1].tv_usec = myTempModTime.tv_usec; 672 } 673 } 674 675 result = EX_OK; 676finish: 677 return result; 678} 679 680/******************************************************************************* 681 *******************************************************************************/ 682ExitStatus 683getFilePathTimes( 684 const char * filePath, 685 struct timeval cacheFileTimes[2]) 686{ 687 struct stat statBuffer; 688 ExitStatus result = EX_SOFTWARE; 689 690 result = statPath(filePath, &statBuffer); 691 if (result != EX_OK) { 692 goto finish; 693 } 694 695 TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &statBuffer.st_atimespec); 696 TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &statBuffer.st_mtimespec); 697 698 result = EX_OK; 699finish: 700 return result; 701} 702 703 704/******************************************************************************* 705 *******************************************************************************/ 706ExitStatus 707statURL(CFURLRef anURL, struct stat * statBuffer) 708{ 709 ExitStatus result = EX_OSERR; 710 char path[PATH_MAX]; 711 712 if (!CFURLGetFileSystemRepresentation(anURL, /* resolveToBase */ true, 713 (UInt8 *)path, sizeof(path))) 714 { 715 OSKextLogStringError(/* kext */ NULL); 716 goto finish; 717 } 718 719 result = statPath(path, statBuffer); 720 if (!result) { 721 goto finish; 722 } 723 724 result = EX_OK; 725finish: 726 return result; 727} 728 729/******************************************************************************* 730 *******************************************************************************/ 731ExitStatus 732statPath(const char *path, struct stat *statBuffer) 733{ 734 ExitStatus result = EX_OSERR; 735 736 if (stat(path, statBuffer)) { 737 OSKextLog(/* kext */ NULL, 738 kOSKextLogDebugLevel | kOSKextLogGeneralFlag, 739 "Can't stat %s - %s.", path, strerror(errno)); 740 goto finish; 741 } 742 743 result = EX_OK; 744 745finish: 746 return result; 747} 748 749#if PRAGMA_MARK 750#pragma mark Logging 751#endif /* PRAGMA_MARK */ 752/******************************************************************************* 753*******************************************************************************/ 754OSKextLogSpec _sLogSpecsForVerboseLevels[] = { 755 kOSKextLogErrorLevel | kOSKextLogVerboseFlagsMask, // [0xff1] -v 0 756 kOSKextLogBasicLevel | kOSKextLogVerboseFlagsMask, // [0xff3] -v 1 757 kOSKextLogProgressLevel | kOSKextLogVerboseFlagsMask, // [0xff4] -v 2 758 kOSKextLogStepLevel | kOSKextLogVerboseFlagsMask, // [0xff5] -v 3 759 kOSKextLogDetailLevel | kOSKextLogVerboseFlagsMask, // [0xff6] -v 4 760 kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask, // [0xff7] -v 5 761 kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask | // [0xfff] -v 6 762 kOSKextLogKextOrGlobalMask 763}; 764 765/******************************************************************************* 766* getopt_long_only() doesn't actually handle optional args very well. So, we 767* jump through some hoops here to handle all six possibilities: 768* 769* cmd line optarg argv[optind] 770* ---------------------------------------------- 771* -v (null) (following arg or null) 772* -v arg (null) arg 773* -v=arg (null) -v=arg -- ILLEGAL 774* -verbose (null) (following arg or null) 775* -verbose arg (null) arg 776* -verbose=arg arg (following arg or null) 777* 778* Note that only in the -verbose=arg case does optarg actually get set 779* correctly! 780* 781* If we have not optarg but a following argv[optind], we check it to see if 782* it looks like a legal arg to -v/-verbose; if it matches we increment optind. 783* -v has never allowed the argument to immediately follow (as in -v2), so 784* we still don't handle that. 785*******************************************************************************/ 786#define kBadVerboseOptPrefix "-v=" 787 788ExitStatus setLogFilterForOpt( 789 int argc, 790 char * const * argv, 791 OSKextLogSpec forceOnFlags) 792{ 793 ExitStatus result = EX_USAGE; 794 OSKextLogSpec logFilter = 0; 795 const char * localOptarg = NULL; 796 797 /* Must be a bare -v; just use the extra flags. 798 */ 799 if (!optarg && optind >= argc) { 800 logFilter = _sLogSpecsForVerboseLevels[1]; 801 802 } else { 803 804 if (optarg) { 805 localOptarg = optarg; 806 } else { 807 localOptarg = argv[optind]; 808 } 809 810 if (!strncmp(localOptarg, kBadVerboseOptPrefix, 811 sizeof(kBadVerboseOptPrefix) - 1)) { 812 813 OSKextLog(/* kext */ NULL, 814 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 815 "%s - syntax error (don't use = with single-letter option args).", 816 localOptarg); 817 goto finish; 818 } 819 820 /* Look for '-v0x####' with no space and advance to the 0x part. 821 */ 822 if (localOptarg[0] == '-' && localOptarg[1] == kOptVerbose && 823 localOptarg[2] == '0' && (localOptarg[3] == 'x' || localOptarg[3] == 'X')) { 824 825 localOptarg += 2; 826 } 827 828 /* Look for a 0x#### style verbose arg. 829 */ 830 if (localOptarg[0] == '0' && (localOptarg[1] == 'x' || localOptarg[1] == 'X')) { 831 char * endptr = NULL; 832 OSKextLogSpec parsedFlags = (unsigned)strtoul(localOptarg, &endptr, 16); 833 834 if (endptr[0]) { 835 OSKextLog(/* kext */ NULL, 836 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 837 "Can't parse verbose argument %s.", localOptarg); 838 goto finish; 839 } 840 logFilter = parsedFlags; 841 842 if (!optarg) { 843 optind++; 844 } 845 846 /* Now a 0-6 style verbose arg. 847 */ 848 } else if (((localOptarg[0] >= '0') || (localOptarg[0] <= '6')) && 849 (localOptarg[1] == '\0')) { 850 851 logFilter = _sLogSpecsForVerboseLevels[localOptarg[0] - '0']; 852 if (!optarg) { 853 optind++; 854 } 855 856 /* Must be a -v with command args following; just use the extra flag. 857 */ 858 } else { 859 logFilter = _sLogSpecsForVerboseLevels[1]; 860 } 861 } 862 863 logFilter = logFilter | forceOnFlags; 864 865 OSKextSetLogFilter(logFilter, /* kernel? */ false); 866 OSKextSetLogFilter(logFilter, /* kernel? */ true); 867 868 result = EX_OK; 869 870finish: 871 return result; 872} 873 874/******************************************************************************* 875*******************************************************************************/ 876void beQuiet(void) 877{ 878 fclose(stdout); 879 fclose(stderr); 880 close(1); 881 close(2); 882 OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ false); 883 OSKextSetLogFilter(kOSKextLogSilentFilter, /* kernel? */ true); 884 return; 885} 886 887/******************************************************************************* 888*******************************************************************************/ 889FILE * g_log_stream = NULL; 890aslclient gASLClientHandle = NULL; 891aslmsg gASLMessage = NULL; // reused 892// xxx - need to aslclose() 893 894void tool_openlog(const char * name) 895{ 896 // xxx - do we want separate name & facility? 897 gASLClientHandle = asl_open(/* ident */ name, /* facility*/ name, 898 /* options */ 0); 899 gASLMessage = asl_new(ASL_TYPE_MSG); 900 return; 901} 902 903/******************************************************************************* 904* Basic log function. If any log flags are set, log the message 905* to syslog/stderr. Note: exported as SPI in bootroot.h. 906*******************************************************************************/ 907 908void tool_log( 909 OSKextRef aKext __unused, 910 OSKextLogSpec msgLogSpec, 911 const char * format, ...) 912{ 913 va_list ap; 914 915 if (gASLClientHandle) { 916 int aslLevel = ASL_LEVEL_ERR; 917 OSKextLogSpec kextLogLevel = msgLogSpec & kOSKextLogLevelMask; 918 char messageLogSpec[16]; 919 920 if (kextLogLevel == kOSKextLogErrorLevel) { 921 aslLevel = ASL_LEVEL_ERR; 922 } else if (kextLogLevel == kOSKextLogWarningLevel) { 923 aslLevel = ASL_LEVEL_WARNING; 924 } else if (kextLogLevel == kOSKextLogBasicLevel) { 925 aslLevel = ASL_LEVEL_NOTICE; 926 } else if (kextLogLevel < kOSKextLogDebugLevel) { 927 aslLevel = ASL_LEVEL_INFO; 928 } else { 929 aslLevel = ASL_LEVEL_DEBUG; 930 } 931 932 snprintf(messageLogSpec, sizeof(messageLogSpec), "0x%x", msgLogSpec); 933 asl_set(gASLMessage, "OSKextLogSpec", messageLogSpec); 934 935 va_start(ap, format); 936 asl_vlog(gASLClientHandle, gASLMessage, aslLevel, format, ap); 937 va_end(ap); 938 939 } else { 940 // xxx - change to pick log stream based on log level 941 // xxx - (0 == stdout, all others stderr) 942 943 if (!g_log_stream) { 944 g_log_stream = stderr; 945 } 946 947 va_start(ap, format); 948 vfprintf(g_log_stream, format, ap); 949 va_end(ap); 950 951 fprintf(g_log_stream, "\n"); 952 fflush(g_log_stream); 953 } 954 955 return; 956} 957 958/******************************************************************************* 959*******************************************************************************/ 960void log_CFError( 961 OSKextRef aKext __unused, 962 OSKextLogSpec msgLogSpec, 963 CFErrorRef error) 964{ 965 CFStringRef errorString = NULL; // must release 966 char * cstring = NULL; // must release 967 968 if (!error) { 969 return; 970 } 971 errorString = CFErrorCopyDescription(error); 972 if (errorString) { 973 cstring = createUTF8CStringForCFString(errorString); 974 OSKextLog(/* kext */ NULL, msgLogSpec, 975 "CFError descripton: %s.", cstring); 976 SAFE_RELEASE_NULL(errorString); 977 SAFE_FREE_NULL(cstring); 978 } 979 980 errorString = CFErrorCopyFailureReason(error); 981 if (errorString) { 982 cstring = createUTF8CStringForCFString(errorString); 983 OSKextLog(/* kext */ NULL, msgLogSpec, 984 "CFError reason: %s.", cstring); 985 SAFE_RELEASE_NULL(errorString); 986 SAFE_FREE_NULL(cstring); 987 } 988 989 return; 990} 991 992#if !TARGET_OS_EMBEDDED 993// log helper for libbless, exported as SPI via bootroot.h 994int32_t 995BRBLLogFunc(void *refcon __unused, int32_t level, const char *string) 996{ 997 OSKextLogSpec logSpec = kOSKextLogGeneralFlag; 998 switch (level) { 999 case kBLLogLevelVerbose: 1000 logSpec |= kOSKextLogDebugLevel; 1001 break; 1002 case kBLLogLevelError: 1003 logSpec |= kOSKextLogErrorLevel; 1004 break; 1005 default: 1006 logSpec |= kOSKextLogWarningLevel; 1007 } 1008 OSKextLog(NULL, logSpec, "%s", string); 1009 return 0; 1010} 1011#endif // !TARGET_OS_EMBEDDED 1012 1013/******************************************************************************* 1014* safe_mach_error_string() 1015*******************************************************************************/ 1016const char * safe_mach_error_string(mach_error_t error_code) 1017{ 1018 const char * result = mach_error_string(error_code); 1019 if (!result) { 1020 result = "(unknown)"; 1021 } 1022 return result; 1023} 1024 1025#if PRAGMA_MARK 1026#pragma mark User Input 1027#endif /* PRAGMA_MARK */ 1028/******************************************************************************* 1029* user_approve() 1030* 1031* Ask the user a question and wait for a yes/no answer. 1032*******************************************************************************/ 1033int user_approve(Boolean ask_all, int default_answer, const char * format, ...) 1034{ 1035 int result = REPLY_YES; 1036 va_list ap; 1037 char fake_buffer[2]; 1038 int output_length; 1039 char * output_string; 1040 int c, x; 1041 1042 va_start(ap, format); 1043 output_length = vsnprintf(fake_buffer, 1, format, ap); 1044 va_end(ap); 1045 1046 output_string = (char *)malloc(output_length + 1); 1047 if (!output_string) { 1048 result = REPLY_ERROR; 1049 goto finish; 1050 } 1051 1052 va_start(ap, format); 1053 vsnprintf(output_string, output_length + 1, format, ap); 1054 va_end(ap); 1055 1056 while ( 1 ) { 1057 fprintf(stderr, "%s [%s/%s", output_string, 1058 (default_answer == REPLY_YES) ? "Y" : "y", 1059 (default_answer == REPLY_NO) ? "N" : "n"); 1060 if (ask_all) { 1061 fprintf(stderr, "/%s", 1062 (default_answer == REPLY_ALL) ? "A" : "a"); 1063 } 1064 fprintf(stderr, "]? "); 1065 fflush(stderr); 1066 1067 c = fgetc(stdin); 1068 1069 if (c == EOF) { 1070 result = REPLY_ERROR; 1071 goto finish; 1072 } 1073 1074 /* Make sure we get a newline. 1075 */ 1076 if ( c != '\n' ) { 1077 do { 1078 x = fgetc(stdin); 1079 } while (x != '\n' && x != EOF); 1080 1081 if (x == EOF) { 1082 result = REPLY_ERROR; 1083 goto finish; 1084 } 1085 } 1086 1087 if (c == '\n') { 1088 result = default_answer; 1089 goto finish; 1090 } else if (tolower(c) == 'y') { 1091 result = REPLY_YES; 1092 goto finish; 1093 } else if (tolower(c) == 'n') { 1094 result = REPLY_NO; 1095 goto finish; 1096 } else if (ask_all && tolower(c) == 'a') { 1097 result = REPLY_ALL; 1098 goto finish; 1099 } else { 1100 fprintf(stderr, "Please answer 'y' or 'n'%s.\n", 1101 ask_all ? " or 'a'" : ""); 1102 } 1103 } 1104 1105finish: 1106 if (output_string) free(output_string); 1107 1108 return result; 1109} 1110 1111/******************************************************************************* 1112* user_input() 1113* 1114* Ask the user for input. 1115*******************************************************************************/ 1116const char * user_input(Boolean * eof, const char * format, ...) 1117{ 1118 char * result = NULL; // return value 1119 va_list ap; 1120 char fake_buffer[2]; 1121 int output_length; 1122 char * output_string = NULL; 1123 unsigned index; 1124 size_t size = 80; // more than enough to input a hex address 1125 int c; 1126 1127 if (eof) { 1128 *eof = false; 1129 } 1130 1131 result = (char *)malloc(size); 1132 if (!result) { 1133 goto finish; 1134 } 1135 index = 0; 1136 1137 va_start(ap, format); 1138 output_length = vsnprintf(fake_buffer, 1, format, ap); 1139 va_end(ap); 1140 1141 output_string = (char *)malloc(output_length + 1); 1142 if (!output_string) { 1143 if (result) free(result); 1144 result = NULL; 1145 goto finish; 1146 } 1147 1148 va_start(ap, format); 1149 vsnprintf(output_string, output_length + 1, format, ap); 1150 va_end(ap); 1151 1152 fprintf(stderr, "%s ", output_string); 1153 fflush(stderr); 1154 1155 c = fgetc(stdin); 1156 while (c != '\n' && c != EOF) { 1157 if (index >= (size - 1)) { 1158 fprintf(stderr, "input line too long\n"); 1159 if (result) free(result); 1160 result = NULL; 1161 goto finish; 1162 } 1163 result[index++] = (char)c; 1164 c = fgetc(stdin); 1165 } 1166 1167 result[index] = '\0'; 1168 1169 if (c == EOF) { 1170 if (result) free(result); 1171 result = NULL; 1172 if (eof) { 1173 *eof = true; 1174 } 1175 goto finish; 1176 } 1177 1178finish: 1179 if (output_string) free(output_string); 1180 1181 return result; 1182} 1183 1184#if PRAGMA_MARK 1185#pragma mark Caches 1186#endif /* PRAGMA_MARK */ 1187/******************************************************************************* 1188*******************************************************************************/ 1189Boolean readSystemKextPropertyValues( 1190 CFStringRef propertyKey, 1191 const NXArchInfo * arch, 1192 Boolean forceUpdateFlag, 1193 CFArrayRef * valuesOut) 1194{ 1195 Boolean result = false; 1196 CFArrayRef sysExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs(); 1197 CFMutableArrayRef values = NULL; // must release 1198 CFStringRef cacheBasename = NULL; // must release 1199 CFArrayRef kexts = NULL; // must release 1200 CFMutableDictionaryRef newDict = NULL; // must release 1201 CFStringRef kextPath = NULL; // must release 1202 CFTypeRef value = NULL; // do not release 1203 CFStringRef kextVersion = NULL; // do not release 1204 CFIndex count, i; 1205 1206 cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault, 1207 /* formatOptions */ NULL, CFSTR("%s%@"), 1208 _kKextPropertyValuesCacheBasename, 1209 propertyKey); 1210 if (!cacheBasename) { 1211 OSKextLogMemError(); 1212 goto finish; 1213 } 1214 1215 if (OSKextGetUsesCaches() && !forceUpdateFlag) { 1216 1217 /* See if we have an up-to-date cache containing an array, and return 1218 * that if we have one. 1219 */ 1220 if (_OSKextReadCache(sysExtensionsFolderURLs, cacheBasename, 1221 arch, _kOSKextCacheFormatCFXML, /* parseXML? */ true, 1222 (CFPropertyListRef *)&values)) { 1223 1224 if (values && CFGetTypeID(values) == CFArrayGetTypeID()) { 1225 result = true; 1226 goto finish; 1227 } 1228 } 1229 } 1230 1231 values = CFArrayCreateMutable(kCFAllocatorDefault, /* capacity */ 0, 1232 &kCFTypeArrayCallBacks); 1233 if (!values) { 1234 OSKextLogMemError(); 1235 goto finish; 1236 } 1237 1238 kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault, 1239 sysExtensionsFolderURLs); 1240 1241 if (!kexts) { 1242 // Create function should log error 1243 goto finish; 1244 } 1245 1246 count = CFArrayGetCount(kexts); 1247 1248 for (i = 0; i < count; i++) { 1249 OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i); 1250 1251 SAFE_RELEASE_NULL(newDict); 1252 SAFE_RELEASE_NULL(kextPath); 1253 // do not release kextVersion 1254 kextVersion = NULL; 1255 1256 if ((OSKextGetSimulatedSafeBoot() || OSKextGetActualSafeBoot()) && 1257 !OSKextIsLoadableInSafeBoot(aKext)) { 1258 1259 continue; 1260 } 1261 //??? if (OSKextGetLoadFailed(aKext)) continue; -- don't have in OSKext 1262 1263 value = OSKextGetValueForInfoDictionaryKey(aKext, propertyKey); 1264 if (!value) { 1265 continue; 1266 } 1267 1268 newDict = CFDictionaryCreateMutable( 1269 kCFAllocatorDefault, 0, 1270 &kCFTypeDictionaryKeyCallBacks, 1271 &kCFTypeDictionaryValueCallBacks); 1272 if (!newDict) { 1273 goto finish; 1274 } 1275 1276 CFDictionarySetValue(newDict, CFSTR("Data"), value); 1277 1278 CFDictionarySetValue(newDict, CFSTR("CFBundleIdentifier"), 1279 OSKextGetIdentifier(aKext)); 1280 1281 kextPath = copyKextPath(aKext); 1282 if (!kextPath) { 1283 goto finish; 1284 } 1285 CFDictionarySetValue(newDict, CFSTR("OSBundlePath"), kextPath); 1286 1287 kextVersion = OSKextGetValueForInfoDictionaryKey(aKext, 1288 CFSTR("CFBundleVersion")); 1289 if (!kextVersion) { 1290 goto finish; 1291 } 1292 CFDictionarySetValue(newDict, CFSTR("CFBundleVersion"), 1293 kextVersion); 1294 1295 CFArrayAppendValue(values, newDict); 1296 } 1297 1298 if (OSKextGetUsesCaches() || forceUpdateFlag) { 1299 _OSKextWriteCache(sysExtensionsFolderURLs, cacheBasename, 1300 arch, _kOSKextCacheFormatCFXML, values); 1301 } 1302 1303 result = true; 1304 1305finish: 1306 if (result && valuesOut && values) { 1307 *valuesOut = (CFArrayRef)CFRetain(values); 1308 } 1309 1310 SAFE_RELEASE(values); 1311 SAFE_RELEASE(cacheBasename); 1312 SAFE_RELEASE(kexts); 1313 SAFE_RELEASE(newDict); 1314 SAFE_RELEASE(kextPath); 1315 1316 return result; 1317} 1318 1319