[libvirt] [PATCH 0/3] child process cleanups

In preparing for making iohelper useful with O_DIRECT fds, I noticed a resource leak on failure in fdstreams. Further investigation found that most clients of virCommandRunAsync passed a NULL pointer, and the only one that didn't was risking closing an unrelated process under a pid reuse scenario. Eric Blake (3): command: introduce virPidWait, virPidAbort fdstream: avoid child process leak on error virnetsocket: use new API for uniform child cleanup docs/internals/command.html.in | 17 +++++ src/fdstream.c | 8 +-- src/libvirt_private.syms | 2 + src/rpc/virnetsocket.c | 13 +---- src/util/command.c | 140 ++++++++++++++++++++++++++++------------ src/util/command.h | 28 ++++++++ 6 files changed, 148 insertions(+), 60 deletions(-) -- 1.7.4.4

When using virCommandRunAsync and saving the pid for later, it is useful to be able to reap that pid in the same way that it would have been auto-reaped by virCommand if we had passed NULL for the pid argument in the first place. * src/util/command.c (virPidWait, virPidAbort): New functions, created from... (virCommandWait, virCommandAbort): ...bodies of these. (includes): Drop duplicate <stdlib.h>. Ensure that our pid_t assumptions hold. (virCommandRunAsync): Improve documentation. * src/util/command.h (virPidWait, virPidAbort): New prototypes. * src/libvirt_private.syms: Export them. * docs/internals/command.html.in: Document them. --- docs/internals/command.html.in | 17 +++++ src/libvirt_private.syms | 2 + src/util/command.c | 140 ++++++++++++++++++++++++++++------------ src/util/command.h | 28 ++++++++ 4 files changed, 146 insertions(+), 41 deletions(-) diff --git a/docs/internals/command.html.in b/docs/internals/command.html.in index 27dcf9c..8a194ec 100644 --- a/docs/internals/command.html.in +++ b/docs/internals/command.html.in @@ -497,6 +497,23 @@ error if not. </p> + <p> + There are two approaches to child process cleanup, determined by + how long you want to keep the virCommand object in scope. + </p> + + <p>1. If the virCommand object will outlast the child process, + then pass NULL for the pid argument, and the child process will + automatically be reaped at virCommandFree, unless you reap it + sooner via virCommandWait or virCommandAbort. + </p> + + <p>2. If the child process must exist on at least one code path + after virCommandFree, then pass a pointer for the pid argument. + Later, to clean up the child, call virPidWait or virPidAbort. + Before virCommandFree, you can still use virCommandWait or + virCommandAbort to reap the process. + </p> <h3><a name="release">Releasing resources</a></h3> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 3237d18..f95d341 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -134,6 +134,8 @@ virCommandTranslateStatus; virCommandWait; virCommandWriteArgLog; virFork; +virPidAbort; +virPidWait; virRun; diff --git a/src/util/command.c b/src/util/command.c index eae58b2..c194fb1 100644 --- a/src/util/command.c +++ b/src/util/command.c @@ -41,8 +41,7 @@ #include "files.h" #include "buf.h" #include "ignore-value.h" - -#include <stdlib.h> +#include "verify.h" #define VIR_FROM_THIS VIR_FROM_NONE @@ -50,6 +49,9 @@ virReportErrorHelper(VIR_FROM_NONE, code, __FILE__, \ __FUNCTION__, __LINE__, __VA_ARGS__) +/* We have quite a bit of changes to make if this doesn't hold. */ +verify(sizeof(pid_t) <= sizeof(int)); + /* Flags for virExecWithHook */ enum { VIR_EXEC_NONE = 0, @@ -1897,6 +1899,48 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) /* + * Wait for a child process to complete. + * Return -1 on any error waiting for + * completion. Returns 0 if the command + * finished with the exit status set + */ +int +virPidWait(pid_t pid, int *exitstatus) +{ + int ret; + int status; + + if (pid <= 0) { + virReportSystemError(EINVAL, _("unable to wait for process %d"), pid); + return -1; + } + + /* Wait for intermediate process to exit */ + while ((ret = waitpid(pid, &status, 0)) == -1 && + errno == EINTR); + + if (ret == -1) { + virReportSystemError(errno, _("unable to wait for process %d"), pid); + return -1; + } + + if (exitstatus == NULL) { + if (status != 0) { + char *st = virCommandTranslateStatus(status); + virCommandError(VIR_ERR_INTERNAL_ERROR, + _("Child process (%d) status unexpected: %s"), + pid, NULLSTR(st)); + VIR_FREE(st); + return -1; + } + } else { + *exitstatus = status; + } + + return 0; +} + +/* * Wait for the async command to complete. * Return -1 on any error waiting for * completion. Returns 0 if the command @@ -1906,7 +1950,7 @@ int virCommandWait(virCommandPtr cmd, int *exitstatus) { int ret; - int status; + int status = 0; if (!cmd ||cmd->has_error == ENOMEM) { virReportOOMError(); @@ -1924,22 +1968,17 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) return -1; } - - /* Wait for intermediate process to exit */ - while ((ret = waitpid(cmd->pid, &status, 0)) == -1 && - errno == EINTR); - - if (ret == -1) { - virReportSystemError(errno, _("unable to wait for process %d"), - cmd->pid); - return -1; - } - - cmd->pid = -1; - cmd->reap = false; - - if (exitstatus == NULL) { - if (status != 0) { + /* If virPidWait reaps pid but then returns failure because + * exitstatus was NULL, then a second virCommandWait would risk + * calling waitpid on an unrelated process. Besides, that error + * message is not as detailed as what we can provide. So, we + * guarantee that virPidWait only fails due to failure to wait, + * and repeat the exitstatus check code ourselves. */ + ret = virPidWait(cmd->pid, exitstatus ? exitstatus : &status); + if (ret == 0) { + cmd->pid = -1; + cmd->reap = false; + if (status) { char *str = virCommandToString(cmd); char *st = virCommandTranslateStatus(status); virCommandError(VIR_ERR_INTERNAL_ERROR, @@ -1949,75 +1988,94 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) VIR_FREE(st); return -1; } - } else { - *exitstatus = status; } - return 0; + return ret; } #ifndef WIN32 /* - * Abort an async command if it is running, without issuing - * any errors or affecting errno. Designed for error paths - * where some but not all paths to the cleanup code might - * have started the child process. + * Abort a child process if PID is positive and that child is still + * running, without issuing any errors or affecting errno. Designed + * for error paths where some but not all paths to the cleanup code + * might have started the child process. */ void -virCommandAbort(virCommandPtr cmd) +virPidAbort(pid_t pid) { int saved_errno; int ret; int status; char *tmp = NULL; - if (!cmd || cmd->pid == -1) + if (pid <= 0) return; /* See if intermediate process has exited; if not, try a nice * SIGTERM followed by a more severe SIGKILL. */ saved_errno = errno; - VIR_DEBUG("aborting child process %d", cmd->pid); - while ((ret = waitpid(cmd->pid, &status, WNOHANG)) == -1 && + VIR_DEBUG("aborting child process %d", pid); + while ((ret = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR); - if (ret == cmd->pid) { + if (ret == pid) { tmp = virCommandTranslateStatus(status); VIR_DEBUG("process has ended: %s", tmp); goto cleanup; } else if (ret == 0) { - VIR_DEBUG("trying SIGTERM to child process %d", cmd->pid); - kill(cmd->pid, SIGTERM); + VIR_DEBUG("trying SIGTERM to child process %d", pid); + kill(pid, SIGTERM); usleep(10 * 1000); - while ((ret = waitpid(cmd->pid, &status, WNOHANG)) == -1 && + while ((ret = waitpid(pid, &status, WNOHANG)) == -1 && errno == EINTR); - if (ret == cmd->pid) { + if (ret == pid) { tmp = virCommandTranslateStatus(status); VIR_DEBUG("process has ended: %s", tmp); goto cleanup; } else if (ret == 0) { - VIR_DEBUG("trying SIGKILL to child process %d", cmd->pid); - kill(cmd->pid, SIGKILL); - while ((ret = waitpid(cmd->pid, &status, 0)) == -1 && + VIR_DEBUG("trying SIGKILL to child process %d", pid); + kill(pid, SIGKILL); + while ((ret = waitpid(pid, &status, 0)) == -1 && errno == EINTR); - if (ret == cmd->pid) { + if (ret == pid) { tmp = virCommandTranslateStatus(status); VIR_DEBUG("process has ended: %s", tmp); goto cleanup; } } } - VIR_DEBUG("failed to reap child %d, abandoning it", cmd->pid); + VIR_DEBUG("failed to reap child %d, abandoning it", pid); cleanup: VIR_FREE(tmp); + errno = saved_errno; +} + +/* + * Abort an async command if it is running, without issuing + * any errors or affecting errno. Designed for error paths + * where some but not all paths to the cleanup code might + * have started the child process. + */ +void +virCommandAbort(virCommandPtr cmd) +{ + if (!cmd || cmd->pid == -1) + return; + virPidAbort(cmd->pid); cmd->pid = -1; cmd->reap = false; - errno = saved_errno; } #else /* WIN32 */ void +virPidAbort(pid_t pid) +{ + /* Not yet ported to mingw. Any volunteers? */ + VIR_DEBUG("failed to reap child %d, abandoning it", pid); +} + +void virCommandAbort(virCommandPtr cmd ATTRIBUTE_UNUSED) { /* Mingw lacks WNOHANG and kill(). But since we haven't ported diff --git a/src/util/command.h b/src/util/command.h index e9f4227..c8a04f1 100644 --- a/src/util/command.h +++ b/src/util/command.h @@ -292,11 +292,31 @@ int virCommandRun(virCommandPtr cmd, * Run the command asynchronously * Returns -1 on any error executing the * command. Returns 0 if the command executed. + * + * There are two approaches to child process cleanup. + * 1. Use auto-cleanup, by passing NULL for pid. The child will be + * auto-reaped by virCommandFree, unless you reap it earlier via + * virCommandWait or virCommandAbort. Good for where cmd is in + * scope for the duration of the child process. + * 2. Use manual cleanup, by passing the address of a pid_t variable + * for pid. While cmd is still in scope, you may reap the child via + * virCommandWait or virCommandAbort. But after virCommandFree, if + * you have not yet reaped the child, then it continues to run until + * you call virPidWait or virPidAbort. */ int virCommandRunAsync(virCommandPtr cmd, pid_t *pid) ATTRIBUTE_RETURN_CHECK; /* + * Wait for a child process to complete. + * Return -1 on any error waiting for + * completion. Returns 0 if the command + * finished with the exit status set. + */ +int virPidWait(pid_t pid, + int *exitstatus) ATTRIBUTE_RETURN_CHECK; + +/* * Wait for the async command to complete. * Return -1 on any error waiting for * completion. Returns 0 if the command @@ -328,6 +348,14 @@ int virCommandHandshakeNotify(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; /* + * Abort a child process if PID is positive and that child is still + * running, without issuing any errors or affecting errno. Designed + * for error paths where some but not all paths to the cleanup code + * might have started the child process. + */ +void virPidAbort(pid_t pid); + +/* * Abort an async command if it is running, without issuing * any errors or affecting errno. Designed for error paths * where some but not all paths to the cleanup code might -- 1.7.4.4

