[Libvir] PATCH: Check for /usr/bin/qemu* binaries before starting guest

The attached patch fixes two issues - It explicitly checks to see if the requested /usr/bin/qemu* binary actually exists before fork()/exec()'ing it. While its technically possible to catch the execve() failure of ENOENT, its a real pain to feed this error back to the qemu daemon because we're in a sub-process at that point. The obvious & easy solution is to thus check to see if the binary exists before trying to fork. - It only passes the -no-kqemu flag if we're running matching arch to the host. Previously it would pass the -no-kqemu flag even if running an x86_64 guest on i686 platform - QEMU then exited with error because the -no-kqemu flag is not recognised on x86_64 when running on i686 host. Dan. -- |=- Red Hat, Engineering, Emerging Technologies, Boston. +1 978 392 2496 -=| |=- Perl modules: http://search.cpan.org/~danberr/ -=| |=- Projects: http://freshmeat.net/~danielpb/ -=| |=- GnuPG: 7D3B9505 F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 -=|

On Sun, Apr 15, 2007 at 09:59:35PM +0100, Daniel P. Berrange wrote:
The attached patch fixes two issues
- It explicitly checks to see if the requested /usr/bin/qemu* binary actually exists before fork()/exec()'ing it. While its technically possible to catch the execve() failure of ENOENT, its a real pain to feed this error back to the qemu daemon because we're in a sub-process at that point. The obvious & easy solution is to thus check to see if the binary exists before trying to fork.
- It only passes the -no-kqemu flag if we're running matching arch to the host. Previously it would pass the -no-kqemu flag even if running an x86_64 guest on i686 platform - QEMU then exited with error because the -no-kqemu flag is not recognised on x86_64 when running on i686 host.
Looking purely at the code this looks fine to me, Daniel -- Red Hat Virtualization group http://redhat.com/virtualization/ Daniel Veillard | virtualization library http://libvirt.org/ veillard@redhat.com | libxml GNOME XML XSLT toolkit http://xmlsoft.org/ http://veillard.com/ | Rpmfind RPM search engine http://rpmfind.net/

Daniel P. Berrange wrote:
The attached patch fixes two issues
- It explicitly checks to see if the requested /usr/bin/qemu* binary actually exists before fork()/exec()'ing it. While its technically possible to catch the execve() failure of ENOENT, its a real pain to feed this error back to the qemu daemon because we're in a sub-process at that point. The obvious & easy solution is to thus check to see if the binary exists before trying to fork.
This is quite a hard problem to fix properly, as I'm sure you know. If we were to change to use execvp, so making libvirt less dependent on the precise location of qemu binaries[1] then a complete test would also need to parse $PATH. With SELinux an exec can fail for a lot more reasons than just lack of the binary or lack of execute bit. And additionally, just because we manage to execvp the qemu-* binary, that doesn't mean that it initialises fully. In Real Life rpm ensures that we can't install libvirt without having qemu, so qemu initialisation failures are likely to be far more common than cannot-exec-qemu failures. Olwm (the old OpenLook window manager) solved both problems in this way. From the olwm man page[2]: -syncpid process-id When olwm has completed its initialization, it will send a signal (SIGALRM by default) to process-id. The signal will be sent only if this option is present. This is useful for running olwm from shell scripts (such as .xinitrc) in such a way that the script waits for olwm to finish its initialization, while leaving olwm as a child process of the shell script. This can be done using the following sh(1) construct: sleep 15 & pid=$! olwm -syncpid $pid & wait $pid -syncsignal signal Specifies the signal to send instead of SIGALRM. The signal is specified as a number, not symbolically. (In the shell-script example shown there is no way to catch errors, but you can easily extend this by sending SIGALRM to the parent -- indicating correct exec + initialisation -- or timing out). OK, so this requires an upstream patch to qemu. Worth getting this in now with the view that a few years down the line it'll be useful? In the present, qemu has two features which seem to allow us to detect partial or full initialisation. The -daemonize flag in qemu explicitly allows you to do this. They use a pipe from the child back to the parent which sends a signal back to the parent after full initialisation has happened. Unfortunately this means that qemu is not only not a child of libvirt, but also we no longer have access to stdin/stdout (ie. console). We would need to make another (good) change to allow qemu to write console sockets into a well-known directory using qemu -monitor unix:/var/... The -pidfile flag is a more immediate, partial solution. The pidfile is written part way through initialisation -- the qemu process has been exec'd and has done some work, and then works its way through the command line arguments until it reaches this one, whereupon it creates the file immediately. After parsing command line arguments, some further initialisation is done, so this is not a full solution. What do people think? Worth me working on fixing this problem properly? Rich. [1] It's arguable whether we should do that, or rely on configure-time detection of the binaries. Perhaps configure-time configuration is more secure. [2] http://www.umanitoba.ca/cgi-bin/man.cgi?section=1&topic=olwm

On Mon, 2007-04-16 at 11:49 +0100, Richard W.M. Jones wrote:
Olwm (the old OpenLook window manager) solved both problems in this way. From the olwm man page[2]:
-syncpid process-id When olwm has completed its initialization, it will send a signal (SIGALRM by default) to process-id.
X does something similar, in that if you set the signal handler to SIG_IGN for USR1 before exec-ing it, then X will send a USR1 to its parent once it has initialised. This is solving a different problem, though - how does the parent process know when the child process has initialised and is ready to e.g. start accepting connections? That's why it has "sync" in the name - it's a synchronisation thing, rather than an error reporting thing. i.e. for error handling, you don't want to have to block for this "I'm ready" signal, you don't want to have to have timeout for the case where you never get the signal, and you want to actually know what went wrong if there was an error. The best way for a child to report an errno from exec() is to pass it back via a pipe which was created by the parent. That way the parent doesn't have to block. Note, the child should mark its side of the pipe with CLOEXEC. Cheers, Mark.
participants (4)
-
Daniel P. Berrange
-
Daniel Veillard
-
Mark McLoughlin
-
Richard W.M. Jones