libvirt/src/util/vircommand.h

217 lines
7.1 KiB
C
Raw Normal View History

/*
* vircommand.h: Child command execution
*
* Copyright (C) 2010-2014 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*
*/
#ifndef __VIR_COMMAND_H__
# define __VIR_COMMAND_H__
# include "internal.h"
# include "virbuffer.h"
typedef struct _virCommand virCommand;
typedef virCommand *virCommandPtr;
/* This will execute in the context of the first child
* after fork() but before execve(). As such, it is unsafe to
* call any function that is not async-signal-safe. */
typedef int (*virExecHook)(void *data);
virFork: simplify semantics The old semantics of virFork() violates the priciple of good usability: it requires the caller to check the pid argument after use, *even when virFork returned -1*, in order to properly abort a child process that failed setup done immediately after fork() - that is, the caller must call _exit() in the child. While uses in virfile.c did this correctly, uses in 'virsh lxc-enter-namespace' and 'virt-login-shell' would happily return from the calling function in both the child and the parent, leading to very confusing results. [Thankfully, I found the problem by inspection, and can't actually trigger the double return on error without an LD_PRELOAD library.] It is much better if the semantics of virFork are impossible to abuse. Looking at virFork(), the parent could only ever return -1 with a non-negative pid if it misused pthread_sigmask, but this never happens. Up until this patch series, the child could return -1 with non-negative pid if it fails to set up signals correctly, but we recently fixed that to make the child call _exit() at that point instead of forcing the caller to do it. Thus, the return value and contents of the pid argument are now redundant (a -1 return now happens only for failure to fork, a child 0 return only happens for a successful 0 pid, and a parent 0 return only happens for a successful non-zero pid), so we might as well return the pid directly rather than an integer of whether it succeeded or failed; this is also good from the interface design perspective as users are already familiar with fork() semantics. One last change in this patch: before returning the pid directly, I found cases where using virProcessWait unconditionally on a cleanup path of a virFork's -1 pid return would be nicer if there were a way to avoid it overwriting an earlier message. While such paths are a bit harder to come by with my change to a direct pid return, I decided to keep the virProcessWait change in this patch. * src/util/vircommand.h (virFork): Change signature. * src/util/vircommand.c (virFork): Guarantee that child will only return on success, to simplify callers. Return pid rather than status, now that the situations are always the same. (virExec): Adjust caller, also avoid open-coding process death. * src/util/virprocess.c (virProcessWait): Tweak semantics when pid is -1. (virProcessRunInMountNamespace): Adjust caller. * src/util/virfile.c (virFileAccessibleAs, virFileOpenForked) (virDirCreate): Likewise. * tools/virt-login-shell.c (main): Likewise. * tools/virsh-domain.c (cmdLxcEnterNamespace): Likewise. * tests/commandtest.c (test23): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2013-12-22 00:54:33 +00:00
pid_t virFork(void) ATTRIBUTE_RETURN_CHECK;
int virRun(const char *const*argv, int *status) ATTRIBUTE_RETURN_CHECK;
virCommandPtr virCommandNew(const char *binary) ATTRIBUTE_NONNULL(1);
virCommandPtr virCommandNewArgs(const char *const*args) ATTRIBUTE_NONNULL(1);
virCommandPtr virCommandNewArgList(const char *binary, ...)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_SENTINEL;
virCommandPtr virCommandNewVAList(const char *binary, va_list list)
ATTRIBUTE_NONNULL(1);
/* All error report from these setup APIs is
* delayed until the Run/RunAsync methods
*/
enum {
/* Close the FD in the parent */
VIR_COMMAND_PASS_FD_CLOSE_PARENT = (1 << 0),
};
void virCommandPassFD(virCommandPtr cmd,
int fd,
unsigned int flags);
void virCommandPassListenFDs(virCommandPtr cmd);
int virCommandPassFDGetFDIndex(virCommandPtr cmd,
int fd);
void virCommandSetPidFile(virCommandPtr cmd,
const char *pidfile) ATTRIBUTE_NONNULL(2);
void virCommandSetGID(virCommandPtr cmd, gid_t gid);
void virCommandSetUID(virCommandPtr cmd, uid_t uid);
void virCommandSetMaxMemLock(virCommandPtr cmd, unsigned long long bytes);
void virCommandSetMaxProcesses(virCommandPtr cmd, unsigned int procs);
void virCommandSetMaxFiles(virCommandPtr cmd, unsigned int files);
void virCommandSetUmask(virCommandPtr cmd, int umask);
void virCommandClearCaps(virCommandPtr cmd);
void virCommandAllowCap(virCommandPtr cmd,
int capability);
void virCommandSetSELinuxLabel(virCommandPtr cmd,
const char *label);
void virCommandSetAppArmorProfile(virCommandPtr cmd,
const char *profile);
void virCommandDaemonize(virCommandPtr cmd);
void virCommandNonblockingFDs(virCommandPtr cmd);
util: make it easier to grab only regular command exit Auditing all callers of virCommandRun and virCommandWait that passed a non-NULL pointer for exit status turned up some interesting observations. Many callers were merely passing a pointer to avoid the overall command dying, but without caring what the exit status was - but these callers would be better off treating a child death by signal as an abnormal exit. Other callers were actually acting on the status, but not all of them remembered to filter by WIFEXITED and convert with WEXITSTATUS; depending on the platform, this can result in a status being reported as 256 times too big. And among those that correctly parse the output, it gets rather verbose. Finally, there were the callers that explicitly checked that the status was 0, and gave their own message, but with fewer details than what virCommand gives for free. So the best idea is to move the complexity out of callers and into virCommand - by default, we return the actual exit status already cleaned through WEXITSTATUS and treat signals as a failed command; but the few callers that care can ask for raw status and act on it themselves. * src/util/vircommand.h (virCommandRawStatus): New prototype. * src/libvirt_private.syms (util/command.h): Export it. * docs/internals/command.html.in: Document it. * src/util/vircommand.c (virCommandRawStatus): New function. (virCommandWait): Adjust semantics. * tests/commandtest.c (test1): Test it. * daemon/remote.c (remoteDispatchAuthPolkit): Adjust callers. * src/access/viraccessdriverpolkit.c (virAccessDriverPolkitCheck): Likewise. * src/fdstream.c (virFDStreamCloseInt): Likewise. * src/lxc/lxc_process.c (virLXCProcessStart): Likewise. * src/qemu/qemu_command.c (qemuCreateInBridgePortWithHelper): Likewise. * src/xen/xen_driver.c (xenUnifiedXendProbe): Simplify. * tests/reconnect.c (mymain): Likewise. * tests/statstest.c (mymain): Likewise. * src/bhyve/bhyve_process.c (virBhyveProcessStart) (virBhyveProcessStop): Don't overwrite virCommand error. * src/libvirt.c (virConnectAuthGainPolkit): Likewise. * src/openvz/openvz_driver.c (openvzDomainGetBarrierLimit) (openvzDomainSetBarrierLimit): Likewise. * src/util/virebtables.c (virEbTablesOnceInit): Likewise. * src/util/viriptables.c (virIpTablesOnceInit): Likewise. * src/util/virnetdevveth.c (virNetDevVethCreate): Fix debug message. * src/qemu/qemu_capabilities.c (virQEMUCapsInitQMP): Add comment. * src/storage/storage_backend_iscsi.c (virStorageBackendISCSINodeUpdate): Likewise. Signed-off-by: Eric Blake <eblake@redhat.com>
2014-02-20 00:32:19 +00:00
void virCommandRawStatus(virCommandPtr cmd);
void virCommandAddEnvFormat(virCommandPtr cmd, const char *format, ...)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
void virCommandAddEnvPair(virCommandPtr cmd,
const char *name,
const char *value) ATTRIBUTE_NONNULL(2);
void virCommandAddEnvString(virCommandPtr cmd,
const char *str) ATTRIBUTE_NONNULL(2);
void virCommandAddEnvBuffer(virCommandPtr cmd,
virBufferPtr buf);
void virCommandAddEnvPassBlockSUID(virCommandPtr cmd,
const char *name,
const char *defvalue) ATTRIBUTE_NONNULL(2);
void virCommandAddEnvPassAllowSUID(virCommandPtr cmd,
const char *name) ATTRIBUTE_NONNULL(2);
void virCommandAddEnvPassCommon(virCommandPtr cmd);
void virCommandAddArg(virCommandPtr cmd,
const char *val) ATTRIBUTE_NONNULL(2);
void virCommandAddArgBuffer(virCommandPtr cmd,
virBufferPtr buf);
void virCommandAddArgFormat(virCommandPtr cmd,
const char *format, ...)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_FMT_PRINTF(2, 3);
void virCommandAddArgPair(virCommandPtr cmd,
const char *name,
const char *val)
ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3);
void virCommandAddArgSet(virCommandPtr cmd,
const char *const*vals) ATTRIBUTE_NONNULL(2);
void virCommandAddArgList(virCommandPtr cmd,
... /* const char *arg, ..., NULL */)
ATTRIBUTE_SENTINEL;
void virCommandSetWorkingDirectory(virCommandPtr cmd,
const char *pwd) ATTRIBUTE_NONNULL(2);
void virCommandSetInputBuffer(virCommandPtr cmd,
const char *inbuf) ATTRIBUTE_NONNULL(2);
void virCommandSetOutputBuffer(virCommandPtr cmd,
char **outbuf) ATTRIBUTE_NONNULL(2);
void virCommandSetErrorBuffer(virCommandPtr cmd,
char **errbuf) ATTRIBUTE_NONNULL(2);
void virCommandSetInputFD(virCommandPtr cmd,
int infd);
void virCommandSetOutputFD(virCommandPtr cmd,
int *outfd) ATTRIBUTE_NONNULL(2);
void virCommandSetErrorFD(virCommandPtr cmd,
int *errfd) ATTRIBUTE_NONNULL(2);
void virCommandSetPreExecHook(virCommandPtr cmd,
virExecHook hook,
void *opaque) ATTRIBUTE_NONNULL(2);
void virCommandWriteArgLog(virCommandPtr cmd,
int logfd);
char *virCommandToString(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
int virCommandExec(virCommandPtr cmd) ATTRIBUTE_RETURN_CHECK;
int virCommandRun(virCommandPtr cmd,
int *exitstatus) ATTRIBUTE_RETURN_CHECK;
int virCommandRunAsync(virCommandPtr cmd,
pid_t *pid) ATTRIBUTE_RETURN_CHECK;
int virCommandWait(virCommandPtr cmd,
int *exitstatus) ATTRIBUTE_RETURN_CHECK;
void virCommandRequireHandshake(virCommandPtr cmd);
int virCommandHandshakeWait(virCommandPtr cmd)
ATTRIBUTE_RETURN_CHECK;
int virCommandHandshakeNotify(virCommandPtr cmd)
ATTRIBUTE_RETURN_CHECK;
void virCommandAbort(virCommandPtr cmd);
void virCommandFree(virCommandPtr cmd);
void virCommandDoAsyncIO(virCommandPtr cmd);
typedef int (*virCommandRunRegexFunc)(char **const groups,
void *data);
typedef int (*virCommandRunNulFunc)(size_t n_tokens,
char **const groups,
void *data);
int virCommandRunRegex(virCommandPtr cmd,
int nregex,
const char **regex,
int *nvars,
virCommandRunRegexFunc func,
void *data,
const char *cmd_to_ignore);
int virCommandRunNul(virCommandPtr cmd,
size_t n_columns,
virCommandRunNulFunc func,
void *data);
#endif /* __VIR_COMMAND_H__ */