diff --git a/io/io_fd.3 b/io/io_fd.3 index 342c094..7529dc7 100644 --- a/io/io_fd.3 +++ b/io/io_fd.3 @@ -8,9 +8,14 @@ int \fBio_fd\fP(int64 fd); .SH DESCRIPTION If you want to use io_canread() and io_canwrite() on a descriptor, you have to use io_wait() on it first, and io_wait() has to know which -descriptors you are interested in. Use io_fd() for this. +descriptors you are interested in. io_fd() informs io_wait() that you +are interested in this fd. -io_pipe and io_socketpair already call io_fd for you. +If you know that the descriptor is writable, call io_fd_canwrite() +instead. Most fresh descriptors are writable, e.g. if it came from +pipe(2) or socketpair(2) or accept(2). This will save one syscall. + +io_pipe and io_socketpair already call io_fd_canwrite() for you. Waiting on descriptors only works for sockets, fifos and pipes. It may also work on devices and TTYs, but that is platform dependent -- you @@ -18,4 +23,4 @@ should not rely on that. It does not work on files. .SH "RETURN VALUE" io_fd returns 1 on success, 0 on error. .SH "SEE ALSO" -io_wait(3), io_wantread(3), io_canread(3), io_eagain(3), io_nonblock(3) +io_fd_canwrite(3), io_wait(3), io_wantread(3), io_canread(3), io_eagain(3), io_nonblock(3) diff --git a/io/io_pipe.c b/io/io_pipe.c index dd2ede5..40bafbb 100644 --- a/io/io_pipe.c +++ b/io/io_pipe.c @@ -11,7 +11,7 @@ int io_pipe(int64* d) { if (pipe(fds)==-1) return 0; #endif - if (io_fd(fds[1]) && io_fd(fds[0])) { + if (io_fd_canwrite(fds[1]) && io_fd(fds[0])) { d[0]=fds[0]; d[1]=fds[1]; return 1; diff --git a/io/io_socketpair.c b/io/io_socketpair.c index b10f435..21fd4fa 100644 --- a/io/io_socketpair.c +++ b/io/io_socketpair.c @@ -15,7 +15,7 @@ int io_socketpair(int64* d) { if (socketpair(AF_INET6,SOCK_STREAM,IPPROTO_TCP,fds)==-1) if (socketpair(AF_INET,SOCK_STREAM,IPPROTO_TCP,fds)==-1) return 0; - if (io_fd(fds[1]) && io_fd(fds[0])) { + if (io_fd_canwrite(fds[1]) && io_fd_canwrite(fds[0])) { d[0]=fds[0]; d[1]=fds[1]; return 1; diff --git a/socket.h b/socket.h index 175674e..f41766f 100644 --- a/socket.h +++ b/socket.h @@ -39,6 +39,17 @@ int socket_bind6_reuse(int s,const char* ip,uint16 port,uint32 scope_id); int socket_listen(int s,unsigned int backlog); int socket_accept4(int s,char* ip,uint16* port); int socket_accept6(int s,char* ip,uint16* port,uint32* scope_id); +int socket_accept4_makenonblocking(int s,char* ip,uint16* port); +int socket_accept6_makenonblocking(int s,char* ip,uint16* port,uint32* scope_id); +int socket_accept4_setcloseonexec(int s,char* ip,uint16* port); +int socket_accept6_setcloseonexec(int s,char* ip,uint16* port,uint32* scope_id); +int socket_accept4_makenonblocking_setcloseonexec(int s,char* ip,uint16* port); +int socket_accept6_makenonblocking_setcloseonexec(int s,char* ip,uint16* port,uint32* scope_id); + +/* These are internal wrappers around accept4, not meant for external use. + * flags can be SOCK_NONBLOCK or SOCK_CLOEXEC or both (defined in sys/socket.h) */ +int socket_accept4_flags(int s,char* ip,uint16* port, int flags); +int socket_accept6_flags(int s,char* ip,uint16* port,uint32* scope_id, int flags); att_writen(2,3) att_nonnull(2) ssize_t socket_recv4(int s,char* buf,size_t len,char* ip,uint16* port); diff --git a/socket/socket_accept4.3 b/socket/socket_accept4.3 index 17d24cd..04062c0 100644 --- a/socket/socket_accept4.3 +++ b/socket/socket_accept4.3 @@ -31,4 +31,7 @@ appropriately, without creating a new socket. socket_accept4(s,ip,&p); .SH "SEE ALSO" -socket_accept6(3), socket_connected(3) +socket_accept6(3), socket_connected(3), +socket_accept4_makenonblocking(3), +socket_accept4_setcloseonexec(3), +socket_accept4_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept4.c b/socket/socket_accept4.c index bb2ea11..f399f86 100644 --- a/socket/socket_accept4.c +++ b/socket/socket_accept4.c @@ -42,7 +42,7 @@ incoming: fd=e->next_accept; e->next_accept=0; if (e->nonblock) { - if (io_fd(fd)) { + if (io_fd_canwrite(fd)) { io_entry* f=array_get(&io_fds,sizeof(io_entry),fd); if (f) { f->nonblock=1; diff --git a/socket/socket_accept4_flags.c b/socket/socket_accept4_flags.c new file mode 100644 index 0000000..345e24b --- /dev/null +++ b/socket/socket_accept4_flags.c @@ -0,0 +1,118 @@ +#define _GNU_SOURCE +#ifndef __MINGW32__ +#include +#include +#include +#include +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" +#include "havesl.h" + +#ifdef __MINGW32__ +#include +#include +#include +#include +#include "io_internal.h" +#endif + +int socket_accept4_flags(int s, char *ip, uint16 *port, int flags) { + struct sockaddr_in si; + socklen_t len = sizeof si; + int fd; + +#ifdef __MINGW32__ + io_entry* e=array_get(&io_fds,sizeof(io_entry),s); + if (e && e->inuse) { + int sa2len; + fd=-1; + if (e->acceptqueued==1) { + errno=EAGAIN; + return -1; + } + if (e->acceptqueued==2) { +incoming: + /* incoming! */ + { + struct sockaddr* x,* y; + GetAcceptExSockaddrs(e->inbuf,0,200,200,&x,&sa2len,&y,&len); + if (len>sizeof(si)) len=sizeof(si); + memcpy(&si,y,len); + } + fd=e->next_accept; + e->next_accept=0; + if (e->nonblock) { + if (io_fd_canwrite(fd)) { + io_entry* f=array_get(&io_fds,sizeof(io_entry),fd); + if (f) { + f->nonblock=1; +// printf("setting fd %lu to non-blocking\n",(int)fd); + } + } + } + } + + /* no accept queued, queue one now. */ + if (e->next_accept==0) { + e->next_accept=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if (e==-1) + return winsock2errno(-1); + } + if (AcceptEx(s,e->next_accept,e->inbuf,0,200,200,&e->errorcode,&e->or)) + goto incoming; + if (WSAGetLastError() != ERROR_IO_PENDING) + return winsock2errno(-1); + e->acceptqueued=1; + if (fd==-1) { + errno=EAGAIN; + return fd; + } + + } else { +#endif + + static int noaccept4; // auto initialized to 0 + if (noaccept4) + fd=-1; + else { + if ((fd=accept4(s,(void*) &si, &len, flags))==-1) { + if (errno != ENOSYS) + return -1; + // if we get here, fd==-1 && errno==ENOSYS + noaccept4=1; + } + } + if (fd==-1) { + int fl = 0; + /* if we get here, the kernel did not support accept4. */ + if ((fd=accept(s,(void*) &si,&len))==-1) + return -1; + if (flags & SOCK_NONBLOCK) fl |= O_NDELAY; + if (flags & SOCK_CLOEXEC) fl |= O_CLOEXEC; + /* On BSD the accepted socket inherits O_NDELAY and O_CLOEXEC, on + * Linux it doesn't. accept4 makes this explicit. So for the + * fallback, make it explicit as well */ +#ifdef __linux__ + if (fl) { +#endif + if (fcntl(fd,F_SETFL,(fcntl(fd,F_GETFL,0) | fl) &~ (O_NDELAY | O_CLOEXEC)) == -1) { + /* This should never fail! */ + close(fd); + return -1; + } +#ifdef __linux__ + } +#endif + } + +#ifdef __MINGW32__ + } +#endif + if (ip) *(uint32*)ip = *(uint32*)&si.sin_addr; + if (port) uint16_unpack_big((char *) &si.sin_port,port); + return fd; +} diff --git a/socket/socket_accept4_makenonblocking.3 b/socket/socket_accept4_makenonblocking.3 new file mode 100644 index 0000000..31c4386 --- /dev/null +++ b/socket/socket_accept4_makenonblocking.3 @@ -0,0 +1,31 @@ +.TH socket_accept4_makenonblocking 3 +.SH NAME +socket_accept4_makenonblocking \- accept an IPv4 TCP connection on a socket and set it to non-blocking I/O +.SH SYNTAX +.B #include + +int \fBsocket_accept4_makenonblocking\fP(int \fIs\fR,char \fIip\fR[4],uint16 *\fIport\fR); +.SH DESCRIPTION +This is functionally equivalent to calling \fIsocket_accept4\fR and then +calling \fIndelay_on\fR on the returned socket. + +However, if may save a syscall or two and in the process avoid a race +condition. + +.SH EXAMPLE + #include + + int \fIs\fR; + char \fIip\fR[4]; + uint16 \fIp\fR; + + \fIs\fR = socket_tcp4(); + socket_bind4(s,ip,p); + socket_listen(s,16); + socket_accept4_makenonblocking(s,ip,&p); + +.SH "SEE ALSO" +socket_accept4(3), socket_connected(3), +socket_accept6_makenonblocking(3), +socket_accept4_setcloseonexec(3), +socket_accept4_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept4_makenonblocking.c b/socket/socket_accept4_makenonblocking.c new file mode 100644 index 0000000..67525b8 --- /dev/null +++ b/socket/socket_accept4_makenonblocking.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept4_makenonblocking(int s,char *ip,uint16 *port) { + return socket_accept4_flags(s,ip,port,SOCK_NONBLOCK); +} diff --git a/socket/socket_accept4_makenonblocking_setcloseonexec.3 b/socket/socket_accept4_makenonblocking_setcloseonexec.3 new file mode 100644 index 0000000..c4057d1 --- /dev/null +++ b/socket/socket_accept4_makenonblocking_setcloseonexec.3 @@ -0,0 +1,31 @@ +.TH socket_accept4_makenonblocking_setcloseonexec 3 +.SH NAME +socket_accept4_makenonblocking_setcloseonexec \- accept an IPv4 TCP connection on a socket and set it to non-blocking I/O and close-on-exec +.nSH SYNTAX +.B #include + +int \fBsocket_accept4_makenonblocking_setcloseonexec\fP(int \fIs\fR,char \fIip\fR[4],uint16 *\fIport\fR); +.SH DESCRIPTION +This is functionally equivalent to calling \fIsocket_accept4\fR and then +calling \fIndelay_on\fR and \fIio_tcloseonexec\fR on the returned socket. + +However, if may save a syscall or two and in the process avoid a race +condition. + +.SH EXAMPLE + #include + + int \fIs\fR; + char \fIip\fR[4]; + uint16 \fIp\fR; + + \fIs\fR = socket_tcp4(); + socket_bind4(s,ip,p); + socket_listen(s,16); + socket_accept4_makenonblocking_setcloseonexec(s,ip,&p); + +.SH "SEE ALSO" +socket_accept4(3), socket_connected(3), +socket_accept4_makenonblocking(3), +socket_accept4_setcloseonexec(3), +socket_accept6_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept4_makenonblocking_setcloseonexec.c b/socket/socket_accept4_makenonblocking_setcloseonexec.c new file mode 100644 index 0000000..700510c --- /dev/null +++ b/socket/socket_accept4_makenonblocking_setcloseonexec.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept4_makenonblocking(int s,char *ip,uint16 *port) { + return socket_accept4_flags(s,ip,port,SOCK_NONBLOCK | SOCK_CLOEXEC); +} diff --git a/socket/socket_accept4_setcloseonexec.3 b/socket/socket_accept4_setcloseonexec.3 new file mode 100644 index 0000000..a7d8b95 --- /dev/null +++ b/socket/socket_accept4_setcloseonexec.3 @@ -0,0 +1,31 @@ +.TH socket_accept4_setcloseonexec 3 +.SH NAME +socket_accept4_setcloseonexec \- accept an IPv4 TCP connection on a socket and set it to close on exec +.SH SYNTAX +.B #include + +int \fBsocket_accept4_setcloseonexec\fP(int \fIs\fR,char \fIip\fR[4],uint16 *\fIport\fR); +.SH DESCRIPTION +This is functionally equivalent to calling \fIsocket_accept4\fR and then +calling \fIio_closeonexec\fR on the returned socket. + +However, if may save a syscall or two and in the process avoid a race +condition. + +.SH EXAMPLE + #include + + int \fIs\fR; + char \fIip\fR[4]; + uint16 \fIp\fR; + + \fIs\fR = socket_tcp4(); + socket_bind4(s,ip,p); + socket_listen(s,16); + socket_accept4_setcloseonexec(s,ip,&p); + +.SH "SEE ALSO" +socket_accept4(3), socket_connected(3), +socket_accept4_makenonblocking(3), +socket_accept6_setcloseonexec(3), +socket_accept4_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept4_setcloseonexec.c b/socket/socket_accept4_setcloseonexec.c new file mode 100644 index 0000000..e64d9f1 --- /dev/null +++ b/socket/socket_accept4_setcloseonexec.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept4_makenonblocking(int s,char *ip,uint16 *port) { + return socket_accept4_flags(s,ip,port,SOCK_CLOEXEC); +} diff --git a/socket/socket_accept6.3 b/socket/socket_accept6.3 index 6c9400c..14008cc 100644 --- a/socket/socket_accept6.3 +++ b/socket/socket_accept6.3 @@ -35,4 +35,7 @@ appropriately, without creating a new socket. socket_accept6(s,ip,&port,&scope_id); .SH "SEE ALSO" -socket_accept4(3), socket_connected(3) +socket_accept4(3), socket_connected(3), +socket_accept6_makenonblocking(3), +socket_accept6_setcloseonexec(3), +socket_accept6_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept6_flags.c b/socket/socket_accept6_flags.c new file mode 100644 index 0000000..3202b1b --- /dev/null +++ b/socket/socket_accept6_flags.c @@ -0,0 +1,153 @@ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#ifndef __MINGW32__ +#include +#include +#endif +#include "windoze.h" +#include "byte.h" +#include "socket.h" +#include "ip6.h" +#include "haveip6.h" +#include "havesl.h" +#include "havescope.h" + +#ifdef __MINGW32__ +#include +#include +#include +#include +#include "io_internal.h" +#endif + +int socket_accept6_flags(int s, char* ip, uint16* port, uint32* scope_id, int flags) +{ +#ifdef LIBC_HAS_IP6 + struct sockaddr_in6 sa; +#else + struct sockaddr_in sa; +#endif + socklen_t dummy = sizeof sa; + int fd; + +#ifdef __MINGW32__ + // Windows uses overlapped I/O instead of non-blocking I/O + io_entry* e=array_get(&io_fds,sizeof(io_entry),s); + if (e && e->inuse) { + int sa2len; + fd=-1; + if (e->acceptqueued==1) { + errno=EAGAIN; + return -1; + } + if (e->acceptqueued==2) { +incoming: + /* incoming! */ + { + struct sockaddr* x,* y; + GetAcceptExSockaddrs(e->inbuf,0,200,200,&x,&sa2len,&y,&dummy); + if (dummy>sizeof(sa)) dummy=sizeof(sa); + memcpy(&sa,y,dummy); + } + fd=e->next_accept; + e->next_accept=0; + if (e->nonblock) { + if (io_fd(fd)) { + io_entry* f=array_get(&io_fds,sizeof(io_entry),fd); + if (f) { + f->nonblock=1; +// printf("setting fd %lu to non-blocking\n",(int)fd); + } + } + } + } + + /* no accept queued, queue one now. */ + if (e->next_accept==0) { + e->next_accept=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if (e==-1) + return winsock2errno(-1); + } + if (AcceptEx(s,e->next_accept,e->inbuf,0,200,200,&e->errorcode,&e->or)) + goto incoming; + if (WSAGetLastError() != ERROR_IO_PENDING) + return winsock2errno(-1); + e->acceptqueued=1; + if (fd==-1) { + errno=EAGAIN; + return fd; + } + + } else { +#endif + + static int noaccept4; // auto initialized to 0 + if (noaccept4) + fd=-1; + else { + if ((fd=accept4(s,(void*) &sa, &dummy, flags))==-1) { + if (errno != ENOSYS) + return -1; + noaccept4=1; + } + } + if (fd==-1) { + int fl = 0; + fd = accept(s, (struct sockaddr *) &sa, &dummy); + if (fd == -1) + return -1; + if (flags & SOCK_NONBLOCK) fl |= O_NDELAY; + if (flags & SOCK_CLOEXEC) fl |= O_CLOEXEC; + /* On BSD the accepted socket inherits O_NDELAY and O_CLOEXEC, on + * Linux it doesn't. accept4 makes this explicit. So for the + * fallback, make it explicit as well */ +#ifdef __linux__ + if (fl) { +#endif + if (fcntl(fd,F_SETFL,(fcntl(fd,F_GETFL,0) | fl) &~ (O_NDELAY | O_CLOEXEC)) == -1) { + /* This should never fail! */ + close(fd); + return -1; + } +#ifdef __linux__ + } +#endif + } +#ifdef __MINGW32__ + } +#endif + +#ifdef LIBC_HAS_IP6 + if (noipv6 || sa.sin6_family==AF_INET || sa.sin6_family==PF_INET) { + struct sockaddr_in *sa4=(struct sockaddr_in*)&sa; + if (ip) { + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *) &sa4->sin_addr); + } + if (port) uint16_unpack_big((char *) &sa4->sin_port,port); + return fd; + } + if (ip) byte_copy(ip,16,(char *) &sa.sin6_addr); + if (port) uint16_unpack_big((char *) &sa.sin6_port,port); +#ifdef LIBC_HAS_SCOPE_ID + if (scope_id) *scope_id=sa.sin6_scope_id; +#else + if (scope_id) *scope_id=0; +#endif + + return fd; +#else + if (ip) { + byte_copy(ip,12,V4mappedprefix); + byte_copy(ip+12,4,(char *) &sa.sin_addr); + } + if (port) uint16_unpack_big((char *) &sa.sin_port,port); + if (scope_id) *scope_id=0; + return fd; +#endif +} diff --git a/socket/socket_accept6_makenonblocking.3 b/socket/socket_accept6_makenonblocking.3 new file mode 100644 index 0000000..383b490 --- /dev/null +++ b/socket/socket_accept6_makenonblocking.3 @@ -0,0 +1,32 @@ +.TH socket_accept6_makenonblocking 3 +.SH NAME +socket_accept6_makenonblocking \- accept an IPv6 TCP connection on a socket +.SH SYNTAX +.B #include + +int \fBsocket_accept6_makenonblocking\fP(int \fIs\fR,char \fIip\fR[16],uint16 *\fIport\fR,uint32 *\fIscope_id\fR); +.SH DESCRIPTION +This is functionally equivalent to calling \fIsocket_accept4\fR and then +calling \fIndelay_on\fR on the returned socket. + +However, if may save a syscall or two and in the process avoid a race +condition. + +.SH EXAMPLE + #include + + int \fIs\fR; + char \fIip\fR[16]; + uint16 \fIport\fR; + uint32 \fIscope_id\fR; + + \fIs\fR = socket_tcp6(); + socket_bind6(s,ip,port,scope_id); + socket_listen(s,16); + socket_accept6_makenonblocking(s,ip,&port,&scope_id); + +.SH "SEE ALSO" +socket_accept6(3), socket_connected(3), +socket_accept4_makenonblocking(3), +socket_accept6_setcloseonexec(3), +socket_accept6_makenonblocking_setcloseonexec(3) diff --git a/socket/socket_accept6_makenonblocking.c b/socket/socket_accept6_makenonblocking.c new file mode 100644 index 0000000..17fddb0 --- /dev/null +++ b/socket/socket_accept6_makenonblocking.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept6_makenonblocking(int s,char *ip,uint16 *port,uint32* scope_id) { + return socket_accept6_flags(s,ip,port,scope_id,SOCK_NONBLOCK); +} diff --git a/socket/socket_accept6_makenonblocking_setcloseonexec.c b/socket/socket_accept6_makenonblocking_setcloseonexec.c new file mode 100644 index 0000000..4741bb9 --- /dev/null +++ b/socket/socket_accept6_makenonblocking_setcloseonexec.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept6_makenonblocking(int s,char *ip,uint16 *port,uint32* scope_id) { + return socket_accept6_flags(s,ip,port,scope_id,SOCK_NONBLOCK | SOCK_CLOEXEC); +} diff --git a/socket/socket_accept6_setcloseonexec.c b/socket/socket_accept6_setcloseonexec.c new file mode 100644 index 0000000..0bfc4a6 --- /dev/null +++ b/socket/socket_accept6_setcloseonexec.c @@ -0,0 +1,12 @@ +#ifndef __MINGW32__ +#define _GNU_SOURCE +#include +#include +#endif +#include +#include "windoze.h" +#include "socket.h" + +int socket_accept6_makenonblocking(int s,char *ip,uint16 *port,uint32* scope_id) { + return socket_accept6_flags(s,ip,port,scope_id,SOCK_CLOEXEC); +}