mirror of
https://gitlab.winehq.org/wine/wine-staging.git
synced 2024-11-21 16:46:54 -08:00
441 lines
15 KiB
Diff
441 lines
15 KiB
Diff
From aa5c2e5b4bf9716af3ea2065a3d3de10c840f59b Mon Sep 17 00:00:00 2001
|
|
From: "Erich E. Hoover" <erich.e.hoover@gmail.com>
|
|
Date: Mon, 18 Nov 2013 17:22:04 -0700
|
|
Subject: server: Implement an interface change notification object.
|
|
|
|
---
|
|
server/event.c | 13 +++
|
|
server/named_pipe.c | 13 ---
|
|
server/object.h | 1 +
|
|
server/sock.c | 289 ++++++++++++++++++++++++++++++++++++++++++++++++++-
|
|
4 files changed, 301 insertions(+), 15 deletions(-)
|
|
|
|
diff --git a/server/event.c b/server/event.c
|
|
index b8515af..e8a3888 100644
|
|
--- a/server/event.c
|
|
+++ b/server/event.c
|
|
@@ -124,6 +124,19 @@ struct event *create_event( struct directory *root, const struct unicode_str *na
|
|
return event;
|
|
}
|
|
|
|
+obj_handle_t alloc_wait_event( struct process *process )
|
|
+{
|
|
+ obj_handle_t handle = 0;
|
|
+ struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
|
|
+
|
|
+ if (event)
|
|
+ {
|
|
+ handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
|
|
+ release_object( event );
|
|
+ }
|
|
+ return handle;
|
|
+}
|
|
+
|
|
struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access )
|
|
{
|
|
return (struct event *)get_handle_obj( process, handle, access, &event_ops );
|
|
diff --git a/server/named_pipe.c b/server/named_pipe.c
|
|
index 4c85104..6ba2145 100644
|
|
--- a/server/named_pipe.c
|
|
+++ b/server/named_pipe.c
|
|
@@ -587,19 +587,6 @@ static enum server_fd_type pipe_client_get_fd_type( struct fd *fd )
|
|
return FD_TYPE_PIPE;
|
|
}
|
|
|
|
-static obj_handle_t alloc_wait_event( struct process *process )
|
|
-{
|
|
- obj_handle_t handle = 0;
|
|
- struct event *event = create_event( NULL, NULL, 0, 1, 0, NULL );
|
|
-
|
|
- if (event)
|
|
- {
|
|
- handle = alloc_handle( process, event, EVENT_ALL_ACCESS, 0 );
|
|
- release_object( event );
|
|
- }
|
|
- return handle;
|
|
-}
|
|
-
|
|
static obj_handle_t pipe_server_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
|
|
int blocking, const void *data, data_size_t size )
|
|
{
|
|
diff --git a/server/object.h b/server/object.h
|
|
index bb3ff21..bad162f 100644
|
|
--- a/server/object.h
|
|
+++ b/server/object.h
|
|
@@ -159,6 +159,7 @@ extern struct event *create_event( struct directory *root, const struct unicode_
|
|
const struct security_descriptor *sd );
|
|
extern struct keyed_event *create_keyed_event( struct directory *root, const struct unicode_str *name,
|
|
unsigned int attr, const struct security_descriptor *sd );
|
|
+extern obj_handle_t alloc_wait_event( struct process *process );
|
|
extern struct event *get_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
|
|
extern struct keyed_event *get_keyed_event_obj( struct process *process, obj_handle_t handle, unsigned int access );
|
|
extern void pulse_event( struct event *event );
|
|
diff --git a/server/sock.c b/server/sock.c
|
|
index 1a3a8f7..4e41b72 100644
|
|
--- a/server/sock.c
|
|
+++ b/server/sock.c
|
|
@@ -44,11 +44,17 @@
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
+#ifdef HAVE_LINUX_RTNETLINK_H
|
|
+# include <linux/rtnetlink.h>
|
|
+#endif
|
|
+
|
|
#include "ntstatus.h"
|
|
#define WIN32_NO_STATUS
|
|
#include "windef.h"
|
|
#include "winternl.h"
|
|
#include "winerror.h"
|
|
+#define USE_WS_PREFIX
|
|
+#include "winsock2.h"
|
|
|
|
#include "process.h"
|
|
#include "file.h"
|
|
@@ -107,8 +113,12 @@ struct sock
|
|
struct sock *deferred; /* socket that waits for a deferred accept */
|
|
struct async_queue *read_q; /* queue for asynchronous reads */
|
|
struct async_queue *write_q; /* queue for asynchronous writes */
|
|
+ struct async_queue *ifchange_q; /* queue for interface change notifications */
|
|
+ struct list ifchange_entry; /* entry in ifchange notification list */
|
|
};
|
|
|
|
+static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data );
|
|
+
|
|
static void sock_dump( struct object *obj, int verbose );
|
|
static int sock_signaled( struct object *obj, struct wait_queue_entry *entry );
|
|
static struct fd *sock_get_fd( struct object *obj );
|
|
@@ -117,6 +127,8 @@ static void sock_destroy( struct object *obj );
|
|
static int sock_get_poll_events( struct fd *fd );
|
|
static void sock_poll_event( struct fd *fd, int event );
|
|
static enum server_fd_type sock_get_fd_type( struct fd *fd );
|
|
+static obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async,
|
|
+ int blocking, const void *data, data_size_t size );
|
|
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count );
|
|
static void sock_reselect_async( struct fd *fd, struct async_queue *queue );
|
|
static void sock_cancel_async( struct fd *fd, struct process *process, struct thread *thread, client_ptr_t iosb );
|
|
@@ -151,12 +163,15 @@ static const struct fd_ops sock_fd_ops =
|
|
sock_poll_event, /* poll_event */
|
|
no_flush, /* flush */
|
|
sock_get_fd_type, /* get_fd_type */
|
|
- default_fd_ioctl, /* ioctl */
|
|
+ sock_ioctl, /* ioctl */
|
|
sock_queue_async, /* queue_async */
|
|
sock_reselect_async, /* reselect_async */
|
|
sock_cancel_async /* cancel_async */
|
|
};
|
|
|
|
+/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
|
|
+static struct object *ifchange_object = NULL;
|
|
+
|
|
|
|
/* Permutation of 0..FD_MAX_EVENTS - 1 representing the order in which
|
|
* we post messages if there are multiple events. Used to send
|
|
@@ -518,6 +533,39 @@ static enum server_fd_type sock_get_fd_type( struct fd *fd )
|
|
return FD_TYPE_SOCKET;
|
|
}
|
|
|
|
+obj_handle_t sock_ioctl( struct fd *fd, ioctl_code_t code, const async_data_t *async_data,
|
|
+ int blocking, const void *data, data_size_t size )
|
|
+{
|
|
+ struct sock *sock = get_fd_user( fd );
|
|
+ obj_handle_t wait_handle = 0;
|
|
+ async_data_t new_data;
|
|
+
|
|
+ assert( sock->obj.ops == &sock_ops );
|
|
+
|
|
+ if (blocking)
|
|
+ {
|
|
+ if (!(wait_handle = alloc_wait_event( current->process ))) return 0;
|
|
+ new_data = *async_data;
|
|
+ new_data.event = wait_handle;
|
|
+ async_data = &new_data;
|
|
+ }
|
|
+ switch(code)
|
|
+ {
|
|
+ case WS_SIO_ADDRESS_LIST_CHANGE:
|
|
+ if (sock_add_ifchange( sock, async_data ))
|
|
+ {
|
|
+ set_error( STATUS_PENDING );
|
|
+ return wait_handle;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ close_handle( current->process, wait_handle );
|
|
+ return default_fd_ioctl(fd, code, async_data, blocking, data, size);
|
|
+ }
|
|
+ close_handle( current->process, wait_handle );
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static void sock_queue_async( struct fd *fd, const async_data_t *data, int type, int count )
|
|
{
|
|
struct sock *sock = get_fd_user( fd );
|
|
@@ -587,11 +635,17 @@ static void sock_destroy( struct object *obj )
|
|
|
|
/* FIXME: special socket shutdown stuff? */
|
|
|
|
- if ( sock->deferred )
|
|
+ if (sock->deferred)
|
|
release_object( sock->deferred );
|
|
|
|
free_async_queue( sock->read_q );
|
|
free_async_queue( sock->write_q );
|
|
+ if (sock->ifchange_q)
|
|
+ {
|
|
+ free_async_queue( sock->ifchange_q );
|
|
+ list_remove( &sock->ifchange_entry );
|
|
+ release_object( ifchange_object );
|
|
+ }
|
|
if (sock->event) release_object( sock->event );
|
|
if (sock->fd)
|
|
{
|
|
@@ -618,6 +672,7 @@ static void init_sock(struct sock *sock)
|
|
sock->deferred = NULL;
|
|
sock->read_q = NULL;
|
|
sock->write_q = NULL;
|
|
+ sock->ifchange_q = NULL;
|
|
memset( sock->errors, 0, sizeof(sock->errors) );
|
|
}
|
|
|
|
@@ -906,6 +961,236 @@ static void sock_set_error(void)
|
|
set_error( sock_get_ntstatus( errno ) );
|
|
}
|
|
|
|
+static void ifchange_dump( struct object *obj, int verbose );
|
|
+static struct fd *ifchange_get_fd( struct object *obj );
|
|
+static void ifchange_destroy( struct object *obj );
|
|
+
|
|
+static int ifchange_get_poll_events( struct fd *fd );
|
|
+static void ifchange_poll_event( struct fd *fd, int event );
|
|
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue );
|
|
+
|
|
+struct ifchange
|
|
+{
|
|
+ struct object obj; /* object header */
|
|
+ struct fd *fd; /* interface change file descriptor */
|
|
+ struct list sockets; /* list of sockets to send interface change notifications */
|
|
+};
|
|
+
|
|
+static const struct object_ops ifchange_ops =
|
|
+{
|
|
+ sizeof(struct ifchange), /* size */
|
|
+ ifchange_dump, /* dump */
|
|
+ no_get_type, /* get_type */
|
|
+ add_queue, /* add_queue */
|
|
+ NULL, /* remove_queue */
|
|
+ NULL, /* signaled */
|
|
+ no_satisfied, /* satisfied */
|
|
+ no_signal, /* signal */
|
|
+ ifchange_get_fd, /* get_fd */
|
|
+ default_fd_map_access, /* map_access */
|
|
+ default_get_sd, /* get_sd */
|
|
+ default_set_sd, /* set_sd */
|
|
+ no_lookup_name, /* lookup_name */
|
|
+ no_open_file, /* open_file */
|
|
+ no_close_handle, /* close_handle */
|
|
+ ifchange_destroy /* destroy */
|
|
+};
|
|
+
|
|
+static const struct fd_ops ifchange_fd_ops =
|
|
+{
|
|
+ ifchange_get_poll_events, /* get_poll_events */
|
|
+ ifchange_poll_event, /* poll_event */
|
|
+ NULL, /* flush */
|
|
+ NULL, /* get_fd_type */
|
|
+ NULL, /* ioctl */
|
|
+ NULL, /* queue_async */
|
|
+ ifchange_reselect_async, /* reselect_async */
|
|
+ NULL /* cancel_async */
|
|
+};
|
|
+
|
|
+static int init_ifchange( struct ifchange *ifchange )
|
|
+{
|
|
+#if defined(NETLINK_ROUTE)
|
|
+ struct sockaddr_nl addr;
|
|
+ int unix_fd;
|
|
+
|
|
+ list_init( &ifchange->sockets );
|
|
+ unix_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
|
|
+ if (unix_fd == -1)
|
|
+ {
|
|
+ sock_set_error();
|
|
+ return 0;
|
|
+ }
|
|
+ fcntl( unix_fd, F_SETFL, O_NONBLOCK ); /* make socket nonblocking */
|
|
+ memset( &addr, 0, sizeof(addr) );
|
|
+ addr.nl_family = AF_NETLINK;
|
|
+ addr.nl_groups = RTMGRP_IPV4_IFADDR;
|
|
+ /* bind the socket to the special netlink kernel interface */
|
|
+ if (bind( unix_fd, (struct sockaddr *)&addr, sizeof(addr) ) == -1)
|
|
+ {
|
|
+ sock_set_error();
|
|
+ close( unix_fd );
|
|
+ return 0;
|
|
+ }
|
|
+ if (!(ifchange->fd = create_anonymous_fd( &ifchange_fd_ops, unix_fd, &ifchange->obj, 0 )))
|
|
+ {
|
|
+ close( unix_fd );
|
|
+ return 0;
|
|
+ }
|
|
+ /* enable read wakeup on the file descriptor */
|
|
+ set_fd_events( ifchange->fd, POLLIN );
|
|
+ return 1;
|
|
+#else
|
|
+ fprintf(stderr, "Interface change notification is not supported on this platform.\n");
|
|
+ set_error( STATUS_NOT_SUPPORTED );
|
|
+ return 0;
|
|
+#endif
|
|
+}
|
|
+
|
|
+/* create a new ifchange notifier or, if one already exists, reuse the existing one */
|
|
+static struct object *create_ifchange( void )
|
|
+{
|
|
+ struct ifchange *ifchange;
|
|
+
|
|
+ /* we only need one of these interface notification objects, all of the sockets dependent upon
|
|
+ * it will wake up when a notification event occurs */
|
|
+ if (ifchange_object)
|
|
+ return grab_object( ifchange_object );
|
|
+ if (!(ifchange = alloc_object( &ifchange_ops )))
|
|
+ return NULL;
|
|
+ if (!init_ifchange( ifchange ))
|
|
+ {
|
|
+ release_object( ifchange );
|
|
+ return NULL;
|
|
+ }
|
|
+ ifchange_object = &ifchange->obj;
|
|
+ return ifchange_object;
|
|
+}
|
|
+
|
|
+/* add a socket to the interface change notification's list of sockets */
|
|
+void ifchange_add_sock( struct object *obj, struct sock *sock )
|
|
+{
|
|
+ struct ifchange *ifchange = (struct ifchange *)obj;
|
|
+
|
|
+ list_add_tail( &ifchange->sockets, &sock->ifchange_entry );
|
|
+}
|
|
+
|
|
+/* wake up an ifchange notification queue for a socket and decrement the ifchange object refcount */
|
|
+void sock_ifchange_wake_up( struct sock *sock, unsigned int status )
|
|
+{
|
|
+ assert( sock->ifchange_q );
|
|
+ async_wake_up( sock->ifchange_q, status );
|
|
+ free_async_queue( sock->ifchange_q );
|
|
+ sock->ifchange_q = NULL;
|
|
+ list_remove( &sock->ifchange_entry );
|
|
+ release_object( ifchange_object );
|
|
+}
|
|
+
|
|
+/* add interface change notification to a socket */
|
|
+int sock_add_ifchange( struct sock *sock, const async_data_t *async_data )
|
|
+{
|
|
+ struct object *ifchange = ifchange_object;
|
|
+ struct async *async;
|
|
+ struct fd *fd;
|
|
+
|
|
+ if (!sock->ifchange_q)
|
|
+ {
|
|
+ /* associate this socket with the interface change object */
|
|
+ ifchange = create_ifchange();
|
|
+ if (!ifchange) return FALSE;
|
|
+ ifchange_add_sock( ifchange, sock ); /* add this socket to the change notification list */
|
|
+ if (!(fd = ifchange_get_fd( ifchange ))) goto fail;
|
|
+ sock->ifchange_q = create_async_queue( fd );
|
|
+ release_object( fd );
|
|
+ if (!sock->ifchange_q) goto fail;
|
|
+ }
|
|
+ if (!(async = create_async( current, sock->ifchange_q, async_data ))) goto fail;
|
|
+ release_object( async );
|
|
+ return TRUE;
|
|
+
|
|
+fail:
|
|
+ free_async_queue( sock->ifchange_q );
|
|
+ sock->ifchange_q = NULL;
|
|
+ release_object( ifchange );
|
|
+ return FALSE;
|
|
+}
|
|
+
|
|
+static void ifchange_dump( struct object *obj, int verbose )
|
|
+{
|
|
+ assert( obj->ops == &ifchange_ops );
|
|
+ printf( "ifchange\n" );
|
|
+}
|
|
+
|
|
+static struct fd *ifchange_get_fd( struct object *obj )
|
|
+{
|
|
+ struct ifchange *ifchange = (struct ifchange *)obj;
|
|
+ return (struct fd *)grab_object( ifchange->fd );
|
|
+}
|
|
+
|
|
+static void ifchange_destroy( struct object *obj )
|
|
+{
|
|
+ struct ifchange *ifchange = (struct ifchange *)obj;
|
|
+ assert( obj->ops == &ifchange_ops );
|
|
+
|
|
+ /* reset the global ifchange object so that it will be recreated if it is needed again */
|
|
+ ifchange_object = NULL;
|
|
+ /* shut the socket down to force pending poll() calls in the client to return */
|
|
+ shutdown( get_unix_fd(ifchange->fd), SHUT_RDWR );
|
|
+ release_object( ifchange->fd );
|
|
+}
|
|
+
|
|
+static int ifchange_get_poll_events( struct fd *fd )
|
|
+{
|
|
+ return POLLIN;
|
|
+}
|
|
+
|
|
+/* wake up all the sockets waiting for a change notification event */
|
|
+static void ifchange_wake_up( struct object *obj, unsigned int status )
|
|
+{
|
|
+ struct ifchange *ifchange = (struct ifchange *) obj;
|
|
+ struct list *ptr, *next;
|
|
+
|
|
+ assert( obj->ops == &ifchange_ops );
|
|
+ LIST_FOR_EACH_SAFE( ptr, next, &ifchange->sockets )
|
|
+ {
|
|
+ struct sock *sock = LIST_ENTRY( ptr, struct sock, ifchange_entry );
|
|
+
|
|
+ sock_ifchange_wake_up( sock, status );
|
|
+ }
|
|
+}
|
|
+
|
|
+static void ifchange_poll_event( struct fd *fd, int event )
|
|
+{
|
|
+ struct object *ifchange = get_fd_user( fd );
|
|
+ int r, unix_fd, wakeup = FALSE;
|
|
+ char buffer[0x1000];
|
|
+
|
|
+ unix_fd = get_unix_fd( fd );
|
|
+ r = recv( unix_fd, buffer, sizeof(buffer), 0 );
|
|
+ if (r < 0)
|
|
+ {
|
|
+ fprintf(stderr,"ifchange_poll_event(): ifchange read failed!\n");
|
|
+ return;
|
|
+ }
|
|
+ else if (r != 0)
|
|
+ {
|
|
+#if defined(NETLINK_ROUTE)
|
|
+ struct nlmsghdr *nlh;
|
|
+
|
|
+ nlh = (struct nlmsghdr*) buffer;
|
|
+ if (NLMSG_OK(nlh, r) && (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR))
|
|
+ wakeup = TRUE;
|
|
+#endif
|
|
+ }
|
|
+ if (wakeup)
|
|
+ ifchange_wake_up( ifchange, STATUS_SUCCESS );
|
|
+}
|
|
+
|
|
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue )
|
|
+{
|
|
+ /* do nothing, this object is about to disappear */
|
|
+}
|
|
+
|
|
/* create a socket */
|
|
DECL_HANDLER(create_socket)
|
|
{
|
|
--
|
|
1.7.9.5
|
|
|