/*
 * vircommand.c: Child command execution
 *
 * Copyright (C) 2010-2014 Red Hat, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 *
 */

#include <config.h>

#ifndef WIN32
# include <poll.h>
#endif
#include <signal.h>
#include <stdarg.h>
#include <sys/stat.h>
#ifndef WIN32
# include <sys/wait.h>
#endif
#include <fcntl.h>
#include <unistd.h>

#if WITH_CAPNG
# include <cap-ng.h>
#endif

#if defined(WITH_SECDRIVER_SELINUX)
# include <selinux/selinux.h>
#endif
#if defined(WITH_SECDRIVER_APPARMOR)
# include <sys/apparmor.h>
#endif

#define LIBVIRT_VIRCOMMANDPRIV_H_ALLOW
#include "viralloc.h"
#include "vircommandpriv.h"
#include "virerror.h"
#include "virutil.h"
#include "virlog.h"
#include "virfile.h"
#include "virpidfile.h"
#include "virprocess.h"
#include "virbuffer.h"
#include "virthread.h"
#include "virstring.h"

#define VIR_FROM_THIS VIR_FROM_NONE

VIR_LOG_INIT("util.command");

/* Flags for virExec */
enum {
    VIR_EXEC_NONE       = 0,
    VIR_EXEC_NONBLOCK   = (1 << 0),
    VIR_EXEC_DAEMON     = (1 << 1),
    VIR_EXEC_CLEAR_CAPS = (1 << 2),
    VIR_EXEC_RUN_SYNC   = (1 << 3),
    VIR_EXEC_ASYNC_IO   = (1 << 4),
};

typedef struct _virCommandFD virCommandFD;
typedef virCommandFD *virCommandFDPtr;

struct _virCommandFD {
    int fd;
    unsigned int flags;
};

typedef struct _virCommandSendBuffer virCommandSendBuffer;
typedef virCommandSendBuffer *virCommandSendBufferPtr;

struct _virCommandSendBuffer {
    int fd;
    unsigned char *buffer;
    size_t buflen;
    off_t offset;
};

struct _virCommand {
    int has_error; /* ENOMEM on allocation failure, -1 for anything else.  */

    char **args;
    size_t nargs;
    size_t maxargs;

    char **env;
    size_t nenv;
    size_t maxenv;

    char *pwd;

    size_t npassfd;
    virCommandFDPtr passfd;

    unsigned int flags;

    char *inbuf;
    char **outbuf;
    char **errbuf;

    int infd;
    int inpipe;
    int outfd;
    int errfd;
    int *outfdptr;
    int *errfdptr;

    virThreadPtr asyncioThread;

    bool handshake;
    int handshakeWait[2];
    int handshakeNotify[2];

    virExecHook hook;
    void *opaque;

    pid_t pid;
    char *pidfile;
    bool reap;
    bool rawStatus;

    unsigned long long maxMemLock;
    unsigned int maxProcesses;
    unsigned int maxFiles;
    bool setMaxCore;
    unsigned long long maxCore;

    uid_t uid;
    gid_t gid;
    unsigned long long capabilities;
#if defined(WITH_SECDRIVER_SELINUX)
    char *seLinuxLabel;
#endif
#if defined(WITH_SECDRIVER_APPARMOR)
    char *appArmorProfile;
#endif
    int mask;

    virCommandSendBufferPtr sendBuffers;
    size_t numSendBuffers;
};

/* See virCommandSetDryRun for description for this variable */
static virBufferPtr dryRunBuffer;
static virCommandDryRunCallback dryRunCallback;
static void *dryRunOpaque;
#ifndef WIN32
static int dryRunStatus;
#endif /* !WIN32 */

/*
 * virCommandFDIsSet:
 * @cmd: pointer to virCommand
 * @fd: file descriptor to query
 *
 * Check if FD is already in @set or not.
 *
 * Returns true if @set contains @fd,
 * false otherwise.
 */
static bool
virCommandFDIsSet(virCommandPtr cmd,
                  int fd)
{
    size_t i = 0;
    if (!cmd)
        return false;

    while (i < cmd->npassfd)
        if (cmd->passfd[i++].fd == fd)
            return true;

    return false;
}

/*
 * virCommandFDSet:
 * @cmd: pointer to virCommand
 * @fd: file descriptor to pass
 * @flags: extra flags; binary-OR of virCommandPassFDFlags
 *
 * This is practically generalized implementation
 * of FD_SET() as we do not want to be limited
 * by FD_SETSIZE.
 *
 * Returns: 0 on success,
 *          -1 on usage error,
 *          ENOMEM on OOM
 */
static int
virCommandFDSet(virCommandPtr cmd,
                int fd,
                unsigned int flags)
{
    if (!cmd || fd < 0)
        return -1;

    if (virCommandFDIsSet(cmd, fd))
        return 0;

    if (VIR_EXPAND_N(cmd->passfd, cmd->npassfd, 1) < 0)
        return ENOMEM;

    cmd->passfd[cmd->npassfd - 1].fd = fd;
    cmd->passfd[cmd->npassfd - 1].flags = flags;

    return 0;
}

#ifndef WIN32

static void virDummyHandler(int sig G_GNUC_UNUSED)
{
}

/**
 * virFork:
 *
 * Wrapper around fork() that avoids various race/deadlock conditions.
 *
 * Like fork(), there are several return possibilities:
 * 1. No child was created: the return is -1, errno is set, and an error
 * message has been reported.  The semantics of virWaitProcess() recognize
 * this to avoid clobbering the error message from here.
 * 2. This is the parent: the return is > 0.  The parent can now attempt
 * to interact with the child (but be aware that unlike raw fork(), the
 * child may not return - some failures in the child result in this
 * function calling _exit(EXIT_CANCELED) if the child cannot be set up
 * correctly).
 * 3. This is the child: the return is 0.  If this happens, the parent
 * is also guaranteed to return.
 */
pid_t
virFork(void)
{
    sigset_t oldmask, newmask;
    struct sigaction sig_action;
    int saved_errno;
    pid_t pid;

    /*
     * Need to block signals now, so that child process can safely
     * kill off caller's signal handlers without a race.
     */
    sigfillset(&newmask);
    if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
        virReportSystemError(errno,
                             "%s", _("cannot block signals"));
        return -1;
    }

    /* Ensure we hold the logging lock, to protect child processes
     * from deadlocking on another thread's inherited mutex state */
    virLogLock();

    pid = fork();
    saved_errno = errno; /* save for caller */

    /* Unlock for both parent and child process */
    virLogUnlock();

    if (pid < 0) {
        /* attempt to restore signal mask, but ignore failure, to
         * avoid obscuring the fork failure */
        ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
        virReportSystemError(saved_errno,
                             "%s", _("cannot fork child process"));
        errno = saved_errno;

    } else if (pid) {
        /* parent process */

        /* Restore our original signal mask now that the child is
         * safely running. Only documented failures are EFAULT (not
         * possible, since we are using just-grabbed mask) or EINVAL
         * (not possible, since we are using correct arguments).  */
        ignore_value(pthread_sigmask(SIG_SETMASK, &oldmask, NULL));

    } else {
        /* child process */

        int logprio;
        size_t i;

        /* Remove any error callback so errors in child now get sent
         * to stderr where they stand a fighting chance of being seen
         * and logged */
        virSetErrorFunc(NULL, NULL);
        virSetErrorLogPriorityFunc(NULL);

        /* Make sure any hook logging is sent to stderr, since child
         * process may close the logfile FDs */
        logprio = virLogGetDefaultPriority();
        virLogReset();
        virLogSetDefaultPriority(logprio);

        /* Clear out all signal handlers from parent so nothing
         * unexpected can happen in our child once we unblock
         * signals */
        sig_action.sa_handler = SIG_DFL;
        sig_action.sa_flags = 0;
        sigemptyset(&sig_action.sa_mask);

        for (i = 1; i < NSIG; i++) {
            /* Only possible errors are EFAULT or EINVAL The former
             * won't happen, the latter we expect, so no need to check
             * return value */
            ignore_value(sigaction(i, &sig_action, NULL));
        }

        /* Code that runs between fork & execve might trigger
         * SIG_PIPE, so we must explicitly set that to a no-op
         * handler. This handler will get reset to SIG_DFL when
         * execve() runs
         */
        sig_action.sa_handler = virDummyHandler;
        ignore_value(sigaction(SIGPIPE, &sig_action, NULL));

        /* Unmask all signals in child, since we've no idea what the
         * caller's done with their signal mask and don't want to
         * propagate that to children */
        sigemptyset(&newmask);
        if (pthread_sigmask(SIG_SETMASK, &newmask, NULL) != 0) {
            virReportSystemError(errno, "%s", _("cannot unblock signals"));
            virDispatchError(NULL);
            _exit(EXIT_CANCELED);
        }
    }
    return pid;
}

/*
 * Ensure that *null is an fd visiting /dev/null.  Return 0 on
 * success, -1 on failure.  Allows for lazy opening of shared
 * /dev/null fd only as required.
 */
static int
getDevNull(int *null)
{
    if (*null == -1 && (*null = open("/dev/null", O_RDWR|O_CLOEXEC)) < 0) {
        virReportSystemError(errno,
                             _("cannot open %s"),
                             "/dev/null");
        return -1;
    }
    return 0;
}

/* Ensure that STD is an inheritable copy of FD.  Return 0 on success,
 * -1 on failure.  */
static int
prepareStdFd(int fd, int std)
{
    if (fd == std)
        return virSetInherit(fd, true);
    if (dup2(fd, std) != std)
        return -1;
    return 0;
}

/* virCommandHandshakeChild:
 *
 *   child side of handshake - called by child process in virExec() to
 *   indicate to parent that the child process has successfully
 *   completed its pre-exec initialization.
 */
static int
virCommandHandshakeChild(virCommandPtr cmd)
{
    char c = '1';
    int rv;

    if (!cmd->handshake)
       return true;

    VIR_DEBUG("Notifying parent for handshake start on %d",
              cmd->handshakeWait[1]);
    if (safewrite(cmd->handshakeWait[1], &c, sizeof(c)) != sizeof(c)) {
        virReportSystemError(errno, "%s",
                             _("Unable to notify parent process"));
        return -1;
    }

    VIR_DEBUG("Waiting on parent for handshake complete on %d",
              cmd->handshakeNotify[0]);
    if ((rv = saferead(cmd->handshakeNotify[0], &c,
                       sizeof(c))) != sizeof(c)) {
        if (rv < 0)
            virReportSystemError(errno, "%s",
                                 _("Unable to wait on parent process"));
        else
            virReportSystemError(EIO, "%s",
                                 _("libvirtd quit during handshake"));
        return -1;
    }
    if (c != '1') {
        virReportSystemError(EINVAL,
                             _("Unexpected confirm code '%c' from parent"),
                             c);
        return -1;
    }
    VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
    VIR_FORCE_CLOSE(cmd->handshakeNotify[0]);

    VIR_DEBUG("Handshake with parent is done");
    return 0;
}

