aboutsummaryrefslogtreecommitdiff
path: root/tools/gsr-global-hotkeys/hotplug.c
blob: ba3ef9c60944c59cd78fd82d0b6a85c94406cb1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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 */
    }
}