2011/7/12 Eric Blake <eblake@redhat.com>:
When using virCommandRunAsync and saving the pid for later, it is useful to be able to reap that pid in the same way that it would have been auto-reaped by virCommand if we had passed NULL for the pid argument in the first place.
* src/util/command.c (virPidWait, virPidAbort): New functions, created from... (virCommandWait, virCommandAbort): ...bodies of these. (includes): Drop duplicate <stdlib.h>. Ensure that our pid_t assumptions hold. (virCommandRunAsync): Improve documentation. * src/util/command.h (virPidWait, virPidAbort): New prototypes. * src/libvirt_private.syms: Export them. * docs/internals/command.html.in: Document them. --- docs/internals/command.html.in | 17 +++++ src/libvirt_private.syms | 2 + src/util/command.c | 140 ++++++++++++++++++++++++++++------------ src/util/command.h | 28 ++++++++ 4 files changed, 146 insertions(+), 41 deletions(-)
ACK. -- Matthias Bolte http://photron.blogspot.com

On 07/14/2011 11:34 AM, Matthias Bolte wrote:
2011/7/12 Eric Blake <eblake@redhat.com>:
When using virCommandRunAsync and saving the pid for later, it is useful to be able to reap that pid in the same way that it would have been auto-reaped by virCommand if we had passed NULL for the pid argument in the first place.
ACK.
I'm pushing with this squashed in, as well as patch 2 and 3 as-is. The suggestion for avoiding the doc duplication between .c and .h will be addressed as an upcoming patch 4/3 (unpushed until it gets reviewed). diff --git i/src/util/command.c w/src/util/command.c index bba82b0..e2ece78 100644 --- i/src/util/command.c +++ w/src/util/command.c @@ -1384,8 +1384,8 @@ virCommandToString(virCommandPtr cmd) /* * Translate an exit status into a malloc'd string. Generic helper - * for virCommandRun and virCommandWait status argument, as well as - * raw waitpid and older virRun status. + * for virCommandRun, virCommandWait, and virPidWait status argument, + * as well as raw waitpid and older virRun status. */ char * virCommandTranslateStatus(int status) @@ -1807,6 +1807,17 @@ virCommandHook(void *data) * Run the command asynchronously * Returns -1 on any error executing the * command. Returns 0 if the command executed. + * + * There are two approaches to child process cleanup. + * 1. Use auto-cleanup, by passing NULL for pid. The child will be + * auto-reaped by virCommandFree, unless you reap it earlier via + * virCommandWait or virCommandAbort. Good for where cmd is in + * scope for the duration of the child process. + * 2. Use manual cleanup, by passing the address of a pid_t variable + * for pid. While cmd is still in scope, you may reap the child via + * virCommandWait or virCommandAbort. But after virCommandFree, if + * you have not yet reaped the child, then it continues to run until + * you call virPidWait or virPidAbort. */ int virCommandRunAsync(virCommandPtr cmd, pid_t *pid) @@ -2198,7 +2209,9 @@ int virCommandHandshakeNotify(virCommandPtr cmd) /* - * Release all resources + * Release all resources. The only exception is that if you called + * virCommandRunAsync with a non-null pid, then the asynchronous child + * is not reaped, and you must call virPidWait() yourself. */ void virCommandFree(virCommandPtr cmd) diff --git i/src/util/command.h w/src/util/command.h index c8a04f1..f766839 100644 --- i/src/util/command.h +++ w/src/util/command.h @@ -366,7 +366,7 @@ void virCommandAbort(virCommandPtr cmd); /* * Release all resources. The only exception is that if you called * virCommandRunAsync with a non-null pid, then the asynchronous child - * is not reaped, and you must call waitpid() yourself. + * is not reaped, and you must call virPidWait() yourself. */ void virCommandFree(virCommandPtr cmd); -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