static int
virExecCommon(virCommandPtr cmd, gid_t *groups, int ngroups)
{
    if (cmd->uid != (uid_t)-1 || cmd->gid != (gid_t)-1 ||
        cmd->capabilities || (cmd->flags & VIR_EXEC_CLEAR_CAPS)) {
        VIR_DEBUG("Setting child uid:gid to %d:%d with caps %llx",
                  (int)cmd->uid, (int)cmd->gid, cmd->capabilities);
        if (virSetUIDGIDWithCaps(cmd->uid, cmd->gid, groups, ngroups,
                                 cmd->capabilities,
                                 !!(cmd->flags & VIR_EXEC_CLEAR_CAPS)) < 0)
            return -1;
    }

    if (cmd->pwd) {
        VIR_DEBUG("Running child in %s", cmd->pwd);
        if (chdir(cmd->pwd) < 0) {
            virReportSystemError(errno,
                                 _("Unable to change to %s"), cmd->pwd);
            return -1;
        }
    }
    return 0;
}

# ifdef __linux__
/* On Linux, we can utilize procfs and read the table of opened
 * FDs and selectively close only those FDs we don't want to pass
 * onto child process (well, the one we will exec soon since this
 * is called from the child). */
static int
virCommandMassCloseGetFDsLinux(virCommandPtr cmd G_GNUC_UNUSED,
                               virBitmapPtr fds)
{
    DIR *dp = NULL;
    struct dirent *entry;
    const char *dirName = "/proc/self/fd";
    int rc;
    int ret = -1;

    if (virDirOpen(&dp, dirName) < 0)
        return -1;

    while ((rc = virDirRead(dp, &entry, dirName)) > 0) {
        int fd;

        if (virStrToLong_i(entry->d_name, NULL, 10, &fd) < 0) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("unable to parse FD: %s"),
                           entry->d_name);
            goto cleanup;
        }

        ignore_value(virBitmapSetBit(fds, fd));
    }

    if (rc < 0)
        goto cleanup;

    ret = 0;
 cleanup:
    VIR_DIR_CLOSE(dp);
    return ret;
}

# else /* !__linux__ */

static int
virCommandMassCloseGetFDsGeneric(virCommandPtr cmd G_GNUC_UNUSED,
                                 virBitmapPtr fds)
{
    virBitmapSetAll(fds);
    return 0;
}
# endif /* !__linux__ */

# ifdef __FreeBSD__

static int
virCommandMassClose(virCommandPtr cmd,
                    int childin,
                    int childout,
                    int childerr)
{
    int lastfd = -1;
    int fd = -1;
    size_t i;

    /*
     * Two phases of closing.
     *
     * The first (inefficient) phase iterates over FDs,
     * preserving certain FDs we need to pass down, and
     * closing others. The number of iterations is bounded
     * to the number of the biggest FD we need to preserve.
     *
     * The second (speedy) phase uses closefrom() to cull
     * all remaining FDs in the process.
     *
     * Usually the first phase will be fairly quick only
     * processing a handful of low FD numbers, and thus using
     * closefrom() is a massive win for high ulimit() NFILES
     * values.
     */
    lastfd = MAX(lastfd, childin);
    lastfd = MAX(lastfd, childout);
    lastfd = MAX(lastfd, childerr);

    for (i = 0; i < cmd->npassfd; i++)
        lastfd = MAX(lastfd, cmd->passfd[i].fd);

    for (fd = 0; fd <= lastfd; fd++) {
        if (fd == childin || fd == childout || fd == childerr)
            continue;
        if (!virCommandFDIsSet(cmd, fd)) {
            int tmpfd = fd;
            VIR_MASS_CLOSE(tmpfd);
        } else if (virSetInherit(fd, true) < 0) {
            virReportSystemError(errno, _("failed to preserve fd %d"), fd);
            return -1;
        }
    }

    closefrom(lastfd + 1);

    return 0;
}

# else /* ! __FreeBSD__ */

static int
virCommandMassClose(virCommandPtr cmd,
                    int childin,
                    int childout,
                    int childerr)
{
    g_autoptr(virBitmap) fds = NULL;
    int openmax = sysconf(_SC_OPEN_MAX);
    int fd = -1;

    /* In general, it is not safe to call malloc() between fork() and exec()
     * because the child might have forked at the worst possible time, i.e.
     * when another thread was in malloc() and thus held its lock. That is to
     * say, POSIX does not mandate malloc() to be async-safe. Fortunately,
     * glibc developers are aware of this and made malloc() async-safe.
     * Therefore we can safely allocate memory here (and transitively call
     * opendir/readdir) without a deadlock. */

    if (openmax < 0) {
        virReportSystemError(errno, "%s", _("sysconf(_SC_OPEN_MAX) failed"));
        return -1;
    }

    if (!(fds = virBitmapNew(openmax)))
        return -1;

#  ifdef __linux__
    if (virCommandMassCloseGetFDsLinux(cmd, fds) < 0)
        return -1;
#  else
    if (virCommandMassCloseGetFDsGeneric(cmd, fds) < 0)
        return -1;
#  endif

    fd = virBitmapNextSetBit(fds, 2);
    for (; fd >= 0; fd = virBitmapNextSetBit(fds, fd)) {
        if (fd == childin || fd == childout || fd == childerr)
            continue;
        if (!virCommandFDIsSet(cmd, fd)) {
            int tmpfd = fd;
            VIR_MASS_CLOSE(tmpfd);
        } else if (virSetInherit(fd, true) < 0) {
            virReportSystemError(errno, _("failed to preserve fd %d"), fd);
            return -1;
        }
    }

    return 0;
}

# endif /* ! __FreeBSD__ */

/*
 * virExec:
 * @cmd virCommandPtr containing all information about the program to
 *      exec.
 */
