From 050ed3e6298ea89ed54eb06e1257dc801cee8cb8 Mon Sep 17 00:00:00 2001 From: Michal Privoznik Date: Fri, 4 Mar 2022 10:09:46 +0100 Subject: Implement virDomainQemuMonitorCommandWithFiles() override With libvirt-8.2.0 there's a new API: virDomainQemuMonitorCommandWithFiles(). Since the API has both input and output arguments we need to provide an alternative implementation. Moreover, since FD passing works only on UNIX-like systems we can query the returned FDs for their flags and construct mode for python File object. Signed-off-by: Michal Privoznik --- generator.py | 1 + libvirt-qemu-override.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++ libvirt-qemu-override.py | 31 ++++++++++++- 3 files changed, 149 insertions(+), 1 deletion(-) diff --git a/generator.py b/generator.py index e62abae..6bc5d56 100755 --- a/generator.py +++ b/generator.py @@ -626,6 +626,7 @@ qemu_skip_function = { # "virDomainQemuAttach", 'virConnectDomainQemuMonitorEventRegister', # overridden in -qemu.py 'virConnectDomainQemuMonitorEventDeregister', # overridden in -qemu.py + 'virDomainQemuMonitorCommandWithFiles', # overridden in -qemu.py } # Generate C code, but skip python impl diff --git a/libvirt-qemu-override.c b/libvirt-qemu-override.c index ba2d308..a554498 100644 --- a/libvirt-qemu-override.c +++ b/libvirt-qemu-override.c @@ -20,6 +20,9 @@ #include "typewrappers.h" #include "libvirt-utils.h" #include "build/libvirt-qemu.h" +#ifndef __CYGWIN__ +# include +#endif #ifndef __CYGWIN__ extern PyObject *PyInit_libvirtmod_qemu(void); @@ -325,6 +328,118 @@ libvirt_qemu_virConnectDomainQemuMonitorEventDeregister(PyObject *self ATTRIBUTE } #endif /* LIBVIR_CHECK_VERSION(1, 2, 3) */ +#if LIBVIR_CHECK_VERSION(8, 2, 0) +static PyObject * +libvirt_qemu_virDomainQemuMonitorCommandWithFiles(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args) +{ + PyObject *pyobj_domain; + const char *cmd; + PyObject *pyobj_files; + unsigned int flags; + virDomainPtr domain; + unsigned int ninfiles; + int *infiles = NULL; + unsigned int noutfiles = 0; + int *outfiles = NULL; + char *result = NULL; + ssize_t i; + PyObject *py_outfiles = NULL; + PyObject *py_retval = NULL; + int c_retval; + + if (!PyArg_ParseTuple(args, + (char *) "Os|OI:virDomainQemuMonitorCommandWithFiles", + &pyobj_domain, &cmd, &pyobj_files, &flags)) + return NULL; + domain = (virDomainPtr) PyvirDomain_Get(pyobj_domain); + + ninfiles = PyList_Size(pyobj_files); + + if (VIR_ALLOC_N(infiles, ninfiles) < 0) + return PyErr_NoMemory(); + + for (i = 0; i < ninfiles; i++) { + PyObject *pyfd; + int fd; + + pyfd = PyList_GetItem(pyobj_files, i); + + if (libvirt_intUnwrap(pyfd, &fd) < 0) + goto cleanup; + + infiles[i] = fd; + } + + LIBVIRT_BEGIN_ALLOW_THREADS; + c_retval = virDomainQemuMonitorCommandWithFiles(domain, cmd, ninfiles, infiles, + &noutfiles, &outfiles, &result, flags); + LIBVIRT_END_ALLOW_THREADS; + + if (c_retval < 0) { + py_retval = VIR_PY_NONE; + goto cleanup; + } + + if (!(py_outfiles = PyList_New(0)) || + !(py_retval = PyTuple_New(2))) { + goto error; + } + + for (i = 0; i < noutfiles; i++) { + int fd = outfiles[i]; + const char *mode = "r+b"; + + /* Since FD passing works only on UNIX-like systems, we can do this. */ +#ifndef __CYGWIN__ + int fflags; + + if ((fflags = fcntl(fd, F_GETFL)) < 0) + goto error; + + switch (fflags & (O_ACCMODE | O_APPEND)) { + case O_RDONLY: + mode = "rb"; + break; + case O_WRONLY: + mode = "wb"; + break; + case O_RDWR: + mode = "r+b"; + break; + case O_WRONLY | O_APPEND: + mode = "ab"; + break; + case O_RDWR | O_APPEND: + mode = "a+b"; + break; + } +#endif + + VIR_PY_LIST_APPEND_GOTO(py_outfiles, PyFile_FromFd(fd, NULL, mode, 0, NULL, NULL, NULL, 1), error); + } + + VIR_PY_TUPLE_SET_GOTO(py_retval, 0, libvirt_charPtrWrap(result), error); + VIR_PY_TUPLE_SET_GOTO(py_retval, 1, py_outfiles, error); + /* stolen by py_retval */ + py_outfiles = NULL; + + cleanup: + Py_XDECREF(py_outfiles); + VIR_FREE(result); + VIR_FREE(outfiles); + VIR_FREE(infiles); + return py_retval; + + error: + while (noutfiles > 0) { + VIR_FORCE_CLOSE(outfiles[--noutfiles]); + } + Py_CLEAR(py_retval); + goto cleanup; +} +#endif /* LIBVIR_CHECK_VERSION(8, 2, 0) */ + /************************************************************************ * * * The registration stuff * @@ -340,6 +455,9 @@ static PyMethodDef libvirtQemuMethods[] = { {(char *) "virConnectDomainQemuMonitorEventRegister", libvirt_qemu_virConnectDomainQemuMonitorEventRegister, METH_VARARGS, NULL}, {(char *) "virConnectDomainQemuMonitorEventDeregister", libvirt_qemu_virConnectDomainQemuMonitorEventDeregister, METH_VARARGS, NULL}, #endif /* LIBVIR_CHECK_VERSION(1, 2, 3) */ +#if LIBVIR_CHECK_VERSION(8, 2, 0) + {(char *) "virDomainQemuMonitorCommandWithFiles", libvirt_qemu_virDomainQemuMonitorCommandWithFiles, METH_VARARGS, NULL}, +#endif /* LIBVIR_CHECK_VERSION(8, 2, 0) */ {NULL, NULL, 0, NULL} }; diff --git a/libvirt-qemu-override.py b/libvirt-qemu-override.py index fae1db7..600dc30 100644 --- a/libvirt-qemu-override.py +++ b/libvirt-qemu-override.py @@ -1,5 +1,5 @@ # Manually written part of python bindings for libvirt-qemu -from typing import Any, Callable, Dict +from typing import Any, Callable, Dict, List, IO def _dispatchQemuMonitorEventCallback(conn: libvirt.virConnect, dom: libvirt.virDomain, event: str, seconds: int, micros: int, details: str, cbData: Dict[str, Any]) -> int: @@ -38,3 +38,32 @@ def qemuMonitorEventRegister(conn: libvirt.virConnect, dom: libvirt.virDomain, e raise libvirt.libvirtError('virConnectDomainQemuMonitorEventRegister() failed') conn.qemuMonitorEventCallbackID[ret] = opaque # type: ignore return ret + +def qemuMonitorCommandWithFiles(domain: libvirt.virDomain, cmd: str, files: List[int] = [], flags: int = 0) -> (str, List[IO]): + """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 @files to + domain through the QEMU monitor and optionally return a list of files + in the returned tuple. 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. + + Returns a tuple consisting of the string output from @cmd and a list of + files respectively.""" + ret = libvirtmod_qemu.virDomainQemuMonitorCommandWithFiles(domain._o, cmd, files, flags) + if ret is None: + raise libvirt.libvirtError('virDomainQemuMonitorCommandWithFiles() failed') + return ret -- cgit v1.2.1