add support for saving syscalls with accept4

master
leitner 4 years ago
parent c4b0dd6b81
commit b5f21a3900

@ -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)

@ -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;

@ -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;

@ -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);

@ -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)

@ -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;

@ -0,0 +1,118 @@
#define _GNU_SOURCE
#ifndef __MINGW32__
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/fcntl.h>
#include <unistd.h>
#endif
#include <string.h>
#include "windoze.h"
#include "socket.h"
#include "havesl.h"
#ifdef __MINGW32__
#include <windows.h>
#include <mswsock.h>
#include <errno.h>
#include <stdio.h>
#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;
}

@ -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 <libowfat/socket.h>
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 <libowfat/socket.h>
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)

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}

@ -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 <libowfat/socket.h>
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 <libowfat/socket.h>
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)

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}

@ -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 <libowfat/socket.h>
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 <libowfat/socket.h>
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)

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}

@ -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)

@ -0,0 +1,153 @@
#define _GNU_SOURCE
#include <sys/param.h>
#include <sys/types.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/fcntl.h>
#ifndef __MINGW32__
#include <sys/socket.h>
#include <netinet/in.h>
#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 <windows.h>
#include <mswsock.h>
#include <errno.h>
#include <stdio.h>
#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
}

@ -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 <libowfat/socket.h>
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 <libowfat/socket.h>
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)

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}

@ -0,0 +1,12 @@
#ifndef __MINGW32__
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
#endif
#include <string.h>
#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);
}
Loading…
Cancel
Save