static int
virExec(virCommandPtr cmd)
{
    pid_t pid;
    int null = -1;
    int pipeout[2] = {-1, -1};
    int pipeerr[2] = {-1, -1};
    int pipesync[2] = {-1, -1};
    int childin = cmd->infd;
    int childout = -1;
    int childerr = -1;
    g_autofree char *binarystr = NULL;
    const char *binary = NULL;
    int ret;
    g_autofree gid_t *groups = NULL;
    int ngroups;

    if (cmd->args[0][0] != '/') {
        if (!(binary = binarystr = virFindFileInPath(cmd->args[0]))) {
            virReportSystemError(ENOENT,
                                 _("Cannot find '%s' in path"),
                                 cmd->args[0]);
            return -1;
        }
    } else {
        binary = cmd->args[0];
    }

    if (childin < 0) {
        if (getDevNull(&null) < 0)
            goto cleanup;
        childin = null;
    }

    if (cmd->outfdptr != NULL) {
        if (*cmd->outfdptr == -1) {
            if (virPipe(pipeout) < 0)
                goto cleanup;

            if ((cmd->flags & VIR_EXEC_NONBLOCK) &&
                virSetNonBlock(pipeout[0]) == -1) {
                virReportSystemError(errno, "%s",
                                     _("Failed to set non-blocking file descriptor flag"));
                goto cleanup;
            }

            childout = pipeout[1];
        } else {
            childout = *cmd->outfdptr;
        }
    } else {
        if (getDevNull(&null) < 0)
            goto cleanup;
        childout = null;
    }

    if (cmd->errfdptr != NULL) {
        if (cmd->errfdptr == cmd->outfdptr) {
            childerr = childout;
        } else if (*cmd->errfdptr == -1) {
            if (virPipe(pipeerr) < 0)
                goto cleanup;

            if ((cmd->flags & VIR_EXEC_NONBLOCK) &&
                virSetNonBlock(pipeerr[0]) == -1) {
                virReportSystemError(errno, "%s",
                                     _("Failed to set non-blocking file descriptor flag"));
                goto cleanup;
            }

            childerr = pipeerr[1];
        } else {
            childerr = *cmd->errfdptr;
        }
    } else {
        if (getDevNull(&null) < 0)
            goto cleanup;
        childerr = null;
    }

    if ((ngroups = virGetGroupList(cmd->uid, cmd->gid, &groups)) < 0)
        goto cleanup;

    pid = virFork();

    if (pid < 0)
        goto cleanup;

    if (pid) { /* parent */
        VIR_FORCE_CLOSE(null);
        if (cmd->outfdptr && *cmd->outfdptr == -1) {
            VIR_FORCE_CLOSE(pipeout[1]);
            *cmd->outfdptr = pipeout[0];
        }
        if (cmd->errfdptr && *cmd->errfdptr == -1) {
            VIR_FORCE_CLOSE(pipeerr[1]);
            *cmd->errfdptr = pipeerr[0];
        }

        cmd->pid = pid;

        return 0;
    }

    /* child */

    if (cmd->mask)
        umask(cmd->mask);
    ret = EXIT_CANCELED;

    if (virCommandMassClose(cmd, childin, childout, childerr) < 0)
        goto fork_error;

    if (prepareStdFd(childin, STDIN_FILENO) < 0) {
        virReportSystemError(errno,
                             "%s", _("failed to setup stdin file handle"));
        goto fork_error;
    }
    if (childout > 0 && prepareStdFd(childout, STDOUT_FILENO) < 0) {
        virReportSystemError(errno,
                             "%s", _("failed to setup stdout file handle"));
        goto fork_error;
    }
    if (childerr > 0 && prepareStdFd(childerr, STDERR_FILENO) < 0) {
        virReportSystemError(errno,
                             "%s", _("failed to setup stderr file handle"));
        goto fork_error;
    }

    if (childin != STDIN_FILENO && childin != null &&
        childin != childerr && childin != childout)
        VIR_FORCE_CLOSE(childin);
    if (childout > STDERR_FILENO && childout != null && childout != childerr)
        VIR_FORCE_CLOSE(childout);
    if (childerr > STDERR_FILENO && childerr != null)
        VIR_FORCE_CLOSE(childerr);
    VIR_FORCE_CLOSE(null);

    /* Initialize full logging for a while */
    virLogSetFromEnv();

    if (cmd->pidfile &&
        virPipe(pipesync) < 0)
        goto fork_error;

    /* Daemonize as late as possible, so the parent process can detect
     * the above errors with wait* */
    if (cmd->flags & VIR_EXEC_DAEMON) {
        char c;

        if (setsid() < 0) {
            virReportSystemError(errno,
                                 "%s", _("cannot become session leader"));
            goto fork_error;
        }

        if (chdir("/") < 0) {
            virReportSystemError(errno,
                                 "%s", _("cannot change to root directory"));
            goto fork_error;
        }

        pid = fork();
        if (pid < 0) {
            virReportSystemError(errno,
                                 "%s", _("cannot fork child process"));
            goto fork_error;
        }

        if (pid > 0) {
            /* The parent expect us to have written the pid file before
             * exiting. Wait here for the child to write it and signal us. */
            if (cmd->pidfile &&
                saferead(pipesync[0], &c, sizeof(c)) != sizeof(c)) {
                virReportSystemError(errno, "%s",
                                     _("Unable to wait for child process"));
                _exit(EXIT_FAILURE);
            }
            _exit(EXIT_SUCCESS);
        }
    }

    if (virProcessSetMaxMemLock(0, cmd->maxMemLock) < 0)
        goto fork_error;
    if (virProcessSetMaxProcesses(0, cmd->maxProcesses) < 0)
        goto fork_error;
    if (virProcessSetMaxFiles(0, cmd->maxFiles) < 0)
        goto fork_error;
    if (cmd->setMaxCore &&
        virProcessSetMaxCoreSize(0, cmd->maxCore) < 0)
        goto fork_error;
    if (cmd->pidfile) {
        int pidfilefd = -1;
        char c;

        pidfilefd = virPidFileAcquirePath(cmd->pidfile, false, getpid());
        if (pidfilefd < 0)
            goto fork_error;
        if (virSetInherit(pidfilefd, true) < 0) {
            virReportSystemError(errno, "%s",
                                 _("Cannot disable close-on-exec flag"));
            goto fork_error;
        }

        c = '1';
        if (safewrite(pipesync[1], &c, sizeof(c)) != sizeof(c)) {
            virReportSystemError(errno, "%s", _("Unable to notify child process"));
            goto fork_error;
        }
        VIR_FORCE_CLOSE(pipesync[0]);
        VIR_FORCE_CLOSE(pipesync[1]);

        /* pidfilefd is intentionally leaked. */
    }

    if (cmd->hook) {
        VIR_DEBUG("Run hook %p %p", cmd->hook, cmd->opaque);
        ret = cmd->hook(cmd->opaque);
        VIR_DEBUG("Done hook %d", ret);
        if (ret < 0)
           goto fork_error;
    }

# if defined(WITH_SECDRIVER_SELINUX)
    if (cmd->seLinuxLabel) {
        VIR_DEBUG("Setting child security label to %s", cmd->seLinuxLabel);
        if (setexeccon_raw(cmd->seLinuxLabel) == -1) {
            virReportSystemError(errno,
                                 _("unable to set SELinux security context "
                                   "'%s' for '%s'"),
                                 cmd->seLinuxLabel, cmd->args[0]);
            if (security_getenforce() == 1)
                goto fork_error;
        }
    }
# endif
# if defined(WITH_SECDRIVER_APPARMOR)
    if (cmd->appArmorProfile) {
        VIR_DEBUG("Setting child AppArmor profile to %s", cmd->appArmorProfile);
        if (aa_change_profile(cmd->appArmorProfile) < 0) {
            virReportSystemError(errno,
                                 _("unable to set AppArmor profile '%s' "
                                   "for '%s'"),
                                 cmd->appArmorProfile, cmd->args[0]);
            goto fork_error;
        }
    }
# endif

    if (virExecCommon(cmd, groups, ngroups) < 0)
        goto fork_error;

    if (virCommandHandshakeChild(cmd) < 0)
       goto fork_error;

    /* Close logging again to ensure no FDs leak to child */
    virLogReset();

    if (cmd->env)
        execve(binary, cmd->args, cmd->env);
    else
        execv(binary, cmd->args);

    ret = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;
    virReportSystemError(errno,
                         _("cannot execute binary %s"),
                         cmd->args[0]);

 fork_error:
    virDispatchError(NULL);
    _exit(ret);

 cleanup:
    /* This is cleanup of parent process only - child
       should never jump here on error */

    /* NB we don't virReportError() on any failures here
       because the code which jumped here already raised
       an error condition which we must not overwrite */
    VIR_FORCE_CLOSE(pipeerr[0]);
    VIR_FORCE_CLOSE(pipeerr[1]);
    VIR_FORCE_CLOSE(pipeout[0]);
    VIR_FORCE_CLOSE(pipeout[1]);
    VIR_FORCE_CLOSE(null);
    return -1;
}


#else /* WIN32 */

pid_t
virFork(void)
{
    errno = ENOTSUP;

    return -1;
}

#endif /* WIN32 */


/**
 * 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)
{
    const char *const args[] = { binary, NULL };

    return virCommandNewArgs(args);
}

/**
 * virCommandNewArgs:
 * @args: array of arguments
 *
 * Create a new command with a NULL terminated
 * 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)
{
    virCommandPtr cmd;

    if (VIR_ALLOC(cmd) < 0)
        return NULL;

    cmd->handshakeWait[0] = -1;
    cmd->handshakeWait[1] = -1;
    cmd->handshakeNotify[0] = -1;
    cmd->handshakeNotify[1] = -1;

    cmd->infd = cmd->inpipe = cmd->outfd = cmd->errfd = -1;
    cmd->pid = -1;
    cmd->uid = -1;
    cmd->gid = -1;

    virCommandAddArgSet(cmd, 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.  More arguments can
 * be added later.  @binary is handled as in virCommandNew.
 */
virCommandPtr
virCommandNewArgList(const char *binary, ...)
{
    virCommandPtr cmd;
    va_list list;

    va_start(list, binary);
    cmd = virCommandNewVAList(binary, list);
    va_end(list);

    return cmd;
}

/**
 * virCommandNewVAList:
 * @binary: program to run
 * @va_list: additional arguments
 *
 * Create a new command with a NULL terminated
 * variable argument list.  @binary is handled as in virCommandNew.
 */
virCommandPtr
virCommandNewVAList(const char *binary, va_list list)
{
    virCommandPtr cmd = virCommandNew(binary);
    const char *arg;

    if (!cmd || cmd->has_error)
        return cmd;

    while ((arg = va_arg(list, const char *)) != NULL)
        virCommandAddArg(cmd, arg);
    return cmd;
}


#define VIR_COMMAND_MAYBE_CLOSE_FD(fd, flags) \
    if ((fd > STDERR_FILENO) && \
        (flags & VIR_COMMAND_PASS_FD_CLOSE_PARENT)) \
        VIR_FORCE_CLOSE(fd)

/**
 * virCommandPassFD:
 * @cmd: the command to modify
 * @fd: fd to reassign to the child
 * @flags: extra flags; binary-OR of virCommandPassFDFlags
 *
 * Transfer the specified file descriptor to the child, instead
 * of closing it on exec. @fd must not be one of the three
 * standard streams.
 *
 * If the flag VIR_COMMAND_PASS_FD_CLOSE_PARENT is set then fd will
 * be closed in the parent no later than Run/RunAsync/Free. The parent
 * should cease using the @fd when this call completes
 */
void
virCommandPassFD(virCommandPtr cmd, int fd, unsigned int flags)
{
    int ret = 0;

    if (!cmd) {
        VIR_COMMAND_MAYBE_CLOSE_FD(fd, flags);
        return;
    }

    if (fd <= STDERR_FILENO) {
        VIR_DEBUG("invalid fd %d", fd);
        VIR_COMMAND_MAYBE_CLOSE_FD(fd, flags);
        if (!cmd->has_error)
            cmd->has_error = -1;
        return;
    }

    if ((ret = virCommandFDSet(cmd, fd, flags)) != 0) {
        if (!cmd->has_error)
            cmd->has_error = ret;
        VIR_DEBUG("cannot preserve %d", fd);
        VIR_COMMAND_MAYBE_CLOSE_FD(fd, flags);
        return;
    }
}

/*
 * virCommandPassFDGetFDIndex:
 * @cmd: pointer to virCommand
 * @fd: FD to get index of
 *
 * Determine the index of the FD in the transfer set.
 *
 * Returns index >= 0 if @set contains @fd,
 * -1 otherwise.
 */
int
virCommandPassFDGetFDIndex(virCommandPtr cmd, int fd)
{
    size_t i = 0;

    if (!cmd || cmd->has_error)
        return -1;

    while (i < cmd->npassfd) {
        if (cmd->passfd[i].fd == fd)
            return i;
        i++;
    }

    return -1;
}

/**
 * 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 and the child will inherit opened and locked FD to the
 * pidfile.
 */
void
virCommandSetPidFile(virCommandPtr cmd, const char *pidfile)
{
    if (!cmd || cmd->has_error)
        return;

    VIR_FREE(cmd->pidfile);
    cmd->pidfile = g_strdup(pidfile);
}


gid_t
virCommandGetGID(virCommandPtr cmd)
{
    return cmd->gid;
}


uid_t
virCommandGetUID(virCommandPtr cmd)
{
    return cmd->uid;
}


void
virCommandSetGID(virCommandPtr cmd, gid_t gid)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->gid = gid;
}

void
virCommandSetUID(virCommandPtr cmd, uid_t uid)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->uid = uid;
}

void
virCommandSetMaxMemLock(virCommandPtr cmd, unsigned long long bytes)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->maxMemLock = bytes;
}

void
virCommandSetMaxProcesses(virCommandPtr cmd, unsigned int procs)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->maxProcesses = procs;
}

void
virCommandSetMaxFiles(virCommandPtr cmd, unsigned int files)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->maxFiles = files;
}

void virCommandSetMaxCoreSize(virCommandPtr cmd, unsigned long long bytes)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->maxCore = bytes;
    cmd->setMaxCore = true;
}

void virCommandSetUmask(virCommandPtr cmd, int mask)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->mask = mask;
}

/**
 * virCommandClearCaps:
 * @cmd: the command to modify
 *
 * Remove all capabilities from the child, after any hooks have been run.
 */