By requesting the pid in virCommandRunAsync, fdstream was claiming that it would manually wait for the process. But on the failure path, the child process was being leaked. * src/fdstream.c (virFDStreamOpenFileInternal): Auto-reap child. --- src/fdstream.c | 8 +------- 1 files changed, 1 insertions(+), 7 deletions(-) diff --git a/src/fdstream.c b/src/fdstream.c index 54f8198..d111813 100644 --- a/src/fdstream.c +++ b/src/fdstream.c @@ -31,7 +31,6 @@ # include <sys/un.h> #endif #include <netinet/in.h> -#include <signal.h> #include "fdstream.h" #include "virterror_internal.h" @@ -514,7 +513,6 @@ virFDStreamOpenFileInternal(virStreamPtr st, struct stat sb; virCommandPtr cmd = NULL; int errfd = -1; - pid_t pid = 0; VIR_DEBUG("st=%p path=%s flags=%d offset=%llu length=%llu mode=%d delete=%d", st, path, flags, offset, length, mode, delete); @@ -588,7 +586,7 @@ virFDStreamOpenFileInternal(virStreamPtr st, } virCommandSetErrorFD(cmd, &errfd); - if (virCommandRunAsync(cmd, &pid) < 0) + if (virCommandRunAsync(cmd, NULL) < 0) goto error; VIR_FORCE_CLOSE(childfd); @@ -611,10 +609,6 @@ virFDStreamOpenFileInternal(virStreamPtr st, return 0; error: -#ifndef WIN32 - if (pid) - kill(SIGTERM, pid); -#endif virCommandFree(cmd); VIR_FORCE_CLOSE(fds[0]); VIR_FORCE_CLOSE(fds[1]); -- 1.7.4.4

2011/7/12 Eric Blake <eblake@redhat.com>:
By requesting the pid in virCommandRunAsync, fdstream was claiming that it would manually wait for the process. But on the failure path, the child process was being leaked.
This difference in behavior between virCommandRunAsync(..., NULL) and virCommandRunAsync(..., &pid) is not documented on virCommandRunAsync itself but only on virCommandFree. Also virCommandFree refers to waitpid instead of you're newly added virPidWait. Finally I noticed that the comments in command.h and command.c are out of sync (at least for virCommandFree). Why are you documenting in two places making it prone to get out of sync? ACK, to this patch. -- Matthias Bolte http://photron.blogspot.com

On 07/14/2011 11:42 AM, Matthias Bolte wrote:
2011/7/12 Eric Blake <eblake@redhat.com>:
By requesting the pid in virCommandRunAsync, fdstream was claiming that it would manually wait for the process. But on the failure path, the child process was being leaked.
This difference in behavior between virCommandRunAsync(..., NULL) and virCommandRunAsync(..., &pid) is not documented on virCommandRunAsync itself but only on virCommandFree.
That was the case in libvirt.git right now, but I thought I addressed that properly in patch 1/3.
Also virCommandFree refers to waitpid instead of you're newly added virPidWait.
Oh, good point. I'll tweak patch 1 to fix that before I push.
Finally I noticed that the comments in command.h and command.c are out of sync (at least for virCommandFree). Why are you documenting in two places making it prone to get out of sync?
Which file is better, the .c or the .h? I'm fine with dropping docs from one of the two locations, if we have a preference on which file is more likely to be referenced. .c: PRO - the documentation is next to the implementation, and hopefully easier to keep the two in sync PRO - matches how we do things in libvirt.h vs. libvirt.c CON - much larger file to search through when you are looking up the reference .h: PRO - smaller file CON - further away from implementation, could go stale so I'm leaning towards .c only.
ACK, to this patch.
-- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

