From b8998cc670f7b1b11a83276050e49dce7efba333 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Wed, 14 Oct 2020 12:08:28 -0500 Subject: [PATCH] qemu: add monitor functions for handling file descriptors add-fd, remove-fd, and query-fdsets provide functionality that can be used for passing fds to qemu and closing fdsets that are no longer necessary. Signed-off-by: Jonathon Jongsma Reviewed-by: Laine Stump --- src/qemu/qemu_monitor.c | 93 +++++++++++++++++++ src/qemu/qemu_monitor.h | 41 +++++++++ src/qemu/qemu_monitor_json.c | 173 +++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 12 +++ 4 files changed, 319 insertions(+) diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index 834e1c9e8f..b66d278ee5 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -2649,6 +2649,99 @@ qemuMonitorGraphicsRelocate(qemuMonitorPtr mon, } +/** + * qemuMonitorAddFileHandleToSet: + * @mon: monitor object + * @fd: file descriptor to pass to qemu + * @fdset: the fdset to register this fd with, -1 to create a new fdset + * @opaque: opaque data to associated with this fd + * @info: structure that will be updated with the fd and fdset returned by qemu + * + * Attempts to register a file descriptor with qemu that can then be referenced + * via the file path /dev/fdset/$FDSETID + * Returns 0 if ok, and -1 on failure */ +int +qemuMonitorAddFileHandleToSet(qemuMonitorPtr mon, + int fd, + int fdset, + const char *opaque, + qemuMonitorAddFdInfoPtr info) +{ + VIR_DEBUG("fd=%d,fdset=%i,opaque=%s", fd, fdset, opaque); + + QEMU_CHECK_MONITOR(mon); + + if (fd < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("fd must be valid")); + return -1; + } + + return qemuMonitorJSONAddFileHandleToSet(mon, fd, fdset, opaque, info); +} + + +/** + * qemuMonitorRemoveFdset: + * @mon: monitor object + * @fdset: the fdset to remove + * + * Attempts to remove a fdset from qemu and close associated file descriptors + * Returns 0 if ok, and -1 on failure */ +int +qemuMonitorRemoveFdset(qemuMonitorPtr mon, + int fdset) +{ + VIR_DEBUG("fdset=%d", fdset); + + QEMU_CHECK_MONITOR(mon); + + if (fdset < 0) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("fdset must be valid")); + return -1; + } + + return qemuMonitorJSONRemoveFdset(mon, fdset); +} + + +void qemuMonitorFdsetsFree(qemuMonitorFdsetsPtr fdsets) +{ + size_t i; + + for (i = 0; i < fdsets->nfdsets; i++) { + size_t j; + qemuMonitorFdsetInfoPtr set = &fdsets->fdsets[i]; + + for (j = 0; j < set->nfds; j++) + g_free(set->fds[j].opaque); + } + g_free(fdsets->fdsets); + g_free(fdsets); +} + + +/** + * qemuMonitorQueryFdsets: + * @mon: monitor object + * @fdsets: a pointer that is filled with a new qemuMonitorFdsets struct + * + * Queries qemu for the fdsets that are registered with that instance, and + * returns a structure describing those fdsets. The returned struct should be + * freed with qemuMonitorFdsetsFree(); + * + * Returns 0 if ok, and -1 on failure */ +int +qemuMonitorQueryFdsets(qemuMonitorPtr mon, + qemuMonitorFdsetsPtr *fdsets) +{ + QEMU_CHECK_MONITOR(mon); + + return qemuMonitorJSONQueryFdsets(mon, fdsets); +} + + int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index f2c632a7ac..017dc5bb7e 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -880,6 +880,47 @@ int qemuMonitorGraphicsRelocate(qemuMonitorPtr mon, int tlsPort, const char *tlsSubject); +typedef struct _qemuMonitorAddFdInfo qemuMonitorAddFdInfo; +typedef qemuMonitorAddFdInfo *qemuMonitorAddFdInfoPtr; +struct _qemuMonitorAddFdInfo { + int fd; + int fdset; +}; +int +qemuMonitorAddFileHandleToSet(qemuMonitorPtr mon, + int fd, + int fdset, + const char *opaque, + qemuMonitorAddFdInfoPtr info); + +int +qemuMonitorRemoveFdset(qemuMonitorPtr mon, + int fdset); + +typedef struct _qemuMonitorFdsetFdInfo qemuMonitorFdsetFdInfo; +typedef qemuMonitorFdsetFdInfo *qemuMonitorFdsetFdInfoPtr; +struct _qemuMonitorFdsetFdInfo { + int fd; + char *opaque; +}; +typedef struct _qemuMonitorFdsetInfo qemuMonitorFdsetInfo; +typedef qemuMonitorFdsetInfo *qemuMonitorFdsetInfoPtr; +struct _qemuMonitorFdsetInfo { + int id; + qemuMonitorFdsetFdInfoPtr fds; + int nfds; +}; +typedef struct _qemuMonitorFdsets qemuMonitorFdsets; +typedef qemuMonitorFdsets *qemuMonitorFdsetsPtr; +struct _qemuMonitorFdsets { + qemuMonitorFdsetInfoPtr fdsets; + int nfdsets; +}; +void qemuMonitorFdsetsFree(qemuMonitorFdsetsPtr fdsets); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMonitorFdsets, qemuMonitorFdsetsFree); +int qemuMonitorQueryFdsets(qemuMonitorPtr mon, + qemuMonitorFdsetsPtr *fdsets); + int qemuMonitorSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd); diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index e88e6aebaf..6c763ecc12 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -3929,6 +3929,179 @@ int qemuMonitorJSONGraphicsRelocate(qemuMonitorPtr mon, } +static int +qemuAddfdInfoParse(virJSONValuePtr msg, + qemuMonitorAddFdInfoPtr fdinfo) +{ + virJSONValuePtr returnObj; + + if (!(returnObj = virJSONValueObjectGetObject(msg, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid return data in add-fd response")); + return -1; + } + + if (virJSONValueObjectGetNumberInt(returnObj, "fd", &fdinfo->fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid fd in add-fd response")); + return -1; + } + + if (virJSONValueObjectGetNumberInt(returnObj, "fdset-id", &fdinfo->fdset) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Missing or invalid fdset-id in add-fd response")); + return -1; + } + + return 0; +} + + +/* if fdset is negative, qemu will create a new fdset and add the fd to that */ +int qemuMonitorJSONAddFileHandleToSet(qemuMonitorPtr mon, + int fd, + int fdset, + const char *opaque, + qemuMonitorAddFdInfoPtr fdinfo) +{ + virJSONValuePtr args = NULL; + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) cmd = NULL; + + if (virJSONValueObjectCreate(&args, "S:opaque", opaque, NULL) < 0) + return -1; + + if (fdset >= 0) + if (virJSONValueObjectAdd(args, "j:fdset-id", fdset, NULL) < 0) + return -1; + + if (!(cmd = qemuMonitorJSONMakeCommandInternal("add-fd", args))) + return -1; + + if (qemuMonitorJSONCommandWithFd(mon, cmd, fd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + if (qemuAddfdInfoParse(reply, fdinfo) < 0) + return -1; + + return 0; +} + +static int +qemuMonitorJSONQueryFdsetsParse(virJSONValuePtr msg, + qemuMonitorFdsetsPtr *fdsets) +{ + virJSONValuePtr returnArray, entry; + size_t i; + g_autoptr(qemuMonitorFdsets) sets = g_new0(qemuMonitorFdsets, 1); + int ninfo; + + returnArray = virJSONValueObjectGetArray(msg, "return"); + + ninfo = virJSONValueArraySize(returnArray); + if (ninfo > 0) + sets->fdsets = g_new0(qemuMonitorFdsetInfo, ninfo); + sets->nfdsets = ninfo; + + for (i = 0; i < ninfo; i++) { + size_t j; + const char *tmp; + virJSONValuePtr fdarray; + qemuMonitorFdsetInfoPtr fdsetinfo = &sets->fdsets[i]; + + if (!(entry = virJSONValueArrayGet(returnArray, i))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-fdsets return data missing fdset array element")); + return -1; + } + + if (virJSONValueObjectGetNumberInt(entry, "fdset-id", &fdsetinfo->id) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-fdsets reply was missing 'fdset-id'")); + return -1; + + } + + fdarray = virJSONValueObjectGetArray(entry, "fds"); + fdsetinfo->nfds = virJSONValueArraySize(fdarray); + if (fdsetinfo->nfds > 0) + fdsetinfo->fds = g_new0(qemuMonitorFdsetFdInfo, fdsetinfo->nfds); + + for (j = 0; j < fdsetinfo->nfds; j++) { + qemuMonitorFdsetFdInfoPtr fdinfo = &fdsetinfo->fds[j]; + virJSONValuePtr fdentry; + + if (!(fdentry = virJSONValueArrayGet(fdarray, j))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-fdsets return data missing fd array element")); + return -1; + } + + if (virJSONValueObjectGetNumberInt(fdentry, "fd", &fdinfo->fd) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-fdsets return data missing 'fd'")); + return -1; + } + + /* opaque is optional and may be missing */ + tmp = virJSONValueObjectGetString(fdentry, "opaque"); + if (tmp) + fdinfo->opaque = g_strdup(tmp); + } + } + + *fdsets = g_steal_pointer(&sets); + return 0; +} + + +int qemuMonitorJSONQueryFdsets(qemuMonitorPtr mon, + qemuMonitorFdsetsPtr *fdsets) +{ + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) cmd = qemuMonitorJSONMakeCommand("query-fdsets", + NULL); + + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + if (qemuMonitorJSONQueryFdsetsParse(reply, fdsets) < 0) + return -1; + + return 0; +} + + +int qemuMonitorJSONRemoveFdset(qemuMonitorPtr mon, + int fdset) +{ + g_autoptr(virJSONValue) reply = NULL; + g_autoptr(virJSONValue) cmd = qemuMonitorJSONMakeCommand("remove-fd", + "i:fdset-id", fdset, + NULL); + + if (!cmd) + return -1; + + if (qemuMonitorJSONCommand(mon, cmd, &reply) < 0) + return -1; + + if (qemuMonitorJSONCheckError(cmd, reply) < 0) + return -1; + + return 0; +} + + int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index e9107eaade..38e23ef3c5 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -202,6 +202,18 @@ int qemuMonitorJSONAddPCINetwork(qemuMonitorPtr mon, int qemuMonitorJSONRemovePCIDevice(qemuMonitorPtr mon, virPCIDeviceAddress *guestAddr); +int qemuMonitorJSONAddFileHandleToSet(qemuMonitorPtr mon, + int fd, + int fdset, + const char *opaque, + qemuMonitorAddFdInfoPtr info); + +int qemuMonitorJSONRemoveFdset(qemuMonitorPtr mon, + int fdset); + +int qemuMonitorJSONQueryFdsets(qemuMonitorPtr mon, + qemuMonitorFdsetsPtr *fdsets); + int qemuMonitorJSONSendFileHandle(qemuMonitorPtr mon, const char *fdname, int fd);