#include "../include/Hotplug.hpp" #include #include #include #include #include namespace gsr { Hotplug::~Hotplug() { if(fd > 0) close(fd); } bool Hotplug::start() { if(started) return false; struct sockaddr_nl nls = { AF_NETLINK, 0, (unsigned int)getpid(), (unsigned int)-1 }; fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); if(fd == -1) return false; /* Not root user */ if(bind(fd, (const struct sockaddr*)&nls, sizeof(struct sockaddr_nl))) { close(fd); fd = -1; return false; } started = true; return true; } int Hotplug::steal_fd() { const int val = fd; fd = -1; return val; } void Hotplug::process_event_data(int fd, const HotplugEventCallback &callback) { const int bytes_read = read(fd, event_data, sizeof(event_data)); if(bytes_read <= 0) return; /* Hotplug data ends with a newline and a null terminator */ int data_index = 0; while(data_index < bytes_read) { parse_netlink_data(event_data + data_index, callback); data_index += strlen(event_data + data_index) + 1; /* Skip null terminator as well */ } } /* TODO: This assumes SUBSYSTEM= is output before DEVNAME=, is that always true? */ void Hotplug::parse_netlink_data(const char *line, const HotplugEventCallback &callback) { const char *at_symbol = strchr(line, '@'); if(at_symbol) { event_is_add = strncmp(line, "add@", 4) == 0; event_is_remove = strncmp(line, "remove@", 7) == 0; subsystem_is_input = false; } else if(event_is_add || event_is_remove) { if(strcmp(line, "SUBSYSTEM=input") == 0) subsystem_is_input = true; if(subsystem_is_input && strncmp(line, "DEVNAME=", 8) == 0) { if(event_is_add) callback(HotplugAction::ADD, line+8); else if(event_is_remove) callback(HotplugAction::REMOVE, line+8); event_is_add = false; event_is_remove = false; } } } }