/* * Copyright (c) 2006 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include "QEQuery.h" #include "kextfind_main.h" #include "kextfind_query.h" #include "kextfind_commands.h" /***** * Version expressions on the command line get parsed into an operator * and one or two binary versions. */ typedef enum { kVersionNone, kVersionEqual, kVersionNotEqual, kVersionGreaterThan, kVersionGreaterOrEqual, kVersionLessThan, kVersionLessOrEqual, kVersionRange } VersionOperator; /******************************************************************************* * Predicate option processing * * Include the query predicates and commands so that we can intelligently * handle sub-options. *******************************************************************************/ struct option echo_opt_info[] = { { kPredOptNameNoNewline, no_argument, NULL, kPredOptNoNewline }, { kOptNameNulTerminate, no_argument, NULL, kOptNulTerminate }, QUERY_PREDICATES QUERY_COMMANDS { NULL, 0, NULL, 0 } // sentinel to terminate list }; #define ECHO_OPTS "0n" /**********/ struct option property_opt_info[] = { { kPredOptNameCaseInsensitive, no_argument, NULL, kPredOptCaseInsensitive }, { kPredOptNameSubstring, no_argument, NULL, kPredOptSubstring }, QUERY_PREDICATES QUERY_COMMANDS { NULL, 0, NULL, 0 } // sentinel to terminate list }; #define PROPERTY_OPTS "is" /**********/ struct option print_opt_info[] = { { kOptNameNulTerminate, no_argument, NULL, kOptNulTerminate }, QUERY_PREDICATES QUERY_COMMANDS { NULL, 0, NULL, 0 } // sentinel to terminate list }; #define PRINT_OPTS "0" /******************************************************************************* * Module-private functions. *******************************************************************************/ static int parseVersionArg(const char * string, VersionOperator * versionOperator, OSKextVersion * version1, OSKextVersion * version2, uint32_t * error); Boolean parseStringElementOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error); Boolean parseEchoOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error); Boolean parsePrintOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error); Boolean handleNonOption(int opt_char, char * const argv[], QueryContext * context, QEQueryError * error); /******************************************************************************* * *******************************************************************************/ Boolean parseArgument( CFMutableDictionaryRef element, char * const argv[], uint32_t * num_used, void * user_data __unused, QEQueryError * error) { Boolean result = false; uint32_t index = 0; // this function starts with the arg itself CFStringRef arg = NULL; // must release if (!argv[index]) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } arg = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], kCFStringEncodingUTF8); index++; *num_used += index; // this is not a QE callback! :-) QEQueryElementAppendArgument(element, arg); result = true; finish: if (arg) CFRelease(arg); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseProperty( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; CFStringRef predicate = NULL; // don't release Boolean needValue = true; uint32_t index = 1; predicate = QEQueryElementGetPredicate(element); if (CFEqual(predicate, CFSTR(kPredNamePropertyExists))) { CFDictionarySetValue(element, CFSTR(kSearchStyleKeyExists), kCFBooleanTrue); needValue = false; } else if (!parseStringElementOptions(element, argc, argv, &index, context, error)) { goto finish; } /* Fudge the predicate so we can use one eval callback. */ QEQueryElementSetPredicate(element, CFSTR(kPredNameProperty)); if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } if (needValue) { if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } } result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseBundleName( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; uint32_t index = 1; if (!parseStringElementOptions(element, argc, argv, &index, context, error)) { goto finish; } if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } QEQueryElementSetPredicate(element, CFSTR(kPredNameBundleName)); result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalBundleName( CFDictionaryRef element, void * object, void * user_data, QEQueryError * error __unused) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; OSKextRef theKext = (OSKextRef)object; CFStringRef queryName = QEQueryElementGetArgumentAtIndex(element, 0); CFURLRef kextURL = NULL; // do not release CFStringRef bundleName = NULL; // must release kextURL = OSKextGetURL(theKext); if (!kextURL) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Kext has no URL!"); goto finish; } bundleName = CFURLCopyLastPathComponent(kextURL); if (!bundleName) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Kext has bad URL."); goto finish; } CFOptionFlags searchOptions = 0; if (context->caseInsensitive || CFDictionaryGetValue(element, CFSTR(kSearchStyleCaseInsensitive))) { searchOptions |= kCFCompareCaseInsensitive; } /* If the global or predicate substring flag was set, do a substring * match. */ if (context->substrings || CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { CFRange findResult = CFStringFind(bundleName, queryName, searchOptions); if (findResult.location != kCFNotFound) { result = true; } } else { CFComparisonResult compareResult = CFStringCompareWithOptions( bundleName, queryName, CFRangeMake(0, CFStringGetLength(bundleName)), searchOptions); if (compareResult == kCFCompareEqualTo) { result = true; } } finish: SAFE_RELEASE(bundleName); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseShorthand( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; CFStringRef predicate = QEQueryElementGetPredicate(element); uint32_t index = 1; if (CFEqual(predicate, CFSTR(kPredNameBundleID))) { if (!parseStringElementOptions(element, argc, argv, &index, context, error)) { goto finish; } QEQueryElementAppendArgument(element, kCFBundleIdentifierKey); if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } } else { if (CFEqual(predicate, CFSTR(kPredNameRoot))) { QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredRoot)); CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); } else if (CFEqual(predicate, CFSTR(kPredNameConsole))) { QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredConsole)); CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); } else if (CFEqual(predicate, CFSTR(kPredNameLocalRoot))) { QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredLocalRoot)); CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); } else if (CFEqual(predicate, CFSTR(kPredNameNetworkRoot))) { QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredNetworkRoot)); CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); } else if (CFEqual(predicate, CFSTR(kPredNameSafeBoot))) { QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredSafeBoot)); CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); } else { goto finish; } } QEQueryElementSetPredicate(element, CFSTR(kPredNameProperty)); result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean _evalPropertyInDict( CFDictionaryRef element, void * object, CFDictionaryRef searchDict, void * user_data, QEQueryError * error) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; QueryContext * context = (QueryContext *)user_data; CFStringRef propName = NULL; CFStringRef queryValue = NULL; CFTypeRef foundValue = NULL; CFTypeID foundType = 0; CFLocaleRef locale = NULL; // must release CFNumberFormatterRef formatter = NULL; // must release CFNumberRef searchNumber = NULL; // must release char * scratchCString = NULL; // must free propName = QEQueryElementGetArgumentAtIndex(element, 0); if (searchDict) { foundValue = CFDictionaryGetValue(searchDict, propName); } else { foundValue = OSKextGetValueForInfoDictionaryKey(theKext, propName); } if (!foundValue) { goto finish; } foundType = CFGetTypeID(foundValue); if (CFDictionaryGetValue(element, CFSTR(kSearchStyleKeyExists))) { if (foundValue) { result = true; } goto finish; } queryValue = QEQueryElementGetArgumentAtIndex(element, 1); if (foundType == CFStringGetTypeID()) { /* Exact searches trump any global settings in the query context. */ if (CFDictionaryGetValue(element, CFSTR(kSearchStyleExact))) { result = CFEqual(foundValue, queryValue); goto finish; } else { CFOptionFlags searchOptions = 0; if (context->caseInsensitive || CFDictionaryGetValue(element, CFSTR(kSearchStyleCaseInsensitive))) { searchOptions |= kCFCompareCaseInsensitive; } /* If the global or predicate substring flag was set, do a substring * match. */ if (context->substrings || CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { CFRange findResult = CFStringFind(foundValue, queryValue, searchOptions); if (findResult.location != kCFNotFound) { result = true; } } else { CFComparisonResult compareResult = CFStringCompareWithOptions( foundValue, queryValue, CFRangeMake(0, CFStringGetLength(foundValue)), searchOptions); if (compareResult == kCFCompareEqualTo) { result = true; } } goto finish; } } else if (foundType == CFNumberGetTypeID()) { /* Don't apply the global query context's substrings flag to numbers. */ if (CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { /* Substrings on numeric values doesn't really make sense. */ goto finish; } else { CFNumberRef foundNumber = NULL; // do not release CFRange stringRange; foundNumber = (CFNumberRef)foundValue; locale = CFLocaleCopyCurrent(); if (!locale) { *error = kQEQueryErrorEvaluationCallbackFailed; OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't get current locale."); goto finish; } formatter = CFNumberFormatterCreate(kCFAllocatorDefault, locale, kCFNumberFormatterNoStyle); if (!formatter) { *error = kQEQueryErrorEvaluationCallbackFailed; goto finish; } stringRange = CFRangeMake(0, CFStringGetLength(queryValue)); searchNumber = CFNumberFormatterCreateNumberFromString( kCFAllocatorDefault, formatter, queryValue, &stringRange, 0); if (!formatter) { *error = kQEQueryErrorEvaluationCallbackFailed; scratchCString = createUTF8CStringForCFString(queryValue); OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Failed to parse number string '%s'.", scratchCString); SAFE_FREE_NULL(scratchCString); goto finish; } if (CFEqual(foundNumber, searchNumber)) { result = true; goto finish; } } } else if (foundType == CFBooleanGetTypeID()) { /* Don't apply the global query context's substrings flag to numbers. */ if (CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { /* Substrings on boolean values really don't make sense. */ goto finish; } else { CFBooleanRef foundBoolean = (CFBooleanRef)foundValue; if (CFBooleanGetValue(foundBoolean) == true) { if (CFEqual(queryValue, CFSTR(kWordTrue)) || CFEqual(queryValue, CFSTR(kWordYes)) || CFEqual(queryValue, CFSTR(kWord1))) { result = true; goto finish; } } else { if (CFEqual(queryValue, CFSTR(kWordFalse)) || CFEqual(queryValue, CFSTR(kWordNo)) || CFEqual(queryValue, CFSTR(kWord0))) { result = true; goto finish; } } } } finish: SAFE_RELEASE(locale); SAFE_RELEASE(formatter); SAFE_RELEASE(searchNumber); SAFE_FREE(scratchCString); return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalProperty( CFDictionaryRef element, void * object, void * user_data, QEQueryError * error) { Boolean result = false; result = _evalPropertyInDict(element, object, /* dict */ NULL, user_data, error); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseMatchProperty( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; CFStringRef predicate = NULL; // don't release Boolean needValue = true; uint32_t index = 1; predicate = QEQueryElementGetPredicate(element); if (CFEqual(predicate, CFSTR(kPredNameMatchPropertyExists))) { CFDictionarySetValue(element, CFSTR(kSearchStyleKeyExists), kCFBooleanTrue); needValue = false; } else { if (!parseStringElementOptions(element, argc, argv, &index, context, error)) { goto finish; } } /* Fudge the predicate so we can use one eval callback. */ QEQueryElementSetPredicate(element, CFSTR(kPredNameMatchProperty)); if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } if (needValue) { if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } } result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalMatchProperty( CFDictionaryRef element, void * object, void * user_data, QEQueryError * error) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; CFArrayRef personalities = OSKextCopyPersonalitiesArray(theKext); CFIndex count, i; if (!personalities) { goto finish; } count = CFArrayGetCount(personalities); for (i = 0; i < count; i++) { CFDictionaryRef personality = CFArrayGetValueAtIndex(personalities, i); if (_evalPropertyInDict(element, object, personality, user_data, error)) { result = true; goto finish; } } finish: if (personalities) CFRelease(personalities); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseIntegrity( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; uint32_t index = 1; // don't care about predicate CFStringRef name = NULL; // must release CFStringRef value = NULL; // must release if (!argv[index]) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } if (strcmp(argv[index], kIntegrityCorrect) && strcmp(argv[index], kIntegrityUnknown) && strcmp(argv[index], kIntegrityNotApple) && strcmp(argv[index], kIntegrityNoReceipt) && strcmp(argv[index], kIntegrityModified)) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } value = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], kCFStringEncodingUTF8); index++; *num_used += index; QEQueryElementAppendArgument(element, value); context->checkIntegrity = true; /* Kext integrity is no longer used on SnowLeopard. */ OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Integrity states are no longer used; no kexts will match."); result = true; finish: if (name) CFRelease(name); if (value) CFRelease(value); return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalIntegrity( CFDictionaryRef element __unused, void * object __unused, void * user_data __unused, QEQueryError * error __unused) { Boolean result = false; /* Kext integrity is no longer used on SnowLeopard. */ return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseFlag( CFMutableDictionaryRef element, int argc __unused, char * const argv[] __unused, uint32_t * num_used, void * user_data, QEQueryError * error __unused) { CFStringRef flag = QEQueryElementGetPredicate(element); QueryContext * context = (QueryContext *)user_data; QEQueryElementSetPredicate(element, CFSTR(kPredNameFlag)); CFDictionarySetValue(element, CFSTR(kKeywordFlag), flag); if (CFEqual(flag, CFSTR(kPredNameLoaded))) { context->checkLoaded = true; } *num_used += 1; return true; } /******************************************************************************* * *******************************************************************************/ Boolean evalFlag( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error __unused) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; CFStringRef flag = CFDictionaryGetValue(element, CFSTR(kKeywordFlag)); if (CFEqual(flag, CFSTR(kPredNameLoaded))) { return OSKextIsLoaded(theKext); } else if (CFEqual(flag, CFSTR(kPredNameDuplicate))) { CFStringRef kextIdentifier = OSKextGetIdentifier(theKext); if (kextIdentifier) { CFArrayRef kexts = OSKextCopyKextsWithIdentifier(kextIdentifier); if (kexts && CFArrayGetCount(kexts) > 1) { result = true; } SAFE_RELEASE(kexts); } } else if (CFEqual(flag, CFSTR(kPredNameValid))) { return OSKextIsValid(theKext); } if (CFEqual(flag, CFSTR(kPredNameInvalid))) { return !OSKextIsValid(theKext); } else if (CFEqual(flag, CFSTR(kPredNameAuthentic))) { return OSKextIsAuthentic(theKext); } else if (CFEqual(flag, CFSTR(kPredNameInauthentic))) { return !OSKextIsAuthentic(theKext); } else if (CFEqual(flag, CFSTR(kPredNameDependenciesMet))) { return OSKextResolveDependencies(theKext); } else if (CFEqual(flag, CFSTR(kPredNameDependenciesMissing))) { return !OSKextResolveDependencies(theKext); } else if (CFEqual(flag, CFSTR(kPredNameLoadable))) { return OSKextIsLoadable(theKext); } else if (CFEqual(flag, CFSTR(kPredNameNonloadable))) { return !OSKextIsLoadable(theKext); } else if (CFEqual(flag, CFSTR(kPredNameWarnings))) { CFDictionaryRef warnings = OSKextCopyDiagnostics(theKext, kOSKextDiagnosticsFlagWarnings); if (warnings && CFDictionaryGetCount(warnings)) { result = true; } SAFE_RELEASE(warnings); } else if (CFEqual(flag, CFSTR(kPredNameIsLibrary))) { if (OSKextGetCompatibleVersion(theKext) > 0) { result = true; } } else if (CFEqual(flag, CFSTR(kPredNameHasPlugins))) { CFArrayRef plugins = OSKextCopyPlugins(theKext); if (plugins && CFArrayGetCount(plugins)) { result = true; } SAFE_RELEASE(plugins); } else if (CFEqual(flag, CFSTR(kPredNameIsPlugin))) { return OSKextIsPlugin(theKext); } else if (CFEqual(flag, CFSTR(kPredNameHasDebugProperties))) { return OSKextHasLogOrDebugFlags(theKext); } else if (CFEqual(flag, CFSTR(kPredNameIsKernelResource))) { return OSKextIsKernelComponent(theKext); } else if (CFEqual(flag, CFSTR(kPredNameExecutable))) { return OSKextDeclaresExecutable(theKext); } else if (CFEqual(flag, CFSTR(kPredNameNoExecutable))) { return !OSKextDeclaresExecutable(theKext); } return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseVersion( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data __unused, QEQueryError * error) { Boolean result = false; uint32_t index = 1; // don't care about predicate VersionOperator versionOperator; OSKextVersion version1 = 0; OSKextVersion version2 = 0; CFNumberRef vOpNum = NULL; // must release CFDataRef version1Data = NULL; // must release CFDataRef version2Data = NULL; // must release if (!argv[index]) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } if (!parseVersionArg(argv[index], &versionOperator, &version1, &version2, error)) { goto finish; } index++; vOpNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &versionOperator); if (!vOpNum) { *error = kQEQueryErrorNoMemory; goto finish; } version1Data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&version1, sizeof(version1)); if (!version1Data) { *error = kQEQueryErrorNoMemory; goto finish; } version2Data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&version2, sizeof(version2)); if (!version2Data) { *error = kQEQueryErrorNoMemory; goto finish; } QEQueryElementAppendArgument(element, vOpNum); QEQueryElementAppendArgument(element, version1Data); QEQueryElementAppendArgument(element, version2Data); result = true; finish: *num_used += index; if (vOpNum) CFRelease(vOpNum); if (version1Data) CFRelease(version1Data); if (version2Data) CFRelease(version2Data); return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalVersion( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; OSKextVersion kextVers = OSKextGetVersion(theKext); CFNumberRef vOpNum = NULL; // must release CFDataRef version1Data = NULL; // must release CFDataRef version2Data = NULL; // must release VersionOperator versionOperator; OSKextVersion version1; OSKextVersion version2; vOpNum = QEQueryElementGetArgumentAtIndex(element, 0); version1Data = QEQueryElementGetArgumentAtIndex(element, 1); version2Data = QEQueryElementGetArgumentAtIndex(element, 2); if (!CFNumberGetValue(vOpNum,kCFNumberSInt32Type,&versionOperator)) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Internal error getting version operator."); *error = kQEQueryErrorEvaluationCallbackFailed; goto finish; } version1 = *(OSKextVersion *)CFDataGetBytePtr(version1Data); version2 = *(OSKextVersion *)CFDataGetBytePtr(version2Data); switch (versionOperator) { case kVersionEqual: if (kextVers == version1) { result = true; } break; case kVersionNotEqual: if (kextVers != version1) { result = true; } break; case kVersionGreaterThan: if (kextVers > version1) { result = true; } break; case kVersionGreaterOrEqual: if (kextVers >= version1) { result = true; } break; case kVersionLessThan: if (kextVers < version1) { result = true; } break; case kVersionLessOrEqual: if (kextVers <= version1) { result = true; } break; case kVersionRange: if ((kextVers >= version1) && (kextVers <= version2)) { result = true; } break; default: OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Internal evaluation error."); *error = kQEQueryErrorEvaluationCallbackFailed; break; } finish: return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseCompatibleWithVersion( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data __unused, QEQueryError * error) { Boolean result = false; uint32_t index = 1; // don't care about predicate OSKextVersion compatible_version = 0; CFDataRef versionData = NULL; // must release if (!argv[index]) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } compatible_version = OSKextParseVersionString(argv[index]); if (compatible_version == -1) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Invalid version string '%s'.", argv[index]); goto finish; } index++; versionData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&compatible_version, sizeof(compatible_version)); if (!versionData) { *error = kQEQueryErrorNoMemory; goto finish; } QEQueryElementAppendArgument(element, versionData); result = true; finish: *num_used += index; if (versionData) CFRelease(versionData); return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalCompatibleWithVersion( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error __unused) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; CFDataRef versionData = NULL; // must release OSKextVersion compatible_version = 0; versionData = QEQueryElementGetArgumentAtIndex(element, 0); compatible_version = *(OSKextVersion *)CFDataGetBytePtr(versionData); if (OSKextIsCompatibleWithVersion(theKext, compatible_version)) { result = true; } return result; } /******************************************************************************* * parseVersionArg() *******************************************************************************/ static int parseVersionArg(const char * string, VersionOperator * versionOperator, OSKextVersion * version1, OSKextVersion * version2, uint32_t * error) { int result = -1; // assume parse error char * scratch = NULL; // must free char * v1string = NULL; // don't free char * hyphen = NULL; // don't free char * v2string = NULL; // don't free scratch = strdup(string); if (!scratch) { OSKextLogMemError(); result = 0; goto finish; } v1string = scratch; *versionOperator = kVersionNone; switch (scratch[0]) { case 'e': *versionOperator = kVersionEqual; v1string = &scratch[1]; break; case 'n': if (scratch[1] != 'e') { goto finish; } *versionOperator = kVersionNotEqual; v1string = &scratch[2]; break; case 'g': if (scratch[1] == 'e') { *versionOperator = kVersionGreaterOrEqual; } else if (scratch[1] == 't') { *versionOperator = kVersionGreaterThan; } else { goto finish; } v1string = &scratch[2]; break; case 'l': if (scratch[1] == 'e') { *versionOperator = kVersionLessOrEqual; } else if (scratch[1] == 't') { result = kVersionLessThan; } else { goto finish; } v1string = &scratch[2]; break; } hyphen = index(v1string, '-'); if (hyphen) { if (*versionOperator != kVersionNone) { goto finish; } *versionOperator = kVersionRange; v2string = hyphen + 1; *hyphen = '\0'; } *version1 = OSKextParseVersionString(v1string); if (*version1 == -1) { goto finish; } if (hyphen) { *version2 = OSKextParseVersionString(v2string); if (*version2 == -1) { goto finish; } } if (*versionOperator == kVersionNone) { *versionOperator = kVersionEqual; } result = 1; finish: if (scratch) { free(scratch); scratch = NULL; } if (result == -1) { *error = kQEQueryErrorInvalidOrMissingArgument; OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Invalid version string '%s'.", string); result = 0; } return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseArch( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data __unused, QEQueryError * error) { Boolean result = false; uint32_t index = 1; // don't care about predicate CFStringRef archString = NULL; // must release CFArrayRef arches = NULL; // must release CFIndex count, i; char * arch = NULL; // must free if (!argv[index]) { *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } archString = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], kCFStringEncodingUTF8); if (!archString) { *error = kQEQueryErrorNoMemory; goto finish; } index++; arches = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,archString, CFSTR(",")); if (!arches) { goto finish; } count = CFArrayGetCount(arches); for (i = 0; i < count; i++) { CFStringRef archString = NULL; const NXArchInfo * archinfo = NULL; archString = CFArrayGetValueAtIndex(arches, i); arch = createUTF8CStringForCFString(archString); if (!arch) { OSKextLogStringError(/* kext */ NULL); goto finish; } archinfo = NXGetArchInfoFromName(arch); if (!archinfo) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Unknown architecture %s.", arch); *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } free(arch); arch = NULL; } QEQueryElementSetArgumentsArray(element, arches); result = true; finish: *num_used += index; if (archString) CFRelease(archString); if (arches) CFRelease(arches); if (arch) free(arch); return result; } /******************************************************************************* * *******************************************************************************/ Boolean _checkArches( OSKextRef theKext, CFArrayRef arches) { Boolean result = false; CFIndex count, i; char * arch = NULL; // must free count = CFArrayGetCount(arches); for (i = 0; i < count; i++) { CFStringRef archString = NULL; const NXArchInfo * archinfo = NULL; archString = CFArrayGetValueAtIndex(arches, i); arch = createUTF8CStringForCFString(archString); if (!arch) { OSKextLogStringError(theKext); goto finish; } archinfo = NXGetArchInfoFromName(arch); if (!archinfo) { goto finish; } free(arch); arch = NULL; if (!OSKextSupportsArchitecture(theKext, archinfo)) { goto finish; } } result = true; finish: SAFE_FREE(arch); return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalArch( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error __unused) { OSKextRef theKext = (OSKextRef)object; CFArrayRef arches = NULL; // do not release arches = QEQueryElementGetArguments(element); if (!arches) { return false; } return _checkArches(theKext, arches); } /******************************************************************************* * *******************************************************************************/ typedef struct { struct fat_header fat_hdr; struct fat_arch fat_arch; } FakeFatHeader; Boolean evalArchExact( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error __unused) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; CFArrayRef arches = NULL; // do not release CFIndex count, i; fat_iterator fiter = NULL; // must close char * arch = NULL; // must free struct mach_header * farch; const NXArchInfo * archinfo = NULL; fiter = createFatIteratorForKext(theKext); if (!fiter) { goto finish; } arches = QEQueryElementGetArguments(element); if (!arches) { goto finish; } count = CFArrayGetCount(arches); /* First make sure every architecture requested exists in the executable. */ if (!_checkArches(theKext, arches)) { goto finish; } /* Now make sure every arch in the executable is represtented by at least * one requested arch. */ while ((farch = (struct mach_header *)fat_iterator_next_arch(fiter, NULL))) { int swap = 0; struct fat_arch fakeFatArch; Boolean thisArchFound = false; #if DEBUG const NXArchInfo * info; #endif if (farch->magic == MH_CIGAM || farch->magic == MH_CIGAM_64) { swap = 1; } fakeFatArch.cputype = CondSwapInt32(swap, farch->cputype); fakeFatArch.cpusubtype = CondSwapInt32(swap, farch->cpusubtype); #if DEBUG info = NXGetArchInfoFromCpuType(fakeFatArch.cputype, fakeFatArch.cpusubtype); fprintf(stderr, " checking architecture %s\n", info->name); #endif /* Find at least one requested arch that matches our faked-up fat * header. */ for (i = 0; i < count; i++) { CFStringRef archString = CFArrayGetValueAtIndex(arches, i); arch = createUTF8CStringForCFString(archString); if (!arch) { OSKextLogStringError(theKext); goto finish; } archinfo = NXGetArchInfoFromName(arch); if (!archinfo) { goto finish; } free(arch); arch = NULL; #if DEBUG fprintf(stderr, " %s? ", archinfo->name); #endif if (NXFindBestFatArch(archinfo->cputype, archinfo->cpusubtype, &fakeFatArch, 1)) { thisArchFound = true; break; #if DEBUG fprintf(stderr, "yes\n"); #endif } else { #if DEBUG fprintf(stderr, "no\n"); #endif } } if (!thisArchFound) { goto finish; } } result = true; finish: if (arch) free(arch); if (fiter) fat_iterator_close(fiter); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseDefinesOrReferencesSymbol( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; uint32_t index = 1; // don't care about predicate if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalDefinesOrReferencesSymbol( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error __unused) { Boolean result = false; OSKextRef theKext = (OSKextRef)object; CFStringRef predicate = NULL; // don't release Boolean seekingReference = false; char * symbol = NULL; // must free fat_iterator fiter = NULL; // must close struct mach_header * farch = NULL; void * farch_end = NULL; Boolean isKernelComponent; uint8_t nlist_type; predicate = QEQueryElementGetPredicate(element); if (CFEqual(predicate, CFSTR(kPredNameReferencesSymbol))) { seekingReference = true; } symbol = createUTF8CStringForCFString( QEQueryElementGetArgumentAtIndex(element, 0)); if (!symbol) { goto finish; } fiter = createFatIteratorForKext(theKext); if (!fiter) { goto finish; } /* KPI kexts have the symbols listed as undefined, and won't have * any unresolved references to anything. So, if seekingReference * is true, we have nothing to do. */ isKernelComponent = OSKextIsKernelComponent(theKext); if (isKernelComponent && seekingReference) { goto finish; } while ((farch = fat_iterator_next_arch(fiter, &farch_end))) { macho_seek_result seek_result = macho_find_symbol( farch, farch_end, symbol, &nlist_type, NULL); if (seek_result == macho_seek_result_found_no_value || seek_result == macho_seek_result_found) { uint8_t n_type = N_TYPE & nlist_type; /* A non-kernel component symbol matches if we're seeking: * - a reference (-rsym) and the symbol n_type is N_UNDF, or * - a definition (-dsym) and the symbol n_type is anything else. * * For kernel components we only care about defined symbols, * and in a KPI file those will be either N_UNDF or N_INDR. */ if (!isKernelComponent) { if ((seekingReference && (n_type == N_UNDF)) || (!seekingReference && (n_type != N_UNDF))) { result = true; } } else { if ((!seekingReference && (n_type == N_UNDF || n_type == N_INDR))) { result = true; } } if (result) { goto finish; } if ((seekingReference && (n_type == N_UNDF || n_type == N_INDR)) || (!seekingReference && (n_type != N_UNDF))) { result = true; goto finish; } } } finish: if (fiter) fat_iterator_close(fiter); if (symbol) free(symbol); return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseCommand( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; CFStringRef predicate = QEQueryElementGetPredicate(element); uint32_t index = 1; CFDictionarySetValue(element, CFSTR(kKeywordCommand), predicate); QEQueryElementSetPredicate(element, CFSTR(kPredNameCommand)); /* For the echo command, look for a -n option or -- to end options. */ if (CFEqual(predicate, CFSTR(kPredNameEcho))) { if (!parseEchoOptions(element, argc, argv, &index, context, error)) { goto finish; } } else if (CFStringHasPrefix(predicate, CFSTR(kPredPrefixPrint)) && !CFEqual(predicate, CFSTR(kPredNamePrint0)) && !CFEqual(predicate, CFSTR(kPredNamePrintDiagnostics))) { if (!parsePrintOptions(element, argc, argv, &index, context, error)) { goto finish; } } else if (CFEqual(predicate, CFSTR(kPredNamePrint0))) { CFDictionarySetValue(element, CFSTR(kKeywordCommand), CFSTR(kPredNamePrint)); CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), kCFBooleanTrue); } if (CFEqual(predicate, CFSTR(kPredNameEcho)) || CFEqual(predicate, CFSTR(kPredNamePrintProperty)) || CFEqual(predicate, CFSTR(kPredNamePrintMatchProperty))) { if (!parseArgument(element, &argv[index], &index, user_data, error)) { goto finish; } } context->commandSpecified = true; result = true; finish: *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ char terminatorForElement(CFDictionaryRef element) { if (CFDictionaryGetValue(element, CFSTR(kOptNameNulTerminate))) { return '\0'; } return '\n'; } /******************************************************************************* * *******************************************************************************/ Boolean evalCommand( CFDictionaryRef element, void * object, void * user_data, QEQueryError * error) { OSKextRef theKext = (OSKextRef)object; QueryContext * context = (QueryContext *)user_data; CFStringRef command = CFDictionaryGetValue(element, CFSTR(kKeywordCommand)); CFArrayRef arguments = NULL; CFStringRef arg = NULL; char * string = NULL; // must free if (CFEqual(command, CFSTR(kPredNameEcho))) { arguments = QEQueryElementGetArguments(element); arg = CFArrayGetValueAtIndex(arguments, 0); string = createUTF8CStringForCFString(arg); printf("%s", string); if (!CFDictionaryGetValue(element, CFSTR(kPredOptNameNoNewline))) { printf("%c", terminatorForElement(element)); } goto finish; } else if (CFEqual(command, CFSTR(kPredNamePrint))) { printKext(theKext, context->pathSpec, context->extraInfo, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNameBundleName))) { printKext(theKext, kPathsNone, context->extraInfo, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintDiagnostics))) { g_log_stream = stdout; OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); g_log_stream = stderr; } else if (CFEqual(command, CFSTR(kPredNamePrintProperty))) { arguments = QEQueryElementGetArguments(element); arg = CFArrayGetValueAtIndex(arguments, 0); printKextProperty(theKext, arg, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintMatchProperty))) { arguments = QEQueryElementGetArguments(element); arg = CFArrayGetValueAtIndex(arguments, 1); printKextMatchProperty(theKext, arg, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintArches))) { printKextArches(theKext, terminatorForElement(element), true); } else if (CFEqual(command, CFSTR(kPredNamePrintDependencies))) { printKextDependencies(theKext, context->pathSpec, context->extraInfo, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintDependents))) { printKextDependents(theKext, context->pathSpec, context->extraInfo, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintPlugins))) { printKextPlugins(theKext, context->pathSpec, context->extraInfo, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintIntegrity))) { /* Kext integrity is no longer used on SnowLeopard. */ printf("%s%c", "n/a", terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintInfoDictionary))) { printKextInfoDictionary(theKext, context->pathSpec, terminatorForElement(element)); } else if (CFEqual(command, CFSTR(kPredNamePrintExecutable))) { printKextExecutable(theKext, context->pathSpec, terminatorForElement(element)); } else { *error = kQEQueryErrorEvaluationCallbackFailed; goto finish; } finish: if (string) free(string); return true; } /******************************************************************************* * *******************************************************************************/ Boolean parseExec( CFMutableDictionaryRef element, int argc __unused, char * const argv[], uint32_t * num_used, void * user_data, QEQueryError * error) { Boolean result = false; QueryContext * context = (QueryContext *)user_data; uint32_t index = 1; // don't care about predicate CFStringRef arg = NULL; // must release context->commandSpecified = true; while (argv[index]) { if (!strcmp(argv[index], kExecTerminator)) { result = true; index++; goto finish; } SAFE_RELEASE_NULL(arg); arg = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], kCFStringEncodingUTF8); if (!arg) { *error = kQEQueryErrorNoMemory; goto finish; } QEQueryElementAppendArgument(element, arg); index++; } OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "No terminating ; for %s.", kPredNameExec); *error = kQEQueryErrorInvalidOrMissingArgument; finish: SAFE_RELEASE(arg); *num_used += index; return result; } /******************************************************************************* * *******************************************************************************/ Boolean evalExec( CFDictionaryRef element, void * object, void * user_data __unused, QEQueryError * error) { Boolean result = false; pid_t pid; int status; OSKextRef theKext = (OSKextRef)object; CFURLRef kextURL = NULL; // do not release CFStringRef kextPath = NULL; // must release CFBundleRef kextBundle = NULL; // must release CFURLRef infoDictURL = NULL; // must release CFStringRef infoDictPath = NULL; // must release CFURLRef executableURL = NULL; // must release CFStringRef executablePath = NULL; // must release char ** command_argv = NULL; // must free each, and whole CFArrayRef arguments = QEQueryElementGetArguments(element); CFMutableStringRef scratch = NULL; // must release char kextPathBuffer[PATH_MAX]; CFIndex count, i; *error = kQEQueryErrorEvaluationCallbackFailed; if (!arguments) { goto finish; } count = CFArrayGetCount(arguments); kextURL = OSKextGetURL(theKext); if (!kextURL) { OSKextLog(theKext, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Kext has no URL!"); *error = kQEQueryErrorUnspecified; goto finish; } if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ false, (UInt8 *)kextPathBuffer, sizeof(kextPathBuffer))) { OSKextLogStringError(theKext); *error = kQEQueryErrorUnspecified; goto finish; } kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle); if (!kextPath) { OSKextLogMemError(); *error = kQEQueryErrorNoMemory; goto finish; } kextBundle = CFBundleCreate(kCFAllocatorDefault, kextURL); if (!kextBundle) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, "Can't create bundle for %s.", kextPathBuffer); *error = kQEQueryErrorNoMemory; goto finish; } infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle); if (infoDictURL) { infoDictPath = CFURLCopyFileSystemPath(infoDictURL, kCFURLPOSIXPathStyle); } executableURL = CFBundleCopyExecutableURL(kextBundle); if (executableURL) { executablePath = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle); } /* Not having these two is an error. The executable may not exist. */ if (!kextPath || !infoDictPath) { goto finish; } command_argv = (char **)malloc((1 + count) * sizeof(char *)); if (!command_argv) { goto finish; } bzero(&command_argv, sizeof((1 + count) * sizeof(char *))); for (i = 0; i < count; i++) { scratch = CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFArrayGetValueAtIndex(arguments, i)); if (!scratch) { goto finish; } CFStringFindAndReplace(scratch, CFSTR(kExecInfoDictionaryReplace), infoDictPath, CFRangeMake(0, CFStringGetLength(scratch)), 0); if (executablePath) { CFStringFindAndReplace(scratch, CFSTR(kExecExecutableReplace), executablePath, CFRangeMake(0, CFStringGetLength(scratch)), 0); } CFStringFindAndReplace(scratch, CFSTR(kExecBundlePathReplace), kextPath, CFRangeMake(0, CFStringGetLength(scratch)), 0); command_argv[i] = createUTF8CStringForCFString(scratch); if (!command_argv[i]) { goto finish; } CFRelease(scratch); scratch = NULL; } command_argv[i] = NULL; pid = fork(); switch (pid) { case 0: // child execvp(command_argv[0], command_argv); break; case -1: // error perror("error forking for -exec"); goto finish; break; default: // parent waitpid(-1, &status, 0); if (WIFEXITED(status)) { // Zero exit status is true result = WEXITSTATUS(status) ? false : true; } break; } *error = kQEQueryErrorNone; finish: SAFE_RELEASE(scratch); SAFE_RELEASE(kextPath); SAFE_RELEASE(kextBundle); SAFE_RELEASE(infoDictURL); SAFE_RELEASE(infoDictPath); SAFE_RELEASE(executableURL); SAFE_RELEASE(executablePath); if (command_argv) { char ** arg = command_argv; while (*arg) { free(*arg); arg++; } free(command_argv); } return result; } /******************************************************************************* * *******************************************************************************/ static int last_optind; void reset_getopt(void) { optreset = 1; opterr = 0; optind = 1; last_optind = optind; return; } /******************************************************************************* * *******************************************************************************/ Boolean parseStringElementOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error) { Boolean result = true; // have to assume success until we hit a bad one int opt_char; reset_getopt(); while ((opt_char = getopt_long_only(argc, argv, PROPERTY_OPTS, property_opt_info, NULL)) != -1) { switch (opt_char) { case kPredOptCaseInsensitive: CFDictionarySetValue(element, CFSTR(kSearchStyleCaseInsensitive), kCFBooleanTrue); last_optind = optind; // save optind for a subsequent mismatch break; case kPredOptSubstring: CFDictionarySetValue(element, CFSTR(kSearchStyleSubstring), kCFBooleanTrue); last_optind = optind; // save optind for a subsequent mismatch break; case 0: /* Fall through. */ default: result = handleNonOption(opt_char, argv, context, error); goto finish; break; } } finish: if (index) { *index = optind; // we set rather than adding cause of getopt } return result; } /******************************************************************************* * *******************************************************************************/ Boolean parsePrintOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error) { Boolean result = true; // have to assume success until we hit a bad one int opt_char; reset_getopt(); while ((opt_char = getopt_long_only(argc, argv, PRINT_OPTS, print_opt_info, NULL)) != -1) { switch (opt_char) { case kOptNulTerminate: CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), kCFBooleanTrue); last_optind = optind; break; case 0: /* -print takes no arguments so if we see a query predicate we * are good to go. */ if (longopt == kLongOptQueryPredicate) { optind = last_optind; goto finish; } default: result = handleNonOption(opt_char, argv, context, error); goto finish; break; } } finish: if (index) { *index = optind; // we set rather than adding cause of getopt } return result; } /******************************************************************************* * *******************************************************************************/ Boolean parseEchoOptions( CFMutableDictionaryRef element, int argc, char * const argv[], uint32_t * index, QueryContext * context, QEQueryError * error) { Boolean result = true; // have to assume success until we hit a bad one int opt_char; reset_getopt(); while ((opt_char = getopt_long_only(argc, argv, ECHO_OPTS, echo_opt_info, NULL)) != -1) { switch (opt_char) { case kPredOptNoNewline: CFDictionarySetValue(element, CFSTR(kPredOptNameNoNewline), kCFBooleanTrue); last_optind = optind; // save optind for a subsequent mismatch break; case kOptNulTerminate: CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), kCFBooleanTrue); last_optind = optind; break; case 0: /* Fall through. */ default: result = handleNonOption(opt_char, argv, context, error); goto finish; break; } } finish: if (index) { *index = optind; // we set rather than adding cause of getopt } return result; } /******************************************************************************* * *******************************************************************************/ #define QUIBBLE_PRED "%s looks like a predicate, " \ "but is being used as a property flag argument." #define QUIBBLE_OPT "%s looks like an (illegal) option, " \ "but is being used as a property flag argument." #define PICKY_PRED "%s is a predicate keyword (use -- to end options " \ "before arguments starting with a hyphen)." #define PICKY_OPT "Invalid option %s for %s (use -- to end options " \ "before arguments starting with a hyphen)." Boolean handleNonOption(int opt_char, char * const argv[], QueryContext * context, QEQueryError * error) { Boolean result = false; /* getopt_long's lookahead is SO NOT THE RIGHT BEHAVIOR. * Nor is its handling of empty arguments. */ if ( (argv[last_optind][0] != '-') || !argv[last_optind][0]) { optind = last_optind; result = true; goto finish; } if (opt_char) { if (context->assertiveness == kKextfindQuibbling) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, QUIBBLE_OPT, argv[last_optind]); optind = last_optind; } else if (context->assertiveness == kKextfindPicky) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, PICKY_OPT, argv[last_optind], argv[0]); *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } optind = last_optind; } else { switch (longopt) { case kLongOptQueryPredicate: if (context->assertiveness == kKextfindQuibbling) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, QUIBBLE_PRED, argv[last_optind]); optind = last_optind; } else if (context->assertiveness == kKextfindPicky) { OSKextLog(/* kext */ NULL, kOSKextLogErrorLevel | kOSKextLogGeneralFlag, PICKY_PRED, argv[last_optind]); *error = kQEQueryErrorInvalidOrMissingArgument; goto finish; } optind = last_optind; break; default: // should never see this *error = kQEQueryErrorParseCallbackFailed; optind = last_optind; goto finish; break; } } result = true; finish: return result; }