On 10/7/19 5:48 AM, Vladimir Sementsov-Ogievskiy wrote:
We want to wait until listening socket is prepared..
In shell:
qemu-nbd --pid-file=/path/to/file ...
while [ ! -e /path/to/file ]; do
sleep ... # fractional second, or exponential, or whatever...
done
# Now the listening socket is indeed prepared
You'd have to translate that idiom to python.
Don't see, how it is better than what I've done in 04.. But I can resend with
this.
At least, the fact that socket is initialized before creating pid file is
undocumented..
I just posted a patch to rectify that.
Or:
pre-open Unix socket at /path/to/socket
LISTEN_PID=... LISTEN_FDS=1 qemu-nbd ... 3<>/path/to/socket
Now the socket is pre-created and passed into qemu-nbd via systemd socket
activation, so you know the listening socket is ready without having to do any
loop at all. Here's a patch in libnbd where we just switched from waiting for
the port to appear (because the test predated qemu-nbd pidfile support) to
instead using socket activation, for reference:
https://github.com/libguestfs/libnbd/commit/352331d177
Hmm, I'm afraid I need more help in it, I don't know socket activation and
googling for some time didn't help.
How to pre-open the socket? How to set LISTEN_PID? Looking at the code I see
that activation path failed if
LISTEN_PID != getpid().. So I need to know qemu-nbd pid before starting it? o_O
I'm not sure if a shell can open a Unix socket as a server. The shell
_does_ have the 'exec' builtin, such that you can manipulate the
environment in shell and then use exec to guarantee the qemu-nbd process
has the same pid as the shell process that just manipulated the
environment; but it doesn't help unless you can also pass in the Unix
socket pre-bound, and I'm not aware of command line tools that make that
easy (maybe nc can do something like it, but then you have to wonder if
nc is adding yet another layer of bounce-buffering). But in other
languages (C, Python, ...) you create a Unix socket, call bind() on it,
then call fork(). In the parent process, you then close the just-bound
fd, and call connect() on the socket - the connect will block until the
child process uses the socket, but you are guaranteed that it won't fail
because the server side is already bound. In the child process, you use
dup2() to move the fd to 3 and clear O_CLOEXEC, manipulate the
environment to set LISTEN_PID to the current process id and LISTEN_FDS
to 1, then exec. The child process then has the correct id to realize
that it has been handed a pre-bound socket, and skips any code that it
would normally do to create the socket and bind it.
Note that setting LISTEN_PID in the child process after a fork() from a
multi-threaded parent is _extremely_ difficult to do correctly (setenv()
is not async-signal-safe, and anything that is not async-signal-safe can
cause deadlock if invoked between fork() and exec() if the parent
process was multi-threaded) - so it may even be easier to do a
double-exec() - the first exec is to a shim to get rid of the
async-signal-safe restrictions, and can then set LISTEN_PID without
issues, and the second exec to the actual target. But I don't know of
any such shim designed for common use.
That said, socket activation isn't a necessity to use, just a
convenience. I don't care if the regression test uses socket
activation, as long as it works at testing nbd reconnect.
--
Eric Blake, Principal Software Engineer
Red Hat, Inc. +1-919-301-3226
Virtualization: qemu.org | libvirt.org