November 5, 2018

Learning a little about notification chains in the Linux kernel

Part 3 of a series on messing with controlling NUC LEDs in which I start rooting around in the guts of the kernel.

Learning a little about notification chains in the Linux kernel

Part 3 of a series on messing with controlling NUC LEDs in which I start rooting around in the guts of the kernel.

For the last part of my miniseries on playing with my Intel NUC, I'm solving the issue of the front LEDs not turning themselves off when I shut off the machine. This meant that when I'm using them as status indicators, if the NUC shut off then they would be frozen showing the status at the point of shutting down.

I needed some way of finding out when the NUC was in the process of shutting down so I could quickly tell the LEDs to switch off before it shuts off completely. This had to be something internal to the kernel module as it was a fairly fundamental feature and having it handled by an outside process would be downright UGLY!

This was a bit of a challenge being someone who has a healthy attitude of "if it's at the core of your OS and you don't understand it, leave it the hell alone!". Documentation for the kernel isn't the most accessible thing in the world (I mean who doesn't love browsing mailing lists for people who would find this issue trivial?) so I was reduced to general googling around the topic.

Through this roundabout googling, I managed to find this site. Rest assured, I haven't added a keylogger to the module, but notification chains definitely seem like a promising solution. Surely if there's a notification chain for something like a keyboard event, there must be one for the computer shutting down!

Surely enough, I found a reboot_notifier_list of functions to be run before shutting down the computer. Following the example in the link above, I could modify milesp20's kernel module to automatically turn off the LEDs on shutdown as so:

static struct notifier_block nb = {
	.notifier_call = turn_off_led
};

static int turn_off_led(struct notifier_block *nb, unsigned long action, void *data){
    /*Code to turn off the LEDS*/
}

static int __init init_nuc_led(void)
{
    /*Original function*/
    register_reboot_notifier(&nb);
}

static void __exit unload_nuc_led(void)
{
    unregister_reboot_notifier(&nb);
    /*Original function*/
}

Notice how I have to register my notifier_block on the chain when I load my module and remove it from the chain when my module exits. Once I've done this turn_off_led is called whenever the NUC shuts down.

This site shows another good example of using notifier chains to track events in the Linux kernel, this time tracking USB devices being added and removed.

Hopefully if you've stumbled onto this post trying to figure out, like me, how to get your first kernel module tracking events.  This should be enough to give you a push in the right direction without having to do too much googling on the topic.

Back to thinking about LEDs though... milesp20 doesn't seem to be active any more on Github so you can find my updated kernel module for the NUC LED control here. It's not the prettiest but its firmly in the "good enough" category until I pick it up again. (Feel free to have a look and check for keyloggers ;-) )