#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/kext/KXKextManager.h>
#include "kextfind.h"
#include "kextfind_tables.h"
#include "kextfind_query.h"
#include "kextfind_commands.h"
#include "kextfind_report.h"
#include "utility.h"
#include "QEQuery.h"
#define kKextSuffix ".kext"
Boolean addSearchItem(
const char * optarg,
CFMutableArrayRef repositoryDirectories,
CFMutableArrayRef kextNames);
static int allocateArray(CFMutableArrayRef * array);
static void usage(int level);
const char * progname = "(unknown)";
int g_verbose_level = kKXKextManagerLogLevelDefault;
int main(int argc, char * const *argv)
{
int exit_code = 0;
int opt_char = 0;
int last_optind;
CFIndex count, i;
QEQueryRef query = NULL;
struct querySetup * queryCallback = queryCallbackList;
QEQueryRef reportQuery = NULL;
struct querySetup * reportCallback = reportCallbackList;
uint32_t reportStartIndex;
QueryContext queryContext = { 0, };
Boolean queryStarted = false;
uint32_t numArgsUsed = 0;
CFMutableArrayRef repositoryDirectories = NULL; CFMutableArrayRef kextNames = NULL;
KXKextManagerRef theKextManager = NULL; KXKextRef theKext = NULL; KXKextManagerError kmErr;
int addKextsResult = 1;
CFArrayRef candidateKexts = NULL;
queryContext.assertiveness = kKextfindPicky;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
if (!allocateArray(&repositoryDirectories)) {
goto finish;
}
if (!allocateArray(&kextNames)) {
goto finish;
}
opterr = 0;
last_optind = optind;
while ((opt_char = getopt_long_only(argc, argv, kOPT_CHARS,
opt_info, NULL)) != -1) {
switch (opt_char) {
case kOptHelp:
usage(2);
exit_code = 0;
goto finish;
break;
case kOptCaseInsensitive:
queryContext.caseInsensitive = true;
break;
case kOptSearchItem:
if (!check_dir(optarg, 0 , 0 )) {
qerror("%s is not a kext or directory\n", optarg);
goto finish;
}
if (!addSearchItem(optarg, repositoryDirectories, kextNames)) {
goto finish;
};
break;
case kOptSubstring:
queryContext.substrings = true;
break;
case kOptSystemExtensions:
CFArrayAppendValue(repositoryDirectories, kKXSystemExtensionsFolder);
break;
case 0:
switch (longopt) {
case kLongOptQueryPredicate:
optind = last_optind;
if (argv[last_optind] && (argv[last_optind][0] != '-')) {
goto options_finished;
}
goto begin_query_or_report;
break;
#ifdef EXTRA_INFO
case kLongOptExtraInfo:
queryContext.extraInfo = true;
break;
#endif
case kLongOptRelativePaths:
queryContext.pathSpec = kPathsRelative;
break;
case kLongOptNoPaths:
queryContext.pathSpec = kPathsNone;
break;
#ifdef MEEK_PICKY
case kLongOptMeek:
queryContext.assertiveness = kKextfindMeek;
break;
case kLongOptPicky:
queryContext.assertiveness = kKextfindPicky;
break;
#endif
default:
qerror("internal argument processing error\n");
goto finish;
break;
}
longopt = 0;
break;
default:
optind = last_optind;
goto options_finished;
break;
}
last_optind = optind;
}
options_finished:
while (check_dir(argv[optind], 0 , 0 )) {
if (!addSearchItem(argv[optind], repositoryDirectories, kextNames)) {
goto finish;
};
optind++;
}
begin_query_or_report:
query = QEQueryCreate(&queryContext);
if (!query) {
fprintf(stderr, "Can't create query\n");
goto finish;
}
while (queryCallback->longName) {
if (queryCallback->parseCallback) {
QEQuerySetParseCallbackForPredicate(query, queryCallback->longName,
queryCallback->parseCallback);
if (queryCallback->shortName) {
QEQuerySetSynonymForPredicate(query, queryCallback->shortName,
queryCallback->longName);
}
}
if (queryCallback->evalCallback) {
QEQuerySetEvaluationCallbackForPredicate(query,
queryCallback->longName,
queryCallback->evalCallback);
}
queryCallback++;
}
QEQuerySetSynonymForPredicate(query, CFSTR("!"), CFSTR(kQEQueryTokenNot));
numArgsUsed = optind;
if (argv[numArgsUsed] && strcmp(argv[numArgsUsed], kKeywordReport)) {
while (QEQueryAppendElementFromArgs(query, argc - numArgsUsed,
&argv[numArgsUsed], &numArgsUsed)) {
queryStarted = true;
if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kKeywordReport)) {
break;
}
}
}
if (QEQueryLastError(query) != kQEQueryErrorNone) {
switch (QEQueryLastError(query)) {
case kQEQueryErrorNoMemory:
fprintf(stderr, "memory allocation failure\n");
break;
case kQEQueryErrorEmptyGroup:
fprintf(stderr, "empty group near arg #%d\n", numArgsUsed);
break;
case kQEQueryErrorSyntax:
fprintf(stderr, "query syntax error near '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
break;
case kQEQueryErrorNoParseCallback:
if (queryStarted) {
fprintf(stderr, "expected query predicate, found '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
} else {
fprintf(stderr, "unknown option/query predicate '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
usage(1);
}
break;
case kQEQueryErrorInvalidOrMissingArgument:
fprintf(stderr, "invalid/missing option or argument for '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
break;
case kQEQueryErrorParseCallbackFailed:
fprintf(stderr, "query parsing callback failed\n");
break;
default:
break;
}
goto finish;
}
if (!QEQueryIsComplete(query)) {
fprintf(stderr, "unbalanced groups or trailing operator\n");
goto finish;
}
if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kKeywordReport)) {
numArgsUsed++;
if (argv[numArgsUsed] && !strcmp(argv[numArgsUsed], kNoReportHeader)) {
numArgsUsed++;
queryContext.reportStarted = true; }
if (queryContext.commandSpecified) {
fprintf(stderr, "can't do report; query has commands\n");
goto finish;
}
reportQuery = QEQueryCreate(&queryContext);
if (!reportQuery) {
fprintf(stderr, "Can't create report engine\n");
goto finish;
}
QEQuerySetShortCircuits(reportQuery, false);
while (reportCallback->longName) {
if (reportCallback->parseCallback) {
QEQuerySetParseCallbackForPredicate(reportQuery,
reportCallback->longName,
reportCallback->parseCallback);
if (reportCallback->shortName) {
QEQuerySetSynonymForPredicate(reportQuery,
reportCallback->shortName,
reportCallback->longName);
}
}
if (reportCallback->evalCallback) {
QEQuerySetEvaluationCallbackForPredicate(reportQuery,
reportCallback->longName,
reportCallback->evalCallback);
}
reportCallback++;
}
reportStartIndex = numArgsUsed;
while (QEQueryAppendElementFromArgs(reportQuery, argc - numArgsUsed,
&argv[numArgsUsed], &numArgsUsed)) {
}
if (reportStartIndex == numArgsUsed) {
fprintf(stderr, "no report predicates specified\n");
usage(1);
goto finish;
}
if (QEQueryLastError(reportQuery) != kQEQueryErrorNone) {
switch (QEQueryLastError(reportQuery)) {
case kQEQueryErrorNoMemory:
fprintf(stderr, "memory allocation failure\n");
break;
case kQEQueryErrorEmptyGroup:
fprintf(stderr, "empty group near arg #%d\n", numArgsUsed);
break;
case kQEQueryErrorSyntax:
fprintf(stderr, "report syntax error near '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
break;
case kQEQueryErrorNoParseCallback:
if (1) {
fprintf(stderr, "expected report predicate, found '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
} else {
fprintf(stderr, "unknown option/report predicate '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
}
break;
case kQEQueryErrorInvalidOrMissingArgument:
fprintf(stderr, "invalid/missing option or argument for '%s' (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
break;
case kQEQueryErrorParseCallbackFailed:
fprintf(stderr, "query parsing callback failed\n");
break;
default:
break;
}
goto finish;
}
if (!QEQueryIsComplete(reportQuery)) {
fprintf(stderr, "unbalanced groups or trailing operator\n");
goto finish;
}
}
if (numArgsUsed < argc) {
fprintf(stderr, "leftover elements '%s'... (arg #%d)\n",
argv[numArgsUsed], numArgsUsed);
goto finish;
}
theKextManager = KXKextManagerCreate(kCFAllocatorDefault);
if (!theKextManager) {
qerror("can't allocate kernel extension manager\n");
goto finish;
}
kmErr = KXKextManagerInit(theKextManager, true ,
false );
if (kmErr != kKXKextManagerErrorNone) {
qerror("can't initialize kernel extension manager (%s)\n",
KXKextManagerErrorStaticCStringForError(kmErr));
goto finish;
}
KXKextManagerSetPerformsFullTests(theKextManager, true);
KXKextManagerSetPerformsStrictAuthentication(theKextManager, true);
KXKextManagerSetLogLevel(theKextManager, kKXKextManagerLogLevelSilent);
KXKextManagerSetLogFunction(theKextManager, &verbose_log);
KXKextManagerSetErrorLogFunction(theKextManager, &error_log);
KXKextManagerDisableClearRelationships(theKextManager);
count = CFArrayGetCount(repositoryDirectories);
if (count == 0 && CFArrayGetCount(kextNames) == 0) {
CFArrayAppendValue(repositoryDirectories, kKXSystemExtensionsFolder);
}
count = CFArrayGetCount(repositoryDirectories);
for (i = 0; i < count; i++) {
CFStringRef directory = (CFStringRef)CFArrayGetValueAtIndex(
repositoryDirectories, i);
CFURLRef directoryURL =
CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
directory, kCFURLPOSIXPathStyle, true);
if (!directoryURL) {
qerror("memory allocation failure\n");
goto finish;
}
kmErr = KXKextManagerAddRepositoryDirectory(theKextManager,
directoryURL, true ,
false , NULL);
if (kmErr != kKXKextManagerErrorNone) {
qerror("can't add repository (%s).\n",
KXKextManagerErrorStaticCStringForError(kmErr));
goto finish;
}
CFRelease(directoryURL);
directoryURL = NULL;
}
addKextsResult = addKextsToManager(theKextManager, kextNames,
NULL , false );
if (addKextsResult < 1) {
if (addKextsResult < 0) {
goto finish;
}
}
if (queryContext.checkAuthentic || queryContext.checkLoadable) {
KXKextManagerAuthenticateKexts(theKextManager);
}
if (queryContext.checkIntegrity) {
KXKextManagerVerifyIntegrityOfAllKexts(theKextManager);
}
KXKextManagerEnableClearRelationships(theKextManager);
KXKextManagerCalculateVersionRelationships(theKextManager);
KXKextManagerResolveAllKextDependencies(theKextManager);
if (queryContext.checkLoaded) {
KXKextManagerCheckForLoadedKexts(theKextManager);
}
candidateKexts = KXKextManagerCopyAllKexts(theKextManager);
if (!candidateKexts) {
qerror("internal error\n");
goto finish;
}
count = CFArrayGetCount(candidateKexts);
for (i = 0; i < count; i++) {
theKext = (KXKextRef)CFArrayGetValueAtIndex(candidateKexts, i);
if (QEQueryEvaluate(query, theKext)) {
if (!queryContext.commandSpecified) {
if (!reportQuery) {
printKext(theKext, queryContext.pathSpec,
queryContext.extraInfo, '\n');
} else {
if (!queryContext.reportStarted) {
queryContext.reportRowStarted = false;
QEQueryEvaluate(reportQuery, theKext);
printf("\n");
if ((QEQueryLastError(reportQuery) != kQEQueryErrorNone)) {
fprintf(stderr, "report evaluation error; aborting\n");
goto finish;
}
queryContext.reportStarted = true;
}
queryContext.reportRowStarted = false;
QEQueryEvaluate(reportQuery, theKext);
printf("\n");
if ((QEQueryLastError(reportQuery) != kQEQueryErrorNone)) {
fprintf(stderr, "report evaluation error; aborting\n");
goto finish;
}
}
}
} else if (QEQueryLastError(query) != kQEQueryErrorNone) {
fprintf(stderr, "query evaluation error; aborting\n");
goto finish;
}
}
exit_code = 0;
finish:
if (query) QEQueryFree(query);
if (repositoryDirectories) CFRelease(repositoryDirectories);
if (kextNames) CFRelease(kextNames);
if (theKextManager) CFRelease(theKextManager);
if (candidateKexts) CFRelease(candidateKexts);
exit(exit_code);
return exit_code;
}
Boolean addSearchItem(
const char * optarg,
CFMutableArrayRef repositoryDirectories,
CFMutableArrayRef kextNames)
{
Boolean result = false;
CFStringRef name = NULL;
name = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!name) {
qerror(kErrorStringMemoryAllocation);
goto finish;
}
if (CFStringHasSuffix(name, CFSTR(kKextSuffix))) {
CFArrayAppendValue(kextNames, name);
} else {
CFArrayAppendValue(repositoryDirectories, name);
}
result = true;
finish:
if (name) CFRelease(name);
return result;
}
static int allocateArray(CFMutableArrayRef * array) {
int result = 1;
if (!array) {
qerror("internal error\n");
result = 0;
goto finish;
}
*array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!*array) {
result = 0;
qerror(kErrorStringMemoryAllocation);
goto finish;
}
finish:
return result;
}
static void usage(int level)
{
FILE * stream = stderr;
fprintf(stream,
"usage: %s [options] [directory or extension ...] [query]\n"
" [-report [-no-header] report_predicate...]"
"\n",
progname);
if (level < 1) {
return;
}
if (level == 1) {
fprintf(stream, "use %s -%s for a list of options\n",
progname, kOptNameHelp);
return;
}
fprintf(stream, "Options\n");
fprintf(stream, " -%s -%s\n",
kOptNameHelp, kOptNameCaseInsensitive);
#ifdef EXTRA_INFO
fprintf(stream, " -%s -%s\n",
kOptNameExtraInfo, kOptNameNulTerminate);
#endif
fprintf(stream, " -%s -%s\n",
kOptNameRelativePaths, kOptNameSubstring);
fprintf(stream, " -%s\n",
kOptNameNoPaths);
fprintf(stream, "\n");
fprintf(stream, "Handy Query Predicates\n");
fprintf(stream, " %s [-s] [-i] id\n", kPredNameBundleID);
fprintf(stream, " %s [-s] [-i] id\n", kPredNameBundleName);
fprintf(stream, " %s [-s] [-i] name value\n", kPredNameMatchProperty);
fprintf(stream, " %s [-s] [-i] name value\n", kPredNameProperty);
fprintf(stream, "\n");
fprintf(stream, " %s %s\n",
kPredNameLoaded, kPredNameNonloadable);
fprintf(stream, " %s %s\n",
kPredNameInvalid, kPredNameInauthentic);
fprintf(stream, " %s %s\n",
kPredNameDependenciesMissing, kPredNameWarnings);
fprintf(stream, "\n");
fprintf(stream, " %s arch1[,arch2...] %s arch1[,arch2...]\n",
kPredNameArch, kPredNameArchExact);
fprintf(stream, " %s %s\n",
kPredNameExecutable, kPredNameIsLibrary);
fprintf(stream, " %s symbol %s symbol\n",
kPredNameDefinesSymbol, kPredNameReferencesSymbol);
fprintf(stream, "\n");
fprintf(stream, "See the man page for the full list.\n");
return;
}