void
virCommandClearCaps(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->flags |= VIR_EXEC_CLEAR_CAPS;
}

/**
 * virCommandAllowCap:
 * @cmd: the command to modify
 * @capability: what to allow
 *
 * Allow specific capabilities
 */
void
virCommandAllowCap(virCommandPtr cmd,
                   int capability)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->capabilities |= (1ULL << capability);
}


/**
 * virCommandSetSELinuxLabel:
 * @cmd: the command to modify
 * @label: the SELinux label to use for the child process
 *
 * Saves a copy of @label to use when setting the SELinux context
 * label (with setexeccon_raw()) after the child process has been
 * started. If SELinux isn't compiled into libvirt, or if label is
 * NULL, nothing will be done.
 */
void
virCommandSetSELinuxLabel(virCommandPtr cmd,
                          const char *label G_GNUC_UNUSED)
{
    if (!cmd || cmd->has_error)
        return;

#if defined(WITH_SECDRIVER_SELINUX)
    VIR_FREE(cmd->seLinuxLabel);
    cmd->seLinuxLabel = g_strdup(label);
#endif
    return;
}


/**
 * virCommandSetAppArmorProfile:
 * @cmd: the command to modify
 * @profile: the AppArmor profile to use
 *
 * Saves a copy of @profile to use when aa_change_profile() after the
 * child process has been started. If AppArmor support isn't
 * configured into libvirt, or if profile is NULL, nothing will be done.
 */
void
virCommandSetAppArmorProfile(virCommandPtr cmd,
                             const char *profile G_GNUC_UNUSED)
{
    if (!cmd || cmd->has_error)
        return;

#if defined(WITH_SECDRIVER_APPARMOR)
    VIR_FREE(cmd->appArmorProfile);
    cmd->appArmorProfile = g_strdup(profile);
#endif
    return;
}


/**
 * 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)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->flags |= VIR_EXEC_DAEMON;
}

/**
 * virCommandNonblockingFDs:
 * @cmd: the command to modify
 *
 * Set FDs created by virCommandSetOutputFD and virCommandSetErrorFD
 * as non-blocking in the parent.
 */
void
virCommandNonblockingFDs(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->flags |= VIR_EXEC_NONBLOCK;
}

/**
 * virCommandRawStatus:
 * @cmd: the command to modify
 *
 * Mark this command as returning raw exit status via virCommandRun() or
 * virCommandWait() (caller must use WIFEXITED() and friends, and can
 * detect death from signals) instead of the default of only allowing
 * normal exit status (caller must not use WEXITSTATUS(), and death from
 * signals returns -1).
 */
void
virCommandRawStatus(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->rawStatus = true;
}

/* Add an environment variable to the cmd->env list.  'env' is a
 * string like "name=value".  If the named environment variable is
 * already set, then it is replaced in the list.
 */
