From 43766ff1bc190f282afe39fd3229b6e7376828ee Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 29 Nov 2015 13:46:48 +1100 Subject: [PATCH 01/14] src/lib/socket.c: If SOCK_NONBLOCK and/or SOCK_CLOEXEC are defined; use them in so_socket --- src/lib/socket.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 1074019..28917ea 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -666,18 +666,26 @@ static int so_type2mask(mode_t, int, int, int); int so_socket(int domain, int type, const struct so_options *opts, int *_error) { int error, fd, flags, mask, need; -#if defined SOCK_CLOEXEC - if (-1 == (fd = socket(domain, type|SOCK_CLOEXEC, 0))) - goto syerr; -#else - if (-1 == (fd = socket(domain, type, 0))) - goto syerr; -#endif - flags = so_opts2flags(opts, &mask); mask &= so_type2mask(S_IFSOCK, domain, type, 0); need = ~(SO_F_NODELAY|SO_F_NOPUSH|SO_F_NOSIGPIPE|SO_F_OOBINLINE); +#if defined SOCK_NONBLOCK + if (flags & SO_F_NONBLOCK) { + type |= SOCK_NONBLOCK; + } + mask &= ~SO_F_NONBLOCK; +#endif +#if defined SOCK_CLOEXEC + if (flags & SO_F_CLOEXEC) { + type |= SOCK_CLOEXEC; + } + mask &= ~SO_F_CLOEXEC; +#endif + + if (-1 == (fd = socket(domain, type, 0))) + goto syerr; + if ((error = so_setfl(fd, flags, mask, need))) goto error; From a4a408d2d0ff6dc64ad20fffc82edb02ea4ed9e0 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 29 Nov 2015 13:51:40 +1100 Subject: [PATCH 02/14] src/lib/socket.c: Only call 'SETFL' if 'GETFL' doesn't return what we wanted --- src/lib/socket.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 28917ea..0b8f9d1 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -778,10 +778,14 @@ int so_cloexec(int fd, _Bool cloexec) { int so_nonblock(int fd, _Bool nonblock) { - int flags, mask = (nonblock)? ~0 : (~O_NONBLOCK); + int flags, newflags; - if (-1 == (flags = fcntl(fd, F_GETFL)) - || -1 == fcntl(fd, F_SETFL, mask & (flags | O_NONBLOCK))) + if (-1 == (flags = fcntl(fd, F_GETFL))) + return so_syerr(); + + newflags = (nonblock ? ~0 : ~O_NONBLOCK) & (flags | O_NONBLOCK); + + if (flags != newflags && (-1 == fcntl(fd, F_SETFL, newflags))) return so_syerr(); return 0; @@ -861,11 +865,15 @@ int so_nopush(int fd, _Bool nopush) { int so_nosigpipe(int fd, _Bool nosigpipe) { #if defined O_NOSIGPIPE - int flags, mask = (nosigpipe)? ~0 : (~O_NOSIGPIPE); + int flags, newflags; - if (-1 == (flags = fcntl(fd, F_GETFL)) - || -1 == fcntl(fd, F_SETFL, mask & (flags | O_NOSIGPIPE))) - return errno; + if (-1 == (flags = fcntl(fd, F_GETFL))) + return so_syerr(); + + newflags = (nosigpipe ? ~0 : ~O_NOSIGPIPE) & (flags | O_NOSIGPIPE); + + if (flags != newflags && (-1 == fcntl(fd, F_SETFL, newflags))) + return so_syerr(); return 0; #elif defined F_SETNOSIGPIPE From 69d0851354c7fce71410a1515cfb7ea18a8461c6 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 29 Nov 2015 13:54:18 +1100 Subject: [PATCH 03/14] src/lib/socket.c: Only call 'SETFD' if 'GETFD' doesn't return what we wanted --- src/lib/socket.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 0b8f9d1..a33baaa 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -769,7 +769,14 @@ int so_cloexec(int fd, _Bool cloexec) { #if _WIN32 return 0; #else - if (-1 == fcntl(fd, F_SETFD, cloexec)) + int flags, newflags; + + if (-1 == (flags = fcntl(fd, F_GETFD))) + return so_syerr(); + + newflags = (cloexec ? ~0 : ~FD_CLOEXEC) & (flags | FD_CLOEXEC); + + if (flags != newflags && (-1 == fcntl(fd, F_SETFD, flags))) return so_syerr(); return 0; From 0f1725ad7b62f02b040f3896603de07cacedc6dd Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 29 Nov 2015 13:55:01 +1100 Subject: [PATCH 04/14] src/socket.c: Pass SOCK_NONBLOCK and SOCK_CLOEXEC to socketpair if possible --- src/socket.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/socket.c b/src/socket.c index 31e06b7..ddbed1c 100644 --- a/src/socket.c +++ b/src/socket.c @@ -1249,13 +1249,14 @@ static lso_nargs_t lso_pair(lua_State *L) { a = lso_newsocket(L, type); b = lso_newsocket(L, type); +#if defined SOCK_NONBLOCK + type |= SOCK_NONBLOCK; +#endif #if defined SOCK_CLOEXEC - if (0 != socketpair(AF_UNIX, type|SOCK_CLOEXEC, PF_UNSPEC, fd)) - goto syerr; -#else + type |= SOCK_CLOEXEC; +#endif if (0 != socketpair(AF_UNIX, type, PF_UNSPEC, fd)) goto syerr; -#endif opts.fd_close.arg = a; opts.fd_close.cb = &lso_closefd; From 7ef0d96b91c05748703be22a73cfbcf0e8e0f9a9 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 30 Nov 2015 16:30:55 +1100 Subject: [PATCH 05/14] src/lib/socket: ftype can fill in only what is unknown --- src/lib/socket.c | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index a33baaa..1207f93 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -623,34 +623,42 @@ static so_error_t so_ftype(int fd, mode_t *mode, int *domain, int *type, int *pr struct stat st; int error; - if (0 != fstat(fd, &st)) - return errno; - *mode = S_IFMT & st.st_mode; + if (*mode == 0) { + if (0 != fstat(fd, &st)) + return errno; + *mode = S_IFMT & st.st_mode; + } if (!S_ISSOCK(*mode)) return 0; + if (*domain == 0) { #if defined SO_DOMAIN - if (0 != getsockopt(fd, SOL_SOCKET, SO_DOMAIN, domain, &(socklen_t){ sizeof *domain })) { - if (errno != ENOPROTOOPT) - return errno; + if (0 != getsockopt(fd, SOL_SOCKET, SO_DOMAIN, domain, &(socklen_t){ sizeof *domain })) { + if (errno != ENOPROTOOPT) + return errno; + if ((error = so_ffamily(fd, domain))) + return error; + } +#else if ((error = so_ffamily(fd, domain))) return error; - } -#else - if ((error = so_ffamily(fd, domain))) - return error; #endif + } - if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, type, &(socklen_t){ sizeof *type })) - return errno; + if (*type == 0) { + if (0 != getsockopt(fd, SOL_SOCKET, SO_TYPE, type, &(socklen_t){ sizeof *type })) + return errno; + } #if defined SO_PROTOCOL - if (0 != getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, protocol, &(socklen_t){ sizeof *protocol })) { - if (errno != ENOPROTOOPT) - return errno; + if (*protocol == 0) { + if (0 != getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, protocol, &(socklen_t){ sizeof *protocol })) { + if (errno != ENOPROTOOPT) + return errno; + } } #else (void)protocol; From 1105656bf82352dc4812a233da6f84123b87a992 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sun, 29 Nov 2015 14:30:48 +1100 Subject: [PATCH 06/14] src/lib/socket.c: Skip so_ftype call when information already available --- src/lib/socket.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/lib/socket.c b/src/lib/socket.c index 1207f93..4d76b3e 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -1353,6 +1353,10 @@ static int so_socket_(struct socket *so) { if (-1 == (so->fd = so_socket(so->host->ai_family, so->host->ai_socktype, &so->opts, &error))) return error; + so->mode = S_IFSOCK; + so->domain = so->host->ai_family; + so->type = so->host->ai_socktype; + so->protocol = 0; if ((error = so_ftype(so->fd, &so->mode, &so->domain, &so->type, &so->protocol))) return error; From 50e52842ad52345bf22893a44b52cd2838782fd5 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 30 Nov 2015 15:32:59 +1100 Subject: [PATCH 07/14] src/lib/socket.c: Assume natural state of newly created socket is all flags off --- src/lib/socket.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 4d76b3e..e30196f 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -694,7 +694,8 @@ int so_socket(int domain, int type, const struct so_options *opts, int *_error) if (-1 == (fd = socket(domain, type, 0))) goto syerr; - if ((error = so_setfl(fd, flags, mask, need))) + /* assumes natural state of socket is all flags off */ + if ((error = so_setfl(fd, flags, mask&flags, need))) goto error; return fd; From 060bd60b24c95102243d149cc7843242e3074274 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Mon, 30 Nov 2015 15:33:59 +1100 Subject: [PATCH 08/14] src/socket.c: reuseaddr can be turned off by default for sockets that will connect --- src/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/socket.c b/src/socket.c index ddbed1c..9b05f29 100644 --- a/src/socket.c +++ b/src/socket.c @@ -892,7 +892,7 @@ static lso_nargs_t lso_connect2(lua_State *L) { port = luaL_checkstring(L, -1); } } else { - opts = *so_opts(); + opts = *so_opts(.sin_reuseaddr = 0); host = luaL_checkstring(L, 1); port = luaL_checkstring(L, 2); family = luaL_optinteger(L, 3, AF_INET); From 48e30e150112792d30f267c8c95697f56ad69a1c Mon Sep 17 00:00:00 2001 From: daurnimator Date: Wed, 6 Jan 2016 16:07:39 +1100 Subject: [PATCH 09/14] src/cqueues: Only call kevent() once --- src/cqueues.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/cqueues.c b/src/cqueues.c index 2b513be..d0c04f6 100644 --- a/src/cqueues.c +++ b/src/cqueues.c @@ -598,44 +598,48 @@ static int kpoll_ctl(struct kpoll *kp, int fd, short *state, short events, void return 0; #else - struct kevent event; - - if (*state == events) - return 0; + struct kevent changelist[2]; + int nchanges = 0; if (events & POLLIN) { if (!(*state & POLLIN)) { - KP_SET(&event, fd, EVFILT_READ, EV_ADD, 0, 0, udata); + KP_SET(changelist+nchanges, fd, EVFILT_READ, EV_ADD, 0, 0, udata); + nchanges++; + } + } else if (*state & POLLIN) { + KP_SET(changelist+nchanges, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + nchanges++; + } - if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) - return errno; + if (events & POLLOUT) { + if (!(*state & POLLOUT)) { + KP_SET(changelist+nchanges, fd, EVFILT_WRITE, EV_ADD, 0, 0, udata); + nchanges++; + } + } else if (*state & POLLOUT) { + KP_SET(changelist+nchanges, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + nchanges++; + } + + if (0 == nchanges) + return 0; + if (0 != kevent(kp->fd, changelist, nchanges, NULL, 0, &(struct timespec){ 0, 0 })) + return errno; + + if (events & POLLIN) { + if (!(*state & POLLIN)) { *state |= POLLIN; } } else if (*state & POLLIN) { - KP_SET(&event, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - - if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) - return errno; - *state &= ~POLLIN; } if (events & POLLOUT) { if (!(*state & POLLOUT)) { - KP_SET(&event, fd, EVFILT_WRITE, EV_ADD, 0, 0, udata); - - if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) - return errno; - *state |= POLLOUT; } } else if (*state & POLLOUT) { - KP_SET(&event, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); - - if (0 != kevent(kp->fd, &event, 1, NULL, 0, &(struct timespec){ 0, 0 })) - return errno; - *state &= ~POLLOUT; } From 68b13beacc3bdf110723003b605a5ddc25a0edb4 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Nov 2016 10:45:44 +1100 Subject: [PATCH 10/14] src/lib/socket: Have a single helper function for all types of shutdown() --- src/lib/socket.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 6424d0c..113e935 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -1616,17 +1616,6 @@ static int so_rstlowat_(struct socket *so) { } /* so_rstlowat_() */ -static int so_shutwr_(struct socket *so) { - if (so->fd != -1 && 0 != shutdown(so->fd, SHUT_WR)) - return so_soerr(); - - so->shut.wr = 1; - so->st.sent.eof = 1; - - return 0; -} /* so_shutwr_() */ - - static _Bool so_isconn(int fd) { struct sockaddr sa; socklen_t slen = sizeof sa; @@ -1634,8 +1623,8 @@ static _Bool so_isconn(int fd) { return 0 == getpeername(fd, &sa, &slen) || so_soerr() != SO_ENOTCONN; } /* so_isconn() */ -static int so_shutrd_(struct socket *so) { - if (so->fd != -1 && 0 != shutdown(so->fd, SHUT_RD)) { +static int so_shutdown_(struct socket *so, int how) { + if (so->fd != -1 && 0 != shutdown(so->fd, how)) { /* * NOTE: OS X will fail with ENOTCONN if the requested * SHUT_RD or SHUT_WR flag is already set, including if the @@ -1648,7 +1637,14 @@ static int so_shutrd_(struct socket *so) { return SO_ENOTCONN; } - so->shut.rd = 1; + if (how == SHUT_WR || how == SHUT_RDWR) { + so->shut.wr = 1; + so->st.sent.eof = 1; + } + + if (how == SHUT_RD || how == SHUT_RDWR) { + so->shut.rd = 1; + } return 0; } /* so_shutrd_() */ @@ -1755,14 +1751,14 @@ static int so_exec(struct socket *so) { goto exec; case SO_S_SHUTWR: - if ((error = so_shutwr_(so))) + if ((error = so_shutdown_(so, SHUT_WR))) goto error; so->done |= state; goto exec; case SO_S_SHUTRD: - if ((error = so_shutrd_(so))) + if ((error = so_shutdown_(so, SHUT_RD))) goto error; so->done |= state; From 5206d2db09fa88ac60c5f6b837e0fbf3d6412d8f Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Nov 2016 10:51:38 +1100 Subject: [PATCH 11/14] src/lib/socket: Remove unread 'shut' struct member --- src/lib/socket.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 113e935..cc54ce5 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -1252,11 +1252,6 @@ struct socket { struct so_stat st; - struct { - _Bool rd; - _Bool wr; - } shut; - struct addrinfo *host; short events; @@ -1638,14 +1633,9 @@ static int so_shutdown_(struct socket *so, int how) { } if (how == SHUT_WR || how == SHUT_RDWR) { - so->shut.wr = 1; so->st.sent.eof = 1; } - if (how == SHUT_RD || how == SHUT_RDWR) { - so->shut.rd = 1; - } - return 0; } /* so_shutrd_() */ From 70c05de28a9b1b91919dc1abe31ecb83c03c9470 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Nov 2016 10:58:32 +1100 Subject: [PATCH 12/14] src/lib/socket: Use SHUT_RDWR if wanting to shutdown both read and write --- src/lib/socket.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index cc54ce5..d3a304b 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -1741,17 +1741,11 @@ static int so_exec(struct socket *so) { goto exec; case SO_S_SHUTWR: - if ((error = so_shutdown_(so, SHUT_WR))) - goto error; - - so->done |= state; - - goto exec; case SO_S_SHUTRD: - if ((error = so_shutdown_(so, SHUT_RD))) + if ((error = so_shutdown_(so, (so->todo & SO_S_SHUTRD)?(so->todo & SO_S_SHUTWR)?SHUT_RDWR:SHUT_RD:SHUT_WR))) goto error; - so->done |= state; + so->done |= (so->todo & (SO_S_SHUTWR|SO_S_SHUTRD)); goto exec; } /* so_exec() */ From 3317f1a00f80494093cf652f3a7db4b7178dba99 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Thu, 3 Nov 2016 12:25:08 +1100 Subject: [PATCH 13/14] add new so_accept_socket function that accepts() and returns a full so_socket object --- src/lib/socket.c | 38 ++++++++++++++++++++++++++++++++++++++ src/lib/socket.h | 2 ++ src/socket.c | 14 ++++---------- 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/lib/socket.c b/src/lib/socket.c index 7a46e46..e2ac660 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -2140,6 +2140,44 @@ int so_accept(struct socket *so, struct sockaddr *saddr, socklen_t *slen, int *e } /* so_accept() */ +struct socket *so_accept_socket(struct socket *accept_so, const struct so_options *opts, int *error_) { + union sockaddr_any saddr; + struct socket *so; + int flags, mask, need, error; + + if (!(so = so_make(opts, &error))) + goto error; + + if (-1 == (so->fd = so_accept(accept_so, &saddr.sa, &(socklen_t){ sizeof saddr }, &error))) + goto error; + + so->mode = S_IFSOCK; + so->domain = saddr.sa.sa_family; + + if ((error = so_ftype(so->fd, &so->mode, &so->domain, &so->type, &so->protocol))) + goto error; + + flags = so_opts2flags(opts, &mask); + mask &= so_type2mask(so->mode, so->domain, so->type, so->protocol); + need = ~(SO_F_NODELAY|SO_F_NOPUSH|SO_F_NOSIGPIPE|SO_F_OOBINLINE); + /* we accept with CLOEXEC set */ + mask &= ~SO_F_CLOEXEC; + /* reuseaddr doesn't matter, the socket is already bound */ + mask &= ~SO_F_REUSEADDR; + + if ((error = so_rstfl(so->fd, &so->flags, flags, mask, need))) + goto error; + + return so; +error: + so_close(so); + + *error_ = error; + + return 0; +} /* so_accept_socket() */ + + static void so_resetssl(struct socket *so) { ssl_discard(&so->ssl.ctx); so->ssl.state = 0; diff --git a/src/lib/socket.h b/src/lib/socket.h index 873bed2..660730b 100644 --- a/src/lib/socket.h +++ b/src/lib/socket.h @@ -551,6 +551,8 @@ int so_listen(struct socket *); int so_accept(struct socket *, struct sockaddr *, socklen_t *, int *); +struct socket *so_accept_socket(struct socket *, const struct so_options *, int *); + struct so_starttls { SSL_METHOD *method; SSL_CTX *context; diff --git a/src/socket.c b/src/socket.c index 69d8992..be05289 100644 --- a/src/socket.c +++ b/src/socket.c @@ -2705,25 +2705,19 @@ static lso_nargs_t lso_accept(lua_State *L) { S = lso_newsocket(L, A->type); + if ((error = lso_prepsocket(S))) + goto error; + opts.fd_close.arg = S; opts.fd_close.cb = &lso_closefd; so_clear(A->socket); - if (-1 == (fd = so_accept(A->socket, 0, 0, &error))) - goto error; - - if ((error = lso_prepsocket(S))) - goto error; - - if (!(S->socket = so_fdopen(fd, &opts, &error))) + if (!(S->socket = so_accept_socket(A->socket, &opts, &error))) goto error; return 1; -syerr: - error = errno; error: - cqs_closefd(&fd); lua_pushnil(L); lua_pushinteger(L, error); From 1890117b4684044e630680f85089612828f9e377 Mon Sep 17 00:00:00 2001 From: daurnimator Date: Sat, 2 Jun 2018 16:17:20 +1000 Subject: [PATCH 14/14] src/cqueues.c: Only move threads to pending queue when their events of interest occur See https://github.com/wahern/cqueues/commit/d86f5c80a8cb3a2ea8946079d00690cc7f53b16b#r12597913 --- src/cqueues.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/cqueues.c b/src/cqueues.c index 58e8de8..11ccd40 100644 --- a/src/cqueues.c +++ b/src/cqueues.c @@ -1532,14 +1532,14 @@ static cqs_error_t fileno_signal(struct cqueue *Q, struct fileno *fileno, short int error = 0, _error; LIST_FOREACH(event, &fileno->events, fle) { - /* XXX: If POLLPRI should we always mark as pending? */ - if (event->events & events) + if (event->events & events) { event->pending = 1; - thread_move(event->thread, &Q->thread.pending); + thread_move(event->thread, &Q->thread.pending); - if ((_error = cqueue_tryalert(Q))) - error = _error; + if ((_error = cqueue_tryalert(Q))) + error = _error; + } } return error;