Public facing docs and changes added.
This commit is contained in:
375
desk_reminder.c
375
desk_reminder.c
@@ -1,229 +1,194 @@
|
||||
// Simple listener for Alcor AU9540 smartcard reader using libusb-1.0
|
||||
// Device: Bus 001 Device 002: ID 058f:9540 Alcor Micro Corp. AU9540 Smartcard Reader
|
||||
//
|
||||
// This program opens the USB device by VID/PID, finds the first
|
||||
// interrupt IN endpoint, and listens for slot change / card events.
|
||||
// It does not implement full CCID or smartcard protocols; it just
|
||||
// prints raw bytes received from the interrupt endpoint.
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <linux/input.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <libusb-1.0/libusb.h>
|
||||
// Interval in seconds between reminders (default 20 minutes),
|
||||
// can be overridden with -i (supports optional s/m suffix).
|
||||
static unsigned long long interval_seconds = 20ULL * 60ULL;
|
||||
|
||||
// Target USB device: IC Reader (Bus 001 Device 011: ID ffff:0035)
|
||||
#define VENDOR_ID 0xffff
|
||||
#define PRODUCT_ID 0x0035
|
||||
// Shared timestamp of when the "standing" event last occurred.
|
||||
static unsigned long long last_time_standing = 0;
|
||||
|
||||
static volatile int keep_running = 1;
|
||||
|
||||
static void handle_signal(int signum)
|
||||
// Map a subset of key codes (digits and Enter) to characters.
|
||||
static char keycode_to_char(unsigned short code)
|
||||
{
|
||||
(void)signum;
|
||||
keep_running = 0;
|
||||
switch (code) {
|
||||
case KEY_0: return '0';
|
||||
case KEY_1: return '1';
|
||||
case KEY_2: return '2';
|
||||
case KEY_3: return '3';
|
||||
case KEY_4: return '4';
|
||||
case KEY_5: return '5';
|
||||
case KEY_6: return '6';
|
||||
case KEY_7: return '7';
|
||||
case KEY_8: return '8';
|
||||
case KEY_9: return '9';
|
||||
default: return '\0';
|
||||
}
|
||||
}
|
||||
|
||||
struct listener_context {
|
||||
libusb_device_handle *handle;
|
||||
unsigned char endpoint_addr;
|
||||
};
|
||||
|
||||
static void LIBUSB_CALL transfer_callback(struct libusb_transfer *transfer)
|
||||
// Thread that once per second prints how long it has been since
|
||||
// last_time_standing was updated.
|
||||
static void *time_watcher_thread(void *arg)
|
||||
{
|
||||
struct listener_context *ctx = (struct listener_context *)transfer->user_data;
|
||||
(void)arg;
|
||||
for (;;) {
|
||||
sleep(1);
|
||||
unsigned long long now = (unsigned long long)time(NULL);
|
||||
if (last_time_standing != 0) {
|
||||
unsigned long long delta = now - last_time_standing;
|
||||
printf("Seconds since last_time_standing: %llu (interval=%llus)\n",
|
||||
delta, interval_seconds);
|
||||
fflush(stdout);
|
||||
// If we've exceeded the configured interval, lock the screen
|
||||
if (delta >= interval_seconds) {
|
||||
// Use xinput to disable all keyboard-like devices until user stands again.
|
||||
// This pipeline lists input devices, filters keyboards (excluding XTEST),
|
||||
// extracts their IDs, and disables each one.
|
||||
system("xinput --list --short | "
|
||||
"grep -i 'keyboard' | grep -v 'XTEST' | "
|
||||
"sed -E 's/.*id=([0-9]+).*/\\1/' | "
|
||||
"xargs -r -n1 xinput disable");
|
||||
|
||||
if (transfer->status == LIBUSB_TRANSFER_COMPLETED) {
|
||||
printf("[event] %d bytes: ", transfer->actual_length);
|
||||
for (int i = 0; i < transfer->actual_length; ++i) {
|
||||
printf("%02x ", transfer->buffer[i]);
|
||||
}
|
||||
printf("\n");
|
||||
fflush(stdout);
|
||||
|
||||
if (keep_running) {
|
||||
int rc = libusb_submit_transfer(transfer);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to resubmit transfer: %s\n", libusb_error_name(rc));
|
||||
libusb_free_transfer(transfer);
|
||||
system("i3lock -n -i /usr/local/share/lockscreens/standing_gopher.png");
|
||||
}
|
||||
} else {
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
} else if (transfer->status == LIBUSB_TRANSFER_CANCELLED) {
|
||||
libusb_free_transfer(transfer);
|
||||
} else {
|
||||
fprintf(stderr, "Transfer error: status=%d (%s)\n",
|
||||
transfer->status, libusb_error_name(transfer->status));
|
||||
libusb_free_transfer(transfer);
|
||||
}
|
||||
|
||||
(void)ctx; // currently unused, kept for future extensions
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int find_interrupt_in_endpoint(libusb_device_handle *handle,
|
||||
unsigned char *endpoint_addr_out)
|
||||
{
|
||||
libusb_device *dev = libusb_get_device(handle);
|
||||
struct libusb_config_descriptor *config = NULL;
|
||||
int rc = libusb_get_active_config_descriptor(dev, &config);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to get config descriptor: %s\n", libusb_error_name(rc));
|
||||
return -1;
|
||||
int main(int argc, char **argv) {
|
||||
// Replace with the specific event node for your keyboard
|
||||
// Use 'lsusb' and 'cat /proc/bus/input/devices' to find the right one
|
||||
const char *dev = "/dev/input/by-id/usb-IC_Reader_IC_Reader_08FF20171101-event-kbd";
|
||||
struct input_event ev;
|
||||
int fd;
|
||||
|
||||
// Optional: -i <interval>[s|m] to override interval_seconds
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "i:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'i': {
|
||||
char *end = NULL;
|
||||
unsigned long long v = strtoull(optarg, &end, 10);
|
||||
if (!optarg[0] || end == optarg || v == 0) {
|
||||
fprintf(stderr, "Invalid interval '%s' for -i (must start with positive integer)\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
char unit = 'm'; // default unit: minutes
|
||||
if (*end != '\0') {
|
||||
// Allow a single unit character 's' or 'm'
|
||||
if (end[1] != '\0') {
|
||||
fprintf(stderr, "Invalid interval suffix in '%s' (use optional 's' or 'm')\n", optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
unit = (char)tolower((unsigned char)*end);
|
||||
if (unit != 's' && unit != 'm') {
|
||||
fprintf(stderr, "Unknown interval unit '%c' in '%s' (use 's' or 'm')\n", *end, optarg);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (unit == 's') {
|
||||
interval_seconds = v;
|
||||
} else { // 'm'
|
||||
interval_seconds = v * 60ULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
fprintf(stderr, "Usage: %s [-i interval{s|m}]\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
int found = 0;
|
||||
for (int i = 0; i < config->bNumInterfaces && !found; ++i) {
|
||||
const struct libusb_interface *interface = &config->interface[i];
|
||||
for (int j = 0; j < interface->num_altsetting && !found; ++j) {
|
||||
const struct libusb_interface_descriptor *alt = &interface->altsetting[j];
|
||||
for (int k = 0; k < alt->bNumEndpoints && !found; ++k) {
|
||||
const struct libusb_endpoint_descriptor *ep = &alt->endpoint[k];
|
||||
if ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK) == LIBUSB_TRANSFER_TYPE_INTERRUPT &&
|
||||
(ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN) {
|
||||
*endpoint_addr_out = ep->bEndpointAddress;
|
||||
found = 1;
|
||||
fd = open(dev, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
perror("Cannot open device");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Grab the device: This blocks all other apps from seeing these keystrokes
|
||||
if (ioctl(fd, EVIOCGRAB, 1) == -1) {
|
||||
perror("Could not grab device");
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Input blocked from %s. Press Ctrl+C in this terminal to stop.\n", dev);
|
||||
|
||||
// Buffer to accumulate the characters the reader "types"
|
||||
char buf_str[128];
|
||||
size_t buf_len = 0;
|
||||
|
||||
// Initialize last_time_standing and start the watcher thread
|
||||
last_time_standing = (unsigned long long)time(NULL);
|
||||
pthread_t watcher;
|
||||
if (pthread_create(&watcher, NULL, time_watcher_thread, NULL) != 0) {
|
||||
perror("Failed to create watcher thread");
|
||||
ioctl(fd, EVIOCGRAB, 0);
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
pthread_detach(watcher);
|
||||
// Continuous loop to capture events
|
||||
while (1) {
|
||||
ssize_t n = read(fd, &ev, sizeof(ev));
|
||||
if (n == (ssize_t)-1) {
|
||||
perror("Read error");
|
||||
break;
|
||||
} else if (n != sizeof(ev)) {
|
||||
continue;
|
||||
}
|
||||
// Process only key events (type 1)
|
||||
if (ev.type == EV_KEY) {
|
||||
if (ev.value == 1) { // key press
|
||||
if (ev.code == KEY_ENTER || ev.code == KEY_KPENTER) {
|
||||
// End of the "typed" sequence: print the accumulated string
|
||||
if (buf_len > 0) {
|
||||
buf_str[buf_len] = '\0';
|
||||
long long val = strtoll(buf_str, NULL, 10);
|
||||
// Get the SHA-256 hash of the value
|
||||
unsigned char hash[SHA256_DIGEST_LENGTH];
|
||||
SHA256((unsigned char*)buf_str, buf_len, hash);
|
||||
printf("SHA-256: ");
|
||||
unsigned long long hash_val = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
hash_val = (hash_val << 8) | hash[i];
|
||||
}
|
||||
printf(" (as number: %llu)", hash_val);
|
||||
printf("\n");
|
||||
printf("Typed: %s (as number: %lld)\n", buf_str, val);
|
||||
buf_len = 0;
|
||||
if (hash_val == 4798061567229363866L) {
|
||||
last_time_standing = (unsigned long long)time(NULL);
|
||||
printf("Desk reminder timer reset at %llu seconds since epoch.\n", last_time_standing);
|
||||
system("pkill i3lock");
|
||||
system("xinput --list --short | "
|
||||
"grep -i 'keyboard' | grep -v 'XTEST' | "
|
||||
"sed -E 's/.*id=([0-9]+).*/\\1/' | "
|
||||
"xargs -r -n1 xinput enable");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
char c = keycode_to_char(ev.code);
|
||||
if (c != '\0' && buf_len + 1 < sizeof(buf_str))
|
||||
buf_str[buf_len++] = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libusb_free_config_descriptor(config);
|
||||
|
||||
if (!found) {
|
||||
fprintf(stderr, "No interrupt IN endpoint found on device.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Release the grab before exiting
|
||||
ioctl(fd, EVIOCGRAB, 0);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
libusb_context *ctx = NULL;
|
||||
libusb_device_handle *handle = NULL;
|
||||
int rc;
|
||||
|
||||
signal(SIGINT, handle_signal);
|
||||
signal(SIGTERM, handle_signal);
|
||||
|
||||
rc = libusb_init(&ctx);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "libusb_init failed: %s\n", libusb_error_name(rc));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
libusb_set_option(ctx, LIBUSB_OPTION_LOG_LEVEL, LIBUSB_LOG_LEVEL_INFO);
|
||||
|
||||
handle = libusb_open_device_with_vid_pid(ctx, VENDOR_ID, PRODUCT_ID);
|
||||
if (!handle) {
|
||||
fprintf(stderr, "Unable to open device %04x:%04x. Are you running as root or with correct udev rules?\n",
|
||||
VENDOR_ID, PRODUCT_ID);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
libusb_device *dev = libusb_get_device(handle);
|
||||
uint8_t bus = libusb_get_bus_number(dev);
|
||||
uint8_t addr = libusb_get_device_address(dev);
|
||||
printf("Opened device on bus %03u device %03u (VID:PID %04x:%04x)\n",
|
||||
bus, addr, VENDOR_ID, PRODUCT_ID);
|
||||
|
||||
// Try to claim interface 0 (most CCID readers use it). Detach kernel driver if needed.
|
||||
int iface = 0;
|
||||
if (libusb_kernel_driver_active(handle, iface) == 1) {
|
||||
printf("Kernel driver active on interface %d, detaching...\n", iface);
|
||||
rc = libusb_detach_kernel_driver(handle, iface);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to detach kernel driver: %s\n", libusb_error_name(rc));
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
rc = libusb_claim_interface(handle, iface);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to claim interface %d: %s\n", iface, libusb_error_name(rc));
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
unsigned char endpoint_addr = 0;
|
||||
if (find_interrupt_in_endpoint(handle, &endpoint_addr) != 0) {
|
||||
libusb_release_interface(handle, iface);
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("Listening on interrupt IN endpoint 0x%02x. Press Ctrl+C to stop.\n",
|
||||
endpoint_addr);
|
||||
|
||||
const int buf_size = 64; // typical interrupt packet size
|
||||
unsigned char *buffer = (unsigned char *)malloc(buf_size);
|
||||
if (!buffer) {
|
||||
fprintf(stderr, "Failed to allocate buffer.\n");
|
||||
libusb_release_interface(handle, iface);
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct libusb_transfer *transfer = libusb_alloc_transfer(0);
|
||||
if (!transfer) {
|
||||
fprintf(stderr, "Failed to allocate transfer.\n");
|
||||
free(buffer);
|
||||
libusb_release_interface(handle, iface);
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
struct listener_context listener_ctx;
|
||||
listener_ctx.handle = handle;
|
||||
listener_ctx.endpoint_addr = endpoint_addr;
|
||||
|
||||
libusb_fill_interrupt_transfer(transfer,
|
||||
handle,
|
||||
endpoint_addr,
|
||||
buffer,
|
||||
buf_size,
|
||||
transfer_callback,
|
||||
&listener_ctx,
|
||||
0); // timeout 0 = infinite
|
||||
|
||||
rc = libusb_submit_transfer(transfer);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to submit transfer: %s\n", libusb_error_name(rc));
|
||||
libusb_free_transfer(transfer);
|
||||
free(buffer);
|
||||
libusb_release_interface(handle, iface);
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
while (keep_running) {
|
||||
rc = libusb_handle_events(ctx);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "libusb_handle_events error: %s\n", libusb_error_name(rc));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up: ask libusb to cancel the transfer; the callback is
|
||||
// responsible for freeing the transfer and buffer when cancellation
|
||||
// completes or if an error occurred.
|
||||
libusb_cancel_transfer(transfer);
|
||||
|
||||
libusb_release_interface(handle, iface);
|
||||
libusb_close(handle);
|
||||
libusb_exit(ctx);
|
||||
|
||||
printf("Exiting.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user