1/* 2 * Copyright (c) 2006 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#include <CoreFoundation/CFBundlePriv.h> 24 25#include <IOKit/kext/OSKext.h> 26#include <IOKit/kext/OSKextPrivate.h> 27#include <IOKit/kext/fat_util.h> 28#include <IOKit/kext/macho_util.h> 29 30#include "kextfind_commands.h" 31#include "kextfind_main.h" 32#include "kextfind_query.h" 33 34/******************************************************************************* 35* 36*******************************************************************************/ 37CFStringRef copyPathForKext( 38 OSKextRef theKext, 39 PathSpec pathSpec) 40{ 41 CFStringRef result = CFSTR("(can't determine kext path)"); 42 43 CFURLRef kextURL = OSKextGetURL(theKext); // do not release 44 CFURLRef absURL = NULL; // must release 45 OSKextRef containerKext = NULL; // must release 46 CFURLRef containerURL = NULL; // do not release 47 CFURLRef containerAbsURL = NULL; // must release 48 CFURLRef repositoryURL = NULL; // must release 49 CFStringRef repositoryPath = NULL; // must release 50 CFStringRef kextPath = NULL; // must release 51 52 53 if (!kextURL) { 54 OSKextLog(theKext, 55 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 56 "Kext has no URL!"); 57 goto finish; 58 } 59 60 if (pathSpec == kPathsNone) { 61 result = CFURLCopyLastPathComponent(kextURL); 62 } else if (pathSpec == kPathsFull) { 63 absURL = CFURLCopyAbsoluteURL(kextURL); 64 if (!absURL) { 65 OSKextLogMemError(); 66 goto finish; 67 } 68 result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 69 } else if (pathSpec == kPathsRelative) { 70 CFRange relativeRange; 71 absURL = CFURLCopyAbsoluteURL(kextURL); 72 if (!absURL) { 73 OSKextLogMemError(); 74 goto finish; 75 } 76 77 containerKext = OSKextCopyContainerForPluginKext(theKext); 78 if (containerKext) { 79 containerURL = OSKextGetURL(containerKext); 80 if (!containerURL) { 81 OSKextLog(containerKext, 82 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 83 "Container kext has no URL!"); 84 goto finish; 85 } 86 containerAbsURL = CFURLCopyAbsoluteURL(containerURL); 87 if (!containerAbsURL) { 88 OSKextLogMemError(); 89 goto finish; 90 } 91 repositoryURL = CFURLCreateCopyDeletingLastPathComponent( 92 kCFAllocatorDefault, containerAbsURL); 93 if (!repositoryURL) { 94 OSKextLogMemError(); 95 goto finish; 96 } 97 } else { 98 repositoryURL = CFURLCreateCopyDeletingLastPathComponent( 99 kCFAllocatorDefault, absURL); 100 if (!repositoryURL) { 101 OSKextLogMemError(); 102 goto finish; 103 } 104 } 105 106 repositoryPath = CFURLCopyFileSystemPath(repositoryURL, kCFURLPOSIXPathStyle); 107 kextPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 108 if (!repositoryPath || !kextPath) { 109 OSKextLogMemError(); 110 goto finish; 111 } 112 113 /* We add 1 to the length of the repositoryPath to handle the 114 * intermediate '/' character. 115 */ 116 relativeRange = CFRangeMake(1+CFStringGetLength(repositoryPath), 117 CFStringGetLength(kextPath) - (1+CFStringGetLength(repositoryPath))); 118 result = CFStringCreateWithSubstring(kCFAllocatorDefault, 119 kextPath, relativeRange); 120 } else { 121 OSKextLog(/* kext */ NULL, 122 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 123 "Internal error."); 124 goto finish; 125 } 126 127finish: 128 SAFE_RELEASE(absURL); 129 SAFE_RELEASE(containerKext); 130 SAFE_RELEASE(containerAbsURL); 131 SAFE_RELEASE(repositoryURL); 132 SAFE_RELEASE(repositoryPath); 133 SAFE_RELEASE(kextPath); 134 135 return result; 136} 137 138/******************************************************************************* 139* 140*******************************************************************************/ 141void printKext( 142 OSKextRef theKext, 143 PathSpec pathSpec, 144 Boolean extra_info, 145 char lineEnd) 146{ 147 CFStringRef bundleID = NULL; // do NOT release 148 CFStringRef bundleVersion = NULL; // do NOT release 149 150 CFStringRef kextPath = NULL; // must release 151 char * kext_path = NULL; // must free 152 char * bundle_id = NULL; // must free 153 char * bundle_version = NULL; // must free 154 155 kextPath = copyPathForKext(theKext, pathSpec); 156 if (!kextPath) { 157 OSKextLogMemError(); 158 goto finish; 159 } 160 161 kext_path = createUTF8CStringForCFString(kextPath); 162 if (!kext_path) { 163 OSKextLogMemError(); 164 goto finish; 165 } 166 167 if (extra_info) { 168 bundleID = OSKextGetIdentifier(theKext); 169 bundleVersion = OSKextGetValueForInfoDictionaryKey(theKext, 170 kCFBundleVersionKey); 171 172 bundle_id = createUTF8CStringForCFString(bundleID); 173 bundle_version = createUTF8CStringForCFString(bundleVersion); 174 if (!bundle_id || !bundle_version) { 175 OSKextLogMemError(); 176 goto finish; 177 } 178 179 fprintf(stdout, "%s\t%s\t%s%c", kext_path, bundle_id, 180 bundle_version, lineEnd); 181 } else { 182 fprintf(stdout, "%s%c", kext_path, lineEnd); 183 } 184 185finish: 186 SAFE_RELEASE(kextPath); 187 SAFE_FREE(kext_path); 188 SAFE_FREE(bundle_id); 189 SAFE_FREE(bundle_version); 190 191 return; 192} 193 194/******************************************************************************* 195* 196*******************************************************************************/ 197#define kMaxPrintableCFDataLength (36) 198#define kByteGroupSize (4) 199 200char * stringForData(const UInt8 * data, int numBytes) 201{ 202 char * result = NULL; 203 char * scan = NULL; 204 int numByteGroups; 205 int i; 206 char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', 207 '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; 208 209 /* Put a space between every 2 bytes (4 chars). 210 */ 211 numByteGroups = numBytes / kByteGroupSize; 212 213 result = malloc(((numBytes * 2) + numByteGroups + 1) * sizeof(char)); 214 if (!result) { 215 goto finish; 216 } 217 218 scan = result; 219 220 for (i = 0; i < numBytes; i++) { 221 int binaryDigit1 = (data[i] & 0xF0) >> 4; 222 int binaryDigit2 = data[i] & 0xF; 223 if (i > 0 && i % kByteGroupSize == 0) { 224 *scan++ = ' '; 225 } 226 *scan++ = digits[binaryDigit1]; 227 *scan++ = digits[binaryDigit2]; 228 } 229 *scan++ = '\0'; 230#pragma unused(scan) 231 232finish: 233 return result; 234} 235 236/******************************************************************************* 237* 238*******************************************************************************/ 239void printProperty( 240 CFStringRef label, 241 CFStringRef propKey, 242 CFTypeRef value, 243 char lineEnd) 244{ 245 CFTypeID type = CFGetTypeID(value); 246 CFStringRef propString = NULL; // must release 247 CFStringRef labeledString = NULL; // must release 248 CFStringRef outputString = NULL; // do not release 249 char * allocString = NULL; // must free 250 char * dataString = NULL; // must free 251 252 if (type == CFStringGetTypeID()) { 253 propString = CFStringCreateWithFormat( 254 kCFAllocatorDefault, NULL, CFSTR("%@ = \"%@\"%c"), 255 propKey, value, lineEnd); 256 } else if (type == CFNumberGetTypeID()) { 257 propString = CFStringCreateWithFormat( 258 kCFAllocatorDefault, NULL, CFSTR("%@ = %@%c"), 259 propKey, value, lineEnd); 260 } else if (type == CFBooleanGetTypeID()) { 261 propString = CFStringCreateWithFormat( 262 kCFAllocatorDefault, NULL, CFSTR("%@ = %@%c"), 263 propKey, value, lineEnd); 264 } else if (type == CFDataGetTypeID()) { 265 CFIndex length = 0; 266 length = CFDataGetLength(value); 267 const UInt8 * data = CFDataGetBytePtr(value); 268 if (!data) { 269 propString = CFStringCreateWithFormat( 270 kCFAllocatorDefault, NULL, CFSTR("%@[%zd] = <null data pointer>%c"), 271 propKey, length, lineEnd); 272 } else { 273 int numBytes = (int)MIN(length, kMaxPrintableCFDataLength); 274 dataString = stringForData(data, MIN(numBytes, kMaxPrintableCFDataLength)); 275 if (length > kMaxPrintableCFDataLength) { 276 propString = CFStringCreateWithFormat( 277 kCFAllocatorDefault, NULL, 278 CFSTR("%@ = <data (%zd bytes): %s...>%c"), 279 propKey, length, dataString, lineEnd); 280 } else { 281 propString = CFStringCreateWithFormat( 282 kCFAllocatorDefault, NULL, 283 CFSTR("%@ = <data (%zd bytes): %s>%c"), 284 propKey, length, dataString, lineEnd); 285 } 286 } 287 } else if (type == CFDictionaryGetTypeID()) { 288 propString = CFStringCreateWithFormat( 289 kCFAllocatorDefault, NULL, CFSTR("%@ = <dictionary of %zd items>%c"), 290 propKey, CFDictionaryGetCount(value), lineEnd); 291 } else if (type == CFArrayGetTypeID()) { 292 propString = CFStringCreateWithFormat( 293 kCFAllocatorDefault, NULL, CFSTR("%@ = <array of %zd items>%c"), 294 propKey, CFArrayGetCount(value), lineEnd); 295 } else { 296 propString = CFStringCreateWithFormat( 297 kCFAllocatorDefault, NULL, CFSTR("%@ = <value of unknown type>%c"), 298 propKey, lineEnd); 299 } 300 301 if (!propString) { 302 goto finish; 303 } 304 305 if (label) { 306 labeledString = CFStringCreateWithFormat( 307 kCFAllocatorDefault, NULL, CFSTR("%@: %@"), label, propString); 308 outputString = labeledString; 309 } else { 310 labeledString = CFStringCreateWithFormat( 311 kCFAllocatorDefault, NULL, CFSTR("%@"), propString); 312 outputString = labeledString; 313 } 314 315 if (!outputString) { 316 goto finish; 317 } 318 319 allocString = createUTF8CStringForCFString(outputString); 320 if (!allocString) { 321 goto finish; 322 } 323 324 fprintf(stdout, "%s", allocString); 325 326finish: 327 if (propString) CFRelease(propString); 328 if (labeledString) CFRelease(labeledString); 329 if (allocString) free(allocString); 330 if (dataString) free(dataString); 331 return; 332} 333 334/******************************************************************************* 335* 336*******************************************************************************/ 337void printKextProperty( 338 OSKextRef theKext, 339 CFStringRef propKey, 340 char lineEnd) 341{ 342 CFTypeRef value = NULL; 343 344 value = OSKextGetValueForInfoDictionaryKey(theKext, propKey); 345 if (value) { 346 printProperty(NULL, propKey, value, lineEnd); 347 } 348 349 return; 350} 351 352/******************************************************************************* 353* 354*******************************************************************************/ 355void printKextMatchProperty( 356 OSKextRef theKext, 357 CFStringRef propKey, 358 char lineEnd) 359{ 360 CFDictionaryRef personalitiesDict = NULL; 361 CFStringRef * names = NULL; 362 CFDictionaryRef * personalities = NULL; 363 CFIndex numPersonalities; 364 CFIndex i; 365 366 personalitiesDict = OSKextGetValueForInfoDictionaryKey(theKext, 367 CFSTR(kIOKitPersonalitiesKey)); 368 if (!personalitiesDict) { 369 goto finish; 370 } 371 372 numPersonalities = CFDictionaryGetCount(personalitiesDict); 373 if (!numPersonalities) { 374 goto finish; 375 } 376 377 names = malloc(numPersonalities * sizeof(CFStringRef)); 378 personalities = malloc(numPersonalities * sizeof(CFDictionaryRef)); 379 if (!names || !personalities) { 380 goto finish; 381 } 382 383 CFDictionaryGetKeysAndValues(personalitiesDict, (const void **)names, 384 (const void **)personalities); 385 386 for (i = 0; i < numPersonalities; i++) { 387 CFTypeRef value = CFDictionaryGetValue(personalities[i], propKey); 388 if (value) { 389 printProperty(names[i], propKey, value, lineEnd); 390 } 391 } 392 393finish: 394 if (names) free(names); 395 if (personalities) free(personalities); 396 397 return; 398} 399 400/******************************************************************************* 401* 402*******************************************************************************/ 403void printKextArches( 404 OSKextRef theKext, 405 char lineEnd, 406 Boolean printLineEnd) 407{ 408 fat_iterator fiter = NULL; 409 struct mach_header * farch = NULL; 410 const NXArchInfo * archinfo = NULL; 411 Boolean printedOne = false; 412 413 fiter = createFatIteratorForKext(theKext); 414 if (!fiter) { 415 goto finish; 416 } 417 418 while ((farch = fat_iterator_next_arch(fiter, NULL))) { 419 int swap = ISSWAPPEDMACHO(farch->magic); 420 archinfo = NXGetArchInfoFromCpuType(CondSwapInt32(swap, farch->cputype), 421 CondSwapInt32(swap, farch->cpusubtype)); 422 if (archinfo) { 423 fprintf(stdout, "%s%s", printedOne ? "," : "", archinfo->name); 424 printedOne = true; 425 } 426 } 427 428finish: 429 if (printLineEnd && printedOne) { 430 fprintf(stdout, "%c", lineEnd); 431 } 432 if (fiter) fat_iterator_close(fiter); 433 434 return; 435} 436 437/******************************************************************************* 438* 439*******************************************************************************/ 440void printKextDependencies( 441 OSKextRef theKext, 442 PathSpec pathSpec, 443 Boolean extra_info, 444 char lineEnd) 445{ 446 CFArrayRef kextDependencies = OSKextCopyAllDependencies(theKext, 447 /* needAll? */ false); 448 CFIndex count, i; 449 450 if (!kextDependencies) { 451 goto finish; 452 } 453 454 count = CFArrayGetCount(kextDependencies); 455 for (i = 0; i < count; i++) { 456 OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kextDependencies, i); 457 printKext(thisKext, pathSpec, extra_info, lineEnd); 458 } 459 460finish: 461 if (kextDependencies) CFRelease(kextDependencies); 462 return; 463} 464 465/******************************************************************************* 466* 467*******************************************************************************/ 468void printKextDependents( 469 OSKextRef theKext, 470 PathSpec pathSpec, 471 Boolean extra_info, 472 char lineEnd) 473{ 474 CFArrayRef kextDependents = OSKextCopyDependents(theKext, 475 /* direct? */ false); 476 CFIndex count, i; 477 478 if (!kextDependents) { 479 goto finish; 480 } 481 482 count = CFArrayGetCount(kextDependents); 483 for (i = 0; i < count; i++) { 484 OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(kextDependents, i); 485 printKext(thisKext, pathSpec, extra_info, lineEnd); 486 } 487 488finish: 489 if (kextDependents) CFRelease(kextDependents); 490 return; 491} 492 493/******************************************************************************* 494* 495*******************************************************************************/ 496void printKextPlugins( 497 OSKextRef theKext, 498 PathSpec pathSpec, 499 Boolean extra_info, 500 char lineEnd) 501{ 502 CFArrayRef plugins = OSKextCopyPlugins(theKext); // must release 503 CFIndex count, i; 504 505 if (!plugins) { 506 goto finish; 507 } 508 509 count = CFArrayGetCount(plugins); 510 for (i = 0; i < count; i++) { 511 OSKextRef thisKext = (OSKextRef)CFArrayGetValueAtIndex(plugins, i); 512 printKext(thisKext, pathSpec, extra_info, lineEnd); 513 } 514 515finish: 516 SAFE_RELEASE(plugins); 517 return; 518} 519 520/******************************************************************************* 521* copyAdjustedPathForURL() 522* 523* This function takes an URL with a given kext, and adjusts it to be absolute 524* or relative to the kext's containing repository, properly handling plugin 525* kexts to include the repository-path of the containing kext as well. 526*******************************************************************************/ 527CFStringRef copyAdjustedPathForURL( 528 OSKextRef theKext, 529 CFURLRef urlToAdjust, 530 PathSpec pathSpec) 531{ 532 CFStringRef result = NULL; 533 CFURLRef absURL = NULL; // must release 534 CFStringRef absPath = NULL; // must release 535 CFStringRef kextAbsPath = NULL; // must release 536 CFStringRef kextRelPath = NULL; // must release 537 CFStringRef pathInKext = NULL; // must release 538 CFRange scratchRange; 539 540 if (pathSpec != kPathsFull && pathSpec != kPathsRelative) { 541 OSKextLog(theKext, 542 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 543 "Invalid argument to copyAdjustedPathForURL()."); 544 } 545 546 absURL = CFURLCopyAbsoluteURL(urlToAdjust); 547 if (!absURL) { 548 OSKextLogMemError(); 549 goto finish; 550 } 551 552 if (pathSpec == kPathsFull) { 553 result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 554 goto finish; 555 } 556 557 /***** 558 * Okay, we are doing repository-relative paths here. Here's how! 559 * We are strip the matching part of the kext's absolute path 560 * from the URL/path handed in, which gives us the path in the kext. 561 * Then we tack that back onto the kext's repository-relative path. Got it? 562 */ 563 564 kextAbsPath = copyPathForKext(theKext, kPathsFull); 565 kextRelPath = copyPathForKext(theKext, kPathsRelative); 566 absPath = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle); 567 if (!kextAbsPath || !kextRelPath || !absPath) { 568 goto finish; 569 } 570 571 scratchRange = CFRangeMake(CFStringGetLength(kextAbsPath), 572 CFStringGetLength(absPath) - CFStringGetLength(kextAbsPath)); 573 pathInKext = CFStringCreateWithSubstring(kCFAllocatorDefault, absPath, 574 scratchRange); 575 if (!pathInKext) { 576 OSKextLogMemError(); 577 } 578 result = CFStringCreateWithFormat(kCFAllocatorDefault, /* options */ 0, 579 CFSTR("%@%@"), kextRelPath, pathInKext); 580 581finish: 582 SAFE_RELEASE(absURL); 583 SAFE_RELEASE(absPath); 584 SAFE_RELEASE(kextAbsPath); 585 SAFE_RELEASE(kextRelPath); 586 SAFE_RELEASE(pathInKext); 587 return result; 588} 589 590/******************************************************************************* 591* XXX: I'm really not sure this is completely reliable for getting a relative 592* XXX: path. 593*******************************************************************************/ 594CFStringRef copyKextInfoDictionaryPath( 595 OSKextRef theKext, 596 PathSpec pathSpec) 597{ 598 CFStringRef result = NULL; 599 CFURLRef kextURL = NULL; // do not release 600 CFURLRef kextAbsURL = NULL; // must release 601 CFBundleRef kextBundle = NULL; // must release 602 CFURLRef infoDictURL = NULL; // must release 603 604 kextURL = OSKextGetURL(theKext); 605 if (!kextURL) { 606 OSKextLog(theKext, 607 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 608 "Kext has no URL!"); 609 goto finish; 610 } 611 kextAbsURL = CFURLCopyAbsoluteURL(kextURL); 612 if (!kextAbsURL) { 613 OSKextLogMemError(); 614 goto finish; 615 } 616 617 kextBundle = CFBundleCreate(kCFAllocatorDefault, kextAbsURL); 618 if (!kextBundle) { 619 OSKextLogMemError(); 620 goto finish; 621 } 622 infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle); 623 if (!infoDictURL) { 624 // not able to determine error here, bundle might have no plist 625 // (well, we should never have gotten here if that were the case) 626 result = CFStringCreateWithCString(kCFAllocatorDefault, "", 627 kCFStringEncodingUTF8); 628 goto finish; 629 } 630 631 result = copyAdjustedPathForURL(theKext, infoDictURL, pathSpec); 632 633finish: 634 SAFE_RELEASE(infoDictURL); 635 SAFE_RELEASE(kextBundle); 636 SAFE_RELEASE(kextAbsURL); 637 return result; 638} 639 640/******************************************************************************* 641* 642*******************************************************************************/ 643void printKextInfoDictionary( 644 OSKextRef theKext, 645 PathSpec pathSpec, 646 char lineEnd) 647{ 648 CFStringRef infoDictPath = NULL; // must release 649 char * infoDictPathCString = NULL; // must free 650 651 infoDictPath = copyKextInfoDictionaryPath(theKext, pathSpec); 652 if (!infoDictPath) { 653 OSKextLogMemError(); 654 goto finish; 655 } 656 657 infoDictPathCString = createUTF8CStringForCFString(infoDictPath); 658 if (!infoDictPathCString) { 659 OSKextLogMemError(); 660 goto finish; 661 } 662 663 printf("%s%c", infoDictPathCString, lineEnd); 664 665 666finish: 667 SAFE_FREE(infoDictPathCString); 668 SAFE_RELEASE(infoDictPath); 669 return; 670} 671 672/******************************************************************************* 673* XXX: I'm really not sure this is completely reliable for getting a relative 674* XXX: path. 675*******************************************************************************/ 676CFStringRef copyKextExecutablePath( 677 OSKextRef theKext, 678 PathSpec pathSpec) 679{ 680 CFStringRef result = NULL; 681 CFURLRef kextURL = NULL; // do not release 682 CFURLRef kextAbsURL = NULL; // must release 683 CFURLRef executableURL = NULL; // must release 684 685 kextURL = OSKextGetURL(theKext); 686 if (!kextURL) { 687 OSKextLog(theKext, 688 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 689 "Kext has no URL!"); 690 goto finish; 691 } 692 kextAbsURL = CFURLCopyAbsoluteURL(kextURL); 693 if (!kextAbsURL) { 694 OSKextLogMemError(); 695 goto finish; 696 } 697 698 executableURL = _CFBundleCopyExecutableURLInDirectory(kextAbsURL); 699 if (!executableURL) { 700 // not able to determine error here, bundle might have no executable 701 result = CFStringCreateWithCString(kCFAllocatorDefault, "", 702 kCFStringEncodingUTF8); 703 goto finish; 704 } 705 result = copyAdjustedPathForURL(theKext, executableURL, pathSpec); 706 707finish: 708 SAFE_RELEASE(executableURL); 709 SAFE_RELEASE(kextAbsURL); 710 return result; 711} 712 713/******************************************************************************* 714* XXX: I'm really not sure this is completely reliable for getting a relative 715* XXX: path. 716*******************************************************************************/ 717void printKextExecutable( 718 OSKextRef theKext, 719 PathSpec pathSpec, 720 char lineEnd) 721{ 722 CFStringRef executablePath = NULL; // must release 723 char * executablePathCString = NULL; // must free 724 725 executablePath = copyKextExecutablePath(theKext, pathSpec); 726 if (!executablePath) { 727 OSKextLogMemError(); 728 goto finish; 729 } 730 731 executablePathCString = createUTF8CStringForCFString(executablePath); 732 if (!executablePathCString) { 733 OSKextLogMemError(); 734 goto finish; 735 } 736 737 printf("%s%c", executablePathCString, lineEnd); 738 739 740finish: 741 SAFE_FREE(executablePathCString); 742 SAFE_RELEASE(executablePath); 743 return; 744} 745