Skip to content

Commit

Permalink
Merge pull request #49 from ghostmansd/socket_errors_support
Browse files Browse the repository at this point in the history
Socket errors support
  • Loading branch information
arr2036 authored Jan 11, 2019
2 parents 28374d6 + 6d9638c commit f641903
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 60 deletions.
2 changes: 1 addition & 1 deletion src/common/kevent.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ kevent_copyin_one(struct kqueue *kq, const struct kevent *src)
memcpy(&kn->kev, src, sizeof(kn->kev));
kn->kev.flags &= ~EV_ENABLE;
kn->kev.flags |= EV_ADD;//FIXME why?
kn->kn_kq = kq;
kn->kn_kq = kq;
assert(filt->kn_create);
if (filt->kn_create(filt, kn) < 0) {
knote_release(kn);
Expand Down
20 changes: 15 additions & 5 deletions src/common/private.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,24 @@ struct eventfd {
/*
* Flags used by knote->kn_flags
*/
#define KNFL_PASSIVE_SOCKET (0x01) /* Socket is in listen(2) mode */
#define KNFL_REGULAR_FILE (0x02) /* File descriptor is a regular file */
#define KNFL_STREAM_SOCKET (0x03) /* File descriptor is a stream socket */
#define KNFL_KNOTE_DELETED (0x10) /* The knote object is no longer valid */
#define KNFL_FILE (1U << 0U)
#define KNFL_PIPE (1U << 1U)
#define KNFL_BLOCKDEV (1U << 2U)
#define KNFL_CHARDEV (1U << 3U)
#define KNFL_SOCKET_PASSIVE (1U << 4U)
#define KNFL_SOCKET_STREAM (1U << 5U)
#define KNFL_SOCKET_DGRAM (1U << 6U)
#define KNFL_SOCKET_RDM (1U << 7U)
#define KNFL_SOCKET_SEQPACKET (1U << 8U)
#define KNFL_KNOTE_DELETED (1U << 31U)
#define KNFL_SOCKET (KNFL_SOCKET_STREAM |\
KNFL_SOCKET_DGRAM |\
KNFL_SOCKET_RDM |\
KNFL_SOCKET_SEQPACKET)

struct knote {
struct kevent kev;
int kn_flags;
unsigned int kn_flags;
union {
/* OLD */
int pfd; /* Used by timerfd */
Expand Down
100 changes: 76 additions & 24 deletions src/linux/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -552,32 +552,94 @@ linux_get_descriptor_type(struct knote *kn)
{
socklen_t slen;
struct stat sb;
int i, lsock, stype;
int ret, lsock, stype;
socklen_t out_len;
const int fd = (int)kn->kev.ident;

/*
* Test if the descriptor is a socket.
* Determine the actual descriptor type.
*/
if (fstat(kn->kev.ident, &sb) < 0) {
if (fstat(fd, &sb) < 0) {
dbg_perror("fstat(2)");
return (-1);
}
if (S_ISREG(sb.st_mode)) {
kn->kn_flags |= KNFL_REGULAR_FILE;
dbg_printf("fd %d is a regular file\n", (int)kn->kev.ident);
return (0);
switch (sb.st_mode & S_IFMT) {
case S_IFREG:
dbg_printf("fd %d is a regular file\n", fd);
kn->kn_flags |= KNFL_FILE;
break;

case S_IFIFO:
dbg_printf("fd %d is a pipe\n", fd);
kn->kn_flags |= KNFL_PIPE;
break;

case S_IFBLK:
dbg_printf("fd %d is a block device\n", fd);
kn->kn_flags |= KNFL_BLOCKDEV;
break;

case S_IFCHR:
dbg_printf("fd %d is a character device\n", fd);
kn->kn_flags |= KNFL_CHARDEV;
break;

case S_IFSOCK:
dbg_printf("fd %d is a socket\n", fd);
break; /* deferred type determination */

default:
errno = EBADF;
dbg_perror("unknown fd type");
return -1;
}

/*
* Test if the socket is active or passive.
*/
if (! S_ISSOCK(sb.st_mode))
if (!S_ISSOCK(sb.st_mode))
return (0);

/*
* Determine socket type.
*/
slen = sizeof(stype);
stype = 0;
ret = getsockopt(fd, SOL_SOCKET, SO_TYPE, &stype, &slen);
if (ret < 0) {
dbg_perror("getsockopt(3)");
return (-1);
}
switch (stype) {
case SOCK_STREAM:
dbg_printf("fd %d is a stream socket\n", fd);
kn->kn_flags |= KNFL_SOCKET_STREAM;
break;

case SOCK_DGRAM:
dbg_printf("fd %d is a datagram socket\n", fd);
kn->kn_flags |= KNFL_SOCKET_DGRAM;
break;

case SOCK_RDM:
dbg_printf("fd %d is a reliable datagram socket\n", fd);
kn->kn_flags |= KNFL_SOCKET_RDM;
break;

case SOCK_SEQPACKET:
dbg_printf("fd %d is a sequenced and reliable datagram socket\n", fd);
kn->kn_flags |= KNFL_SOCKET_SEQPACKET;
break;

default:
errno = EBADF;
dbg_perror("unknown socket type");
return (-1);
}
slen = sizeof(lsock);
lsock = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, (char *) &lsock, &slen);
if (i < 0) {
ret = getsockopt(fd, SOL_SOCKET, SO_ACCEPTCONN, &lsock, &slen);
if (ret < 0) {
switch (errno) {
case ENOTSOCK: /* same as lsock = 0 */
break;
Expand All @@ -587,7 +649,7 @@ linux_get_descriptor_type(struct knote *kn)
}
} else {
if (lsock)
kn->kn_flags |= KNFL_PASSIVE_SOCKET;
kn->kn_flags |= KNFL_SOCKET_PASSIVE;
}

/*
Expand All @@ -597,8 +659,8 @@ linux_get_descriptor_type(struct knote *kn)
* Looking at SO_GET_FILTER is a good way of doing this.
*/
out_len = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_GET_FILTER, NULL, &out_len);
if (i < 0) {
ret = getsockopt(fd, SOL_SOCKET, SO_GET_FILTER, NULL, &out_len);
if (ret < 0) {
switch (errno) {
case ENOTSOCK: /* same as lsock = 0 */
break;
Expand All @@ -608,18 +670,8 @@ linux_get_descriptor_type(struct knote *kn)
}
} else {
if (out_len)
kn->kn_flags |= KNFL_PASSIVE_SOCKET;
}

slen = sizeof(stype);
stype = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_TYPE, (char *) &lsock, &slen);
if (i < 0) {
dbg_perror("getsockopt(3)");
return (-1);
kn->kn_flags |= KNFL_SOCKET_PASSIVE;
}
if (stype == SOCK_STREAM)
kn->kn_flags |= KNFL_STREAM_SOCKET;

return (0);
}
Expand Down
25 changes: 16 additions & 9 deletions src/linux/read.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,13 @@ get_eof_offset(int fd)
int
evfilt_read_copyout(struct kevent *dst, struct knote *src, void *ptr)
{
int ret;
int serr;
socklen_t slen = sizeof(serr);
struct epoll_event * const ev = (struct epoll_event *) ptr;

/* Special case: for regular files, return the offset from current position to end of file */
if (src->kn_flags & KNFL_REGULAR_FILE) {
if (src->kn_flags & KNFL_FILE) {
memcpy(dst, &src->kev, sizeof(*dst));
dst->data = get_eof_offset(src->kev.ident);

Expand Down Expand Up @@ -113,10 +116,14 @@ evfilt_read_copyout(struct kevent *dst, struct knote *src, void *ptr)
if (ev->events & EPOLLHUP)
dst->flags |= EV_EOF;
#endif
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual socket error */
if (ev->events & EPOLLERR) {
if (src->kn_flags & KNFL_SOCKET) {
ret = getsockopt(src->kev.ident, SOL_SOCKET, SO_ERROR, &serr, &slen);
dst->fflags = ((ret < 0) ? errno : serr);
} else { dst->fflags = EIO; }
}

if (src->kn_flags & KNFL_PASSIVE_SOCKET) {
if (src->kn_flags & KNFL_SOCKET_PASSIVE) {
/* On return, data contains the length of the
socket backlog. This is not available under Linux.
*/
Expand All @@ -132,7 +139,7 @@ evfilt_read_copyout(struct kevent *dst, struct knote *src, void *ptr)
dst->data = 0;
} else {
dst->data = i;
if (dst->data == 0 && src->kn_flags & KNFL_STREAM_SOCKET)
if (dst->data == 0 && src->kn_flags & KNFL_SOCKET_STREAM)
dst->flags |= EV_EOF;
}
}
Expand Down Expand Up @@ -164,7 +171,7 @@ evfilt_read_knote_create(struct filter *filt, struct knote *kn)
ev.data.ptr = kn;

/* Special case: for regular files, add a surrogate eventfd that is always readable */
if (kn->kn_flags & KNFL_REGULAR_FILE) {
if (kn->kn_flags & KNFL_FILE) {
int evfd;

kn->kn_epollfd = filter_epfd(filt);
Expand Down Expand Up @@ -210,7 +217,7 @@ evfilt_read_knote_delete(struct filter *filt, struct knote *kn)
if (kn->kev.flags & EV_DISABLE)
return (0);

if ((kn->kn_flags & KNFL_REGULAR_FILE) && (kn->kdata.kn_eventfd != -1)) {
if ((kn->kn_flags & KNFL_FILE) && (kn->kdata.kn_eventfd != -1)) {
if (kn->kn_registered && epoll_ctl(kn->kn_epollfd, EPOLL_CTL_DEL, kn->kdata.kn_eventfd, NULL) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);
Expand All @@ -236,7 +243,7 @@ evfilt_read_knote_enable(struct filter *filt, struct knote *kn)
ev.events = kn->data.events;
ev.data.ptr = kn;

if (kn->kn_flags & KNFL_REGULAR_FILE) {
if (kn->kn_flags & KNFL_FILE) {
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_ADD, kn->kdata.kn_eventfd, &ev) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);
Expand All @@ -254,7 +261,7 @@ evfilt_read_knote_enable(struct filter *filt, struct knote *kn)
int
evfilt_read_knote_disable(struct filter *filt, struct knote *kn)
{
if (kn->kn_flags & KNFL_REGULAR_FILE) {
if (kn->kn_flags & KNFL_FILE) {
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_DEL, kn->kdata.kn_eventfd, NULL) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);
Expand Down
40 changes: 24 additions & 16 deletions src/linux/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@
#include "private.h"

int
evfilt_socket_copyout(struct kevent *dst, struct knote *src, void *ptr)
evfilt_write_copyout(struct kevent *dst, struct knote *src, void *ptr)
{
int ret;
int serr;
socklen_t slen = sizeof(serr);
struct epoll_event * const ev = (struct epoll_event *) ptr;

epoll_event_dump(ev);
Expand All @@ -44,8 +47,12 @@ evfilt_socket_copyout(struct kevent *dst, struct knote *src, void *ptr)
if (ev->events & EPOLLHUP)
dst->flags |= EV_EOF;
#endif
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual socket error */
if (ev->events & EPOLLERR) {
if (src->kn_flags & KNFL_SOCKET) {
ret = getsockopt(src->kev.ident, SOL_SOCKET, SO_ERROR, &serr, &slen);
dst->fflags = ((ret < 0) ? errno : serr);
} else { dst->fflags = EIO; }
}

/* On return, data contains the the amount of space remaining in the write buffer */
if (ioctl(dst->ident, SIOCOUTQ, &dst->data) < 0) {
Expand All @@ -58,16 +65,17 @@ evfilt_socket_copyout(struct kevent *dst, struct knote *src, void *ptr)
}

int
evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
evfilt_write_knote_create(struct filter *filt, struct knote *kn)
{
struct epoll_event ev;

if (linux_get_descriptor_type(kn) < 0)
return (-1);

/* TODO: return EBADF? */
if (kn->kn_flags & KNFL_REGULAR_FILE)
if (kn->kn_flags & KNFL_FILE) {
errno = EBADF;
return (-1);
}

/* Convert the kevent into an epoll_event */
kn->data.events = EPOLLOUT;
Expand All @@ -84,7 +92,7 @@ evfilt_socket_knote_create(struct filter *filt, struct knote *kn)
}

int
evfilt_socket_knote_modify(struct filter *filt, struct knote *kn,
evfilt_write_knote_modify(struct filter *filt, struct knote *kn,
const struct kevent *kev)
{
(void) filt;
Expand All @@ -94,7 +102,7 @@ evfilt_socket_knote_modify(struct filter *filt, struct knote *kn,
}

int
evfilt_socket_knote_delete(struct filter *filt, struct knote *kn)
evfilt_write_knote_delete(struct filter *filt, struct knote *kn)
{
if (kn->kev.flags & EV_DISABLE)
return (0);
Expand All @@ -103,7 +111,7 @@ evfilt_socket_knote_delete(struct filter *filt, struct knote *kn)
}

int
evfilt_socket_knote_enable(struct filter *filt, struct knote *kn)
evfilt_write_knote_enable(struct filter *filt, struct knote *kn)
{
struct epoll_event ev;

Expand All @@ -115,7 +123,7 @@ evfilt_socket_knote_enable(struct filter *filt, struct knote *kn)
}

int
evfilt_socket_knote_disable(struct filter *filt, struct knote *kn)
evfilt_write_knote_disable(struct filter *filt, struct knote *kn)
{
return epoll_update(EPOLL_CTL_DEL, filt, kn, NULL);
}
Expand All @@ -124,10 +132,10 @@ const struct filter evfilt_write = {
EVFILT_WRITE,
NULL,
NULL,
evfilt_socket_copyout,
evfilt_socket_knote_create,
evfilt_socket_knote_modify,
evfilt_socket_knote_delete,
evfilt_socket_knote_enable,
evfilt_socket_knote_disable,
evfilt_write_copyout,
evfilt_write_knote_create,
evfilt_write_knote_modify,
evfilt_write_knote_delete,
evfilt_write_knote_enable,
evfilt_write_knote_disable,
};
8 changes: 4 additions & 4 deletions src/windows/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -197,24 +197,24 @@ windows_get_descriptor_type(struct knote *kn)
lsock = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_ACCEPTCONN, (char *)&lsock, &slen);
if (i == 0 && lsock)
kn->kn_flags |= KNFL_PASSIVE_SOCKET;
kn->kn_flags |= KNFL_SOCKET_PASSIVE;

slen = sizeof(stype);
stype = 0;
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_TYPE, (char *) &lsock, &slen);
i = getsockopt(kn->kev.ident, SOL_SOCKET, SO_TYPE, (char *)&stype, &slen);
if (i < 0) {
dbg_perror("getsockopt(3)");
return (-1);
}
if (stype == SOCK_STREAM)
kn->kn_flags |= KNFL_STREAM_SOCKET;
kn->kn_flags |= KNFL_SOCKET_STREAM;
break;
}
default: {
struct stat sb;
if (fstat((int)kn->kev.ident, &sb) == 0) {
dbg_printf("HANDLE %d appears to be a regular file", kn->kev.ident);
kn->kn_flags |= KNFL_REGULAR_FILE;
kn->kn_flags |= KNFL_FILE;
}
}
}
Expand Down
Loading

0 comments on commit f641903

Please sign in to comment.