Handle ENOTSOCK properly

git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/trunk@282 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2010-08-03 02:46:27 +00:00
parent 5df12e7eb2
commit cc1c0f78fd
9 changed files with 120 additions and 14 deletions

View File

@ -1,3 +1,13 @@
2010-08-01 v0.9 r282
------------------------------------------------------------------------
* Set kevent.data = 1 for passive sockets that have at least one pending
connection.
(Credit to Julien Blache for finding and researching this bug)
* Fix various compilation errors under Solaris.
(Credit to Joakim Johansson for testing and providing patches)
2010-07-21 v0.8 r264
------------------------------------------------------------------------

View File

@ -1,5 +1,5 @@
program="libkqueue"
version="0.9pre"
version="0.9"
abi_major="0"
abi_minor="0"
abi_version="$abi_major.$abi_minor"

8
configure vendored
View File

@ -165,15 +165,15 @@ check_linker() {
# Appears to be a problem with GCC 4.0 and binutils
#
default_ld="$cc"
ldflags="-o $program.so.$abi_major.$abi_minor $ldflags"
ldflags="-shared -o $program.so.$abi_major.$abi_minor $ldflags"
# FIXME: port to solaris
if [ "$target" = "linux" ] ; then
ldflags="-shared $ldflags -Wl,-export-dynamic -Wl,-soname,$program.so.$abi_major"
ldflags="$ldflags -Wl,-export-dynamic -Wl,-soname,$program.so.$abi_major"
fi
if [ "$target" = "solaris" ] ; then
default_ld="/usr/ccs/bin/ld"
ldflags="-G $ldflags"
ldflags="$ldflags"
fi
finalize ld "$default_ld"

View File

@ -17,6 +17,8 @@
#include <stdlib.h>
#include <string.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "private.h"
@ -139,3 +141,31 @@ knote_events_pending(struct filter *filt)
return (res);
}
/*
* Test if a socket is active or passive.
*/
int
knote_get_socket_type(struct knote *kn)
{
socklen_t slen;
int i, lsock;
slen = sizeof(lsock);
lsock = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, &lsock, &slen);
if (i < 0) {
switch (errno) {
case ENOTSOCK: /* same as lsock = 0 */
return (0);
break;
default:
dbg_printf("getsockopt(3) failed: %s", strerror(errno));
return (-1);
}
} else {
if (lsock)
kn->flags |= KNFL_PASSIVE_SOCKET;
return (0);
}
}

View File

@ -61,11 +61,17 @@ struct kqueue;
struct kevent;
struct evfilt_data;
/*
* Flags used by knote->flags
*/
#define KNFL_PASSIVE_SOCKET (0x01) /* Socket is in listen(2) mode */
/* TODO: Make this a variable length structure and allow
each filter to add custom fields at the end.
*/
struct knote {
struct kevent kev;
int flags;
union {
int pfd; /* Used by timerfd */
int events; /* Used by socket */
@ -135,6 +141,7 @@ struct knote * knote_new(void);
void knote_free(struct filter *, struct knote *);
void knote_free_all(struct filter *);
void knote_insert(struct filter *, struct knote *);
int knote_get_socket_type(struct knote *);
/* TODO: these deal with the eventlist, should use a different prefix */
void knote_enqueue(struct filter *, struct knote *);

View File

@ -127,15 +127,22 @@ evfilt_socket_copyout(struct filter *filt,
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual socket error */
/* On return, data contains the number of bytes of protocol
data available to read.
*/
if (ioctl(dst->ident,
(dst->filter == EVFILT_READ) ? SIOCINQ : SIOCOUTQ,
&dst->data) < 0) {
/* race condition with socket close, so ignore this error */
dbg_puts("ioctl(2) of socket failed");
dst->data = 0;
if (kn->flags & KNFL_PASSIVE_SOCKET) {
/* On return, data contains the length of the
socket backlog. This is not available under Linux.
*/
dst->data = 1;
} else {
/* On return, data contains the number of bytes of protocol
data available to read.
*/
if (ioctl(dst->ident,
(dst->filter == EVFILT_READ) ? SIOCINQ : SIOCOUTQ,
&dst->data) < 0) {
/* race condition with socket close, so ignore this error */
dbg_puts("ioctl(2) of socket failed");
dst->data = 0;
}
}
if (kn->kev.flags & EV_DISPATCH) {
@ -159,6 +166,9 @@ evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
{
struct epoll_event ev;
if (knote_get_socket_type(kn) < 0)
return (-1);
/* Convert the kevent into an epoll_event */
if (kn->kev.filter == EVFILT_READ)
kn->data.events = EPOLLIN | EPOLLRDHUP;

View File

@ -91,6 +91,9 @@ evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
#if TODO
struct epoll_event ev;
if (knote_get_socket_type(kn) < 0)
return (-1);
/* Convert the kevent into an epoll_event */
if (kn->kev.filter == EVFILT_READ)
kn->kn_events = EPOLLIN | EPOLLRDHUP;

View File

@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <arpa/inet.h>
#include <sys/socket.h>
#include "common.h"
static int __thread kqfd;
@ -183,6 +185,46 @@ test_kevent_socket_oneshot(void)
kevent_socket_drain();
}
/*
* Test if the data field returns 1 when a listen(2) socket has
* a pending connection.
*/
void
test_kevent_socket_listen_backlog(void)
{
struct kevent kev;
struct sockaddr_in sain;
socklen_t sa_len = sizeof(sain);
int one = 1;
const short port = 14973;
int clnt, srvr;
/* Create a passive socket */
sain.sin_family = AF_INET;
sain.sin_port = htons(port);
if ((srvr = socket(PF_INET, SOCK_STREAM, 0)) < 0) abort();
if (setsockopt(srvr, SOL_SOCKET, SO_REUSEADDR,
(char *) &one, sizeof(one)) != 0) abort();
if (bind(srvr, (struct sockaddr *) &sain, sa_len) < 0) abort();
if (listen(srvr, 100) < 0) abort();
/* Watch for events on the socket */
test_no_kevents(kqfd);
kevent_add(kqfd, &kev, srvr, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, NULL);
test_no_kevents(kqfd);
/* Simulate a client connecting to the server */
sain.sin_family = AF_INET;
sain.sin_port = htons(port);
sain.sin_addr.s_addr = inet_addr("127.0.0.1");
if ((clnt = socket(AF_INET, SOCK_STREAM, 0)) < 0) abort();
if (connect(clnt, (struct sockaddr *) &sain, sa_len) < 0) abort();
/* Verify that data=1 */
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
test_no_kevents(kqfd);
}
#if HAVE_EV_DISPATCH
void
@ -284,6 +326,7 @@ test_evfilt_read(int _kqfd)
#if HAVE_EV_DISPATCH
test(kevent_socket_dispatch);
#endif
test(kevent_socket_listen_backlog);
test(kevent_socket_eof);
close(sockfd[0]);
close(sockfd[1]);

View File

@ -119,6 +119,9 @@ When an EVFILT_SIGNAL event is generated, the <code>data</code> field
is set to 1 regardless of how many times the signal was received by the process.
</li>
<li>
When an EVFILT_READ event occurs on a listening socket, the <code>data</code> field is set to 1 regardless of how many pending connections are available.
<!--
<li>
The EVFILT_PROC filter only supports the NOTE_EXIT flag, and the <code>ident</code> field must contain the PID of a child of the current process. This filter is only suitable as an event-driven replacement for the <code>wait(2)</code> family of system calls.