Updating Kernel Extensions with Radmind: Best Practices

One of the problems that I’ve run into so far using Radmind to manage Mac OS X—specifically, the Leopard to Snow Leopard transition—is that kextd helpfully starts recreating your kernel extension cache as soon as you modify anything in /System/Library/Extensions. This can be problematic when you’re updating core system files; as you update the 10.5 kernel extensions to their 10.6 counterparts, you don’t want the 10.5 version of kextd creating a cache of 10.6 kernel extensions, especially as the kernel extension cache has moved (from /System/Library/Extensions.mkext to /System/Library/Caches/com.apple.kext.caches/). So, should you handle this? My solution is to stop kextd if I know that I’m updating kernel extensions; that way, they won’t be re-created until reboot. Here’s the script:

/private/var/radmind/preapply/update_kernel_extensions:

#!/bin/sh

# update_kernel_extensions: Manage the replacement of old kernel extensions.
#                           If there are updates, kill kextd and destroy the
#                           caches.

KEXT_CACHE="/System/Library/Caches/com.apple.kext.caches"
KEXT_FOLDER="/System/Library/Extensions"
KEXTD_LAUNCHD="com.apple.kextd"
SYSTEM_LAUNCHD_FOLDER="/System/Library/LaunchDaemons"

transcript="${1}"
result="${transcript}.$$"

/usr/bin/grep "${KEXT_FOLDER}" "${transcript}" > "${result}"

if test -n "${result}"; then #result is non-empty
    # Disable kextd to prevent it from recreating kernel extension caches, which
    # will be re-created at startup.
    if test -n "$(/bin/launchctl list | /usr/bin/grep ${KEXTD_LAUNCHD})"; then
        #kextd is running
        /bin/launchctl unload "${SYSTEM_LAUNCHD_FOLDER}/${KEXTD_LAUNCHD}.plist"
    fi

    # Remove kernel extension cache.
    /bin/rm -rf "${KEXT_CACHE}"
fi

rm -f "${result}"

In its present form, it only works on Snow Leopard, but I’ll be updating it to work on Leopard as well.

Take Me Home Now Free

Take Me Home is an ancient, buggy iPhone app that was my “Hello, World!” in the App Store. Its sale rate has plummeted to only a couple of buyers a week and, with the prevalence of real, honest-to-God GPS apps in the store these days, its usefulness is questionable. So from here on out, it’s free. I can’t promise to continue supporting it—especially for new devices and APIs—so the best I can do may be to remove it from the store in the event that some update breaks it.

Enjoy!

Quick Tip: Don’t Do This

I could not find out where a bug was coming from for the life of me today. Naturally it one of those “assignment instead of equality” bugs that seem to crop up when trying to code too quickly. The difference here was that I had implemented a subclass of NSObject that reimplemented the method +resolveInstanceMethod:. So, the code went like this:

+ ( BOOL )resolveInstanceMethod:( SEL )sel
{
    if ( sel = @selector( setFoobar: )) {
        return class_addMethod([ self class ], sel, ( IMP )setFB, "v@:@" );
    } else if ( sel = @selector( foobar )) {
        return class_addMethod([ self class ], sel, ( IMP )getFB, "@@:" );
    } else {
        return [ super resolveInstanceMethod:sel ];
    }
}


For those of you keeping score at home, when the Objective-C runtime tried to resolve any selector, this method happily added a selector for -setFoobar: to self’s class and returned YES.

Don’t do this.

Big Nerd Ranch

Big Nerd Ranch Cabin
My cabin at the Big Nerd Ranch.

Hopefully, I will continue to improve the content of this site in the months to come. To that end—among others—I’m at the Big Nerd Ranch in Atlanta, Georgia for their Advanced Mac OS X Programming course this week. I’m very excited and hope to learn as much as I can possibly fit into my brain.