#include #include #include #include #include #include #include #include #include #include #include #include #include "common/packet.h" struct core { int input; }; // we assume 64bit host, see packet.h for rant #if UINTPTR_MAX != 0xffffffffffffffff #define NOT_64BIT struct timeval_64 { uint64_t tv_sec, tv_usec; }; struct input_event_64 { struct timeval_64 time; uint16_t type; uint16_t code; int32_t value; }; #endif static int _ioctl(const int fd, const unsigned long request, void *arg) { int ret; do { ret = ioctl(fd, request, arg); } while (ret == -1 && (errno == EINTR || errno == EAGAIN)); return ret; } #define ioctl(...) error "Use _ioctl instead" static bool uinputd_ioctl_other(const enum ioctl_dir dir, const uint8_t type, const uint8_t nr, const uint16_t bytes, void *arg) { const struct packet packet = { .type = PACKET_IOCTL, .ioctl.dir = dir, .ioctl.type = type, .ioctl.nr = nr, .ioctl.arg = IOCTL_ARG_OTHER, .ioctl.bytes = bytes, }; if (fwrite(&packet, 1, sizeof(packet), stdout) != sizeof(packet)) { warn("fwrite: failed to write %u bytes", sizeof(packet)); return false; } if (bytes > 0 && fwrite(arg, 1, bytes, stdout) != bytes) { warn("fwrite: failed to write %u bytes", bytes); return false; } return true; } static bool uinputd_ioctl_int(const enum ioctl_dir dir, const uint8_t type, const uint8_t nr, int arg) { const struct packet packet = { .type = PACKET_IOCTL, .ioctl.dir = dir, .ioctl.type = type, .ioctl.nr = nr, .ioctl.arg = IOCTL_ARG_INT, .ioctl.integer = arg, }; if (fwrite(&packet, 1, sizeof(packet), stdout) != sizeof(packet)) { warn("fwrite: failed to write %u bytes", sizeof(packet)); return false; } return true; } static bool uinputd_write(const int32_t bytes, const void *data) { if (bytes <= 0 || !data) return true; const struct packet packet = { .type = PACKET_WRITE, .write.bytes = bytes, }; if (fwrite(&packet, 1, sizeof(packet), stdout) != sizeof(packet)) { warn("fwrite: failed to write %u bytes", sizeof(packet)); return false; } if (fwrite(data, 1, bytes, stdout) != bytes) { warn("fwrite: failed to write %u bytes", bytes); return false; } return true; } static void fd_close_or_exit(const int fd) { if (fd >= 0 && close(fd) != 0) err(EXIT_FAILURE, "close"); } static void core_release(struct core *core) { if (!core) return; fd_close_or_exit(core->input); *core = (struct core){0}; } static void core_init(struct core *core, const char *path) { assert(core); if ((core->input = open(path, O_RDONLY | O_CLOEXEC)) < 0) err(EXIT_FAILURE, "open(%s)", path); int evbit; if (_ioctl(core->input, EVIOCGBIT(0, sizeof(evbit)), &evbit) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGBIT)"); for (int i = 0; i < EV_MAX; ++i) { if (!(evbit & (1 << i))) continue; if (!uinputd_ioctl_int(IOCTL_IOW, UINPUT_IOCTL_BASE, 100, i)) errx(EXIT_FAILURE, "ioctl(UI_SET_EVBIT): %d", i); } if ((evbit & (1 << EV_KEY))) { unsigned char keybit[KEY_MAX / 8 + 1]; if (_ioctl(core->input, EVIOCGBIT(EV_KEY, sizeof(keybit)), &keybit) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGBIT:EV_KEY)"); for (int i = 0; i < KEY_MAX; ++i) { if (!(keybit[i / 8] & (1 << (i % 8)))) continue; if (!uinputd_ioctl_int(IOCTL_IOW, UINPUT_IOCTL_BASE, 101, i)) errx(EXIT_FAILURE, "ioctl(UI_SET_KEYBIT): %d", i); } } if ((evbit & (1 << EV_REL))) { int keybit; if (_ioctl(core->input, EVIOCGBIT(EV_REL, sizeof(keybit)), &keybit) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGBIT:EV_ABS)"); for (int i = 0; i < REL_MAX; ++i) { if (!(keybit & (1 << i))) continue; if (!uinputd_ioctl_int(IOCTL_IOW, UINPUT_IOCTL_BASE, 102, i)) errx(EXIT_FAILURE, "ioctl(UI_SET_RELBIT): %d", i); } } if ((evbit & (1 << EV_ABS))) { int keybit; if (_ioctl(core->input, EVIOCGBIT(EV_ABS, sizeof(keybit)), &keybit) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGBIT:EV_ABS)"); for (int i = 0; i < ABS_MAX; ++i) { if (!(keybit & (1 << i))) continue; if (!uinputd_ioctl_int(IOCTL_IOW, UINPUT_IOCTL_BASE, 103, i)) errx(EXIT_FAILURE, "ioctl(UI_SET_ABSBIT): %d", i); } } struct uinput_user_dev setup = {0}; if (_ioctl(core->input, EVIOCGNAME(sizeof(setup.name)), setup.name) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGNAME)"); for (size_t i = 0; (evbit & (1 << EV_ABS)) && i < ABS_MAX; ++i) { struct input_absinfo info; if (_ioctl(core->input, EVIOCGABS(i), &info) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGABS)"); setup.absmax[i] = info.maximum; setup.absmin[i] = info.minimum; setup.absfuzz[i] = info.fuzz; setup.absflat[i] = info.flat; } if (_ioctl(core->input, EVIOCGID, &setup.id) == -1) err(EXIT_FAILURE, "ioctl(EVIOCGID)"); if (!uinputd_write(sizeof(setup), &setup)) errx(EXIT_FAILURE, "write(uinput_user_dev)"); if (!uinputd_ioctl_other(IOCTL_NONE, UINPUT_IOCTL_BASE, 1, 0, NULL)) errx(EXIT_FAILURE, "ioctl(UI_DEV_CREATE)"); } static void core_on_exit(const int signal, void *core) { assert(core); warnx("core_on_exit (%d)", signal); core_release(core); } static void info(void) { const char *remote = getenv("TCPREMOTEIP"); warnx("version %s", UINPUTD_VERSION); warnx("remote ip: %s", (remote ? remote : "none")); warnx("sizeof(struct packet) is %zu bytes", sizeof(struct packet)); #ifdef NOT_64BIT warnx("sizeof(struct input_event) is %zu bytes", sizeof(struct input_event_64)); #else warnx("sizeof(struct input_event) is %zu bytes", sizeof(struct input_event)); #endif warnx("sizeof(struct uinput_user_dev) is %zu bytes", sizeof(struct uinput_user_dev)); } static void sigterm(const int signal) { warnx("%s", (signal == SIGTERM ? "SIGTERM" : "SIGINT")); exit(EXIT_SUCCESS); } int main(int argc, const char *argv[]) { if (argc < 2) errx(EXIT_FAILURE, "usage: %s input-device [write-fd]", argv[0]); { const struct sigaction action = { .sa_handler = sigterm, }; if (sigaction(SIGTERM, &action, NULL) != 0 || sigaction(SIGINT, &action, NULL) != 0) err(EXIT_FAILURE, "sigaction"); } if (argc > 2) dup2(strtol(argv[2], NULL, 10), STDOUT_FILENO); setvbuf(stdout, NULL, _IONBF, 0); info(); struct core core = {0}; on_exit(core_on_exit, &core); core_init(&core, argv[1]); { struct input_event event; while (read(core.input, &event, sizeof(event)) == sizeof(event)) { #ifdef NOT_64BIT struct input_event_64 ev = { .time.tv_sec = event.time.tv_sec, .time.tv_usec = event.time.tv_usec, .type = event.type, .code = event.code, .value = event.value, }; uinputd_write(sizeof(ev), &ev); #else uinputd_write(sizeof(event), &event); #endif } err(EXIT_FAILURE, "read"); } exit(EXIT_SUCCESS); return EXIT_SUCCESS; }