[libvirt] [PATCHv2 3/8] Introduce new APIs for spawning processes

Eric Blake eblake at redhat.com
Thu Dec 2 22:33:24 UTC 2010


On 12/01/2010 02:42 PM, Matthias Bolte wrote:
>> +++ b/cfg.mk
>> @@ -77,6 +77,7 @@ useless_free_options =                                \
>>   --name=virCapabilitiesFreeHostNUMACell       \
>>   --name=virCapabilitiesFreeMachines           \
>>   --name=virCgroupFree                         \
>> +  --name=virCommandFree                                \
>>   --name=virConfFreeList                       \
>>   --name=virConfFreeValue                      \
>>   --name=virDomainChrDefFree                   \
> 
> You should also add virCommandError to the msg_gen_function list.

Good catch, and done.

>> +void
>> +virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf)
>> +{
>> +    if (!cmd || cmd->has_error)
>> +        return;
>> +
>> +    if (cmd->outfd != -1) {
> 
> To detect double assignment properly you need to check for this I think:
> 
>     if (cmd->outfd != -1 || cmd->outfdptr || cmd->outbuf) {

Almost.  There are really only two functions that affect stdout settings
before a command (set fd assigns outfdptr, set buffer assigns outbuf and
outfdptr), so checking cmd->outfdptr for NULL is sufficient to check for
no duplicate call.

>> +void
>> +virCommandSetInputFD(virCommandPtr cmd, int infd)
>> +{
>> +    if (!cmd || cmd->has_error)
>> +        return;
>> +
>> +    if (infd < 0 || cmd->inbuf) {
> 
> I think you meant to check here for this instead:
> 
>     if (cmd->infd != -1 || cmd->inbuf) {

Hmm; actually, there's two issues.  One of validating that input is set
at most once (cmd->infd != -1 || cmd->inbuf), and one of validating that
the incoming argument is valid (infd < 0), worth two separate messages.
 Thanks for forcing me to think about this more.

>> +void
>> +virCommandWriteArgLog(virCommandPtr cmd, int logfd)
>> +{
>> +    int ioError = 0;
>> +    size_t i;
>> +
>> +    /* Any errors will be reported later by virCommandRun, which means
>> +     * no command will be run, so there is nothing to log. */
>> +    if (!cmd || cmd->has_error)
>> +        return;
>> +
>> +    for (i = 0 ; i < cmd->nenv ; i++) {
>> +        if (safewrite(logfd, cmd->env[i], strlen(cmd->env[i])) < 0)
>> +            ioError = errno;
>> +        if (safewrite(logfd, " ", 1) < 0)
>> +            ioError = errno;
>> +    }
>> +    for (i = 0 ; i < cmd->nargs ; i++) {
>> +        if (safewrite(logfd, cmd->args[i], strlen(cmd->args[i])) < 0)
>> +            ioError = errno;
>> +        if (safewrite(logfd, i == cmd->nargs - 1 ? "\n" : " ", 1) < 0)
>> +            ioError = errno;
>> +    }
> 
> As written this will only save the last occurred error in ioError. Is
> this intended? Looks like a best effort approach, try to write as much
> as possible ignoring errors.

Well, the function has no return value, so yes, that's the best we can
do - log as much as possible, and issue a VIR_WARN if we didn't log
everything.  But I couldn't seem to justify returning an error and
stopping the log at the first error - how do you log that you had an
error logging, when logging is orthogonal to running the command in the
first place?

> 
> In virCommandProcessIO you say that cmd->{out|err}fd is a valid FD
> (created in virExecWithHook called by virCommandRunAsync) when
> cmd->{out|err}buf is not NULL. As far as I can tell from the code that
> is true when the caller had requested to capture stdout and stderr.
> But in case that caller didn't do this then you setup buffers to
> capture stdout and stderr for logging. In that case
> virCommandProcessIO will be called with cmd->{out|err}buf being
> non-NULL but cmd->{out|err}fd being invalid as you explicitly set them
> to -1 in the two if blocks before the call to virCommandProcessIO.

Yep, that needed reordering.  If virCommandRun detects that nothing set
output, then outfd needs to be set prior to virCommandRunAsync so as to
force the creation of a pipe rather than assigning fds to /dev/null.

>> +
>> +#ifndef __linux__
> 
> What's Linux specific in this test? Shouldn't the command API and this
> test be working on all systems that support fork/exec?

It should really be #if !HAVE_FORK (aka #ifdef WIN32).

I'm testing the impacts of squashing this in...

diff --git i/cfg.mk w/cfg.mk
index 8a8da18..29de9d9 100644
--- i/cfg.mk
+++ w/cfg.mk
@@ -369,9 +369,9 @@ msg_gen_function += umlReportError
 msg_gen_function += vah_error
 msg_gen_function += vah_warning
 msg_gen_function += vboxError
+msg_gen_function += virCommandError
 msg_gen_function += virConfError
 msg_gen_function += virDomainReportError
-msg_gen_function += virSecurityReportError
 msg_gen_function += virHashError
 msg_gen_function += virLibConnError
 msg_gen_function += virLibDomainError
@@ -380,6 +380,7 @@ msg_gen_function += virNodeDeviceReportError
 msg_gen_function += virRaiseError
 msg_gen_function += virReportErrorHelper
 msg_gen_function += virReportSystemError
+msg_gen_function += virSecurityReportError
 msg_gen_function += virSexprError
 msg_gen_function += virStorageReportError
 msg_gen_function += virXMLError
diff --git i/src/util/command.c w/src/util/command.c
index 948a957..aa43f76 100644
--- i/src/util/command.c
+++ w/src/util/command.c
@@ -91,7 +91,7 @@ virCommandNew(const char *binary)

 /*
  * Create a new command with a NULL terminated
- * set of args, taking binary from argv[0]
+ * set of args, taking binary from args[0]
  */
 virCommandPtr
 virCommandNewArgs(const char *const*args)
@@ -543,7 +543,7 @@ virCommandSetOutputBuffer(virCommandPtr cmd, char
**outbuf)
     if (!cmd || cmd->has_error)
         return;

-    if (cmd->outfd != -1) {
+    if (cmd->outfdptr) {
         cmd->has_error = -1;
         VIR_DEBUG0("cannot specify output twice");
         return;
@@ -563,7 +563,7 @@ virCommandSetErrorBuffer(virCommandPtr cmd, char
**errbuf)
     if (!cmd || cmd->has_error)
         return;

-    if (cmd->errfd != -1) {
+    if (cmd->errfdptr) {
         cmd->has_error = -1;
         VIR_DEBUG0("cannot specify stderr twice");
         return;
@@ -583,11 +583,16 @@ virCommandSetInputFD(virCommandPtr cmd, int infd)
     if (!cmd || cmd->has_error)
         return;

-    if (infd < 0 || cmd->inbuf) {
+    if (cmd->infd != -1 || cmd->inbuf) {
         cmd->has_error = -1;
         VIR_DEBUG0("cannot specify input twice");
         return;
     }
+    if (infd < 0) {
+        cmd->has_error = -1;
+        VIR_DEBUG0("cannot specify invalid input fd");
+        return;
+    }

     cmd->infd = infd;
 }
@@ -602,7 +607,7 @@ virCommandSetOutputFD(virCommandPtr cmd, int *outfd)
     if (!cmd || cmd->has_error)
         return;

-    if (!outfd || cmd->outfd != -1) {
+    if (cmd->outfdptr) {
         cmd->has_error = -1;
         VIR_DEBUG0("cannot specify output twice");
         return;
@@ -621,7 +626,7 @@ virCommandSetErrorFD(virCommandPtr cmd, int *errfd)
     if (!cmd || cmd->has_error)
         return;

-    if (!errfd || cmd->errfd != -1) {
+    if (cmd->errfdptr) {
         cmd->has_error = -1;
         VIR_DEBUG0("cannot specify stderr twice");
         return;
@@ -642,6 +647,11 @@ virCommandSetPreExecHook(virCommandPtr cmd,
virExecHook hook, void *opaque)
     if (!cmd || cmd->has_error)
         return;

+    if (cmd->hook) {
+        cmd->has_error = -1;
+        VIR_DEBUG0("cannot specify hook twice");
+        return;
+    }
     cmd->hook = hook;
     cmd->opaque = opaque;
 }
@@ -778,7 +788,7 @@ virCommandProcessIO(virCommandPtr cmd)
         if (nfds == 0)
             break;

-        if (poll(fds,nfds, -1) < 0) {
+        if (poll(fds, nfds, -1) < 0) {
             if ((errno == EAGAIN) || (errno == EINTR))
                 continue;
             virReportSystemError(errno, "%s",
@@ -882,12 +892,25 @@ virCommandRun(virCommandPtr cmd, int *exitstatus)
         if (pipe(infd) < 0) {
             virReportSystemError(errno, "%s",
                                  _("unable to open pipe"));
+            cmd->has_error = -1;
             return -1;
         }
         cmd->infd = infd[0];
         cmd->inpipe = infd[1];
     }

+    /* If caller hasn't requested capture of stdout/err, then capture
+     * it ourselves so we can log it.
+     */
+    if (!cmd->outfdptr) {
+        cmd->outfdptr = &cmd->outfd;
+        cmd->outbuf = &outbuf;
+    }
+    if (!cmd->errfdptr) {
+        cmd->errfdptr = &cmd->errfd;
+        cmd->errbuf = &errbuf;
+    }
+
     if (virCommandRunAsync(cmd, NULL) < 0) {
         if (cmd->inbuf) {
             int tmpfd = infd[0];
@@ -897,26 +920,10 @@ virCommandRun(virCommandPtr cmd, int *exitstatus)
             if (VIR_CLOSE(infd[1]) < 0)
                 VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
         }
+        cmd->has_error = -1;
         return -1;
     }

-    /* If caller hasn't requested capture of
-     * stdout/err, then capture it ourselves
-     * so we can log it
-     */
-    if (!cmd->outbuf &&
-        !cmd->outfdptr) {
-        cmd->outfd = -1;
-        cmd->outfdptr = &cmd->outfd;
-        cmd->outbuf = &outbuf;
-    }
-    if (!cmd->errbuf &&
-        !cmd->errfdptr) {
-        cmd->errfd = -1;
-        cmd->errfdptr = &cmd->errfd;
-        cmd->errbuf = &errbuf;
-    }
-
     if (cmd->inbuf || cmd->outbuf || cmd->errbuf)
         ret = virCommandProcessIO(cmd);

@@ -1012,7 +1019,7 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
     }


-    str = virArgvToString((const char *const *)cmd->args);
+    str = virCommandToString(cmd);
     VIR_DEBUG("About to run %s", str ? str : cmd->args[0]);
     VIR_FREE(str);

@@ -1090,7 +1097,7 @@ virCommandWait(virCommandPtr cmd, int *exitstatus)
     if (exitstatus == NULL) {
         if (status != 0) {
             virCommandError(VIR_ERR_INTERNAL_ERROR,
-                            _("Intermediate daemon process exited with
status %d."),
+                            _("Child process exited with status %d."),
                             WEXITSTATUS(status));
             return -1;
         }
diff --git i/tests/commandtest.c w/tests/commandtest.c
index 46d6ae5..0be8e94 100644
--- i/tests/commandtest.c
+++ w/tests/commandtest.c
@@ -36,7 +36,7 @@
 #include "command.h"
 #include "files.h"

-#ifndef __linux__
+#ifdef WIN32

 static int
 mymain(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
@@ -625,6 +625,6 @@ mymain(int argc, char **argv)
     return(ret==0 ? EXIT_SUCCESS : EXIT_FAILURE);
 }

-#endif /* __linux__ */
+#endif /* !WIN32 */

 VIRT_TEST_MAIN(mymain)


-- 
Eric Blake   eblake at redhat.com    +1-801-349-2682
Libvirt virtualization library http://libvirt.org

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 619 bytes
Desc: OpenPGP digital signature
URL: <http://listman.redhat.com/archives/libvir-list/attachments/20101202/a41885fe/attachment-0001.sig>


More information about the libvir-list mailing list