Hi Rainer,
On 17/7/25 13:49, Rainer Weikusat via Dng wrote:
>>> enum {
>>> FD_POLL_SIGNAL = 0,
>>> FD_POLL_PIPE,
>>> FD_POLL_NETLINK,
>>> FD_POLL_MAX
>>> };
> This means FD_POLL_PIPE has the value 1.
Yes.
> pfd[FD_POLL_PIPE].fd = pipe[1];
> Which means this is polling the write end of the pipe on systems where
> pipes are indeed unidirectional. The convention is the same here as for
> standard I/O file descriptors. File descriptor 0 is the standard input
> and 1 is the standard output. When creating a pipe, the first file
> descriptor (offset 0) is for reading and the second one for writing.
I did mistakes in my example. As Didier has pointed out in another email, it should be pipefd[0].
Nevertheless, I'm considering a new approach because I don't like the idea of forking a new process
in the function:
int vdev_os_next_device(struct vdev_device_request* vreq, void* cls)
that yields the next device event (see line 471 of vdevd/os/linux.c) and is invoked over and over
again within the loop:
int vdev_os_main(struct vdev_os_context* vos) { int rc = 0;
while(vos->running) { (......) } }
in vdevd/os/common.c
On the other hand, if we fork a new process outside the loop in common.c the linux netlink would be used
in a file that, according to Jude Nelson's design, aims to be OS-agnostic with the purpose of serving as
an example for future ports, say BSD. And I don't want to affect the legibility of the original program.
This brings me to the idea of using a daemonlet in common.c. If you read the **Advanced device handling**
in the 'how-to-test.md' file:
/*------------------------- JUDE NELSON's EXPLANTIONS ---------------------------*/
By default, `vdevd` will `fork()` and `exec()` the `command` for each device request in the system shell.
However, as an optimization, `vdevd` can run the command as a *daemonlet*. This means that the `command`
will be expected to stay resident once it proceses a device request, and receive and process subsequent
device requests from `vdevd` instead of simply exiting after each one. Doing so saves `vdevd` from having
to `fork()` and `exec()` the same command over and over again for common types of devices, and lets it avoid
having to repeatly incur long `command` start-up times. It also allows the administrator to define stateful
or long-running actions that can work on sets of devices.
The programming model for daemonlet commands is as follows:
1. `vdevd` will `fork()` and `exec()` the command directly. It will *not* invoke the system shell to run it.
2. `vdevd` will write a sequence of newline-terminated strings to the command's `stdin`, followed by an empty
newline string. These strings encode the request's environment variables, and are in the form `NAME=VALUE\n`.
3. The `command` is expected to write an ASCII-encoded exit code to `stdout` to indicate the success/failure of
processing the request. 0 indicates success, and non-zero indicates failure.
If the `command` crashes or misbehaves, `vdevd` will log as such and attempt to restart it.
/* ----------------------------------- END --------------------------------------------*/
Time ago I moved part of the code from vdevd/* to libvdev/* and built a shared library in a way that standalone
vdev helpers can make use of these *daemonlets*. For instance, there is 'tiny vdev' that works fine during the
initrd phase, but I still didn't implement it in gnuinos because it fails for some unknown reason in virtual
machines (at least in qemu). However, I'll push these examples to git because I think they illustrate very well
the use of daemonlets.
This said, in my new approach the daemonlet would be started in an OS-agnostic way before the while() loop in
vdev_os_main(). In the next iteration, the function vdev_os_next_device() would look for the next coldplugged
device or, on the contrary, for the next hotplugged device, depending on what we got when feeding the already
running daemonlet at the end of the previous iteration.
I'll let you know whether this new take goes somewhere...
Thanks, Didier and Rainer!
Aitor.