<-- Home

Flash ThinkPad light for HDD status

The ThinkPad Edge series of laptops have an extremely minimalistic design, with no stickers or labels on bottom and with no LEDs on top. I'm sure somebody thought this was quite visually appealing, but not having a hard drive indicator light is in my opinion a serious drawback. Fortunately, under Linux, it is possible to control the little red dot that is part of the ThinkPad logo.

The program shown below will flash the ThinkPad LED when the hard drive is being accessed (or in fact upon any sort of disk IO). You must run it as root. It consumes approximately no CPU. There are numbers you can adjust to change the flash rate if you wish. It could also be adapted to flash the caps lock or something, but figuring that out is up to you!

Download via the following link: hdd_led.cc
Run this command to compile it:
	g++ -Wall -Wextra -O2 -o hdd_led hdd_led.cc
... and then run it as root.
	sudo ./hdd_led
You might want to call it from /etc/rc.d/rc.local so it starts automatically upon boot.
// This script flashes the ThinkPad light when there is hard drive activity.
// Run this command to compile it:
//   g++ -Wall -Wextra -O2 -o hdd_led hdd_led.cc
// Then run as root.
//
// Script by Dan Stahlke, dan@stahlke.org.  BSD license.  No warranty.

#include <string>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include <unistd.h>

// This is the interface to the LED.
static const char *led_fn = "/proc/acpi/ibm/led";
// The file to watch.  Whenever this changes, the LED flashes.  You could for example tie the
// LED to network activity or something by changing this.
static const char *stats_fn = "/proc/diskstats";
// This defines the check and flash intervals, in microseconds.
static const int check_interval = 200000; // 5 Hz
static const int flash_off_interval = 40000; // flash at 20 Hz, 20% duty cycle
static const int flash_on_interval  = 10000;

void set_led(int new_status) {
	int led_id = 0;
	std::ofstream fh(led_fn, std::ios::out);
	if(fh) {
		fh << led_id << " " << (new_status ? "on" : "off") << std::endl;
	} else {
		std::cerr << "Could not open " << led_fn << ", aborting." << std::endl;
		exit(1);
	}
}

// Easiest way to read a file, from
// http://insanecoding.blogspot.com/2011/11/how-to-read-in-file-in-c.html
// The faster methods don't work, probably because of bad interaction of seek with /proc files.
std::string get_file_contents(const char *fn) {
	std::ifstream in(fn, std::ios::in | std::ios::binary);
	if(in) {
		return std::string(
			std::istreambuf_iterator<char>(in),
			std::istreambuf_iterator<char>()
		);
	}
	std::cerr << "Error reading " << fn << ", aborting." << std::endl;
	exit(1);
}

int main() {
	std::string last_stat = get_file_contents(stats_fn);
	for(;;) {
		while(get_file_contents(stats_fn) != last_stat) {
			last_stat = get_file_contents(stats_fn);
			set_led(0);
			usleep(flash_off_interval);
			set_led(1);
			usleep(flash_on_interval);
		}
		usleep(check_interval);
	}
}

<-- Home