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 .SH DESCRIPTION
If you want to use io_canread() and io_canwrite() on a descriptor, you 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 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 Waiting on descriptors only works for sockets, fifos and pipes. It may
also work on devices and TTYs, but that is platform dependent -- you 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" .SH "RETURN VALUE"
io_fd returns 1 on success, 0 on error. io_fd returns 1 on success, 0 on error.
.SH "SEE ALSO" .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) if (pipe(fds)==-1)
return 0; return 0;
#endif #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[0]=fds[0];
d[1]=fds[1]; d[1]=fds[1];
return 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_INET6,SOCK_STREAM,IPPROTO_TCP,fds)==-1)
if (socketpair(AF_INET,SOCK_STREAM,IPPROTO_TCP,fds)==-1) if (socketpair(AF_INET,SOCK_STREAM,IPPROTO_TCP,fds)==-1)
return 0; 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[0]=fds[0];
d[1]=fds[1]; d[1]=fds[1];
return 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_listen(int s,unsigned int backlog);
int socket_accept4(int s,char* ip,uint16* port); int socket_accept4(int s,char* ip,uint16* port);
int socket_accept6(int s,char* ip,uint16* port,uint32* scope_id); 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) att_writen(2,3) att_nonnull(2)
ssize_t socket_recv4(int s,char* buf,size_t len,char* ip,uint16* port); 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); socket_accept4(s,ip,&p);
.SH "SEE ALSO" .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; fd=e->next_accept;
e->next_accept=0; e->next_accept=0;
if (e->nonblock) { if (e->nonblock) {
if (io_fd(fd)) { if (io_fd_canwrite(fd)) {
io_entry* f=array_get(&io_fds,sizeof(io_entry),fd); io_entry* f=array_get(&io_fds,sizeof(io_entry),fd);
if (f) { if (f) {
f->nonblock=1; 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); socket_accept6(s,ip,&port,&scope_id);
.SH "SEE ALSO" .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