implement file-less hotplugging
This commit is contained in:
38
README.md
38
README.md
@@ -1,6 +1,6 @@
|
||||
# libudev-zero
|
||||
|
||||
Drop-in replacement for `libudev` that intended to work with any device manager
|
||||
Drop-in replacement for `libudev` intended to work with any device manager
|
||||
|
||||
## Why?
|
||||
|
||||
@@ -11,15 +11,11 @@ to rewrite[0] this crappy library because `libinput` hard-depends on `udev`.
|
||||
Without `libinput` you can't use `wayland` and many other cool stuff.
|
||||
|
||||
Michael Forney (author of `cproc`, `samurai`, Oasis Linux, ...) decided to
|
||||
fork[1] `libinput` and remove the hard dependency on `udev`. Is this a
|
||||
solution? Yes. Is this a complete solution? No. This fork has a lot of
|
||||
disadvantages like requiring patching applications to use `libinput_netlink`
|
||||
fork[1] `libinput` and remove the hard dependency on `udev`. However, this
|
||||
fork has a drawback that requires patching applications to use `libinput_netlink`
|
||||
instead of the `libinput_udev` API in order to use the automatic detection of
|
||||
input devices and hotplugging. Static configuration is also required for
|
||||
anything other than input devices (e.g drm devices). Moreover hotplugging is
|
||||
vulnerable to race conditions when `libinput` handles the `uevent` faster than
|
||||
the device manager which can lead to file permission issues. `libudev-zero`
|
||||
prevents these race conditions by design.
|
||||
input devices and hotplugging. Static configuration is also required for anything
|
||||
other than input devices (e.g drm devices).
|
||||
|
||||
Thankfully `udev` has stable API and hopefully no changes will be made to it
|
||||
the future. On this basis I decided to create this clean-room implementation
|
||||
@@ -45,7 +41,6 @@ of `libudev` which can be used with any or without a device manager.
|
||||
* C99 compiler (build time)
|
||||
* POSIX make (build time)
|
||||
* POSIX & XSI libc
|
||||
* inotify & eventfd
|
||||
* Linux >= 2.6.39
|
||||
|
||||
## Installation
|
||||
@@ -60,18 +55,21 @@ make PREFIX=/usr install
|
||||
Note that hotplugging support is fully optional. You can skip
|
||||
this step if you don't have a need for the hotplugging capability.
|
||||
|
||||
In order to use hotplugging, you need to configure device manager to send
|
||||
`uevent` messages to `UDEV_MONITOR_DIR`. `UDEV_MONITOR_DIR` is arbitrary
|
||||
shared directory used by `libudev-zero` to receive `uevent` messages. By
|
||||
default, `UDEV_MONITOR_DIR` points to `/tmp/.libudev-zero`. You can change
|
||||
that directory at compile time by passing `-DUDEV_MONITOR_DIR=<dir>` to
|
||||
`CFLAGS` or at runtime by setting `UDEV_MONITOR_DIR` environment variable.
|
||||
If you're using mdev-like device manager, refer to [mdev.conf](contrib/mdev.conf)
|
||||
for config example.
|
||||
|
||||
Keep in mind that already processed `uevent` messages wouldn't be automatically
|
||||
purged. You can set `UDEV_MONITOR_DIR` to directory on tmpfs to purge them on
|
||||
reboot/shutdown.
|
||||
If you're using other device manager, you need to configure it to rebroadcast
|
||||
kernel uevents. You can do this by either patching(see below) device manager
|
||||
or simply executing [helper.c](contrib/helper.c) for each uevent.
|
||||
|
||||
Refer to [contrib](contrib) for usage examples and configs.
|
||||
If you're developing your own device manager, you need to rebroadcast kernel
|
||||
uevents to `0x4` netlink group of `NETLINK_KOBJECT_UEVENT`. This is required
|
||||
because libudev-zero can't simply listen to kernel uevents due to potential
|
||||
race conditions. Refer(but don't copy blindly) to [helper.c](contrib/helper.c)
|
||||
for example how it could be implemented in C.
|
||||
|
||||
Don't hesitate to ask me everything you don't understand. I'm usually hanging
|
||||
around in #kisslinux at libera.chat, but you can also email me or open an issue here.
|
||||
|
||||
## Donate
|
||||
|
||||
|
||||
@@ -15,50 +15,27 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
*
|
||||
* NOTE: you don't need this if you have mdev/mdevd, refer to mdev.conf
|
||||
* NOTE: you need this if you want to use bare-bones CONFIG_UEVENT_HELPER
|
||||
*
|
||||
* 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
|
||||
* Construct uevent message from environment and send it to 0x4 netlink group.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct sockaddr_nl sa = {0};
|
||||
struct msghdr hdr = {0};
|
||||
struct iovec iov = {0};
|
||||
extern char **environ;
|
||||
char path[PATH_MAX];
|
||||
char *dir;
|
||||
int fd, i;
|
||||
char buf[8192];
|
||||
size_t len;
|
||||
int i, fd;
|
||||
|
||||
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 2;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "%s/uevent.XXXXXX", dir);
|
||||
fd = mkstemp(path);
|
||||
|
||||
if (fd == -1) {
|
||||
perror("mkstemp");
|
||||
return 1;
|
||||
}
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = 0;
|
||||
|
||||
for (i = 0; environ[i]; i++) {
|
||||
if (strncmp(environ[i], "PATH=", 5) == 0 ||
|
||||
@@ -66,16 +43,44 @@ int main(int argc, char **argv)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (write(fd, environ[i], strlen(environ[i])) == -1 ||
|
||||
write(fd, "\n", 1) == -1) {
|
||||
perror("write");
|
||||
close(fd);
|
||||
unlink(path);
|
||||
len = strlen(environ[i]) + 1;
|
||||
|
||||
if (iov.iov_len + len > sizeof(buf)) {
|
||||
fprintf(stderr, "%s: uevent exceeds buffer size", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
memcpy(buf + iov.iov_len, environ[i], len);
|
||||
iov.iov_len += len;
|
||||
}
|
||||
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = 0x4; // XXX
|
||||
|
||||
hdr.msg_name = &sa;
|
||||
hdr.msg_namelen = sizeof(sa);
|
||||
hdr.msg_iov = &iov;
|
||||
hdr.msg_iovlen = 1;
|
||||
|
||||
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
|
||||
|
||||
if (fd == -1) {
|
||||
perror("socket");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
|
||||
perror("bind");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (sendmsg(fd, &hdr, 0) == -1) {
|
||||
perror("sendmsg");
|
||||
close(fd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
fchmod(fd, 0444);
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
#!/bin/sh -f
|
||||
#
|
||||
# NOTE: you don't need this if you have mdev/mdevd, refer to mdev.conf
|
||||
# NOTE: you need this if you want to use bare-bones CONFIG_UEVENT_HELPER
|
||||
#
|
||||
# usage:
|
||||
# echo /full/path/to/helper.sh > /proc/sys/kernel/hotplug
|
||||
# echo "/full/path/to/helper.sh UDEV_MONITOR_DIR" > /proc/sys/kernel/hotplug
|
||||
|
||||
exec env > "${1:-/tmp/.libudev-zero}/uevent.$$"
|
||||
@@ -1,12 +1,10 @@
|
||||
#
|
||||
# example rules for mdev.conf
|
||||
#
|
||||
# NOTE: you must change "/tmp/.libudev-zero" if you use non-default UDEV_MONITOR_DIR
|
||||
# NOTE: you don't need helper.c or helper.sh, just add this to /etc/mdev.conf
|
||||
# NOTE: replace /path/to/helper with path to compiled binary of helper.c
|
||||
|
||||
# handle all uevents(not recommended)
|
||||
#-.* root:root 660 *env > /tmp/.libudev-zero/uevent.$$
|
||||
#-.* root:root 660 */path/to/helper
|
||||
|
||||
# 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.$$
|
||||
SUBSYSTEM=drm;.* root:video 660 */path/to/helper
|
||||
SUBSYSTEM=input;.* root:input 660 */path/to/helper
|
||||
|
||||
2
udev.h
2
udev.h
@@ -129,7 +129,7 @@ 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);
|
||||
struct udev_device *udev_device_new_from_uevent(struct udev *udev, char *buf, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -632,35 +632,20 @@ 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)
|
||||
struct udev_device *udev_device_new_from_uevent(struct udev *udev, char *buf, size_t len)
|
||||
{
|
||||
char line[LINE_MAX], syspath[PATH_MAX], devnode[PATH_MAX];
|
||||
char syspath[PATH_MAX], devnode[PATH_MAX];
|
||||
struct udev_device *udev_device;
|
||||
struct stat st;
|
||||
char *sysname;
|
||||
FILE *file;
|
||||
int i, cnt;
|
||||
char *pos;
|
||||
|
||||
if (stat(path, &st) != 0 || st.st_size > 8192) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
file = fopen(path, "r");
|
||||
|
||||
if (!file) {
|
||||
return NULL;
|
||||
}
|
||||
const char *sysname;
|
||||
char *end, *pos;
|
||||
int i, cnt = 0;
|
||||
|
||||
udev_device = calloc(1, sizeof(*udev_device));
|
||||
|
||||
if (!udev_device) {
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
cnt = 0;
|
||||
|
||||
udev_device->udev = udev;
|
||||
udev_device->refcount = 1;
|
||||
udev_device->parent = NULL;
|
||||
@@ -668,13 +653,11 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat
|
||||
udev_list_entry_init(&udev_device->properties);
|
||||
udev_list_entry_init(&udev_device->sysattrs);
|
||||
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
line[strlen(line) - 1] = '\0';
|
||||
|
||||
if (strncmp(line, "DEVPATH=", 8) == 0) {
|
||||
snprintf(syspath, sizeof(syspath), "/sys%s", line + 8);
|
||||
for (end = buf + len; buf < end; buf += strlen(buf) + 1) {
|
||||
if (strncmp(buf, "DEVPATH=", 8) == 0) {
|
||||
snprintf(syspath, sizeof(syspath), "/sys%s", buf + 8);
|
||||
udev_list_entry_add(&udev_device->properties, "SYSPATH", syspath, 0);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVPATH", line + 8, 0);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVPATH", buf + 8, 0);
|
||||
|
||||
sysname = strrchr(syspath, '/') + 1;
|
||||
udev_list_entry_add(&udev_device->properties, "SYSNAME", sysname, 0);
|
||||
@@ -688,35 +671,30 @@ struct udev_device *udev_device_new_from_file(struct udev *udev, const char *pat
|
||||
|
||||
cnt++;
|
||||
}
|
||||
else if (strncmp(line, "DEVNAME=", 8) == 0) {
|
||||
snprintf(devnode, sizeof(devnode), "/dev/%s", line + 8);
|
||||
else if (strncmp(buf, "DEVNAME=", 8) == 0) {
|
||||
snprintf(devnode, sizeof(devnode), "/dev/%s", buf + 8);
|
||||
udev_list_entry_add(&udev_device->properties, "DEVNAME", devnode, 0);
|
||||
}
|
||||
else {
|
||||
pos = strchr(line, '=');
|
||||
pos = strchr(buf, '=');
|
||||
|
||||
// file is malformed, abort here.
|
||||
if (!pos) {
|
||||
cnt = 0;
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
*pos = '\0';
|
||||
|
||||
if (strncmp(line, "SUBSYSTEM", 9) == 0 ||
|
||||
strncmp(line, "ACTION", 6) == 0 ||
|
||||
strncmp(line, "SEQNUM", 6) == 0) {
|
||||
if (strcmp(buf, "SUBSYSTEM") == 0 ||
|
||||
strcmp(buf, "ACTION") == 0 ||
|
||||
strcmp(buf, "SEQNUM") == 0) {
|
||||
cnt++;
|
||||
}
|
||||
|
||||
udev_list_entry_add(&udev_device->properties, line, pos + 1, 0);
|
||||
udev_list_entry_add(&udev_device->properties, buf, pos + 1, 0);
|
||||
*pos = '=';
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file);
|
||||
|
||||
// https://freedesktop.org/software/systemd/man/udev_device_new_from_environment.html
|
||||
// > The keys DEVPATH, SUBSYSTEM, ACTION, and SEQNUM are mandatory.
|
||||
if (cnt != 4) {
|
||||
udev_device_unref(udev_device);
|
||||
return NULL;
|
||||
|
||||
200
udev_monitor.c
200
udev_monitor.c
@@ -15,36 +15,26 @@
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <poll.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <linux/netlink.h>
|
||||
|
||||
#include "udev.h"
|
||||
#include "udev_list.h"
|
||||
|
||||
#ifndef UDEV_MONITOR_DIR
|
||||
#define UDEV_MONITOR_DIR "/tmp/.libudev-zero"
|
||||
#ifndef UDEV_MONITOR_NLGRP
|
||||
#define UDEV_MONITOR_NLGRP 0x4
|
||||
#endif
|
||||
|
||||
struct udev_monitor {
|
||||
struct udev_list_entry subsystem_match;
|
||||
struct udev_list_entry devtype_match;
|
||||
struct udev *udev;
|
||||
pthread_t thread;
|
||||
const char *dir;
|
||||
int signal_fd;
|
||||
int refcount;
|
||||
int sfd[2];
|
||||
int ifd;
|
||||
int nlgrp;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static int filter_devtype(struct udev_monitor *udev_monitor, struct udev_device *udev_device)
|
||||
@@ -103,106 +93,75 @@ static int filter_subsystem(struct udev_monitor *udev_monitor, struct udev_devic
|
||||
|
||||
struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
char file[PATH_MAX], data[4096];
|
||||
struct udev_device *udev_device;
|
||||
|
||||
if (recv(udev_monitor->sfd[0], data, sizeof(data), 0) == -1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check truncation error to make gcc happy
|
||||
if ((unsigned)snprintf(file, sizeof(file), "%s/%s", udev_monitor->dir, data) >= sizeof(file)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_device = udev_device_new_from_file(udev_monitor->udev, file);
|
||||
|
||||
if (!udev_device) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!filter_subsystem(udev_monitor, udev_device) ||
|
||||
!filter_devtype(udev_monitor, udev_device)) {
|
||||
udev_device_unref(udev_device);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return udev_device;
|
||||
}
|
||||
|
||||
static void *handle_event(void *ptr)
|
||||
{
|
||||
struct udev_monitor *udev_monitor = ptr;
|
||||
struct inotify_event *event;
|
||||
struct pollfd poll_fds[2];
|
||||
char data[4096];
|
||||
struct sockaddr_nl sa = {0};
|
||||
struct msghdr hdr = {0};
|
||||
struct iovec iov = {0};
|
||||
char buf[8192];
|
||||
ssize_t len;
|
||||
int i;
|
||||
|
||||
poll_fds[0].fd = udev_monitor->ifd;
|
||||
poll_fds[0].events = POLLIN;
|
||||
iov.iov_base = buf;
|
||||
iov.iov_len = sizeof(buf);
|
||||
|
||||
poll_fds[1].fd = udev_monitor->signal_fd;
|
||||
poll_fds[1].events = POLLIN;
|
||||
hdr.msg_name = &sa;
|
||||
hdr.msg_namelen = sizeof(sa);
|
||||
hdr.msg_iov = &iov;
|
||||
hdr.msg_iovlen = 1;
|
||||
|
||||
while (1) {
|
||||
if (poll(poll_fds, 2, -1) == -1) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
len = recvmsg(udev_monitor->fd, &hdr, 0);
|
||||
|
||||
return NULL;
|
||||
if (len <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
// exit on explicit signal
|
||||
if (poll_fds[1].revents & POLLIN) {
|
||||
return NULL;
|
||||
if (hdr.msg_flags & MSG_TRUNC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// exit on poll error
|
||||
if (!(poll_fds[0].revents & POLLIN)) {
|
||||
return NULL;
|
||||
if (sa.nl_groups == 0x0 || (sa.nl_groups == 0x1 && sa.nl_pid)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
len = read(udev_monitor->ifd, data, sizeof(data));
|
||||
udev_device = udev_device_new_from_uevent(udev_monitor->udev, buf, len);
|
||||
|
||||
if (len == -1) {
|
||||
return NULL;
|
||||
if (!udev_device) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i += sizeof(struct inotify_event) + event->len) {
|
||||
event = (struct inotify_event *)&data[i];
|
||||
|
||||
// TODO directory is removed
|
||||
if (event->mask & IN_IGNORED) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (event->mask & IN_ISDIR) {
|
||||
continue;
|
||||
}
|
||||
|
||||
send(udev_monitor->sfd[1], event->name, event->len, 0);
|
||||
if (!filter_subsystem(udev_monitor, udev_device) ||
|
||||
!filter_devtype(udev_monitor, udev_device)) {
|
||||
udev_device_unref(udev_device);
|
||||
continue;
|
||||
}
|
||||
|
||||
return udev_device;
|
||||
}
|
||||
|
||||
// unreachable
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
return udev_monitor ? (pthread_create(&udev_monitor->thread, NULL, handle_event, udev_monitor) == 0) - 1 : -1;
|
||||
struct sockaddr_nl sa = {0};
|
||||
|
||||
if (!udev_monitor) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sa.nl_family = AF_NETLINK;
|
||||
sa.nl_groups = udev_monitor->nlgrp;
|
||||
return bind(udev_monitor->fd, (struct sockaddr *)&sa, sizeof(sa));
|
||||
}
|
||||
|
||||
/* XXX NOT IMPLEMENTED */ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
|
||||
int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size)
|
||||
{
|
||||
return 0;
|
||||
return udev_monitor ? setsockopt(udev_monitor->fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) : -1;
|
||||
}
|
||||
|
||||
int udev_monitor_get_fd(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
return udev_monitor ? udev_monitor->sfd[0] : -1;
|
||||
return udev_monitor ? udev_monitor->fd : -1;
|
||||
}
|
||||
|
||||
struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor)
|
||||
@@ -254,60 +213,27 @@ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_monitor->signal_fd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
|
||||
|
||||
if (udev_monitor->signal_fd == -1) {
|
||||
goto free_monitor;
|
||||
if (strcmp(name, "udev") == 0) {
|
||||
udev_monitor->nlgrp = UDEV_MONITOR_NLGRP;
|
||||
}
|
||||
else if (strcmp(name, "kernel") == 0) {
|
||||
udev_monitor->nlgrp = 0x1;
|
||||
}
|
||||
else {
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_monitor->ifd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK);
|
||||
udev_monitor->fd = socket(AF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, NETLINK_KOBJECT_UEVENT);
|
||||
|
||||
if (udev_monitor->ifd == -1) {
|
||||
goto close_signal_fd;
|
||||
}
|
||||
|
||||
// TODO docs
|
||||
udev_monitor->dir = getenv("UDEV_MONITOR_DIR");
|
||||
|
||||
if (!udev_monitor->dir || udev_monitor->dir[0] == '\0') {
|
||||
udev_monitor->dir = UDEV_MONITOR_DIR;
|
||||
|
||||
if (access(udev_monitor->dir, F_OK) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
goto close_ifd;
|
||||
}
|
||||
|
||||
if (mkdir(udev_monitor->dir, 0) == -1) {
|
||||
goto close_ifd;
|
||||
}
|
||||
|
||||
if (chmod(udev_monitor->dir, 0777) == -1) {
|
||||
goto close_ifd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inotify_add_watch(udev_monitor->ifd, udev_monitor->dir, IN_CLOSE_WRITE | IN_EXCL_UNLINK | IN_ONLYDIR) == -1) {
|
||||
goto close_ifd;
|
||||
}
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0, udev_monitor->sfd) == -1) {
|
||||
goto close_ifd;
|
||||
if (udev_monitor->fd == -1) {
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
udev_monitor->refcount = 1;
|
||||
udev_monitor->udev = udev;
|
||||
return udev_monitor;
|
||||
|
||||
close_ifd:
|
||||
close(udev_monitor->ifd);
|
||||
|
||||
close_signal_fd:
|
||||
close(udev_monitor->signal_fd);
|
||||
|
||||
free_monitor:
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
|
||||
@@ -322,8 +248,6 @@ struct udev_monitor *udev_monitor_ref(struct udev_monitor *udev_monitor)
|
||||
|
||||
struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!udev_monitor) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -332,20 +256,10 @@ struct udev_monitor *udev_monitor_unref(struct udev_monitor *udev_monitor)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Wake up the event thread
|
||||
eventfd_write(udev_monitor->signal_fd, 1);
|
||||
// waiting for event thread to end before freeing udev_monitor
|
||||
pthread_join(udev_monitor->thread, NULL);
|
||||
|
||||
udev_list_entry_free_all(&udev_monitor->devtype_match);
|
||||
udev_list_entry_free_all(&udev_monitor->subsystem_match);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
close(udev_monitor->sfd[i]);
|
||||
}
|
||||
|
||||
close(udev_monitor->signal_fd);
|
||||
close(udev_monitor->ifd);
|
||||
close(udev_monitor->fd);
|
||||
free(udev_monitor);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user