static void
virCommandAddEnv(virCommandPtr cmd, char *env)
{
    size_t namelen;
    size_t i;

    /* Search for the name in the existing environment. */
    namelen = strcspn(env, "=");
    for (i = 0; i < cmd->nenv; ++i) {
        /* + 1 because we want to match the '=' character too. */
        if (STREQLEN(cmd->env[i], env, namelen + 1)) {
            VIR_FREE(cmd->env[i]);
            cmd->env[i] = env;
            return;
        }
    }

    /* Arg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 1 + 1) < 0) {
        VIR_FREE(env);
        cmd->has_error = ENOMEM;
        return;
    }

    cmd->env[cmd->nenv++] = env;
}

/**
 * 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, ...)
{
    char *env;
    va_list list;

    if (!cmd || cmd->has_error)
        return;

    va_start(list, format);
    env = g_strdup_vprintf(format, list);
    va_end(list);

    virCommandAddEnv(cmd, 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
 */
void
virCommandAddEnvPair(virCommandPtr cmd, const char *name, const char *value)
{
    virCommandAddEnvFormat(cmd, "%s=%s", name, 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
 */
void
virCommandAddEnvString(virCommandPtr cmd, const char *str)
{
    char *env;

    if (!cmd || cmd->has_error)
        return;

    env = g_strdup(str);

    virCommandAddEnv(cmd, env);
}


/**
 * 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.
 */
void
virCommandAddEnvBuffer(virCommandPtr cmd, virBufferPtr buf)
{
    if (!cmd || cmd->has_error) {
        virBufferFreeAndReset(buf);
        return;
    }

    if (!virBufferUse(buf)) {
        cmd->has_error = EINVAL;
        return;
    }

    virCommandAddEnv(cmd, virBufferContentAndReset(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's value
 */
void
virCommandAddEnvPass(virCommandPtr cmd, const char *name)
{
    const char *value;
    if (!cmd || cmd->has_error)
        return;

    value = getenv(name);
    if (value)
        virCommandAddEnvPair(cmd, name, value);
}


/**
 * virCommandAddEnvPassCommon:
 * @cmd: the command to modify
 *
 * Set LC_ALL to C, and propagate other essential environment
 * variables (such as PATH) from the parent process.
 */
void
virCommandAddEnvPassCommon(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    if (VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 9) < 0) {
        cmd->has_error = ENOMEM;
        return;
    }

    virCommandAddEnvPair(cmd, "LC_ALL", "C");

    virCommandAddEnvPass(cmd, "LD_PRELOAD");
    virCommandAddEnvPass(cmd, "LD_LIBRARY_PATH");
    virCommandAddEnvPass(cmd, "PATH");
    virCommandAddEnvPass(cmd, "HOME");
    virCommandAddEnvPass(cmd, "USER");
    virCommandAddEnvPass(cmd, "LOGNAME");
    virCommandAddEnvPass(cmd, "TMPDIR");
}


void
virCommandAddEnvXDG(virCommandPtr cmd, const char *baseDir)
{
    if (!cmd || cmd->has_error)
        return;

    if (VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 3) < 0) {
        cmd->has_error = ENOMEM;
        return;
    }

    virCommandAddEnvFormat(cmd, "XDG_DATA_HOME=%s/%s",
                           baseDir, ".local/share");
    virCommandAddEnvFormat(cmd, "XDG_CACHE_HOME=%s/%s",
                           baseDir, ".cache");
    virCommandAddEnvFormat(cmd, "XDG_CONFIG_HOME=%s/%s",
                           baseDir, ".config");
}


/**
 * virCommandAddArg:
 * @cmd: the command to modify
 * @val: the argument to add
 *
 * Add a command line argument to the child
 */
void
virCommandAddArg(virCommandPtr cmd, const char *val)
{
    char *arg;

    if (!cmd || cmd->has_error)
        return;

    if (val == NULL) {
        cmd->has_error = EINVAL;
        abort();
        return;
    }

    arg = g_strdup(val);

    /* Arg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
        VIR_FREE(arg);
        cmd->has_error = ENOMEM;
        return;
    }

    cmd->args[cmd->nargs++] = arg;
}


/**
 * 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.
 */
void
virCommandAddArgBuffer(virCommandPtr cmd, virBufferPtr buf)
{
    if (!cmd || cmd->has_error) {
        virBufferFreeAndReset(buf);
        return;
    }

    /* Arg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
        cmd->has_error = ENOMEM;
        virBufferFreeAndReset(buf);
        return;
    }

    cmd->args[cmd->nargs] = virBufferContentAndReset(buf);
    if (!cmd->args[cmd->nargs])
        cmd->args[cmd->nargs] = g_strdup("");
    cmd->nargs++;
}


/**
 * 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, ...)
{
    char *arg;
    va_list list;

    if (!cmd || cmd->has_error)
        return;

    va_start(list, format);
    arg = g_strdup_vprintf(format, list);
    va_end(list);

    /* Arg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1) < 0) {
        VIR_FREE(arg);
        cmd->has_error = ENOMEM;
        return;
    }

    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
virCommandAddArgPair(virCommandPtr cmd, const char *name, const char *val)
{
    if (name == NULL || val == NULL) {
        cmd->has_error = EINVAL;
        return;
    }
    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
virCommandAddArgSet(virCommandPtr cmd, const char *const*vals)
{
    int narg = 0;

    if (!cmd || cmd->has_error)
        return;

    if (vals[0] == NULL) {
        cmd->has_error = EINVAL;
        return;
    }

    while (vals[narg] != NULL)
        narg++;

    /* narg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
        cmd->has_error = ENOMEM;
        return;
    }

    narg = 0;
    while (vals[narg] != NULL) {
        char *arg;

        arg = g_strdup(vals[narg++]);
        cmd->args[cmd->nargs++] = arg;
    }
}

/**
 * virCommandAddArgList:
 * @cmd: the command to modify
 * @...: list of arguments to add
 *
 * Add a NULL terminated list of args.
 */
void
virCommandAddArgList(virCommandPtr cmd, ...)
{
    va_list list;
    int narg = 0;

    if (!cmd || cmd->has_error)
        return;

    va_start(list, cmd);
    while (va_arg(list, const char *) != NULL)
        narg++;
    va_end(list);

    /* narg plus trailing NULL. */
    if (VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1) < 0) {
        cmd->has_error = ENOMEM;
        return;
    }

    va_start(list, cmd);
    while (1) {
        char *arg = va_arg(list, char *);
        if (!arg)
            break;
        arg = g_strdup(arg);
        cmd->args[cmd->nargs++] = arg;
    }
    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.
 */
void
virCommandSetWorkingDirectory(virCommandPtr cmd, const char *pwd)
{
    if (!cmd || cmd->has_error)
        return;

    if (cmd->pwd) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot set directory twice");
    } else {
        cmd->pwd = g_strdup(pwd);
    }
}


static int
virCommandGetNumSendBuffers(virCommandPtr cmd)
{
    return cmd->numSendBuffers;
}


static void
virCommandFreeSendBuffers(virCommandPtr cmd)
{
    size_t i;

    for (i = 0; i < virCommandGetNumSendBuffers(cmd); i++) {
        VIR_FORCE_CLOSE(cmd->sendBuffers[i].fd);
        VIR_FREE(cmd->sendBuffers[i].buffer);
    }
    VIR_FREE(cmd->sendBuffers);
}


#ifndef WIN32
/**
 * virCommandSetSendBuffer
 * @cmd: the command to modify
 *
 * Pass a buffer to virCommand that will be written into the
 * given file descriptor. The buffer will be freed automatically
 * and the file descriptor closed.
 */
int
virCommandSetSendBuffer(virCommandPtr cmd,
                        int fd,
                        unsigned char *buffer, size_t buflen)
{
    size_t i;

    if (!cmd || cmd->has_error)
        return -1;

    if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
        virReportSystemError(errno, "%s",
                             _("fcntl failed to set O_NONBLOCK"));
        cmd->has_error = errno;
        return -1;
    }

    i = virCommandGetNumSendBuffers(cmd);
    if (VIR_REALLOC_N(cmd->sendBuffers, i + 1) < 0) {
        cmd->has_error = ENOMEM;
        return -1;
    }

    cmd->sendBuffers[i].fd = fd;
    cmd->sendBuffers[i].buffer = buffer;
    cmd->sendBuffers[i].buflen = buflen;
    cmd->sendBuffers[i].offset = 0;

    cmd->numSendBuffers++;

    return 0;
}


static int
virCommandSendBuffersFillPollfd(virCommandPtr cmd,
                                struct pollfd *fds,
                                int startidx)
{
    size_t i, j;

    for (i = 0, j = 0; i < virCommandGetNumSendBuffers(cmd); i++) {
        if (cmd->sendBuffers[i].fd >= 0) {
            fds[startidx + j].fd = cmd->sendBuffers[i].fd;
            fds[startidx + j].events = POLLOUT;
            fds[startidx + j].revents = 0;
            j++;
        }
    }

    return j;
}


static int
virCommandSendBuffersHandlePoll(virCommandPtr cmd,
                                struct pollfd *fds)
{
    size_t i;
    ssize_t done;

    for (i = 0; i < virCommandGetNumSendBuffers(cmd); i++) {
        if (fds->fd == cmd->sendBuffers[i].fd)
            break;
    }
    if (i == virCommandGetNumSendBuffers(cmd))
        return 0;

    done = write(fds->fd,
                 cmd->sendBuffers[i].buffer + cmd->sendBuffers[i].offset,
                 cmd->sendBuffers[i].buflen - cmd->sendBuffers[i].offset);
    if (done < 0) {
        if (errno == EPIPE) {
            VIR_DEBUG("child closed PIPE early, ignoring EPIPE "
                      "on fd %d", cmd->sendBuffers[i].fd);
            VIR_FORCE_CLOSE(cmd->sendBuffers[i].fd);
        } else if (errno != EINTR && errno != EAGAIN) {
            virReportSystemError(errno, "%s",
                                 _("unable to write to child input"));
            return -1;
        }
    } else {
        cmd->sendBuffers[i].offset += done;
        if (cmd->sendBuffers[i].offset == cmd->sendBuffers[i].buflen)
            VIR_FORCE_CLOSE(cmd->sendBuffers[i].fd);
    }
    return 0;
}

#endif /* !WIN32 */


/**
 * 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() or combination of virCommandDoAsyncIO and
 * virCommandRunAsync. The buffer is forgotten after each @cmd run.
 */
void
virCommandSetInputBuffer(virCommandPtr cmd, const char *inbuf)
{
    if (!cmd || cmd->has_error)
        return;

    if (cmd->infd != -1 || cmd->inbuf) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify input twice");
        return;
    }

    cmd->inbuf = g_strdup(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 or virCommandRunAsync; caller is responsible for
 * freeing *outbuf. This requires the use of virCommandRun() or
 * combination of virCommandDoAsyncIO and virCommandRunAsync. The
 * buffer is forgotten after each @cmd run.
 */
void
virCommandSetOutputBuffer(virCommandPtr cmd, char **outbuf)
{
    *outbuf = NULL;
    if (!cmd || cmd->has_error)
        return;

    if (cmd->outfdptr) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify output twice");
        return;
    }

    cmd->outbuf = outbuf;
    cmd->outfdptr = &cmd->outfd;
}


/**
 * 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 or virCommandRunAsync; caller is responsible for
 * freeing *errbuf. It is possible to pass the same pointer as
 * for virCommandSetOutputBuffer(), in which case the child
 * process will interleave all output into a single string.  This
 * requires the use of virCommandRun() or combination of
 * virCommandDoAsyncIO and virCommandRunAsync.The buffer is
 * forgotten after each @cmd run.
 */
void
virCommandSetErrorBuffer(virCommandPtr cmd, char **errbuf)
{
    *errbuf = NULL;
    if (!cmd || cmd->has_error)
        return;

    if (cmd->errfdptr) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify stderr twice");
        return;
    }

    cmd->errbuf = errbuf;
    cmd->errfdptr = &cmd->errfd;
}


/**
 * virCommandSetInputFD:
 * @cmd: the command to modify
 * @infd: the descriptor to use
 *
 * Attach a file descriptor to the child's stdin
 */
void
virCommandSetInputFD(virCommandPtr cmd, int infd)
{
    if (!cmd || cmd->has_error)
        return;

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

    cmd->infd = infd;
}


/**
 * 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)
{
    if (!cmd || cmd->has_error)
        return;

    if (cmd->outfdptr) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify output twice");
        return;
    }

    cmd->outfdptr = outfd;
}


/**
 * 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)
{
    if (!cmd || cmd->has_error)
        return;

    if (cmd->errfdptr) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify stderr twice");
        return;
    }

    cmd->errfdptr = 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)
{
    if (!cmd || cmd->has_error)
        return;

    if (cmd->hook) {
        cmd->has_error = -1;
        VIR_DEBUG("cannot specify hook twice");
        return;
    }
    cmd->hook = hook;
    cmd->opaque = 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
 * out-of-memory condition while building cmd), nothing will be logged.
 */
void
virCommandWriteArgLog(virCommandPtr cmd, int logfd)
{
    int ioError = 0;
    size_t i;

    /* Any errors will be reported later by virCommandRun, which means
     * no command will be run, so there is nothing to log. */
    if (!cmd || cmd->has_error)
        return;

    for (i = 0; i < cmd->nenv; i++) {
        if (safewrite(logfd, cmd->env[i], strlen(cmd->env[i])) < 0)
            ioError = errno;
        if (safewrite(logfd, " ", 1) < 0)
            ioError = errno;
    }
    for (i = 0; i < cmd->nargs; i++) {
        if (safewrite(logfd, cmd->args[i], strlen(cmd->args[i])) < 0)
            ioError = errno;
        if (safewrite(logfd, i == cmd->nargs - 1 ? "\n" : " ", 1) < 0)
            ioError = errno;
    }

    if (ioError) {
        VIR_WARN("Unable to write command %s args to logfile: %s",
                 cmd->args[0], g_strerror(ioError));
    }
}


/**
 * virCommandToString:
 * @cmd: the command to convert
 * @linebreaks: true to break line after each env var or option
 *
 * Call after adding all arguments and environment settings, but
 * before Run/RunAsync, to return a string representation of the
 * environment and arguments of cmd, suitably quoted for pasting into
 * a shell.  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, bool linebreaks)
{
    size_t i;
    g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
    bool prevopt = false;

    /* Cannot assume virCommandRun will be called; so report the error
     * now.  If virCommandRun is called, it will report the same error. */
    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return NULL;
    }
    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return NULL;
    }

    for (i = 0; i < cmd->nenv; i++) {
        /* In shell, a='b c' has a different meaning than 'a=b c', so
         * we must determine where the '=' lives.  */
        char *eq = strchr(cmd->env[i], '=');

        if (!eq) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("invalid use of command API"));
            return NULL;
        }
        eq++;
        virBufferAdd(&buf, cmd->env[i], eq - cmd->env[i]);
        virBufferEscapeShell(&buf, eq);
        virBufferAddChar(&buf, ' ');
        if (linebreaks)
            virBufferAddLit(&buf, "\\\n");
    }
    virBufferEscapeShell(&buf, cmd->args[0]);
    for (i = 1; i < cmd->nargs; i++) {
        virBufferAddChar(&buf, ' ');
        if (linebreaks) {
            /* Line break if this is a --arg or if
             * the previous arg was a positional option
             */
            if (cmd->args[i][0] == '-' ||
                !prevopt)
                virBufferAddLit(&buf, "\\\n");
        }
        virBufferEscapeShell(&buf, cmd->args[i]);
        prevopt = (cmd->args[i][0] == '-');
    }

    return virBufferContentAndReset(&buf);
}


int
virCommandGetArgList(virCommandPtr cmd,
                     char ***args,
                     size_t *nargs)
{
    size_t i;

    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    *args = g_new0(char *, cmd->nargs);
    *nargs = cmd->nargs - 1;

    for (i = 1; i < cmd->nargs; i++)
        (*args)[i - 1] = g_strdup(cmd->args[i]);

    return 0;
}


#ifndef WIN32
/*
 * Manage input and output to the child process.
 */
