Hey everyone, I've just pushed my first stab at libudev-compat to the vdev repository. It took a while to work out how to remove the need for udev to send libudev clients device events, but I think I've figured out something that works.
Instead of sending events via a netlink multicast group, the device manager is expected to atomically put a serialized struct udev_device message into a set of process-specific directories (i.e. as strings that can be parsed by udev_device_new_from_nulstr()). Libudev-compat clients create these directories when they create struct udev_monitor instances, so the device manager need only write the event file somewhere privately, hard-link it into each such directory (ensuring "atomic send" semantics), and then unlink it. This means that pretty much any program you want can trivially send device messages to other programs as long as it can write to their directories, and inspect or even re-order pending messages. Instead of a netlink socket, a struct udev_monitor in libudev-compat contains an epoll handle, an inotify handle, and a socket pair. The epoll handle is used to indicate the readiness of the inotify handle, the "sink" end of the socket pair, or both, and is the pollable handle returned by udev_monitor_get_fd(). Libudev-compat ensures that a struct udev_monitor's handle will poll as ready-to-read if and only if there is at least one unprocessed device event. The inotify handle is set up to watch the process-specific directory for IN_CREATE events, in a one-shot fashion (the directory itself is created as part of udev_monitor_new()). When a client program receives a device event with udev_monitor_receive_device(), libudev-compat resets the one-shot inotify handle, scans the directory, and sends as many files' contents as it can as messages to the "source" end of the monitor's socket pair. Libudev-compat filters unwanted devices on the socket pair using a BPF program in the same way that libudev does for netlinks sockets (i.e. the logic is the same in both libraries). Devices events are sent in lexicographic order by name, and are consumed from the "sink" end of the socket pair. Because we're not using netlink anymore, we have to preserve multicast semantics across fork() as well--that is, if the process fork()'s and the device manager subsequently sends a device event, both the parent and child must receive it. To do so, libudev-compat maintains a global table of all existing monitors, which it does as part of udev_monitor_new() and udev_monitor_unref(). Upon fork(), as part of a pthread_atfork() handler, the libudev-compat child creates a new events directory, and re-targets all of its monitors' inotify handles to watch it instead. It also closes and re-opens each monitor's socket pairs, so it will not accidentally consume its parent's buffered device events. This ensures that the next time the child calls udev_monitor_receive_device(), it will look in its own directory, and it will receive and consume events independently of its parent, as desired. I have lightly tested libudev-compat's ability to do all of the above with a helper program for vdev (vdevd/helpers/LINUX/event-put.c), and with a test program that can be compiled with the "test" target in libudev-compat/ (i.e. "cd libudev-compat && make test"). It appears to work so far, but I invite the community to review the code and try it themselves. What I need to do next is create vdev scripts that will cause it to send off events to libudev-compat clients, using event-put. To do so, I'll also need to add helper scripts to generate and maintain /run/udev/, since the struct udev_device's that libudev-compat clients will expect to receive must include all of the metadata udev would generate for them. Thanks, Jude
_______________________________________________ Dng mailing list Dng@lists.dyne.org https://mailinglists.dyne.org/cgi-bin/mailman/listinfo/dng