[libvirt] [PATCH] Allow handshake with child process during startup

Daniel P. Berrange berrange at redhat.com
Tue Apr 19 13:45:50 UTC 2011


On Mon, Apr 04, 2011 at 01:31:40PM -0600, Eric Blake wrote:
> On 04/04/2011 06:55 AM, Daniel P. Berrange wrote:
> > Allow the parent process to perform a bi-directional handshake
> > with the child process during fork/exec. The child process
> > will fork and do its initial setup. Immediately prior to the
> > exec(), it will stop & wait for a handshake from the parent
> > process. The parent process will spawn the child and wait
> > until the child reaches the handshake point. It will do
> > whatever extra setup work is required, before signalling the
> > child to continue.
> > 
> > The implementation of this is done using two pairs of blocking
> > pipes. The first pair is used to block the parent, until the
> > child writes a single byte. Then the second pair pair is used
> > to block the child, until the parent confirms with another
> > single byte.
> 
> How worried are we about the child not doing any async-unsafe actions if
> it wishes to avoid deadlock?  We've already previously identified this
> as a bug (such as in our handling of malloc in the child), but it's
> somewhat of a corner-case, and I'm not sure how invasive it will be to
> fix properly; so I am okay if a fixed version of this patch goes in
> while we still leave that larger issue open for thought.

IMHO, this patch doesn't make the existing problem any worse.
eg, if VIR_DEBUG was going to cause deadlock, it would have
happened before we get as far this new code I'm adding.

> > +++ b/src/util/command.c
> > @@ -35,6 +35,11 @@
> >  #include "files.h"
> >  #include "buf.h"
> >  
> > +#include <stdlib.h>
> > +#include <stdbool.h>
> 
> "internal.h" guaranteed this one for us.

Opps, yes.

> > +#include <poll.h>
> > +#include <sys/wait.h>
> 
> Why do we need this one?  We're already using WIFEXITED and friends
> without explicitly needing this header.

I'll double check whether we need this.

> 
> > @@ -1115,7 +1129,6 @@ virCommandRun(virCommandPtr cmd, int *exitstatus)
> >      return ret;
> >  }
> >  
> > -
> >  /*
> >   * Perform all virCommand-specific actions, along with the user hook.
> >   */
> 
> Spurious whitespace change?
> 
> > @@ -1125,12 +1138,61 @@ virCommandHook(void *data)
> >      virCommandPtr cmd = data;
> >      int res = 0;
> >  
> > -    if (cmd->hook)
> > +    if (cmd->hook) {
> > +        VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
> >          res = cmd->hook(cmd->opaque);
> > +        VIR_DEBUG("Done hook %d", res);
> 
> This adds yet more calls to malloc() inside the child
> 
> > +    }
> >      if (res == 0 && cmd->pwd) {
> >          VIR_DEBUG("Running child in %s", cmd->pwd);
> 
> ...but no worse than what we've already been doing.  If VIR_DEBUG were
> the only use of malloc() in the child, then it could just boil down to
> conditionally compiling VIR_DEBUG statements where they can be turned on
> for debugging the implementation, but compiled out later to avoid
> deadlock (at least, I'm assuming that VIR_DEBUG uses malloc() to format
> a timestamp prior to the message when posting to the circular buffer).

In addition, VIR_DEBUG should be a no-op in normal operation.
Only if someone raises the debug level significantly will it
do work that triggers malloc & thus exposes the risk.

> >          res = chdir(cmd->pwd);
> > +        if (res < 0) {
> > +            virReportSystemError(errno,
> > +                                 _("Unable to change to %s"), cmd->pwd);
> > +        }
> > +    }
> > +    if (cmd->handshake) {
> > +        char c = res < 0 ? '0' : '1';
> > +        int rv;
> > +        VIR_DEBUG("Notifying parent for handshake start on %d", cmd->handshakeWait[1]);
> > +        if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) {
> 
> Since c is char, sizeof(c) == 1 by definition.  But I'm okay with you
> spelling out the longer form.
> 
> > +            virReportSystemError(errno, "%s", _("Unable to notify parent process"));
> > +            return -1;
> > +        }
> > +
> > +        /* On failure we pass the error message back to parent,
> > +         * so they don't have to dig through stderr logs
> > +         */
> > +        if (res < 0) {
> > +            virErrorPtr err = virGetLastError();
> > +            const char *msg = err ? err->message :
> > +                _("Unknown failure during hook execution");
> 
> Hmm, now that's a lot more use of malloc(), and not just in VIR_DEBUG()
> calls.

Code earlier that actually use virRaiseError would have already
triggered malloc, so I still think this isn't making things
any worse.

> > +
> > +void virCommandRequireHandshake(virCommandPtr cmd)
> > +{
> > +    if (pipe(cmd->handshakeWait) < 0) {
> 
> NULL dereference if cmd had a previous failure.  You need to guard with:
> 
> if (!cmd || cmd->has_error) return;

Opps, yeah.

> 
> > +
> > +int virCommandHandshakeWait(virCommandPtr cmd)
> > +{
> > +    char c;
> > +    int rv;
> > +    VIR_DEBUG("Wait for handshake on %d", cmd->handshakeWait[0]);
> 
> Likewise.
> 
> > +
> > +int virCommandHandshakeNotify(virCommandPtr cmd)
> > +{
> > +    char c = '1';
> > +    VIR_DEBUG("Notify handshake on %d", cmd->handshakeWait[0]);
> 
> Likewise.
> 
> > +++ b/src/util/command.h
> > @@ -274,6 +274,11 @@ int virCommandRunAsync(virCommandPtr cmd,
> >  int virCommandWait(virCommandPtr cmd,
> >                     int *exitstatus) ATTRIBUTE_RETURN_CHECK;
> >  
> > +void virCommandRequireHandshake(virCommandPtr cmd);
> > +
> > +int virCommandHandshakeWait(virCommandPtr cmd);
> > +int virCommandHandshakeNotify(virCommandPtr cmd);
> 
> These last two could use ATTRIBUTE_RETURN_CHECK.  All three could use
> documentation.

Ok.

Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list