static int
virCommandProcessIO(virCommandPtr cmd)
{
    int outfd = -1, errfd = -1;
    size_t inlen = 0, outlen = 0, errlen = 0;
    size_t inoff = 0;
    int ret = 0;
    g_autofree struct pollfd *fds = NULL;

    if (dryRunBuffer || dryRunCallback) {
        VIR_DEBUG("Dry run requested, skipping I/O processing");
        return 0;
    }

    /* With an input buffer, feed data to child
     * via pipe */
    if (cmd->inbuf)
        inlen = strlen(cmd->inbuf);

    /* With out/err buffer, the outfd/errfd have been filled with an
     * FD for us.  Guarantee an allocated string with partial results
     * even if we encounter a later failure, as well as freeing any
     * results accumulated over a prior run of the same command.  */
    if (cmd->outbuf) {
        outfd = cmd->outfd;
        VIR_FREE(*cmd->outbuf);
        if (VIR_ALLOC_N(*cmd->outbuf, 1) < 0)
            ret = -1;
    }
    if (cmd->errbuf) {
        errfd = cmd->errfd;
        VIR_FREE(*cmd->errbuf);
        if (VIR_ALLOC_N(*cmd->errbuf, 1) < 0)
            ret = -1;
    }
    if (ret == -1)
        goto cleanup;
    ret = -1;

    if (VIR_ALLOC_N(fds, 3 + virCommandGetNumSendBuffers(cmd)) < 0)
        goto cleanup;

    for (;;) {
        size_t i;
        int nfds = 0;

        if (cmd->inpipe != -1) {
            fds[nfds].fd = cmd->inpipe;
            fds[nfds].events = POLLOUT;
            fds[nfds].revents = 0;
            nfds++;
        }
        if (outfd != -1) {
            fds[nfds].fd = outfd;
            fds[nfds].events = POLLIN;
            fds[nfds].revents = 0;
            nfds++;
        }
        if (errfd != -1) {
            fds[nfds].fd = errfd;
            fds[nfds].events = POLLIN;
            fds[nfds].revents = 0;
            nfds++;
        }

        nfds += virCommandSendBuffersFillPollfd(cmd, fds, nfds);

        if (nfds == 0)
            break;

        if (poll(fds, nfds, -1) < 0) {
            if (errno == EAGAIN || errno == EINTR)
                continue;
            virReportSystemError(errno, "%s",
                                 _("unable to poll on child"));
            goto cleanup;
        }

        for (i = 0; i < nfds; i++) {
            if (fds[i].revents & (POLLIN | POLLHUP | POLLERR) &&
                (fds[i].fd == errfd || fds[i].fd == outfd)) {
                char data[1024];
                char **buf;
                size_t *len;
                int done;
                if (fds[i].fd == outfd) {
                    buf = cmd->outbuf;
                    len = &outlen;
                } else {
                    buf = cmd->errbuf;
                    len = &errlen;
                }
                /* Silence a false positive from clang. */
                sa_assert(buf);

                done = read(fds[i].fd, data, sizeof(data));
                if (done < 0) {
                    if (errno != EINTR &&
                        errno != EAGAIN) {
                        virReportSystemError(errno, "%s",
                                             (fds[i].fd == outfd) ?
                                             _("unable to read child stdout") :
                                             _("unable to read child stderr"));
                        goto cleanup;
                    }
                } else if (done == 0) {
                    if (fds[i].fd == outfd)
                        outfd = -1;
                    else
                        errfd = -1;
                } else {
                    if (VIR_REALLOC_N(*buf, *len + done + 1) < 0)
                        goto cleanup;
                    memcpy(*buf + *len, data, done);
                    *len += done;
                }
            }

            if (fds[i].revents & (POLLOUT | POLLHUP | POLLERR) &&
                fds[i].fd == cmd->inpipe) {
                int done;

                done = write(cmd->inpipe, cmd->inbuf + inoff,
                             inlen - inoff);
                if (done < 0) {
                    if (errno == EPIPE) {
                        VIR_DEBUG("child closed stdin early, ignoring EPIPE "
                                  "on fd %d", cmd->inpipe);
                        VIR_FORCE_CLOSE(cmd->inpipe);
                    } else if (errno != EINTR && errno != EAGAIN) {
                        virReportSystemError(errno, "%s",
                                             _("unable to write to child input"));
                        goto cleanup;
                    }
                } else {
                    inoff += done;
                    if (inoff == inlen)
                        VIR_FORCE_CLOSE(cmd->inpipe);
                }
            } else if (fds[i].revents & (POLLOUT | POLLHUP | POLLERR)) {
                if (virCommandSendBuffersHandlePoll(cmd, &fds[i]) < 0)
                    goto cleanup;
            }
        }
    }

    ret = 0;
 cleanup:
    if (cmd->outbuf && *cmd->outbuf)
        (*cmd->outbuf)[outlen] = '\0';
    if (cmd->errbuf && *cmd->errbuf)
        (*cmd->errbuf)[errlen] = '\0';
    return ret;
}

/**
 * virCommandExec:
 * @cmd: command to run
 * @groups: array of supplementary group IDs used for the command
 * @ngroups: number of group IDs in @groups
 *
 * Exec the command, replacing the current process. Meant to be called
 * 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.
 */
int virCommandExec(virCommandPtr cmd, gid_t *groups, int ngroups)
{
    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    if (virExecCommon(cmd, groups, ngroups) < 0)
        return -1;

    execve(cmd->args[0], cmd->args, cmd->env);

    virReportSystemError(errno,
                         _("cannot execute binary %s"),
                         cmd->args[0]);
    return -1;
}


/**
 * 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.  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.
 */
int
virCommandRun(virCommandPtr cmd, int *exitstatus)
{
    int ret = 0;
    char *outbuf = NULL;
    char *errbuf = NULL;
    struct stat st;
    bool string_io;
    bool async_io = false;
    char *str;
    int tmpfd;

    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    /* Avoid deadlock, by requiring that any open fd not under our
     * control must be visiting a regular file, or that we are
     * daemonized and no string io is required.  */
    string_io = cmd->inbuf || cmd->outbuf || cmd->errbuf;
    if (cmd->infd != -1 &&
        (fstat(cmd->infd, &st) < 0 || !S_ISREG(st.st_mode)))
        async_io = true;
    if (cmd->outfdptr && cmd->outfdptr != &cmd->outfd &&
        (*cmd->outfdptr == -1 ||
         fstat(*cmd->outfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
        async_io = true;
    if (cmd->errfdptr && cmd->errfdptr != &cmd->errfd &&
        (*cmd->errfdptr == -1 ||
         fstat(*cmd->errfdptr, &st) < 0 || !S_ISREG(st.st_mode)))
        async_io = true;
    if (async_io) {
        if (!(cmd->flags & VIR_EXEC_DAEMON) || string_io) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot mix caller fds with blocking execution"));
            return -1;
        }
    } else {
        if ((cmd->flags & VIR_EXEC_DAEMON) && string_io) {
            virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                           _("cannot mix string I/O with daemon"));
            return -1;
        }
    }

    /* If caller requested the same string for stdout and stderr, then
     * merge those into one string.  */
    if (cmd->outbuf && cmd->outbuf == cmd->errbuf) {
        cmd->errfdptr = &cmd->outfd;
        cmd->errbuf = NULL;
    }

    /* If caller hasn't requested capture of stdout/err, then capture
     * it ourselves so we can log it.  But the intermediate child for
     * a daemon has no expected output, and we don't want our
     * capturing pipes passed on to the daemon grandchild.
     */
    if (!(cmd->flags & VIR_EXEC_DAEMON)) {
        if (!cmd->outfdptr) {
            cmd->outfdptr = &cmd->outfd;
            cmd->outbuf = &outbuf;
            string_io = true;
        }
        if (!cmd->errfdptr) {
            cmd->errfdptr = &cmd->errfd;
            cmd->errbuf = &errbuf;
            string_io = true;
        }
    }

    cmd->flags |= VIR_EXEC_RUN_SYNC;
    if (virCommandRunAsync(cmd, NULL) < 0) {
        cmd->has_error = -1;
        return -1;
    }

    if (string_io) {
        VIR_FORCE_CLOSE(cmd->infd);
        ret = virCommandProcessIO(cmd);
    }

    if (virCommandWait(cmd, exitstatus) < 0)
        ret = -1;

    str = (exitstatus ? virProcessTranslateStatus(*exitstatus)
           : (char *) "status 0");
    VIR_DEBUG("Result %s, stdout: '%s' stderr: '%s'",
              NULLSTR(str),
              cmd->outbuf ? NULLSTR(*cmd->outbuf) : "(null)",
              cmd->errbuf ? NULLSTR(*cmd->errbuf) : "(null)");
    if (exitstatus)
        VIR_FREE(str);

    /* Reset any capturing, in case caller runs
     * this identical command again */
    VIR_FORCE_CLOSE(cmd->inpipe);
    if (cmd->outbuf == &outbuf) {
        tmpfd = cmd->outfd;
        if (VIR_CLOSE(cmd->outfd) < 0)
            VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
        cmd->outfdptr = NULL;
        cmd->outbuf = NULL;
        VIR_FREE(outbuf);
    }
    if (cmd->errbuf == &errbuf) {
        tmpfd = cmd->errfd;
        if (VIR_CLOSE(cmd->errfd) < 0)
            VIR_DEBUG("ignoring failed close on fd %d", tmpfd);
        cmd->errfdptr = NULL;
        cmd->errbuf = NULL;
        VIR_FREE(errbuf);
    }

    return ret;
}


static void
virCommandDoAsyncIOHelper(void *opaque)
{
    virCommandPtr cmd = opaque;
    if (virCommandProcessIO(cmd) < 0) {
        /* If something went wrong, save errno or -1 */
        cmd->has_error = errno ? errno : -1;
    }
}


/**
 * 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.
 *
 * 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 virProcessWait or virProcessAbort.
 */
int
virCommandRunAsync(virCommandPtr cmd, pid_t *pid)
{
    int ret = -1;
    g_autofree char *str = NULL;
    size_t i;
    bool synchronous = false;
    int infd[2] = {-1, -1};

    if (!cmd || cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    synchronous = cmd->flags & VIR_EXEC_RUN_SYNC;
    cmd->flags &= ~VIR_EXEC_RUN_SYNC;

    /* Buffer management can only be requested via virCommandRun or
     * virCommandDoAsyncIO. */
    if (cmd->inbuf && cmd->infd == -1 &&
        (synchronous || cmd->flags & VIR_EXEC_ASYNC_IO)) {
        if (virPipe(infd) < 0) {
            cmd->has_error = -1;
            return -1;
        }
        cmd->infd = infd[0];
        cmd->inpipe = infd[1];

        if (fcntl(cmd->inpipe, F_SETFL, O_NONBLOCK) < 0) {
            virReportSystemError(errno, "%s",
                                 _("fcntl failed to set O_NONBLOCK"));
            cmd->has_error = -1;
            ret = -1;
            goto cleanup;
        }
    } else if ((cmd->inbuf && cmd->infd == -1) ||
               (cmd->outbuf && cmd->outfdptr != &cmd->outfd) ||
               (cmd->errbuf && cmd->errfdptr != &cmd->errfd)) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("cannot mix string I/O with asynchronous command"));
        return -1;
    }

    if (cmd->pid != -1) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("command is already running as pid %lld"),
                       (long long) cmd->pid);
        goto cleanup;
    }

    if (!synchronous && (cmd->flags & VIR_EXEC_DAEMON)) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("daemonized command cannot use virCommandRunAsync"));
        goto cleanup;
    }
    if (cmd->pwd && (cmd->flags & VIR_EXEC_DAEMON)) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       _("daemonized command cannot set working directory %s"),
                       cmd->pwd);
        goto cleanup;
    }
    if (cmd->pidfile && !(cmd->flags & VIR_EXEC_DAEMON)) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("creation of pid file requires daemonized command"));
        goto cleanup;
    }

    str = virCommandToString(cmd, false);
    if (dryRunBuffer || dryRunCallback) {
        dryRunStatus = 0;
        if (!str) {
            /* error already reported by virCommandToString */
            goto cleanup;
        }

        if (dryRunBuffer) {
            VIR_DEBUG("Dry run requested, appending stringified "
                      "command to dryRunBuffer=%p", dryRunBuffer);
            virBufferAdd(dryRunBuffer, str, -1);
            virBufferAddChar(dryRunBuffer, '\n');
        }
        if (dryRunCallback) {
            dryRunCallback((const char *const*)cmd->args,
                           (const char *const*)cmd->env,
                           cmd->inbuf, cmd->outbuf, cmd->errbuf,
                           &dryRunStatus, dryRunOpaque);
        }
        ret = 0;
        goto cleanup;
    }

    VIR_DEBUG("About to run %s", str ? str : cmd->args[0]);
    ret = virExec(cmd);
    VIR_DEBUG("Command result %d, with PID %d",
              ret, (int)cmd->pid);

    for (i = 0; i < cmd->npassfd; i++) {
        if (cmd->passfd[i].flags & VIR_COMMAND_PASS_FD_CLOSE_PARENT)
            VIR_FORCE_CLOSE(cmd->passfd[i].fd);
    }
    cmd->npassfd = 0;
    VIR_FREE(cmd->passfd);

    if (ret == 0 && pid)
        *pid = cmd->pid;
    else
        cmd->reap = true;

    if (ret == 0 && cmd->flags & VIR_EXEC_ASYNC_IO) {
        if (cmd->inbuf)
            VIR_FORCE_CLOSE(cmd->infd);
        /* clear any error so we can catch if the helper thread reports one */
        cmd->has_error = 0;
        if (VIR_ALLOC(cmd->asyncioThread) < 0 ||
            virThreadCreateFull(cmd->asyncioThread, true,
                                virCommandDoAsyncIOHelper,
                                "cmd-async-io", false, cmd) < 0) {
            virReportSystemError(errno, "%s",
                                 _("Unable to create thread "
                                   "to process command's IO"));
            VIR_FREE(cmd->asyncioThread);
            virCommandAbort(cmd);
            ret = -1;
        }
    }

 cleanup:
    if (ret < 0) {
        VIR_FORCE_CLOSE(cmd->infd);
        VIR_FORCE_CLOSE(cmd->inpipe);
    }
    return ret;
}


