server: Implement the interface change notification object.
This commit is contained in:
parent
b121f27b72
commit
67c47e401b
206
server/sock.c
206
server/sock.c
|
@ -46,6 +46,10 @@
|
||||||
#endif
|
#endif
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#ifdef HAVE_LINUX_RTNETLINK_H
|
||||||
|
# include <linux/rtnetlink.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ntstatus.h"
|
#include "ntstatus.h"
|
||||||
#define WIN32_NO_STATUS
|
#define WIN32_NO_STATUS
|
||||||
|
@ -979,16 +983,208 @@ static int sock_add_ifchange( struct sock *sock, const async_data_t *async_data
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stub ifchange object */
|
#ifdef HAVE_LINUX_RTNETLINK_H
|
||||||
static struct object *get_ifchange( void )
|
|
||||||
|
/* 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
|
||||||
{
|
{
|
||||||
set_error( STATUS_NOT_SUPPORTED );
|
struct object obj; /* object header */
|
||||||
return NULL;
|
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" );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* stub ifchange add socket to list */
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
/* add the socket to the interface change notification list */
|
||||||
static void ifchange_add_sock( struct object *obj, struct sock *sock )
|
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 */
|
/* create a new ifchange queue for a specific socket or, if one already exists, reuse the existing one */
|
||||||
|
|
Loading…
Reference in New Issue