diff --git a/CHANGES b/CHANGES index a198b0c..f2795b2 100644 --- a/CHANGES +++ b/CHANGES @@ -1,5 +1,6 @@ 0.19: add io_socketpair + add io_passfd and io_receivefd (and test/fdpassing.c) 0.18: make libowfat compile on BSD again (sorry, and thanks to everyone who diff --git a/io.h b/io.h index df86336..8e84296 100644 --- a/io.h +++ b/io.h @@ -7,12 +7,16 @@ #include "taia.h" /* like open(s,O_RDONLY) */ +/* return 1 if ok, 0 on error */ int io_readfile(int64* d,const char* s); /* like open(s,O_WRONLY|O_CREAT|O_TRUNC,0600) */ +/* return 1 if ok, 0 on error */ int io_createfile(int64* d,const char* s); /* like pipe(d) */ +/* return 1 if ok, 0 on error */ int io_pipe(int64* d); /* like socketpair() */ +/* return 1 if ok, 0 on error */ int io_socketpair(int64* d); /* non-blocking read(), -1 for EAGAIN and -3+errno for other errors */ @@ -82,6 +86,14 @@ void io_finishandshutdown(void); /* return number of bytes written */ int64 io_sendfile(int64 s,int64 fd,uint64 off,uint64 n); +/* Pass fd over sock (must be a unix domain socket) to other process. + * Return 0 if ok, -1 on error, setting errno. */ +int io_passfd(int64 sock,int64 fd); + +/* Receive fd over sock (must be a unix domain socket) from other + * process. Return sock if ok, -1 on error, setting errno. */ +int64 io_receivefd(int64 sock); + #ifdef __MINGW32__ #include_next #endif diff --git a/io/io_passfd.c b/io/io_passfd.c new file mode 100644 index 0000000..53d0cf3 --- /dev/null +++ b/io/io_passfd.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include +#include "io_internal.h" + +union fdmsg { + struct cmsghdr h; + char buf[CMSG_SPACE(sizeof(int))]; +}; + +int io_passfd(int64 sock,int64 fd) { + struct msghdr msg = {0}; + struct cmsghdr *cmsg; + struct iovec iov; + char buf[CMSG_SPACE(sizeof(int))]; + iov.iov_len=1; + iov.iov_base="x"; + msg.msg_control = buf; + msg.msg_controllen = sizeof buf; + msg.msg_iov=&iov; + msg.msg_iovlen=1; + msg.msg_name=0; + msg.msg_namelen=0; + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*)CMSG_DATA(cmsg))=fd; + msg.msg_controllen = cmsg->cmsg_len; + return sendmsg(sock,&msg,0); +} diff --git a/io/io_receivefd.c b/io/io_receivefd.c new file mode 100644 index 0000000..0967235 --- /dev/null +++ b/io/io_receivefd.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include "io_internal.h" + +union fdmsg { + struct cmsghdr h; + char buf[CMSG_SPACE(sizeof(int))]; +}; + +int64 io_receivefd(int64 sock) { + struct iovec iov; + struct msghdr msg; + union fdmsg cmsg; + struct cmsghdr* h; + char x[100]; + char name[100]; + iov.iov_base=x; + iov.iov_len=100; + msg.msg_name=name; + msg.msg_namelen=100; + msg.msg_control=cmsg.buf; + msg.msg_controllen=sizeof(union fdmsg); + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_flags=0; + h=CMSG_FIRSTHDR(&msg); + h->cmsg_len=CMSG_LEN(sizeof(int)); + h->cmsg_level=SOL_SOCKET; + h->cmsg_type=SCM_RIGHTS; + *((int*)CMSG_DATA(h))=-1; + if (recvmsg(sock,&msg,0)==-1) + return -1; + h=CMSG_FIRSTHDR(&msg); + if (!h || h->cmsg_len!=CMSG_LEN(sizeof(int)) || h->cmsg_level!=SOL_SOCKET || h->cmsg_type!=SCM_RIGHTS) { +#ifdef EPROTO + errno=EPROTO; +#else + errno=EINVAL; +#endif + return -1; + } + return *((int*)CMSG_DATA(h)); +} diff --git a/test/fdpassing.c b/test/fdpassing.c new file mode 100644 index 0000000..ccceede --- /dev/null +++ b/test/fdpassing.c @@ -0,0 +1,39 @@ +#include +#include "io.h" +#include "buffer.h" + +void child(int64 fd) { + int64 x=io_receivefd(fd); + char buf[8192]; + int i; + if (x==-1) { + buffer_putsflush(buffer_2,"fd passing failed!\n"); + exit(1); + } + i=read(x,buf,sizeof(buf)); + write(1,buf,i); + io_close(x); +} + +void father(int64 fd) { + int64 x; + if (io_readfile(&x,"/etc/resolv.conf")) + io_passfd(fd,x); +} + +main() { + int64 sp[2]; + if (io_socketpair(sp)==-1) return 1; + switch (fork()) { + case -1: return 1; + case 0: /* child */ + io_close(sp[0]); + child(sp[1]); + break; + default: + io_close(sp[1]); + father(sp[0]); + break; + } + return 0; +}