/* * 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 * . * */ #include #ifndef WIN32 # include #endif #include #include #include #ifndef WIN32 # include #endif #include #include #if WITH_CAPNG # include #endif #if defined(WITH_SECDRIVER_SELINUX) # include #endif #if defined(WITH_SECDRIVER_APPARMOR) # include #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; struct _virCommandFD { int fd; unsigned int flags; }; typedef struct _virCommandSendBuffer virCommandSendBuffer; struct _virCommandSendBuffer { int fd; unsigned char *buffer; size_t buflen; off_t offset; }; struct _virCommand { int has_error; /* 0 on success, -1 on error */ char **args; size_t nargs; size_t maxargs; char **env; size_t nenv; size_t maxenv; char *pwd; size_t npassfd; virCommandFD *passfd; unsigned int flags; char *inbuf; char **outbuf; char **errbuf; int infd; int inpipe; int outfd; int errfd; int *outfdptr; int *errfdptr; virThread *asyncioThread; bool handshake; int handshakeWait[2]; int handshakeNotify[2]; virExecHook hook; void *opaque; pid_t pid; char *pidfile; bool reap; bool rawStatus; bool setMaxMemLock; unsigned long long maxMemLock; bool setMaxProcesses; unsigned int maxProcesses; bool setMaxFiles; 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; virCommandSendBuffer *sendBuffers; size_t numSendBuffers; }; /* See virCommandSetDryRun for description for this variable */ static virBuffer *dryRunBuffer; static bool dryRunBufferArgLinebreaks; static bool dryRunBufferCommandStripPath; static virCommandDryRunCallback dryRunCallback; static void *dryRunOpaque; #ifndef WIN32 static int dryRunStatus; #endif /* !WIN32 */ static bool virCommandHasError(virCommand *cmd) { return !cmd || cmd->has_error != 0; } static int virCommandRaiseError(virCommand *cmd) { if (!cmd || cmd->has_error != 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("invalid use of command API")); return -1; } return 0; } /* * 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(virCommand *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. */ static void virCommandFDSet(virCommand *cmd, int fd, unsigned int flags) { if (virCommandFDIsSet(cmd, fd)) return; VIR_EXPAND_N(cmd->passfd, cmd->npassfd, 1); cmd->passfd[cmd->npassfd - 1].fd = fd; cmd->passfd[cmd->npassfd - 1].flags = flags; } #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(virCommand *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(virCommand *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(virCommand *cmd G_GNUC_UNUSED, virBitmap *fds) { g_autoptr(DIR) dp = NULL; struct dirent *entry; const char *dirName = "/proc/self/fd"; int rc; 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); return -1; } ignore_value(virBitmapSetBit(fds, fd)); } if (rc < 0) return -1; return 0; } # else /* !__linux__ */ static int virCommandMassCloseGetFDsGeneric(virCommand *cmd G_GNUC_UNUSED, virBitmap *fds) { virBitmapSetAll(fds); return 0; } # endif /* !__linux__ */ # ifdef __FreeBSD__ static int virCommandMassClose(virCommand *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(virCommand *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; } fds = virBitmapNew(openmax); # 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 virCommand * containing all information about the program to * exec. */ static int virExec(virCommand *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 (!g_path_is_absolute(cmd->args[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); } } pid = getpid(); if (cmd->pidfile) { int pidfilefd = -1; char c; pidfilefd = virPidFileAcquirePath(cmd->pidfile, false, pid); 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->setMaxMemLock && virProcessSetMaxMemLock(pid, cmd->maxMemLock) < 0) goto fork_error; if (cmd->setMaxProcesses && virProcessSetMaxProcesses(pid, cmd->maxProcesses) < 0) goto fork_error; if (cmd->setMaxFiles && virProcessSetMaxFiles(pid, cmd->maxFiles) < 0) goto fork_error; if (cmd->setMaxCore && virProcessSetMaxCoreSize(pid, cmd->maxCore) < 0) goto fork_error; 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). */ virCommand * 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. */ virCommand * virCommandNewArgs(const char *const*args) { virCommand *cmd; cmd = g_new0(virCommand, 1); 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. */ virCommand * virCommandNewArgList(const char *binary, ...) { virCommand *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. */ virCommand * virCommandNewVAList(const char *binary, va_list list) { virCommand *cmd = virCommandNew(binary); const char *arg; if (virCommandHasError(cmd)) 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) /** * virCommandPassFDIndex: * @cmd: the command to modify * @fd: fd to reassign to the child * @flags: extra flags; binary-OR of virCommandPassFDFlags * @idx: pointer to fill with the index of the FD in the transfer set * * 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 virCommandPassFDIndex(virCommand *cmd, int fd, unsigned int flags, size_t *idx) { 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; } virCommandFDSet(cmd, fd, flags); if (idx) *idx = cmd->npassfd - 1; } /** * 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(virCommand *cmd, int fd, unsigned int flags) { virCommandPassFDIndex(cmd, fd, flags, NULL); } /* * 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(virCommand *cmd, int fd) { size_t i = 0; if (virCommandHasError(cmd)) 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(virCommand *cmd, const char *pidfile) { if (virCommandHasError(cmd)) return; VIR_FREE(cmd->pidfile); cmd->pidfile = g_strdup(pidfile); } gid_t virCommandGetGID(virCommand *cmd) { return cmd->gid; } uid_t virCommandGetUID(virCommand *cmd) { return cmd->uid; } void virCommandSetGID(virCommand *cmd, gid_t gid) { if (virCommandHasError(cmd)) return; cmd->gid = gid; } void virCommandSetUID(virCommand *cmd, uid_t uid) { if (virCommandHasError(cmd)) return; cmd->uid = uid; } void virCommandSetMaxMemLock(virCommand *cmd, unsigned long long bytes) { if (virCommandHasError(cmd)) return; cmd->maxMemLock = bytes; cmd->setMaxMemLock = true; } void virCommandSetMaxProcesses(virCommand *cmd, unsigned int procs) { if (virCommandHasError(cmd)) return; cmd->maxProcesses = procs; cmd->setMaxProcesses = true; } void virCommandSetMaxFiles(virCommand *cmd, unsigned int files) { if (virCommandHasError(cmd)) return; cmd->maxFiles = files; cmd->setMaxFiles = true; } void virCommandSetMaxCoreSize(virCommand *cmd, unsigned long long bytes) { if (virCommandHasError(cmd)) return; cmd->maxCore = bytes; cmd->setMaxCore = true; } void virCommandSetUmask(virCommand *cmd, int mask) { if (virCommandHasError(cmd)) return; cmd->mask = mask; } /** * virCommandClearCaps: * @cmd: the command to modify * * Remove all capabilities from the child, after any hooks have been run. */ void virCommandClearCaps(virCommand *cmd) { if (virCommandHasError(cmd)) return; cmd->flags |= VIR_EXEC_CLEAR_CAPS; } /** * virCommandAllowCap: * @cmd: the command to modify * @capability: what to allow * * Allow specific capabilities */ void virCommandAllowCap(virCommand *cmd, int capability) { if (virCommandHasError(cmd)) 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(virCommand *cmd, const char *label G_GNUC_UNUSED) { if (virCommandHasError(cmd)) 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(virCommand *cmd, const char *profile G_GNUC_UNUSED) { if (virCommandHasError(cmd)) 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(virCommand *cmd) { if (virCommandHasError(cmd)) 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(virCommand *cmd) { if (virCommandHasError(cmd)) 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(virCommand *cmd) { if (virCommandHasError(cmd)) 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(virCommand *cmd, char *envstr) { g_autofree char *env = envstr; 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] = g_steal_pointer(&env); return; } } /* Arg plus trailing NULL. */ VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 1 + 1); cmd->env[cmd->nenv++] = g_steal_pointer(&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(virCommand *cmd, const char *format, ...) { char *env; va_list list; if (virCommandHasError(cmd)) 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(virCommand *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(virCommand *cmd, const char *str) { char *env; if (virCommandHasError(cmd)) return; env = g_strdup(str); virCommandAddEnv(cmd, env); } /** * 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(virCommand *cmd, const char *name) { const char *value; if (virCommandHasError(cmd)) 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(virCommand *cmd) { if (virCommandHasError(cmd)) return; VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 9); 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(virCommand *cmd, const char *baseDir) { if (virCommandHasError(cmd)) return; VIR_RESIZE_N(cmd->env, cmd->maxenv, cmd->nenv, 3); 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(virCommand *cmd, const char *val) { if (virCommandHasError(cmd)) return; if (val == NULL) { cmd->has_error = EINVAL; return; } /* Arg plus trailing NULL. */ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1); cmd->args[cmd->nargs++] = g_strdup(val); } /** * virCommandAddArgBuffer: * @cmd: the command to modify * @buf: buffer that contains argument string, which will be reset on return * * Convert a buffer into a command line argument to the child. * Correctly transfers memory errors or contents from buf to cmd. */ void virCommandAddArgBuffer(virCommand *cmd, virBuffer *buf) { g_autofree char *str = virBufferContentAndReset(buf); if (virCommandHasError(cmd)) return; if (!str) str = g_strdup(""); /* Arg plus trailing NULL. */ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1); cmd->args[cmd->nargs] = g_steal_pointer(&str); 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(virCommand *cmd, const char *format, ...) { char *arg; va_list list; if (virCommandHasError(cmd)) return; va_start(list, format); arg = g_strdup_vprintf(format, list); va_end(list); /* Arg plus trailing NULL. */ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, 1 + 1); 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(virCommand *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(virCommand *cmd, const char *const*vals) { int narg = 0; if (virCommandHasError(cmd)) return; if (vals[0] == NULL) { cmd->has_error = EINVAL; return; } while (vals[narg] != NULL) narg++; /* narg plus trailing NULL. */ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1); 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(virCommand *cmd, ...) { va_list list; int narg = 0; if (virCommandHasError(cmd)) return; va_start(list, cmd); while (va_arg(list, const char *) != NULL) narg++; va_end(list); /* narg plus trailing NULL. */ VIR_RESIZE_N(cmd->args, cmd->maxargs, cmd->nargs, narg + 1); 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(virCommand *cmd, const char *pwd) { if (virCommandHasError(cmd)) return; if (cmd->pwd) { cmd->has_error = -1; VIR_DEBUG("cannot set directory twice"); } else { cmd->pwd = g_strdup(pwd); } } static int virCommandGetNumSendBuffers(virCommand *cmd) { return cmd->numSendBuffers; } static void virCommandFreeSendBuffers(virCommand *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 * @buffer: buffer to pass to the filedescriptror * @buflen: length of @buffer * * Registers @buffer as an input buffer for @cmd which will be accessible via * the returned file descriptor. The returned file descriptor is already * registered to be passed to @cmd, so callers must use it only to format the * appropriate argument of @cmd. * * @buffer is always stolen regardless of the return value. This function * doesn't raise a libvirt error, but rather propagates the error via virCommand. * Thus callers don't need to take a special action if -1 is returned. */ int virCommandSetSendBuffer(virCommand *cmd, unsigned char *buffer, size_t buflen) { g_autofree unsigned char *localbuf = g_steal_pointer(&buffer); int pipefd[2] = { -1, -1 }; size_t i; if (virCommandHasError(cmd)) return -1; if (virPipeQuiet(pipefd) < 0) { cmd->has_error = errno; return -1; } if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) < 0) { cmd->has_error = errno; VIR_FORCE_CLOSE(pipefd[0]); VIR_FORCE_CLOSE(pipefd[1]); return -1; } i = virCommandGetNumSendBuffers(cmd); VIR_REALLOC_N(cmd->sendBuffers, i + 1); cmd->sendBuffers[i].fd = pipefd[1]; cmd->sendBuffers[i].buffer = g_steal_pointer(&localbuf); cmd->sendBuffers[i].buflen = buflen; cmd->sendBuffers[i].offset = 0; cmd->numSendBuffers++; virCommandPassFD(cmd, pipefd[0], VIR_COMMAND_PASS_FD_CLOSE_PARENT); return pipefd[0]; } static int virCommandSendBuffersFillPollfd(virCommand *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(virCommand *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(virCommand *cmd, const char *inbuf) { if (virCommandHasError(cmd)) 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(virCommand *cmd, char **outbuf) { *outbuf = NULL; if (virCommandHasError(cmd)) 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(virCommand *cmd, char **errbuf) { *errbuf = NULL; if (virCommandHasError(cmd)) 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(virCommand *cmd, int infd) { if (virCommandHasError(cmd)) 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(virCommand *cmd, int *outfd) { if (virCommandHasError(cmd)) 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(virCommand *cmd, int *errfd) { if (virCommandHasError(cmd)) 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(virCommand *cmd, virExecHook hook, void *opaque) { if (virCommandHasError(cmd)) 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(virCommand *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 (virCommandHasError(cmd)) 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)); } } /** * virCommandToStringBuf: * @cmd: the command to convert * @buf: buffer to format @cmd into * @linebreaks: true to break line after each env var or option * @stripCommandPath: strip the path leading to the binary of @cmd * * 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), -1 will be returned. */ int virCommandToStringBuf(virCommand *cmd, virBuffer *buf, bool linebreaks, bool stripCommandPath) { size_t i; const char *command = cmd->args[0]; g_autofree char *basename = NULL; bool had_option = false; /* Cannot assume virCommandRun will be called; so report the error * now. If virCommandRun is called, it will report the same error. */ if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); return -1; } 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 -1; } eq++; virBufferAdd(buf, cmd->env[i], eq - cmd->env[i]); virBufferEscapeShell(buf, eq); virBufferAddChar(buf, ' '); if (linebreaks) virBufferAddLit(buf, "\\\n"); } if (stripCommandPath) command = basename = g_path_get_basename(command); virBufferEscapeShell(buf, command); for (i = 1; i < cmd->nargs; i++) { virBufferAddChar(buf, ' '); if (linebreaks) { /* we don't want a linebreak only if * - the previous argument is an option (starts with '-') * - there was already an option and another option follows */ bool linebreak = true; if (cmd->args[i][0] != '-') { if (had_option) { size_t j; /* we know that arg[i - 1] is valid and arg[i] is not an option */ for (j = i - 1; j < cmd->nargs; j++) { if (cmd->args[j][0] == '-') { linebreak = false; break; } } } } else { had_option = true; } if (linebreak) virBufferAddLit(buf, "\\\n"); } virBufferEscapeShell(buf, cmd->args[i]); } return 0; } char * virCommandToStringFull(virCommand *cmd, bool linebreaks, bool stripCommandPath) { g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER; if (virCommandToStringBuf(cmd, &buf, linebreaks, stripCommandPath)) return NULL; return virBufferContentAndReset(&buf); } char * virCommandToString(virCommand *cmd, bool linebreaks) { return virCommandToStringFull(cmd, linebreaks, false); } int virCommandGetArgList(virCommand *cmd, char ***args, size_t *nargs) { size_t i; if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); 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(virCommand *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); *cmd->outbuf = g_new0(char, 1); } if (cmd->errbuf) { errfd = cmd->errfd; VIR_FREE(*cmd->errbuf); *cmd->errbuf = g_new0(char, 1); } if (ret == -1) goto cleanup; ret = -1; fds = g_new0(struct pollfd, 3 + virCommandGetNumSendBuffers(cmd)); 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; } 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 { VIR_REALLOC_N(*buf, *len + done + 1); 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(virCommand *cmd, gid_t *groups, int ngroups) { if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); 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(virCommand *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 (virCommandHasError(cmd)) { virCommandRaiseError(cmd); 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) { virCommand *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(virCommand *cmd, pid_t *pid) { int ret = -1; g_autofree char *str = NULL; size_t i; bool synchronous = false; int infd[2] = {-1, -1}; if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); 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; } if (dryRunBuffer || dryRunCallback) { g_autofree char *cmdstr = NULL; dryRunStatus = 0; if (!(cmdstr = virCommandToStringFull(cmd, dryRunBufferArgLinebreaks, dryRunBufferCommandStripPath))) goto cleanup; if (dryRunBuffer) { VIR_DEBUG("Dry run requested, appending stringified " "command to dryRunBuffer=%p", dryRunBuffer); virBufferAdd(dryRunBuffer, cmdstr, -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; } str = virCommandToString(cmd, false); 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; cmd->asyncioThread = g_new0(virThread, 1); if (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(virCommand *cmd, int *exitstatus) { int ret; int status = 0; if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); 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(virCommand *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(virCommand *cmd) { if (virCommandHasError(cmd)) 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(virCommand *cmd) { char c; int rv; if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); return -1; } if (!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; msg = g_new0(char, 1024); /* 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(virCommand *cmd) { char c = '1'; if (virCommandHasError(cmd)) { virCommandRaiseError(cmd); return -1; } if (!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(virCommand *cmd, unsigned char *buffer G_GNUC_UNUSED, size_t buflen G_GNUC_UNUSED) { if (virCommandHasError(cmd)) return -1; cmd->has_error = ENOTSUP; return -1; } int virCommandExec(virCommand *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(virCommand *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(virCommand *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(virCommand *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(virCommand *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(virCommand *cmd) { if (virCommandHasError(cmd)) return; cmd->has_error = ENOSYS; } int virCommandHandshakeWait(virCommand *cmd G_GNUC_UNUSED) { virReportSystemError(ENOSYS, "%s", _("Executing new processes is not supported on Win32 platform")); return -1; } int virCommandHandshakeNotify(virCommand *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(virCommand *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; g_free(cmd->passfd); if (cmd->asyncioThread) { virThreadJoin(cmd->asyncioThread); g_free(cmd->asyncioThread); } g_free(cmd->inbuf); VIR_FORCE_CLOSE(cmd->outfd); VIR_FORCE_CLOSE(cmd->errfd); for (i = 0; i < cmd->nargs; i++) g_free(cmd->args[i]); g_free(cmd->args); for (i = 0; i < cmd->nenv; i++) g_free(cmd->env[i]); g_free(cmd->env); g_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]); } g_free(cmd->pidfile); if (cmd->reap) virCommandAbort(cmd); #if defined(WITH_SECDRIVER_SELINUX) g_free(cmd->seLinuxLabel); #endif #if defined(WITH_SECDRIVER_APPARMOR) g_free(cmd->appArmorProfile); #endif virCommandFreeSendBuffers(cmd); g_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(virCommand *cmd) { if (virCommandHasError(cmd)) return; cmd->flags |= VIR_EXEC_ASYNC_IO | VIR_EXEC_NONBLOCK; } struct _virCommandDryRunToken { int dummy; }; /** * virCommandDryRunTokenNew: * * Returns a token which is used with virCommandSetDryRun. Freeing the token * with the appropriate automatic cleanup function ensures that the dry run * environment is reset. */ virCommandDryRunToken * virCommandDryRunTokenNew(void) { return g_new0(virCommandDryRunToken, 1); } /** * virCommandDryRunTokenFree: * * Helper to free a virCommandDryRunToken. Do not use this function directly, * always declare virCommandDryRunToken as a g_autoptr. */ void virCommandDryRunTokenFree(virCommandDryRunToken *tok) { dryRunBuffer = NULL; dryRunBufferArgLinebreaks = false; dryRunBufferCommandStripPath = false; dryRunCallback = NULL; dryRunOpaque = NULL; g_free(tok); } /** * virCommandSetDryRun: * @tok: a virCommandDryRunToken obtained from virCommandDryRunTokenNew * @buf: buffer to store stringified commands * @bufArgLinebreaks: add linebreaks after command and every argument or argument pair * @bufCommandStripPath: strip leading paths of command * @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); * * virCommand *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(virCommandDryRunToken *tok, virBuffer *buf, bool bufArgLinebreaks, bool bufCommandStripPath, virCommandDryRunCallback cb, void *opaque) { if (!tok) abort(); dryRunBuffer = buf; dryRunBufferArgLinebreaks = bufArgLinebreaks; dryRunBufferCommandStripPath = bufCommandStripPath; 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(virCommand *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; g_auto(GStrv) lines = NULL; int ret = -1; /* Compile all regular expressions */ reg = g_new0(GRegex *, nregex); 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 */ groups = g_new0(char *, totgroups); virCommandSetOutputBuffer(cmd, &outbuf); if (virCommandRun(cmd, exitstatus) < 0) goto cleanup; if (!outbuf) { /* no output */ ret = 0; goto cleanup; } if (!(lines = g_strsplit(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(virCommand *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; v = g_new0(char *, n_columns); 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(virCommand *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(virCommand *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 */