A better way to resolve the problem (when a user is not in a VT there are no LEDs displayed from tleds, in kernels >= 2.6.18) was suggested by Bauke Jan Douma: use the HID input event interface.
It is documented here: http://www.frogmouth.net/hid-doco/linux-hid.html That I have now done that by adding an '-e' option. In that case /dev/input is searched for input devices that support LEDs and the right kind of LEDs and if so write to those devices with plain write instead of using the ioctl interface. The patch is attached. It solves the problem: although I found few users who reported the problem. Now the latest kernel (2.6.19.2) causes no problems displaying LEDs in X. Hugo
diff -Naur tleds-1.05beta10-orig/tleds.c tleds-1.05beta10/tleds.c --- tleds-1.05beta10-orig/tleds.c 2006-10-10 15:20:26.000000000 -0500 +++ tleds-1.05beta10/tleds.c 2007-01-15 08:45:12.000000000 -0600 @@ -43,6 +43,13 @@ * E. Hull (1999-08-20, 1999-05-14) for cleaner shutdown, security fixes to * the PID handling, use of daemon for backgrounding, and the -n option. */ +/* Added the -e option to use the HID interface instead of the conioctl + * interface. This incase the LEDs don't function in X. Only as root. + * 01/14/07 - began changes: writes but no LEds + + */ + + #define VERSION "1.05beta10" #define MYNAME "tleds" @@ -89,6 +96,18 @@ /* needed by getfd and friends to support console deallocating */ #include <errno.h> +#include <linux/input.h> +#include <dirent.h> +#include <malloc.h> +#include <asm/types.h> +#include <stdint.h> +#include <syslog.h> +#include <stdarg.h> +#define BASICDIR "/dev/input" +#define NOINPUTDEVICES 20 +#define MAX_STR_LEN 256 +#define test_bit(bit, array) (array[bit/8] & (1<<(bit%8))) + #ifndef TRUE #define TRUE 1 #define FALSE 0 @@ -158,6 +177,20 @@ /* from kbd-0.99, needed to support console deallocating */ int getfd(); +int device_has_leds(char *device); +int device_has_ncs_locks(char *device); +void my_write(int * fd_array, char ** start_array, const void *buf, size_t count); +void my_fflush(int * fd_array, char ** start_array, const void *buf, size_t count); +void close_it(int * fd_array, char ** start_array); +void free_it(int * fd_array, char ** start_array); +int find_devices(void); +int open_the_devices(void); +void hid_led(int what, LedMode mode, ActionMode doAction); +void do_log_it(char *vsname, ...); + +char ** start_array; +int * fd_array; + /* Global and static variables */ static const char devFileName[] = NETDEVFILENAME; static char pidFileName[30] = ""; /* 30 should be enough */ @@ -176,7 +209,8 @@ static ushort previousActive = (ushort) (MAXVT + 1); static int remindVTcoef = 0; static int opt_b = FALSE, opt_d = FALSE, opt_h = FALSE, opt_k = FALSE, opt_q -= FALSE, opt_v = FALSE, opt_V = FALSE, opt_c = FALSE, opt_n = FALSE; += FALSE, opt_v = FALSE, opt_V = FALSE, opt_c = FALSE, opt_n = FALSE, +opt_e = FALSE; static int inled = NUMLOCKLED, outled = SCROLLLOCKLED; /* The code */ @@ -190,8 +224,12 @@ pid_t pid; int sleeptime; int wasInDeepSleep; + + int num_devs; + struct timeval sleeptimeval; + interfaceName = NULL; sleeptime = 0; check_kernel_version(); /* May die here */ @@ -236,8 +274,10 @@ "Maybe later there will be. Kill me (-k) if ya want."); } + do_log_it("Before daemon %d %s\n",__LINE__,__FILE__); if (!opt_b) { if (-1 == daemon(0, (geteuid() != 0))) { + do_log_it("Daemon failure! %d %s\n",__LINE__,__FILE__); perror("tleds: daemon"); return 1; } @@ -250,11 +290,13 @@ (opt_b ? "fore" : "back"), (long) pid); } + do_log_it("Running in %sground. Pid: %ld\n",(opt_b ? "fore" : "back"),(long) pid); if (atexit(my_exit)) { perror("tleds: atexit() failed"); return 1; } if (!opt_b) { + do_log_it("parent_wants_me_dead! %d %s\n",__LINE__,__FILE__); signal(SIGUSR1, parent_wants_me_dead); } signal(SIGHUP, SIG_IGN); @@ -265,9 +307,27 @@ signal(SIGUSR2, SIG_IGN); signal(SIGPIPE, my_signal_handler); if (!geteuid()) { /* We are running as EUID root - CONSOLE */ - if (-1 == (keyboardDevice = open(KEYBOARDDEVICE, O_RDONLY))) { + if ( opt_e ) { + num_devs=find_devices(); + printf("%d keyboards found\n",num_devs); + do_log_it("%d keyboards found\n",num_devs); + if (num_devs == 0) { + do_log_it("exit %d %s\n",__LINE__,__FILE__); + exit(1); + } + if ( open_the_devices() == 1 ) { + printf("Open error\n"); + do_log_it("Open error\n"); + if ( num_devs > 0 ) + free_it(fd_array, start_array); + do_log_it("exit %d %s\n",__LINE__,__FILE__); + exit(1); + } + } + else if (-1 == (keyboardDevice = open(KEYBOARDDEVICE, O_RDONLY))) { perror("tleds"); fprintf(stderr, "%s:%s", KEYBOARDDEVICE, TERMINATESTR); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } } else { /* EUID not root */ @@ -277,6 +337,7 @@ perror( "tleds: Can't open X DISPLAY on the current host."); fprintf(stderr, TERMINATESTR); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } #else @@ -286,6 +347,7 @@ "Error reading current led setting.\n%s\n", "Maybe stdin is not a VT?"); fprintf(stderr, TERMINATESTR); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } #endif @@ -324,6 +386,7 @@ if (!(devFile = fopen(devFileName, "r"))) { perror(devFileName); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } /* Skip two lines. (the header) */ @@ -432,6 +495,11 @@ static ulong ledReminder = 0x00; ulong ledVal; + if (opt_e) { + hid_led(led,mode,doAction); + return; + } + #if (! REMOVE_X_CODE) XKeyboardControl values; @@ -464,6 +532,7 @@ if (doAction != FINISH) { if (ioctl(keyboardDevice, KDGETLED, &ledVal)) { perror("led: tleds: KDGETLED"); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } } else { @@ -484,6 +553,7 @@ break; default: perror("led: tleds: wrong led-value"); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } if (opt_c && doAction != FINISH) { @@ -494,6 +564,7 @@ ledVal |= ledReminder; if (ioctl(keyboardDevice, KDSETLED, (char) ledVal)) { perror("led: tleds: KDSETLED"); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } ledReminder = 0x00; @@ -609,16 +680,24 @@ void parent_wants_me_dead(int x) { + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(x); } void my_signal_handler(int x) { + do_log_it("handler invoked=%d\n",x); + if (opt_e) { + close_it(fd_array, start_array); + free_it(fd_array, start_array); + } + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(x); } void my_exit() { + do_log_it("Exit invoked!\n"); if (opt_b && !opt_q) printf("Bye-Bye !\n"); if (myDisplay) { @@ -743,6 +822,7 @@ MYNAME, pidFileName, rootPidFileName, "Program terminated."); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } } @@ -758,6 +838,7 @@ "\nSorry, can't run. There might be another", name, "If not, try: rm", pidFileName, rootPidFileName); kill(pid, SIGUSR1); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } @@ -765,12 +846,14 @@ if (fd < 0 || !(pidFile = fdopen(fd, "w"))) { perror(tmpPidFileName); kill(pid, SIGUSR1); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } fprintf(pidFile, "%ld\n", (long) pid); fclose(pidFile); if (chmod(tmpPidFileName, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) { perror(tmpPidFileName); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } } @@ -794,7 +877,7 @@ { int c; - while (EOF != (c = getopt(argc, argv, "bncd:hkqvV"))) { + while (EOF != (c = getopt(argc, argv, "bncd:ehkqvV"))) { switch (c) { case 'V': opt_V = TRUE; @@ -813,6 +896,9 @@ *sleeptime = get_sleeptime(TRUE, NULL); break; + case 'e': + opt_e = TRUE; + break; case 'h': opt_h = TRUE; break; @@ -879,6 +965,7 @@ if (-1 == uname(&buffer)) { perror("tleds: check_kernel_version()"); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } #if KERNEL2_0 @@ -886,12 +973,14 @@ fprintf(stderr, "%s was compiled for v2.0 kernel. Check Makefile. %s", MYNAME, TERMINATESTR); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } #else if (!strncmp("2.0.", (const char *) buffer.release, 4)) { fprintf(stderr, "%s was compiled for v2.1 (2.2?) kernel. %s", MYNAME, TERMINATESTR); + do_log_it("exit %d %s\n",__LINE__,__FILE__); exit(1); } #endif @@ -902,13 +991,15 @@ printf("Usage: %s [-bchkqv] [-d <update_delay>] <interface_name>\n", name); printf("Example: %s -d 300 ppp0\n", name); - printf("Options:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + printf("Options:\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "\t-b\tDon't go to the background.", "\t-c\tFix the CapsLED in VTs. Only for EUID root.", "\t-d N\tSet update delay.", "\t\tN must be between 1 and 10000 (milliseconds)", + "\t-e\tUse the HID interface", "\t-h\tHelp. (this)", "\t-k\tKill (old) (x)tleds running.", + "\t-n\tUse ScrollLock for in+out.", "\t-q\tBe quiet.", "\t-v\tPrint version information.", "\t\t(`cat /proc/net/dev` to see your interfaces.)"); @@ -1001,3 +1092,258 @@ ppp0: 391413 4588 1 0 0 0 0 0 4447 6560 0 0 0 0 0 0 $ */ + + +int device_has_leds(char *device) +{ + int fd = -1; /* the file descriptor for the device */ + if ((fd = open(device, O_WRONLY)) >= 0) { + uint8_t evtype_bitmask[EV_MAX/8 + 1]; + memset(evtype_bitmask, 0, sizeof(evtype_bitmask)); + if (ioctl(fd, EVIOCGBIT(0, sizeof(evtype_bitmask)), evtype_bitmask) >= 0) { + if (test_bit((int)EV_LED, evtype_bitmask)) { + close(fd); + return 1; + } + else { + close(fd); + return 0; + } + } + else { + return 0; + } + } + else { + return 0; + } +} + +int device_has_ncs_locks(char *device) +{ + int fd = -1; /* the file descriptor for the device */ + + if ((fd = open(device, O_WRONLY)) >= 0) { + uint8_t led_bitmask[LED_MAX/8 + 1]; + memset(led_bitmask, 0, sizeof(led_bitmask)); + if (ioctl(fd, EVIOCGBIT(EV_LED, sizeof(led_bitmask)), led_bitmask) >= 0) { + if (test_bit((int)LED_NUML, led_bitmask) && + test_bit((int)LED_CAPSL, led_bitmask) && + test_bit((int)LED_SCROLLL, led_bitmask)) { + close(fd); + return 1; + } + else { + close(fd); + return 0; + } + } + else { + return 0; + } + } + else { + return 0; + } +} + +void my_write(int * fd_array, char ** start_array, const void *buf, size_t count) +{ + int i,retval; + i = 0; + while ( start_array[i] != NULL ) { + retval = write(fd_array[i], buf, count); +// printf("written %d bytes for fd=%x %s (%d)\n",retval,fd_array[i],start_array[i],__LINE__); + i++; + } + +} + +void close_it(int * fd_array, char ** start_array) +{ + int i = 0; + while ( start_array[i] != NULL ) { + printf("closing fd for %s (%d)\n",start_array[i],__LINE__); + close(fd_array[i]); + i++; + } +} + +void free_it(int * fd_array, char ** start_array) +{ + int i = 0; + while ( start_array[i] != NULL ) { + free(start_array[i]); + i++; + } + free(start_array); + printf("free'd arrays (%d)\n",__LINE__); + +} + +int find_devices(void) +{ + DIR *dirp; + struct dirent *next; + int ip,num_devs; + char buffer[MAX_STR_LEN]; + + dirp = opendir (BASICDIR); + if (!dirp) { + printf( "Opendir ERROR = %s\n", strerror(errno) ); + return 0; + } + errno = 0; + + ip = NOINPUTDEVICES + 1; + num_devs = 0; + start_array = (char **)malloc( ip * sizeof(char*) ); + fd_array = (int *)malloc( ip * sizeof(int) ); +// test for insufficient memory? + printf( "Allocating array of size %d %d %s\n", ip,__LINE__,__FILE__ ); + start_array[num_devs] = NULL; // in case there is nothing + + while (1) { + next = readdir(dirp); + if (next) { + if (next->d_type == DT_CHR) { + printf("%s/%s is a DT_CHR type\n",BASICDIR,next->d_name); + sprintf(buffer,"%s/%s",BASICDIR,next->d_name); + if ( num_devs <= NOINPUTDEVICES && device_has_leds(buffer) && device_has_ncs_locks(buffer)) { + printf("allocating %s/%s (%d)\n",BASICDIR,next->d_name,__LINE__); + start_array[num_devs] = (char *)malloc(MAX_STR_LEN*sizeof(char)); +// test for insufficient memory? + fd_array[num_devs] = 0; + strcpy(start_array[num_devs],buffer); + num_devs++; + } + else + errno = 0; + } + } + else if (errno != 0) { + printf( "Readdir ERROR = %s\n", strerror(errno) ); + return 0; + } + else { + start_array[num_devs] = NULL; + fd_array[num_devs] = -1; + break; + } + } + closedir(dirp); + return num_devs; + +} + +int open_the_devices(void) +{ + + int i = 0; + int fd = -1; /* the file descriptor for the device */ + while ( start_array[i] != NULL ) { +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍthe peculiarityÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + fd_array[i] = -1; + fd = open(start_array[i], O_WRONLY); + fd_array[i] = fd; +//ÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ + if (fd_array[i] < 0) { + return 1; + } + else + printf("opened fd=%x for %s\n",fd_array[i],start_array[i]); + i++; + } + return 0; +} + +void hid_led(int led, LedMode mode, ActionMode doAction) +{ + struct input_event ev; /* the event */ + + ev.type = EV_LED; + + switch (led) { + case CAPSLOCKLED: + ev.code = LED_CAPSL; + break; + case NUMLOCKLED: + ev.code = LED_NUML; + break; + case SCROLLLOCKLED: + ev.code = LED_SCROLLL; + } + + switch (mode) { + case SET: + ev.value = 1; + break; + case CLEAR: + ev.value = 0; + break; + case TOGGLE: + ev.value = 1; + } + +// printf("writing value %d for %d (%d)\n",ev.value,ev.code,__LINE__); + my_write(fd_array, start_array, &ev, sizeof(struct input_event)); + if (doAction != DELAYED) + my_fflush(fd_array, start_array, &ev, sizeof(struct input_event)); +} + +void my_fflush(int * fd_array, char ** start_array, const void *buf, size_t count) +{ +// there is no flushing - syncing function for write... + int i,retval; + i = 0; + retval = 0; + while ( start_array[i] != NULL ) { +// flushall(); +// retval = fflush(fd_array[i]); +// printf("written %d bytes for %s (%d)\n",retval,start_array[i],__LINE__); + i++; + } + +} + +void do_log_it(char *vsname, ...) +{ + va_list argptr; + char buffer2[400]; + va_start(argptr, vsname); + vsprintf(buffer2, vsname, argptr); + va_end(argptr); + + openlog( "tleds", LOG_PID, LOG_USER); + syslog( LOG_INFO | LOG_USER, "%s\n",buffer2); + closelog(); + +} + + + + + + + + + + + + + + + + + + + + + + + + + + + +