diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2023-02-04 10:19:55 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2023-02-04 10:19:55 +0000 |
commit | 579510e196a544b42bd8bca9cc61688d4d1211ac (patch) | |
tree | c15a0c372280e558298b4e671b560b84c7f554b2 /monitor/fds.c | |
parent | 0730eab4d38f74589da4a7d55814773260491f89 (diff) | |
parent | 864a3fa439276148b6d7abcf2d43ee8dbe4c4062 (diff) | |
download | qemu-579510e196a544b42bd8bca9cc61688d4d1211ac.zip qemu-579510e196a544b42bd8bca9cc61688d4d1211ac.tar.gz qemu-579510e196a544b42bd8bca9cc61688d4d1211ac.tar.bz2 |
Merge tag 'pull-monitor-2023-02-03-v2' of https://repo.or.cz/qemu/armbru into staging
Monitor patches for 2023-02-03
# -----BEGIN PGP SIGNATURE-----
#
# iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmPeAkgSHGFybWJydUBy
# ZWRoYXQuY29tAAoJEDhwtADrkYZTUagP/iZ24jXaWoFOKaO70wdQ/tdoQObWZnUV
# 8xJNJYmYYbWoiq9wQXHebi/yEgBudso1lLzAnp8lsF12ybnNV1zsjyV/yumEKSNW
# 3nL1NZIcuY9IDmCe97clY9nm9H2lUhjjyCG3gnjg+uC3JjlSjO/T8lbkdT+fYnkR
# AInVTCPYFjSO9MIOhN0WNIY73HlAjr4zx5TEgS/D4pFj6iGq2qEniSDGMRf+/fVr
# uSbIXbQlum+VAdxbGMSVf8yQPlNcFUXUpSJrbgJE272H6saQuvn5mkwD0RcYXyaI
# OlfXpATDRNTsP3yYImxgr7y29Exo1HnCuC6T1n/+fwkirtMR3a7X6XjaQwFsWcrx
# xxGiHQOve3r/I3DAO6A64T2ceD/XuI43LygqkkljfuoXifnJz7Lo39P9HrY0dhpC
# KSld2n/Vv4xYyykvqAzpvzijwq679ILIbTplhm9gOrfrDRZjWad3uLAcYxsTXXR8
# BQbHGovcAzTOEx/0Quo3NThpAeNYPGyrPz3xBIV+XtPJGWvFsrA/s/po4qWDTmF6
# UTzPoEmznsD+DRboNOKfinCsOnpTAru4gbXevi7sfmMHQbLYN5xgsrF7WdlaxWa6
# 4QbJyNUq0O+aL0gyfVLuiZBCQ32Jaz1WvowK856Yl4jwczP5HM0ujyyM75+Kx072
# PdnMgxYYLSij
# =d+wL
# -----END PGP SIGNATURE-----
# gpg: Signature made Sat 04 Feb 2023 06:59:20 GMT
# gpg: using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg: issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>" [full]
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* tag 'pull-monitor-2023-02-03-v2' of https://repo.or.cz/qemu/armbru: (35 commits)
monitor: Rename misc.c to hmp-target.c
monitor: Loosen coupling between misc.c and monitor.c slightly
monitor: Move remaining QMP stuff from misc.c to qmp-cmds.c
monitor: Move remaining HMP commands from misc.c to hmp-cmds.c
monitor: Move target-dependent HMP commands to hmp-cmds-target.c
monitor: Move monitor_putc() next to monitor_puts & external linkage
monitor: Split file descriptor passing stuff off misc.c
qdev: Move HMP command completion from monitor to softmmu/
acpi: Move the QMP command from monitor/ to hw/acpi/
stats: Move HMP commands from monitor/ to stats/
stats: Move QMP commands from monitor/ to stats/
runstate: Move HMP commands from monitor/ to softmmu/
tpm: Move HMP commands from monitor/ to softmmu/
virtio: Move HMP commands from monitor/ to hw/virtio/
migration: Move the QMP command from monitor/ to migration/
migration: Move HMP commands from monitor/ to migration/
net: Move hmp_info_network() to net-hmp-cmds.c
net: Move HMP commands from monitor to net/
hmp: Rewrite strlist_from_comma_list() as hmp_split_at_comma()
rocker: Move HMP commands from monitor to hw/net/rocker/
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'monitor/fds.c')
-rw-r--r-- | monitor/fds.c | 468 |
1 files changed, 468 insertions, 0 deletions
diff --git a/monitor/fds.c b/monitor/fds.c new file mode 100644 index 0000000..26b39a0 --- /dev/null +++ b/monitor/fds.c @@ -0,0 +1,468 @@ +/* + * QEMU monitor file descriptor passing + * + * Copyright (c) 2003-2004 Fabrice Bellard + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "qemu/osdep.h" +#include "monitor-internal.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qmp/qerror.h" +#include "qemu/ctype.h" +#include "qemu/cutils.h" +#include "sysemu/runstate.h" + +/* file descriptors passed via SCM_RIGHTS */ +typedef struct mon_fd_t mon_fd_t; +struct mon_fd_t { + char *name; + int fd; + QLIST_ENTRY(mon_fd_t) next; +}; + +/* file descriptor associated with a file descriptor set */ +typedef struct MonFdsetFd MonFdsetFd; +struct MonFdsetFd { + int fd; + bool removed; + char *opaque; + QLIST_ENTRY(MonFdsetFd) next; +}; + +/* file descriptor set containing fds passed via SCM_RIGHTS */ +typedef struct MonFdset MonFdset; +struct MonFdset { + int64_t id; + QLIST_HEAD(, MonFdsetFd) fds; + QLIST_HEAD(, MonFdsetFd) dup_fds; + QLIST_ENTRY(MonFdset) next; +}; + +/* Protects mon_fdsets */ +static QemuMutex mon_fdsets_lock; +static QLIST_HEAD(, MonFdset) mon_fdsets; + +void qmp_getfd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + mon_fd_t *monfd; + int fd, tmp_fd; + + fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + return; + } + + if (qemu_isdigit(fdname[0])) { + close(fd); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", + "a name not starting with a digit"); + return; + } + + QEMU_LOCK_GUARD(&cur_mon->mon_lock); + QLIST_FOREACH(monfd, &cur_mon->fds, next) { + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + tmp_fd = monfd->fd; + monfd->fd = fd; + /* Make sure close() is outside critical section */ + close(tmp_fd); + return; + } + + monfd = g_new0(mon_fd_t, 1); + monfd->name = g_strdup(fdname); + monfd->fd = fd; + + QLIST_INSERT_HEAD(&cur_mon->fds, monfd, next); +} + +void qmp_closefd(const char *fdname, Error **errp) +{ + Monitor *cur_mon = monitor_cur(); + mon_fd_t *monfd; + int tmp_fd; + + qemu_mutex_lock(&cur_mon->mon_lock); + QLIST_FOREACH(monfd, &cur_mon->fds, next) { + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + QLIST_REMOVE(monfd, next); + tmp_fd = monfd->fd; + g_free(monfd->name); + g_free(monfd); + qemu_mutex_unlock(&cur_mon->mon_lock); + /* Make sure close() is outside critical section */ + close(tmp_fd); + return; + } + + qemu_mutex_unlock(&cur_mon->mon_lock); + error_setg(errp, "File descriptor named '%s' not found", fdname); +} + +int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp) +{ + mon_fd_t *monfd; + + QEMU_LOCK_GUARD(&mon->mon_lock); + QLIST_FOREACH(monfd, &mon->fds, next) { + int fd; + + if (strcmp(monfd->name, fdname) != 0) { + continue; + } + + fd = monfd->fd; + assert(fd >= 0); + + /* caller takes ownership of fd */ + QLIST_REMOVE(monfd, next); + g_free(monfd->name); + g_free(monfd); + + return fd; + } + + error_setg(errp, "File descriptor named '%s' has not been found", fdname); + return -1; +} + +static void monitor_fdset_cleanup(MonFdset *mon_fdset) +{ + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_next; + + QLIST_FOREACH_SAFE(mon_fdset_fd, &mon_fdset->fds, next, mon_fdset_fd_next) { + if ((mon_fdset_fd->removed || + (QLIST_EMPTY(&mon_fdset->dup_fds) && mon_refcount == 0)) && + runstate_is_running()) { + close(mon_fdset_fd->fd); + g_free(mon_fdset_fd->opaque); + QLIST_REMOVE(mon_fdset_fd, next); + g_free(mon_fdset_fd); + } + } + + if (QLIST_EMPTY(&mon_fdset->fds) && QLIST_EMPTY(&mon_fdset->dup_fds)) { + QLIST_REMOVE(mon_fdset, next); + g_free(mon_fdset); + } +} + +void monitor_fdsets_cleanup(void) +{ + MonFdset *mon_fdset; + MonFdset *mon_fdset_next; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH_SAFE(mon_fdset, &mon_fdsets, next, mon_fdset_next) { + monitor_fdset_cleanup(mon_fdset); + } +} + +AddfdInfo *qmp_add_fd(bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + int fd; + Monitor *mon = monitor_cur(); + AddfdInfo *fdinfo; + + fd = qemu_chr_fe_get_msgfd(&mon->chr); + if (fd == -1) { + error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); + goto error; + } + + fdinfo = monitor_fdset_add_fd(fd, has_fdset_id, fdset_id, opaque, errp); + if (fdinfo) { + return fdinfo; + } + +error: + if (fd != -1) { + close(fd); + } + return NULL; +} + +void qmp_remove_fd(int64_t fdset_id, bool has_fd, int64_t fd, Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + char fd_str[60]; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + if (mon_fdset->id != fdset_id) { + continue; + } + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + if (has_fd) { + if (mon_fdset_fd->fd != fd) { + continue; + } + mon_fdset_fd->removed = true; + break; + } else { + mon_fdset_fd->removed = true; + } + } + if (has_fd && !mon_fdset_fd) { + goto error; + } + monitor_fdset_cleanup(mon_fdset); + return; + } + +error: + if (has_fd) { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64 ", fd:%" PRId64, + fdset_id, fd); + } else { + snprintf(fd_str, sizeof(fd_str), "fdset-id:%" PRId64, fdset_id); + } + error_setg(errp, "File descriptor named '%s' not found", fd_str); +} + +FdsetInfoList *qmp_query_fdsets(Error **errp) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd; + FdsetInfoList *fdset_list = NULL; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + FdsetInfo *fdset_info = g_malloc0(sizeof(*fdset_info)); + + fdset_info->fdset_id = mon_fdset->id; + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + FdsetFdInfo *fdsetfd_info; + + fdsetfd_info = g_malloc0(sizeof(*fdsetfd_info)); + fdsetfd_info->fd = mon_fdset_fd->fd; + fdsetfd_info->opaque = g_strdup(mon_fdset_fd->opaque); + + QAPI_LIST_PREPEND(fdset_info->fds, fdsetfd_info); + } + + QAPI_LIST_PREPEND(fdset_list, fdset_info); + } + + return fdset_list; +} + +AddfdInfo *monitor_fdset_add_fd(int fd, bool has_fdset_id, int64_t fdset_id, + const char *opaque, Error **errp) +{ + MonFdset *mon_fdset = NULL; + MonFdsetFd *mon_fdset_fd; + AddfdInfo *fdinfo; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + if (has_fdset_id) { + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + /* Break if match found or match impossible due to ordering by ID */ + if (fdset_id <= mon_fdset->id) { + if (fdset_id < mon_fdset->id) { + mon_fdset = NULL; + } + break; + } + } + } + + if (mon_fdset == NULL) { + int64_t fdset_id_prev = -1; + MonFdset *mon_fdset_cur = QLIST_FIRST(&mon_fdsets); + + if (has_fdset_id) { + if (fdset_id < 0) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdset-id", + "a non-negative value"); + return NULL; + } + /* Use specified fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id < mon_fdset_cur->id) { + break; + } + } + } else { + /* Use first available fdset ID */ + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + mon_fdset_cur = mon_fdset; + if (fdset_id_prev == mon_fdset_cur->id - 1) { + fdset_id_prev = mon_fdset_cur->id; + continue; + } + break; + } + } + + mon_fdset = g_malloc0(sizeof(*mon_fdset)); + if (has_fdset_id) { + mon_fdset->id = fdset_id; + } else { + mon_fdset->id = fdset_id_prev + 1; + } + + /* The fdset list is ordered by fdset ID */ + if (!mon_fdset_cur) { + QLIST_INSERT_HEAD(&mon_fdsets, mon_fdset, next); + } else if (mon_fdset->id < mon_fdset_cur->id) { + QLIST_INSERT_BEFORE(mon_fdset_cur, mon_fdset, next); + } else { + QLIST_INSERT_AFTER(mon_fdset_cur, mon_fdset, next); + } + } + + mon_fdset_fd = g_malloc0(sizeof(*mon_fdset_fd)); + mon_fdset_fd->fd = fd; + mon_fdset_fd->removed = false; + mon_fdset_fd->opaque = g_strdup(opaque); + QLIST_INSERT_HEAD(&mon_fdset->fds, mon_fdset_fd, next); + + fdinfo = g_malloc0(sizeof(*fdinfo)); + fdinfo->fdset_id = mon_fdset->id; + fdinfo->fd = mon_fdset_fd->fd; + + return fdinfo; +} + +int monitor_fdset_dup_fd_add(int64_t fdset_id, int flags) +{ +#ifdef _WIN32 + return -ENOENT; +#else + MonFdset *mon_fdset; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + MonFdsetFd *mon_fdset_fd; + MonFdsetFd *mon_fdset_fd_dup; + int fd = -1; + int dup_fd; + int mon_fd_flags; + + if (mon_fdset->id != fdset_id) { + continue; + } + + QLIST_FOREACH(mon_fdset_fd, &mon_fdset->fds, next) { + mon_fd_flags = fcntl(mon_fdset_fd->fd, F_GETFL); + if (mon_fd_flags == -1) { + return -1; + } + + if ((flags & O_ACCMODE) == (mon_fd_flags & O_ACCMODE)) { + fd = mon_fdset_fd->fd; + break; + } + } + + if (fd == -1) { + errno = EACCES; + return -1; + } + + dup_fd = qemu_dup_flags(fd, flags); + if (dup_fd == -1) { + return -1; + } + + mon_fdset_fd_dup = g_malloc0(sizeof(*mon_fdset_fd_dup)); + mon_fdset_fd_dup->fd = dup_fd; + QLIST_INSERT_HEAD(&mon_fdset->dup_fds, mon_fdset_fd_dup, next); + return dup_fd; + } + + errno = ENOENT; + return -1; +#endif +} + +static int64_t monitor_fdset_dup_fd_find_remove(int dup_fd, bool remove) +{ + MonFdset *mon_fdset; + MonFdsetFd *mon_fdset_fd_dup; + + QEMU_LOCK_GUARD(&mon_fdsets_lock); + QLIST_FOREACH(mon_fdset, &mon_fdsets, next) { + QLIST_FOREACH(mon_fdset_fd_dup, &mon_fdset->dup_fds, next) { + if (mon_fdset_fd_dup->fd == dup_fd) { + if (remove) { + QLIST_REMOVE(mon_fdset_fd_dup, next); + g_free(mon_fdset_fd_dup); + if (QLIST_EMPTY(&mon_fdset->dup_fds)) { + monitor_fdset_cleanup(mon_fdset); + } + return -1; + } else { + return mon_fdset->id; + } + } + } + } + + return -1; +} + +int64_t monitor_fdset_dup_fd_find(int dup_fd) +{ + return monitor_fdset_dup_fd_find_remove(dup_fd, false); +} + +void monitor_fdset_dup_fd_remove(int dup_fd) +{ + monitor_fdset_dup_fd_find_remove(dup_fd, true); +} + +int monitor_fd_param(Monitor *mon, const char *fdname, Error **errp) +{ + int fd; + + if (!qemu_isdigit(fdname[0]) && mon) { + fd = monitor_get_fd(mon, fdname, errp); + } else { + fd = qemu_parse_fd(fdname); + if (fd < 0) { + error_setg(errp, "Invalid file descriptor number '%s'", + fdname); + } + } + + return fd; +} + +static void __attribute__((__constructor__)) monitor_fds_init(void) +{ + qemu_mutex_init(&mon_fdsets_lock); +} |