|  | /* | 
|  | * 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; | 
|  |  | 
|  | static bool monitor_add_fd(Monitor *mon, int fd, const char *fdname, Error **errp) | 
|  | { | 
|  | mon_fd_t *monfd; | 
|  |  | 
|  | if (qemu_isdigit(fdname[0])) { | 
|  | close(fd); | 
|  | error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "fdname", | 
|  | "a name not starting with a digit"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* See close() call below. */ | 
|  | qemu_mutex_lock(&mon->mon_lock); | 
|  | QLIST_FOREACH(monfd, &mon->fds, next) { | 
|  | int tmp_fd; | 
|  |  | 
|  | if (strcmp(monfd->name, fdname) != 0) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | tmp_fd = monfd->fd; | 
|  | monfd->fd = fd; | 
|  | qemu_mutex_unlock(&mon->mon_lock); | 
|  | /* Make sure close() is outside critical section */ | 
|  | close(tmp_fd); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | monfd = g_new0(mon_fd_t, 1); | 
|  | monfd->name = g_strdup(fdname); | 
|  | monfd->fd = fd; | 
|  |  | 
|  | QLIST_INSERT_HEAD(&mon->fds, monfd, next); | 
|  | qemu_mutex_unlock(&mon->mon_lock); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_POSIX | 
|  | void qmp_getfd(const char *fdname, Error **errp) | 
|  | { | 
|  | Monitor *cur_mon = monitor_cur(); | 
|  | int fd; | 
|  |  | 
|  | fd = qemu_chr_fe_get_msgfd(&cur_mon->chr); | 
|  | if (fd == -1) { | 
|  | error_setg(errp, "No file descriptor supplied via SCM_RIGHTS"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | monitor_add_fd(cur_mon, fd, fdname, errp); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | #ifdef WIN32 | 
|  | void qmp_get_win32_socket(const char *infos, const char *fdname, Error **errp) | 
|  | { | 
|  | g_autofree WSAPROTOCOL_INFOW *info = NULL; | 
|  | gsize len; | 
|  | SOCKET sk; | 
|  | int fd; | 
|  |  | 
|  | info = (void *)g_base64_decode(infos, &len); | 
|  | if (len != sizeof(*info)) { | 
|  | error_setg(errp, "Invalid WSAPROTOCOL_INFOW value"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | sk = WSASocketW(FROM_PROTOCOL_INFO, | 
|  | FROM_PROTOCOL_INFO, | 
|  | FROM_PROTOCOL_INFO, | 
|  | info, 0, 0); | 
|  | if (sk == INVALID_SOCKET) { | 
|  | error_setg_win32(errp, WSAGetLastError(), "Couldn't import socket"); | 
|  | return; | 
|  | } | 
|  |  | 
|  | fd = _open_osfhandle(sk, _O_BINARY); | 
|  | if (fd < 0) { | 
|  | error_setg_errno(errp, errno, "Failed to associate a FD with the SOCKET"); | 
|  | closesocket(sk); | 
|  | return; | 
|  | } | 
|  |  | 
|  | monitor_add_fd(monitor_cur(), fd, fdname, errp); | 
|  | } | 
|  | #endif | 
|  |  | 
|  |  | 
|  | 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); | 
|  | } |