#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 <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/mach_error.h>
#include <mach-o/arch.h>
#include <libc.h>
#include <servers/bootstrap.h>
#include <signal.h>
#include <sysexits.h>
#include <IOKit/kext/KXKextManager.h>
#include "globals.h"
#include "request.h"
#include "logging.h"
#include "queue.h"
#include "PTLock.h"
#include "paths.h"
static const char * KEXTD_SERVER_NAME = "com.apple.KernelExtensionServer";
#define kKXROMExtensionsFolder "/System/Library/Caches/com.apple.romextensions/"
#define kKXCSystemExtensionsFolder "/System/Library/Extensions"
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 = 0; Boolean safe_boot_mode = false;
static Boolean parent_received_sigterm = false;
static Boolean parent_received_sigchld = false;
static Boolean parent_received_sigalrm = false;
char * g_kernel_file = NULL; char * g_patch_dir = NULL; char * g_symbol_dir = NULL; Boolean gOverwrite_symbols = true;
mach_port_t g_io_master_port;
KXKextManagerRef gKextManager = NULL; CFRunLoopRef gMainRunLoop = NULL; CFRunLoopSourceRef gRescanRunLoopSource = 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 = "/mach";
queue_head_t g_request_queue;
PTLockRef gKernelRequestQueueLock = NULL;
PTLockRef gRunLoopSourceLock = NULL;
static mach_port_t gBootstrap_port = 0;
static Boolean kextd_is_running(mach_port_t * bootstrap_port_ref);
static int kextd_get_mach_ports(void);
static int kextd_fork(void);
static Boolean kextd_set_up_server(void);
static void kextd_release_parent_task(void);
void kextd_register_signals(void);
void kextd_parent_handle_signal(int signum);
void kextd_handle_sighup(int signum);
void kextd_handle_rescan(void * info);
static Boolean kextd_find_rom_mkexts(void);
static void check_extensions_mkext(void);
static Boolean kextd_download_personalities(void);
static void usage(int level);
char * CFURLCopyCString(CFURLRef anURL);
void kextd_runloop_handle_sighup(
CFMachPortRef port,
void *msg,
CFIndex size,
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;
CFMutableArrayRef repositoryDirectories = NULL;
progname = rindex(argv[0], '/');
if (progname) {
progname++; } else {
progname = (char *)argv[0];
}
if (kextd_is_running(NULL)) {
exit_status = EX_UNAVAILABLE;
goto finish;
}
repositoryDirectories = CFArrayCreateMutable(kCFAllocatorDefault, 0,
&kCFTypeArrayCallBacks);
if (!repositoryDirectories) {
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, kCFStringEncodingMacRoman);
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 = 1;
} 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 = 1;
} 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 = 1;
}
}
}
break;
case 'x':
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 (!kextd_get_mach_ports()) {
exit_status = 1;
goto finish;
}
if (!debug && jettison_kernel_linker) {
if (!kextd_fork()) {
exit_status = 1;
goto finish;
}
kextd_openlog("kextd"); }
kextd_register_signals();
if (jettison_kernel_linker) {
kern_return_t kern_result;
kern_result = IOCatalogueSendData(g_io_master_port,
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) {
goto finish;
}
check_extensions_mkext();
gKextManager = KXKextManagerCreate(kCFAllocatorDefault);
if (!gKextManager) {
kextd_error_log("can't allocate kext manager");
exit_status = 1;
goto finish;
}
result = KXKextManagerInit(gKextManager,
false, 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;
}
if (!kextd_launch_kernel_request_thread()) {
exit_status = 1;
goto finish;
}
if (!kextd_download_personalities()) {
exit_status = 1;
goto finish;
}
if (!debug) {
kextd_release_parent_task();
}
CFRunLoopRun();
finish:
if (gKextManager) CFRelease(gKextManager);
if (gMainRunLoop) CFRelease(gMainRunLoop);
#ifndef NO_CFUserNotification
if (gPendedNonsecureKextPaths) CFRelease(gPendedNonsecureKextPaths);
if (gNotifiedNonsecureKextPaths) CFRelease(gNotifiedNonsecureKextPaths);
#endif
exit(exit_status);
return exit_status;
}
#define TEMP_FILE "/tmp/com.apple.iokit.kextd.XX"
#define MKEXTUNPACK_COMMAND "/usr/sbin/mkextunpack " \
"-d "kKXROMExtensionsFolder" "
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;
strcpy(temp_file, TEMP_FILE);
mktemp(temp_file);
outfd = open(temp_file, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (-1 == outfd) {
kextd_error_log("can't create %s - %s\n", temp_file,
strerror(errno));
err = kKXKextManagerErrorFileAccess;
goto finish;
}
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\n", 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;
int 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);
}
#define REBUILDMKEXT_COMMAND "/usr/sbin/kextcache -elF -a "
static void check_extensions_mkext(void)
{
struct stat extensions_stat_buf;
struct stat mkext_stat_buf;
Boolean rebuild;
char rebuildmkext_cmd[1 + strlen(REBUILDMKEXT_COMMAND) + 64];
rebuild = (0 != stat(kKXCSystemExtensionsFolder ".mkext", &mkext_stat_buf));
if (!rebuild && (0 == stat(kKXCSystemExtensionsFolder, &extensions_stat_buf)))
rebuild = (mkext_stat_buf.st_mtime != (extensions_stat_buf.st_mtime + 1));
if (rebuild) do
{
const NXArchInfo *
arch = NXGetLocalArchInfo();
if (arch)
arch = NXGetArchInfoFromCpuType(arch->cputype, CPU_SUBTYPE_MULTIPLE);
if (!arch)
{
kextd_error_log("unknown architecture");
break;
}
strcpy(rebuildmkext_cmd, REBUILDMKEXT_COMMAND);
strcat(rebuildmkext_cmd, arch->name);
if (0 != system(rebuildmkext_cmd))
{
kextd_error_log(rebuildmkext_cmd);
kextd_error_log("failed");
break;
}
}
while (false);
}
static Boolean kextd_is_running(mach_port_t * bootstrap_port_ref)
{
boolean_t active = FALSE;
Boolean result = false;
kern_return_t kern_result = KERN_SUCCESS;
mach_port_t the_bootstrap_port;
if (bootstrap_port_ref && (*bootstrap_port_ref != PORT_NULL)) {
the_bootstrap_port = *bootstrap_port_ref;
} else {
kern_result = task_get_bootstrap_port(mach_task_self(),
&the_bootstrap_port);
if (kern_result != KERN_SUCCESS) {
kextd_error_log("task_get_bootstrap_port(): %s\n",
mach_error_string(kern_result));
exit (EX_UNAVAILABLE);
}
if (bootstrap_port_ref) {
*bootstrap_port_ref = the_bootstrap_port;
}
}
kern_result = bootstrap_status(the_bootstrap_port,
(char *)KEXTD_SERVER_NAME, &active);
switch (kern_result) {
case BOOTSTRAP_SUCCESS:
if (active) {
kextd_error_log("kextd: '%s' is already active\n",
KEXTD_SERVER_NAME);
result = true;
goto finish;
}
break;
case BOOTSTRAP_UNKNOWN_SERVICE:
result = false;
goto finish;
break;
default:
kextd_error_log("bootstrap_status(): %s\n",
mach_error_string(kern_result));
exit(EX_UNAVAILABLE);
}
finish:
return result;
}
static int kextd_get_mach_ports(void)
{
kern_return_t kern_result;
kern_result = IOMasterPort(NULL, &g_io_master_port);
if (kern_result != KERN_SUCCESS) {
kextd_error_log("couldn't get catalog port");
return 0;
}
return 1;
}
int kextd_fork(void)
{
sigset_t signal_set;
sigset_t old_signal_set;
uid_t pid;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGTERM);
sigaddset(&signal_set, SIGCHLD);
sigaddset(&signal_set, SIGALRM);
if (sigprocmask(SIG_BLOCK, &signal_set, &old_signal_set)) {
kextd_error_log("sigprocmask() failed");
return 0;
}
signal(SIGTERM, kextd_parent_handle_signal);
signal(SIGCHLD, kextd_parent_handle_signal);
signal(SIGALRM, kextd_parent_handle_signal);
pid = fork();
switch (pid) {
case -1:
return 0; break;
case 0:
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
if (sigprocmask(SIG_UNBLOCK, &old_signal_set, NULL)) {
kextd_error_log("sigprocmask() failed");
return 0;
}
if (!kextd_get_mach_ports()) {
exit(1);
}
_CFRunLoopSetCurrent(NULL);
break;
default: kextd_openlog("kextd-parent");
struct itimerval timer_buf;
timer_buf.it_interval.tv_sec = 0;
timer_buf.it_interval.tv_usec = 0;
timer_buf.it_value.tv_sec = 40;
timer_buf.it_value.tv_usec = 0;
setitimer(ITIMER_REAL, &timer_buf, NULL);
while (1) {
sigsuspend(&old_signal_set);
if (parent_received_sigterm) {
kern_return_t kern_result;
mach_timespec_t waitTime = { 40, 0 };
kern_result = IOKitWaitQuiet(g_io_master_port, &waitTime);
if (kern_result == kIOReturnTimeout) {
kextd_error_log("IOKitWaitQuiet() timed out",
kern_result);
} else if (kern_result != kIOReturnSuccess) {
kextd_error_log("IOKitWaitQuiet() failed with result code %lx",
kern_result);
exit(1);
}
exit(0);
} else if (parent_received_sigchld) {
kextd_error_log("kextd forked child task exited abnormally");
exit(1);
} else if (parent_received_sigalrm) {
kextd_error_log("kextd parent task timed out waiting for signal from child");
exit(1);
}
}
break;
}
if (setsid() == -1) {
return 0;
}
if (chdir("/") == -1) {
return 0;
}
return 1;
}
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;
if (kextd_is_running(&gBootstrap_port)) {
result = false;
goto finish;
}
gRunLoopSourceLock = PTLockCreate();
if (!gRunLoopSourceLock) {
kextd_error_log(
"failed to create kernel request run loop source lock");
result = false;
goto finish;
}
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_rescan;
gRescanRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault,
sourcePriority++, &sourceContext);
if (!gRescanRunLoopSource) {
kextd_error_log("couldn't create signal-handling run loop source");
result = false;
goto finish;
}
CFRunLoopAddSource(gMainRunLoop, gRescanRunLoopSource,
kCFRunLoopDefaultMode);
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 = CFMachPortCreate(kCFAllocatorDefault,
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);
gKextdSignalMachPort = CFMachPortCreate(kCFAllocatorDefault,
kextd_runloop_handle_sighup, 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
kextd_log("registering service \"%s\"", KEXTD_SERVER_NAME);
kern_result = bootstrap_register(bootstrap_port,
(char *)KEXTD_SERVER_NAME, CFMachPortGetPort(kextdMachPort));
switch (kern_result) {
case BOOTSTRAP_SUCCESS:
break;
case BOOTSTRAP_NOT_PRIVILEGED:
kextd_error_log("bootstrap_register(): bootstrap not privileged");
exit(EX_OSERR);
case BOOTSTRAP_SERVICE_ACTIVE:
kextd_error_log("bootstrap_register(): bootstrap service active");
exit(EX_OSERR);
default:
kextd_error_log("bootstrap_register(): %s",
mach_error_string(kern_result));
exit(EX_OSERR);
}
finish:
if (gRescanRunLoopSource) CFRelease(gRescanRunLoopSource);
if (gKernelRequestRunLoopSource) CFRelease(gKernelRequestRunLoopSource);
if (gClientRequestRunLoopSource) CFRelease(gClientRequestRunLoopSource);
#ifndef NO_CFUserNotification
if (gNotificationQueueRunLoopSource) CFRelease(gNotificationQueueRunLoopSource);
#endif
if (kextdMachPort) CFRelease(kextdMachPort);
return result;
}
void kextd_release_parent_task(void)
{
kill(getppid(), SIGTERM);
return;
}
void kextd_register_signals(void)
{
signal(SIGHUP, kextd_handle_sighup);
return;
}
void kextd_parent_handle_signal(int signum)
{
switch (signum) {
case SIGTERM:
parent_received_sigterm = true;
break;
case SIGCHLD:
parent_received_sigchld = true;
break;
case SIGALRM:
parent_received_sigalrm = true;
break;
}
return;
}
void kextd_handle_sighup(int signum)
{
mach_msg_empty_send_t msg;
mach_msg_option_t options;
kern_return_t kern_result;
if (signum != SIGHUP) {
return;
}
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
msg.header.msgh_size = sizeof(msg);
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,
msg.header.msgh_size,
0,
MACH_PORT_NULL,
0,
MACH_PORT_NULL);
return;
}
void kextd_runloop_handle_sighup(
CFMachPortRef port,
void *msg,
CFIndex size,
void *info)
{
if (gRescanRunLoopSource) {
PTLockTakeLock(gRunLoopSourceLock);
kextd_log("received SIGHUP; rescanning all kexts and resetting catalogue");
CFRunLoopSourceSignal(gRescanRunLoopSource);
CFRunLoopWakeUp(gMainRunLoop);
PTLockUnlock(gRunLoopSourceLock);
} else {
kextd_log("received SIGHUP before entering run loop; ignoring");
}
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;
}
#endif
void kextd_handle_rescan(void * info)
{
#ifndef NO_CFUserNotification
kextd_clear_all_notifications();
#endif
KXKextManagerResetAllRepositories(gKextManager);
kextd_download_personalities();
return;
}
static Boolean kextd_download_personalities(void)
{
KXKextManagerError result;
result = KXKextManagerSendAllKextPersonalitiesToCatalog(gKextManager);
return (kKXKextManagerErrorNone == result) ? true : false;
}
static Boolean kextd_process_ndrvs(CFArrayRef repositoryDirectories)
{
Boolean result = true;
CFIndex repositoryCount, r;
CFStringRef thisPath = NULL; CFURLRef repositoryURL = NULL; CFURLRef ndrvDirURL = NULL;
repositoryCount = CFArrayGetCount(repositoryDirectories);
for (r = 0; r < repositoryCount; r++) {
if (repositoryURL) {
CFRelease(repositoryURL);
repositoryURL = NULL;
}
if (ndrvDirURL) {
CFRelease(ndrvDirURL);
ndrvDirURL = NULL;
}
thisPath = (CFStringRef)CFArrayGetValueAtIndex(
repositoryDirectories, r);
repositoryURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
thisPath, kCFURLPOSIXPathStyle, true);
if (!repositoryURL) {
kextd_error_log("memory allocation failure");
result = 0;
goto finish;
}
ndrvDirURL = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
repositoryURL, CFSTR("AppleNDRV"), true);
if (!ndrvDirURL) {
kextd_error_log("memory allocation failure");
result = 0;
goto finish;
}
IOLoadPEFsFromURL( ndrvDirURL, MACH_PORT_NULL );
}
finish:
if (repositoryURL) CFRelease(repositoryURL);
if (ndrvDirURL) CFRelease(ndrvDirURL);
return result;
}
static void usage(int level)
{
fprintf(stderr,
"usage: %s [-c] [-d] [-f] [-h] [-j] [-r directory] ... [-v [1-6]] [-x]",
progname);
if (level > 1) {
kextd_error_log(" -c don't use repository caches; scan repository folders\n");
kextd_error_log(" -d run in debug mode (don't fork daemon)\n");
kextd_error_log(" -f don't fork when loading (for debugging only)\n");
kextd_error_log(" -h help; print this list\n");
kextd_error_log(" -j don't jettison kernel linker; "
"just load NDRVs and exit (for startup from install CD)\n");
kextd_error_log(" -r start up with kexts in directory in addition to "
"those in /System/Library/Extensions\n");
kextd_error_log(" -v verbose mode\n");
kextd_error_log(" -x run in safe boot mode.\n");
}
return;
}
char * CFURLCopyCString(CFURLRef anURL)
{
char * string = NULL; CFIndex bufferLength;
CFStringRef urlString = NULL; Boolean error = false;
urlString = CFURLCopyFileSystemPath(anURL, kCFURLPOSIXPathStyle);
if (!urlString) {
goto finish;
}
bufferLength = 1 + CFStringGetLength(urlString);
string = (char *)malloc(bufferLength * sizeof(char));
if (!string) {
goto finish;
}
if (!CFStringGetCString(urlString, string, bufferLength,
kCFStringEncodingMacRoman)) {
error = true;
goto finish;
}
finish:
if (error) {
free(string);
string = NULL;
}
if (urlString) CFRelease(urlString);
return string;
}