lib: Introduce 'virDomainQemuMonitorCommandWithFiles'

This API has the same semantics as 'virDomainQemuMonitorCommand' but
accepts file descriptors which are then forwarded to qemu.

Signed-off-by: Peter Krempa <pkrempa@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
Peter Krempa 2022-01-27 16:48:00 +01:00
parent 7cfbfe66fc
commit f87fa77ca9
8 changed files with 259 additions and 1 deletions

View File

@ -37,6 +37,14 @@ typedef enum {
int virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd,
char **result, unsigned int flags);
int virDomainQemuMonitorCommandWithFiles(virDomainPtr domain,
const char *cmd,
unsigned int ninfiles,
int *infiles,
unsigned int *noutfiles,
int **outfiles,
char **result,
unsigned int flags);
virDomainPtr virDomainQemuAttach(virConnectPtr domain,
unsigned int pid_value,

View File

@ -874,6 +874,15 @@ typedef int
const char *cmd,
char **result,
unsigned int flags);
typedef int
(*virDrvDomainQemuMonitorCommandWithFiles)(virDomainPtr domain,
const char *cmd,
unsigned int ninfiles,
int *infiles,
unsigned int *noutfiles,
int **outfiles,
char **result,
unsigned int flags);
typedef char *
(*virDrvDomainQemuAgentCommand)(virDomainPtr domain,
@ -1597,6 +1606,7 @@ struct _virHypervisorDriver {
virDrvDomainRevertToSnapshot domainRevertToSnapshot;
virDrvDomainSnapshotDelete domainSnapshotDelete;
virDrvDomainQemuMonitorCommand domainQemuMonitorCommand;
virDrvDomainQemuMonitorCommandWithFiles domainQemuMonitorCommandWithFiles;
virDrvDomainQemuAttach domainQemuAttach;
virDrvDomainQemuAgentCommand domainQemuAgentCommand;
virDrvConnectDomainQemuMonitorEventRegister connectDomainQemuMonitorEventRegister;

View File

@ -96,6 +96,95 @@ virDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd,
}
/**
* virDomainQemuMonitorCommandWithFiles:
* @domain: a domain object
* @cmd: the qemu monitor command string
* @ninfiles: number of filedescriptors passed in @infiles
* @infiles: filedescriptors to be passed to qemu with the command
* @noutfiles: if non-NULL filled with number of returned file descriptors
* @outfiles: if non-NULL filled with an array of returned file descriptors
* @result: a string returned by @cmd
* @flags: bitwise-or of supported virDomainQemuMonitorCommandFlags
*
* This API is QEMU specific, so it will only work with hypervisor
* connections to the QEMU driver with local connections using the unix socket.
*
* Send an arbitrary monitor command @cmd with file descriptors @infiles to
* @domain through the qemu monitor and optionally return file descriptors via
* @outfiles. There are several requirements to safely and successfully use
* this API:
*
* - A @cmd that queries state without making any modifications is safe
* - A @cmd that alters state that is also tracked by libvirt is unsafe,
* and may cause libvirtd to crash
* - A @cmd that alters state not tracked by the current version of
* libvirt is possible as a means to test new qemu features before
* they have support in libvirt, but no guarantees are made to safety
*
* If VIR_DOMAIN_QEMU_MONITOR_COMMAND_HMP is set, the command is
* considered to be a human monitor command and libvirt will automatically
* convert it into QMP if needed. In that case the @result will also
* be converted back from QMP.
*
* If successful, @result will be filled with the string output of the
* @cmd, and the caller must free this string.
*
* Returns 0 in case of success, -1 in case of failure
*/
int
virDomainQemuMonitorCommandWithFiles(virDomainPtr domain,
const char *cmd,
unsigned int ninfiles,
int *infiles,
unsigned int *noutfiles,
int **outfiles,
char **result,
unsigned int flags)
{
virConnectPtr conn;
VIR_DOMAIN_DEBUG(domain,
"cmd=%s, ninfiles=%u, infiles=%p, noutfiles=%p, outfiles=%p, result=%p, flags=0x%x",
cmd, ninfiles, infiles, noutfiles, outfiles, result, flags);
virResetLastError();
virCheckDomainReturn(domain, -1);
conn = domain->conn;
if (ninfiles > 0 || outfiles) {
int rc;
if ((rc = VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn,
VIR_DRV_FEATURE_FD_PASSING) <= 0)) {
if (rc == 0)
virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
_("fd passing is not supported by this connection"));
goto error;
}
}
virCheckNonNullArgGoto(result, error);
virCheckReadOnlyGoto(conn->flags, error);
if (conn->driver->domainQemuMonitorCommandWithFiles) {
int ret;
ret = conn->driver->domainQemuMonitorCommandWithFiles(domain, cmd,
ninfiles, infiles,
noutfiles, outfiles,
result, flags);
if (ret < 0)
goto error;
return ret;
}
virReportUnsupportedError();
error:
virDispatchError(conn);
return -1;
}
/**
* virDomainQemuAttach:
* @conn: pointer to a hypervisor connection

View File

@ -30,3 +30,8 @@ LIBVIRT_QEMU_1.2.3 {
virConnectDomainQemuMonitorEventDeregister;
virConnectDomainQemuMonitorEventRegister;
} LIBVIRT_QEMU_0.10.0;
LIBVIRT_QEMU_8.2.0 {
global:
virDomainQemuMonitorCommandWithFiles;
} LIBVIRT_QEMU_1.2.3;

View File

@ -47,6 +47,14 @@ struct qemu_domain_monitor_event_msg {
u_int micros;
remote_string details;
};
struct qemu_domain_monitor_command_with_files_args {
remote_nonnull_domain dom;
remote_nonnull_string cmd;
u_int flags;
};
struct qemu_domain_monitor_command_with_files_ret {
remote_nonnull_string result;
};
enum qemu_procedure {
QEMU_PROC_DOMAIN_MONITOR_COMMAND = 1,
QEMU_PROC_DOMAIN_ATTACH = 2,
@ -54,4 +62,5 @@ enum qemu_procedure {
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_REGISTER = 4,
QEMU_PROC_CONNECT_DOMAIN_MONITOR_EVENT_DEREGISTER = 5,
QEMU_PROC_DOMAIN_MONITOR_EVENT = 6,
QEMU_PROC_DOMAIN_MONITOR_COMMAND_WITH_FILES = 7,
};

View File

@ -79,6 +79,17 @@ struct qemu_domain_monitor_event_msg {
remote_string details;
};
struct qemu_domain_monitor_command_with_files_args {
remote_nonnull_domain dom;
remote_nonnull_string cmd;
unsigned int flags;
};
struct qemu_domain_monitor_command_with_files_ret {
remote_nonnull_string result;
};
/* Define the program number, protocol version and procedure numbers here. */
const QEMU_PROGRAM = 0x20008087;
const QEMU_PROTOCOL_VERSION = 1;
@ -151,5 +162,12 @@ enum qemu_procedure {
* @generate: both
* @acl: none
*/
QEMU_PROC_DOMAIN_MONITOR_EVENT = 6
QEMU_PROC_DOMAIN_MONITOR_EVENT = 6,
/**
* @generate: none
* @priority: low
* @acl: domain:write
*/
QEMU_PROC_DOMAIN_MONITOR_COMMAND_WITH_FILES = 7
};