/**
 * 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.  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.
 */
int
virCommandWait(virCommandPtr cmd, int *exitstatus)
{
    int ret;
    int status = 0;

    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    if (dryRunBuffer || dryRunCallback) {
        VIR_DEBUG("Dry run requested, returning status %d",
                  dryRunStatus);
        if (exitstatus)
            *exitstatus = dryRunStatus;
        else if (dryRunStatus)
            return -1;
        return 0;
    }

    if (cmd->pid == -1) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("command is not yet running"));
        return -1;
    }

    /* If virProcessWait 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 virProcessWait only fails due to failure to wait,
     * and repeat the exitstatus check code ourselves.  */
    ret = virProcessWait(cmd->pid, &status, true);
    if (cmd->flags & VIR_EXEC_ASYNC_IO) {
        cmd->flags &= ~VIR_EXEC_ASYNC_IO;
        virThreadJoin(cmd->asyncioThread);
        VIR_FREE(cmd->asyncioThread);
        VIR_FORCE_CLOSE(cmd->inpipe);
        if (cmd->has_error) {
            const char *msg = _("Error while processing command's IO");
            if (cmd->has_error < 0)
                virReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
            else
                virReportSystemError(cmd->has_error, "%s", msg);
            ret = -1;
        }
    }
    if (ret == 0) {
        cmd->pid = -1;
        cmd->reap = false;
        if (exitstatus && (cmd->rawStatus || WIFEXITED(status))) {
            *exitstatus = cmd->rawStatus ? status : WEXITSTATUS(status);
        } else if (status) {
            g_autofree char *str = virCommandToString(cmd, false);
            g_autofree char *st = virProcessTranslateStatus(status);
            bool haveErrMsg = cmd->errbuf && *cmd->errbuf && (*cmd->errbuf)[0];

            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Child process (%s) unexpected %s%s%s"),
                           str ? str : cmd->args[0], NULLSTR(st),
                           haveErrMsg ? ": " : "",
                           haveErrMsg ? *cmd->errbuf : "");
            return -1;
        }
    }

    return ret;
}


/**
 * 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
 * have started the child process.
 */
void
virCommandAbort(virCommandPtr cmd)
{
    if (!cmd || cmd->pid == -1)
        return;
    virProcessAbort(cmd->pid);
    cmd->pid = -1;
    cmd->reap = false;
}


/**
 * 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)
        return;

    if (cmd->handshake) {
        cmd->has_error = -1;
        VIR_DEBUG("Cannot require handshake twice");
        return;
    }

    if (virPipeQuiet(cmd->handshakeWait) < 0) {
        cmd->has_error = errno;
        return;
    }
    if (virPipeQuiet(cmd->handshakeNotify) < 0) {
        VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
        VIR_FORCE_CLOSE(cmd->handshakeWait[1]);
        cmd->has_error = errno;
        return;
    }

    VIR_DEBUG("Transfer handshake wait=%d notify=%d, "
              "keep handshake wait=%d notify=%d",
              cmd->handshakeWait[1], cmd->handshakeNotify[0],
              cmd->handshakeWait[0], cmd->handshakeNotify[1]);
    virCommandPassFD(cmd, cmd->handshakeWait[1],
                     VIR_COMMAND_PASS_FD_CLOSE_PARENT);
    virCommandPassFD(cmd, cmd->handshakeNotify[0],
                     VIR_COMMAND_PASS_FD_CLOSE_PARENT);
    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;
    int rv;
    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error || !cmd->handshake) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    if (cmd->handshakeWait[0] == -1) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Handshake is already complete"));
        return -1;
    }

    VIR_DEBUG("Wait for handshake on %d", cmd->handshakeWait[0]);
    if ((rv = saferead(cmd->handshakeWait[0], &c, sizeof(c))) != sizeof(c)) {
        if (rv < 0)
            virReportSystemError(errno, "%s",
                                 _("Unable to wait for child process"));
        else
            virReportSystemError(EIO, "%s",
                                 _("Child quit during startup handshake"));
        VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
        return -1;
    }
    if (c != '1') {
        g_autofree char *msg = NULL;
        ssize_t len;
        if (VIR_ALLOC_N(msg, 1024) < 0) {
            VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
            return -1;
        }
        /* Close the handshakeNotify fd before trying to read anything
         * further on the handshakeWait pipe; so that a child waiting
         * on our acknowledgment will die rather than deadlock.  */
        VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);

        if ((len = saferead(cmd->handshakeWait[0], msg, 1024)) < 0) {
            VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
            virReportSystemError(errno, "%s",
                                 _("No error message from child failure"));
            return -1;
        }
        VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
        msg[len-1] = '\0';
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s", msg);
        return -1;
    }
    VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
    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';
    if (!cmd ||cmd->has_error == ENOMEM) {
        virReportOOMError();
        return -1;
    }
    if (cmd->has_error || !cmd->handshake) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("invalid use of command API"));
        return -1;
    }

    if (cmd->handshakeNotify[1] == -1) {
        virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
                       _("Handshake is already complete"));
        return -1;
    }

    VIR_DEBUG("Notify handshake on %d", cmd->handshakeNotify[1]);
    if (safewrite(cmd->handshakeNotify[1], &c, sizeof(c)) != sizeof(c)) {
        virReportSystemError(errno, "%s", _("Unable to notify child process"));
        VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
        return -1;
    }
    VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
    return 0;
}
#else /* WIN32 */
int
virCommandSetSendBuffer(virCommandPtr cmd,
                        int fd G_GNUC_UNUSED,
                        unsigned char *buffer G_GNUC_UNUSED,
                        size_t buflen G_GNUC_UNUSED)
{
    if (!cmd || cmd->has_error)
        return -1;

    cmd->has_error = ENOTSUP;

    return -1;
}


int
virCommandExec(virCommandPtr cmd G_GNUC_UNUSED, gid_t *groups G_GNUC_UNUSED,
               int ngroups G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}


int
virCommandRun(virCommandPtr cmd G_GNUC_UNUSED, int *exitstatus G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}


int
virCommandRunAsync(virCommandPtr cmd G_GNUC_UNUSED, pid_t *pid G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}


int
virCommandWait(virCommandPtr cmd G_GNUC_UNUSED, int *exitstatus G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}


void
virCommandAbort(virCommandPtr cmd G_GNUC_UNUSED)
{
    /* Mingw lacks WNOHANG and kill().  But since we haven't ported
     * virExec to mingw yet, there's no process to be killed,
     * making this implementation trivially correct for now :)  */
}


void virCommandRequireHandshake(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->has_error = ENOSYS;
}


int virCommandHandshakeWait(virCommandPtr cmd G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}


int virCommandHandshakeNotify(virCommandPtr cmd G_GNUC_UNUSED)
{
    virReportSystemError(ENOSYS, "%s",
                         _("Executing new processes is not supported on Win32 platform"));
    return -1;
}
#endif /* WIN32 */


/**
 * 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 virProcessWait() or virProcessAbort() yourself.
 */
void
virCommandFree(virCommandPtr cmd)
{
    size_t i;
    if (!cmd)
        return;

    for (i = 0; i < cmd->npassfd; i++) {
        if (cmd->passfd[i].flags & VIR_COMMAND_PASS_FD_CLOSE_PARENT)
            VIR_FORCE_CLOSE(cmd->passfd[i].fd);
    }
    cmd->npassfd = 0;
    VIR_FREE(cmd->passfd);

    if (cmd->asyncioThread) {
        virThreadJoin(cmd->asyncioThread);
        VIR_FREE(cmd->asyncioThread);
    }
    VIR_FREE(cmd->inbuf);
    VIR_FORCE_CLOSE(cmd->outfd);
    VIR_FORCE_CLOSE(cmd->errfd);

    for (i = 0; i < cmd->nargs; i++)
        VIR_FREE(cmd->args[i]);
    VIR_FREE(cmd->args);

    for (i = 0; i < cmd->nenv; i++)
        VIR_FREE(cmd->env[i]);
    VIR_FREE(cmd->env);

    VIR_FREE(cmd->pwd);

    if (cmd->handshake) {
        /* The other 2 fds in these arrays are closed
         * due to use with virCommandPassFD
         */
        VIR_FORCE_CLOSE(cmd->handshakeWait[0]);
        VIR_FORCE_CLOSE(cmd->handshakeNotify[1]);
    }

    VIR_FREE(cmd->pidfile);

    if (cmd->reap)
        virCommandAbort(cmd);

#if defined(WITH_SECDRIVER_SELINUX)
    VIR_FREE(cmd->seLinuxLabel);
#endif
#if defined(WITH_SECDRIVER_APPARMOR)
    VIR_FREE(cmd->appArmorProfile);
#endif

    virCommandFreeSendBuffers(cmd);

    VIR_FREE(cmd);
}

