/*
* vireventglibwatch.c: GSource impl for sockets
*
* Copyright (C) 2015-2020 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
* .
*/
#include
#include "vireventglibwatch.h"
#ifndef WIN32
typedef struct virEventGLibFDSource virEventGLibFDSource;
struct virEventGLibFDSource {
GSource parent;
GPollFD pollfd;
int fd;
GIOCondition condition;
};
static gboolean
virEventGLibFDSourcePrepare(GSource *source G_GNUC_UNUSED,
gint *timeout)
{
*timeout = -1;
return FALSE;
}
static gboolean
virEventGLibFDSourceCheck(GSource *source)
{
virEventGLibFDSource *ssource = (virEventGLibFDSource *)source;
return ssource->pollfd.revents & ssource->condition;
}
static gboolean
virEventGLibFDSourceDispatch(GSource *source,
GSourceFunc callback,
gpointer user_data)
{
virEventGLibSocketFunc func = (virEventGLibSocketFunc)callback;
virEventGLibFDSource *ssource = (virEventGLibFDSource *)source;
return (*func)(ssource->fd,
ssource->pollfd.revents & ssource->condition,
user_data);
}
static void
virEventGLibFDSourceFinalize(GSource *source G_GNUC_UNUSED)
{
}
GSourceFuncs virEventGLibFDSourceFuncs = {
.prepare = virEventGLibFDSourcePrepare,
.check = virEventGLibFDSourceCheck,
.dispatch = virEventGLibFDSourceDispatch,
.finalize = virEventGLibFDSourceFinalize
};
GSource *virEventGLibCreateSocketWatch(int fd,
GIOCondition condition)
{
GSource *source;
virEventGLibFDSource *ssource;
source = g_source_new(&virEventGLibFDSourceFuncs,
sizeof(virEventGLibFDSource));
ssource = (virEventGLibFDSource *)source;
ssource->condition = condition;
ssource->fd = fd;
ssource->pollfd.fd = fd;
ssource->pollfd.events = condition;
g_source_add_poll(source, &ssource->pollfd);
return source;
}
#else /* WIN32 */
# define WIN32_LEAN_AND_MEAN
# include
typedef struct virEventGLibSocketSource virEventGLibSocketSource;
struct virEventGLibSocketSource {
GSource parent;
GPollFD pollfd;
int fd;
SOCKET socket;
HANDLE event;
int revents;
GIOCondition condition;
};
static gboolean
virEventGLibSocketSourcePrepare(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
virEventGLibSocketSourceCheck(GSource *source)
{
static struct timeval tv0;
virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source;
WSANETWORKEVENTS ev;
fd_set rfds, wfds, xfds;
if (!ssource->condition)
return 0;
WSAEnumNetworkEvents(ssource->socket, ssource->event, &ev);
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
virEventGLibSocketSourceDispatch(GSource *source,
GSourceFunc callback,
gpointer user_data)
{
virEventGLibSocketFunc func = (virEventGLibSocketFunc)callback;
virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source;
return (*func)(ssource->fd, ssource->revents, user_data);
}
static void
virEventGLibSocketSourceFinalize(GSource *source)
{
virEventGLibSocketSource *ssource = (virEventGLibSocketSource *)source;
WSAEventSelect(ssource->socket, NULL, 0);
CloseHandle(ssource->event);
}
GSourceFuncs virEventGLibSocketSourceFuncs = {
.prepare = virEventGLibSocketSourcePrepare,
.check = virEventGLibSocketSourceCheck,
.dispatch = virEventGLibSocketSourceDispatch,
.finalize = virEventGLibSocketSourceFinalize
};
GSource *virEventGLibCreateSocketWatch(int fd,
GIOCondition condition)
{
GSource *source;
virEventGLibSocketSource *ssource;
source = g_source_new(&virEventGLibSocketSourceFuncs,
sizeof(virEventGLibSocketSource));
ssource = (virEventGLibSocketSource *)source;
ssource->condition = condition;
ssource->fd = fd;
ssource->socket = _get_osfhandle(fd);
ssource->event = CreateEvent(NULL, FALSE, FALSE, NULL);
ssource->revents = 0;
ssource->pollfd.fd = (gintptr)ssource->event;
ssource->pollfd.events = G_IO_IN;
WSAEventSelect(ssource->socket, ssource->event,
FD_READ | FD_ACCEPT | FD_CLOSE |
FD_CONNECT | FD_WRITE | FD_OOB);
g_source_add_poll(source, &ssource->pollfd);
return source;
}
#endif /* WIN32 */
guint virEventGLibAddSocketWatch(int fd,
GIOCondition condition,
GMainContext *context,
virEventGLibSocketFunc func,
gpointer opaque,
GDestroyNotify notify)
{
g_autoptr(GSource) source = NULL;
source = virEventGLibCreateSocketWatch(fd, condition);
g_source_set_callback(source, (GSourceFunc)func, opaque, notify);
return g_source_attach(source, context);
}