#include <CoreFoundation/CoreFoundation.h>
#include <CoreFoundation/CFPriv.h> // for _CFRunLoopSetCurrent()
#include <IOKit/IOKitLib.h>
#include <IOKit/IOKitServer.h>
#include <IOKit/IOCFURLAccess.h>
#include <IOKit/IOCFUnserialize.h>
#include <IOKit/storage/RAID/AppleRAIDUserLib.h>
#include <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/mach_error.h>
#include <mach-o/arch.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <libc.h>
#include <servers/bootstrap.h>
#include <signal.h>
#include <sysexits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/resource.h>
#include <unistd.h>
#include <paths.h>
#include <IOKit/kext/KXKextManager.h>
#include <IOKit/kext/kextmanager_types.h>
#include <IOKit/kext/kernel_check.h>
#include <bootfiles.h>
#include "globals.h"
#include "logging.h"
#include "queue.h"
#include "request.h"
#include "watchvol.h"
#include "PTLock.h"
#include "bootcaches.h"
#include "utility.h"
#define kAppleSetupDonePath "/var/db/.AppleSetupDone"
#define kKXROMExtensionsFolder "/System/Library/Caches/com.apple.romextensions/"
const char * progname = "(unknown)"; Boolean use_repository_caches = true;
Boolean debug = false;
Boolean load_in_task = false;
Boolean jettison_kernel_linker = true;
int g_verbose_level = kKXKextManagerLogLevelDefault; Boolean g_first_boot = false;
Boolean g_safe_boot_mode = false;
static Boolean gStaleMkext = false;
Boolean gStaleBootNotificationNeeded = false;
char * g_kernel_file = NULL; char * g_patch_dir = NULL; char * g_symbol_dir = NULL; Boolean gOverwrite_symbols = true;
KXKextManagerRef gKextManager = NULL; CFRunLoopRef gMainRunLoop = NULL;
CFRunLoopTimerRef gStaleMkextMessageRunLoopTimer = NULL;
CFRunLoopSourceRef gKernelRequestRunLoopSource = NULL;
CFRunLoopSourceRef gClientRequestRunLoopSource = NULL;
static CFMachPortRef gKextdSignalMachPort = NULL;
static CFRunLoopSourceRef gSignalRunLoopSource = NULL;
#ifndef NO_CFUserNotification
CFRunLoopSourceRef gNotificationQueueRunLoopSource = NULL; #endif
const char * default_kernel_file = NULL;
queue_head_t g_request_queue;
PTLockRef gKernelRequestQueueLock = NULL;
PTLockRef gKernSymfileDataLock = NULL;
static CFRunLoopTimerRef sDiskArbWaiter = NULL;
static Boolean is_safe_boot(void);
static Boolean kextd_prepare_link_kernel();
static void kext_send_finished(void);
static Boolean is_netboot(void);
static Boolean kextd_set_up_server(void);
static void try_diskarb(CFRunLoopTimerRef timer, void *ctx);
void kextd_register_signals(void);
static Boolean kextd_find_rom_mkexts(void);
static void check_extensions_mkext(void);
static Boolean kextd_download_personalities(void);
static void usage(int level);
void kextd_handle_signal(int signum);
void kextd_runloop_signal(
CFMachPortRef port,
void * msg,
CFIndex size,
void * info);
void kextd_rescan(void);
void kextd_log_stale_mkext(CFRunLoopTimerRef timer, void *info);
int main (int argc, const char * argv[])
{
int exit_status = 0;
KXKextManagerError result = kKXKextManagerErrorNone;
int optchar;
CFIndex count, i, rom_repository_idx = -1;
Boolean have_rom_mkexts = false;
struct stat stat_buf;
CFMutableArrayRef repositoryDirectories = NULL;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
repositoryDirectories = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!repositoryDirectories) {
fprintf(stderr, "%s: memory allocation failure\n", progname);
exit_status = 1;
goto finish;
}
gKextloadedKextPaths = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!gKextloadedKextPaths) {
fprintf(stderr, "%s: memory allocation failure\n", progname);
exit_status = 1;
goto finish;
}
#ifndef NO_CFUserNotification
gPendedNonsecureKextPaths = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!gPendedNonsecureKextPaths) {
fprintf(stderr, "%s: memory allocation failure\n", progname);
exit_status = 1;
goto finish;
}
gNotifiedNonsecureKextPaths = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!gNotifiedNonsecureKextPaths) {
fprintf(stderr, "%s: memory allocation failure\n", progname);
exit_status = 1;
goto finish;
}
#endif
while ((optchar = getopt(argc, (char * const *)argv, "bcdfhjf:r:vx")) !=
-1) {
CFStringRef optArg = NULL;
switch (optchar) {
case 'b':
fprintf(stderr, "%s: -b is unused; ignoring", progname);
break;
case 'c':
use_repository_caches = false;
break;
case 'd':
debug = true;
break;
case 'f':
load_in_task = true;
break;
case 'h':
usage(1);
exit_status = 1;
goto finish;
break;
case 'j':
jettison_kernel_linker = false;
break;
case 'r':
if (!optarg) {
kextd_error_log("%s: no argument for -f", progname);
usage(0);
exit_status = 1;
goto finish;
}
optArg = CFStringCreateWithCString(kCFAllocatorDefault,
optarg, kCFStringEncodingUTF8);
if (!optArg) {
fprintf(stderr, "%s: memory allocation failure\n", progname);
exit_status = 1;
goto finish;
}
CFArrayAppendValue(repositoryDirectories, optArg);
CFRelease(optArg);
optArg = NULL;
break;
case 'v':
{
const char * next;
if (optind >= argc) {
g_verbose_level = kKXKextManagerLogLevelBasic;
} else {
next = argv[optind];
if ((next[0] == '1' || next[0] == '2' || next[0] == '3' ||
next[0] == '4' || next[0] == '5' || next[0] == '6') &&
next[1] == '\0') {
g_verbose_level = atoi(next);
optind++;
} else if (next[0] == '-') {
g_verbose_level = kKXKextManagerLogLevelBasic;
} else if (optind < (argc - 1)) {
fprintf(stderr,"%s: invalid argument to -v option",
progname);
usage(0);
exit_status = 1;
goto finish;
} else {
g_verbose_level = kKXKextManagerLogLevelBasic;
}
}
}
break;
case 'x':
g_safe_boot_mode = true;
use_repository_caches = false; break;
default:
usage(0);
exit_status = 1;
goto finish;
}
}
argc -= optind;
argv += optind;
if (argc != 0) {
usage(0);
exit_status = 1;
goto finish;
}
if (!debug) {
kextd_openlog("kextd"); }
if (is_bootroot_active() && !is_netboot()) {
gStaleMkext = bootedFromDifferentMkext();
if (gStaleMkext)
kextd_log("WARNING: mkext on root filesystem does not match "
"the one used for startup");
}
if (is_safe_boot()) {
kextd_log("safe boot detected");
g_safe_boot_mode = true;
use_repository_caches = false; }
if (g_safe_boot_mode) {
kextd_log("safe boot; invalidating extensions caches");
utimes("/System/Library/Extensions", NULL);
}
if (stat(kAppleSetupDonePath, &stat_buf) == -1 && errno == ENOENT) {
g_first_boot = true;
}
check_extensions_mkext();
if (gStaleMkext) {
gStaleBootNotificationNeeded = true;
}
if (jettison_kernel_linker && !gStaleMkext) {
kern_return_t kern_result;
kern_result = IOCatalogueSendData(kIOMasterPortDefault,
kIOCatalogRemoveKernelLinker, 0, 0);
if (kern_result != KERN_SUCCESS) {
kextd_error_log(
"couldn't remove linker from kernel; error %d "
"(may have been removed already)", kern_result);
}
have_rom_mkexts = kextd_find_rom_mkexts();
}
CFArrayInsertValueAtIndex(repositoryDirectories, 0,
kKXSystemExtensionsFolder);
if (have_rom_mkexts) {
rom_repository_idx = 1;
CFArrayInsertValueAtIndex(repositoryDirectories, rom_repository_idx,
CFSTR(kKXROMExtensionsFolder));
}
if (!jettison_kernel_linker) {
kext_send_finished();
goto finish;
}
gKextManager = KXKextManagerCreate(kCFAllocatorDefault);
if (!gKextManager) {
kextd_error_log("can't allocate kext manager");
exit_status = 1;
goto finish;
}
result = KXKextManagerInit(gKextManager,
false, g_safe_boot_mode);
if (result != kKXKextManagerErrorNone) {
kextd_error_log("can't initialize manager (%s)",
KXKextManagerErrorStaticCStringForError(result));
exit_status = 1;
goto finish;
}
KXKextManagerSetPerformLoadsInTask(gKextManager, load_in_task);
KXKextManagerSetPerformsStrictAuthentication(gKextManager, true);
KXKextManagerSetPerformsFullTests(gKextManager, false);
KXKextManagerSetLogLevel(gKextManager, g_verbose_level);
KXKextManagerSetLogFunction(gKextManager, kextd_log);
KXKextManagerSetErrorLogFunction(gKextManager, kextd_error_log);
KXKextManagerSetWillUpdateCatalog(gKextManager, true);
KXKextManagerDisableClearRelationships(gKextManager);
count = CFArrayGetCount(repositoryDirectories);
for (i = 0; i < count; i++) {
CFStringRef directory = (CFStringRef)CFArrayGetValueAtIndex(
repositoryDirectories, i);
CFURLRef directoryURL =
CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
directory, kCFURLPOSIXPathStyle, true);
if (!directoryURL) {
kextd_error_log("memory allocation failure");
exit_status = 1;
goto finish;
}
result = KXKextManagerAddRepositoryDirectory(gKextManager,
directoryURL, true ,
(use_repository_caches && (i != rom_repository_idx)),
NULL);
if (result != kKXKextManagerErrorNone) {
kextd_error_log("can't add repository (%s).",
KXKextManagerErrorStaticCStringForError(result));
}
CFRelease(directoryURL);
directoryURL = NULL;
}
CFRelease(repositoryDirectories);
repositoryDirectories = NULL;
KXKextManagerEnableClearRelationships(gKextManager);
if (!kextd_set_up_server()) {
exit_status = 1;
goto finish;
}
kextd_register_signals();
if (!kextd_launch_kernel_request_thread()) {
exit_status = 1;
goto finish;
}
if (!gStaleMkext) {
if (!kextd_download_personalities()) {
exit_status = 1;
goto finish;
}
}
if (!kextd_prepare_link_kernel()) {
goto finish;
}
kext_send_finished();
CFRunLoopRun();
finish:
kextd_stop_volwatch(); if (gKextManager) CFRelease(gKextManager);
if (gMainRunLoop) CFRelease(gMainRunLoop);
if (gKextloadedKextPaths) CFRelease(gKextloadedKextPaths);
#ifndef NO_CFUserNotification
if (gPendedNonsecureKextPaths) CFRelease(gPendedNonsecureKextPaths);
if (gNotifiedNonsecureKextPaths) CFRelease(gNotifiedNonsecureKextPaths);
#endif
exit(exit_status);
return exit_status;
}
#define MIB_LENGTH (2)
Boolean is_safe_boot(void)
{
Boolean result = false;
int kern_safe_boot;
size_t length;
int mib_name[MIB_LENGTH] = { CTL_KERN, KERN_SAFEBOOT };
length = sizeof(kern_safe_boot);
if (sysctl(mib_name, MIB_LENGTH, &kern_safe_boot, &length, NULL, 0) == 0) {
result = kern_safe_boot ? true : false;
} else {
kextd_error_log("sysctl failed for KERN_SAFEBOOT");
}
return result;
}
void * kextd_write_link_kernel_async(void * arg)
{
mach_timespec_t waitTime = { 40, 0 };
kern_return_t kern_result;
char * symfile_path = kKernelSymfile;
int symfile_fd = -1;
Boolean unlink_symfile = false;
Boolean free_kernel_linkedit = false;
setiopolicy_np(IOPOL_TYPE_DISK, IOPOL_SCOPE_THREAD, IOPOL_THROTTLE);
kern_result = IOKitWaitQuiet(kIOMasterPortDefault, &waitTime);
if (kern_result == kIOReturnTimeout) {
kextd_error_log("IOKitWaitQuiet() timed out waiting to write kernel symbols");
} else if (kern_result != kIOReturnSuccess) {
kextd_error_log("IOKitWaitQuiet() failed, waiting to write kernel symbols, "
"with result code %x", kern_result);
}
sleep(10);
symfile_fd = open(symfile_path, O_CREAT|O_EXCL|O_WRONLY, 0644);
if (symfile_fd == -1) {
kextd_error_log("failed to open kernel link data file (%s) - %s",
symfile_path, strerror(errno));
} else {
ssize_t total_written = 0;
ssize_t written = 0;
kextd_log("writing kernel link data to %s", symfile_path);
while (total_written < _kload_optimized_kern_sym_length) {
written = write(symfile_fd, _kload_optimized_kern_sym_data + total_written,
_kload_optimized_kern_sym_length - total_written);
if (written == -1) {
break;
}
total_written += written;
}
if (total_written == _kload_optimized_kern_sym_length) {
free_kernel_linkedit = true;
} else {
if (g_verbose_level >= kKXKextManagerLogLevelBasic) {
kextd_log("failed to write link data to %s",
symfile_path);
}
unlink_symfile = true;
}
}
if (unlink_symfile) {
unlink(symfile_path);
}
PTLockTakeLock(gKernSymfileDataLock);
vm_deallocate(mach_task_self(),
(vm_address_t)_kload_optimized_kern_sym_data, _kload_optimized_kern_sym_length);
_kload_optimized_kern_sym_data = NULL;
_kload_optimized_kern_sym_length = 0;
PTLockUnlock(gKernSymfileDataLock);
if (free_kernel_linkedit) {
mach_port_t host_port = MACH_PORT_NULL;
void * data = 0;
mach_msg_type_number_t data_size = 0;
if (!unlink_symfile && g_verbose_level >= kKXKextManagerLogLevelBasic) {
kextd_log("using %s for kernel link symbols", symfile_path);
}
host_port = mach_host_self();
if (!MACH_PORT_VALID(host_port)) {
kextd_error_log("can't get host port to communicate with kernel");
goto finish;
}
kmod_control(host_port, 0, KMOD_CNTL_FREE_LINKEDIT_DATA, &data,
&data_size);
mach_port_deallocate(mach_task_self(), host_port);
}
finish:
if (symfile_fd != -1) {
close(symfile_fd);
}
pthread_exit(NULL);
return NULL;
}
static Boolean kextd_prepare_link_kernel()
{
Boolean result = false;
mach_port_t host_port = MACH_PORT_NULL;
char * which_kernel = NULL;
struct statfs root_statfs_buf;
kern_return_t kern_result;
int compareResult;
Boolean free_kernel_linkedit = false;
host_port = mach_host_self();
if (!MACH_PORT_VALID(host_port)) {
kextd_error_log("can't get host port to communicate with kernel");
goto finish;
}
gKernSymfileDataLock = PTLockCreate();
if (!gKernSymfileDataLock) {
kextd_error_log("failed to create kernel symbol data lock");
goto finish;
}
if (statfs("/", &root_statfs_buf) != 0) {
kextd_error_log("can't stat root filesystem");
} else if (root_statfs_buf.f_flags & MNT_RDONLY) {
kextd_error_log("root filesystem is read-only; "
"skipping kernel link data generation");
} else {
if (g_verbose_level >= kKXKextManagerLogLevelBasic) {
kextd_log("requesting link data from running kernel");
}
PTLockTakeLock(gKernSymfileDataLock);
kern_result = kmod_control(host_port, 0, KMOD_CNTL_GET_KERNEL_SYMBOLS,
(kmod_args_t *)&_kload_optimized_kern_sym_data, &_kload_optimized_kern_sym_length);
PTLockUnlock(gKernSymfileDataLock);
if (kern_result == KERN_SUCCESS && _kload_optimized_kern_sym_length > 0) {
pthread_attr_t thread_attr;
pthread_t thread;
pthread_attr_init(&thread_attr);
pthread_create(&thread,
&thread_attr,
kextd_write_link_kernel_async, NULL);
pthread_detach(thread);
which_kernel = "(running kernel)";
} else if (kern_result == KERN_MEMORY_FAILURE) {
kextd_error_log("kernel freed its link data; "
"trying on-disk kernel file (%s)", kDefaultKernel);
} else {
kextd_error_log("can't get link data from running kernel");
}
}
if (g_verbose_level >= kKXKextManagerLogLevelBasic) {
kextd_log("checking %s for UUID match and kernel link symbols",
kDefaultKernel);
}
compareResult = machoUUIDsMatch(host_port, NULL ,
kDefaultKernel);
if (1 == compareResult) {
if (!which_kernel) {
which_kernel = kDefaultKernel;
}
free_kernel_linkedit = true;
} else if (0 == compareResult) {
kextd_error_log("notice - UUID of on-disk kernel (%s) "
"does not match running kernel", kDefaultKernel);
} else {
kextd_error_log("error - can't compare running/on-disk kernel UUIDs");
}
if (free_kernel_linkedit) {
void * data = 0;
mach_msg_type_number_t data_size = 0;
kmod_control(host_port, 0, KMOD_CNTL_FREE_LINKEDIT_DATA, &data,
&data_size);
}
result = true;
finish:
if (MACH_PORT_NULL != host_port) {
mach_port_deallocate(mach_task_self(), host_port);
host_port = MACH_PORT_NULL;
}
if (which_kernel && g_verbose_level >= kKXKextManagerLogLevelBasic) {
kextd_log("using %s for kernel link symbols", which_kernel);
}
return result;
}
#define TEMP_FILE "/tmp/com.apple.iokit.kextd.XXXXXX"
#define MKEXTUNPACK_COMMAND "/usr/sbin/mkextunpack " \
"-d "kKXROMExtensionsFolder" "
#define TEMP_FILE_PERMS (0644)
static kern_return_t process_mkext(const UInt8 * bytes, CFIndex length)
{
kern_return_t err;
char temp_file[1 + strlen(TEMP_FILE)];
char mkextunpack_cmd[1 + strlen(TEMP_FILE) + strlen(MKEXTUNPACK_COMMAND)];
const char * rom_ext_dir = kKXROMExtensionsFolder;
int outfd = -1;
struct stat stat_buf;
mode_t real_umask;
strcpy(temp_file, TEMP_FILE);
outfd = mkstemp(temp_file);
if (-1 == outfd) {
kextd_error_log("can't create %s - %s", temp_file,
strerror(errno));
err = kKXKextManagerErrorFileAccess;
goto finish;
}
real_umask = umask(0);
umask(real_umask);
if (-1 == fchmod(outfd, TEMP_FILE_PERMS & ~real_umask)) {
kextd_error_log("unable to chmod temp file %s: %s", temp_file,
strerror(errno));
}
if (length != write(outfd, bytes, length))
err = kKXKextManagerErrorDiskFull;
else
err = kKXKextManagerErrorNone;
if (kKXKextManagerErrorNone != err) {
kextd_error_log("couldn't write output");
goto finish;
}
close(outfd);
outfd = -1;
if (-1 == stat(rom_ext_dir, &stat_buf))
{
if (0 != mkdir(rom_ext_dir, 0755)) {
kextd_error_log("mkdir(%s) failed: %s", rom_ext_dir, strerror(errno));
err = kKXKextManagerErrorFileAccess;
goto finish;
}
}
strcpy(mkextunpack_cmd, MKEXTUNPACK_COMMAND);
strcat(mkextunpack_cmd, temp_file);
if (0 != system(mkextunpack_cmd)) {
kextd_error_log(mkextunpack_cmd);
kextd_error_log("failed");
err = kKXKextManagerErrorChildTask;
goto finish;
}
finish:
if (-1 != outfd) {
close(outfd);
}
unlink(temp_file);
return err;
}
static Boolean kextd_find_rom_mkexts(void)
{
kern_return_t kr;
CFSetRef set = NULL;
CFDataRef * mkexts = NULL;
CFIndex count, idx;
char * propertiesBuffer;
uint32_t loaded_bytecount;
enum { _kIOCatalogGetROMMkextList = 4 };
kr = IOCatalogueGetData(MACH_PORT_NULL, _kIOCatalogGetROMMkextList,
&propertiesBuffer, &loaded_bytecount);
if (kIOReturnSuccess == kr)
{
set = (CFSetRef)
IOCFUnserialize(propertiesBuffer, kCFAllocatorDefault, 0, 0);
vm_deallocate(mach_task_self(), (vm_address_t) propertiesBuffer, loaded_bytecount);
}
if (!set)
return false;
count = CFSetGetCount(set);
if (count)
{
mkexts = (CFDataRef *) calloc(count, sizeof(CFDataRef));
CFSetGetValues(set, (const void **) mkexts);
for (idx = 0; idx < count; idx++)
{
process_mkext(CFDataGetBytePtr(mkexts[idx]), CFDataGetLength(mkexts[idx]));
}
free(mkexts);
}
CFRelease(set);
return (count > 0);
}
static void check_extensions_mkext(void)
{
struct stat extensions_stat_buf;
struct stat mkext_stat_buf;
Boolean outOfDate;
outOfDate = (0 != stat(kDefaultMkext, &mkext_stat_buf));
if (!outOfDate && (0 == stat(kSystemExtensionsDir, &extensions_stat_buf)))
outOfDate = (mkext_stat_buf.st_mtime != (extensions_stat_buf.st_mtime + 1));
if (outOfDate) {
do {
const NXArchInfo * arch = NXGetLocalArchInfo();
if (is_bootroot_active()) {
kextd_error_log("WARNING: mkext unexpectedly out of date w/rt Extensions folder");
gStaleMkext = true;
break; }
if (arch) {
arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE);
}
if (!arch) {
kextd_error_log("unknown architecture");
break;
}
#ifdef NO_4243070
fork_result = fork_program(kextcache_cmd, kextcache_argv,
g_first_boot ? KEXTCACHE_DELAY_FIRST_BOOT : KEXTCACHE_DELAY_STD ,
false );
if (fork_result < 0) {
kextd_error_log("couldn't fork/exec kextcache to update mkext");
}
#endif // NO_4243070
} while (false);
}
return;
}
static Boolean is_netboot(void)
{
Boolean result = false;
int netboot_mib_name[] = { CTL_KERN, KERN_NETBOOT };
int netboot = 0;
size_t netboot_len = sizeof(netboot);
if (sysctl(netboot_mib_name, sizeof(netboot_mib_name) / sizeof(int),
&netboot, &netboot_len, NULL, 0) != 0) {
kextd_error_log("sysctl for netboot failed");
goto finish;
}
result = netboot ? true : false;
finish:
return result;
}
static void kext_send_finished(void)
{
kern_return_t kern_result;
kern_result = IOCatalogueSendData(kIOMasterPortDefault,
kIOCatalogKextdFinishedLaunching, 0, 0);
if (kern_result != KERN_SUCCESS) {
kextd_error_log(
"couldn't notify kernel of kextd launch; error %d", kern_result);
}
return;
}
static void try_diskarb(CFRunLoopTimerRef timer, void *spctx)
{
static int retries = 0;
CFIndex priority = (CFIndex)(intptr_t)spctx;
int result = -1;
result = kextd_watch_volumes(priority);
if (result == 0 || ++retries >= kKXDiskArbMaxRetries) {
CFRunLoopTimerInvalidate(sDiskArbWaiter); sDiskArbWaiter = NULL;
}
if (result) {
if (retries > 1) {
if (retries < kKXDiskArbMaxRetries) {
kextd_log("diskarb isn't ready yet; we'll try again soon");
} else {
kextd_error_log("giving up on diskarb; auto-rebuild disabled");
(void)kextd_giveup_volwatch(); }
}
}
}
extern void kextd_mach_port_callback(
CFMachPortRef port,
void *msg,
CFIndex size,
void *info);
static Boolean kextd_set_up_server(void)
{
Boolean result = true;
kern_return_t kern_result = KERN_SUCCESS;
CFRunLoopSourceContext sourceContext;
unsigned int sourcePriority = 1;
CFMachPortRef kextdMachPort = NULL; mach_port_limits_t limits; mach_port_t servicePort;
CFRunLoopTimerContext spctx = { 0, }; CFAbsoluteTime diskArbDelay;
kern_result = bootstrap_check_in(bootstrap_port,
KEXTD_SERVER_NAME, &servicePort);
if (kern_result != BOOTSTRAP_SUCCESS) {
kextd_error_log("bootstrap_check_in(): %s", bootstrap_strerror(kern_result));
exit(EX_OSERR);
}
gMainRunLoop = CFRunLoopGetCurrent();
if (!gMainRunLoop) {
kextd_error_log("couldn't create run loop");
result = false;
goto finish;
}
bzero(&sourceContext, sizeof(CFRunLoopSourceContext));
sourceContext.version = 0;
sourceContext.perform = kextd_handle_kernel_request;
gKernelRequestRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault,
sourcePriority++, &sourceContext);
if (!gKernelRequestRunLoopSource) {
kextd_error_log("couldn't create kernel request run loop source");
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gKernelRequestRunLoopSource,
kCFRunLoopDefaultMode);
kextdMachPort = CFMachPortCreateWithPort(kCFAllocatorDefault,
servicePort, kextd_mach_port_callback, NULL, NULL);
gClientRequestRunLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, kextdMachPort, sourcePriority++);
if (!gClientRequestRunLoopSource) {
kextd_error_log("couldn't create client request run loop source");
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gClientRequestRunLoopSource,
kCFRunLoopDefaultMode);
diskArbDelay =g_first_boot ? KEXTCACHE_DELAY_FIRST_BOOT:KEXTCACHE_DELAY_STD;
spctx.info = (void*)(intptr_t)sourcePriority++;
sDiskArbWaiter = CFRunLoopTimerCreate(nil, CFAbsoluteTimeGetCurrent() +
diskArbDelay,diskArbDelay,0,sourcePriority++,try_diskarb,&spctx);
if (!sDiskArbWaiter) {
result = false;
goto finish;
}
CFRunLoopAddTimer(gMainRunLoop, sDiskArbWaiter, kCFRunLoopDefaultMode);
CFRelease(sDiskArbWaiter);
gKextdSignalMachPort = CFMachPortCreate(kCFAllocatorDefault,
kextd_runloop_signal, NULL, NULL);
limits.mpl_qlimit = 1;
kern_result = mach_port_set_attributes(mach_task_self(),
CFMachPortGetPort(gKextdSignalMachPort),
MACH_PORT_LIMITS_INFO,
(mach_port_info_t)&limits,
MACH_PORT_LIMITS_INFO_COUNT);
if (kern_result != KERN_SUCCESS) {
kextd_error_log("failed to set signal-handling port limits");
}
gSignalRunLoopSource = CFMachPortCreateRunLoopSource(
kCFAllocatorDefault, gKextdSignalMachPort, sourcePriority++);
if (!gSignalRunLoopSource) {
kextd_error_log("couldn't create signal-handling run loop source");
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gSignalRunLoopSource,
kCFRunLoopDefaultMode);
#ifndef NO_CFUserNotification
sourceContext.perform = kextd_check_notification_queue;
gNotificationQueueRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault,
sourcePriority++, &sourceContext);
if (!gNotificationQueueRunLoopSource) {
kextd_error_log("couldn't create alert run loop source");
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gNotificationQueueRunLoopSource,
kCFRunLoopDefaultMode);
#endif
if (gStaleMkext) {
gStaleMkextMessageRunLoopTimer=CFRunLoopTimerCreate(kCFAllocatorDefault,
0, 30.0 , 0, 0, &kextd_log_stale_mkext, NULL);
if (!gStaleMkextMessageRunLoopTimer) {
kextd_error_log("couldn't create kernel stale message run loop timer");
result = false;
goto finish;
}
CFRunLoopAddTimer(gMainRunLoop, gStaleMkextMessageRunLoopTimer,
kCFRunLoopDefaultMode);
}
CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(),
NULL, updateRAIDSet,
CFSTR(kAppleRAIDNotificationSetChanged),
NULL, CFNotificationSuspensionBehaviorHold);
kern_result = AppleRAIDEnableNotifications();
if (kern_result != KERN_SUCCESS) {
kextd_error_log("couldn't register for RAID notifications");
}
finish:
if (gStaleMkextMessageRunLoopTimer) CFRelease(gStaleMkextMessageRunLoopTimer);
if (gKernelRequestRunLoopSource) CFRelease(gKernelRequestRunLoopSource);
if (gClientRequestRunLoopSource) CFRelease(gClientRequestRunLoopSource);
if (gKextdSignalMachPort) CFRelease(gKextdSignalMachPort);
if (gSignalRunLoopSource) CFRelease(gSignalRunLoopSource);
#ifndef NO_CFUserNotification
if (gNotificationQueueRunLoopSource) CFRelease(gNotificationQueueRunLoopSource);
#endif
if (kextdMachPort) CFRelease(kextdMachPort);
return result;
}
void kextd_register_signals(void)
{
signal(SIGHUP, kextd_handle_signal);
signal(SIGTERM, kextd_handle_signal);
return;
}
typedef struct {
mach_msg_header_t header;
int signum;
} kextd_mach_msg_signal_t;
void kextd_handle_signal(int signum)
{
kextd_mach_msg_signal_t msg;
mach_msg_option_t options;
kern_return_t kern_result;
if (signum != SIGHUP && signum != SIGTERM) {
return;
}
msg.signum = signum;
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
msg.header.msgh_size = sizeof(msg.header);
msg.header.msgh_remote_port = CFMachPortGetPort(gKextdSignalMachPort);
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = 0;
options = MACH_SEND_TIMEOUT;
kern_result = mach_msg(&msg.header,
MACH_SEND_MSG | options,
sizeof(msg),
0,
MACH_PORT_NULL,
0,
MACH_PORT_NULL);
return;
}
void kextd_clear_all_notifications(void);
void kextd_rescan(void)
{
#ifndef NO_CFUserNotification
kextd_clear_all_notifications();
#endif
KXKextManagerResetAllRepositories(gKextManager);
kextd_download_personalities();
}
void kextd_runloop_signal(CFMachPortRef port, void * msg, CFIndex size, void * info)
{
kextd_mach_msg_signal_t * signal_msg = (kextd_mach_msg_signal_t *)msg;
int signum = signal_msg->signum;
if (signum == SIGHUP) {
kextd_rescan();
} else if (signum == SIGTERM) {
CFRunLoopStop(CFRunLoopGetCurrent());
}
return;
}
#ifndef NO_CFUserNotification
void kextd_clear_all_notifications(void)
{
CFArrayRemoveAllValues(gPendedNonsecureKextPaths);
if (gCurrentNotification) {
CFUserNotificationCancel(gCurrentNotification);
CFRelease(gCurrentNotification);
gCurrentNotification = NULL;
}
if (gCurrentNotificationRunLoopSource) {
CFRunLoopRemoveSource(gMainRunLoop, gCurrentNotificationRunLoopSource,
kCFRunLoopDefaultMode);
CFRelease(gCurrentNotificationRunLoopSource);
gCurrentNotificationRunLoopSource = NULL;
}
CFDictionaryRemoveAllValues(gNotifiedNonsecureKextPaths);
return;
}
#else
#define kextd_clear_all_notifications() do { } while(0)
#endif
void kextd_log_stale_mkext(
CFRunLoopTimerRef timer,
void *info
)
{
kextd_error_log("in-memory mkext doesn't match on-disk mkext; "
"non-boot kexts won't be loaded");
return;
}
bool is_bootroot_active(void)
{
int result = false;
io_service_t chosen = 0; CFTypeRef bootrootProp = 0;
chosen = IORegistryEntryFromPath(kIOMasterPortDefault,
"IODeviceTree:/chosen");
if (!chosen) {
goto finish;
}
bootrootProp = IORegistryEntryCreateCFProperty(
chosen, CFSTR(kBootRootActiveKey), kCFAllocatorDefault,
0 );
if (bootrootProp) {
result = true;
}
finish:
if (chosen) IOObjectRelease(chosen);
if (bootrootProp) CFRelease(bootrootProp);
return result;
}
static Boolean kextd_download_personalities(void)
{
KXKextManagerError result;
result = KXKextManagerSendAllKextPersonalitiesToCatalog(gKextManager);
return (kKXKextManagerErrorNone == result) ? true : false;
}
static void usage(int level)
{
fprintf(stderr,
"usage: %s [-c] [-d] [-f] [-h] [-j] [-r dir] ... [-v [1-6]] [-x]\n",
progname);
if (level > 0) {
fprintf(stderr, " -c don't use repository caches; scan repository folders\n");
fprintf(stderr, " -d run in debug mode (don't fork daemon)\n");
fprintf(stderr, " -f don't fork when loading (for debugging only)\n");
fprintf(stderr, " -h help; print this list\n");
fprintf(stderr, " -j don't jettison kernel linker; "
"just load NDRVs and exit (for install CD)\n");
fprintf(stderr, " -r <dir> use <dir> in addition to "
"/System/Library/Extensions\n");
fprintf(stderr, " -v verbose mode\n");
fprintf(stderr, " -x run in safe boot mode.\n");
}
return;
}