/**
 * virCommandDoAsyncIO:
 * @cmd: command to do async IO on
 *
 * This requests asynchronous string IO on @cmd. It is useful in
 * combination with virCommandRunAsync():
 *
 *      g_autoptr(virCommand) cmd = virCommandNew*(...);
 *      g_autofree char *buf = NULL;
 *
 *      ...
 *
 *      virCommandSetOutputBuffer(cmd, &buf);
 *      virCommandDoAsyncIO(cmd);
 *
 *      if (virCommandRunAsync(cmd, NULL) < 0)
 *          return;
 *
 *      ...
 *
 *      if (virCommandWait(cmd, NULL) < 0)
 *          return;
 *
 *      // @buf now contains @cmd's stdout
 *      VIR_DEBUG("STDOUT: %s", NULLSTR(buf));
 *
 *      ...
 *
 *
 * The libvirt's event loop is used for handling stdios of @cmd.
 * Since current implementation uses strlen to determine length
 * of data to be written to @cmd's stdin, don't pass any binary
 * data. If you want to re-run command, you need to call this and
 * buffer setting functions (virCommandSet.*Buffer) prior each run.
 */
void
virCommandDoAsyncIO(virCommandPtr cmd)
{
    if (!cmd || cmd->has_error)
        return;

    cmd->flags |= VIR_EXEC_ASYNC_IO | VIR_EXEC_NONBLOCK;
}

/**
 * virCommandSetDryRun:
 * @buf: buffer to store stringified commands
 * @callback: callback to process input/output/args
 *
 * Sometimes it's desired to not actually run given command, but
 * see its string representation without having to change the
 * callee. Unit testing serves as a great example. In such cases,
 * the callee constructs the command and calls it via
 * virCommandRun* API. The virCommandSetDryRun allows you to
 * modify this behavior: once called, every call to
 * virCommandRun* results in command string representation being
 * appended to @buf instead of being executed. If @callback is
 * provided, then it is invoked with the argv, env and stdin
 * data string for the command. It is expected to fill the stdout
 * and stderr data strings and exit status variables.
 *
 * The strings stored in @buf are escaped for a shell and
 * separated by a newline. For example:
 *
 * virBuffer buffer = VIR_BUFFER_INITIALIZER;
 * virCommandSetDryRun(&buffer);
 *
 * virCommandPtr echocmd = virCommandNewArgList("/bin/echo", "Hello world", NULL);
 * virCommandRun(echocmd, NULL);
 *
 * After this, the @buffer should contain:
 *
 * /bin/echo 'Hello world'\n
 *
 * To cancel this effect pass NULL for @buf and @callback.
 */
void
virCommandSetDryRun(virBufferPtr buf,
                    virCommandDryRunCallback cb,
                    void *opaque)
{
    dryRunBuffer = buf;
    dryRunCallback = cb;
    dryRunOpaque = opaque;
}

#ifndef WIN32
/**
 * virCommandRunRegex:
 * @cmd: command to run
 * @nregex: number of regexes to apply
 * @regex: array of regexes to apply
 * @nvars: array of numbers of variables each regex will produce
 * @func: callback function that is called for every line of output,
 * needs to return 0 on success
 * @data: additional data that will be passed to the callback function
 * @prefix: prefix that will be skipped at the beginning of each line
 * @exitstatus: allows the caller to handle command run exit failures
 *
 * Run an external program.
 *
 * Read its output and apply a series of regexes to each line
 * When the entire set of regexes has matched consecutively
 * then run a callback passing in all the matches on the current line.
 *
 * Returns: 0 on success, -1 on memory allocation error, virCommandRun
 * error or callback function error
 */
int
virCommandRunRegex(virCommandPtr cmd,
                   int nregex,
                   const char **regex,
                   int *nvars,
                   virCommandRunRegexFunc func,
                   void *data,
                   const char *prefix,
                   int *exitstatus)
{
    GRegex **reg = NULL;
    size_t i, j, k;
    int totgroups = 0, ngroup = 0;
    char **groups;
    g_autofree char *outbuf = NULL;
    VIR_AUTOSTRINGLIST lines = NULL;
    int ret = -1;

    /* Compile all regular expressions */
    if (VIR_ALLOC_N(reg, nregex) < 0)
        return -1;

    for (i = 0; i < nregex; i++) {
        g_autoptr(GError) err = NULL;
        reg[i] = g_regex_new(regex[i], G_REGEX_OPTIMIZE, 0, &err);
        if (!reg[i]) {
            virReportError(VIR_ERR_INTERNAL_ERROR,
                           _("Failed to compile regex %s"), err->message);
            for (j = 0; j < i; j++)
                g_regex_unref(reg[j]);
            VIR_FREE(reg);
            return -1;
        }

        totgroups += nvars[i];
    }

    /* Storage for matched variables */
    if (VIR_ALLOC_N(groups, totgroups) < 0)
        goto cleanup;

    virCommandSetOutputBuffer(cmd, &outbuf);
    if (virCommandRun(cmd, exitstatus) < 0)
        goto cleanup;

    if (!outbuf) {
        /* no output */
        ret = 0;
        goto cleanup;
    }

    if (!(lines = virStringSplit(outbuf, "\n", 0)))
        goto cleanup;

    for (k = 0; lines[k]; k++) {
        g_autoptr(GMatchInfo) info = NULL;
        const char *p = NULL;

        /* ignore any command prefix */
        if (prefix)
            p = STRSKIP(lines[k], prefix);
        if (!p)
            p = lines[k];

        ngroup = 0;
        for (i = 0; i < nregex; i++) {
            if (!(g_regex_match(reg[i], p, 0, &info)))
                break;

            /* NB match #0 is the full pattern, so we offset j by 1 */
            for (j = 1; j <= nvars[i]; j++)
                groups[ngroup++] = g_match_info_fetch(info, j);
        }
        /* We've matched on the last regex, so callback time */
        if (i == nregex) {
            if (((*func)(groups, data)) < 0)
                goto cleanup;
        }

        for (j = 0; j < ngroup; j++)
            VIR_FREE(groups[j]);
    }

    ret = 0;
 cleanup:
    if (groups) {
        for (j = 0; j < totgroups; j++)
            VIR_FREE(groups[j]);
        VIR_FREE(groups);
    }

    for (i = 0; i < nregex; i++)
        g_regex_unref(reg[i]);

    VIR_FREE(reg);
    return ret;
}

/*
 * Run an external program and read from its standard output
 * a stream of tokens from IN_STREAM, applying FUNC to
 * each successive sequence of N_COLUMNS tokens.
 * If FUNC returns < 0, stop processing input and return -1.
 * Return -1 if N_COLUMNS == 0.
 * Return -1 upon memory allocation error.
 * If the number of input tokens is not a multiple of N_COLUMNS,
 * then the final FUNC call will specify a number smaller than N_COLUMNS.
 * If there are no input tokens (empty input), call FUNC with N_COLUMNS == 0.
 */
int
virCommandRunNul(virCommandPtr cmd,
                 size_t n_columns,
                 virCommandRunNulFunc func,
                 void *data)
{
    size_t n_tok = 0;
    int fd = -1;
    FILE *fp = NULL;
    char **v;
    int ret = -1;
    size_t i;

    if (n_columns == 0)
        return -1;

    if (VIR_ALLOC_N(v, n_columns) < 0)
        return -1;
    for (i = 0; i < n_columns; i++)
        v[i] = NULL;

    virCommandSetOutputFD(cmd, &fd);
    if (virCommandRunAsync(cmd, NULL) < 0)
        goto cleanup;

    if ((fp = VIR_FDOPEN(fd, "r")) == NULL) {
        virReportError(VIR_ERR_INTERNAL_ERROR,
                       "%s", _("cannot open file using fd"));
        goto cleanup;
    }

    while (1) {
        char *buf = NULL;
        size_t buf_len = 0;
        /* Be careful: even when it returns -1,
           this use of getdelim allocates memory.  */
        ssize_t tok_len = getdelim(&buf, &buf_len, 0, fp);
        v[n_tok] = buf;
        if (tok_len < 0) {
            /* Maybe EOF, maybe an error.
               If n_tok > 0, then we know it's an error.  */
            if (n_tok && func(n_tok, v, data) < 0)
                goto cleanup;
            break;
        }
        ++n_tok;
        if (n_tok == n_columns) {
            if (func(n_tok, v, data) < 0)
                goto cleanup;
            n_tok = 0;
            for (i = 0; i < n_columns; i++)
                VIR_FREE(v[i]);
        }
    }

    if (feof(fp) < 0) {
        virReportSystemError(errno, "%s",
                             _("read error on pipe"));
        goto cleanup;
    }

    ret = virCommandWait(cmd, NULL);
 cleanup:
    for (i = 0; i < n_columns; i++)
        VIR_FREE(v[i]);
    VIR_FREE(v);

    VIR_FORCE_FCLOSE(fp);
    VIR_FORCE_CLOSE(fd);

    return ret;
}

#else /* WIN32 */

int
virCommandRunRegex(virCommandPtr cmd G_GNUC_UNUSED,
                   int nregex G_GNUC_UNUSED,
                   const char **regex G_GNUC_UNUSED,
                   int *nvars G_GNUC_UNUSED,
                   virCommandRunRegexFunc func G_GNUC_UNUSED,
                   void *data G_GNUC_UNUSED,
                   const char *prefix G_GNUC_UNUSED,
                   int *exitstatus G_GNUC_UNUSED)
{
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("%s not implemented on Win32"), __FUNCTION__);
    return -1;
}

int
virCommandRunNul(virCommandPtr cmd G_GNUC_UNUSED,
                 size_t n_columns G_GNUC_UNUSED,
                 virCommandRunNulFunc func G_GNUC_UNUSED,
                 void *data G_GNUC_UNUSED)
{
    virReportError(VIR_ERR_INTERNAL_ERROR,
                   _("%s not implemented on Win32"), __FUNCTION__);
    return -1;
}
#endif /* WIN32 */
