mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-06 15:24:46 +00:00
ntdll: Implement IOCTL_AFD_POLL.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50975 Signed-off-by: Zebediah Figura <z.figura12@gmail.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
30fd3019e1
commit
834e2f04c6
|
@ -235,6 +235,159 @@ static NTSTATUS sock_recv( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, voi
|
|||
return status;
|
||||
}
|
||||
|
||||
|
||||
struct async_poll_ioctl
|
||||
{
|
||||
struct async_fileio io;
|
||||
unsigned int count;
|
||||
struct afd_poll_params *input, *output;
|
||||
struct poll_socket_output sockets[1];
|
||||
};
|
||||
|
||||
static ULONG_PTR fill_poll_output( struct async_poll_ioctl *async, NTSTATUS status )
|
||||
{
|
||||
struct afd_poll_params *input = async->input, *output = async->output;
|
||||
unsigned int i, count = 0;
|
||||
|
||||
memcpy( output, input, offsetof( struct afd_poll_params, sockets[0] ) );
|
||||
|
||||
if (!status)
|
||||
{
|
||||
for (i = 0; i < async->count; ++i)
|
||||
{
|
||||
if (async->sockets[i].flags)
|
||||
{
|
||||
output->sockets[count].socket = input->sockets[i].socket;
|
||||
output->sockets[count].flags = async->sockets[i].flags;
|
||||
output->sockets[count].status = async->sockets[i].status;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
output->count = count;
|
||||
return offsetof( struct afd_poll_params, sockets[count] );
|
||||
}
|
||||
|
||||
static NTSTATUS async_poll_proc( void *user, IO_STATUS_BLOCK *io, NTSTATUS status )
|
||||
{
|
||||
struct async_poll_ioctl *async = user;
|
||||
ULONG_PTR information = 0;
|
||||
|
||||
if (status == STATUS_ALERTED)
|
||||
{
|
||||
SERVER_START_REQ( get_async_result )
|
||||
{
|
||||
req->user_arg = wine_server_client_ptr( async );
|
||||
wine_server_set_reply( req, async->sockets, async->count * sizeof(async->sockets[0]) );
|
||||
status = wine_server_call( req );
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
information = fill_poll_output( async, status );
|
||||
}
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
io->Status = status;
|
||||
io->Information = information;
|
||||
free( async->input );
|
||||
release_fileio( &async->io );
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* we could handle this ioctl entirely on the server side, but the differing
|
||||
* structure size makes it painful */
|
||||
static NTSTATUS sock_poll( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
|
||||
void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
|
||||
{
|
||||
const struct afd_poll_params *params = in_buffer;
|
||||
struct poll_socket_input *input;
|
||||
struct async_poll_ioctl *async;
|
||||
HANDLE wait_handle;
|
||||
DWORD async_size;
|
||||
NTSTATUS status;
|
||||
unsigned int i;
|
||||
ULONG options;
|
||||
|
||||
if (in_size < sizeof(*params) || out_size < in_size || !params->count
|
||||
|| in_size < offsetof( struct afd_poll_params, sockets[params->count] ))
|
||||
return STATUS_INVALID_PARAMETER;
|
||||
|
||||
TRACE( "timeout %s, count %u, unknown %#x, padding (%#x, %#x, %#x), sockets[0] {%04lx, %#x}\n",
|
||||
wine_dbgstr_longlong(params->timeout), params->count, params->unknown,
|
||||
params->padding[0], params->padding[1], params->padding[2],
|
||||
params->sockets[0].socket, params->sockets[0].flags );
|
||||
|
||||
if (params->unknown) FIXME( "unknown boolean is %#x\n", params->unknown );
|
||||
if (params->padding[0]) FIXME( "padding[0] is %#x\n", params->padding[0] );
|
||||
if (params->padding[1]) FIXME( "padding[1] is %#x\n", params->padding[1] );
|
||||
if (params->padding[2]) FIXME( "padding[2] is %#x\n", params->padding[2] );
|
||||
for (i = 0; i < params->count; ++i)
|
||||
{
|
||||
if (params->sockets[i].flags & ~0x1ff)
|
||||
FIXME( "unknown socket flags %#x\n", params->sockets[i].flags );
|
||||
}
|
||||
|
||||
if (!(input = malloc( params->count * sizeof(*input) )))
|
||||
return STATUS_NO_MEMORY;
|
||||
|
||||
async_size = offsetof( struct async_poll_ioctl, sockets[params->count] );
|
||||
|
||||
if (!(async = (struct async_poll_ioctl *)alloc_fileio( async_size, async_poll_proc, handle )))
|
||||
{
|
||||
free( input );
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (!(async->input = malloc( in_size )))
|
||||
{
|
||||
release_fileio( &async->io );
|
||||
free( input );
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy( async->input, in_buffer, in_size );
|
||||
|
||||
async->count = params->count;
|
||||
async->output = out_buffer;
|
||||
|
||||
for (i = 0; i < params->count; ++i)
|
||||
{
|
||||
input[i].socket = params->sockets[i].socket;
|
||||
input[i].flags = params->sockets[i].flags;
|
||||
}
|
||||
|
||||
SERVER_START_REQ( poll_socket )
|
||||
{
|
||||
req->async = server_async( handle, &async->io, event, apc, apc_user, io );
|
||||
req->timeout = params->timeout;
|
||||
wine_server_add_data( req, input, params->count * sizeof(*input) );
|
||||
wine_server_set_reply( req, async->sockets, params->count * sizeof(async->sockets[0]) );
|
||||
status = wine_server_call( req );
|
||||
wait_handle = wine_server_ptr_handle( reply->wait );
|
||||
options = reply->options;
|
||||
if (wait_handle && status != STATUS_PENDING)
|
||||
{
|
||||
io->Status = status;
|
||||
io->Information = fill_poll_output( async, status );
|
||||
}
|
||||
}
|
||||
SERVER_END_REQ;
|
||||
|
||||
free( input );
|
||||
|
||||
if (status != STATUS_PENDING)
|
||||
{
|
||||
free( async->input );
|
||||
release_fileio( &async->io );
|
||||
}
|
||||
|
||||
if (wait_handle) status = wait_async( wait_handle, (options & FILE_SYNCHRONOUS_IO_ALERT) );
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc_user, IO_STATUS_BLOCK *io,
|
||||
ULONG code, void *in_buffer, ULONG in_size, void *out_buffer, ULONG out_size )
|
||||
{
|
||||
|
@ -299,6 +452,10 @@ NTSTATUS sock_ioctl( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc, void *apc
|
|||
break;
|
||||
}
|
||||
|
||||
case IOCTL_AFD_POLL:
|
||||
status = sock_poll( handle, event, apc, apc_user, io, in_buffer, in_size, out_buffer, out_size );
|
||||
break;
|
||||
|
||||
default:
|
||||
{
|
||||
FIXME( "Unknown ioctl %#x (device %#x, access %#x, function %#x, method %#x)\n",
|
||||
|
|
|
@ -27,6 +27,20 @@
|
|||
|
||||
#define IOCTL_AFD_LISTEN CTL_CODE(FILE_DEVICE_BEEP, 0x802, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_AFD_RECV CTL_CODE(FILE_DEVICE_BEEP, 0x805, METHOD_NEITHER, FILE_ANY_ACCESS)
|
||||
#define IOCTL_AFD_POLL CTL_CODE(FILE_DEVICE_BEEP, 0x809, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
||||
#define AFD_POLL_READ 0x0001
|
||||
#define AFD_POLL_OOB 0x0002
|
||||
#define AFD_POLL_WRITE 0x0004
|
||||
#define AFD_POLL_HUP 0x0008
|
||||
#define AFD_POLL_RESET 0x0010
|
||||
#define AFD_POLL_CLOSE 0x0020
|
||||
#define AFD_POLL_CONNECT 0x0040
|
||||
#define AFD_POLL_ACCEPT 0x0080
|
||||
#define AFD_POLL_CONNECT_ERR 0x0100
|
||||
/* I have never seen these reported, but StarCraft Remastered polls for them. */
|
||||
#define AFD_POLL_UNK1 0x0200
|
||||
#define AFD_POLL_UNK2 0x0400
|
||||
|
||||
struct afd_listen_params
|
||||
{
|
||||
|
@ -50,6 +64,22 @@ struct afd_recv_params
|
|||
int msg_flags;
|
||||
};
|
||||
|
||||
#include <pshpack4.h>
|
||||
struct afd_poll_params
|
||||
{
|
||||
LONGLONG timeout;
|
||||
unsigned int count;
|
||||
BOOLEAN unknown;
|
||||
BOOLEAN padding[3];
|
||||
struct
|
||||
{
|
||||
SOCKET socket;
|
||||
int flags;
|
||||
NTSTATUS status;
|
||||
} sockets[1];
|
||||
};
|
||||
#include <poppack.h>
|
||||
|
||||
#define IOCTL_AFD_WINE_CREATE CTL_CODE(FILE_DEVICE_NETWORK, 200, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_AFD_WINE_ACCEPT CTL_CODE(FILE_DEVICE_NETWORK, 201, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
#define IOCTL_AFD_WINE_ACCEPT_INTO CTL_CODE(FILE_DEVICE_NETWORK, 202, METHOD_BUFFERED, FILE_ANY_ACCESS)
|
||||
|
|
|
@ -1834,6 +1834,36 @@ struct recv_socket_reply
|
|||
};
|
||||
|
||||
|
||||
struct poll_socket_input
|
||||
{
|
||||
obj_handle_t socket;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct poll_socket_output
|
||||
{
|
||||
int flags;
|
||||
unsigned int status;
|
||||
};
|
||||
|
||||
|
||||
struct poll_socket_request
|
||||
{
|
||||
struct request_header __header;
|
||||
char __pad_12[4];
|
||||
async_data_t async;
|
||||
timeout_t timeout;
|
||||
/* VARARG(sockets,poll_socket_input); */
|
||||
};
|
||||
struct poll_socket_reply
|
||||
{
|
||||
struct reply_header __header;
|
||||
obj_handle_t wait;
|
||||
unsigned int options;
|
||||
/* VARARG(sockets,poll_socket_output); */
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct get_next_console_request_request
|
||||
{
|
||||
|
@ -5485,6 +5515,7 @@ enum request
|
|||
REQ_enable_socket_event,
|
||||
REQ_set_socket_deferred,
|
||||
REQ_recv_socket,
|
||||
REQ_poll_socket,
|
||||
REQ_get_next_console_request,
|
||||
REQ_read_directory_changes,
|
||||
REQ_read_change,
|
||||
|
@ -5767,6 +5798,7 @@ union generic_request
|
|||
struct enable_socket_event_request enable_socket_event_request;
|
||||
struct set_socket_deferred_request set_socket_deferred_request;
|
||||
struct recv_socket_request recv_socket_request;
|
||||
struct poll_socket_request poll_socket_request;
|
||||
struct get_next_console_request_request get_next_console_request_request;
|
||||
struct read_directory_changes_request read_directory_changes_request;
|
||||
struct read_change_request read_change_request;
|
||||
|
@ -6047,6 +6079,7 @@ union generic_reply
|
|||
struct enable_socket_event_reply enable_socket_event_reply;
|
||||
struct set_socket_deferred_reply set_socket_deferred_reply;
|
||||
struct recv_socket_reply recv_socket_reply;
|
||||
struct poll_socket_reply poll_socket_reply;
|
||||
struct get_next_console_request_reply get_next_console_request_reply;
|
||||
struct read_directory_changes_reply read_directory_changes_reply;
|
||||
struct read_change_reply read_change_reply;
|
||||
|
@ -6267,7 +6300,7 @@ union generic_reply
|
|||
|
||||
/* ### protocol_version begin ### */
|
||||
|
||||
#define SERVER_PROTOCOL_VERSION 704
|
||||
#define SERVER_PROTOCOL_VERSION 705
|
||||
|
||||
/* ### protocol_version end ### */
|
||||
|
||||
|
|
|
@ -1490,6 +1490,30 @@ enum server_fd_type
|
|||
@END
|
||||
|
||||
|
||||
struct poll_socket_input
|
||||
{
|
||||
obj_handle_t socket; /* socket handle */
|
||||
int flags; /* events to poll for */
|
||||
};
|
||||
|
||||
struct poll_socket_output
|
||||
{
|
||||
int flags; /* events signaled */
|
||||
unsigned int status; /* socket status */
|
||||
};
|
||||
|
||||
/* Perform an async poll on a socket */
|
||||
@REQ(poll_socket)
|
||||
async_data_t async; /* async I/O parameters */
|
||||
timeout_t timeout; /* timeout */
|
||||
VARARG(sockets,poll_socket_input); /* list of sockets to poll */
|
||||
@REPLY
|
||||
obj_handle_t wait; /* handle to wait on for blocking poll */
|
||||
unsigned int options; /* file open options */
|
||||
VARARG(sockets,poll_socket_output); /* data returned */
|
||||
@END
|
||||
|
||||
|
||||
/* Retrieve the next pending console ioctl request */
|
||||
@REQ(get_next_console_request)
|
||||
obj_handle_t handle; /* console server handle */
|
||||
|
|
|
@ -178,6 +178,7 @@ DECL_HANDLER(get_socket_info);
|
|||
DECL_HANDLER(enable_socket_event);
|
||||
DECL_HANDLER(set_socket_deferred);
|
||||
DECL_HANDLER(recv_socket);
|
||||
DECL_HANDLER(poll_socket);
|
||||
DECL_HANDLER(get_next_console_request);
|
||||
DECL_HANDLER(read_directory_changes);
|
||||
DECL_HANDLER(read_change);
|
||||
|
@ -459,6 +460,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] =
|
|||
(req_handler)req_enable_socket_event,
|
||||
(req_handler)req_set_socket_deferred,
|
||||
(req_handler)req_recv_socket,
|
||||
(req_handler)req_poll_socket,
|
||||
(req_handler)req_get_next_console_request,
|
||||
(req_handler)req_read_directory_changes,
|
||||
(req_handler)req_read_change,
|
||||
|
@ -1073,6 +1075,12 @@ C_ASSERT( sizeof(struct recv_socket_request) == 64 );
|
|||
C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 );
|
||||
C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 );
|
||||
C_ASSERT( sizeof(struct recv_socket_reply) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct poll_socket_request, async) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct poll_socket_request, timeout) == 56 );
|
||||
C_ASSERT( sizeof(struct poll_socket_request) == 64 );
|
||||
C_ASSERT( FIELD_OFFSET(struct poll_socket_reply, wait) == 8 );
|
||||
C_ASSERT( FIELD_OFFSET(struct poll_socket_reply, options) == 12 );
|
||||
C_ASSERT( sizeof(struct poll_socket_reply) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, handle) == 12 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, signal) == 16 );
|
||||
C_ASSERT( FIELD_OFFSET(struct get_next_console_request_request, read) == 20 );
|
||||
|
|
306
server/sock.c
306
server/sock.c
|
@ -96,6 +96,23 @@
|
|||
#include "request.h"
|
||||
#include "user.h"
|
||||
|
||||
static struct list poll_list = LIST_INIT( poll_list );
|
||||
|
||||
struct poll_req
|
||||
{
|
||||
struct list entry;
|
||||
struct async *async;
|
||||
struct iosb *iosb;
|
||||
struct timeout_user *timeout;
|
||||
unsigned int count;
|
||||
struct poll_socket_output *output;
|
||||
struct
|
||||
{
|
||||
struct sock *sock;
|
||||
int flags;
|
||||
} sockets[1];
|
||||
};
|
||||
|
||||
struct accept_req
|
||||
{
|
||||
struct list entry;
|
||||
|
@ -151,6 +168,7 @@ struct sock
|
|||
struct async_queue ifchange_q; /* queue for interface change notifications */
|
||||
struct async_queue accept_q; /* queue for asynchronous accepts */
|
||||
struct async_queue connect_q; /* queue for asynchronous connects */
|
||||
struct async_queue poll_q; /* queue for asynchronous polls */
|
||||
struct object *ifchange_obj; /* the interface change notification object */
|
||||
struct list ifchange_entry; /* entry in ifchange notification list */
|
||||
struct list accept_list; /* list of pending accept requests */
|
||||
|
@ -621,6 +639,106 @@ static void complete_async_connect( struct sock *sock )
|
|||
}
|
||||
}
|
||||
|
||||
static void free_poll_req( void *private )
|
||||
{
|
||||
struct poll_req *req = private;
|
||||
unsigned int i;
|
||||
|
||||
if (req->timeout) remove_timeout_user( req->timeout );
|
||||
|
||||
for (i = 0; i < req->count; ++i)
|
||||
release_object( req->sockets[i].sock );
|
||||
release_object( req->async );
|
||||
release_object( req->iosb );
|
||||
list_remove( &req->entry );
|
||||
free( req );
|
||||
}
|
||||
|
||||
static int is_oobinline( struct sock *sock )
|
||||
{
|
||||
int oobinline;
|
||||
socklen_t len = sizeof(oobinline);
|
||||
return !getsockopt( get_unix_fd( sock->fd ), SOL_SOCKET, SO_OOBINLINE, (char *)&oobinline, &len ) && oobinline;
|
||||
}
|
||||
|
||||
static int get_poll_flags( struct sock *sock, int event )
|
||||
{
|
||||
int flags = 0;
|
||||
|
||||
/* A connection-mode socket which has never been connected does not return
|
||||
* write or hangup events, but Linux reports POLLOUT | POLLHUP. */
|
||||
if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING)))
|
||||
event &= ~(POLLOUT | POLLHUP);
|
||||
|
||||
if (event & POLLIN)
|
||||
{
|
||||
if (sock->state & FD_WINE_LISTENING)
|
||||
flags |= AFD_POLL_ACCEPT;
|
||||
else
|
||||
flags |= AFD_POLL_READ;
|
||||
}
|
||||
if (event & POLLPRI)
|
||||
flags |= is_oobinline( sock ) ? AFD_POLL_READ : AFD_POLL_OOB;
|
||||
if (event & POLLOUT)
|
||||
flags |= AFD_POLL_WRITE;
|
||||
if (sock->state & FD_WINE_CONNECTED)
|
||||
flags |= AFD_POLL_CONNECT;
|
||||
if (event & POLLHUP)
|
||||
flags |= AFD_POLL_HUP;
|
||||
if (event & POLLERR)
|
||||
flags |= AFD_POLL_CONNECT_ERR;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static void complete_async_polls( struct sock *sock, int event, int error )
|
||||
{
|
||||
int flags = get_poll_flags( sock, event );
|
||||
struct poll_req *req, *next;
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE( req, next, &poll_list, struct poll_req, entry )
|
||||
{
|
||||
struct iosb *iosb = req->iosb;
|
||||
unsigned int i;
|
||||
|
||||
if (iosb->status != STATUS_PENDING) continue;
|
||||
|
||||
for (i = 0; i < req->count; ++i)
|
||||
{
|
||||
if (req->sockets[i].sock != sock) continue;
|
||||
if (!(req->sockets[i].flags & flags)) continue;
|
||||
|
||||
if (debug_level)
|
||||
fprintf( stderr, "completing poll for socket %p, wanted %#x got %#x\n",
|
||||
sock, req->sockets[i].flags, flags );
|
||||
|
||||
req->output[i].flags = req->sockets[i].flags & flags;
|
||||
req->output[i].status = sock_get_ntstatus( error );
|
||||
|
||||
iosb->status = STATUS_SUCCESS;
|
||||
iosb->out_data = req->output;
|
||||
iosb->out_size = req->count * sizeof(*req->output);
|
||||
async_terminate( req->async, STATUS_ALERTED );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void async_poll_timeout( void *private )
|
||||
{
|
||||
struct poll_req *req = private;
|
||||
struct iosb *iosb = req->iosb;
|
||||
|
||||
req->timeout = NULL;
|
||||
|
||||
if (iosb->status != STATUS_PENDING) return;
|
||||
|
||||
iosb->status = STATUS_TIMEOUT;
|
||||
iosb->out_data = req->output;
|
||||
iosb->out_size = req->count * sizeof(*req->output);
|
||||
async_terminate( req->async, STATUS_ALERTED );
|
||||
}
|
||||
|
||||
static int sock_dispatch_asyncs( struct sock *sock, int event, int error )
|
||||
{
|
||||
if (event & (POLLIN | POLLPRI))
|
||||
|
@ -820,6 +938,8 @@ static void sock_poll_event( struct fd *fd, int event )
|
|||
event |= POLLHUP;
|
||||
}
|
||||
|
||||
complete_async_polls( sock, event, error );
|
||||
|
||||
event = sock_dispatch_asyncs( sock, event, error );
|
||||
sock_dispatch_events( sock, prevstate, event, error );
|
||||
|
||||
|
@ -835,11 +955,34 @@ static void sock_dump( struct object *obj, int verbose )
|
|||
sock->mask, sock->pending_events, sock->reported_events );
|
||||
}
|
||||
|
||||
static int poll_flags_from_afd( struct sock *sock, int flags )
|
||||
{
|
||||
int ev = 0;
|
||||
|
||||
/* A connection-mode socket which has never been connected does
|
||||
* not return write or hangup events, but Linux returns
|
||||
* POLLOUT | POLLHUP. */
|
||||
if (sock->type == WS_SOCK_STREAM && !(sock->state & (FD_CONNECT | FD_WINE_CONNECTED | FD_WINE_LISTENING)))
|
||||
return -1;
|
||||
|
||||
if (flags & (AFD_POLL_READ | AFD_POLL_ACCEPT))
|
||||
ev |= POLLIN;
|
||||
if ((flags & AFD_POLL_HUP) && sock->type == WS_SOCK_STREAM)
|
||||
ev |= POLLIN;
|
||||
if (flags & AFD_POLL_OOB)
|
||||
ev |= is_oobinline( sock ) ? POLLIN : POLLPRI;
|
||||
if (flags & AFD_POLL_WRITE)
|
||||
ev |= POLLOUT;
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
static int sock_get_poll_events( struct fd *fd )
|
||||
{
|
||||
struct sock *sock = get_fd_user( fd );
|
||||
unsigned int mask = sock->mask & ~sock->reported_events;
|
||||
unsigned int smask = sock->state & mask;
|
||||
struct poll_req *req;
|
||||
int ev = 0;
|
||||
|
||||
assert( sock->obj.ops == &sock_ops );
|
||||
|
@ -869,6 +1012,18 @@ static int sock_get_poll_events( struct fd *fd )
|
|||
else if (smask & FD_WRITE)
|
||||
ev |= POLLOUT;
|
||||
|
||||
LIST_FOR_EACH_ENTRY( req, &poll_list, struct poll_req, entry )
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < req->count; ++i)
|
||||
{
|
||||
if (req->sockets[i].sock != sock) continue;
|
||||
|
||||
ev |= poll_flags_from_afd( sock, req->sockets[i].flags );
|
||||
}
|
||||
}
|
||||
|
||||
return ev;
|
||||
}
|
||||
|
||||
|
@ -933,18 +1088,45 @@ static struct fd *sock_get_fd( struct object *obj )
|
|||
static int sock_close_handle( struct object *obj, struct process *process, obj_handle_t handle )
|
||||
{
|
||||
struct sock *sock = (struct sock *)obj;
|
||||
struct accept_req *req, *next;
|
||||
|
||||
if (sock->obj.handle_count == 1) /* last handle */
|
||||
{
|
||||
struct accept_req *accept_req, *accept_next;
|
||||
struct poll_req *poll_req, *poll_next;
|
||||
|
||||
if (sock->accept_recv_req)
|
||||
async_terminate( sock->accept_recv_req->async, STATUS_CANCELLED );
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE( req, next, &sock->accept_list, struct accept_req, entry )
|
||||
async_terminate( req->async, STATUS_CANCELLED );
|
||||
LIST_FOR_EACH_ENTRY_SAFE( accept_req, accept_next, &sock->accept_list, struct accept_req, entry )
|
||||
async_terminate( accept_req->async, STATUS_CANCELLED );
|
||||
|
||||
if (sock->connect_req)
|
||||
async_terminate( sock->connect_req->async, STATUS_CANCELLED );
|
||||
|
||||
LIST_FOR_EACH_ENTRY_SAFE( poll_req, poll_next, &poll_list, struct poll_req, entry )
|
||||
{
|
||||
struct iosb *iosb = poll_req->iosb;
|
||||
unsigned int i;
|
||||
|
||||
if (iosb->status != STATUS_PENDING) continue;
|
||||
|
||||
for (i = 0; i < poll_req->count; ++i)
|
||||
{
|
||||
if (poll_req->sockets[i].sock == sock)
|
||||
{
|
||||
iosb->status = STATUS_SUCCESS;
|
||||
poll_req->output[i].flags = AFD_POLL_CLOSE;
|
||||
poll_req->output[i].status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (iosb->status != STATUS_PENDING)
|
||||
{
|
||||
iosb->out_data = poll_req->output;
|
||||
iosb->out_size = poll_req->count * sizeof(*poll_req->output);
|
||||
async_terminate( poll_req->async, STATUS_ALERTED );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -968,6 +1150,7 @@ static void sock_destroy( struct object *obj )
|
|||
free_async_queue( &sock->ifchange_q );
|
||||
free_async_queue( &sock->accept_q );
|
||||
free_async_queue( &sock->connect_q );
|
||||
free_async_queue( &sock->poll_q );
|
||||
if (sock->event) release_object( sock->event );
|
||||
if (sock->fd)
|
||||
{
|
||||
|
@ -1007,6 +1190,7 @@ static struct sock *create_socket(void)
|
|||
init_async_queue( &sock->ifchange_q );
|
||||
init_async_queue( &sock->accept_q );
|
||||
init_async_queue( &sock->connect_q );
|
||||
init_async_queue( &sock->poll_q );
|
||||
memset( sock->errors, 0, sizeof(sock->errors) );
|
||||
list_init( &sock->accept_list );
|
||||
return sock;
|
||||
|
@ -1710,6 +1894,101 @@ static int sock_ioctl( struct fd *fd, ioctl_code_t code, struct async *async )
|
|||
}
|
||||
}
|
||||
|
||||
static int poll_socket( struct sock *poll_sock, struct async *async, timeout_t timeout,
|
||||
unsigned int count, const struct poll_socket_input *input )
|
||||
{
|
||||
struct poll_socket_output *output;
|
||||
struct poll_req *req;
|
||||
unsigned int i, j;
|
||||
|
||||
if (!(output = mem_alloc( count * sizeof(*output) )))
|
||||
return 0;
|
||||
memset( output, 0, count * sizeof(*output) );
|
||||
|
||||
if (!(req = mem_alloc( offsetof( struct poll_req, sockets[count] ) )))
|
||||
{
|
||||
free( output );
|
||||
return 0;
|
||||
}
|
||||
|
||||
req->timeout = NULL;
|
||||
if (timeout && timeout != TIMEOUT_INFINITE &&
|
||||
!(req->timeout = add_timeout_user( timeout, async_poll_timeout, req )))
|
||||
{
|
||||
free( req );
|
||||
free( output );
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
req->sockets[i].sock = (struct sock *)get_handle_obj( current->process, input[i].socket, 0, &sock_ops );
|
||||
if (!req->sockets[i].sock)
|
||||
{
|
||||
for (j = 0; j < i; ++j) release_object( req->sockets[i].sock );
|
||||
if (req->timeout) remove_timeout_user( req->timeout );
|
||||
free( req );
|
||||
free( output );
|
||||
return 0;
|
||||
}
|
||||
req->sockets[i].flags = input[i].flags;
|
||||
}
|
||||
|
||||
req->count = count;
|
||||
req->async = (struct async *)grab_object( async );
|
||||
req->iosb = async_get_iosb( async );
|
||||
req->output = output;
|
||||
|
||||
list_add_tail( &poll_list, &req->entry );
|
||||
async_set_completion_callback( async, free_poll_req, req );
|
||||
queue_async( &poll_sock->poll_q, async );
|
||||
|
||||
if (!timeout) req->iosb->status = STATUS_SUCCESS;
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
struct sock *sock = req->sockets[i].sock;
|
||||
struct pollfd pollfd;
|
||||
int flags;
|
||||
|
||||
pollfd.fd = get_unix_fd( sock->fd );
|
||||
pollfd.events = poll_flags_from_afd( sock, req->sockets[i].flags );
|
||||
if (pollfd.events < 0 || poll( &pollfd, 1, 0 ) < 0) continue;
|
||||
|
||||
if ((req->sockets[i].flags & AFD_POLL_HUP) && (pollfd.revents & POLLIN) &&
|
||||
sock->type == WS_SOCK_STREAM)
|
||||
{
|
||||
char dummy;
|
||||
|
||||
if (!recv( get_unix_fd( sock->fd ), &dummy, 1, MSG_PEEK ))
|
||||
{
|
||||
pollfd.revents &= ~POLLIN;
|
||||
pollfd.revents |= POLLHUP;
|
||||
}
|
||||
}
|
||||
|
||||
flags = get_poll_flags( sock, pollfd.revents ) & req->sockets[i].flags;
|
||||
if (flags)
|
||||
{
|
||||
req->iosb->status = STATUS_SUCCESS;
|
||||
output[i].flags = flags;
|
||||
output[i].status = sock_get_ntstatus( sock_error( sock->fd ) );
|
||||
}
|
||||
}
|
||||
|
||||
if (req->iosb->status != STATUS_PENDING)
|
||||
{
|
||||
req->iosb->out_data = output;
|
||||
req->iosb->out_size = count * sizeof(*output);
|
||||
async_terminate( req->async, STATUS_ALERTED );
|
||||
}
|
||||
|
||||
for (i = 0; i < req->count; ++i)
|
||||
sock_reselect( req->sockets[i].sock );
|
||||
set_error( STATUS_PENDING );
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LINUX_RTNETLINK_H
|
||||
|
||||
/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
|
||||
|
@ -2201,3 +2480,24 @@ DECL_HANDLER(recv_socket)
|
|||
}
|
||||
release_object( sock );
|
||||
}
|
||||
|
||||
DECL_HANDLER(poll_socket)
|
||||
{
|
||||
struct sock *sock = (struct sock *)get_handle_obj( current->process, req->async.handle, 0, &sock_ops );
|
||||
const struct poll_socket_input *input = get_req_data();
|
||||
struct async *async;
|
||||
unsigned int count;
|
||||
|
||||
if (!sock) return;
|
||||
|
||||
count = get_req_data_size() / sizeof(*input);
|
||||
|
||||
if ((async = create_request_async( sock->fd, get_fd_comp_flags( sock->fd ), &req->async )))
|
||||
{
|
||||
reply->wait = async_handoff( async, poll_socket( sock, async, req->timeout, count, input ), NULL, 0 );
|
||||
reply->options = get_fd_options( sock->fd );
|
||||
release_object( async );
|
||||
}
|
||||
|
||||
release_object( sock );
|
||||
}
|
||||
|
|
|
@ -1372,6 +1372,38 @@ static void dump_varargs_handle_infos( const char *prefix, data_size_t size )
|
|||
fputc( '}', stderr );
|
||||
}
|
||||
|
||||
static void dump_varargs_poll_socket_input( const char *prefix, data_size_t size )
|
||||
{
|
||||
const struct poll_socket_input *input;
|
||||
|
||||
fprintf( stderr, "%s{", prefix );
|
||||
while (size >= sizeof(*input))
|
||||
{
|
||||
input = cur_data;
|
||||
fprintf( stderr, "{socket=%04x,flags=%08x}", input->socket, input->flags );
|
||||
size -= sizeof(*input);
|
||||
remove_data( sizeof(*input) );
|
||||
if (size) fputc( ',', stderr );
|
||||
}
|
||||
fputc( '}', stderr );
|
||||
}
|
||||
|
||||
static void dump_varargs_poll_socket_output( const char *prefix, data_size_t size )
|
||||
{
|
||||
const struct poll_socket_output *output;
|
||||
|
||||
fprintf( stderr, "%s{", prefix );
|
||||
while (size >= sizeof(*output))
|
||||
{
|
||||
output = cur_data;
|
||||
fprintf( stderr, "{flags=%08x,status=%s}", output->flags, get_status_name( output->status ) );
|
||||
size -= sizeof(*output);
|
||||
remove_data( sizeof(*output) );
|
||||
if (size) fputc( ',', stderr );
|
||||
}
|
||||
fputc( '}', stderr );
|
||||
}
|
||||
|
||||
typedef void (*dump_func)( const void *req );
|
||||
|
||||
/* Everything below this line is generated automatically by tools/make_requests */
|
||||
|
@ -2107,6 +2139,20 @@ static void dump_recv_socket_reply( const struct recv_socket_reply *req )
|
|||
fprintf( stderr, ", options=%08x", req->options );
|
||||
}
|
||||
|
||||
static void dump_poll_socket_request( const struct poll_socket_request *req )
|
||||
{
|
||||
dump_async_data( " async=", &req->async );
|
||||
dump_timeout( ", timeout=", &req->timeout );
|
||||
dump_varargs_poll_socket_input( ", sockets=", cur_size );
|
||||
}
|
||||
|
||||
static void dump_poll_socket_reply( const struct poll_socket_reply *req )
|
||||
{
|
||||
fprintf( stderr, " wait=%04x", req->wait );
|
||||
fprintf( stderr, ", options=%08x", req->options );
|
||||
dump_varargs_poll_socket_output( ", sockets=", cur_size );
|
||||
}
|
||||
|
||||
static void dump_get_next_console_request_request( const struct get_next_console_request_request *req )
|
||||
{
|
||||
fprintf( stderr, " handle=%04x", req->handle );
|
||||
|
@ -4555,6 +4601,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = {
|
|||
(dump_func)dump_enable_socket_event_request,
|
||||
(dump_func)dump_set_socket_deferred_request,
|
||||
(dump_func)dump_recv_socket_request,
|
||||
(dump_func)dump_poll_socket_request,
|
||||
(dump_func)dump_get_next_console_request_request,
|
||||
(dump_func)dump_read_directory_changes_request,
|
||||
(dump_func)dump_read_change_request,
|
||||
|
@ -4833,6 +4880,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = {
|
|||
NULL,
|
||||
NULL,
|
||||
(dump_func)dump_recv_socket_reply,
|
||||
(dump_func)dump_poll_socket_reply,
|
||||
(dump_func)dump_get_next_console_request_reply,
|
||||
NULL,
|
||||
(dump_func)dump_read_change_reply,
|
||||
|
@ -5111,6 +5159,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = {
|
|||
"enable_socket_event",
|
||||
"set_socket_deferred",
|
||||
"recv_socket",
|
||||
"poll_socket",
|
||||
"get_next_console_request",
|
||||
"read_directory_changes",
|
||||
"read_change",
|
||||
|
|
Loading…
Reference in a new issue