Signed-off-by: Peter Krempa <pkrempa(a)redhat.com>
---
docs/docs.rst | 3 -
docs/internals/command.html.in | 596 -------------------------------
docs/internals/meson.build | 1 -
docs/kbase/index.rst | 3 +
docs/kbase/internals/command.rst | 465 ++++++++++++++++++++++++
docs/kbase/internals/meson.build | 1 +
6 files changed, 469 insertions(+), 600 deletions(-)
delete mode 100644 docs/internals/command.html.in
create mode 100644 docs/kbase/internals/command.rst
diff --git a/docs/docs.rst b/docs/docs.rst
index 299c26d09b..a92c7c26ab 100644
--- a/docs/docs.rst
+++ b/docs/docs.rst
@@ -157,9 +157,6 @@ Project development
`Event loop and worker pool <internals/eventloop.html>`__
Libvirt's event loop and worker pool mode
-`Spawning commands <internals/command.html>`__
- Spawning commands from libvirt driver code
-
`RPC protocol & APIs <internals/rpc.html>`__
RPC protocol information and API / dispatch guide
diff --git a/docs/internals/command.html.in b/docs/internals/command.html.in
deleted file mode 100644
index d9f53933c6..0000000000
--- a/docs/internals/command.html.in
+++ /dev/null
@@ -1,596 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE html>
-<html
xmlns="http://www.w3.org/1999/xhtml">
- <body>
- <h1>Spawning processes / commands from libvirt drivers</h1>
-
- <ul id="toc"></ul>
-
- <p>
- This page describes the usage of libvirt APIs for
- spawning processes / commands from libvirt drivers.
- All code is required to use these APIs
- </p>
-
- <h2><a id="posix">Problems with standard POSIX
APIs</a></h2>
-
- <p>
- The POSIX specification includes a number of APIs for
- spawning processes / commands, but they suffer from
- a number of flaws
- </p>
-
- <ul>
- <li><code>fork+exec</code>: The lowest & most flexible
- level, but very hard to use correctly / safely. It
- is easy to leak file descriptors, have unexpected
- signal handler behaviour and not handle edge cases.
- Furthermore, it is not portable to mingw.
- </li>
- <li><code>system</code>: Convenient if you don't care
- about capturing command output, but has the serious
- downside that the command string is interpreted by
- the shell. This makes it very dangerous to use, because
- improperly validated user input can lead to exploits
- via shell meta characters.
- </li>
- <li><code>popen</code>: Inherits the flaws of
- <code>system</code>, and has no option for bi-directional
- communication.
- </li>
- <li><code>posix_spawn</code>: A half-way house between
- simplicity of system() and the flexibility of fork+exec.
- It does not allow for a couple of important features
- though, such as running a hook between the fork+exec
- stage, or closing all open file descriptors.</li>
- </ul>
-
- <p>
- Due to the problems mentioned with each of these,
- libvirt driver code <strong>must not use</strong> any
- of the above APIs. Historically libvirt provided a
- higher level API known as virExec. This was wrapper
- around fork+exec, in a similar style to posix_spawn,
- but with a few more features.
- </p>
-
- <p>
- This wrapper still suffered from a number of problems.
- Handling command cleanup via waitpid() is overly
- complex & error prone for most usage. Building up the
- argv[] + env[] string arrays is quite cumbersome and
- error prone, particularly wrt memory leak / OOM handling.
- </p>
-
- <h2><a id="api">The libvirt command execution
API</a></h2>
-
- <p>
- There is now a high level API that provides a safe and
- flexible way to spawn commands, which prevents the most
- common errors & is easy to code against. This
- code is provided in the <code>src/util/vircommand.h</code>
- header which can be imported using <code>#include
"vircommand.h"</code>
- </p>
-
- <h3><a id="initial">Defining commands in
libvirt</a></h3>
-
- <p>
- The first step is to declare what command is to be
- executed. The command name can be either a fully
- qualified path, or a bare command name. In the latter
- case it will be resolved wrt the <code>$PATH</code>
- environment variable.
- </p>
-
-<pre>
-virCommand *cmd = virCommandNew("/usr/bin/dnsmasq");
-</pre>
-
- <p>
- There is no need to check for allocation failure after
- <code>virCommandNew</code>. This will be detected and
- reported at a later time.
- </p>
-
- <h3><a id="args">Adding arguments to the
command</a></h3>
-
- <p>
- There are a number of APIs for adding arguments to a
- command. To add a direct string arg
- </p>
-
-<pre>
-virCommandAddArg(cmd, "-strict-order");
-</pre>
-
- <p>
- If an argument takes an attached value of the form
- <code>-arg=val</code>, then this can be done using
- </p>
-
-<pre>
-virCommandAddArgPair(cmd, "--conf-file", "/etc/dnsmasq.conf");
-</pre>
-
- <p>
- If an argument needs to be formatted as if by
- <code>printf</code>:
- </p>
-
-<pre>
-virCommandAddArgFormat(cmd, "%d", count);
-</pre>
-
- <p>
- To add an entire NULL terminated array of arguments in one go,
- there are two options.
- </p>
-
-<pre>
-const char *const args[] = {
- "--strict-order", "--except-interface", "lo", NULL
-};
-virCommandAddArgSet(cmd, args);
-virCommandAddArgList(cmd, "--domain", "localdomain", NULL);
-</pre>
-
- <p>
- This can also be done at the time of initial construction of
- the <code>virCommand *</code> object:
- </p>
-
-<pre>
-const char *const args[] = {
- "/usr/bin/dnsmasq",
- "--strict-order", "--except-interface",
- "lo", "--domain", "localdomain", NULL
-};
-virCommand *cmd1 = virCommandNewArgs(cmd, args);
-virCommand *cmd2 = virCommandNewArgList("/usr/bin/dnsmasq",
- "--domain", "localdomain",
NULL);
-</pre>
-
- <h3><a id="env">Setting up the
environment</a></h3>
-
- <p>
- By default a command will inherit all environment variables
- from the current process. Generally this is not desirable
- and a customized environment will be more suitable. Any
- customization done via the following APIs will prevent
- inheritance of any existing environment variables unless
- explicitly allowed. The first step is usually to pass through
- a small number of variables from the current process.
- </p>
-
-<pre>
-virCommandAddEnvPassCommon(cmd);
-</pre>
-
- <p>
- This has now set up a clean environment for the child, passing
- through <code>PATH</code>, <code>LD_PRELOAD</code>,
- <code>LD_LIBRARY_PATH</code>, <code>HOME</code>,
- <code>USER</code>, <code>LOGNAME</code> and
<code>TMPDIR</code>.
- Furthermore it will explicitly set <code>LC_ALL=C</code> to
- avoid unexpected localization of command output. Further
- variables can be passed through from parent explicitly:
- </p>
-
-<pre>
-virCommandAddEnvPass(cmd, "DISPLAY");
-virCommandAddEnvPass(cmd, "XAUTHORITY");
-</pre>
-
- <p>
- To define an environment variable in the child with an
- separate key / value:
- </p>
-
-<pre>
-virCommandAddEnvPair(cmd, "TERM", "xterm");
-</pre>
-
- <p>
- If the key/value pair is pre-formatted in the right
- format, it can be set directly
- </p>
-
-<pre>
-virCommandAddEnvString(cmd, "TERM=xterm");
-</pre>
-
- <h3><a id="misc">Miscellaneous other
options</a></h3>
-
- <p>
- Normally the spawned command will retain the current
- process and process group as its parent. If the current
- process dies, the child will then (usually) be terminated
- too. If this cleanup is not desired, then the command
- should be marked as daemonized:
- </p>
-
-<pre>
-virCommandDaemonize(cmd);
-</pre>
-
- <p>
- When daemonizing a command, the PID visible from the
- caller will be that of the intermediate process, not
- the actual damonized command. If the PID of the real
- command is required then a pidfile can be requested
- </p>
-
-<pre>
-virCommandSetPidFile(cmd, "/var/run/dnsmasq.pid");
-</pre>
-
- <p>
- This PID file is guaranteed to be written before
- the intermediate process exits. Moreover, the daemonized
- process will inherit the FD of the opened and locked PID
- file.
- </p>
-
- <h3><a id="privs">Reducing command
privileges</a></h3>
-
- <p>
- Normally a command will inherit all privileges of
- the current process. To restrict what a command can
- do, it is possible to request that all its capabilities
- are cleared. With this done it will only be able to
- access resources for which it has explicit DAC permissions
- </p>
-
-<pre>
-virCommandClearCaps(cmd);
-</pre>
-
- <h3><a id="fds">Managing file handles</a></h3>
-
- <p>
- To prevent unintended resource leaks to child processes, the
- child defaults to closing all open file handles, and setting
- stdin/out/err to <code>/dev/null</code>. It is possible to
- allow an open file handle to be passed into the child, while
- controlling whether that handle remains open in the parent or
- guaranteeing that the handle will be closed in the parent after
- virCommandRun, virCommandRunAsync, or virCommandFree.
- </p>
-
-<pre>
-int sharedfd = open("cmd.log", "w+");
-int childfd = open("conf.txt", "r");
-virCommandPassFD(cmd, sharedfd, 0);
-virCommandPassFD(cmd, childfd,
- VIR_COMMAND_PASS_FD_CLOSE_PARENT);
-if (VIR_CLOSE(sharedfd) < 0)
- goto cleanup;
-</pre>
-
- <p>
- With this, both file descriptors sharedfd and childfd in the
- current process remain open as the same file descriptors in the
- child. Meanwhile, after the child is spawned, sharedfd remains
- open in the parent, while childfd is closed.
- </p>
-
- <p>
- For stdin/out/err it is sometimes necessary to map a file
- handle. If a mapped file handle is a pipe fed or consumed by
- the caller, then the caller should use virCommandDaemonize or
- virCommandRunAsync rather than virCommandRun to avoid deadlock
- (mapping a regular file is okay with virCommandRun). To attach
- file descriptor 7 in the current process to stdin in the child:
- </p>
-
-<pre>
-virCommandSetInputFD(cmd, 7);
-</pre>
-
- <p>
- Equivalently to redirect stdout or stderr in the child,
- pass in a pointer to the desired handle
- </p>
-
-<pre>
-int outfd = open("out.log", "w+");
-int errfd = open("err.log", "w+");
-virCommandSetOutputFD(cmd, &outfd);
-virCommandSetErrorFD(cmd, &errfd);
-</pre>
-
- <p>
- Alternatively it is possible to request that a pipe be
- created to fetch stdout/err in the parent, by initializing
- the FD to -1.
- </p>
-
-<pre>
-int outfd = -1;
-int errfd = -1
-virCommandSetOutputFD(cmd, &outfd);
-virCommandSetErrorFD(cmd, &errfd);
-</pre>
-
- <p>
- Once the command is running, <code>outfd</code>
- and <code>errfd</code> will be initialized with
- valid file handles that can be read from. It is
- permissible to pass the same pointer for both outfd
- and errfd, in which case both standard streams in
- the child will share the same fd in the parent.
- </p>
-
- <p>
- Normally, file descriptors opened to collect output from a child
- process perform blocking I/O, but the parent process can request
- non-blocking mode:
- </p>
-
-<pre>
-virCommandNonblockingFDs(cmd);
-</pre>
-
- <h3><a id="buffers">Feeding & capturing strings to/from
the child</a></h3>
-
- <p>
- Often dealing with file handles for stdin/out/err is
- unnecessarily complex; an alternative is to let virCommandRun
- perform the I/O and interact via string buffers. Use of a buffer
- only works with virCommandRun, and cannot be mixed with pipe
- file descriptors. That is, the choice is generally between
- managing all I/O in the caller (any fds not specified are tied
- to /dev/null), or letting virCommandRun manage all I/O via
- strings (unspecified stdin is tied to /dev/null, and unspecified
- output streams get logged but are otherwise discarded).
- </p>
-
- <p>
- It is possible to specify a string buffer to act as the data
- source for the child's stdin, if there are no embedded NUL
- bytes, and if the command will be run with virCommandRun:
- </p>
-
-<pre>
-const char *input = "Hello World\n";
-virCommandSetInputBuffer(cmd, input);
-</pre>
-
- <p>
- Similarly it is possible to request that the child's
- stdout/err be redirected into a string buffer, if the
- output is not expected to contain NUL bytes, and if
- the command will be run with virCommandRun:
- </p>
-
-<pre>
-char *output = NULL, *errors = NULL;
-virCommandSetOutputBuffer(cmd, &output);
-virCommandSetErrorBuffer(cmd, &errors);
-</pre>
-
- <p>
- Once the command has finished executing, these buffers will
- contain the output. Allocation is guaranteed if virCommandRun
- or virCommandWait succeed (if there was no output, then the
- buffer will contain an allocated empty string); if the command
- failed, then the buffers usually contain a best-effort
- allocation of collected information (however, on an
- out-of-memory condition, the buffer may still be NULL). The
- caller is responsible for freeing registered buffers, since the
- buffers are designed to persist beyond virCommandFree. It
- is possible to pass the same pointer to both
- virCommandSetOutputBuffer and virCommandSetErrorBuffer, in which
- case the child process interleaves output into a single string.
- </p>
-
- <h3><a id="directory">Setting working
directory</a></h3>
-
- <p>
- Daemonized commands are always run with "/" as the current
- working directory. All other commands default to running in the
- same working directory as the parent process, but an alternate
- directory can be specified:
- </p>
-
-<pre>
-virCommandSetWorkingDirectory(cmd, LOCALSTATEDIR);
-</pre>
-
- <h3><a id="hooks">Any additional hooks</a></h3>
-
- <p>
- If anything else is needed, it is possible to request a hook
- function that is called in the child after the fork, as the
- last thing before changing directories, dropping capabilities,
- and executing the new process. If hook(opaque) returns
- non-zero, then the child process will not be run.
- </p>
-
-<pre>
-virCommandSetPreExecHook(cmd, hook, opaque);
-</pre>
-
- <h3><a id="logging">Logging commands</a></h3>
-
- <p>
- Sometimes, it is desirable to log what command will be run, or
- even to use virCommand solely for creation of a single
- consolidated string without running anything.
- </p>
-
-<pre>
-int logfd = ...;
-char *timestamp = virTimestamp();
-char *string = NULL;
-
-dprintf(logfd, "%s: ", timestamp);
-VIR_FREE(timestamp);
-virCommandWriteArgLog(cmd, logfd);
-
-string = virCommandToString(cmd, false);
-if (string)
- VIR_DEBUG("about to run %s", string);
-VIR_FREE(string);
-if (virCommandRun(cmd, NULL) < 0)
- return -1;
-</pre>
-
- <h3><a id="sync">Running commands
synchronously</a></h3>
-
- <p>
- For most commands, the desired behaviour is to spawn
- the command, wait for it to complete & exit and then
- check that its exit status is zero
- </p>
-
-<pre>
-if (virCommandRun(cmd, NULL) < 0)
- return -1;
-</pre>
-
- <p>
- <strong>Note:</strong> if the command has been daemonized
- this will only block & wait for the intermediate process,
- not the real command. <code>virCommandRun</code> will
- report on any errors that have occurred upon this point
- with all previous API calls. If the command fails to
- run, or exits with non-zero status an error will be
- reported via normal libvirt error infrastructure. If a
- non-zero exit status can represent a success condition,
- it is possible to request the exit status and perform
- that check manually instead of letting <code>virCommandRun</code>
- raise the error. By default, the captured status is only
- for a normal exit (death from a signal is treated as an error),
- but a caller can use <code>virCommandRawStatus</code> to get
- encoded status that includes any terminating signals.
- </p>
-
-<pre>
-int status;
-if (virCommandRun(cmd, &status) < 0)
- return -1;
-if (status == 1) {
- ...do stuff...
-}
-
-virCommandRawStatus(cmd2);
-if (virCommandRun(cmd2, &status) < 0)
- return -1;
-if (WIFEXITED(status) && WEXITSTATUS(status) == 1) {
- ...do stuff...
-}
-</pre>
-
- <h3><a id="async">Running commands
asynchronously</a></h3>
-
- <p>
- In certain complex scenarios, particularly special
- I/O handling is required for the child's stdin/err/out
- it will be necessary to run the command asynchronously
- and wait for completion separately.
- </p>
-
-<pre>
-pid_t pid;
-if (virCommandRunAsync(cmd, &pid) < 0)
- return -1;
-
-... do something while pid is running ...
-
-int status;
-if (virCommandWait(cmd, &status) < 0)
- return -1;
-
-if (WEXITSTATUS(status)...) {
- ..do stuff..
-}
-</pre>
-
- <p>
- As with <code>virCommandRun</code>, the
<code>status</code>
- arg for <code>virCommandWait</code> can be omitted, in which
- case it will validate that exit status is zero and raise an
- 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 id="release">Releasing resources</a></h3>
-
- <p>
- Once the command has been executed, or if execution
- has been abandoned, it is necessary to release
- resources associated with the <code>virCommand *</code>
- object. This is done with:
- </p>
-
-<pre>
-virCommandFree(cmd);
-</pre>
-
- <p>
- There is no need to check if <code>cmd</code> is NULL
- before calling <code>virCommandFree</code>. This scenario
- is handled automatically. If the command is still running,
- it will be forcibly killed and cleaned up (via waitpid).
- </p>
-
- <h2><a id="example">Complete examples</a></h2>
-
- <p>
- This shows a complete example usage of the APIs roughly
- using the libvirt source src/util/hooks.c
- </p>
-
-<pre>
-int runhook(const char *drvstr, const char *id,
- const char *opstr, const char *subopstr,
- const char *extra)
-{
- g_autofree char *path = NULL;
- g_autoptr(virCommand) cmd = NULL;
-
- virBuildPath(&path, LIBVIRT_HOOK_DIR, drvstr);
-
- cmd = virCommandNew(path);
-
- virCommandAddEnvPassCommon(cmd);
-
- virCommandAddArgList(cmd, id, opstr, subopstr, extra, NULL);
-
- virCommandSetInputBuffer(cmd, input);
-
- return virCommandRun(cmd, NULL);
-}
-</pre>
-
- <p>
- In this example, the command is being run synchronously.
- A pre-formatted string is being fed to the command as
- its stdin. The command takes four arguments, and has a
- minimal set of environment variables passed down. In
- this example, the code does not require any error checking.
- All errors are reported by the <code>virCommandRun</code>
- method, and the exit status from this is returned to
- the caller to handle as desired.
- </p>
-
- </body>
-</html>
diff --git a/docs/internals/meson.build b/docs/internals/meson.build
index 298a55dd88..e5f4bb0a4b 100644
--- a/docs/internals/meson.build
+++ b/docs/internals/meson.build
@@ -1,5 +1,4 @@
internals_in_files = [
- 'command',
'eventloop',
'locking',
'rpc',
diff --git a/docs/kbase/index.rst b/docs/kbase/index.rst
index c6748e8883..01ec5a070d 100644
--- a/docs/kbase/index.rst
+++ b/docs/kbase/index.rst
@@ -85,3 +85,6 @@ Internals
`VM migration internals <internals/migration.html>`__
VM migration implementation details, complementing the info in
`migration <../migration.html>`__
+
+`Spawning commands <internals/command.html>`__
+ Spawning commands from libvirt driver code
diff --git a/docs/kbase/internals/command.rst b/docs/kbase/internals/command.rst
new file mode 100644
index 0000000000..738fb5930a
--- /dev/null
+++ b/docs/kbase/internals/command.rst
@@ -0,0 +1,465 @@
+==================================================
+Spawning processes / commands from libvirt drivers
+==================================================
+
+.. contents::
+
+This page describes the usage of libvirt APIs for spawning processes / commands
+from libvirt drivers. All code is required to use these APIs
+
+Problems with standard POSIX APIs
+---------------------------------
+
+The POSIX specification includes a number of APIs for spawning processes /
+commands, but they suffer from a number of flaws
+
+- ``fork+exec``: The lowest & most flexible level, but very hard to use
+ correctly / safely. It is easy to leak file descriptors, have unexpected
+ signal handler behaviour and not handle edge cases. Furthermore, it is not
+ portable to mingw.
+- ``system``: Convenient if you don't care about capturing command output, but
+ has the serious downside that the command string is interpreted by the shell.
+ This makes it very dangerous to use, because improperly validated user input
+ can lead to exploits via shell meta characters.
+- ``popen``: Inherits the flaws of ``system``, and has no option for
+ bi-directional communication.
+- ``posix_spawn``: A half-way house between simplicity of system() and the
+ flexibility of fork+exec. It does not allow for a couple of important
+ features though, such as running a hook between the fork+exec stage, or
+ closing all open file descriptors.
+
+Due to the problems mentioned with each of these, libvirt driver code **must not
+use** any of the above APIs. Historically libvirt provided a higher level API
+known as virExec. This was wrapper around fork+exec, in a similar style to
+posix_spawn, but with a few more features.
+
+This wrapper still suffered from a number of problems. Handling command cleanup
+via waitpid() is overly complex & error prone for most usage. Building up the
+argv[] + env[] string arrays is quite cumbersome and error prone, particularly
+wrt memory leak / OOM handling.
+
+The libvirt command execution API
+---------------------------------
+
+There is now a high level API that provides a safe and flexible way to spawn
+commands, which prevents the most common errors & is easy to code against. This
+code is provided in the ``src/util/vircommand.h`` header which can be imported
+using ``#include "vircommand.h"``
+
+Defining commands in libvirt
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The first step is to declare what command is to be executed. The command name
+can be either a fully qualified path, or a bare command name. In the latter case
+it will be resolved wrt the ``$PATH`` environment variable.
+
+::
+
+ virCommand *cmd = virCommandNew("/usr/bin/dnsmasq");
+
+There is no need to check for allocation failure after ``virCommandNew``. This
+will be detected and reported at a later time.
+
+Adding arguments to the command
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a number of APIs for adding arguments to a command. To add a direct
+string arg
+
+::
+
+ virCommandAddArg(cmd, "-strict-order");
+
+If an argument takes an attached value of the form ``-arg=val``, then this can
+be done using
+
+::
+
+ virCommandAddArgPair(cmd, "--conf-file", "/etc/dnsmasq.conf");
+
+If an argument needs to be formatted as if by ``printf``:
+
+::
+
+ virCommandAddArgFormat(cmd, "%d", count);
+
+To add an entire NULL terminated array of arguments in one go, there are two
+options.
+
+::
+
+ const char *const args[] = {
+ "--strict-order", "--except-interface", "lo", NULL
+ };
+ virCommandAddArgSet(cmd, args);
+ virCommandAddArgList(cmd, "--domain", "localdomain", NULL);
+
+This can also be done at the time of initial construction of the
+``virCommand *`` object:
+
+::
+
+ const char *const args[] = {
+ "/usr/bin/dnsmasq",
+ "--strict-order", "--except-interface",
+ "lo", "--domain", "localdomain", NULL
+ };
+ virCommand *cmd1 = virCommandNewArgs(cmd, args);
+ virCommand *cmd2 = virCommandNewArgList("/usr/bin/dnsmasq",
+ "--domain",
"localdomain", NULL);
+
+Setting up the environment
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+By default a command will inherit all environment variables from the current
+process. Generally this is not desirable and a customized environment will be
+more suitable. Any customization done via the following APIs will prevent
+inheritance of any existing environment variables unless explicitly allowed. The
+first step is usually to pass through a small number of variables from the
+current process.
+
+::
+
+ virCommandAddEnvPassCommon(cmd);
+
+This has now set up a clean environment for the child, passing through ``PATH``,
+``LD_PRELOAD``, ``LD_LIBRARY_PATH``, ``HOME``, ``USER``, ``LOGNAME`` and
+``TMPDIR``. Furthermore it will explicitly set ``LC_ALL=C`` to avoid unexpected
+localization of command output. Further variables can be passed through from
+parent explicitly:
+
+::
+
+ virCommandAddEnvPass(cmd, "DISPLAY");
+ virCommandAddEnvPass(cmd, "XAUTHORITY");
+
+To define an environment variable in the child with an separate key / value:
+
+::
+
+ virCommandAddEnvPair(cmd, "TERM", "xterm");
+
+If the key/value pair is pre-formatted in the right format, it can be set
+directly
+
+::
+
+ virCommandAddEnvString(cmd, "TERM=xterm");
+
+Miscellaneous other options
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Normally the spawned command will retain the current process and process group
+as its parent. If the current process dies, the child will then (usually) be
+terminated too. If this cleanup is not desired, then the command should be
+marked as daemonized:
+
+::
+
+ virCommandDaemonize(cmd);
+
+When daemonizing a command, the PID visible from the caller will be that of the
+intermediate process, not the actual damonized command. If the PID of the real
+command is required then a pidfile can be requested
+
+::
+
+ virCommandSetPidFile(cmd, "/var/run/dnsmasq.pid");
+
+This PID file is guaranteed to be written before the intermediate process exits.
+Moreover, the daemonized process will inherit the FD of the opened and locked
+PID file.
+
+Reducing command privileges
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Normally a command will inherit all privileges of the current process. To
+restrict what a command can do, it is possible to request that all its
+capabilities are cleared. With this done it will only be able to access
+resources for which it has explicit DAC permissions
+
+::
+
+ virCommandClearCaps(cmd);
+
+Managing file handles
+~~~~~~~~~~~~~~~~~~~~~
+
+To prevent unintended resource leaks to child processes, the child defaults to
+closing all open file handles, and setting stdin/out/err to ``/dev/null``. It is
+possible to allow an open file handle to be passed into the child, while
+controlling whether that handle remains open in the parent or guaranteeing that
+the handle will be closed in the parent after virCommandRun, virCommandRunAsync,
+or virCommandFree.
+
+::
+
+ int sharedfd = open("cmd.log", "w+");
+ int childfd = open("conf.txt", "r");
+ virCommandPassFD(cmd, sharedfd, 0);
+ virCommandPassFD(cmd, childfd,
+ VIR_COMMAND_PASS_FD_CLOSE_PARENT);
+ if (VIR_CLOSE(sharedfd) < 0)
+ goto cleanup;
+
+With this, both file descriptors sharedfd and childfd in the current process
+remain open as the same file descriptors in the child. Meanwhile, after the
+child is spawned, sharedfd remains open in the parent, while childfd is closed.
+
+For stdin/out/err it is sometimes necessary to map a file handle. If a mapped
+file handle is a pipe fed or consumed by the caller, then the caller should use
+virCommandDaemonize or virCommandRunAsync rather than virCommandRun to avoid
+deadlock (mapping a regular file is okay with virCommandRun). To attach file
+descriptor 7 in the current process to stdin in the child:
+
+::
+
+ virCommandSetInputFD(cmd, 7);
+
+Equivalently to redirect stdout or stderr in the child, pass in a pointer to the
+desired handle
+
+::
+
+ int outfd = open("out.log", "w+");
+ int errfd = open("err.log", "w+");
+ virCommandSetOutputFD(cmd, &outfd);
+ virCommandSetErrorFD(cmd, &errfd);
+
+Alternatively it is possible to request that a pipe be created to fetch
+stdout/err in the parent, by initializing the FD to -1.
+
+::
+
+ int outfd = -1;
+ int errfd = -1
+ virCommandSetOutputFD(cmd, &outfd);
+ virCommandSetErrorFD(cmd, &errfd);
+
+Once the command is running, ``outfd`` and ``errfd`` will be initialized with
+valid file handles that can be read from. It is permissible to pass the same
+pointer for both outfd and errfd, in which case both standard streams in the
+child will share the same fd in the parent.
+
+Normally, file descriptors opened to collect output from a child process perform
+blocking I/O, but the parent process can request non-blocking mode:
+
+::
+
+ virCommandNonblockingFDs(cmd);
+
+Feeding & capturing strings to/from the child
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Often dealing with file handles for stdin/out/err is unnecessarily complex; an
+alternative is to let virCommandRun perform the I/O and interact via string
+buffers. Use of a buffer only works with virCommandRun, and cannot be mixed with
+pipe file descriptors. That is, the choice is generally between managing all I/O
+in the caller (any fds not specified are tied to /dev/null), or letting
+virCommandRun manage all I/O via strings (unspecified stdin is tied to
+/dev/null, and unspecified output streams get logged but are otherwise
+discarded).
+
+It is possible to specify a string buffer to act as the data source for the
+child's stdin, if there are no embedded NUL bytes, and if the command will be
+run with virCommandRun:
+
+::
+
+ const char *input = "Hello World\n";
+ virCommandSetInputBuffer(cmd, input);
+
+Similarly it is possible to request that the child's stdout/err be redirected
+into a string buffer, if the output is not expected to contain NUL bytes, and if
+the command will be run with virCommandRun:
+
+::
+
+ char *output = NULL, *errors = NULL;
+ virCommandSetOutputBuffer(cmd, &output);
+ virCommandSetErrorBuffer(cmd, &errors);
+
+Once the command has finished executing, these buffers will contain the output.
+Allocation is guaranteed if virCommandRun or virCommandWait succeed (if there
+was no output, then the buffer will contain an allocated empty string); if the
+command failed, then the buffers usually contain a best-effort allocation of
+collected information (however, on an out-of-memory condition, the buffer may
+still be NULL). The caller is responsible for freeing registered buffers, since
+the buffers are designed to persist beyond virCommandFree. It is possible to
+pass the same pointer to both virCommandSetOutputBuffer and
+virCommandSetErrorBuffer, in which case the child process interleaves output
+into a single string.
+
+Setting working directory
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Daemonized commands are always run with "/" as the current working directory.
+All other commands default to running in the same working directory as the
+parent process, but an alternate directory can be specified:
+
+::
+
+ virCommandSetWorkingDirectory(cmd, LOCALSTATEDIR);
+
+Any additional hooks
+~~~~~~~~~~~~~~~~~~~~
+
+If anything else is needed, it is possible to request a hook function that is
+called in the child after the fork, as the last thing before changing
+directories, dropping capabilities, and executing the new process. If
+hook(opaque) returns non-zero, then the child process will not be run.
+
+::
+
+ virCommandSetPreExecHook(cmd, hook, opaque);
+
+Logging commands
+~~~~~~~~~~~~~~~~
+
+Sometimes, it is desirable to log what command will be run, or even to use
+virCommand solely for creation of a single consolidated string without running
+anything.
+
+::
+
+ int logfd = ...;
+ char *timestamp = virTimestamp();
+ char *string = NULL;
+
+ dprintf(logfd, "%s: ", timestamp);
+ VIR_FREE(timestamp);
+ virCommandWriteArgLog(cmd, logfd);
+
+ string = virCommandToString(cmd, false);
+ if (string)
+ VIR_DEBUG("about to run %s", string);
+ VIR_FREE(string);
+ if (virCommandRun(cmd, NULL) < 0)
+ return -1;
+
+Running commands synchronously
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+For most commands, the desired behaviour is to spawn the command, wait for it to
+complete & exit and then check that its exit status is zero
+
+::
+
+ if (virCommandRun(cmd, NULL) < 0)
+ return -1;
+
+**Note:** if the command has been daemonized this will only block & wait for the
+intermediate process, not the real command. ``virCommandRun`` will report on any
+errors that have occurred upon this point with all previous API calls. If the
+command fails to run, or exits with non-zero status an error will be reported
+via normal libvirt error infrastructure. If a non-zero exit status can represent
+a success condition, it is possible to request the exit status and perform that
+check manually instead of letting ``virCommandRun`` raise the error. By default,
+the captured status is only for a normal exit (death from a signal is treated as
+an error), but a caller can use ``virCommandRawStatus`` to get encoded status
+that includes any terminating signals.
+
+::
+
+ int status;
+ if (virCommandRun(cmd, &status) < 0)
+ return -1;
+ if (status == 1) {
+ ...do stuff...
+ }
+
+ virCommandRawStatus(cmd2);
+ if (virCommandRun(cmd2, &status) < 0)
+ return -1;
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 1) {
+ ...do stuff...
+ }
+
+Running commands asynchronously
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In certain complex scenarios, particularly special I/O handling is required for
+the child's stdin/err/out it will be necessary to run the command asynchronously
+and wait for completion separately.
+
+::
+
+ pid_t pid;
+ if (virCommandRunAsync(cmd, &pid) < 0)
+ return -1;
+
+ ... do something while pid is running ...
+
+ int status;
+ if (virCommandWait(cmd, &status) < 0)
+ return -1;
+
+ if (WEXITSTATUS(status)...) {
+ ..do stuff..
+ }
+
+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.
+
+There are two approaches to child process cleanup, determined by how long you
+want to keep the virCommand object in scope.
+
+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.
+
+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.
+
+Releasing resources
+~~~~~~~~~~~~~~~~~~~
+
+Once the command has been executed, or if execution has been abandoned, it is
+necessary to release resources associated with the ``virCommand *`` object. This
+is done with:
+
+::
+
+ virCommandFree(cmd);
+
+There is no need to check if ``cmd`` is NULL before calling ``virCommandFree``.
+This scenario is handled automatically. If the command is still running, it will
+be forcibly killed and cleaned up (via waitpid).
+
+Complete examples
+-----------------
+
+This shows a complete example usage of the APIs roughly using the libvirt source
+src/util/hooks.c
+
+::
+
+ int runhook(const char *drvstr, const char *id,
+ const char *opstr, const char *subopstr,
+ const char *extra)
+ {
+ g_autofree char *path = NULL;
+ g_autoptr(virCommand) cmd = NULL;
+
+ virBuildPath(&path, LIBVIRT_HOOK_DIR, drvstr);
+
+ cmd = virCommandNew(path);
+
+ virCommandAddEnvPassCommon(cmd);
+
+ virCommandAddArgList(cmd, id, opstr, subopstr, extra, NULL);
+
+ virCommandSetInputBuffer(cmd, input);
+
+ return virCommandRun(cmd, NULL);
+ }
+
+In this example, the command is being run synchronously. A pre-formatted string
+is being fed to the command as its stdin. The command takes four arguments, and
+has a minimal set of environment variables passed down. In this example, the
+code does not require any error checking. All errors are reported by the
+``virCommandRun`` method, and the exit status from this is returned to the
+caller to handle as desired.
diff --git a/docs/kbase/internals/meson.build b/docs/kbase/internals/meson.build
index 923e262706..3486b21852 100644
--- a/docs/kbase/internals/meson.build
+++ b/docs/kbase/internals/meson.build
@@ -1,4 +1,5 @@
docs_kbase_internals_files = [
+ 'command',
'incremental-backup',
'migration',
]
--
2.35.1