diff --git a/src/cqueues.c b/src/cqueues.c index fc2c2f8..11ccd40 100644 --- a/src/cqueues.c +++ b/src/cqueues.c @@ -629,44 +629,48 @@ static int kpoll_ctl(struct kpoll *kp, int fd, short *state, short events, void return 0; #elif ENABLE_KQUEUE - 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; } @@ -1528,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; diff --git a/src/lib/socket.c b/src/lib/socket.c index 6424d0c..e2ac660 100644 --- a/src/lib/socket.c +++ b/src/lib/socket.c @@ -711,34 +711,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; @@ -754,19 +762,28 @@ 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 ((error = so_setfl(fd, flags, mask, need))) +#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; + + /* assumes natural state of socket is all flags off */ + if ((error = so_setfl(fd, flags, mask&flags, need))) goto error; return fd; @@ -849,7 +866,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; @@ -858,10 +882,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; @@ -941,11 +969,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 @@ -1252,11 +1284,6 @@ struct socket { struct so_stat st; - struct { - _Bool rd; - _Bool wr; - } shut; - struct addrinfo *host; short events; @@ -1410,6 +1437,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; @@ -1616,17 +1647,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 +1654,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 +1668,9 @@ static int so_shutrd_(struct socket *so) { return SO_ENOTCONN; } - so->shut.rd = 1; + if (how == SHUT_WR || how == SHUT_RDWR) { + so->st.sent.eof = 1; + } return 0; } /* so_shutrd_() */ @@ -1755,17 +1777,11 @@ static int so_exec(struct socket *so) { goto exec; case SO_S_SHUTWR: - if ((error = so_shutwr_(so))) - goto error; - - so->done |= state; - - goto exec; case SO_S_SHUTRD: - if ((error = so_shutrd_(so))) + 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() */ @@ -2124,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 9aaf78e..be05289 100644 --- a/src/socket.c +++ b/src/socket.c @@ -925,7 +925,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_UNSPEC); @@ -1300,13 +1300,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; @@ -2704,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);