/* * heartbeat++.c -- flash a Lock LED (keyboard) in an hearthbeat fashion, usually without options. * * * The flash code is from the book LNUX DEVICE DRIVER (O'Reilly). * Works even with USB-Keyboards and also with PS/2-Keyboards (even with both at the same time, but * keyboard detection is made only for PS/2). * With an PS/2-USB adapter an USB-Keyboard can be used as an PS/2 keyboard. * * 2005: Changed to 50 % dute-cute and 1/2 Hz at a relative loadaverage (load / cpus (cores)) of 0, 1 Hz at 1, etc.. * * 2007: Added Keylogger detection via controll port reading, because ioctls, udevmonitor --env * and reading in /proc are not sufficient. * * 2008: Added higher time resolution of ns because with a dozen of Cherry, Logitech, Microsoft and noname keyboards * and dozens of thousends of hours usage i have seen no false alarm but with a Steelseries 7G i get about two false * alarms per day, when the load average is about 10. The steelseries keyboard is sleeping about half a second. * It seems the 7G is a USB keyboard with an internal USB to PS/2 adapter which adds extra latency. * I also added a loop counter. * * 2009: Added better integer rounding (mc_POS_DIV), addedd a minimal blink/scan frequency. * Added blinking with the num lock led and opposite phase after the first keyboard disconnect. * New frequency scaling: 1/2 Hz at load 0, 2 Hz at a rel. load (load / cpus (cores)) of 1, 3,5 Hz at 2, 5 Hz at 3, etc.. * * 2010: Changed the scanning for the core count to the shorter string "processor ", because the old format failed under Debian 5.0.4. * * TODO: Signal-Handler, Extension for USB keyboards (maybe udevmonitor --env, input-tools (http://dl.bytesex.org/cvs-snapshots/) or input-layers (http://linuxconsole.sourceforge.net/)), hwinfo ... * * * Dr. Rolf Freitag * Version 2010-08-25 Example usage with two lines in /etc/inittab, after compiling, e. g. wia gcc -Wall -O3 -o heartbeat++ heartbeat++.c and copying via cp -i heartbeat++ /sbin/ : # keyboard led heartbeat++ 13:S12345:respawn:/usr/bin/nice -n+19 /sbin/heartbeat++ --- Citation from the flash code source license: This source code can be redistributed in source or binary form so long as an acknowledgment appears in derived source files. The citation should list that the code comes from "Linux Device Drivers" by Alessandro Rubini, published by O'Reilly & Associates. This code is under copyright and cannot be included in any other book, publication, or educational product without permission from O'Reilly & Associates. No warranty is attached; we cannot take responsibility for errors or fitness for use. Enjoy /alessandro */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* or */ #include // version #define VERSION 1.7 // release date #define DATE 2010-06-14 // Dividing of (unsigned) integer numbers with good rounding (with minimized quantisation error). // Integer division (of positive numbers) ignores everything behind the point while good rounding // rounds up >= .5 and rounds down < .5. # define mc_POS_DIV(a, b) ( (a)/(b) + ( ( (a) % (b) >= (b)/2 ) ? 1 : 0 ) ) int main (int argc, char **argv) { char led; int chosenled = 1; /* default: scroll lock */ char hearth[] = { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }; // heartbeat pattern unsigned int udelay = 200000; // 1/5 s default delay int udelay_tmp = 0; int load = 0, i_cpus = 0, line_counter = 0, i_ac = 0; char *prgname = argv[0]; char string[128]; FILE *f = NULL; char ca_in_line[123 + 1] = { 0 }; uint8_t b_status = 0, b_old_status = 0; // Keyboard status: Start with present keyboard (no timeout flag set). uint64_t u64_lc = 0; // loop counter uint64_t u64_dc = 0; // disconnect counter if (argc > 1 && isdigit (argv[1][0])) { /* the time delay */ udelay_tmp = 1000 * strtol(argv[1], (char **)NULL, 0); if (udelay < 1000) { (void)fprintf (stderr, "%s: delay too short; using default\n", prgname); } else { udelay = udelay_tmp; argv++; argc--; } } nice (19); /* in case it succeeds... */ iopl (3); /* allows access to all I/O-Ports, ioperm doesen't work above the 0x3ff-Limit e. g. at PCI-Cards */ // estimate number of cpus/cores f = fopen ("/proc/cpuinfo", "r"); for (line_counter = 0; (line_counter < 123456) and (NULL != fgets (ca_in_line, 123, f)); line_counter++) { i_ac = sscanf (ca_in_line, "processor %s", string); if (1 == i_ac) i_cpus++; } if (i_cpus < 1) i_cpus = 1; (void)fclose (f); udelay *= 100; /* prepare for a later division, for percent calculation */ /* if (argc > 1 && strnlen (argv[1], 123) == 1) { argv++, argc--; if (tolower (argv[0][0]) == 's') chosenled = 1; // scroll lock else if (tolower (argv[0][0]) == 'n') chosenled = 2; // num lock else if (tolower (argv[0][0]) == 'c') chosenled = 4; // caps lock else { fprintf (stderr, "%s: unknown led '%s'\n", prgname, argv[1]); argc++; } } */ chosenled = 1; // scroll lock if (argc > 1) { (void)fprintf (stderr, "%s: usage \"%s [delay ms]\"\n", prgname, prgname); exit (1); } // ok, now do your loop for (u64_lc=0;;u64_lc++) { int consolefd = open ("/dev/tty0", O_RDONLY); int i = 0; f = fopen ("/proc/loadavg", "r"); if (f) { fscanf (f, "%d.%d", &load, &i); fclose (f); } else { load = i = 0; } // scale load by 300 for 2 Hz at a rel. load of 1, 3,5 Hz at 2, 5 Hz at 3, etc.. load = 3 * ( load * 100 + i ); // scale load by number of cpus/cores for the average load of one core; add offset 100 load = mc_POS_DIV(load, i_cpus) +100; for (i = 0; i < sizeof (hearth) / sizeof (hearth[0]); i++) { if (ioctl (consolefd, KDGETLED, &led) || ioctl (consolefd, KDSETLED, (led & ~(chosenled bitor (u64_dc ? 2 : 0))) | (hearth[i] ? chosenled : (u64_dc ? 2 : 0)))) { (void)fprintf (stderr, "%s: ioctl(): %s\n", prgname, strerror (errno)); exit (-2); } usleep (mc_POS_DIV(udelay,load)); // check if the keyboard is present: Look for the timeout flag 0x40 at port 0x64 b_status = inb (0x64); // printf("%x\n", b_status); // for debugging // filter the timeout flag 0x40 (BIT6) b_status and_eq 0x40; if (b_status) { // usually \a does not work; it's only a one-line try (void)printf ("\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a\a"); // beep via the speaker (install beep first) (void)system ("beep -l 123"); // beep via the (first) soundcard (void)system ("dd if=/dev/urandom of=/dev/dsp bs=1 count=4321"); } if (b_status != b_old_status) // status change: Send Mail { b_old_status = b_status; // store the status if (b_status) // keyboard hangup (vanished) { u64_dc++; (void)system ("date --rfc-3339=ns | mail -s \"The Keyboard has been removed!\" root@localhost"); (void)system ("echo -n \"-\" >> /root/heartbeat++.log; date --rfc-3339=ns >> /root/heartbeat++.log"); (void)printf ("Keyboard status change: The (PS/2) Keyboard has been removed! loop counter %llu\n", (long long unsigned int)u64_lc); } else // keyboard has gone online { (void)system ("date --rfc-3339=ns | mail -s \"The Keyboard removal has been ended!\" root@localhost"); // (void)system ("mail -s \"The Keyboard removal has been ended!\" root@localhost > /root/heartbeat++.log; date --rfc-3339=ns >> /root/heartbeat++.log"); (void)printf ("Keyboard status change: The (PS/2) Keyboard removal has been ended! loop counter %llu\n", (long long unsigned int)u64_lc); } } } i = close (consolefd); } iopl (0); /* release region */ exit (0); /* never happen */ } // main