Erich E. Hoover : server: Implement the interface change notification object.

Alexandre Julliard julliard at wine.codeweavers.com
Tue Feb 10 10:46:04 CST 2015


Module: wine
Branch: master
Commit: 67c47e401ba7bc00b0b178588ff7fbfccc4aef01
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=67c47e401ba7bc00b0b178588ff7fbfccc4aef01

Author: Erich E. Hoover <erich.e.hoover at wine-staging.com>
Date:   Tue May  6 08:49:52 2014 -0600

server: Implement the interface change notification object.

---

 server/sock.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 199 insertions(+), 3 deletions(-)

diff --git a/server/sock.c b/server/sock.c
index 748159f..f3bab85 100644
--- a/server/sock.c
+++ b/server/sock.c
@@ -46,6 +46,10 @@
 #endif
 #include <time.h>
 #include <unistd.h>
+#include <limits.h>
+#ifdef HAVE_LINUX_RTNETLINK_H
+# include <linux/rtnetlink.h>
+#endif
 
 #include "ntstatus.h"
 #define WIN32_NO_STATUS
@@ -979,16 +983,208 @@ static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data
     return 1;
 }
 
-/* stub ifchange object */
-static struct object *get_ifchange( void )
+#ifdef HAVE_LINUX_RTNETLINK_H
+
+/* only keep one ifchange object around, all sockets waiting for wakeups will look to it */
+static struct object *ifchange_object;
+
+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 void ifchange_dump( struct object *obj, int verbose )
+{
+    assert( obj->ops == &ifchange_ops );
+    fprintf( stderr, "Interface change\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 );
+
+    release_object( ifchange->fd );
+
+    /* reset the global ifchange object so that it will be recreated if it is needed again */
+    assert( obj == ifchange_object );
+    ifchange_object = NULL;
+}
+
+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 );
+    assert( obj == ifchange_object );
+
+    LIST_FOR_EACH_SAFE( ptr, next, &ifchange->sockets )
+    {
+        struct sock *sock = LIST_ENTRY( ptr, struct sock, ifchange_entry );
+
+        assert( sock->ifchange_q );
+        async_wake_up( sock->ifchange_q, status ); /* issue ifchange notification for the socket */
+        sock_destroy_ifchange_q( sock ); /* remove socket from list and decrement ifchange refcount */
+    }
+}
+
+static void ifchange_poll_event( struct fd *fd, int event )
+{
+    struct object *ifchange = get_fd_user( fd );
+    unsigned int status = STATUS_PENDING;
+    char buffer[PIPE_BUF];
+    int r;
+
+    r = recv( get_unix_fd(fd), buffer, sizeof(buffer), MSG_DONTWAIT );
+    if (r < 0)
+    {
+        if (errno == EWOULDBLOCK || errno == EAGAIN)
+            return; /* retry when poll() says the socket is ready */
+        status = sock_get_ntstatus( errno );
+    }
+    else if (r > 0)
+    {
+        struct nlmsghdr *nlh;
+
+        for (nlh = (struct nlmsghdr *)buffer; NLMSG_OK(nlh, r); nlh = NLMSG_NEXT(nlh, r))
+        {
+            if (nlh->nlmsg_type == NLMSG_DONE)
+                break;
+            if (nlh->nlmsg_type == RTM_NEWADDR || nlh->nlmsg_type == RTM_DELADDR)
+                status = STATUS_SUCCESS;
+        }
+    }
+    else status = STATUS_CANCELLED;
+
+    if (status != STATUS_PENDING) ifchange_wake_up( ifchange, status );
+}
+
+static void ifchange_reselect_async( struct fd *fd, struct async_queue *queue )
 {
+    /* do nothing, this object is about to disappear */
+}
+
+#endif
+
+/* we only need one of these interface notification objects, all of the sockets dependent upon
+ * it will wake up when a notification event occurs */
+ static struct object *get_ifchange( void )
+ {
+#ifdef HAVE_LINUX_RTNETLINK_H
+    struct ifchange *ifchange;
+    struct sockaddr_nl addr;
+    int unix_fd;
+
+    if (ifchange_object)
+    {
+        /* increment the refcount for each socket that uses the ifchange object */
+        return grab_object( ifchange_object );
+    }
+
+    /* create the socket we need for processing interface change notifications */
+    unix_fd = socket( PF_NETLINK, SOCK_RAW, NETLINK_ROUTE );
+    if (unix_fd == -1)
+    {
+        sock_set_error();
+        return NULL;
+    }
+    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)
+    {
+        close( unix_fd );
+        sock_set_error();
+        return NULL;
+    }
+    if (!(ifchange = alloc_object( &ifchange_ops )))
+    {
+        close( unix_fd );
+        set_error( STATUS_NO_MEMORY );
+        return NULL;
+    }
+    list_init( &ifchange->sockets );
+    if (!(ifchange->fd = create_anonymous_fd( &ifchange_fd_ops, unix_fd, &ifchange->obj, 0 )))
+    {
+        release_object( ifchange );
+        set_error( STATUS_NO_MEMORY );
+        return NULL;
+    }
+    set_fd_events( ifchange->fd, POLLIN ); /* enable read wakeup on the file descriptor */
+
+    /* the ifchange object is now successfully configured */
+    ifchange_object = &ifchange->obj;
+    return &ifchange->obj;
+#else
     set_error( STATUS_NOT_SUPPORTED );
     return NULL;
+#endif
 }
 
-/* stub ifchange add socket to list */
+/* add the socket to the interface change notification list */
 static void ifchange_add_sock( struct object *obj, struct sock *sock )
 {
+#ifdef HAVE_LINUX_RTNETLINK_H
+    struct ifchange *ifchange = (struct ifchange *)obj;
+
+    list_add_tail( &ifchange->sockets, &sock->ifchange_entry );
+#endif
 }
 
 /* create a new ifchange queue for a specific socket or, if one already exists, reuse the existing one */




More information about the wine-cvs mailing list