diff options
Diffstat (limited to 'tools/gsr-global-hotkeys/hotplug.c')
-rw-r--r-- | tools/gsr-global-hotkeys/hotplug.c | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/tools/gsr-global-hotkeys/hotplug.c b/tools/gsr-global-hotkeys/hotplug.c new file mode 100644 index 0000000..6c82929 --- /dev/null +++ b/tools/gsr-global-hotkeys/hotplug.c @@ -0,0 +1,76 @@ +#include "hotplug.h" + +/* C stdlib */ +#include <string.h> + +/* POSIX */ +#include <unistd.h> +#include <sys/socket.h> + +/* LINUX */ +#include <linux/types.h> +#include <linux/netlink.h> + +bool hotplug_event_init(hotplug_event *self) { + memset(self, 0, sizeof(*self)); + + struct sockaddr_nl nls = { + .nl_family = AF_NETLINK, + .nl_pid = getpid(), + .nl_groups = -1 + }; + + const int fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); + if(fd == -1) + return false; /* Not root user */ + + if(bind(fd, (void *)&nls, sizeof(struct sockaddr_nl))) { + close(fd); + return false; + } + + self->fd = fd; + return true; +} + +void hotplug_event_deinit(hotplug_event *self) { + if(self->fd > 0) { + close(self->fd); + self->fd = -1; + } +} + +int hotplug_event_steal_fd(hotplug_event *self) { + const int fd = self->fd; + self->fd = -1; + return fd; +} + +/* TODO: This assumes SUBSYSTEM= is output before DEVNAME=, is that always true? */ +static void hotplug_event_parse_netlink_data(hotplug_event *self, const char *line, hotplug_device_added_callback callback, void *userdata) { + const char *at_symbol = strchr(line, '@'); + if(at_symbol) { + self->event_is_add = strncmp(line, "add@", 4) == 0; + self->subsystem_is_input = false; + } else if(self->event_is_add) { + if(strcmp(line, "SUBSYSTEM=input") == 0) + self->subsystem_is_input = true; + + if(self->subsystem_is_input && strncmp(line, "DEVNAME=", 8) == 0) + callback(line+8, userdata); + } +} + +/* Netlink uevent structure is documented here: https://web.archive.org/web/20160127215232/https://www.kernel.org/doc/pending/hotplug.txt */ +void hotplug_event_process_event_data(hotplug_event *self, int fd, hotplug_device_added_callback callback, void *userdata) { + const int bytes_read = read(fd, self->event_data, sizeof(self->event_data)); + int data_index = 0; + if(bytes_read <= 0) + return; + + /* Hotplug data ends with a newline and a null terminator */ + while(data_index < bytes_read) { + hotplug_event_parse_netlink_data(self, self->event_data + data_index, callback, userdata); + data_index += strlen(self->event_data + data_index) + 1; /* Skip null terminator as well */ + } +} |