2011/7/14 Eric Blake <eblake@redhat.com>:
On 07/14/2011 11:42 AM, Matthias Bolte wrote:
2011/7/12 Eric Blake <eblake@redhat.com>:
By requesting the pid in virCommandRunAsync, fdstream was claiming that it would manually wait for the process. But on the failure path, the child process was being leaked.
This difference in behavior between virCommandRunAsync(..., NULL) and virCommandRunAsync(..., &pid) is not documented on virCommandRunAsync itself but only on virCommandFree.
That was the case in libvirt.git right now, but I thought I addressed that properly in patch 1/3.
Oh, true. So that's okay now.
Also virCommandFree refers to waitpid instead of you're newly added virPidWait.
Oh, good point. I'll tweak patch 1 to fix that before I push.
Finally I noticed that the comments in command.h and command.c are out of sync (at least for virCommandFree). Why are you documenting in two places making it prone to get out of sync?
Which file is better, the .c or the .h? I'm fine with dropping docs from one of the two locations, if we have a preference on which file is more likely to be referenced.
.c: PRO - the documentation is next to the implementation, and hopefully easier to keep the two in sync PRO - matches how we do things in libvirt.h vs. libvirt.c CON - much larger file to search through when you are looking up the reference .h: PRO - smaller file CON - further away from implementation, could go stale
so I'm leaning towards .c only.
I prefer .c, but don't just drop them from .h. merge them instead :) -- Matthias Bolte http://photron.blogspot.com

Rather than trying to clean up the ssh child ourselves, and risk subtle differences from the socket creation error path, we can just use the new APIs. * src/rpc/virnetsocket.c (virNetSocketFree): Use new function. --- src/rpc/virnetsocket.c | 13 +------------ 1 files changed, 1 insertions(+), 12 deletions(-) diff --git a/src/rpc/virnetsocket.c b/src/rpc/virnetsocket.c index 41d9954..7ea1ab7 100644 --- a/src/rpc/virnetsocket.c +++ b/src/rpc/virnetsocket.c @@ -652,18 +652,7 @@ void virNetSocketFree(virNetSocketPtr sock) VIR_FORCE_CLOSE(sock->fd); VIR_FORCE_CLOSE(sock->errfd); -#ifndef WIN32 - if (sock->pid > 0) { - pid_t reap; - kill(sock->pid, SIGTERM); - do { -retry: - reap = waitpid(sock->pid, NULL, 0); - if (reap == -1 && errno == EINTR) - goto retry; - } while (reap != -1 && reap != sock->pid); - } -#endif + virPidAbort(sock->pid); VIR_FREE(sock->localAddrStr); VIR_FREE(sock->remoteAddrStr); -- 1.7.4.4

2011/7/12 Eric Blake <eblake@redhat.com>:
Rather than trying to clean up the ssh child ourselves, and risk subtle differences from the socket creation error path, we can just use the new APIs.
* src/rpc/virnetsocket.c (virNetSocketFree): Use new function. --- src/rpc/virnetsocket.c | 13 +------------ 1 files changed, 1 insertions(+), 12 deletions(-)
ACK. -- Matthias Bolte http://photron.blogspot.com

