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 <IOKit/kext/OSKext.h> 24#include <IOKit/kext/OSKextPrivate.h> 25 26#include <stdio.h> 27#include <stdlib.h> 28#include <unistd.h> 29 30#include <sys/types.h> 31#include <sys/param.h> 32 33#include <mach/mach.h> 34#include <mach/mach_error.h> 35#include <mach/mach_host.h> 36 37#include "kextstat_main.h" 38 39// not a utility.[ch] customer yet 40static const char * progname = "(unknown)"; 41 42void printPList_new(FILE * stream, CFTypeRef aPlist); 43/******************************************************************************* 44* 45*******************************************************************************/ 46ExitStatus main(int argc, char * const * argv) 47{ 48 ExitStatus result = EX_OK; 49 KextstatArgs toolArgs; 50 CFDictionaryRef * kextInfoList = NULL; // must free 51 CFIndex count, i; 52 53 if (argv[0]) { 54 progname = argv[0]; 55 } 56 57 /* Set the OSKext log callback right away. 58 */ 59 OSKextSetLogOutputFunction(&tool_log); 60 61 result = readArgs(argc, argv, &toolArgs); 62 if (result != EX_OK) { 63 if (result == kKextstatExitHelp) { 64 result = EX_OK; 65 } 66 goto finish; 67 } 68 69 toolArgs.runningKernelArch = OSKextGetRunningKernelArchitecture(); 70 if (!toolArgs.runningKernelArch) { 71 result = EX_OSERR; 72 goto finish; 73 } 74 75 toolArgs.loadedKextInfo = OSKextCopyLoadedKextInfo(toolArgs.bundleIDs, 76 NULL /* all info */); 77 78 if (!toolArgs.loadedKextInfo) { 79 OSKextLog(/* kext */ NULL, 80 kOSKextLogErrorLevel | kOSKextLogGeneralFlag | kOSKextLogIPCFlag, 81 "Couldn't get list of loaded kexts from kernel."); 82 result = EX_OSERR; 83 goto finish; 84 } 85 86 if (!toolArgs.flagListOnly) { 87 printf("Index Refs Address "); 88 if (toolArgs.runningKernelArch->cputype & CPU_ARCH_ABI64) { 89 printf(" "); 90 } 91 printf("Size Wired " 92 "Name (Version) <Linked Against>\n"); 93 } 94 95 count = CFDictionaryGetCount(toolArgs.loadedKextInfo); 96 if (!count) { 97 goto finish; 98 } 99 100 kextInfoList = (CFDictionaryRef *)malloc(count * sizeof(CFDictionaryRef)); 101 if (!kextInfoList) { 102 OSKextLogMemError(); 103 result = EX_OSERR; 104 goto finish; 105 } 106 107 CFDictionaryGetKeysAndValues(toolArgs.loadedKextInfo, /* keys */ NULL, 108 (const void **)kextInfoList); 109 qsort(kextInfoList, count, sizeof(CFDictionaryRef), &compareKextInfo); 110 for (i = 0; i < count; i++) { 111 printKextInfo(kextInfoList[i], &toolArgs); 112 } 113 114finish: 115 exit(result); 116 117 SAFE_FREE(kextInfoList); 118 119 return result; 120} 121 122/******************************************************************************* 123*******************************************************************************/ 124ExitStatus readArgs(int argc, char * const * argv, KextstatArgs * toolArgs) 125{ 126 ExitStatus result = EX_USAGE; 127 CFStringRef scratchString = NULL; // must release 128 int optChar = 0; 129 130 bzero(toolArgs, sizeof(*toolArgs)); 131 132 /***** 133 * Allocate collection objects needed for command line argument processing. 134 */ 135 if (!createCFMutableArray(&toolArgs->bundleIDs, &kCFTypeArrayCallBacks)) { 136 goto finish; 137 } 138 139 /***** 140 * Process command-line arguments. 141 */ 142 result = EX_USAGE; 143 144 while ((optChar = getopt_long_only(argc, argv, kOptChars, 145 sOptInfo, NULL)) != -1) { 146 147 SAFE_RELEASE_NULL(scratchString); 148 149 switch (optChar) { 150 151 case kOptHelp: 152 usage(kUsageLevelFull); 153 result = kKextstatExitHelp; 154 goto finish; 155 break; 156 157 case kOptNoKernelComponents: 158 toolArgs->flagNoKernelComponents = true; 159 break; 160 161 case kOptListOnly: 162 toolArgs->flagListOnly = true; 163 break; 164 165 case kOptBundleIdentifier: 166 scratchString = CFStringCreateWithCString(kCFAllocatorDefault, 167 optarg, kCFStringEncodingUTF8); 168 if (!scratchString) { 169 OSKextLogMemError(); 170 result = EX_OSERR; 171 goto finish; 172 } 173 CFArrayAppendValue(toolArgs->bundleIDs, scratchString); 174 break; 175 176 } 177 } 178 179 argc -= optind; 180 argv += optind; 181 182 if (argc) { 183 OSKextLog(/* kext */ NULL, 184 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 185 "Extra arguments starting at %s....", argv[0]); 186 usage(kUsageLevelBrief); 187 goto finish; 188 } 189 190 result = EX_OK; 191 192finish: 193 SAFE_RELEASE_NULL(scratchString); 194 195 if (result == EX_USAGE) { 196 usage(kUsageLevelBrief); 197 } 198 return result; 199} 200 201/******************************************************************************* 202*******************************************************************************/ 203#define kStringInvalidShort "??" 204#define kStringInvalidLong "????" 205 206void printKextInfo(CFDictionaryRef kextInfo, KextstatArgs * toolArgs) 207{ 208 CFBooleanRef isKernelComponent = NULL; // do not release 209 CFNumberRef loadTag = NULL; // do not release 210 CFNumberRef retainCount = NULL; // do not release 211 CFNumberRef loadAddress = NULL; // do not release 212 CFNumberRef loadSize = NULL; // do not release 213 CFNumberRef wiredSize = NULL; // do not release 214 CFStringRef bundleID = NULL; // do not release 215 CFStringRef bundleVersion = NULL; // do not release 216 CFArrayRef dependencyLoadTags = NULL; // do not release 217 CFMutableArrayRef sortedLoadTags = NULL; // must release 218 219 uint32_t loadTagValue = kOSKextInvalidLoadTag; 220 uint32_t retainCountValue = (uint32_t)-1; 221 uint64_t loadAddressValue = (uint64_t)-1; 222 uint32_t loadSizeValue = (uint32_t)-1; 223 uint32_t wiredSizeValue = (uint32_t)-1; 224 char * bundleIDCString = NULL; // must free 225 char * bundleVersionCString = NULL; // must free 226 227 CFIndex count, i; 228 229 loadTag = (CFNumberRef)CFDictionaryGetValue(kextInfo, 230 CFSTR(kOSBundleLoadTagKey)); 231 retainCount = (CFNumberRef)CFDictionaryGetValue(kextInfo, 232 CFSTR(kOSBundleRetainCountKey)); 233 loadAddress = (CFNumberRef)CFDictionaryGetValue(kextInfo, 234 CFSTR(kOSBundleLoadAddressKey)); 235 loadSize = (CFNumberRef)CFDictionaryGetValue(kextInfo, 236 CFSTR(kOSBundleLoadSizeKey)); 237 wiredSize = (CFNumberRef)CFDictionaryGetValue(kextInfo, 238 CFSTR(kOSBundleWiredSizeKey)); 239 bundleID = (CFStringRef)CFDictionaryGetValue(kextInfo, 240 kCFBundleIdentifierKey); 241 bundleVersion = (CFStringRef)CFDictionaryGetValue(kextInfo, 242 kCFBundleVersionKey); 243 dependencyLoadTags = (CFArrayRef)CFDictionaryGetValue(kextInfo, 244 CFSTR(kOSBundleDependenciesKey)); 245 246 /* If the -k flag was given, skip any kernel components unless 247 * they are explicitly requested. 248 */ 249 if (toolArgs->flagNoKernelComponents) { 250 isKernelComponent = (CFBooleanRef)CFDictionaryGetValue(kextInfo, 251 CFSTR(kOSKernelResourceKey)); 252 if (isKernelComponent && CFBooleanGetValue(isKernelComponent)) { 253 if (bundleID && 254 kCFNotFound == CFArrayGetFirstIndexOfValue(toolArgs->bundleIDs, 255 RANGE_ALL(toolArgs->bundleIDs), bundleID)) { 256 257 goto finish; 258 } 259 } 260 } 261 262 if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) { 263 loadTagValue = kOSKextInvalidLoadTag; 264 } 265 266 /* Never print the info for the kernel (loadTag 0, id __kernel__). 267 */ 268 if (loadTagValue == 0) { 269 goto finish; 270 } 271 272 if (!getNumValue(retainCount, kCFNumberSInt32Type, &retainCountValue)) { 273 retainCountValue = (uint32_t)-1; 274 } 275 if (!getNumValue(loadAddress, kCFNumberSInt64Type, &loadAddressValue)) { 276 loadAddressValue = (uint64_t)-1; 277 } 278 if (!getNumValue(loadSize, kCFNumberSInt32Type, &loadSizeValue)) { 279 loadSizeValue = (uint32_t)-1; 280 } 281 if (!getNumValue(wiredSize, kCFNumberSInt32Type, &wiredSizeValue)) { 282 wiredSizeValue = (uint32_t)-1; 283 } 284 285 bundleIDCString = createUTF8CStringForCFString(bundleID); 286 bundleVersionCString = createUTF8CStringForCFString(bundleVersion); 287 288 /* First column has no leading space. 289 * 290 * These field widths are from the old kextstat, may want to change them. 291 */ 292 if (loadTagValue == kOSKextInvalidLoadTag) { 293 fprintf(stdout, "%5s", kStringInvalidShort); 294 } else { 295 fprintf(stdout, "%5d", loadTagValue); 296 } 297 298 if (retainCountValue == (uint32_t)-1) { 299 fprintf(stdout, " %4s", kStringInvalidShort); 300 } else { 301 fprintf(stdout, " %4d", retainCountValue); 302 } 303 304 if (toolArgs->runningKernelArch->cputype & CPU_ARCH_ABI64) { 305 if (loadAddressValue == (uint64_t)-1) { 306 fprintf(stdout, " %-18s", kStringInvalidLong); 307 } else { 308 fprintf(stdout, " %#-18llx", (uint64_t)loadAddressValue); 309 } 310 } else { 311 if (loadAddressValue == (uint64_t)-1) { 312 fprintf(stdout, " %-10s", kStringInvalidLong); 313 } else { 314 fprintf(stdout, " %#-10x", (uint32_t)loadAddressValue); 315 } 316 } 317 318 if (loadSizeValue == (uint32_t)-1) { 319 fprintf(stdout, " %-10s", kStringInvalidLong); 320 } else { 321 fprintf(stdout, " %#-10x", loadSizeValue); 322 } 323 324 if (wiredSizeValue == (uint32_t)-1) { 325 fprintf(stdout, " %-10s", kStringInvalidLong); 326 } else { 327 fprintf(stdout, " %#-10x", wiredSizeValue); 328 } 329 330 fprintf(stdout, " %s", 331 bundleIDCString ? bundleIDCString : kStringInvalidLong); 332 333 fprintf(stdout, " (%s)", 334 bundleVersionCString ? bundleVersionCString : kStringInvalidLong); 335 336 if (dependencyLoadTags && CFArrayGetCount(dependencyLoadTags)) { 337 sortedLoadTags = CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, 338 dependencyLoadTags); 339 if (!sortedLoadTags) { 340 OSKextLogMemError(); 341 goto finish; 342 } 343 344 CFArraySortValues(sortedLoadTags, RANGE_ALL(sortedLoadTags), 345 &compareNumbers, /* context */ NULL); 346 347 fprintf(stdout, " <"); 348 count = CFArrayGetCount(sortedLoadTags); 349 for (i = 0; i < count; i++) { 350 loadTag = (CFNumberRef)CFArrayGetValueAtIndex(sortedLoadTags, i); 351 if (!getNumValue(loadTag, kCFNumberSInt32Type, &loadTagValue)) { 352 loadTagValue = kOSKextInvalidLoadTag; 353 } 354 355 if (loadTagValue == kOSKextInvalidLoadTag) { 356 fprintf(stdout, "%s%s", i == 0 ? "" : " ", kStringInvalidShort); 357 } else { 358 fprintf(stdout, "%s%d", i == 0 ? "" : " ", loadTagValue); 359 } 360 361 } 362 363 fprintf(stdout, ">"); 364 365 } 366 367 fprintf(stdout, "\n"); 368 369finish: 370 371 SAFE_RELEASE(sortedLoadTags); 372 SAFE_FREE(bundleIDCString); 373 SAFE_FREE(bundleVersionCString); 374 return; 375} 376 377/******************************************************************************* 378*******************************************************************************/ 379Boolean getNumValue(CFNumberRef aNumber, CFNumberType type, void * valueOut) 380{ 381 if (aNumber && (CFNumberGetTypeID() == CFGetTypeID(aNumber))) { 382 return CFNumberGetValue(aNumber, type, valueOut); 383 } 384 return false; 385} 386 387/******************************************************************************* 388*******************************************************************************/ 389int compareKextInfo(const void * vKextInfo1, const void * vKextInfo2) 390{ 391 int result = 0; 392 CFDictionaryRef kextInfo1 = *(CFDictionaryRef *)vKextInfo1; 393 CFDictionaryRef kextInfo2 = *(CFDictionaryRef *)vKextInfo2; 394 CFNumberRef loadTag1 = CFDictionaryGetValue(kextInfo1, CFSTR(kOSBundleLoadTagKey)); 395 CFNumberRef loadTag2 = CFDictionaryGetValue(kextInfo2, CFSTR(kOSBundleLoadTagKey)); 396 OSKextLoadTag tag1Value = kOSKextInvalidLoadTag; 397 OSKextLoadTag tag2Value = kOSKextInvalidLoadTag; 398 399 getNumValue(loadTag1, kCFNumberSInt32Type, &tag1Value); 400 getNumValue(loadTag2, kCFNumberSInt32Type, &tag2Value); 401 402 if (tag1Value == tag2Value) { 403 /* Whether invalid or valid, same is same. */ 404 result = 0; 405 } else if (tag1Value == kOSKextInvalidLoadTag) { 406 result = -1; 407 } else if (tag2Value == kOSKextInvalidLoadTag) { 408 result = 1; 409 } else if (tag1Value < tag2Value) { 410 result = -1; 411 } else if (tag2Value < tag1Value) { 412 result = 1; 413 } 414 415 return result; 416} 417 418/******************************************************************************* 419*******************************************************************************/ 420CFComparisonResult compareNumbers( 421 const void * val1, 422 const void * val2, 423 void * context) 424{ 425 CFComparisonResult result = CFNumberCompare((CFNumberRef)val1, 426 (CFNumberRef)val2, context); 427 if (result == kCFCompareLessThan) { 428 result = kCFCompareGreaterThan; 429 } else if (result == kCFCompareGreaterThan) { 430 result = kCFCompareLessThan; 431 } 432 return result; 433} 434 435/******************************************************************************* 436*******************************************************************************/ 437static void usage(UsageLevel usageLevel) 438{ 439 fprintf(stderr, "usage: %s [-k] [-l] [-b bundle_id] ...\n", progname); 440 441 if (usageLevel == kUsageLevelBrief) { 442 fprintf(stderr, "\nUse %s -%s (-%c) for a list of options.\n", 443 progname, kOptNameHelp, kOptHelp); 444 return; 445 } 446 447 fprintf(stderr, "-%s (-%c): show only loadable kexts (omit kernel components).\n", 448 kOptNameNoKernelComponents, kOptNoKernelComponents); 449 fprintf(stderr, "-%s (-%c): print the list only, omitting the header.\n", 450 kOptNameListOnly, kOptListOnly); 451 fprintf(stderr, "-%s (-%c) <bundle_id>: print info for kexts named by identifier.\n", 452 kOptNameBundleIdentifier, kOptBundleIdentifier); 453 454 return; 455} 456 457