| /* | 
 |  * 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.1 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; | 
 |     fd_set rfds, wfds, xfds; | 
 |  | 
 |     if (!ssource->condition) { | 
 |         return 0; | 
 |     } | 
 |  | 
 |     FD_ZERO(&rfds); | 
 |     FD_ZERO(&wfds); | 
 |     FD_ZERO(&xfds); | 
 |     if (ssource->condition & G_IO_IN) { | 
 |         FD_SET(ssource->socket, &rfds); | 
 |     } | 
 |     if (ssource->condition & G_IO_OUT) { | 
 |         FD_SET(ssource->socket, &wfds); | 
 |     } | 
 |     if (ssource->condition & G_IO_PRI) { | 
 |         FD_SET(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 sockfd, | 
 |                                          GIOCondition condition) | 
 | { | 
 |     GSource *source; | 
 |     QIOChannelSocketSource *ssource; | 
 |  | 
 |     qemu_socket_select(sockfd, ioc->event, | 
 |                        FD_READ | FD_ACCEPT | FD_CLOSE | | 
 |                        FD_CONNECT | FD_WRITE | FD_OOB, NULL); | 
 |  | 
 |     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 = _get_osfhandle(sockfd); | 
 |     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; | 
 | } |