We already have a precedent of function documentation in C files, where it is closer to the implementation (witness libvirt.h vs. libvirt.c); maintaining docs in both files risks docs going stale. While I was at it, I used consistent doxygen style on all comments. * src/util/command.h: Remove duplicate docs, and move unique documentation... * src/util/command.c: ...here. Suggested by Matthias Bolte. --- src/util/command.c | 371 ++++++++++++++++++++++++++++++++++++++++------------ src/util/command.h | 223 ++------------------------------ 2 files changed, 299 insertions(+), 295 deletions(-) diff --git a/src/util/command.c b/src/util/command.c index e2ece78..f3c35ed 100644 --- a/src/util/command.c +++ b/src/util/command.c @@ -130,23 +130,25 @@ static int virClearCapabilities(void) # endif -/* virFork() - fork a new process while avoiding various race/deadlock - conditions - - @pid - a pointer to a pid_t that will receive the return value from - fork() - - on return from virFork(), if *pid < 0, the fork failed and there is - no new process. Otherwise, just like fork(), if *pid == 0, it is the - child process returning, and if *pid > 0, it is the parent. - - Even if *pid >= 0, if the return value from virFork() is < 0, it - indicates a failure that occurred in the parent or child process - after the fork. In this case, the child process should call - _exit(EXIT_FAILURE) after doing any additional error reporting. - +/** + * virFork: + * @pid - a pointer to a pid_t that will receive the return value from + * fork() + * + * fork a new process while avoiding various race/deadlock conditions + * + * on return from virFork(), if *pid < 0, the fork failed and there is + * no new process. Otherwise, just like fork(), if *pid == 0, it is the + * child process returning, and if *pid > 0, it is the parent. + * + * Even if *pid >= 0, if the return value from virFork() is < 0, it + * indicates a failure that occurred in the parent or child process + * after the fork. In this case, the child process should call + * _exit(EXIT_FAILURE) after doing any additional error reporting. */ -int virFork(pid_t *pid) { +int +virFork(pid_t *pid) +{ sigset_t oldmask, newmask; struct sigaction sig_action; int saved_errno, ret = -1; @@ -573,6 +575,7 @@ virExecWithHook(const char *const*argv, } /** + * virRun: * @argv NULL terminated argv to run * @status optional variable to return exit status in * @@ -645,8 +648,13 @@ virFork(pid_t *pid) #endif /* WIN32 */ -/* - * Create a new command for named binary +/** + * virCommandNew: + * @binary: program to run + * + * Create a new command for named binary. If @binary is relative, + * it will be found via a PATH search of the parent's PATH (and not + * any altered PATH set by virCommandAddEnv* commands). */ virCommandPtr virCommandNew(const char *binary) @@ -656,9 +664,13 @@ virCommandNew(const char *binary) return virCommandNewArgs(args); } -/* +/** + * virCommandNewArgs: + * @args: array of arguments + * * Create a new command with a NULL terminated - * set of args, taking binary from args[0] + * set of args, taking binary from args[0]. More arguments can + * be added later. @args[0] is handled like @binary of virCommandNew. */ virCommandPtr virCommandNewArgs(const char *const*args) @@ -684,9 +696,14 @@ virCommandNewArgs(const char *const*args) return cmd; } -/* +/** + * virCommandNewArgList: + * @binary: program to run + * @...: additional arguments + * * Create a new command with a NULL terminated - * list of args, starting with the binary to run + * list of args, starting with the binary to run. More arguments can + * be added later. @binary is handled as in virCommandNew. */ virCommandPtr virCommandNewArgList(const char *binary, ...) @@ -730,9 +747,13 @@ virCommandKeepFD(virCommandPtr cmd, int fd, bool transfer) FD_SET(fd, &cmd->transfer); } -/* +/** + * virCommandPreserveFD: + * @cmd: the command to modify + * @fd: fd to mark for inheritance into child + * * Preserve the specified file descriptor - * in the child, instead of closing it. + * in the child, instead of closing it on exec. * The parent is still responsible for managing fd. */ void @@ -741,9 +762,13 @@ virCommandPreserveFD(virCommandPtr cmd, int fd) return virCommandKeepFD(cmd, fd, false); } -/* +/** + * virCommandTransferFD: + * @cmd: the command to modify + * @fd: fd to reassign to the child + * * Transfer the specified file descriptor - * to the child, instead of closing it. + * to the child, instead of closing it on exec. * Close the fd in the parent during Run/RunAsync/Free. */ void @@ -753,8 +778,13 @@ virCommandTransferFD(virCommandPtr cmd, int fd) } -/* - * Save the child PID in a pidfile +/** + * virCommandSetPidFile: + * @cmd: the command to modify + * @pidfile: filename to use + * + * Save the child PID in a pidfile. The pidfile will be populated + * before the exec of the child. */ void virCommandSetPidFile(virCommandPtr cmd, const char *pidfile) @@ -769,8 +799,11 @@ virCommandSetPidFile(virCommandPtr cmd, const char *pidfile) } -/* - * Remove all capabilities from the child +/** + * virCommandClearCaps: + * @cmd: the command to modify + * + * Remove all capabilities from the child, after any hooks have been run. */ void virCommandClearCaps(virCommandPtr cmd) @@ -783,7 +816,11 @@ virCommandClearCaps(virCommandPtr cmd) #if 0 /* XXX Enable if we have a need for capability management. */ -/* +/** + * virCommandAllowCap: + * @cmd: the command to modify + * @capability: what to allow + * * Re-allow a specific capability */ void @@ -799,8 +836,13 @@ virCommandAllowCap(virCommandPtr cmd, #endif /* 0 */ -/* - * Daemonize the child process +/** + * virCommandDaemonize: + * @cmd: the command to modify + * + * Daemonize the child process. The child will have a current working + * directory of /, and must be started with virCommandRun, which will + * complete as soon as the daemon grandchild has started. */ void virCommandDaemonize(virCommandPtr cmd) @@ -811,7 +853,10 @@ virCommandDaemonize(virCommandPtr cmd) cmd->flags |= VIR_EXEC_DAEMON; } -/* +/** + * virCommandNonblockingFDs: + * @cmd: the command to modify + * * Set FDs created by virCommandSetOutputFD and virCommandSetErrorFD * as non-blocking in the parent. */ @@ -824,8 +869,13 @@ virCommandNonblockingFDs(virCommandPtr cmd) cmd->flags |= VIR_EXEC_NONBLOCK; } -/* - * Add an environment variable to the child created by a printf-style format +/** + * virCommandAddEnvFormat: + * @cmd: the command to modify + * @format: format of arguments, end result must be in name=value format + * @...: arguments to be formatted + * + * Add an environment variable to the child created by a printf-style format. */ void virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...) @@ -854,7 +904,12 @@ virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...) cmd->env[cmd->nenv++] = env; } -/* +/** + * virCommandAddEnvPair: + * @cmd: the command to modify + * @name: variable name, must not contain = + * @value: value to assign to name + * * Add an environment variable to the child * using separate name & value strings */ @@ -865,7 +920,11 @@ virCommandAddEnvPair(virCommandPtr cmd, const char *name, const char *value) } -/* +/** + * virCommandAddEnvString: + * @cmd: the command to modify + * @str: name=value format + * * Add an environment variable to the child * using a preformatted env string FOO=BAR */ @@ -893,7 +952,11 @@ virCommandAddEnvString(virCommandPtr cmd, const char *str) } -/* +/** + * virCommandAddEnvBuffer: + * @cmd: the command to modify + * @buf: buffer that contains name=value string, which will be reset on return + * * Convert a buffer containing preformatted name=value into an * environment variable of the child. * Correctly transfers memory errors or contents from buf to cmd. @@ -918,7 +981,11 @@ virCommandAddEnvBuffer(virCommandPtr cmd, virBufferPtr buf) } -/* +/** + * virCommandAddEnvPass: + * @cmd: the command to modify + * @name: the name to look up in current environment + * * Pass an environment variable to the child * using current process' value */ @@ -935,9 +1002,12 @@ virCommandAddEnvPass(virCommandPtr cmd, const char *name) } -/* +/** + * virCommandAddEnvPassCommon: + * @cmd: the command to modify + * * Set LC_ALL to C, and propagate other essential environment - * variables from the parent process. + * variables (such as PATH) from the parent process. */ void virCommandAddEnvPassCommon(virCommandPtr cmd) @@ -960,7 +1030,11 @@ virCommandAddEnvPassCommon(virCommandPtr cmd) virCommandAddEnvPass(cmd, "TMPDIR"); } -/* +/** + * virCommandAddArg: + * @cmd: the command to modify + * @val: the argument to add + * * Add a command line argument to the child */ void @@ -987,7 +1061,11 @@ virCommandAddArg(virCommandPtr cmd, const char *val) } -/* +/** + * virCommandAddArgBuffer: + * @cmd: the command to modify + * @buf: buffer that contains argument string, which will be reset on return + * * Convert a buffer into a command line argument to the child. * Correctly transfers memory errors or contents from buf to cmd. */ @@ -1011,8 +1089,13 @@ virCommandAddArgBuffer(virCommandPtr cmd, virBufferPtr buf) } -/* - * Add a command line argument created by a printf-style format +/** + * virCommandAddArgFormat: + * @cmd: the command to modify + * @format: format of arguments, end result must be in name=value format + * @...: arguments to be formatted + * + * Add a command line argument created by a printf-style format. */ void virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...) @@ -1041,7 +1124,12 @@ virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...) cmd->args[cmd->nargs++] = arg; } -/* +/** + * virCommandAddArgPair: + * @cmd: the command to modify + * @name: left half of argument + * @value: right half of argument + * * Add "NAME=VAL" as a single command line argument to the child */ void @@ -1050,7 +1138,11 @@ virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val) virCommandAddArgFormat(cmd, "%s=%s", name, val); } -/* +/** + * virCommandAddArgSet: + * @cmd: the command to modify + * @vals: array of arguments to add + * * Add a NULL terminated list of args */ void @@ -1086,8 +1178,12 @@ virCommandAddArgSet(virCommandPtr cmd, const char *const*vals) } } -/* - * Add a NULL terminated list of args +/** + * virCommandAddArgList: + * @cmd: the command to modify + * @...: list of arguments to add + * + * Add a NULL terminated list of args. */ void virCommandAddArgList(virCommandPtr cmd, ...) @@ -1125,7 +1221,11 @@ virCommandAddArgList(virCommandPtr cmd, ...) va_end(list); } -/* +/** + * virCommandSetWorkingDirectory: + * @cmd: the command to modify + * @pwd: directory to use + * * Set the working directory of a non-daemon child process, rather * than the parent's working directory. Daemons automatically get / * without using this call. @@ -1147,8 +1247,13 @@ virCommandSetWorkingDirectory(virCommandPtr cmd, const char *pwd) } -/* - * Feed the child's stdin from a string buffer +/** + * virCommandSetInputBuffer: + * @cmd: the command to modify + * @inbuf: string to feed to stdin + * + * Feed the child's stdin from a string buffer. This requires the use + * of virCommandRun(). */ void virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf) @@ -1168,11 +1273,16 @@ virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf) } -/* +/** + * virCommandSetOutputBuffer: + * @cmd: the command to modify + * @outbuf: address of variable to store malloced result buffer + * * Capture the child's stdout to a string buffer. *outbuf is * guaranteed to be allocated after successful virCommandRun or * virCommandWait, and is best-effort allocated after failed * virCommandRun; caller is responsible for freeing *outbuf. + * This requires the use of virCommandRun. */ void virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf) @@ -1192,11 +1302,16 @@ virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf) } -/* +/** + * virCommandSetErrorBuffer: + * @cmd: the command to modify + * @errbuf: address of variable to store malloced result buffer + * * Capture the child's stderr to a string buffer. *errbuf is * guaranteed to be allocated after successful virCommandRun or * virCommandWait, and is best-effort allocated after failed * virCommandRun; caller is responsible for freeing *errbuf. + * This requires the use of virCommandRun. */ void virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf) @@ -1216,7 +1331,11 @@ virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf) } -/* +/** + * virCommandSetInputFD: + * @cmd: the command to modify + * @infd: the descriptor to use + * * Attach a file descriptor to the child's stdin */ void @@ -1240,8 +1359,14 @@ virCommandSetInputFD(virCommandPtr cmd, int infd) } -/* - * Attach a file descriptor to the child's stdout +/** + * virCommandSetOutputFD: + * @cmd: the command to modify + * @outfd: location of output fd + * + * Attach a file descriptor to the child's stdout. If *@outfd is -1 on + * entry, then a pipe will be created and returned in this variable when + * the child is run. Otherwise, *@outfd is used as the output. */ void virCommandSetOutputFD(virCommandPtr cmd, int *outfd) @@ -1259,8 +1384,15 @@ virCommandSetOutputFD(virCommandPtr cmd, int *outfd) } -/* - * Attach a file descriptor to the child's stderr +/** + * virCommandSetErrorFD: + * @cmd: the command to modify + * @errfd: location of error fd + * + * Attach a file descriptor to the child's stderr. If *@errfd is -1 on + * entry, then a pipe will be created and returned in this variable when + * the child is run. Otherwise, *@errfd is used for error collection, + * and may be the same as outfd given to virCommandSetOutputFD(). */ void virCommandSetErrorFD(virCommandPtr cmd, int *errfd) @@ -1278,10 +1410,18 @@ virCommandSetErrorFD(virCommandPtr cmd, int *errfd) } -/* +/** + * virCommandSetPreExecHook: + * @cmd: the command to modify + * @hook: the hook to run + * @opaque: argument to pass to the hook + * * Run HOOK(OPAQUE) in the child as the last thing before changing * directories, dropping capabilities, and executing the new process. * Force the child to fail if HOOK does not return zero. + * + * Since @hook runs in the child, it should be careful to avoid + * any functions that are not async-signal-safe. */ void virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque) @@ -1299,7 +1439,11 @@ virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque) } -/* +/** + * virCommandWriteArgLog: + * @cmd: the command to log + * @logfd: where to log the results + * * Call after adding all arguments and environment settings, but before * Run/RunAsync, to immediately output the environment and arguments of * cmd to logfd. If virCommandRun cannot succeed (because of an @@ -1337,7 +1481,10 @@ virCommandWriteArgLog(virCommandPtr cmd, int logfd) } -/* +/** + * virCommandToString: + * @cmd: the command to convert + * * Call after adding all arguments and environment settings, but before * Run/RunAsync, to return a string representation of the environment and * arguments of cmd. If virCommandRun cannot succeed (because of an @@ -1382,10 +1529,13 @@ virCommandToString(virCommandPtr cmd) } -/* +/** + * virCommandTranslateStatus: + * @status: child exit status to translate + * * Translate an exit status into a malloc'd string. Generic helper - * for virCommandRun, virCommandWait, and virPidWait status argument, - * as well as raw waitpid and older virRun status. + * for virCommandRun(), virCommandWait(), virRun(), and virPidWait() + * status argument, as well as raw waitpid(). */ char * virCommandTranslateStatus(int status) @@ -1551,10 +1701,13 @@ cleanup: return ret; } -/* +/** + * virCommandExec: + * @cmd: command to run + * * Exec the command, replacing the current process. Meant to be called - * after already forking / cloning, so does not attempt to daemonize or - * preserve any FDs. + * in the hook after already forking / cloning, so does not attempt to + * daemonize or preserve any FDs. * * Returns -1 on any error executing the command. * Will not return on success. @@ -1587,11 +1740,16 @@ int virCommandExec(virCommandPtr cmd ATTRIBUTE_UNUSED) } #endif -/* +/** + * virCommandRun: + * @cmd: command to run + * @exitstatus: optional status collection + * * Run the command and wait for completion. * Returns -1 on any error executing the * command. Returns 0 if the command executed, - * with the exit status set + * with the exit status set. If @exitstatus is NULL, then the + * child must exit with status 0 for this to succeed. */ int virCommandRun(virCommandPtr cmd, int *exitstatus) @@ -1803,7 +1961,11 @@ virCommandHook(void *data) } -/* +/** + * virCommandRunAsync: + * @cmd: command to start + * @pid: optional variable to track child pid + * * Run the command asynchronously * Returns -1 on any error executing the * command. Returns 0 if the command executed. @@ -1909,11 +2071,16 @@ virCommandRunAsync(virCommandPtr cmd, pid_t *pid) } -/* +/** + * virPidWait: + * @pid: child to wait on + * @exitstatus: optional status collection + * * Wait for a child process to complete. * Return -1 on any error waiting for * completion. Returns 0 if the command - * finished with the exit status set + * finished with the exit status set. If @exitstatus is NULL, then the + * child must exit with status 0 for this to succeed. */ int virPidWait(pid_t pid, int *exitstatus) @@ -1951,11 +2118,16 @@ virPidWait(pid_t pid, int *exitstatus) return 0; } -/* - * Wait for the async command to complete. - * Return -1 on any error waiting for +/** + * virCommandWait: + * @cmd: command to wait on + * @exitstatus: optional status collection + * + * 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 + * finished with the exit status set. If @exitstatus is NULL, then the + * child must exit with status 0 for this to succeed. */ int virCommandWait(virCommandPtr cmd, int *exitstatus) @@ -2006,11 +2178,15 @@ virCommandWait(virCommandPtr cmd, int *exitstatus) #ifndef WIN32 -/* +/** + * virPidAbort: + * @pid: child process to kill + * * Abort a child process if PID is positive and that child is still * running, without issuing any errors or affecting errno. Designed * for error paths where some but not all paths to the cleanup code - * might have started the child process. + * might have started the child process. If @pid is 0 or negative, + * this does nothing. */ void virPidAbort(pid_t pid) @@ -2063,7 +2239,10 @@ cleanup: errno = saved_errno; } -/* +/** + * virCommandAbort: + * @cmd: command to abort + * * Abort an async command if it is running, without issuing * any errors or affecting errno. Designed for error paths * where some but not all paths to the cleanup code might @@ -2096,6 +2275,15 @@ virCommandAbort(virCommandPtr cmd ATTRIBUTE_UNUSED) #endif +/** + * virCommandRequireHandshake: + * @cmd: command to modify + * + * Request that the child perform a handshake with + * the parent when the hook function has completed + * execution. The child will not exec() until the + * parent has notified + */ void virCommandRequireHandshake(virCommandPtr cmd) { if (!cmd || cmd->has_error) @@ -2125,6 +2313,13 @@ void virCommandRequireHandshake(virCommandPtr cmd) cmd->handshake = true; } +/** + * virCommandHandshakeWait: + * @cmd: command to wait on + * + * Wait for the child to complete execution of its + * hook function. To be called in the parent. + */ int virCommandHandshakeWait(virCommandPtr cmd) { char c; @@ -2178,6 +2373,13 @@ int virCommandHandshakeWait(virCommandPtr cmd) return 0; } +/** + * virCommandHandshakeNotify: + * @cmd: command to resume + * + * Notify the child that it is OK to exec() the + * real binary now. To be called in the parent. + */ int virCommandHandshakeNotify(virCommandPtr cmd) { char c = '1'; @@ -2208,10 +2410,13 @@ int virCommandHandshakeNotify(virCommandPtr cmd) } -/* +/** + * virCommandFree: + * @cmd: optional command to free + * * Release all resources. The only exception is that if you called * virCommandRunAsync with a non-null pid, then the asynchronous child - * is not reaped, and you must call virPidWait() yourself. + * is not reaped, and you must call virPidWait() or virPidAbort() yourself. */ void virCommandFree(virCommandPtr cmd) diff --git a/src/util/command.h b/src/util/command.h index f766839..1386d57 100644 --- a/src/util/command.h +++ b/src/util/command.h @@ -30,34 +30,18 @@ typedef struct _virCommand virCommand; typedef virCommand *virCommandPtr; /* This will execute in the context of the first child - * after fork() but before execve() */ + * after fork() but before execve(). As such, it is unsafe to + * call any function that is not async-signal-safe. */ typedef int (*virExecHook)(void *data); -/* - * Fork wrapper with extra error checking - */ int virFork(pid_t *pid) ATTRIBUTE_RETURN_CHECK; -/* - * Simple synchronous command wrapper - */ int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK; -/* - * Create a new command for named binary - */ virCommandPtr virCommandNew(const char *binary) ATTRIBUTE_NONNULL(1); -/* - * Create a new command with a NULL terminated - * set of args, taking binary from argv[0] - */ virCommandPtr virCommandNewArgs(const char *const*args) ATTRIBUTE_NONNULL(1); -/* - * Create a new command with a NULL terminated - * list of args, starting with the binary to run - */ virCommandPtr virCommandNewArgList(const char *binary, ...) ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL; @@ -65,310 +49,125 @@ virCommandPtr virCommandNewArgList(const char *binary, ...) * delayed until the Run/RunAsync methods */ -/* - * Preserve the specified file descriptor - * in the child, instead of closing it. - * The parent is still responsible for managing fd. - */ void virCommandPreserveFD(virCommandPtr cmd, int fd); -/* - * Transfer the specified file descriptor - * to the child, instead of closing it. - * Close the fd in the parent during Run/RunAsync/Free. - */ void virCommandTransferFD(virCommandPtr cmd, int fd); -/* - * Save the child PID in a pidfile - */ void virCommandSetPidFile(virCommandPtr cmd, const char *pidfile) ATTRIBUTE_NONNULL(2); -/* - * Remove all capabilities from the child - */ void virCommandClearCaps(virCommandPtr cmd); # if 0 -/* - * Re-allow a specific capability - */ void virCommandAllowCap(virCommandPtr cmd, int capability); # endif -/* - * Daemonize the child process - */ void virCommandDaemonize(virCommandPtr cmd); -/* - * Set FDs created by virCommandSetOutputFD and virCommandSetErrorFD - * as non-blocking in the parent. - */ void virCommandNonblockingFDs(virCommandPtr cmd); -/* - * Add an environment variable to the child created by a printf-style format - */ -void -virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...) - ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3); +void virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...) + ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3); -/* - * Add an environment variable to the child - * using separate name & value strings - */ void virCommandAddEnvPair(virCommandPtr cmd, const char *name, const char *value) ATTRIBUTE_NONNULL(2); -/* - * Add an environemnt variable to the child - * using a preformated env string FOO=BAR - */ void virCommandAddEnvString(virCommandPtr cmd, const char *str) ATTRIBUTE_NONNULL(2); -/* - * Convert a buffer containing preformatted name=value into an - * environment variable of the child. - * Correctly transfers memory errors or contents from buf to cmd. - */ void virCommandAddEnvBuffer(virCommandPtr cmd, virBufferPtr buf); -/* - * Pass an environment variable to the child - * using current process' value - */ void virCommandAddEnvPass(virCommandPtr cmd, const char *name) ATTRIBUTE_NONNULL(2); -/* - * Pass a common set of environment variables - * to the child using current process' values - */ + void virCommandAddEnvPassCommon(virCommandPtr cmd); -/* - * Add a command line argument to the child - */ void virCommandAddArg(virCommandPtr cmd, const char *val) ATTRIBUTE_NONNULL(2); -/* - * Convert a buffer into a command line argument to the child. - * Correctly transfers memory errors or contents from buf to cmd. - */ void virCommandAddArgBuffer(virCommandPtr cmd, virBufferPtr buf); -/* - * Add a command line argument created by a printf-style format - */ void virCommandAddArgFormat(virCommandPtr cmd, const char *format, ...) ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3); -/* - * Add a command line argument to the child - */ void virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3); -/* - * Add a NULL terminated array of args - */ + void virCommandAddArgSet(virCommandPtr cmd, const char *const*vals) ATTRIBUTE_NONNULL(2); -/* - * Add a NULL terminated list of args - */ + void virCommandAddArgList(virCommandPtr cmd, ... /* const char *arg, ..., NULL */) ATTRIBUTE_SENTINEL; -/* - * Set the working directory of a non-daemon child process, rather - * than the parent's working directory. Daemons automatically get / - * without using this call. - */ void virCommandSetWorkingDirectory(virCommandPtr cmd, const char *pwd) ATTRIBUTE_NONNULL(2); -/* - * Feed the child's stdin from a string buffer. - * - * NB: Only works with virCommandRun() - */ void virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf) ATTRIBUTE_NONNULL(2); -/* - * Capture the child's stdout to a string buffer - * - * NB: Only works with virCommandRun() - */ + void virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf) ATTRIBUTE_NONNULL(2); -/* - * Capture the child's stderr to a string buffer - * - * NB: Only works with virCommandRun() - */ + void virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf) ATTRIBUTE_NONNULL(2); -/* - * Set a file descriptor as the child's stdin - */ void virCommandSetInputFD(virCommandPtr cmd, int infd); -/* - * Set a file descriptor as the child's stdout - */ + void virCommandSetOutputFD(virCommandPtr cmd, int *outfd) ATTRIBUTE_NONNULL(2); -/* - * Set a file descriptor as the child's stderr - */ + void virCommandSetErrorFD(virCommandPtr cmd, int *errfd) ATTRIBUTE_NONNULL(2); -/* - * A hook function to run between fork + exec - */ void virCommandSetPreExecHook(virCommandPtr cmd, virExecHook hook, void *opaque) ATTRIBUTE_NONNULL(2); -/* - * Call after adding all arguments and environment settings, but before - * Run/RunAsync, to immediately output the environment and arguments of - * cmd to logfd. If virCommandRun cannot succeed (because of an - * out-of-memory condition while building cmd), nothing will be logged. - */ void virCommandWriteArgLog(virCommandPtr cmd, int logfd); -/* - * Call after adding all arguments and environment settings, but before - * Run/RunAsync, to return a string representation of the environment and - * arguments of cmd. If virCommandRun cannot succeed (because of an - * out-of-memory condition while building cmd), NULL will be returned. - * Caller is responsible for freeing the resulting string. - */ char *virCommandToString(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; -/* - * Translate an exit status into a malloc'd string. - */ char *virCommandTranslateStatus(int exitstatus) ATTRIBUTE_RETURN_CHECK; -/* - * Exec the command, replacing the current process. Meant to be called - * after already forking / cloning, so does not attempt to daemonize or - * preserve any FDs. - * - * Returns -1 on any error executing the command. - * Will not return on success. - */ int virCommandExec(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; -/* - * Run the command and wait for completion. - * Returns -1 on any error executing the - * command. Returns 0 if the command executed, - * with the exit status set - */ int virCommandRun(virCommandPtr cmd, int *exitstatus) ATTRIBUTE_RETURN_CHECK; -/* - * Run the command asynchronously - * Returns -1 on any error executing the - * command. Returns 0 if the command executed. - * - * There are two approaches to child process cleanup. - * 1. Use auto-cleanup, by passing NULL for pid. The child will be - * auto-reaped by virCommandFree, unless you reap it earlier via - * virCommandWait or virCommandAbort. Good for where cmd is in - * scope for the duration of the child process. - * 2. Use manual cleanup, by passing the address of a pid_t variable - * for pid. While cmd is still in scope, you may reap the child via - * virCommandWait or virCommandAbort. But after virCommandFree, if - * you have not yet reaped the child, then it continues to run until - * you call virPidWait or virPidAbort. - */ int virCommandRunAsync(virCommandPtr cmd, pid_t *pid) ATTRIBUTE_RETURN_CHECK; -/* - * Wait for a child process to complete. - * Return -1 on any error waiting for - * completion. Returns 0 if the command - * finished with the exit status set. - */ int virPidWait(pid_t pid, int *exitstatus) ATTRIBUTE_RETURN_CHECK; -/* - * Wait for the async command to complete. - * Return -1 on any error waiting for - * completion. Returns 0 if the command - * finished with the exit status set - */ int virCommandWait(virCommandPtr cmd, int *exitstatus) ATTRIBUTE_RETURN_CHECK; -/* - * Request that the child perform a handshake with - * the parent when the hook function has completed - * execution. The child will not exec() until the - * parent has notified - */ void virCommandRequireHandshake(virCommandPtr cmd); -/* - * Wait for the child to complete execution of its - * hook function - */ int virCommandHandshakeWait(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; -/* - * Notify the child that it is OK to exec() the - * real binary now - */ int virCommandHandshakeNotify(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK; -/* - * Abort a child process if PID is positive and that child is still - * running, without issuing any errors or affecting errno. Designed - * for error paths where some but not all paths to the cleanup code - * might have started the child process. - */ void virPidAbort(pid_t pid); -/* - * Abort an async command if it is running, without issuing - * any errors or affecting errno. Designed for error paths - * where some but not all paths to the cleanup code might - * have started the child process. - */ void virCommandAbort(virCommandPtr cmd); -/* - * Release all resources. The only exception is that if you called - * virCommandRunAsync with a non-null pid, then the asynchronous child - * is not reaped, and you must call virPidWait() yourself. - */ void virCommandFree(virCommandPtr cmd); - #endif /* __VIR_COMMAND_H__ */ -- 1.7.4.4
participants (2)
-
Eric Blake
-
Matthias Bolte