aboutsummaryrefslogtreecommitdiff
path: root/src/Hotplug.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Hotplug.cpp')
-rw-r--r--src/Hotplug.cpp81
1 files changed, 81 insertions, 0 deletions
diff --git a/src/Hotplug.cpp b/src/Hotplug.cpp
new file mode 100644
index 0000000..84ed5bb
--- /dev/null
+++ b/src/Hotplug.cpp
@@ -0,0 +1,81 @@
+#include "../include/Hotplug.hpp"
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <linux/types.h>
+#include <linux/netlink.h>
+
+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;
+ }
+ }
+ }
+}