
On 07/23/2018 08:27 AM, Michal Prívozník wrote:
On 07/23/2018 02:07 PM, John Ferlan wrote:
On 07/23/2018 04:01 AM, Michal Prívozník wrote:
On 07/17/2018 08:43 PM, John Ferlan wrote:
On 07/04/2018 05:23 AM, Michal Privoznik wrote:
The documentation to virCommandWait() function states that if @exitstatus is NULL and command finished with error -1 is returned. In other words, if @dryRunCallback is set and returns an error (by setting its @status argument to a nonzero value) we must propagate this error properly honouring the documentation (and also regular run).
That's not how I read virCommandWait:
* Wait for the command previously started with virCommandRunAsync() * to complete. Return -1 on any error waiting for * completion. Returns 0 if the command * finished with the exit status set. If @exitstatus is NULL, then the * child must exit with status 0 for this to succeed. By default, * a non-NULL @exitstatus contains the normal exit status of the child * (death from a signal is treated as execution error); but if * virCommandRawStatus() was used, it instead contains the raw exit * status that the caller must then decipher using WIFEXITED() and friends.
perhaps the author (danpb) of commit id 7b3f1f8c3 would be able to say for sure...
I only see -1 being returned "on any error waiting for completion". Filling @exitstatus with @dryRunStatus is reasonable since it is initialized to 0 in virCommandRunAsync and is what is passed to @dryRunCallback and thus only changed as a result of running @dryRunCallback.
It has nothing to do with virCommandWait AFAICT.
So there are two ways how virCommandWait() can be called. The first is with @exitstatus being non-NULL. In this case, error is returned iff there was an error fetching command's exit status (e.g. because virProcessWait() failed). The second way is to call virCommandWait() with NULL in which case the function fails for all the cases in the first case plus if the command exit status is not zero. This is documented in docs/internals/command.html#async:
As with virCommandRun, the status arg for virCommandWait can be omitted, in which case it will validate that exit status is zero and raise an error if not.
Let's put aside dry run case for a while. Imagine /bin/false was started asynchronously and control now reaches virCommandWait(cmd, NULL). What do you think should be expected return value? I'd expect "Child process (%) unexpected..." error message and return -1. However, this is not the case if dry run callback sets an error.
Michal
Was /bin/false run successfully? It returns 1 (non zero). Isn't that expected? Did virCommandWait fail to wait for /bin/false to return?
Yes. Yes. No.
If someone wants the status from virCommandRunAsync, then they need to pass the @exitstatus in; otherwise, the command itself actually ran to completion and returned the expected result. If I want to know that result, then I should use the proper mechanism which is to pass @exitstatus.
The virCommandWait didn't fail (regardless of DryRun or not) to wait for completion, so returning -1 because the underlying command "failed" seems to be outside it's scope of purpose.
Okay. I believe picture is more than words. So try this example:
diff --git i/tools/virsh.c w/tools/virsh.c index 62226eea4c..2544fe0d74 100644 --- i/tools/virsh.c +++ w/tools/virsh.c @@ -867,6 +867,18 @@ main(int argc, char **argv) virshControl virshCtl; bool ret = true;
+ virCommandPtr cmd = virCommandNewArgList("/bin/false", NULL); + + if (virCommandRunAsync(cmd, NULL) < 0) { + fprintf(stderr, "async()\n"); + return -1; + } + + if (virCommandWait(cmd, NULL) < 0) { + fprintf(stderr, "wait()\n"); + return -1; + } + memset(ctl, 0, sizeof(vshControl)); memset(&virshCtl, 0, sizeof(virshControl)); ctl->name = "virsh"; /* hardcoded name of the binary */
And try replacing /bin/false with /bin/true. Also, try passing an int to virCommandWait() instead of NULL. You'll see what I mean then.
What you expect me to WORK for the answer ;-) Code as written: $ ./run tools/virsh wait() $ echo $? 255 $ <change from /bin/false to /bin/true> $ ./run tools/virsh virCommandWait exitstatus=1 virsh # quit $ <go back to /bin/false, add &exitstatus to virCommandWait, *and* a print of status> $ ./run tools/virsh virCommandWait exitstatus=0 virsh # quit $ <change from /bin/false to /bin/true> $ ./run tools/virsh virCommandWait exitstatus=0 virsh # quit $ I don't see the problem if you decide to pass @exitstatus to virCommandWait and then choose to ignore actually checking the status, then you WYSIWYG. If in the /bin/false case you checked "if (exitstatus != 0)" and fail, then you'd get what I think you're hinting should happen.
And the whole point is that if we have a dry run callback set and it indicates an error we have to make virCommandWait(, NULL) fail - just like when it's failing with real execution.
If the caller passes @exitstatus to virCommandWait, but doesn't check it's value, then who's coding error is that? I see virCommandWait documented as: "If @exitstatus is NULL, then the child must exit with status 0 for this to succeed." IOW, AIUI, usage of @exitstatus gives one finer grained control over knowing whether the command run by virCommandRunAsync failed or whether virCommandWait failed. If exitstatus == NULL, then if either fails, you get a -1 returned; otherwise, using exitstatus you can determine whether the command failed or the wait for the command to run failed. John