pax_global_header00006660000000000000000000000064136644743710014531gustar00rootroot0000000000000052 comment=c4c7997bddb9daa60e4a99f2e50618a58a967d4b inputlirc-34/000077500000000000000000000000001366447437100133325ustar00rootroot00000000000000inputlirc-34/.gitignore000066400000000000000000000000231366447437100153150ustar00rootroot00000000000000names.h inputlircd inputlirc-34/Makefile000066400000000000000000000033201366447437100147700ustar00rootroot00000000000000# inputlircd -- zeroconf LIRC daemon that reads from /dev/input/event devices # Copyright (C) 2006-2019 Guus Sliepen # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA SBIN = inputlircd MAN8 = inputlircd.8 SERVICE = inputlirc.service CC ?= gcc CFLAGS ?= -Wall -g -O2 -pipe PREFIX ?= /usr/local INSTALL ?= install SBINDIR ?= $(PREFIX)/sbin SHAREDIR ?= $(PREFIX)/share MANDIR ?= $(SHAREDIR)/man SYSTEMDDIR ?= $(PREFIX)/lib/systemd/system all: $(SBIN) names.h: /usr/include/linux/input-event-codes.h gennames ./gennames $< > $@ inputlircd: inputlircd.c /usr/include/linux/input.h names.h $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $< install: install-sbin install-man install-service install-sbin: $(SBIN) mkdir -p $(DESTDIR)$(SBINDIR) $(INSTALL) $(SBIN) $(DESTDIR)$(SBINDIR)/ install-man: $(MAN1) $(MAN5) $(MAN8) mkdir -p $(DESTDIR)$(MANDIR)/man8/ $(INSTALL) -m 644 $(MAN8) $(DESTDIR)$(MANDIR)/man8/ install-service: $(SERVICE) mkdir -p $(DESTDIR)$(SYSTEMDDIR) $(INSTALL) -m 644 $(SERVICE) $(DESTDIR)$(SYSTEMDDIR)/ clean: rm -f $(SBIN) names.h .PHONY: all install install-sbin install-man install-service clean inputlirc-34/gennames000077500000000000000000000023031366447437100150530ustar00rootroot00000000000000#!/bin/sh # inputlircd -- zeroconf LIRC daemon that reads from /dev/input/event devices # Copyright (C) 2006-2009 Guus Sliepen # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as published # by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA TYPES="KEY BTN" for type in $TYPES; do grep "^#define ${type}_" < $1 | sed 's/\/\*.*//g' done grep "^#define EV_KEY" < $1 | sed 's/\/\*.*//g' grep "^#define KEY_MAX" < $1 | sed 's/\/\*.*//g' echo "static const char *KEY_NAME[KEY_MAX] = {" for type in $TYPES; do awk " /EV_VERSION/ { next }; /_MAX/ { next }; /#define ${type}_/ { printf(\"\t[ %-16s ] = \\\"%s\\\",\n\", \$2, \$2); } " < $1 | tac done echo "};" inputlirc-34/inputlirc.service000066400000000000000000000004121366447437100167220ustar00rootroot00000000000000[Unit] Documentation=man:inputlircd(8) Description=Zeroconf LIRC daemon using input event devices After=udev lircd [Service] Type=simple EnvironmentFile=/etc/default/inputlirc ExecStart=/usr/sbin/inputlircd -f $OPTIONS $EVENTS [Install] WantedBy=multi-user.target inputlirc-34/inputlircd.8000066400000000000000000000076001366447437100156030ustar00rootroot00000000000000.Dd Mon, 06 Feb 2006 17:42:25 +0100 .Dt INPUTLIRCD 8 .Sh NAME .Nm inputlircd .Nd zeroconf LIRC daemon using input event devices .Sh SYNOPSIS .Nm .Op Fl d Ar socket .Op Fl f .Op Fl c .Op Fl r Ar repeat-rate .Op Fl g .Op Fl m Ar keycode .Op Fl n Ar device name .Op Fl u Ar username .Op Fl t Ar path .Op Fl N Ar rc name .Ar device .Op Ar device Li ... .Sh DESCRIPTION .Nm is a small LIRC daemon that reads from .Pa /dev/input/event Ns X devices and sends the received keycodes to connecting LIRC clients. .Nm needs no configuration, it uses the standardised names for the keycodes as used by the kernel. Many USB remote controls that present HID devices, as well as multimedia keyboards should work out of the box. .Pp .Nm expects a list of input event devices as commandline parameters. It will only read events from those devices. .Sh OPTIONS .Bl -tag -width flag .It Fl d Ar socket Location of the UNIX socket to which LIRC clients can connect. The default is .Pa /run/lirc/lircd . .It Fl f Run in the foreground. .It Fl c Capture modifier keys. This causes the CTRL, SHIFT, ALT and META keys to be treated as modifer keys that, when used in combination with another keys, change the LIRC event from that key rather than being sent as their own LIRC events. .It Fl r Ar repeat-rate Set the repeat rate (in milliseconds) of the remote control. The default is 0. Repeated keys that arrive less than .Ar repeat-rate milliseconds apart will be flagged as as repeat LIRC events. .It Fl g Grab the input device(s). This gives .Nm exclusive access to the input devices and stops events from propagating any further. .It Fl m Ar keycode Minimum keycode to send to LIRC clients. Keycodes lower than this number are filtered out. The default is 88, this filters out the alphanumeric section and the keypad section of normal keyboards, but allows all extended keys. The rationale is that clients should not be able to grab normal keypresses, this could be a security risk. .It Fl n Ar device name Name of an input device to read events from. This scans all available input event devices, and if the symbolic name of an event device matches .Ar device name , adds it to the list of devices to read from. The .Ar device name can contain wildcard patterns, see .Xr glob 7 . To get a list of available devices and their names, cat .Pa /proc/bus/input/devices or use .Xr lsinput 8 . .It Fl u Ar username Set user and group id to that of .Ar username after opening the devices and UNIX socket as root. The default is nobody. .It Fl t Ar path Provides the path to a file containing a mapping between input event key names and the commands which should be reported via lirc. The files should contain lines of the form .Pa KEY_FOO = bar . This is useful for backward compatibility. The default is not to use a translation table. .It Fl N Ar rc name Set the remote control name, that is, a value of the last field of LIRC broadcast messages. If there is more than one input event device, the specified name will be used for all of them. If .Ar rc name is not specified, the filesystem path of each input event device will be used as its remote control name. .It Ar device One or more input event devices. If you want to use .Nm to process multimedia keys on the keyboard, then .Pa /dev/input/event0 is the most likely choice. If you have other input devices, such as USB remote controllers that act like a HID device, then you probably need one of the other event devices present. See .Pa /proc/bus/input/devices for a list of available input devices. If unsure, you can add all available input event devices. .El .Sh FILES .Bl -tag -width indent .It Pa /run/lirc/lircd Default location of the UNIX socket to which LIRC clients can connect. .It Pa /dev/input/event Ns X The kernel input layer's event device files. .It Pa /proc/bus/input/devices List of all input devices. .El .Sh SEE ALSO .Xr irw 1 , .Xr input-events 8 , .Xr setkeycodes 8 , .Pa /usr/include/linux/input-event-codes.h . inputlirc-34/inputlircd.c000066400000000000000000000274631366447437100156670ustar00rootroot00000000000000/* inputlircd -- zeroconf LIRC daemon that reads from /dev/input/event devices Copyright (C) 2006-2019 Guus Sliepen This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "names.h" typedef struct evdev { char *name; int fd; struct evdev *next; } evdev_t; static evdev_t *evdevs = NULL; typedef struct client { int fd; struct client *next; } client_t; static client_t *clients = NULL; static int sockfd; static bool grab = false; static int key_min = 88; static char *device = "/run/lirc/lircd"; static char *rc_name = NULL; static bool capture_modifiers = false; static bool meta = false; static bool alt = false; static bool shift = false; static bool ctrl = false; static long repeat_time = 0L; static struct timeval previous_input; static struct timeval evdev_timeout; static struct input_event previous_event; static int repeat = 0; static void *xalloc(size_t size) { void *buf = malloc(size); if(!buf) { fprintf(stderr, "Could not allocate %zd bytes with malloc(): %s\n", size, strerror(errno)); exit(EX_OSERR); } memset(buf, 0, size); return buf; } static void parse_translation_table(const char *path) { FILE *table; char *line = NULL; size_t line_size = 0; char event_name[100]; char lirc_name[100]; unsigned int i; if(!path) return; table = fopen(path, "r"); if(!table) { fprintf(stderr, "Could not open translation table %s: %s\n", path, strerror(errno)); return; } while(getline(&line, &line_size, table) >= 0) { if (sscanf(line, " %99s = %99s ", event_name, lirc_name) != 2) continue; event_name[99] = '\0'; lirc_name[99] = '\0'; if(strlen(event_name) < 1 || strlen(lirc_name) < 1) continue; if(!(i = strtoul(event_name, NULL, 0))) { for(i = 0; i < KEY_MAX; i++) { if (!KEY_NAME[i]) continue; if(!strcmp(event_name, KEY_NAME[i])) break; } } if(i >= KEY_MAX) continue; KEY_NAME[i] = strdup(lirc_name); if(!KEY_NAME[i]) { fprintf(stderr, "strdup failure: %s\n", strerror(errno)); exit(EX_OSERR); } } fclose(table); free(line); } static int open_evdev(char *name) { int fd; fd = open(name, O_RDONLY); if(fd < 0) { syslog(LOG_ERR, "Could not open %s: %s\n", name, strerror(errno)); return -1; } unsigned long bits = 0; if(ioctl(fd, EVIOCGBIT(0, sizeof bits), &bits) < 0) { close(fd); syslog(LOG_ERR, "Could not read supported event types from %s: %s\n", name, strerror(errno)); return -1; } fprintf(stderr, "%s %lx\n", name, bits); if(!(bits & (1 << EV_KEY))) { close(fd); syslog(LOG_ERR, "%s does not support EV_KEY events\n", name); return -1; } if(grab) { if(ioctl(fd, EVIOCGRAB, 1) < 0) { close(fd); syslog(LOG_ERR, "Failed to grab %s: %s\n", name, strerror(errno)); return -1; } } return fd; } static void rescan_evdevs(fd_set *permset) { evdev_t *evdev; int fd; for(evdev = evdevs; evdev; evdev = evdev->next) { if(evdev->fd == -999) { syslog(LOG_INFO, "Reading device: %s", evdev->name); fd = open_evdev(evdev->name); if(fd >= 0) { evdev->fd = fd; FD_SET(evdev->fd, permset); syslog(LOG_INFO, "Success!"); } } } } static void add_evdev(char *name) { int fd; evdev_t *newdev; fd = open_evdev(name); if(fd < 0) return; newdev = xalloc(sizeof *newdev); newdev->fd = fd; newdev->name = strdup(name); newdev->next = evdevs; evdevs = newdev; } static void add_named(char *pattern) { int i, result, fd; char name[32]; glob_t g; result = glob("/dev/input/event*", GLOB_NOSORT, NULL, &g); if(result == GLOB_NOMATCH) { fprintf(stderr, "No event devices found!\n"); return; } else if(result) { fprintf(stderr, "Could not read /dev/input/event*: %s\n", strerror(errno)); return; } for(i = 0; i < g.gl_pathc; i++) { fd = open(g.gl_pathv[i], O_RDONLY); if(fd < 0) { fprintf(stderr, "Could not open %s: %s\n", g.gl_pathv[i], strerror(errno)); continue; } result = ioctl(fd, EVIOCGNAME(sizeof(name)), name); close(fd); if(result < 0) { fprintf(stderr, "Could not read name of event device %s: %s\n", g.gl_pathv[i], strerror(errno)); continue; } name[(sizeof name) -1] = 0; if(!fnmatch(pattern, name, FNM_CASEFOLD)) add_evdev(g.gl_pathv[i]); } globfree(&g); } static void add_unixsocket(void) { struct sockaddr_un sa = {0}; sockfd = socket(AF_UNIX, SOCK_STREAM, 0); if(sockfd < 0) { fprintf(stderr, "Unable to create an AF_UNIX socket: %s\n", strerror(errno)); exit(EX_OSERR); } char *filename = strdup(device); if(filename) { char *dir = dirname(filename); if(mkdir(dir, 0755)) { if (errno != EEXIST) { fprintf(stderr, "Unable to create %s: %s\n", dir, strerror(errno)); exit(EX_OSERR); } } else { chmod(dir, 0755); } free(filename); } sa.sun_family = AF_UNIX; strncpy(sa.sun_path, device, sizeof sa.sun_path - 1); unlink(device); if(bind(sockfd, (struct sockaddr *)&sa, sizeof sa) < 0) { fprintf(stderr, "Unable to bind AF_UNIX socket to %s: %s\n", device, strerror(errno)); exit(EX_OSERR); } chmod(device, 0666); if(listen(sockfd, 3) < 0) { fprintf(stderr, "Unable to listen on AF_UNIX socket: %s\n", strerror(errno)); exit(EX_OSERR); } } static void processnewclient(void) { client_t *newclient = xalloc(sizeof *newclient); newclient->fd = accept(sockfd, NULL, NULL); if(newclient->fd < 0) { free(newclient); if(errno == ECONNABORTED || errno == EINTR) return; syslog(LOG_ERR, "Error during accept(): %s\n", strerror(errno)); exit(EX_OSERR); } int flags = fcntl(newclient->fd, F_GETFL); fcntl(newclient->fd, F_SETFL, flags | O_NONBLOCK); newclient->next = clients; clients = newclient; } static long time_elapsed(struct timeval *last, struct timeval *current) { long seconds = current->tv_sec - last->tv_sec; return 1000000 * seconds + current->tv_usec - last->tv_usec; } static void processevent(evdev_t *evdev, fd_set *permset) { struct input_event event; char message[1000]; int len; client_t *client, *prev, *next; if(read(evdev->fd, &event, sizeof event) != sizeof event) { syslog(LOG_ERR, "Error processing event from %s: %s\n", evdev->name, strerror(errno)); FD_CLR(evdev->fd, permset); close(evdev->fd); evdev->fd = -999; return; } if(event.type != EV_KEY) return; if(event.code > KEY_MAX || event.code < key_min) return; if(capture_modifiers) { if(event.code == KEY_LEFTCTRL || event.code == KEY_RIGHTCTRL) { ctrl = !!event.value; return; } if(event.code == KEY_LEFTSHIFT || event.code == KEY_RIGHTSHIFT) { shift = !!event.value; return; } if(event.code == KEY_LEFTALT || event.code == KEY_RIGHTALT) { alt = !!event.value; return; } if(event.code == KEY_LEFTMETA || event.code == KEY_RIGHTMETA) { meta = !!event.value; return; } } if(!event.value) return; struct timeval current; gettimeofday(¤t, NULL); if(event.code == previous_event.code && time_elapsed(&previous_input, ¤t) < repeat_time) repeat++; else repeat = 0; char *name = rc_name ? rc_name : evdev->name; if(KEY_NAME[event.code]) len = snprintf(message, sizeof message, "%x %x %s%s%s%s%s %s\n", event.code, repeat, ctrl ? "CTRL_" : "", shift ? "SHIFT_" : "", alt ? "ALT_" : "", meta ? "META_" : "", KEY_NAME[event.code], name); else len = snprintf(message, sizeof message, "%x %x KEY_CODE_%d %s\n", event.code, repeat, event.code, name); previous_input = current; previous_event = event; for(client = clients; client; client = client->next) { if(write(client->fd, message, len) != len) { close(client->fd); client->fd = -1; } } for(prev = NULL, client = clients; client; client = next) { next = client->next; if(client->fd < 0) { if(prev) prev->next = client->next; else clients = client->next; free(client); } else { prev = client; } } } static void main_loop(void) { fd_set permset; fd_set fdset; evdev_t *evdev; int maxfd = 0; int retselect; FD_ZERO(&permset); for(evdev = evdevs; evdev; evdev = evdev->next) { if(evdev->fd < 0) continue; FD_SET(evdev->fd, &permset); if(evdev->fd > maxfd) maxfd = evdev->fd; } FD_SET(sockfd, &permset); if(sockfd > maxfd) maxfd = sockfd; maxfd++; while(true) { fdset = permset; //wait for 30 secs, then rescan devices evdev_timeout.tv_sec = 30; retselect = select(maxfd, &fdset, NULL, NULL, &evdev_timeout); if(retselect > 0) { for(evdev = evdevs; evdev; evdev = evdev->next) if(FD_ISSET(evdev->fd, &fdset)) processevent(evdev, &permset); if(FD_ISSET(sockfd, &fdset)) processnewclient(); } else { if(retselect < 0) { if(errno == EINTR) continue; syslog(LOG_ERR, "Error during select(): %s\n", strerror(errno)); } rescan_evdevs(&permset); } } } int main(int argc, char *argv[]) { char *user = "nobody"; char *translation_path = NULL; int opt, i; bool foreground = false, named = false; gettimeofday(&previous_input, NULL); while((opt = getopt(argc, argv, "cd:gm:n:fu:r:t:N:")) != -1) { switch(opt) { case 'd': device = strdup(optarg); break; case 'g': grab = true; break; case 'c': capture_modifiers = true; break; case 'm': key_min = atoi(optarg); break; case 'n': named = true; add_named(optarg); break; case 'u': user = strdup(optarg); break; case 'f': foreground = true; break; case 'r': repeat_time = atoi(optarg) * 1000L; break; case 't': translation_path = strdup(optarg); break; case 'N': rc_name = strdup(optarg); break; default: fprintf(stderr, "Unknown option!\n"); return EX_USAGE; } } if(argc <= optind && !named) { fprintf(stderr, "Not enough arguments.\n"); return EX_USAGE; } openlog("inputlircd", LOG_PERROR, LOG_DAEMON); glob_t paths = {}; for(i = optind; i < argc; i++) { int result = glob(argv[i], GLOB_NOSORT | GLOB_NOMAGIC | (i > optind ? GLOB_APPEND : 0), NULL, &paths); if (result && result != GLOB_NOMATCH) { fprintf(stderr, "Could not glob %s: %s\n", argv[i], strerror(errno)); return EX_OSERR; } } for(i = 0; i < paths.gl_pathc; i++) add_evdev(paths.gl_pathv[i]); globfree(&paths); if(!evdevs) { fprintf(stderr, "Unable to open any event device!\n"); return EX_OSERR; } parse_translation_table(translation_path); add_unixsocket(); struct passwd *pwd = getpwnam(user); if(!pwd) { fprintf(stderr, "Unable to resolve user %s!\n", user); return EX_OSERR; } if(setgid(pwd->pw_gid) || setuid(pwd->pw_uid)) { fprintf(stderr, "Unable to setuid/setguid to %s!\n", user); return EX_OSERR; } if(!foreground) { closelog(); if(daemon(0, 0)) { fprintf(stderr, "Unable to run in the background: %s\n", strerror(errno)); return EX_OSERR; } openlog("inputlircd", 0, LOG_DAEMON); } syslog(LOG_INFO, "Started"); signal(SIGPIPE, SIG_IGN); main_loop(); return 0; }