348 lines
10 KiB
C
348 lines
10 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#include <errno.h>
|
|
#include <poll.h>
|
|
|
|
#include "libudev.h"
|
|
|
|
#include "alloc-util.h"
|
|
#include "device-monitor-private.h"
|
|
#include "device-private.h"
|
|
#include "device-util.h"
|
|
#include "libudev-device-internal.h"
|
|
#include "libudev-private.h"
|
|
#include "string-util.h"
|
|
|
|
/**
|
|
* SECTION:libudev-monitor
|
|
* @short_description: device event source
|
|
*
|
|
* Connects to a device event source.
|
|
*/
|
|
|
|
/**
|
|
* udev_monitor:
|
|
*
|
|
* Opaque object handling an event source.
|
|
*/
|
|
struct udev_monitor {
|
|
struct udev *udev;
|
|
unsigned n_ref;
|
|
sd_device_monitor *monitor;
|
|
};
|
|
|
|
static MonitorNetlinkGroup monitor_netlink_group_from_string(const char *name) {
|
|
if (!name)
|
|
return MONITOR_GROUP_NONE;
|
|
if (streq(name, "udev"))
|
|
return MONITOR_GROUP_UDEV;
|
|
if (streq(name, "kernel"))
|
|
return MONITOR_GROUP_KERNEL;
|
|
return _MONITOR_NETLINK_GROUP_INVALID;
|
|
}
|
|
|
|
struct udev_monitor *udev_monitor_new_from_netlink_fd(struct udev *udev, const char *name, int fd) {
|
|
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *m = NULL;
|
|
_cleanup_(udev_monitor_unrefp) struct udev_monitor *udev_monitor = NULL;
|
|
MonitorNetlinkGroup g;
|
|
int r;
|
|
|
|
g = monitor_netlink_group_from_string(name);
|
|
if (g < 0) {
|
|
errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
|
|
r = device_monitor_new_full(&m, g, fd);
|
|
if (r < 0) {
|
|
errno = -r;
|
|
return NULL;
|
|
}
|
|
|
|
udev_monitor = new(struct udev_monitor, 1);
|
|
if (!udev_monitor) {
|
|
errno = ENOMEM;
|
|
return NULL;
|
|
}
|
|
|
|
*udev_monitor = (struct udev_monitor) {
|
|
.udev = udev,
|
|
.n_ref = 1,
|
|
.monitor = TAKE_PTR(m),
|
|
};
|
|
|
|
return TAKE_PTR(udev_monitor);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_new_from_netlink:
|
|
* @udev: udev library context
|
|
* @name: name of event source
|
|
*
|
|
* Create new udev monitor and connect to a specified event
|
|
* source. Valid sources identifiers are "udev" and "kernel".
|
|
*
|
|
* Applications should usually not connect directly to the
|
|
* "kernel" events, because the devices might not be useable
|
|
* at that time, before udev has configured them, and created
|
|
* device nodes. Accessing devices at the same time as udev,
|
|
* might result in unpredictable behavior. The "udev" events
|
|
* are sent out after udev has finished its event processing,
|
|
* all rules have been processed, and needed device nodes are
|
|
* created.
|
|
*
|
|
* The initial refcount is 1, and needs to be decremented to
|
|
* release the resources of the udev monitor.
|
|
*
|
|
* Returns: a new udev monitor, or #NULL, in case of an error
|
|
**/
|
|
_public_ struct udev_monitor *udev_monitor_new_from_netlink(struct udev *udev, const char *name) {
|
|
return udev_monitor_new_from_netlink_fd(udev, name, -1);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_filter_update:
|
|
* @udev_monitor: monitor
|
|
*
|
|
* Update the installed socket filter. This is only needed,
|
|
* if the filter was removed or changed.
|
|
*
|
|
* Returns: 0 on success, otherwise a negative error value.
|
|
*/
|
|
_public_ int udev_monitor_filter_update(struct udev_monitor *udev_monitor) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return sd_device_monitor_filter_update(udev_monitor->monitor);
|
|
}
|
|
|
|
int udev_monitor_allow_unicast_sender(struct udev_monitor *udev_monitor, struct udev_monitor *sender) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
assert_return(sender, -EINVAL);
|
|
|
|
return device_monitor_allow_unicast_sender(udev_monitor->monitor, sender->monitor);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_enable_receiving:
|
|
* @udev_monitor: the monitor which should receive events
|
|
*
|
|
* Binds the @udev_monitor socket to the event source.
|
|
*
|
|
* Returns: 0 on success, otherwise a negative error value.
|
|
*/
|
|
_public_ int udev_monitor_enable_receiving(struct udev_monitor *udev_monitor) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return device_monitor_enable_receiving(udev_monitor->monitor);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_set_receive_buffer_size:
|
|
* @udev_monitor: the monitor which should receive events
|
|
* @size: the size in bytes
|
|
*
|
|
* Set the size of the kernel socket buffer. This call needs the
|
|
* appropriate privileges to succeed.
|
|
*
|
|
* Returns: 0 on success, otherwise -1 on error.
|
|
*/
|
|
_public_ int udev_monitor_set_receive_buffer_size(struct udev_monitor *udev_monitor, int size) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return sd_device_monitor_set_receive_buffer_size(udev_monitor->monitor, (size_t) size);
|
|
}
|
|
|
|
int udev_monitor_disconnect(struct udev_monitor *udev_monitor) {
|
|
assert(udev_monitor);
|
|
|
|
return device_monitor_disconnect(udev_monitor->monitor);
|
|
}
|
|
|
|
static struct udev_monitor *udev_monitor_free(struct udev_monitor *udev_monitor) {
|
|
assert(udev_monitor);
|
|
|
|
sd_device_monitor_unref(udev_monitor->monitor);
|
|
return mfree(udev_monitor);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_ref:
|
|
* @udev_monitor: udev monitor
|
|
*
|
|
* Take a reference of a udev monitor.
|
|
*
|
|
* Returns: the passed udev monitor
|
|
**/
|
|
|
|
/**
|
|
* udev_monitor_unref:
|
|
* @udev_monitor: udev monitor
|
|
*
|
|
* Drop a reference of a udev monitor. If the refcount reaches zero,
|
|
* the bound socket will be closed, and the resources of the monitor
|
|
* will be released.
|
|
*
|
|
* Returns: #NULL
|
|
**/
|
|
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(struct udev_monitor, udev_monitor, udev_monitor_free);
|
|
|
|
/**
|
|
* udev_monitor_get_udev:
|
|
* @udev_monitor: udev monitor
|
|
*
|
|
* Retrieve the udev library context the monitor was created with.
|
|
*
|
|
* Returns: the udev library context
|
|
**/
|
|
_public_ struct udev *udev_monitor_get_udev(struct udev_monitor *udev_monitor) {
|
|
assert_return(udev_monitor, NULL);
|
|
|
|
return udev_monitor->udev;
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_get_fd:
|
|
* @udev_monitor: udev monitor
|
|
*
|
|
* Retrieve the socket file descriptor associated with the monitor.
|
|
*
|
|
* Returns: the socket file descriptor
|
|
**/
|
|
_public_ int udev_monitor_get_fd(struct udev_monitor *udev_monitor) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return device_monitor_get_fd(udev_monitor->monitor);
|
|
}
|
|
|
|
int udev_monitor_receive_sd_device(struct udev_monitor *udev_monitor, sd_device **ret) {
|
|
struct pollfd pfd;
|
|
int r;
|
|
|
|
assert(udev_monitor);
|
|
assert(ret);
|
|
|
|
pfd = (struct pollfd) {
|
|
.fd = device_monitor_get_fd(udev_monitor->monitor),
|
|
.events = POLLIN,
|
|
};
|
|
|
|
for (;;) {
|
|
/* r == 0 means a device is received but it does not pass the current filter. */
|
|
r = device_monitor_receive_device(udev_monitor->monitor, ret);
|
|
if (r != 0)
|
|
return r;
|
|
|
|
for (;;) {
|
|
/* wait next message */
|
|
r = poll(&pfd, 1, 0);
|
|
if (r < 0) {
|
|
if (IN_SET(errno, EINTR, EAGAIN))
|
|
continue;
|
|
|
|
return -errno;
|
|
} else if (r == 0)
|
|
return -EAGAIN;
|
|
|
|
/* receive next message */
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_receive_device:
|
|
* @udev_monitor: udev monitor
|
|
*
|
|
* Receive data from the udev monitor socket, allocate a new udev
|
|
* device, fill in the received data, and return the device.
|
|
*
|
|
* Only socket connections with uid=0 are accepted.
|
|
*
|
|
* The monitor socket is by default set to NONBLOCK. A variant of poll() on
|
|
* the file descriptor returned by udev_monitor_get_fd() should to be used to
|
|
* wake up when new devices arrive, or alternatively the file descriptor
|
|
* switched into blocking mode.
|
|
*
|
|
* The initial refcount is 1, and needs to be decremented to
|
|
* release the resources of the udev device.
|
|
*
|
|
* Returns: a new udev device, or #NULL, in case of an error
|
|
**/
|
|
_public_ struct udev_device *udev_monitor_receive_device(struct udev_monitor *udev_monitor) {
|
|
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
|
int r;
|
|
|
|
assert_return(udev_monitor, NULL);
|
|
|
|
r = udev_monitor_receive_sd_device(udev_monitor, &device);
|
|
if (r < 0) {
|
|
errno = -r;
|
|
return NULL;
|
|
}
|
|
|
|
return udev_device_new(udev_monitor->udev, device);
|
|
}
|
|
|
|
int udev_monitor_send_device(
|
|
struct udev_monitor *udev_monitor,
|
|
struct udev_monitor *destination,
|
|
struct udev_device *udev_device) {
|
|
assert(udev_monitor);
|
|
assert(udev_device);
|
|
|
|
return device_monitor_send_device(udev_monitor->monitor,
|
|
destination ? destination->monitor : NULL,
|
|
udev_device->device);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_filter_add_match_subsystem_devtype:
|
|
* @udev_monitor: the monitor
|
|
* @subsystem: the subsystem value to match the incoming devices against
|
|
* @devtype: the devtype value to match the incoming devices against
|
|
*
|
|
* This filter is efficiently executed inside the kernel, and libudev subscribers
|
|
* will usually not be woken up for devices which do not match.
|
|
*
|
|
* The filter must be installed before the monitor is switched to listening mode.
|
|
*
|
|
* Returns: 0 on success, otherwise a negative error value.
|
|
*/
|
|
_public_ int udev_monitor_filter_add_match_subsystem_devtype(struct udev_monitor *udev_monitor, const char *subsystem, const char *devtype) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return sd_device_monitor_filter_add_match_subsystem_devtype(udev_monitor->monitor, subsystem, devtype);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_filter_add_match_tag:
|
|
* @udev_monitor: the monitor
|
|
* @tag: the name of a tag
|
|
*
|
|
* This filter is efficiently executed inside the kernel, and libudev subscribers
|
|
* will usually not be woken up for devices which do not match.
|
|
*
|
|
* The filter must be installed before the monitor is switched to listening mode.
|
|
*
|
|
* Returns: 0 on success, otherwise a negative error value.
|
|
*/
|
|
_public_ int udev_monitor_filter_add_match_tag(struct udev_monitor *udev_monitor, const char *tag) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return sd_device_monitor_filter_add_match_tag(udev_monitor->monitor, tag);
|
|
}
|
|
|
|
/**
|
|
* udev_monitor_filter_remove:
|
|
* @udev_monitor: monitor
|
|
*
|
|
* Remove all filters from monitor.
|
|
*
|
|
* Returns: 0 on success, otherwise a negative error value.
|
|
*/
|
|
_public_ int udev_monitor_filter_remove(struct udev_monitor *udev_monitor) {
|
|
assert_return(udev_monitor, -EINVAL);
|
|
|
|
return sd_device_monitor_filter_remove(udev_monitor->monitor);
|
|
}
|