[libvirt] [PATCH 14/16] Move virRun, virExec*, virFork to util/command

Daniel P. Berrange berrange at redhat.com
Wed May 11 10:35:27 UTC 2011


On Tue, May 10, 2011 at 04:07:53PM -0400, Cole Robinson wrote:
> Seems reasonable to have all command wrappers in the same place
> 
> Signed-off-by: Cole Robinson <crobinso at redhat.com>
> ---
>  cfg.mk                                    |    2 +-
>  src/libvirt_private.syms                  |    5 +-
>  src/lxc/veth.c                            |    2 +-
>  src/nwfilter/nwfilter_ebiptables_driver.c |    1 +
>  src/storage/storage_backend_fs.c          |    2 +-
>  src/storage/storage_backend_logical.c     |    2 +-
>  src/util/command.c                        |  590 +++++++++++++++++++++++++++++
>  src/util/command.h                        |   14 +
>  src/util/ebtables.c                       |    2 +-
>  src/util/pci.c                            |    2 +-
>  src/util/util.c                           |  579 +----------------------------
>  src/util/util.h                           |   24 --
>  src/vmware/vmware_driver.c                |    1 +
>  13 files changed, 616 insertions(+), 610 deletions(-)
> 
> diff --git a/cfg.mk b/cfg.mk
> index 9ee0dd0..09e361a 100644
> --- a/cfg.mk
> +++ b/cfg.mk
> @@ -621,7 +621,7 @@ exclude_file_name_regexp--sc_prohibit_doubled_word = ^po/
>  exclude_file_name_regexp--sc_prohibit_empty_lines_at_EOF = \
>    (^docs/api_extension/|^tests/qemuhelpdata/|\.(gif|ico|png)$$)
>  
> -_src2=src/(util/util|libvirt|lxc/lxc_controller)
> +_src2=src/(util/command|libvirt|lxc/lxc_controller)
>  exclude_file_name_regexp--sc_prohibit_fork_wrappers = \
>    (^docs|^($(_src2)|tests/testutils|daemon/libvirtd)\.c$$)
>  
> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
> index cc900e7..ecf6ab9 100644
> --- a/src/libvirt_private.syms
> +++ b/src/libvirt_private.syms
> @@ -130,6 +130,8 @@ virCommandTransferFD;
>  virCommandTranslateStatus;
>  virCommandWait;
>  virCommandWriteArgLog;
> +virFork;
> +virRun;
>  
>  
>  # conf.h
> @@ -908,7 +910,6 @@ virEnumFromString;
>  virEnumToString;
>  virEventAddHandle;
>  virEventRemoveHandle;
> -virExecWithHook;
>  virFileAbsPath;
>  virFileDeletePid;
>  virFileExists;
> @@ -930,7 +931,6 @@ virFileStripSuffix;
>  virFileWaitForDevices;
>  virFileWriteStr;
>  virFindFileInPath;
> -virFork;
>  virFormatMacAddr;
>  virGenerateMacAddr;
>  virGetGroupID;
> @@ -949,7 +949,6 @@ virParseVersionString;
>  virPipeReadUntilEOF;
>  virRandom;
>  virRandomInitialize;
> -virRun;
>  virSetBlocking;
>  virSetCloseExec;
>  virSetInherit;
> diff --git a/src/lxc/veth.c b/src/lxc/veth.c
> index a00aa23..1a96f82 100644
> --- a/src/lxc/veth.c
> +++ b/src/lxc/veth.c
> @@ -21,7 +21,7 @@
>  #include "internal.h"
>  #include "logging.h"
>  #include "memory.h"
> -#include "util.h"
> +#include "command.h"
>  #include "virterror_internal.h"
>  
>  #define VIR_FROM_THIS VIR_FROM_LXC
> diff --git a/src/nwfilter/nwfilter_ebiptables_driver.c b/src/nwfilter/nwfilter_ebiptables_driver.c
> index 14ce019..b4cd198 100644
> --- a/src/nwfilter/nwfilter_ebiptables_driver.c
> +++ b/src/nwfilter/nwfilter_ebiptables_driver.c
> @@ -39,6 +39,7 @@
>  #include "nwfilter_gentech_driver.h"
>  #include "nwfilter_ebiptables_driver.h"
>  #include "files.h"
> +#include "command.h"
>  
>  
>  #define VIR_FROM_THIS VIR_FROM_NWFILTER
> diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c
> index 0a6b074..d940055 100644
> --- a/src/storage/storage_backend_fs.c
> +++ b/src/storage/storage_backend_fs.c
> @@ -41,7 +41,7 @@
>  #include "storage_backend_fs.h"
>  #include "storage_conf.h"
>  #include "storage_file.h"
> -#include "util.h"
> +#include "command.h"
>  #include "memory.h"
>  #include "xml.h"
>  #include "files.h"
> diff --git a/src/storage/storage_backend_logical.c b/src/storage/storage_backend_logical.c
> index 7809324..c18cd57 100644
> --- a/src/storage/storage_backend_logical.c
> +++ b/src/storage/storage_backend_logical.c
> @@ -34,7 +34,7 @@
>  #include "virterror_internal.h"
>  #include "storage_backend_logical.h"
>  #include "storage_conf.h"
> -#include "util.h"
> +#include "command.h"
>  #include "memory.h"
>  #include "logging.h"
>  #include "files.h"
> diff --git a/src/util/command.c b/src/util/command.c
> index fa6425d..5e65fc7 100644
> --- a/src/util/command.c
> +++ b/src/util/command.c
> @@ -26,6 +26,12 @@
>  #include <stdlib.h>
>  #include <sys/stat.h>
>  #include <sys/wait.h>
> +#include <sys/types.h>
> +#include <fcntl.h>
> +
> +#if HAVE_CAPNG
> +# include <cap-ng.h>
> +#endif
>  
>  #include "command.h"
>  #include "memory.h"
> @@ -34,6 +40,7 @@
>  #include "logging.h"
>  #include "files.h"
>  #include "buf.h"
> +#include "ignore-value.h"
>  
>  #define VIR_FROM_THIS VIR_FROM_NONE
>  
> @@ -41,6 +48,14 @@
>      virReportErrorHelper(VIR_FROM_NONE, code, __FILE__,                 \
>                           __FUNCTION__, __LINE__, __VA_ARGS__)
>  
> +/* Flags for virExecWithHook */
> +enum {
> +    VIR_EXEC_NONE   = 0,
> +    VIR_EXEC_NONBLOCK = (1 << 0),
> +    VIR_EXEC_DAEMON = (1 << 1),
> +    VIR_EXEC_CLEAR_CAPS = (1 << 2),
> +};
> +
>  enum {
>      /* Internal-use extension beyond public VIR_EXEC_ flags */
>      VIR_EXEC_RUN_SYNC = 0x40000000,
> @@ -84,6 +99,581 @@ struct _virCommand {
>      bool reap;
>  };
>  
> +#ifndef WIN32
> +
> +int virSetInherit(int fd, bool inherit) {
> +    int flags;
> +    if ((flags = fcntl(fd, F_GETFD)) < 0)
> +        return -1;
> +    if (inherit)
> +        flags &= ~FD_CLOEXEC;
> +    else
> +        flags |= FD_CLOEXEC;
> +    if ((fcntl(fd, F_SETFD, flags)) < 0)
> +        return -1;
> +    return 0;
> +}
> +
> +
> +# if HAVE_CAPNG
> +static int virClearCapabilities(void)
> +{
> +    int ret;
> +
> +    capng_clear(CAPNG_SELECT_BOTH);
> +
> +    if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
> +        virCommandError(VIR_ERR_INTERNAL_ERROR,
> +                        _("cannot clear process capabilities %d"), ret);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +# else
> +static int virClearCapabilities(void)
> +{
> +//    VIR_WARN0("libcap-ng support not compiled in, unable to clear capabilities");
> +    return 0;
> +}
> +# endif
> +
> +
> +/* virFork() - fork a new process while avoiding various race/deadlock
> +               conditions
> +
> +   @pid - a pointer to a pid_t that will receive the return value from
> +          fork()
> +
> +   on return from virFork(), if *pid < 0, the fork failed and there is
> +   no new process. Otherwise, just like fork(), if *pid == 0, it is the
> +   child process returning, and if *pid > 0, it is the parent.
> +
> +   Even if *pid >= 0, if the return value from virFork() is < 0, it
> +   indicates a failure that occurred in the parent or child process
> +   after the fork. In this case, the child process should call
> +   _exit(EXIT_FAILURE) after doing any additional error reporting.
> +
> + */
> +int virFork(pid_t *pid) {
> +# ifdef HAVE_PTHREAD_SIGMASK
> +    sigset_t oldmask, newmask;
> +# endif
> +    struct sigaction sig_action;
> +    int saved_errno, ret = -1;
> +
> +    *pid = -1;
> +
> +    /*
> +     * Need to block signals now, so that child process can safely
> +     * kill off caller's signal handlers without a race.
> +     */
> +# ifdef HAVE_PTHREAD_SIGMASK
> +    sigfillset(&newmask);
> +    if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
> +        saved_errno = errno;
> +        virReportSystemError(errno,
> +                             "%s", _("cannot block signals"));
> +        goto cleanup;
> +    }
> +# endif
> +
> +    /* 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) {
> +# ifdef HAVE_PTHREAD_SIGMASK
> +        /* attempt to restore signal mask, but ignore failure, to
> +           avoid obscuring the fork failure */
> +        ignore_value (pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
> +# endif
> +        virReportSystemError(saved_errno,
> +                             "%s", _("cannot fork child process"));
> +        goto cleanup;
> +    }
> +
> +    if (*pid) {
> +
> +        /* parent process */
> +
> +# ifdef HAVE_PTHREAD_SIGMASK
> +        /* Restore our original signal mask now that the child is
> +           safely running */
> +        if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) {
> +            saved_errno = errno; /* save for caller */
> +            virReportSystemError(errno, "%s", _("cannot unblock signals"));
> +            goto cleanup;
> +        }
> +# endif
> +        ret = 0;
> +
> +    } else {
> +
> +        /* child process */
> +
> +        int logprio;
> +        int i;
> +
> +        /* Remove any error callback so errors in child now
> +           get sent to stderr where they stand a fighting chance
> +           of being seen / 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 wont happen, the latter we
> +               expect, so no need to check return value */
> +
> +            sigaction(i, &sig_action, NULL);
> +        }
> +
> +# ifdef HAVE_PTHREAD_SIGMASK
> +        /* 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) {
> +            saved_errno = errno; /* save for caller */
> +            virReportSystemError(errno, "%s", _("cannot unblock signals"));
> +            goto cleanup;
> +        }
> +# endif
> +        ret = 0;
> +    }
> +
> +cleanup:
> +    if (ret < 0)
> +        errno = saved_errno;
> +    return ret;
> +}
> +
> +/*
> + * @argv argv to exec
> + * @envp optional environment to use for exec
> + * @keepfd options fd_ret to keep open for child process
> + * @retpid optional pointer to store child process pid
> + * @infd optional file descriptor to use as child input, otherwise /dev/null
> + * @outfd optional pointer to communicate output fd behavior
> + *        outfd == NULL : Use /dev/null
> + *        *outfd == -1  : Use a new fd
> + *        *outfd != -1  : Use *outfd
> + * @errfd optional pointer to communcate error fd behavior. See outfd
> + * @flags possible combination of the following:
> + *        VIR_EXEC_NONE     : Default function behavior
> + *        VIR_EXEC_NONBLOCK : Set child process output fd's as non-blocking
> + *        VIR_EXEC_DAEMON   : Daemonize the child process
> + * @hook optional virExecHook function to call prior to exec
> + * @data data to pass to the hook function
> + * @pidfile path to use as pidfile for daemonized process (needs DAEMON flag)
> + */
> +static int
> +virExecWithHook(const char *const*argv,
> +          const char *const*envp,
> +          const fd_set *keepfd,
> +          pid_t *retpid,
> +          int infd, int *outfd, int *errfd,
> +          int flags,
> +          virExecHook hook,
> +          void *data,
> +          char *pidfile)
> +{
> +    pid_t pid;
> +    int null, i, openmax;
> +    int pipeout[2] = {-1,-1};
> +    int pipeerr[2] = {-1,-1};
> +    int childout = -1;
> +    int childerr = -1;
> +    int tmpfd;
> +    const char *binary = NULL;
> +    int forkRet;
> +    char *argv_str = NULL;
> +    char *envp_str = NULL;
> +
> +    if ((argv_str = virArgvToString(argv)) == NULL) {
> +        virReportOOMError();
> +        return -1;
> +    }
> +
> +    if (envp) {
> +        if ((envp_str = virArgvToString(envp)) == NULL) {
> +            VIR_FREE(argv_str);
> +            virReportOOMError();
> +            return -1;
> +        }
> +        VIR_DEBUG("%s %s", envp_str, argv_str);
> +        VIR_FREE(envp_str);
> +    } else {
> +        VIR_DEBUG0(argv_str);
> +    }
> +    VIR_FREE(argv_str);
> +
> +    if (argv[0][0] != '/') {
> +        if (!(binary = virFindFileInPath(argv[0]))) {
> +            virReportSystemError(ENOENT,
> +                                 _("Cannot find '%s' in path"),
> +                                 argv[0]);
> +            return -1;
> +        }
> +    } else {
> +        binary = argv[0];
> +    }
> +
> +    if ((null = open("/dev/null", O_RDWR)) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot open %s"),
> +                             "/dev/null");
> +        goto cleanup;
> +    }
> +
> +    if (outfd != NULL) {
> +        if (*outfd == -1) {
> +            if (pipe(pipeout) < 0) {
> +                virReportSystemError(errno,
> +                                     "%s", _("cannot create pipe"));
> +                goto cleanup;
> +            }
> +
> +            if ((flags & VIR_EXEC_NONBLOCK) &&
> +                virSetNonBlock(pipeout[0]) == -1) {
> +                virReportSystemError(errno,
> +                                     "%s", _("Failed to set non-blocking file descriptor flag"));
> +                goto cleanup;
> +            }
> +
> +            if (virSetCloseExec(pipeout[0]) == -1) {
> +                virReportSystemError(errno,
> +                                     "%s", _("Failed to set close-on-exec file descriptor flag"));
> +                goto cleanup;
> +            }
> +
> +            childout = pipeout[1];
> +        } else {
> +            childout = *outfd;
> +        }
> +    } else {
> +        childout = null;
> +    }
> +
> +    if (errfd != NULL) {
> +        if (*errfd == -1) {
> +            if (pipe(pipeerr) < 0) {
> +                virReportSystemError(errno,
> +                                     "%s", _("Failed to create pipe"));
> +                goto cleanup;
> +            }
> +
> +            if ((flags & VIR_EXEC_NONBLOCK) &&
> +                virSetNonBlock(pipeerr[0]) == -1) {
> +                virReportSystemError(errno,
> +                                     "%s", _("Failed to set non-blocking file descriptor flag"));
> +                goto cleanup;
> +            }
> +
> +            if (virSetCloseExec(pipeerr[0]) == -1) {
> +                virReportSystemError(errno,
> +                                     "%s", _("Failed to set close-on-exec file descriptor flag"));
> +                goto cleanup;
> +            }
> +
> +            childerr = pipeerr[1];
> +        } else {
> +            childerr = *errfd;
> +        }
> +    } else {
> +        childerr = null;
> +    }
> +
> +    forkRet = virFork(&pid);
> +
> +    if (pid < 0) {
> +        goto cleanup;
> +    }
> +
> +    if (pid) { /* parent */
> +        VIR_FORCE_CLOSE(null);
> +        if (outfd && *outfd == -1) {
> +            VIR_FORCE_CLOSE(pipeout[1]);
> +            *outfd = pipeout[0];
> +        }
> +        if (errfd && *errfd == -1) {
> +            VIR_FORCE_CLOSE(pipeerr[1]);
> +            *errfd = pipeerr[0];
> +        }
> +
> +        if (forkRet < 0) {
> +            goto cleanup;
> +        }
> +
> +        *retpid = pid;
> +
> +        if (binary != argv[0])
> +            VIR_FREE(binary);
> +
> +        return 0;
> +    }
> +
> +    /* child */
> +
> +    if (forkRet < 0) {
> +        /* The fork was sucessful, but after that there was an error
> +         * in the child (which was already logged).
> +        */
> +        goto fork_error;
> +    }
> +
> +    openmax = sysconf (_SC_OPEN_MAX);
> +    for (i = 3; i < openmax; i++)
> +        if (i != infd &&
> +            i != null &&
> +            i != childout &&
> +            i != childerr &&
> +            (!keepfd || i >= FD_SETSIZE || !FD_ISSET(i, keepfd))) {
> +            tmpfd = i;
> +            VIR_FORCE_CLOSE(tmpfd);
> +        }
> +
> +    if (dup2(infd >= 0 ? infd : null, STDIN_FILENO) < 0) {
> +        virReportSystemError(errno,
> +                             "%s", _("failed to setup stdin file handle"));
> +        goto fork_error;
> +    }
> +    if (childout > 0 &&
> +        dup2(childout, STDOUT_FILENO) < 0) {
> +        virReportSystemError(errno,
> +                             "%s", _("failed to setup stdout file handle"));
> +        goto fork_error;
> +    }
> +    if (childerr > 0 &&
> +        dup2(childerr, STDERR_FILENO) < 0) {
> +        virReportSystemError(errno,
> +                             "%s", _("failed to setup stderr file handle"));
> +        goto fork_error;
> +    }
> +
> +    if (infd != STDIN_FILENO)
> +        VIR_FORCE_CLOSE(infd);
> +    VIR_FORCE_CLOSE(null);
> +    if (childout > STDERR_FILENO) {
> +        tmpfd = childout;   /* preserve childout value */
> +        VIR_FORCE_CLOSE(tmpfd);
> +    }
> +    if (childerr > STDERR_FILENO &&
> +        childerr != childout) {
> +        VIR_FORCE_CLOSE(childerr);
> +    }
> +
> +    /* Initialize full logging for a while */
> +    virLogSetFromEnv();
> +
> +    /* Daemonize as late as possible, so the parent process can detect
> +     * the above errors with wait* */
> +    if (flags & VIR_EXEC_DAEMON) {
> +        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) {
> +            if (pidfile && virFileWritePidPath(pidfile,pid)) {
> +                kill(pid, SIGTERM);
> +                usleep(500*1000);
> +                kill(pid, SIGTERM);
> +                virReportSystemError(errno,
> +                                     _("could not write pidfile %s for %d"),
> +                                     pidfile, pid);
> +                goto fork_error;
> +            }
> +            _exit(0);
> +        }
> +    }
> +
> +    if (hook) {
> +        /* virFork reset all signal handlers to the defaults.
> +         * This is good for the child process, but our hook
> +         * risks running something that generates SIGPIPE,
> +         * so we need to temporarily block that again
> +         */
> +        struct sigaction waxon, waxoff;
> +        waxoff.sa_handler = SIG_IGN;
> +        waxoff.sa_flags = 0;
> +        sigemptyset(&waxoff.sa_mask);
> +        memset(&waxon, 0, sizeof(waxon));
> +        if (sigaction(SIGPIPE, &waxoff, &waxon) < 0) {
> +            virReportSystemError(errno, "%s",
> +                                 _("Could not disable SIGPIPE"));
> +            goto fork_error;
> +        }
> +
> +        if ((hook)(data) != 0) {
> +            VIR_DEBUG0("Hook function failed.");
> +            goto fork_error;
> +        }
> +
> +        if (sigaction(SIGPIPE, &waxon, NULL) < 0) {
> +            virReportSystemError(errno, "%s",
> +                                 _("Could not re-enable SIGPIPE"));
> +            goto fork_error;
> +        }
> +    }
> +
> +    /* The steps above may need todo something privileged, so
> +     * we delay clearing capabilities until the last minute */
> +    if ((flags & VIR_EXEC_CLEAR_CAPS) &&
> +        virClearCapabilities() < 0)
> +        goto fork_error;
> +
> +    /* Close logging again to ensure no FDs leak to child */
> +    virLogReset();
> +
> +    if (envp)
> +        execve(binary, (char **) argv, (char**)envp);
> +    else
> +        execv(binary, (char **) argv);
> +
> +    virReportSystemError(errno,
> +                         _("cannot execute binary %s"),
> +                         argv[0]);
> +
> + fork_error:
> +    virDispatchError(NULL);
> +    _exit(EXIT_FAILURE);
> +
> + cleanup:
> +    /* This is cleanup of parent process only - child
> +       should never jump here on error */
> +
> +    if (binary != argv[0])
> +        VIR_FREE(binary);
> +
> +    /* NB we don't virCommandError() on any failures here
> +       because the code which jumped hre 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;
> +}
> +
> +/**
> + * @argv NULL terminated argv to run
> + * @status optional variable to return exit status in
> + *
> + * Run a command without using the shell.
> + *
> + * If status is NULL, then return 0 if the command run and
> + * exited with 0 status; Otherwise return -1
> + *
> + * If status is not-NULL, then return 0 if the command ran.
> + * The status variable is filled with the command exit status
> + * and should be checked by caller for success. Return -1
> + * only if the command could not be run.
> + */
> +int
> +virRun(const char *const*argv, int *status)
> +{
> +    virCommandPtr cmd = virCommandNew(argv[0]);
> +    const char * const *tmp;
> +    int ret;
> +
> +    tmp = argv;
> +    while (*(++tmp)) {
> +        virCommandAddArg(cmd, *tmp);
> +    }
> +
> +    ret = virCommandRun(cmd, status);
> +    virCommandFree(cmd);
> +    return ret;
> +}
> +
> +#else /* WIN32 */
> +
> +int virSetInherit(int fd ATTRIBUTE_UNUSED, bool inherit ATTRIBUTE_UNUSED)
> +{
> +    return -1;
> +}
> +
> +virRun(const char *const *argv ATTRIBUTE_UNUSED,
> +       int *status)
> +{
> +    if (status)
> +        *status = ENOTSUP;
> +    else
> +        virCommandError(VIR_ERR_INTERNAL_ERROR,
> +                        "%s", _("virRun is not implemented for WIN32"));
> +    return -1;
> +}
> +
> +static int
> +virExecWithHook(const char *const*argv ATTRIBUTE_UNUSED,
> +                const char *const*envp ATTRIBUTE_UNUSED,
> +                const fd_set *keepfd ATTRIBUTE_UNUSED,
> +                pid_t *retpid ATTRIBUTE_UNUSED,
> +                int infd ATTRIBUTE_UNUSED,
> +                int *outfd ATTRIBUTE_UNUSED,
> +                int *errfd ATTRIBUTE_UNUSED,
> +                int flags ATTRIBUTE_UNUSED,
> +                virExecHook hook ATTRIBUTE_UNUSED,
> +                void *data ATTRIBUTE_UNUSED,
> +                char *pidfile ATTRIBUTE_UNUSED)
> +{
> +    /* XXX: Some day we can implement pieces of virCommand/virExec on
> +     * top of _spawn() or CreateProcess(), but we can't implement
> +     * everything, since mingw completely lacks fork(), so we cannot
> +     * run hook code in the child.  */
> +    virCommandError(VIR_ERR_INTERNAL_ERROR,
> +                    "%s", _("virExec is not implemented for WIN32"));
> +    return -1;
> +}
> +
> +int
> +virFork(pid_t *pid)
> +{
> +    *pid = -1;
> +    errno = ENOTSUP;
> +
> +    return -1;
> +}
> +
> +#endif /* WIN32 */
> +
> +
>  /*
>   * Create a new command for named binary
>   */
> diff --git a/src/util/command.h b/src/util/command.h
> index b16bc27..e690fda 100644
> --- a/src/util/command.h
> +++ b/src/util/command.h
> @@ -29,6 +29,20 @@
>  typedef struct _virCommand virCommand;
>  typedef virCommand *virCommandPtr;
>  
> +/* This will execute in the context of the first child
> + * after fork() but before execve() */
> +typedef int (*virExecHook)(void *data);
> +
> +/*
> + * Fork wrapper with extra error checking
> + */
> +int virFork(pid_t *pid) ATTRIBUTE_RETURN_CHECK;
> +
> +/*
> + * Simple synchronous command wrapper
> + */
> +int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
> +
>  /*
>   * Create a new command for named binary
>   */
> diff --git a/src/util/ebtables.c b/src/util/ebtables.c
> index 27dce5d..d1afff0 100644
> --- a/src/util/ebtables.c
> +++ b/src/util/ebtables.c
> @@ -41,7 +41,7 @@
>  
>  #include "internal.h"
>  #include "ebtables.h"
> -#include "util.h"
> +#include "command.h"
>  #include "memory.h"
>  #include "virterror_internal.h"
>  #include "logging.h"
> diff --git a/src/util/pci.c b/src/util/pci.c
> index d7f74f9..091d76a 100644
> --- a/src/util/pci.c
> +++ b/src/util/pci.c
> @@ -35,7 +35,7 @@
>  
>  #include "logging.h"
>  #include "memory.h"
> -#include "util.h"
> +#include "command.h"
>  #include "virterror_internal.h"
>  #include "files.h"
>  
> diff --git a/src/util/util.c b/src/util/util.c
> index f83b2d0..9b8d70d 100644
> --- a/src/util/util.c
> +++ b/src/util/util.c
> @@ -34,11 +34,11 @@
>  #include <errno.h>
>  #include <poll.h>
>  #include <time.h>
> -#include <sys/types.h>
> -#include <sys/stat.h>
>  #include <sys/ioctl.h>
>  #include <sys/wait.h>
>  #include <sys/time.h>
> +#include <sys/stat.h>
> +#include <sys/types.h>
>  #if HAVE_MMAP
>  # include <sys/mman.h>
>  #endif
> @@ -69,7 +69,6 @@
>  #include "virterror_internal.h"
>  #include "logging.h"
>  #include "event.h"
> -#include "ignore-value.h"
>  #include "buf.h"
>  #include "util.h"
>  #include "memory.h"
> @@ -255,585 +254,11 @@ int virSetNonBlock(int fd) {
>      return virSetBlocking(fd, false);
>  }
>  
> -
>  int virSetCloseExec(int fd)
>  {
>      return virSetInherit(fd, false);
>  }
>  
> -#ifndef WIN32
> -
> -int virSetInherit(int fd, bool inherit) {
> -    int flags;
> -    if ((flags = fcntl(fd, F_GETFD)) < 0)
> -        return -1;
> -    if (inherit)
> -        flags &= ~FD_CLOEXEC;
> -    else
> -        flags |= FD_CLOEXEC;
> -    if ((fcntl(fd, F_SETFD, flags)) < 0)
> -        return -1;
> -    return 0;
> -}
> -
> -
> -# if HAVE_CAPNG
> -static int virClearCapabilities(void)
> -{
> -    int ret;
> -
> -    capng_clear(CAPNG_SELECT_BOTH);
> -
> -    if ((ret = capng_apply(CAPNG_SELECT_BOTH)) < 0) {
> -        virUtilError(VIR_ERR_INTERNAL_ERROR,
> -                     _("cannot clear process capabilities %d"), ret);
> -        return -1;
> -    }
> -
> -    return 0;
> -}
> -# else
> -static int virClearCapabilities(void)
> -{
> -//    VIR_WARN0("libcap-ng support not compiled in, unable to clear capabilities");
> -    return 0;
> -}
> -# endif
> -
> -
> -/* virFork() - fork a new process while avoiding various race/deadlock conditions
> -
> -   @pid - a pointer to a pid_t that will receive the return value from
> -          fork()
> -
> -   on return from virFork(), if *pid < 0, the fork failed and there is
> -   no new process. Otherwise, just like fork(), if *pid == 0, it is the
> -   child process returning, and if *pid > 0, it is the parent.
> -
> -   Even if *pid >= 0, if the return value from virFork() is < 0, it
> -   indicates a failure that occurred in the parent or child process
> -   after the fork. In this case, the child process should call
> -   _exit(EXIT_FAILURE) after doing any additional error reporting.
> -
> - */
> -int virFork(pid_t *pid) {
> -# ifdef HAVE_PTHREAD_SIGMASK
> -    sigset_t oldmask, newmask;
> -# endif
> -    struct sigaction sig_action;
> -    int saved_errno, ret = -1;
> -
> -    *pid = -1;
> -
> -    /*
> -     * Need to block signals now, so that child process can safely
> -     * kill off caller's signal handlers without a race.
> -     */
> -# ifdef HAVE_PTHREAD_SIGMASK
> -    sigfillset(&newmask);
> -    if (pthread_sigmask(SIG_SETMASK, &newmask, &oldmask) != 0) {
> -        saved_errno = errno;
> -        virReportSystemError(errno,
> -                             "%s", _("cannot block signals"));
> -        goto cleanup;
> -    }
> -# endif
> -
> -    /* 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) {
> -# ifdef HAVE_PTHREAD_SIGMASK
> -        /* attempt to restore signal mask, but ignore failure, to
> -           avoid obscuring the fork failure */
> -        ignore_value (pthread_sigmask(SIG_SETMASK, &oldmask, NULL));
> -# endif
> -        virReportSystemError(saved_errno,
> -                             "%s", _("cannot fork child process"));
> -        goto cleanup;
> -    }
> -
> -    if (*pid) {
> -
> -        /* parent process */
> -
> -# ifdef HAVE_PTHREAD_SIGMASK
> -        /* Restore our original signal mask now that the child is
> -           safely running */
> -        if (pthread_sigmask(SIG_SETMASK, &oldmask, NULL) != 0) {
> -            saved_errno = errno; /* save for caller */
> -            virReportSystemError(errno, "%s", _("cannot unblock signals"));
> -            goto cleanup;
> -        }
> -# endif
> -        ret = 0;
> -
> -    } else {
> -
> -        /* child process */
> -
> -        int logprio;
> -        int i;
> -
> -        /* Remove any error callback so errors in child now
> -           get sent to stderr where they stand a fighting chance
> -           of being seen / 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 wont happen, the latter we
> -               expect, so no need to check return value */
> -
> -            sigaction(i, &sig_action, NULL);
> -        }
> -
> -# ifdef HAVE_PTHREAD_SIGMASK
> -        /* 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) {
> -            saved_errno = errno; /* save for caller */
> -            virReportSystemError(errno, "%s", _("cannot unblock signals"));
> -            goto cleanup;
> -        }
> -# endif
> -        ret = 0;
> -    }
> -
> -cleanup:
> -    if (ret < 0)
> -        errno = saved_errno;
> -    return ret;
> -}
> -
> -/*
> - * @argv argv to exec
> - * @envp optional environment to use for exec
> - * @keepfd options fd_ret to keep open for child process
> - * @retpid optional pointer to store child process pid
> - * @infd optional file descriptor to use as child input, otherwise /dev/null
> - * @outfd optional pointer to communicate output fd behavior
> - *        outfd == NULL : Use /dev/null
> - *        *outfd == -1  : Use a new fd
> - *        *outfd != -1  : Use *outfd
> - * @errfd optional pointer to communcate error fd behavior. See outfd
> - * @flags possible combination of the following:
> - *        VIR_EXEC_NONE     : Default function behavior
> - *        VIR_EXEC_NONBLOCK : Set child process output fd's as non-blocking
> - *        VIR_EXEC_DAEMON   : Daemonize the child process
> - * @hook optional virExecHook function to call prior to exec
> - * @data data to pass to the hook function
> - * @pidfile path to use as pidfile for daemonized process (needs DAEMON flag)
> - */
> -int
> -virExecWithHook(const char *const*argv,
> -          const char *const*envp,
> -          const fd_set *keepfd,
> -          pid_t *retpid,
> -          int infd, int *outfd, int *errfd,
> -          int flags,
> -          virExecHook hook,
> -          void *data,
> -          char *pidfile)
> -{
> -    pid_t pid;
> -    int null, i, openmax;
> -    int pipeout[2] = {-1,-1};
> -    int pipeerr[2] = {-1,-1};
> -    int childout = -1;
> -    int childerr = -1;
> -    int tmpfd;
> -    const char *binary = NULL;
> -    int forkRet;
> -    char *argv_str = NULL;
> -    char *envp_str = NULL;
> -
> -    if ((argv_str = virArgvToString(argv)) == NULL) {
> -        virReportOOMError();
> -        return -1;
> -    }
> -
> -    if (envp) {
> -        if ((envp_str = virArgvToString(envp)) == NULL) {
> -            VIR_FREE(argv_str);
> -            virReportOOMError();
> -            return -1;
> -        }
> -        VIR_DEBUG("%s %s", envp_str, argv_str);
> -        VIR_FREE(envp_str);
> -    } else {
> -        VIR_DEBUG0(argv_str);
> -    }
> -    VIR_FREE(argv_str);
> -
> -    if (argv[0][0] != '/') {
> -        if (!(binary = virFindFileInPath(argv[0]))) {
> -            virReportSystemError(ENOENT,
> -                                 _("Cannot find '%s' in path"),
> -                                 argv[0]);
> -            return -1;
> -        }
> -    } else {
> -        binary = argv[0];
> -    }
> -
> -    if ((null = open("/dev/null", O_RDWR)) < 0) {
> -        virReportSystemError(errno,
> -                             _("cannot open %s"),
> -                             "/dev/null");
> -        goto cleanup;
> -    }
> -
> -    if (outfd != NULL) {
> -        if (*outfd == -1) {
> -            if (pipe(pipeout) < 0) {
> -                virReportSystemError(errno,
> -                                     "%s", _("cannot create pipe"));
> -                goto cleanup;
> -            }
> -
> -            if ((flags & VIR_EXEC_NONBLOCK) &&
> -                virSetNonBlock(pipeout[0]) == -1) {
> -                virReportSystemError(errno,
> -                                     "%s", _("Failed to set non-blocking file descriptor flag"));
> -                goto cleanup;
> -            }
> -
> -            if (virSetCloseExec(pipeout[0]) == -1) {
> -                virReportSystemError(errno,
> -                                     "%s", _("Failed to set close-on-exec file descriptor flag"));
> -                goto cleanup;
> -            }
> -
> -            childout = pipeout[1];
> -        } else {
> -            childout = *outfd;
> -        }
> -    } else {
> -        childout = null;
> -    }
> -
> -    if (errfd != NULL) {
> -        if (*errfd == -1) {
> -            if (pipe(pipeerr) < 0) {
> -                virReportSystemError(errno,
> -                                     "%s", _("Failed to create pipe"));
> -                goto cleanup;
> -            }
> -
> -            if ((flags & VIR_EXEC_NONBLOCK) &&
> -                virSetNonBlock(pipeerr[0]) == -1) {
> -                virReportSystemError(errno,
> -                                     "%s", _("Failed to set non-blocking file descriptor flag"));
> -                goto cleanup;
> -            }
> -
> -            if (virSetCloseExec(pipeerr[0]) == -1) {
> -                virReportSystemError(errno,
> -                                     "%s", _("Failed to set close-on-exec file descriptor flag"));
> -                goto cleanup;
> -            }
> -
> -            childerr = pipeerr[1];
> -        } else {
> -            childerr = *errfd;
> -        }
> -    } else {
> -        childerr = null;
> -    }
> -
> -    forkRet = virFork(&pid);
> -
> -    if (pid < 0) {
> -        goto cleanup;
> -    }
> -
> -    if (pid) { /* parent */
> -        VIR_FORCE_CLOSE(null);
> -        if (outfd && *outfd == -1) {
> -            VIR_FORCE_CLOSE(pipeout[1]);
> -            *outfd = pipeout[0];
> -        }
> -        if (errfd && *errfd == -1) {
> -            VIR_FORCE_CLOSE(pipeerr[1]);
> -            *errfd = pipeerr[0];
> -        }
> -
> -        if (forkRet < 0) {
> -            goto cleanup;
> -        }
> -
> -        *retpid = pid;
> -
> -        if (binary != argv[0])
> -            VIR_FREE(binary);
> -
> -        return 0;
> -    }
> -
> -    /* child */
> -
> -    if (forkRet < 0) {
> -        /* The fork was sucessful, but after that there was an error
> -         * in the child (which was already logged).
> -        */
> -        goto fork_error;
> -    }
> -
> -    openmax = sysconf (_SC_OPEN_MAX);
> -    for (i = 3; i < openmax; i++)
> -        if (i != infd &&
> -            i != null &&
> -            i != childout &&
> -            i != childerr &&
> -            (!keepfd || i >= FD_SETSIZE || !FD_ISSET(i, keepfd))) {
> -            tmpfd = i;
> -            VIR_FORCE_CLOSE(tmpfd);
> -        }
> -
> -    if (dup2(infd >= 0 ? infd : null, STDIN_FILENO) < 0) {
> -        virReportSystemError(errno,
> -                             "%s", _("failed to setup stdin file handle"));
> -        goto fork_error;
> -    }
> -    if (childout > 0 &&
> -        dup2(childout, STDOUT_FILENO) < 0) {
> -        virReportSystemError(errno,
> -                             "%s", _("failed to setup stdout file handle"));
> -        goto fork_error;
> -    }
> -    if (childerr > 0 &&
> -        dup2(childerr, STDERR_FILENO) < 0) {
> -        virReportSystemError(errno,
> -                             "%s", _("failed to setup stderr file handle"));
> -        goto fork_error;
> -    }
> -
> -    if (infd != STDIN_FILENO)
> -        VIR_FORCE_CLOSE(infd);
> -    VIR_FORCE_CLOSE(null);
> -    if (childout > STDERR_FILENO) {
> -        tmpfd = childout;   /* preserve childout value */
> -        VIR_FORCE_CLOSE(tmpfd);
> -    }
> -    if (childerr > STDERR_FILENO &&
> -        childerr != childout) {
> -        VIR_FORCE_CLOSE(childerr);
> -    }
> -
> -    /* Initialize full logging for a while */
> -    virLogSetFromEnv();
> -
> -    /* Daemonize as late as possible, so the parent process can detect
> -     * the above errors with wait* */
> -    if (flags & VIR_EXEC_DAEMON) {
> -        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) {
> -            if (pidfile && virFileWritePidPath(pidfile,pid)) {
> -                kill(pid, SIGTERM);
> -                usleep(500*1000);
> -                kill(pid, SIGTERM);
> -                virReportSystemError(errno,
> -                                     _("could not write pidfile %s for %d"),
> -                                     pidfile, pid);
> -                goto fork_error;
> -            }
> -            _exit(0);
> -        }
> -    }
> -
> -    if (hook) {
> -        /* virFork reset all signal handlers to the defaults.
> -         * This is good for the child process, but our hook
> -         * risks running something that generates SIGPIPE,
> -         * so we need to temporarily block that again
> -         */
> -        struct sigaction waxon, waxoff;
> -        waxoff.sa_handler = SIG_IGN;
> -        waxoff.sa_flags = 0;
> -        sigemptyset(&waxoff.sa_mask);
> -        memset(&waxon, 0, sizeof(waxon));
> -        if (sigaction(SIGPIPE, &waxoff, &waxon) < 0) {
> -            virReportSystemError(errno, "%s",
> -                                 _("Could not disable SIGPIPE"));
> -            goto fork_error;
> -        }
> -
> -        if ((hook)(data) != 0) {
> -            VIR_DEBUG0("Hook function failed.");
> -            goto fork_error;
> -        }
> -
> -        if (sigaction(SIGPIPE, &waxon, NULL) < 0) {
> -            virReportSystemError(errno, "%s",
> -                                 _("Could not re-enable SIGPIPE"));
> -            goto fork_error;
> -        }
> -    }
> -
> -    /* The steps above may need todo something privileged, so
> -     * we delay clearing capabilities until the last minute */
> -    if ((flags & VIR_EXEC_CLEAR_CAPS) &&
> -        virClearCapabilities() < 0)
> -        goto fork_error;
> -
> -    /* Close logging again to ensure no FDs leak to child */
> -    virLogReset();
> -
> -    if (envp)
> -        execve(binary, (char **) argv, (char**)envp);
> -    else
> -        execv(binary, (char **) argv);
> -
> -    virReportSystemError(errno,
> -                         _("cannot execute binary %s"),
> -                         argv[0]);
> -
> - fork_error:
> -    virDispatchError(NULL);
> -    _exit(EXIT_FAILURE);
> -
> - cleanup:
> -    /* This is cleanup of parent process only - child
> -       should never jump here on error */
> -
> -    if (binary != argv[0])
> -        VIR_FREE(binary);
> -
> -    /* NB we don't virUtilError() on any failures here
> -       because the code which jumped hre 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;
> -}
> -
> -/**
> - * @argv NULL terminated argv to run
> - * @status optional variable to return exit status in
> - *
> - * Run a command without using the shell.
> - *
> - * If status is NULL, then return 0 if the command run and
> - * exited with 0 status; Otherwise return -1
> - *
> - * If status is not-NULL, then return 0 if the command ran.
> - * The status variable is filled with the command exit status
> - * and should be checked by caller for success. Return -1
> - * only if the command could not be run.
> - */
> -int
> -virRun(const char *const*argv, int *status)
> -{
> -    virCommandPtr cmd = virCommandNew(argv[0]);
> -    const char * const *tmp;
> -    int ret;
> -
> -    tmp = argv;
> -    while (*(++tmp)) {
> -        virCommandAddArg(cmd, *tmp);
> -    }
> -
> -    ret = virCommandRun(cmd, status);
> -    virCommandFree(cmd);
> -    return ret;
> -}
> -
> -#else /* WIN32 */
> -
> -int virSetInherit(int fd ATTRIBUTE_UNUSED, bool inherit ATTRIBUTE_UNUSED)
> -{
> -    return -1;
> -}
> -
> -virRun(const char *const *argv ATTRIBUTE_UNUSED,
> -       int *status)
> -{
> -    if (status)
> -        *status = ENOTSUP;
> -    else
> -        virUtilError(VIR_ERR_INTERNAL_ERROR,
> -                     "%s", _("virRun is not implemented for WIN32"));
> -    return -1;
> -}
> -
> -int
> -virExecWithHook(const char *const*argv ATTRIBUTE_UNUSED,
> -                const char *const*envp ATTRIBUTE_UNUSED,
> -                const fd_set *keepfd ATTRIBUTE_UNUSED,
> -                pid_t *retpid ATTRIBUTE_UNUSED,
> -                int infd ATTRIBUTE_UNUSED,
> -                int *outfd ATTRIBUTE_UNUSED,
> -                int *errfd ATTRIBUTE_UNUSED,
> -                int flags ATTRIBUTE_UNUSED,
> -                virExecHook hook ATTRIBUTE_UNUSED,
> -                void *data ATTRIBUTE_UNUSED,
> -                char *pidfile ATTRIBUTE_UNUSED)
> -{
> -    /* XXX: Some day we can implement pieces of virCommand/virExec on
> -     * top of _spawn() or CreateProcess(), but we can't implement
> -     * everything, since mingw completely lacks fork(), so we cannot
> -     * run hook code in the child.  */
> -    virUtilError(VIR_ERR_INTERNAL_ERROR,
> -                 "%s", _("virExec is not implemented for WIN32"));
> -    return -1;
> -}
> -
> -int
> -virFork(pid_t *pid)
> -{
> -    *pid = -1;
> -    errno = ENOTSUP;
> -
> -    return -1;
> -}
> -
> -#endif /* WIN32 */
> -
>  int
>  virPipeReadUntilEOF(int outfd, int errfd,
>                      char **outbuf, char **errbuf) {
> diff --git a/src/util/util.h b/src/util/util.h
> index 3e95cae..68a8431 100644
> --- a/src/util/util.h
> +++ b/src/util/util.h
> @@ -42,37 +42,13 @@ ssize_t safewrite(int fd, const void *buf, size_t count)
>  int safezero(int fd, int flags, off_t offset, off_t len)
>      ATTRIBUTE_RETURN_CHECK;
>  
> -enum {
> -    VIR_EXEC_NONE   = 0,
> -    VIR_EXEC_NONBLOCK = (1 << 0),
> -    VIR_EXEC_DAEMON = (1 << 1),
> -    VIR_EXEC_CLEAR_CAPS = (1 << 2),
> -};
> -
>  int virSetBlocking(int fd, bool blocking) ATTRIBUTE_RETURN_CHECK;
>  int virSetNonBlock(int fd) ATTRIBUTE_RETURN_CHECK;
>  int virSetInherit(int fd, bool inherit) ATTRIBUTE_RETURN_CHECK;
>  int virSetCloseExec(int fd) ATTRIBUTE_RETURN_CHECK;
>  
> -/* This will execute in the context of the first child
> - * after fork() but before execve() */
> -typedef int (*virExecHook)(void *data);
> -
> -int virExecWithHook(const char *const*argv,
> -                    const char *const*envp,
> -                    const fd_set *keepfd,
> -                    int *retpid,
> -                    int infd,
> -                    int *outfd,
> -                    int *errfd,
> -                    int flags,
> -                    virExecHook hook,
> -                    void *data,
> -                    char *pidfile) ATTRIBUTE_RETURN_CHECK;
> -int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
>  int virPipeReadUntilEOF(int outfd, int errfd,
>                          char **outbuf, char **errbuf);
> -int virFork(pid_t *pid);
>  
>  int virSetUIDGID(uid_t uid, gid_t gid);
>  
> diff --git a/src/vmware/vmware_driver.c b/src/vmware/vmware_driver.c
> index a3a13c1..9a7fbf1 100644
> --- a/src/vmware/vmware_driver.c
> +++ b/src/vmware/vmware_driver.c
> @@ -29,6 +29,7 @@
>  #include "files.h"
>  #include "memory.h"
>  #include "uuid.h"
> +#include "command.h"
>  #include "vmx.h"
>  #include "vmware_conf.h"
>  #include "vmware_driver.h"

ACK


Daniel
-- 
|: http://berrange.com      -o-    http://www.flickr.com/photos/dberrange/ :|
|: http://libvirt.org              -o-             http://virt-manager.org :|
|: http://autobuild.org       -o-         http://search.cpan.org/~danberr/ :|
|: http://entangle-photo.org       -o-       http://live.gnome.org/gtk-vnc :|




More information about the libvir-list mailing list