View File

@ -4684,6 +4684,68 @@ qemuDispatchDomainMonitorCommand(virNetServer *server G_GNUC_UNUSED,
}
static int
qemuDispatchDomainMonitorCommandWithFiles(virNetServer *server G_GNUC_UNUSED,
virNetServerClient *client,
virNetMessage *msg,
struct virNetMessageError *rerr,
qemu_domain_monitor_command_with_files_args *args,
qemu_domain_monitor_command_with_files_ret *ret)
{
virDomainPtr dom = NULL;
int *infiles = NULL;
unsigned int ninfiles = 0;
int *outfiles = NULL;
unsigned int noutfiles = 0;
int rv = -1;
virConnectPtr conn = remoteGetHypervisorConn(client);
size_t i;
if (!conn)
goto cleanup;
if (!(dom = get_nonnull_domain(conn, args->dom)))
goto cleanup;
infiles = g_new0(int, msg->nfds);
for (i = 0; i < msg->nfds; i++) {
if ((infiles[i] = virNetMessageDupFD(msg, i)) < 0)
goto cleanup;
ninfiles++;
}
/* This API can both receive FDs from the client and send FDs back, but 'msg'
* is being reused. Thus we must clear the list of FDs in it to prevent
* us sending back the FDs client sent us. */
virNetMessageClearFDs(msg);
if (virDomainQemuMonitorCommandWithFiles(dom, args->cmd, ninfiles, infiles,
&noutfiles, &outfiles,
&ret->result, args->flags) < 0)
goto cleanup;
for (i = 0; i < noutfiles; i++) {
if (virNetMessageAddFD(msg, outfiles[i]) < 0)
goto cleanup;
}
/* return 1 here to let virNetServerProgramDispatchCall know we are passing fds */
if (noutfiles > 0)
rv = 1;
else
rv = 0;
cleanup:
for (i = 0; i < noutfiles; i++)
VIR_FORCE_CLOSE(outfiles[i]);
if (rv < 0)
virNetMessageSaveError(rerr);
virObjectUnref(dom);
return rv;
}
static int
remoteDispatchDomainMigrateBegin3(virNetServer *server G_GNUC_UNUSED,
virNetServerClient *client,

View File

@ -5931,6 +5931,62 @@ remoteDomainQemuMonitorCommand(virDomainPtr domain, const char *cmd,
}
static int
remoteDomainQemuMonitorCommandWithFiles(virDomainPtr domain,
const char *cmd,
unsigned int ninfiles,
int *infiles,
unsigned int *noutfiles,
int **outfiles,
char **result,
unsigned int flags)
{
int rv = -1;
qemu_domain_monitor_command_with_files_args args;
qemu_domain_monitor_command_with_files_ret ret;
struct private_data *priv = domain->conn->privateData;
size_t rpc_noutfiles = 0;
g_autofree int *rpc_outfiles = NULL;
size_t i;
remoteDriverLock(priv);
make_nonnull_domain(&args.dom, domain);
args.cmd = (char *)cmd;
args.flags = flags;
memset(&ret, 0, sizeof(ret));
if (callFull(domain->conn, priv, REMOTE_CALL_QEMU,
infiles, ninfiles, &rpc_outfiles, &rpc_noutfiles,
QEMU_PROC_DOMAIN_MONITOR_COMMAND_WITH_FILES,
(xdrproc_t) xdr_qemu_domain_monitor_command_with_files_args, (char *) &args,
(xdrproc_t) xdr_qemu_domain_monitor_command_with_files_ret, (char *) &ret) == -1)
goto done;
if (outfiles)
*outfiles = g_steal_pointer(&rpc_outfiles);
if (noutfiles)
*noutfiles = rpc_noutfiles;
*result = g_strdup(ret.result);
rv = 0;
xdr_free((xdrproc_t) xdr_qemu_domain_monitor_command_with_files_ret, (char *) &ret);
done:
if (rpc_outfiles) {
for (i = 0; rpc_noutfiles < i; i++) {
VIR_FORCE_CLOSE(rpc_outfiles[i]);
}
}
remoteDriverUnlock(priv);
return rv;
}
static char *
remoteDomainMigrateBegin3(virDomainPtr domain,
const char *xmlin,
@ -8506,6 +8562,7 @@ static virHypervisorDriver hypervisor_driver = {
.domainSnapshotHasMetadata = remoteDomainSnapshotHasMetadata, /* 0.9.13 */
.domainSnapshotDelete = remoteDomainSnapshotDelete, /* 0.8.0 */
.domainQemuMonitorCommand = remoteDomainQemuMonitorCommand, /* 0.8.3 */
.domainQemuMonitorCommandWithFiles = remoteDomainQemuMonitorCommandWithFiles, /* 8.2.0 */
.domainQemuAttach = remoteDomainQemuAttach, /* 0.9.4 */
.domainQemuAgentCommand = remoteDomainQemuAgentCommand, /* 0.10.0 */
.connectDomainQemuMonitorEventRegister = remoteConnectDomainQemuMonitorEventRegister, /* 1.2.3 */