| /* |
| * QEMU I/O channels watch helper APIs |
| * |
| * Copyright (c) 2015 Red Hat, Inc. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "io/channel-watch.h" |
| |
| typedef struct QIOChannelFDSource QIOChannelFDSource; |
| struct QIOChannelFDSource { |
| GSource parent; |
| GPollFD fd; |
| QIOChannel *ioc; |
| GIOCondition condition; |
| }; |
| |
| |
| #ifdef CONFIG_WIN32 |
| typedef struct QIOChannelSocketSource QIOChannelSocketSource; |
| struct QIOChannelSocketSource { |
| GSource parent; |
| GPollFD fd; |
| QIOChannel *ioc; |
| SOCKET socket; |
| int revents; |
| GIOCondition condition; |
| }; |
| |
| #endif |
| |
| |
| typedef struct QIOChannelFDPairSource QIOChannelFDPairSource; |
| struct QIOChannelFDPairSource { |
| GSource parent; |
| GPollFD fdread; |
| GPollFD fdwrite; |
| QIOChannel *ioc; |
| GIOCondition condition; |
| }; |
| |
| |
| static gboolean |
| qio_channel_fd_source_prepare(GSource *source G_GNUC_UNUSED, |
| gint *timeout) |
| { |
| *timeout = -1; |
| |
| return FALSE; |
| } |
| |
| |
| static gboolean |
| qio_channel_fd_source_check(GSource *source) |
| { |
| QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; |
| |
| return ssource->fd.revents & ssource->condition; |
| } |
| |
| |
| static gboolean |
| qio_channel_fd_source_dispatch(GSource *source, |
| GSourceFunc callback, |
| gpointer user_data) |
| { |
| QIOChannelFunc func = (QIOChannelFunc)callback; |
| QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; |
| |
| return (*func)(ssource->ioc, |
| ssource->fd.revents & ssource->condition, |
| user_data); |
| } |
| |
| |
| static void |
| qio_channel_fd_source_finalize(GSource *source) |
| { |
| QIOChannelFDSource *ssource = (QIOChannelFDSource *)source; |
| |
| object_unref(OBJECT(ssource->ioc)); |
| } |
| |
| |
| #ifdef CONFIG_WIN32 |
| static gboolean |
| qio_channel_socket_source_prepare(GSource *source G_GNUC_UNUSED, |
| gint *timeout) |
| { |
| *timeout = -1; |
| |
| return FALSE; |
| } |
| |
| |
| /* |
| * NB, this impl only works when the socket is in non-blocking |
| * mode on Win32 |
| */ |
| static gboolean |
| qio_channel_socket_source_check(GSource *source) |
| { |
| static struct timeval tv0; |
| |
| QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; |
| WSANETWORKEVENTS ev; |
| fd_set rfds, wfds, xfds; |
| |
| if (!ssource->condition) { |
| return 0; |
| } |
| |
| WSAEnumNetworkEvents(ssource->socket, ssource->ioc->event, &ev); |
| |
| FD_ZERO(&rfds); |
| FD_ZERO(&wfds); |
| FD_ZERO(&xfds); |
| if (ssource->condition & G_IO_IN) { |
| FD_SET((SOCKET)ssource->socket, &rfds); |
| } |
| if (ssource->condition & G_IO_OUT) { |
| FD_SET((SOCKET)ssource->socket, &wfds); |
| } |
| if (ssource->condition & G_IO_PRI) { |
| FD_SET((SOCKET)ssource->socket, &xfds); |
| } |
| ssource->revents = 0; |
| if (select(0, &rfds, &wfds, &xfds, &tv0) == 0) { |
| return 0; |
| } |
| |
| if (FD_ISSET(ssource->socket, &rfds)) { |
| ssource->revents |= G_IO_IN; |
| } |
| if (FD_ISSET(ssource->socket, &wfds)) { |
| ssource->revents |= G_IO_OUT; |
| } |
| if (FD_ISSET(ssource->socket, &xfds)) { |
| ssource->revents |= G_IO_PRI; |
| } |
| |
| return ssource->revents; |
| } |
| |
| |
| static gboolean |
| qio_channel_socket_source_dispatch(GSource *source, |
| GSourceFunc callback, |
| gpointer user_data) |
| { |
| QIOChannelFunc func = (QIOChannelFunc)callback; |
| QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; |
| |
| return (*func)(ssource->ioc, ssource->revents, user_data); |
| } |
| |
| |
| static void |
| qio_channel_socket_source_finalize(GSource *source) |
| { |
| QIOChannelSocketSource *ssource = (QIOChannelSocketSource *)source; |
| |
| object_unref(OBJECT(ssource->ioc)); |
| } |
| |
| |
| GSourceFuncs qio_channel_socket_source_funcs = { |
| qio_channel_socket_source_prepare, |
| qio_channel_socket_source_check, |
| qio_channel_socket_source_dispatch, |
| qio_channel_socket_source_finalize |
| }; |
| #endif |
| |
| |
| static gboolean |
| qio_channel_fd_pair_source_prepare(GSource *source G_GNUC_UNUSED, |
| gint *timeout) |
| { |
| *timeout = -1; |
| |
| return FALSE; |
| } |
| |
| |
| static gboolean |
| qio_channel_fd_pair_source_check(GSource *source) |
| { |
| QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; |
| GIOCondition poll_condition = ssource->fdread.revents | |
| ssource->fdwrite.revents; |
| |
| return poll_condition & ssource->condition; |
| } |
| |
| |
| static gboolean |
| qio_channel_fd_pair_source_dispatch(GSource *source, |
| GSourceFunc callback, |
| gpointer user_data) |
| { |
| QIOChannelFunc func = (QIOChannelFunc)callback; |
| QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; |
| GIOCondition poll_condition = ssource->fdread.revents | |
| ssource->fdwrite.revents; |
| |
| return (*func)(ssource->ioc, |
| poll_condition & ssource->condition, |
| user_data); |
| } |
| |
| |
| static void |
| qio_channel_fd_pair_source_finalize(GSource *source) |
| { |
| QIOChannelFDPairSource *ssource = (QIOChannelFDPairSource *)source; |
| |
| object_unref(OBJECT(ssource->ioc)); |
| } |
| |
| |
| GSourceFuncs qio_channel_fd_source_funcs = { |
| qio_channel_fd_source_prepare, |
| qio_channel_fd_source_check, |
| qio_channel_fd_source_dispatch, |
| qio_channel_fd_source_finalize |
| }; |
| |
| |
| GSourceFuncs qio_channel_fd_pair_source_funcs = { |
| qio_channel_fd_pair_source_prepare, |
| qio_channel_fd_pair_source_check, |
| qio_channel_fd_pair_source_dispatch, |
| qio_channel_fd_pair_source_finalize |
| }; |
| |
| |
| GSource *qio_channel_create_fd_watch(QIOChannel *ioc, |
| int fd, |
| GIOCondition condition) |
| { |
| GSource *source; |
| QIOChannelFDSource *ssource; |
| |
| source = g_source_new(&qio_channel_fd_source_funcs, |
| sizeof(QIOChannelFDSource)); |
| ssource = (QIOChannelFDSource *)source; |
| |
| ssource->ioc = ioc; |
| object_ref(OBJECT(ioc)); |
| |
| ssource->condition = condition; |
| |
| #ifdef CONFIG_WIN32 |
| ssource->fd.fd = (gint64)_get_osfhandle(fd); |
| #else |
| ssource->fd.fd = fd; |
| #endif |
| ssource->fd.events = condition; |
| |
| g_source_add_poll(source, &ssource->fd); |
| |
| return source; |
| } |
| |
| #ifdef CONFIG_WIN32 |
| GSource *qio_channel_create_socket_watch(QIOChannel *ioc, |
| int socket, |
| GIOCondition condition) |
| { |
| GSource *source; |
| QIOChannelSocketSource *ssource; |
| |
| #ifdef WIN32 |
| WSAEventSelect(socket, ioc->event, |
| FD_READ | FD_ACCEPT | FD_CLOSE | |
| FD_CONNECT | FD_WRITE | FD_OOB); |
| #endif |
| |
| source = g_source_new(&qio_channel_socket_source_funcs, |
| sizeof(QIOChannelSocketSource)); |
| ssource = (QIOChannelSocketSource *)source; |
| |
| ssource->ioc = ioc; |
| object_ref(OBJECT(ioc)); |
| |
| ssource->condition = condition; |
| ssource->socket = socket; |
| ssource->revents = 0; |
| |
| ssource->fd.fd = (gintptr)ioc->event; |
| ssource->fd.events = G_IO_IN; |
| |
| g_source_add_poll(source, &ssource->fd); |
| |
| return source; |
| } |
| #else |
| GSource *qio_channel_create_socket_watch(QIOChannel *ioc, |
| int socket, |
| GIOCondition condition) |
| { |
| return qio_channel_create_fd_watch(ioc, socket, condition); |
| } |
| #endif |
| |
| GSource *qio_channel_create_fd_pair_watch(QIOChannel *ioc, |
| int fdread, |
| int fdwrite, |
| GIOCondition condition) |
| { |
| GSource *source; |
| QIOChannelFDPairSource *ssource; |
| |
| source = g_source_new(&qio_channel_fd_pair_source_funcs, |
| sizeof(QIOChannelFDPairSource)); |
| ssource = (QIOChannelFDPairSource *)source; |
| |
| ssource->ioc = ioc; |
| object_ref(OBJECT(ioc)); |
| |
| ssource->condition = condition; |
| |
| #ifdef CONFIG_WIN32 |
| ssource->fdread.fd = (gint64)_get_osfhandle(fdread); |
| ssource->fdwrite.fd = (gint64)_get_osfhandle(fdwrite); |
| #else |
| ssource->fdread.fd = fdread; |
| ssource->fdwrite.fd = fdwrite; |
| #endif |
| |
| ssource->fdread.events = condition & G_IO_IN; |
| ssource->fdwrite.events = condition & G_IO_OUT; |
| |
| g_source_add_poll(source, &ssource->fdread); |
| g_source_add_poll(source, &ssource->fdwrite); |
| |
| return source; |
| } |