Merge pull request #3 from illiliti/hotplugging_new
implement hotplugging support
This commit is contained in:
48
README.md
48
README.md
@@ -1,15 +1,17 @@
|
||||
libudev-zero
|
||||
============
|
||||
|
||||
Drop-in replacement for libudev intended to work without daemon
|
||||
Drop-in replacement for libudev enables you to use whatever
|
||||
device manager you like without worrying about udev dependency at all!
|
||||
|
||||
What Works
|
||||
----------
|
||||
|
||||
* [x] xorg-server
|
||||
* [x] libinput
|
||||
* [x] wlroots
|
||||
* [x] weston
|
||||
* [ ] libusb - doesn't work for now. temporary workaround is compiling libusb with --disable-udev
|
||||
* [x] libusb
|
||||
* [x] kwin - [fix](https://github.com/dilyn-corner/KISS-kde/commit/0cc72748e46f859a0fced55b0c3fcc1dd9586a38)
|
||||
* [ ] ???
|
||||
|
||||
@@ -19,6 +21,7 @@ Dependencies
|
||||
* C99 compiler (build time)
|
||||
* POSIX make (build time)
|
||||
* POSIX & XSI libc
|
||||
* epoll & inotify
|
||||
* Linux >= 2.6.39
|
||||
|
||||
Installation
|
||||
@@ -31,12 +34,51 @@ make PREFIX=/usr install # will overwrite existing udev libraries if any
|
||||
# here we go !
|
||||
```
|
||||
|
||||
Hotplugging
|
||||
-----------
|
||||
|
||||
There is no complicated or overengineered way to use hotplugging. Everything is
|
||||
portable as much as possible. To use hotplugging the only thing you need is
|
||||
uevent's receiver (device manager, busybox `uevent`, CONFIG_UEVENT_HELPER, ...).
|
||||
I will describe only mdev and CONFIG_UEVENT_HELPER because their usage is very basic.
|
||||
For busybox `uevent` you need to write your own parser which is kinda ... complex.
|
||||
|
||||
UDEV_MONITOR_DIR is arbitrary directory where uevent files stored.
|
||||
Default is `/tmp/.libudev-zero`. You can change it at build time by appending
|
||||
`-DUDEV_MONITOR_DIR=<dir>` to CFLAGS. I don't recommend setting UDEV_MONITOR_DIR
|
||||
to regular fs (i.e non-tmpfs) because unneeded files aren't automatically discarded
|
||||
after reboot or termination (yet).
|
||||
|
||||
* mdev
|
||||
|
||||
- merge [mdev.conf](contrib/mdev.conf) with your mdev.conf
|
||||
- restart mdev daemon
|
||||
|
||||
* CONFIG_UEVENT_HELPER
|
||||
|
||||
- ensure that CONFIG_UEVENT_HELPER enabled in kernel
|
||||
- add full path of [helper.sh](contrib/helper.sh) (must be executable) or
|
||||
[helper.c](contrib/helper.c) (compile it first) to /proc/sys/kernel/hotplug
|
||||
|
||||
example:
|
||||
```sh
|
||||
echo /full/path/to/helper > /proc/sys/kernel/hotplug # will use default UDEV_MONITOR_DIR
|
||||
OR
|
||||
echo "/full/path/to/helper <dir>" > /proc/sys/kernel/hotplug # change <dir> to your UDEV_MONITOR_DIR
|
||||
```
|
||||
|
||||
* run application which uses hotplugging (e.g xorg-server)
|
||||
* unplug and plug something to test working capacity
|
||||
|
||||
That's all! If you realized that this doesn't work for you,
|
||||
you can always open an issue and describe your bug.
|
||||
|
||||
TODO
|
||||
----
|
||||
|
||||
* [x] speed up performance
|
||||
* [x] extend devices support
|
||||
* [ ] implement hotplugging support
|
||||
* [x] implement hotplugging support
|
||||
|
||||
Donate
|
||||
------
|
||||
|
||||
63
contrib/helper.c
Normal file
63
contrib/helper.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* this helper pretty similar to helper.sh, but it
|
||||
* doesn't write unrelated variables to file (e.g PWD or PATH)
|
||||
*
|
||||
* build:
|
||||
* cc helper.c -o helper
|
||||
*
|
||||
* usage:
|
||||
* echo /full/path/to/helper > /proc/sys/kernel/hotplug
|
||||
* echo "/full/path/to/helper UDEV_MONITOR_DIR" > /proc/sys/kernel/hotplug
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
extern char **environ;
|
||||
char path[PATH_MAX];
|
||||
char *dir;
|
||||
int fd, i;
|
||||
|
||||
switch (argc) {
|
||||
case 1:
|
||||
dir = "/tmp/.libudev-zero";
|
||||
break;
|
||||
case 2:
|
||||
dir = argv[1];
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "usage: %s [dir]\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/uevent.XXXXXX", dir);
|
||||
fd = mkstemp(path);
|
||||
|
||||
if (fd == -1) {
|
||||
perror("mkstemp");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (i = 0; environ[i]; i++) {
|
||||
if (strncmp(environ[i], "PATH", 4) == 0 ||
|
||||
strncmp(environ[i], "HOME", 4) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (write(fd, environ[i], strlen(environ[i])) == -1 ||
|
||||
write(fd, "\n", 1) == -1) {
|
||||
perror("write");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
13
contrib/helper.sh
Normal file
13
contrib/helper.sh
Normal file
@@ -0,0 +1,13 @@
|
||||
#!/bin/sh -f
|
||||
#
|
||||
# this helper required for CONFIG_UEVENT_HELPER aka /proc/sys/kernel/hotplug
|
||||
# for mdev you can use simple one-liner, refer to mdev.conf for more info
|
||||
#
|
||||
# usage:
|
||||
# echo /full/path/to/helper.sh > /proc/sys/kernel/hotplug
|
||||
# echo "/full/path/to/helper.sh UDEV_MONITOR_DIR" > /proc/sys/kernel/hotplug
|
||||
#
|
||||
|
||||
# NOTE: writing variables to file (e.g PWD or PATH)
|
||||
# that are not related to uevent properties is harmless
|
||||
env > "${1:-/tmp/.libudev-zero}/uevent.$$"
|
||||
13
contrib/mdev.conf
Normal file
13
contrib/mdev.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
#
|
||||
# example basic mdev config representing libudev-zero usage
|
||||
# uncomment needed rules
|
||||
#
|
||||
# NOTE: you must change "/tmp/.libudev-zero" if you use non-default UDEV_MONITOR_DIR
|
||||
# NOTE: mdev can only handle ADD and/or REMOVE events
|
||||
|
||||
# will handle all uevents
|
||||
#-.* root:root 660 *env > /tmp/.libudev-zero/uevent.$$
|
||||
|
||||
# will handle only drm and input uevents (recommended)
|
||||
#SUBSYSTEM=drm;.* root:video 660 *env > /tmp/.libudev-zero/uevent.$$
|
||||
#SUBSYSTEM=input;.* root:input 660 *env > /tmp/.libudev-zero/uevent.$$
|
||||
3
udev.h
3
udev.h
@@ -96,6 +96,9 @@ struct udev_hwdb *udev_hwdb_ref(struct udev_hwdb *hwdb);
|
||||
struct udev_hwdb *udev_hwdb_unref(struct udev_hwdb *hwdb);
|
||||
struct udev_list_entry *udev_hwdb_get_properties_list_entry(struct udev_hwdb *hwdb, const char *modalias, unsigned int flags);
|
||||
|
||||
// this is "libudev-zero" extension. do not use if portability is concern
|
||||
struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
109
udev_device.c
109
udev_device.c
@@ -9,7 +9,7 @@
|
||||
#include "udev.h"
|
||||
#include "udev_list.h"
|
||||
|
||||
enum { BITS_SIZE = 96 };
|
||||
enum { BITS_MAX = 96 };
|
||||
|
||||
struct udev_device {
|
||||
struct udev_list_entry properties;
|
||||
@@ -148,7 +148,7 @@ int udev_device_get_is_initialized(struct udev_device *udev_device)
|
||||
|
||||
const char *udev_device_get_action(struct udev_device *udev_device)
|
||||
{
|
||||
return NULL;
|
||||
return udev_device_get_property_value(udev_device, "ACTION");
|
||||
}
|
||||
|
||||
int udev_device_has_tag(struct udev_device *udev_device, const char *tag)
|
||||
@@ -193,7 +193,9 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
|
||||
struct udev_list_entry *list_entry;
|
||||
char data[BUFSIZ], path[PATH_MAX];
|
||||
struct stat st;
|
||||
size_t len;
|
||||
FILE *file;
|
||||
char *pos;
|
||||
|
||||
if (!udev_device || !sysattr) {
|
||||
return NULL;
|
||||
@@ -217,13 +219,21 @@ const char *udev_device_get_sysattr_value(struct udev_device *udev_device, const
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (fread(data, 1, sizeof(data), file) != sizeof(data) && ferror(file)) {
|
||||
len = fread(data, 1, sizeof(data), file);
|
||||
|
||||
if (len != sizeof(data) && ferror(file)) {
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((pos = memchr(data, '\n', len))) {
|
||||
*pos = '\0';
|
||||
}
|
||||
else {
|
||||
data[len] = '\0';
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
data[strcspn(data, "\n")] = '\0';
|
||||
list_entry = udev_list_entry_add(&udev_device->sysattrs, sysattr, data, 0);
|
||||
return udev_list_entry_get_value(list_entry);
|
||||
}
|
||||
@@ -282,7 +292,7 @@ static char *udev_device_read_symlink(struct udev_device *udev_device, const cha
|
||||
static void udev_device_set_properties_from_uevent(struct udev_device *udev_device)
|
||||
{
|
||||
char line[LINE_MAX], path[PATH_MAX];
|
||||
char *key, *val, devnode[PATH_MAX];
|
||||
char *pos, devnode[PATH_MAX];
|
||||
FILE *file;
|
||||
|
||||
snprintf(path, sizeof(path), "%s/uevent", udev_device_get_syspath(udev_device));
|
||||
@@ -299,12 +309,9 @@ static void udev_device_set_properties_from_uevent(struct udev_device *udev_devi
|
||||
snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0);
|
||||
}
|
||||
else if (strchr(line, '=')) {
|
||||
val = strchr(line, '=') + 1;
|
||||
key = line;
|
||||
key[strcspn(key, "=")] = '\0';
|
||||
|
||||
udev_list_entry_add(&udev_device->properties, key, val, 1);
|
||||
else if ((pos = strchr(line, '='))) {
|
||||
*pos = '\0';
|
||||
udev_list_entry_add(&udev_device->properties, line, pos + 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +335,7 @@ static int populate_bit(unsigned long *arr, const char *val)
|
||||
|
||||
bit = strtok_r(bits, " ", &save);
|
||||
|
||||
for (i = 0; bit && i < BITS_SIZE; i++) {
|
||||
for (i = 0; bit && i < BITS_MAX; i++) {
|
||||
arr[i] = strtoul(bit, NULL, 16);
|
||||
bit = strtok_r(NULL, " ", &save);
|
||||
}
|
||||
@@ -357,11 +364,11 @@ static int find_bit(unsigned long *arr, int cnt, int bit)
|
||||
static void udev_device_set_properties_from_evdev(struct udev_device *udev_device)
|
||||
{
|
||||
int ev_cnt, rel_cnt, key_cnt, abs_cnt, prop_cnt;
|
||||
unsigned long prop_bits[BITS_SIZE] = {0};
|
||||
unsigned long abs_bits[BITS_SIZE] = {0};
|
||||
unsigned long rel_bits[BITS_SIZE] = {0};
|
||||
unsigned long key_bits[BITS_SIZE] = {0};
|
||||
unsigned long ev_bits[BITS_SIZE] = {0};
|
||||
unsigned long prop_bits[BITS_MAX] = {0};
|
||||
unsigned long abs_bits[BITS_MAX] = {0};
|
||||
unsigned long rel_bits[BITS_MAX] = {0};
|
||||
unsigned long key_bits[BITS_MAX] = {0};
|
||||
unsigned long ev_bits[BITS_MAX] = {0};
|
||||
struct udev_device *parent;
|
||||
const char *subsystem;
|
||||
|
||||
@@ -371,7 +378,7 @@ static void udev_device_set_properties_from_evdev(struct udev_device *udev_devic
|
||||
return;
|
||||
}
|
||||
|
||||
parent = udev_device_get_parent_with_subsystem_devtype(udev_device, "input", NULL);
|
||||
parent = udev_device;
|
||||
|
||||
while (1) {
|
||||
if (!parent) {
|
||||
@@ -548,6 +555,72 @@ struct udev_device *udev_device_new_from_subsystem_sysname(struct udev *udev, co
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct udev_device *udev_device_new_from_file(struct udev *udev, const char *path)
|
||||
{
|
||||
char line[LINE_MAX], syspath[PATH_MAX], devnode[PATH_MAX];
|
||||
struct udev_device *udev_device;
|
||||
char *pos, *sysname;
|
||||
FILE *file;
|
||||
int i;
|
||||
|
||||
udev_device = calloc(1, sizeof(struct udev_device));
|
||||
|
||||
if (!udev_device) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_device->udev = udev;
|
||||
udev_device->refcount = 1;
|
||||
udev_device->parent = NULL;
|
||||
|
||||
udev_list_entry_init(&udev_device->properties);
|
||||
udev_list_entry_init(&udev_device->sysattrs);
|
||||
|
||||
file = fopen(path, "r");
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
|
||||
if (strncmp(line, "DEVPATH", 7) == 0) {
|
||||
snprintf(syspath, sizeof(syspath), "/sys%s", line + 8);
|
||||
udev_list_entry_add(&udev_device->properties, "SYSPATH", syspath, 0);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVPATH", line + 8, 0);
|
||||
|
||||
sysname = strrchr(syspath, '/') + 1;
|
||||
udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0);
|
||||
|
||||
for (i = 0; sysname[i] != '\0'; i++) {
|
||||
if (sysname[i] >= '0' && sysname[i] <= '9') {
|
||||
udev_list_entry_add(&udev_device->properties, "SYSNUM", sysname + i, 0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strncmp(line, "DEVNAME", 7) == 0) {
|
||||
snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0);
|
||||
}
|
||||
else if ((pos = strchr(line, '='))) {
|
||||
*pos = '\0';
|
||||
udev_list_entry_add(&udev_device->properties, line, pos + 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
if (!udev_device_get_syspath(udev_device)) {
|
||||
udev_device_unref(udev_device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_device_set_properties_from_evdev(udev_device);
|
||||
return udev_device;
|
||||
}
|
||||
|
||||
struct udev_device *udev_device_new_from_device_id(struct udev *udev, const char *id)
|
||||
{
|
||||
// XXX NOT IMPLEMENTED
|
||||
|
||||
235
udev_monitor.c
235
udev_monitor.c
@@ -1,33 +1,180 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#include "udev.h"
|
||||
#include "udev_list.h"
|
||||
|
||||
#ifndef UDEV_MONITOR_DIR
|
||||
#define UDEV_MONITOR_DIR "/tmp/.libudev-zero"
|
||||
#endif
|
||||
|
||||
enum { THREADS_MAX = 5 };
|
||||
|
||||
struct udev_monitor {
|
||||
struct udev_list_entry subsystem_match;
|
||||
struct udev_list_entry devtype_match;
|
||||
pthread_t thread[THREADS_MAX];
|
||||
pthread_barrier_t barrier;
|
||||
struct udev *udev;
|
||||
int refcount;
|
||||
int fd[2];
|
||||
int sfd[2];
|
||||
int ifd;
|
||||
int efd;
|
||||
};
|
||||
|
||||
static int udev_monitor_filter_devtype(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
|
||||
{
|
||||
struct udev_list_entry *list_entry;
|
||||
const char *devtype;
|
||||
|
||||
devtype = udev_device_get_devtype(udev_device);
|
||||
list_entry = udev_list_entry_get_next(&udev_monitor->devtype_match);
|
||||
|
||||
if (!list_entry) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!devtype) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (list_entry) {
|
||||
if (strcmp(devtype, udev_list_entry_get_name(list_entry)) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
list_entry = udev_list_entry_get_next(list_entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int udev_monitor_filter_subsystem(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
|
||||
{
|
||||
struct udev_list_entry *list_entry;
|
||||
const char *subsystem;
|
||||
|
||||
subsystem = udev_device_get_subsystem(udev_device);
|
||||
list_entry = udev_list_entry_get_next(&udev_monitor->subsystem_match);
|
||||
|
||||
if (!list_entry) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!subsystem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (list_entry) {
|
||||
if (strcmp(subsystem, udev_list_entry_get_name(list_entry)) == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
list_entry = udev_list_entry_get_next(list_entry);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
return (void *)1;
|
||||
char file[PATH_MAX + sizeof(UDEV_MONITOR_DIR)], data[4096];
|
||||
struct udev_device *udev_device;
|
||||
|
||||
if (recv(udev_monitor->sfd[0], data, sizeof(data), 0) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
snprintf(file, sizeof(file), UDEV_MONITOR_DIR "/%s", data);
|
||||
udev_device = udev_device_new_from_file(udev_monitor->udev, file);
|
||||
|
||||
if (!udev_device) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!udev_monitor_filter_subsystem(udev_monitor, udev_device) ||
|
||||
!udev_monitor_filter_devtype(udev_monitor, udev_device)) {
|
||||
udev_device_unref(udev_device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return udev_device;
|
||||
}
|
||||
|
||||
static void *udev_monitor_handle_event(void *ptr)
|
||||
{
|
||||
struct udev_monitor *udev_monitor = ptr;
|
||||
struct inotify_event *event;
|
||||
struct epoll_event epoll;
|
||||
char data[4096];
|
||||
sigset_t mask;
|
||||
ssize_t len;
|
||||
int i;
|
||||
|
||||
sigemptyset(&mask);
|
||||
|
||||
while (epoll_pwait(udev_monitor->efd, &epoll, 1, -1, &mask) != -1) {
|
||||
len = read(udev_monitor->ifd, data, sizeof(data));
|
||||
|
||||
if (len == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) {
|
||||
event = (struct inotify_event *)&data[i];
|
||||
|
||||
// TODO user deleted directory, what should we do ?
|
||||
if (event->mask & IN_IGNORED) {
|
||||
break;
|
||||
}
|
||||
|
||||
send(udev_monitor->sfd[1], event->name, event->len, 0);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&udev_monitor->barrier);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
int i;
|
||||
|
||||
if (!udev_monitor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_barrier_init(&udev_monitor->barrier, NULL, THREADS_MAX + 1);
|
||||
|
||||
for (i = 0; i < THREADS_MAX; i++) {
|
||||
pthread_create(&udev_monitor->thread[i], &attr, udev_monitor_handle_event, udev_monitor);
|
||||
}
|
||||
|
||||
pthread_attr_destroy(&attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
|
||||
{
|
||||
// XXX NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
return udev_monitor ? udev_monitor->fd[0] : -1;
|
||||
return udev_monitor ? udev_monitor->sfd[0] : -1;
|
||||
}
|
||||
|
||||
struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
|
||||
@@ -37,28 +184,42 @@ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
|
||||
|
||||
int udev_monitor_filter_update(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
// XXX NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_monitor_filter_remove(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
// XXX NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype)
|
||||
{
|
||||
if (!udev_monitor || !subsystem) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
udev_list_entry_add(&udev_monitor->subsystem_match, subsystem, NULL, 0);
|
||||
|
||||
if (devtype) {
|
||||
udev_list_entry_add(&udev_monitor->devtype_match, devtype, NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag)
|
||||
{
|
||||
// XXX NOT IMPLEMENTED
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name)
|
||||
{
|
||||
struct udev_monitor *udev_monitor;
|
||||
int i;
|
||||
struct epoll_event epoll;
|
||||
struct stat st;
|
||||
|
||||
if (!udev || !name) {
|
||||
return NULL;
|
||||
@@ -70,14 +231,54 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO better no-op, HELP WANTED !
|
||||
if (pipe(udev_monitor->fd) == -1) {
|
||||
if (lstat(UDEV_MONITOR_DIR, &st) != 0) {
|
||||
if (mkdir(UDEV_MONITOR_DIR, 0) == -1 ||
|
||||
chmod(UDEV_MONITOR_DIR, 0777) == -1) {
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else if (!S_ISDIR(st.st_mode)) {
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
fcntl(udev_monitor->fd[i], F_SETFD, FD_CLOEXEC | O_NONBLOCK);
|
||||
udev_monitor->efd = epoll_create1(EPOLL_CLOEXEC);
|
||||
|
||||
if (udev_monitor->efd == -1) {
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
|
||||
|
||||
if (udev_monitor->ifd == -1) {
|
||||
close(udev_monitor->efd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (inotify_add_watch(udev_monitor->ifd, UDEV_MONITOR_DIR, IN_CLOSE_WRITE | IN_EXCL_UNLINK) == -1) {
|
||||
close(udev_monitor->ifd);
|
||||
close(udev_monitor->efd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
epoll.events = EPOLLIN | EPOLLET;
|
||||
|
||||
if (epoll_ctl(udev_monitor->efd, EPOLL_CTL_ADD, udev_monitor->ifd, &epoll) == -1) {
|
||||
close(udev_monitor->ifd);
|
||||
close(udev_monitor->efd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) {
|
||||
close(udev_monitor->ifd);
|
||||
close(udev_monitor->efd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_monitor->refcount = 1;
|
||||
@@ -107,10 +308,22 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
close(udev_monitor->fd[i]);
|
||||
udev_list_entry_free_all(&udev_monitor->subsystem_match);
|
||||
udev_list_entry_free_all(&udev_monitor->devtype_match);
|
||||
|
||||
for (i = 0; i < THREADS_MAX; i++) {
|
||||
pthread_cancel(udev_monitor->thread[i]);
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&udev_monitor->barrier);
|
||||
pthread_barrier_destroy(&udev_monitor->barrier);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
close(udev_monitor->sfd[i]);
|
||||
}
|
||||
|
||||
close(udev_monitor->ifd);
|
||||
close(udev_monitor->efd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user