#include <TargetConditionals.h>
#if !TARGET_OS_EMBEDDED
#include <bless.h>
#include "bootcaches.h"
#endif // !TARGET_OS_EMBEDDED
#include <libc.h>
#include <sysexits.h>
#include <asl.h>
#include <syslog.h>
#include <sys/resource.h>
#include <IOKit/kext/OSKext.h>
#include <IOKit/kext/OSKextPrivate.h>
#include "kext_tools_util.h"
#if PRAGMA_MARK
#pragma mark Basic Utility
#endif
char * createUTF8CStringForCFString(CFStringRef aString)
{
char * result = NULL;
CFIndex bufferLength = 0;
if (!aString) {
goto finish;
}
bufferLength = sizeof('\0') +
CFStringGetMaximumSizeForEncoding(CFStringGetLength(aString),
kCFStringEncodingUTF8);
result = (char *)malloc(bufferLength * sizeof(char));
if (!result) {
goto finish;
}
if (!CFStringGetCString(aString, result, bufferLength,
kCFStringEncodingUTF8)) {
SAFE_FREE_NULL(result);
goto finish;
}
finish:
return result;
}
Boolean createCFMutableArray(CFMutableArrayRef * array,
const CFArrayCallBacks * callbacks)
{
Boolean result = true;
*array = CFArrayCreateMutable(kCFAllocatorDefault, 0,
callbacks);
if (!*array) {
result = false;
}
return result;
}
Boolean createCFMutableDictionary(CFMutableDictionaryRef * dict)
{
Boolean result = true;
*dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!*dict) {
result = false;
}
return result;
}
Boolean createCFMutableSet(CFMutableSetRef * setOut,
const CFSetCallBacks * callbacks)
{
Boolean result = true;
*setOut = CFSetCreateMutable(kCFAllocatorDefault, 0,
callbacks);
if (!*setOut) {
result = false;
}
return result;
}
void addToArrayIfAbsent(CFMutableArrayRef array, const void * value)
{
if (kCFNotFound == CFArrayGetFirstIndexOfValue(array, RANGE_ALL(array),
value)) {
CFArrayAppendValue(array, value);
}
return;
}
Boolean createCFDataFromFile(CFDataRef *dataRefOut,
const char *filePath)
{
int fd = -1;
Boolean result = false;
struct stat statBuf;
void *buffer;
CFIndex length;
*dataRefOut = NULL;
fd = open(filePath, O_RDONLY, 0);
if (fd < 0) {
goto finish;
}
if (fstat(fd, &statBuf) != 0) {
goto finish;
}
if ((statBuf.st_mode & S_IFMT) != S_IFREG) {
goto finish;
}
if (statBuf.st_size == 0) {
goto finish;
}
length = (CFIndex) statBuf.st_size;
buffer = CFAllocatorAllocate(kCFAllocatorDefault, length, 0);
if (read(fd, buffer, length) < 0) {
goto finish;
}
*dataRefOut = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault,
(const UInt8 *)buffer,
length,
kCFAllocatorDefault);
if (*dataRefOut == NULL) {
CFAllocatorDeallocate(kCFAllocatorDefault, buffer);
goto finish;
}
result = true;
finish:
if (fd != -1) {
close(fd);
}
if (result == false) {
OSKextLog( NULL,
kOSKextLogErrorLevel,
"%s: failed for '%s'", __func__, filePath);
}
return result;
}
ExitStatus writeToFile(
int fileDescriptor,
const UInt8 * data,
CFIndex length)
{
ExitStatus result = EX_OSERR;
ssize_t bytesWritten = 0;
ssize_t totalBytesWritten = 0;
while (totalBytesWritten < length) {
bytesWritten = write(fileDescriptor, data + totalBytesWritten,
length - totalBytesWritten);
if (bytesWritten < 0) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Write failed - %s", strerror(errno));
goto finish;
}
totalBytesWritten += bytesWritten;
}
result = EX_OK;
finish:
return result;
}
#if !TARGET_OS_EMBEDDED
void postNoteAboutKexts( CFStringRef theNotificationCenterName,
CFMutableDictionaryRef theDict )
{
CFNotificationCenterRef myCenter = NULL;
if (theDict == NULL || theNotificationCenterName == NULL)
return;
myCenter = CFNotificationCenterGetDistributedCenter();
CFRetain(theDict);
CFNotificationCenterPostNotificationWithOptions(
myCenter,
theNotificationCenterName,
NULL,
theDict,
kCFNotificationDeliverImmediately | kCFNotificationPostToAllSessions );
SAFE_RELEASE(theDict);
return;
}
void postNoteAboutKextLoadsMT(CFStringRef theNotificationCenterName,
CFMutableArrayRef theKextPathArray)
{
CFMutableDictionaryRef myInfoDict = NULL; CFNotificationCenterRef myCenter = NULL;
if (theKextPathArray == NULL || theNotificationCenterName == NULL)
return;
myCenter = CFNotificationCenterGetDistributedCenter();
myInfoDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (myInfoDict && myCenter) {
CFDictionaryAddValue(myInfoDict,
CFSTR("KextArrayKey"),
theKextPathArray);
CFNotificationCenterPostNotificationWithOptions(
myCenter,
theNotificationCenterName,
NULL,
myInfoDict,
kCFNotificationDeliverImmediately |
kCFNotificationPostToAllSessions );
}
SAFE_RELEASE(myInfoDict);
return;
}
void addKextToAlertDict( CFMutableDictionaryRef *theDictPtr, OSKextRef theKext )
{
CFStringRef myBundleID; CFStringRef myBundleVersion; CFMutableArrayRef myKextArray; CFURLRef myKextURL = NULL; CFStringRef myKextPath = NULL; CFMutableDictionaryRef myKextInfoDict = NULL; CFMutableDictionaryRef myAlertInfoDict = NULL; CFIndex myCount, i;
if ( theDictPtr == NULL || theKext == NULL ) {
return;
}
myAlertInfoDict = *theDictPtr;
if (myAlertInfoDict == NULL) {
myAlertInfoDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks );
if (myAlertInfoDict == NULL) {
return;
}
*theDictPtr = myAlertInfoDict;
}
myBundleID = OSKextGetIdentifier(theKext);
if ( myBundleID == NULL ) {
goto finish;
}
if ( CFStringHasPrefix(myBundleID, __kOSKextApplePrefix) == true) {
goto finish;
}
myBundleVersion = OSKextGetValueForInfoDictionaryKey(theKext,
kCFBundleVersionKey);
if (myBundleVersion == NULL) {
goto finish;
}
myKextURL = CFURLCopyAbsoluteURL(OSKextGetURL(theKext));
if (myKextURL == NULL) {
goto finish;
}
myKextPath = CFURLCopyFileSystemPath(myKextURL, kCFURLPOSIXPathStyle);
if (myKextPath == NULL) {
goto finish;
}
myKextArray = (CFMutableArrayRef)
CFDictionaryGetValue(myAlertInfoDict, CFSTR("KextInfoArrayKey"));
if (myKextArray == NULL) {
myKextArray = CFArrayCreateMutable(kCFAllocatorDefault,
0,
&kCFTypeArrayCallBacks);
if (myKextArray == NULL) {
goto finish;
}
CFDictionarySetValue(myAlertInfoDict,
CFSTR("KextInfoArrayKey"),
myKextArray);
}
myCount = CFArrayGetCount(myKextArray);
if (myCount > 0) {
for (i = 0; i < myCount; i++) {
CFMutableDictionaryRef myDict;
myDict = (CFMutableDictionaryRef)
CFArrayGetValueAtIndex(myKextArray, i);
if (myDict == NULL) continue;
if ( !CFDictionaryContainsValue(myDict, myBundleID) ) {
continue;
}
if ( !CFDictionaryContainsValue(myDict,
myBundleVersion) ) {
continue;
}
goto finish;
}
}
myKextInfoDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFCopyStringDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (myKextInfoDict == NULL) {
goto finish;
}
CFDictionaryAddValue(myKextInfoDict,
kCFBundleIdentifierKey,
myBundleID);
CFDictionaryAddValue(myKextInfoDict,
kCFBundleVersionKey,
myBundleVersion);
CFDictionaryAddValue(myKextInfoDict,
CFSTR("KextPathKey"),
myKextPath);
CFArrayAppendValue(myKextArray,
myKextInfoDict);
finish:
SAFE_RELEASE(myKextURL);
SAFE_RELEASE(myKextPath);
SAFE_RELEASE(myKextInfoDict);
return;
}
Boolean isDebugSetInBootargs(void)
{
static int didOnce = 0;
static Boolean result = false;
io_registry_entry_t optionsNode = MACH_PORT_NULL; CFStringRef bootargsEntry = NULL;
if (didOnce) {
return(result);
}
optionsNode = IORegistryEntryFromPath(kIOMasterPortDefault,
"IODeviceTree:/options");
if (optionsNode) {
bootargsEntry = (CFStringRef)
IORegistryEntryCreateCFProperty(optionsNode,
CFSTR("boot-args"),
kCFAllocatorDefault, 0);
if (bootargsEntry &&
(CFGetTypeID(bootargsEntry) == CFStringGetTypeID())) {
CFRange findRange;
findRange = CFStringFind(bootargsEntry, CFSTR("debug"), 0);
if (findRange.length != 0) {
result = true;
}
}
}
didOnce++;
if (optionsNode) IOObjectRelease(optionsNode);
SAFE_RELEASE(bootargsEntry);
return(result);
}
#endif // !TARGET_OS_EMBEDDED
#if PRAGMA_MARK
#pragma mark Path & File
#endif
ExitStatus checkPath(
const char * path,
const char * suffix, Boolean directoryRequired,
Boolean writableRequired)
{
Boolean result = EX_USAGE;
Boolean nameBad = FALSE;
struct stat statBuffer;
if (!path) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Internal error - %s - NULL path.",
__FUNCTION__);
result = EX_SOFTWARE;
goto finish;
}
result = EX_USAGE;
if (suffix) {
size_t pathLength = strlen(path);
size_t suffixLength = strlen(suffix);
size_t suffixIndex = 0;
size_t periodIndex = 0;
nameBad = TRUE;
if (!pathLength || !suffixLength) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Internal error - %s - empty string.",
__FUNCTION__);
result = EX_SOFTWARE;
goto finish;
}
while (pathLength-- && path[pathLength] == '/') {
if (!pathLength) {
goto finish;
}
}
pathLength++;
if (suffixLength >= pathLength) {
goto finish;
}
suffixIndex = pathLength - suffixLength;
periodIndex = suffixIndex - 1;
if (path[periodIndex] != '.' ||
strncmp(path + suffixIndex, suffix, suffixLength)) {
goto finish;
}
nameBad = FALSE;
}
result = EX_NOINPUT;
if (0 != stat(path, &statBuffer)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"Can't stat %s - %s.", path,
strerror(errno));
goto finish;
}
if (directoryRequired && ((statBuffer.st_mode & S_IFMT) != S_IFDIR) ) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s is not a directory.",
path);
goto finish;
}
result = EX_NOPERM;
if (writableRequired && access(path, W_OK) == -1) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"%s is not writable.", path);
goto finish;
}
result = EX_OK;
finish:
if (nameBad) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"%s not of type '%s'.", path, suffix);
}
return result;
}
void
saveFile(const void * vKey, const void * vValue, void * vContext)
{
CFStringRef key = (CFStringRef)vKey;
CFDataRef fileData = (CFDataRef)vValue;
SaveFileContext * context = (SaveFileContext *)vContext;
long length;
int fd = -1;
mode_t mode = 0666;
struct stat statBuf;
CFURLRef saveURL = NULL; Boolean fileExists = false;
char savePath[PATH_MAX];
if (context->fatal) {
goto finish;
}
saveURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
context->saveDirURL, key, false);
if (!saveURL) {
context->fatal = true;
goto finish;
}
if (!CFURLGetFileSystemRepresentation(saveURL, true,
(u_char *)savePath, sizeof(savePath))) {
context->fatal = true;
goto finish;
}
if (!context->overwrite) {
fileExists = CFURLResourceIsReachable(saveURL, NULL);
if (fileExists) {
switch (user_approve( TRUE, REPLY_YES,
"%s exists, overwrite", savePath)) {
case REPLY_YES:
break;
case REPLY_ALL:
fprintf(stderr,
"Overwriting all symbol files for kexts in dependency graph.\n");
context->overwrite = TRUE;
break;
case REPLY_NO:
goto finish;
break;
default:
context->fatal = true;
goto finish;
break;
}
}
else {
OSKextLog( NULL, kOSKextLogErrorLevel,
"%s missing '%s'", __func__, savePath);
context->fatal = true;
goto finish;
}
}
length = CFDataGetLength(fileData);
if (0 == stat(savePath, &statBuf)) {
mode = statBuf.st_mode;
}
fd = open(savePath, O_WRONLY|O_CREAT|O_TRUNC, mode);
if (fd != -1 && length) {
ExitStatus result;
result = writeToFile(fd, CFDataGetBytePtr(fileData), length);
if (result != EX_OK) {
OSKextLog( NULL, kOSKextLogErrorLevel,
"%s write failed for '%s'", __func__, savePath);
}
}
else {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogFileAccessFlag,
"%s Failed to save '%s'", __func__, savePath);
}
finish:
if (fd != -1) {
fsync(fd);
close(fd);
}
SAFE_RELEASE(saveURL);
return;
}
CFStringRef copyKextPath(OSKextRef aKext)
{
CFStringRef result = NULL;
CFURLRef absURL = NULL;
if (!OSKextGetURL(aKext)) {
goto finish;
}
absURL = CFURLCopyAbsoluteURL(OSKextGetURL(aKext));
if (!absURL) {
goto finish;
}
result = CFURLCopyFileSystemPath(absURL, kCFURLPOSIXPathStyle);
finish:
SAFE_RELEASE(absURL);
return result;
}
ExitStatus
getLatestTimesFromCFURLArray(
CFArrayRef dirURLArray,
struct timeval dirTimeVals[2])
{
ExitStatus result = EX_SOFTWARE;
int i;
CFURLRef myURL;
struct stat myStatBuf;
struct timeval myTempModTime;
struct timeval myTempAccessTime;
if (dirURLArray == NULL) {
goto finish;
}
bzero(dirTimeVals, (sizeof(struct timeval) * 2));
for (i = 0; i < CFArrayGetCount(dirURLArray); i++) {
myURL = (CFURLRef) CFArrayGetValueAtIndex(dirURLArray, i);
if (myURL == NULL) {
OSKextLog(NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s: NO fileURL at index %d!!!! ", __FUNCTION__, i);
goto finish;
}
result = statURL(myURL, &myStatBuf);
if (result != EX_OK) {
goto finish;
}
TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec);
if (timercmp(&myTempModTime, &dirTimeVals[1], >)) {
dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec;
dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec;
dirTimeVals[1].tv_sec = myTempModTime.tv_sec;
dirTimeVals[1].tv_usec = myTempModTime.tv_usec;
}
}
result = EX_OK;
finish:
return result;
}
ExitStatus
getLatestTimesFromDirURL(
CFURLRef dirURL,
struct timeval dirTimeVals[2])
{
ExitStatus result = EX_SOFTWARE;
CFURLEnumeratorRef myEnumerator = NULL; struct stat myStatBuf;
struct timeval myTempModTime;
struct timeval myTempAccessTime;
bzero(dirTimeVals, (sizeof(struct timeval) * 2));
if (dirURL == NULL) {
goto finish;
}
myEnumerator = CFURLEnumeratorCreateForDirectoryURL(
NULL,
dirURL,
kCFURLEnumeratorDefaultBehavior,
NULL );
if (myEnumerator == NULL) {
OSKextLogMemError();
goto finish;
}
CFURLRef myURL = NULL;
while (CFURLEnumeratorGetNextURL(
myEnumerator,
&myURL,
NULL) == kCFURLEnumeratorSuccess) {
if (statURL(myURL, &myStatBuf) != EX_OK) {
goto finish;
}
TIMESPEC_TO_TIMEVAL(&myTempAccessTime, &myStatBuf.st_atimespec);
TIMESPEC_TO_TIMEVAL(&myTempModTime, &myStatBuf.st_mtimespec);
if (timercmp(&myTempModTime, &dirTimeVals[1], >)) {
dirTimeVals[0].tv_sec = myTempAccessTime.tv_sec;
dirTimeVals[0].tv_usec = myTempAccessTime.tv_usec;
dirTimeVals[1].tv_sec = myTempModTime.tv_sec;
dirTimeVals[1].tv_usec = myTempModTime.tv_usec;
}
}
result = EX_OK;
finish:
if (myEnumerator) CFRelease(myEnumerator);
return result;
}
ExitStatus
getLatestTimesFromDirPath(
const char * dirPath,
struct timeval dirTimeVals[2])
{
ExitStatus result = EX_SOFTWARE;
CFURLRef kernURL = NULL;
if (dirPath == NULL) {
goto finish;
}
kernURL = CFURLCreateFromFileSystemRepresentation(
NULL,
(const UInt8 *)dirPath,
strlen(dirPath),
true );
if (kernURL == NULL) {
OSKextLogMemError();
goto finish;
}
result = getLatestTimesFromDirURL(kernURL, dirTimeVals);
finish:
if (kernURL) CFRelease(kernURL);
return result;
}
ExitStatus
getParentPathTimes(
const char * thePath,
struct timeval cacheFileTimes[2] )
{
ExitStatus result = EX_SOFTWARE;
char * lastSlash = NULL;
char myTempPath[PATH_MAX];
if (thePath == NULL) {
goto finish;
}
lastSlash = strrchr(thePath, '/');
if (lastSlash == NULL || (lastSlash - thePath) < 2) {
goto finish;
}
if (strlcpy(myTempPath,
thePath,
(lastSlash - thePath) + 1) >= PATH_MAX) {
goto finish;
}
result = getFilePathTimes(myTempPath, cacheFileTimes);
finish:
return result;
}
ExitStatus
getFilePathTimes(
const char * filePath,
struct timeval cacheFileTimes[2])
{
struct stat statBuffer;
ExitStatus result = EX_SOFTWARE;
result = statPath(filePath, &statBuffer);
if (result != EX_OK) {
goto finish;
}
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[0], &statBuffer.st_atimespec);
TIMESPEC_TO_TIMEVAL(&cacheFileTimes[1], &statBuffer.st_mtimespec);
result = EX_OK;
finish:
return result;
}
ExitStatus
statURL(CFURLRef anURL, struct stat * statBuffer)
{
ExitStatus result = EX_OSERR;
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(anURL, true,
(UInt8 *)path, sizeof(path)))
{
OSKextLogStringError( NULL);
goto finish;
}
result = statPath(path, statBuffer);
if (!result) {
goto finish;
}
result = EX_OK;
finish:
return result;
}
ExitStatus
statPath(const char *path, struct stat *statBuffer)
{
ExitStatus result = EX_OSERR;
if (stat(path, statBuffer)) {
OSKextLog( NULL,
kOSKextLogDebugLevel | kOSKextLogGeneralFlag,
"Can't stat %s - %s.", path, strerror(errno));
goto finish;
}
result = EX_OK;
finish:
return result;
}
ExitStatus
statParentPath(const char *thePath, struct stat *statBuffer)
{
ExitStatus result = EX_SOFTWARE;
char * lastSlash = NULL;
char myTempPath[PATH_MAX];
if (thePath == NULL) {
goto finish;
}
lastSlash = strrchr(thePath, '/');
if (lastSlash == NULL || (lastSlash - thePath) < 2) {
goto finish;
}
if (strlcpy(myTempPath,
thePath,
(lastSlash - thePath) + 1) >= PATH_MAX) {
goto finish;
}
result = statPath(myTempPath, statBuffer);
finish:
return result;
}
char *
getPathExtension(const char * pathPtr)
{
char * suffixPtr = NULL; CFURLRef pathURL = NULL; CFStringRef tmpCFString = NULL;
pathURL = CFURLCreateFromFileSystemRepresentation(
NULL,
(const UInt8 *)pathPtr,
strlen(pathPtr),
true );
if (pathURL == NULL) {
goto finish;
}
tmpCFString = CFURLCopyPathExtension(pathURL);
if (tmpCFString == NULL) {
goto finish;
}
suffixPtr = createUTF8CStringForCFString(tmpCFString);
finish:
SAFE_RELEASE(pathURL);
SAFE_RELEASE(tmpCFString);
return suffixPtr;
}
#if PRAGMA_MARK
#pragma mark Logging
#endif
OSKextLogSpec _sLogSpecsForVerboseLevels[] = {
kOSKextLogErrorLevel | kOSKextLogVerboseFlagsMask, kOSKextLogBasicLevel | kOSKextLogVerboseFlagsMask, kOSKextLogProgressLevel | kOSKextLogVerboseFlagsMask, kOSKextLogStepLevel | kOSKextLogVerboseFlagsMask, kOSKextLogDetailLevel | kOSKextLogVerboseFlagsMask, kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask, kOSKextLogDebugLevel | kOSKextLogVerboseFlagsMask | kOSKextLogKextOrGlobalMask
};
#define kBadVerboseOptPrefix "-v="
ExitStatus setLogFilterForOpt(
int argc,
char * const * argv,
OSKextLogSpec forceOnFlags)
{
ExitStatus result = EX_USAGE;
OSKextLogSpec logFilter = 0;
const char * localOptarg = NULL;
if (!optarg && optind >= argc) {
logFilter = _sLogSpecsForVerboseLevels[1];
} else {
if (optarg) {
localOptarg = optarg;
} else {
localOptarg = argv[optind];
}
if (!strncmp(localOptarg, kBadVerboseOptPrefix,
sizeof(kBadVerboseOptPrefix) - 1)) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"%s - syntax error (don't use = with single-letter option args).",
localOptarg);
goto finish;
}
if (localOptarg[0] == '-' && localOptarg[1] == kOptVerbose &&
localOptarg[2] == '0' && (localOptarg[3] == 'x' || localOptarg[3] == 'X')) {
localOptarg += 2;
}
if (localOptarg[0] == '0' && (localOptarg[1] == 'x' || localOptarg[1] == 'X')) {
char * endptr = NULL;
OSKextLogSpec parsedFlags = (unsigned)strtoul(localOptarg, &endptr, 16);
if (endptr[0]) {
OSKextLog( NULL,
kOSKextLogErrorLevel | kOSKextLogGeneralFlag,
"Can't parse verbose argument %s.", localOptarg);
goto finish;
}
logFilter = parsedFlags;
if (!optarg) {
optind++;
}
} else if (((localOptarg[0] >= '0') || (localOptarg[0] <= '6')) &&
(localOptarg[1] == '\0')) {
logFilter = _sLogSpecsForVerboseLevels[localOptarg[0] - '0'];
if (!optarg) {
optind++;
}
} else {
logFilter = _sLogSpecsForVerboseLevels[1];
}
}
logFilter = logFilter | forceOnFlags;
OSKextSetLogFilter(logFilter, false);
OSKextSetLogFilter(logFilter, true);
result = EX_OK;
finish:
return result;
}
void beQuiet(void)
{
fclose(stdout);
fclose(stderr);
close(1);
close(2);
OSKextSetLogFilter(kOSKextLogSilentFilter, false);
OSKextSetLogFilter(kOSKextLogSilentFilter, true);
return;
}
FILE * g_log_stream = NULL;
aslclient gASLClientHandle = NULL;
aslmsg gASLMessage = NULL;
void tool_openlog(const char * name)
{
gASLClientHandle = asl_open( name, name,
0);
gASLMessage = asl_new(ASL_TYPE_MSG);
return;
}
#if !TARGET_OS_EMBEDDED
Boolean useDevelopmentKernel(const char * theKernelPath)
{
struct stat statBuf;
char * tempPath = NULL;
size_t length = 0;
Boolean myResult = FALSE;
if (statPath(kAppleInternalPath, &statBuf) != EX_OK) {
return(myResult);
}
tempPath = malloc(PATH_MAX);
while (tempPath) {
length = strlcpy(tempPath, theKernelPath, PATH_MAX);
if (length >= PATH_MAX) break;
length = strlcat(tempPath,
kDefaultKernelSuffix,
PATH_MAX);
if (length >= PATH_MAX) break;
if (statPath(tempPath, &statBuf) == EX_OK) {
myResult = TRUE;
}
break;
}
if (tempPath) free(tempPath);
return(myResult);
}
#endif // !TARGET_OS_EMBEDDED
void tool_log(
OSKextRef aKext __unused,
OSKextLogSpec msgLogSpec,
const char * format, ...)
{
va_list ap;
if (gASLClientHandle) {
int aslLevel = ASL_LEVEL_ERR;
OSKextLogSpec kextLogLevel = msgLogSpec & kOSKextLogLevelMask;
char messageLogSpec[16];
if (kextLogLevel == kOSKextLogErrorLevel) {
aslLevel = ASL_LEVEL_ERR;
} else if (kextLogLevel == kOSKextLogWarningLevel) {
aslLevel = ASL_LEVEL_WARNING;
} else if (kextLogLevel == kOSKextLogBasicLevel) {
aslLevel = ASL_LEVEL_NOTICE;
} else if (kextLogLevel < kOSKextLogDebugLevel) {
aslLevel = ASL_LEVEL_INFO;
} else {
aslLevel = ASL_LEVEL_DEBUG;
}
snprintf(messageLogSpec, sizeof(messageLogSpec), "0x%x", msgLogSpec);
asl_set(gASLMessage, "OSKextLogSpec", messageLogSpec);
va_start(ap, format);
asl_vlog(gASLClientHandle, gASLMessage, aslLevel, format, ap);
va_end(ap);
} else {
if (!g_log_stream) {
g_log_stream = stderr;
}
va_start(ap, format);
vfprintf(g_log_stream, format, ap);
va_end(ap);
fprintf(g_log_stream, "\n");
fflush(g_log_stream);
}
return;
}
void log_CFError(
OSKextRef aKext __unused,
OSKextLogSpec msgLogSpec,
CFErrorRef error)
{
CFStringRef errorString = NULL; char * cstring = NULL;
if (!error) {
return;
}
errorString = CFErrorCopyDescription(error);
if (errorString) {
cstring = createUTF8CStringForCFString(errorString);
OSKextLog( NULL, msgLogSpec,
"CFError descripton: %s.", cstring);
SAFE_RELEASE_NULL(errorString);
SAFE_FREE_NULL(cstring);
}
errorString = CFErrorCopyFailureReason(error);
if (errorString) {
cstring = createUTF8CStringForCFString(errorString);
OSKextLog( NULL, msgLogSpec,
"CFError reason: %s.", cstring);
SAFE_RELEASE_NULL(errorString);
SAFE_FREE_NULL(cstring);
}
return;
}
#if !TARGET_OS_EMBEDDED
int32_t
BRBLLogFunc(void *refcon __unused, int32_t level, const char *string)
{
OSKextLogSpec logSpec = kOSKextLogGeneralFlag;
switch (level) {
case kBLLogLevelVerbose:
logSpec |= kOSKextLogDebugLevel;
break;
case kBLLogLevelError:
logSpec |= kOSKextLogErrorLevel;
break;
default:
logSpec |= kOSKextLogWarningLevel;
}
OSKextLog(NULL, logSpec, "%s", string);
return 0;
}
Boolean getKernelPathForURL(CFURLRef theVolRootURL,
char * theBuffer,
int theBufferSize)
{
CFDictionaryRef myDict = NULL; CFDictionaryRef postBootPathsDict = NULL; CFDictionaryRef kernelCacheDict = NULL; Boolean myResult = FALSE;
if (theBuffer) {
*theBuffer = 0x00;
myDict = copyBootCachesDictForURL(theVolRootURL);
if (myDict != NULL) {
postBootPathsDict = (CFDictionaryRef)
CFDictionaryGetValue(myDict, kBCPostBootKey);
if (postBootPathsDict &&
CFGetTypeID(postBootPathsDict) == CFDictionaryGetTypeID()) {
kernelCacheDict = (CFDictionaryRef)
CFDictionaryGetValue(postBootPathsDict, kBCKernelcacheV3Key);
}
}
}
if (kernelCacheDict &&
CFGetTypeID(kernelCacheDict) == CFDictionaryGetTypeID()) {
CFStringRef myTempStr;
myTempStr = (CFStringRef) CFDictionaryGetValue(kernelCacheDict,
kBCKernelPathKey);
if (myTempStr != NULL &&
CFGetTypeID(myTempStr) == CFStringGetTypeID()) {
if (CFStringGetFileSystemRepresentation(myTempStr,
theBuffer,
theBufferSize)) {
struct stat statBuf;
if (statPath(theBuffer, &statBuf) == EX_OK) {
myResult = TRUE;
}
}
}
}
SAFE_RELEASE(myDict);
return(myResult);
}
CFDictionaryRef copyBootCachesDictForURL(CFURLRef theVolRootURL)
{
CFStringRef myVolRoot = NULL; CFStringRef myPath = NULL; CFURLRef myURL = NULL; CFDictionaryRef myBootCachesPlist = NULL;
if (theVolRootURL) {
myVolRoot = CFURLCopyFileSystemPath(theVolRootURL,
kCFURLPOSIXPathStyle);
if (myVolRoot == NULL) {
goto finish;
}
myPath = CFStringCreateWithFormat(
kCFAllocatorDefault,
NULL,
CFSTR("%@%s"),
myVolRoot,
"/usr/standalone/bootcaches.plist" );
}
else {
myPath = CFStringCreateWithCString(
kCFAllocatorDefault,
"/usr/standalone/bootcaches.plist",
kCFStringEncodingUTF8 );
}
if (myPath == NULL) {
goto finish;
}
myURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
myPath,
kCFURLPOSIXPathStyle,
false );
if (myURL && CFURLResourceIsReachable(myURL, NULL)) {
CFReadStreamRef readStream = NULL; struct stat myStatBuf;
ExitStatus myExitStatus;
myExitStatus = statURL(myURL, &myStatBuf);
if (myExitStatus != EX_OK) {
goto finish;
}
if (myStatBuf.st_uid != 0) {
goto finish;
}
if (myStatBuf.st_mode & S_IWGRP || myStatBuf.st_mode & S_IWOTH) {
goto finish;
}
readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, myURL);
if (readStream) {
if (CFReadStreamOpen(readStream)) {
myBootCachesPlist = CFPropertyListCreateWithStream(
kCFAllocatorDefault,
readStream,
0,
kCFPropertyListMutableContainersAndLeaves,
NULL, NULL);
CFReadStreamClose(readStream);
}
SAFE_RELEASE(readStream);
}
}
finish:
SAFE_RELEASE(myURL);
SAFE_RELEASE(myPath);
SAFE_RELEASE(myVolRoot);
return(myBootCachesPlist);
}
#endif // !TARGET_OS_EMBEDDED
const char * safe_mach_error_string(mach_error_t error_code)
{
const char * result = mach_error_string(error_code);
if (!result) {
result = "(unknown)";
}
return result;
}
#if PRAGMA_MARK
#pragma mark User Input
#endif
int user_approve(Boolean ask_all, int default_answer, const char * format, ...)
{
int result = REPLY_YES;
va_list ap;
char fake_buffer[2];
int output_length;
char * output_string;
int c, x;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
result = REPLY_ERROR;
goto finish;
}
va_start(ap, format);
vsnprintf(output_string, output_length + 1, format, ap);
va_end(ap);
while ( 1 ) {
fprintf(stderr, "%s [%s/%s", output_string,
(default_answer == REPLY_YES) ? "Y" : "y",
(default_answer == REPLY_NO) ? "N" : "n");
if (ask_all) {
fprintf(stderr, "/%s",
(default_answer == REPLY_ALL) ? "A" : "a");
}
fprintf(stderr, "]? ");
fflush(stderr);
c = fgetc(stdin);
if (c == EOF) {
result = REPLY_ERROR;
goto finish;
}
if ( c != '\n' ) {
do {
x = fgetc(stdin);
} while (x != '\n' && x != EOF);
if (x == EOF) {
result = REPLY_ERROR;
goto finish;
}
}
if (c == '\n') {
result = default_answer;
goto finish;
} else if (tolower(c) == 'y') {
result = REPLY_YES;
goto finish;
} else if (tolower(c) == 'n') {
result = REPLY_NO;
goto finish;
} else if (ask_all && tolower(c) == 'a') {
result = REPLY_ALL;
goto finish;
} else {
fprintf(stderr, "Please answer 'y' or 'n'%s.\n",
ask_all ? " or 'a'" : "");
}
}
finish:
if (output_string) free(output_string);
return result;
}
const char * user_input(Boolean * eof, const char * format, ...)
{
char * result = NULL; va_list ap;
char fake_buffer[2];
int output_length;
char * output_string = NULL;
unsigned index;
size_t size = 80; int c;
if (eof) {
*eof = false;
}
result = (char *)malloc(size);
if (!result) {
goto finish;
}
index = 0;
va_start(ap, format);
output_length = vsnprintf(fake_buffer, 1, format, ap);
va_end(ap);
output_string = (char *)malloc(output_length + 1);
if (!output_string) {
if (result) free(result);
result = NULL;
goto finish;
}
va_start(ap, format);
vsnprintf(output_string, output_length + 1, format, ap);
va_end(ap);
fprintf(stderr, "%s ", output_string);
fflush(stderr);
c = fgetc(stdin);
while (c != '\n' && c != EOF) {
if (index >= (size - 1)) {
fprintf(stderr, "input line too long\n");
if (result) free(result);
result = NULL;
goto finish;
}
result[index++] = (char)c;
c = fgetc(stdin);
}
result[index] = '\0';
if (c == EOF) {
if (result) free(result);
result = NULL;
if (eof) {
*eof = true;
}
goto finish;
}
finish:
if (output_string) free(output_string);
return result;
}
#if PRAGMA_MARK
#pragma mark Caches
#endif
Boolean readSystemKextPropertyValues(
CFStringRef propertyKey,
const NXArchInfo * arch,
Boolean forceUpdateFlag,
CFArrayRef * valuesOut)
{
Boolean result = false;
CFArrayRef sysExtensionsFolderURLs = OSKextGetSystemExtensionsFolderURLs();
CFMutableArrayRef values = NULL; CFStringRef cacheBasename = NULL; CFArrayRef kexts = NULL; CFMutableDictionaryRef newDict = NULL; CFStringRef kextPath = NULL; CFTypeRef value = NULL; CFStringRef kextVersion = NULL; CFIndex count, i;
cacheBasename = CFStringCreateWithFormat(kCFAllocatorDefault,
NULL, CFSTR("%s%@"),
_kKextPropertyValuesCacheBasename,
propertyKey);
if (!cacheBasename) {
OSKextLogMemError();
goto finish;
}
if (OSKextGetUsesCaches() && !forceUpdateFlag) {
if (_OSKextReadCache(sysExtensionsFolderURLs, cacheBasename,
arch, _kOSKextCacheFormatCFXML, true,
(CFPropertyListRef *)&values)) {
if (values && CFGetTypeID(values) == CFArrayGetTypeID()) {
result = true;
goto finish;
}
}
}
values = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!values) {
OSKextLogMemError();
goto finish;
}
kexts = OSKextCreateKextsFromURLs(kCFAllocatorDefault,
sysExtensionsFolderURLs);
if (!kexts) {
goto finish;
}
count = CFArrayGetCount(kexts);
for (i = 0; i < count; i++) {
OSKextRef aKext = (OSKextRef)CFArrayGetValueAtIndex(kexts, i);
SAFE_RELEASE_NULL(newDict);
SAFE_RELEASE_NULL(kextPath);
kextVersion = NULL;
if ((OSKextGetSimulatedSafeBoot() || OSKextGetActualSafeBoot()) &&
!OSKextIsLoadableInSafeBoot(aKext)) {
continue;
}
value = OSKextGetValueForInfoDictionaryKey(aKext, propertyKey);
if (!value) {
continue;
}
newDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!newDict) {
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("Data"), value);
CFDictionarySetValue(newDict, CFSTR("CFBundleIdentifier"),
OSKextGetIdentifier(aKext));
kextPath = copyKextPath(aKext);
if (!kextPath) {
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("OSBundlePath"), kextPath);
kextVersion = OSKextGetValueForInfoDictionaryKey(aKext,
CFSTR("CFBundleVersion"));
if (!kextVersion) {
goto finish;
}
CFDictionarySetValue(newDict, CFSTR("CFBundleVersion"),
kextVersion);
CFArrayAppendValue(values, newDict);
}
if (OSKextGetUsesCaches() || forceUpdateFlag) {
_OSKextWriteCache(sysExtensionsFolderURLs, cacheBasename,
arch, _kOSKextCacheFormatCFXML, values);
}
result = true;
finish:
if (result && valuesOut && values) {
*valuesOut = (CFArrayRef)CFRetain(values);
}
SAFE_RELEASE(values);
SAFE_RELEASE(cacheBasename);
SAFE_RELEASE(kexts);
SAFE_RELEASE(newDict);
SAFE_RELEASE(kextPath);
return result;
}