| /* This file is part of the UCB release of Plan 9. It is subject to the license |
| * terms in the LICENSE file found in the top-level directory of this |
| * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No |
| * part of the UCB release of Plan 9, including this file, may be copied, |
| * modified, propagated, or distributed except according to the terms contained |
| * in the LICENSE file. */ |
| |
| /* posix */ |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <string.h> |
| |
| /* bsd extensions */ |
| #include <sys/uio.h> |
| #include <sys/socket.h> |
| #include <netinet/in.h> |
| #include <sys/un.h> |
| |
| #include <sys/plan9_helpers.h> |
| |
| int __libc_accept4(int fd, __SOCKADDR_ARG addr, socklen_t *alen, int a4_flags) |
| { |
| int nfd, lcfd; |
| socklen_t n; |
| Rock *r, *nr; |
| struct sockaddr_in *ip; |
| char name[Ctlsize]; |
| char file[8 + Ctlsize + 1]; |
| const char *net = 0; |
| char listen[Ctlsize]; |
| int open_flags; |
| |
| r = _sock_findrock(fd, 0); |
| if (r == 0) { |
| errno = ENOTSOCK; |
| return -1; |
| } |
| |
| switch (r->domain) { |
| case PF_INET: |
| switch (r->stype) { |
| case SOCK_DGRAM: |
| net = "udp"; |
| break; |
| case SOCK_STREAM: |
| net = "tcp"; |
| break; |
| } |
| /* at this point, our FD is for the data file. we need to open |
| * the listen file. */ |
| _sock_get_conv_filename(r, "listen", listen); |
| open_flags = O_RDWR; |
| /* This is for the listen - maybe don't block on open */ |
| open_flags |= (r->sopts & SOCK_NONBLOCK ? O_NONBLOCK : 0); |
| /* This is for the ctl we get back - maybe CLOEXEC, based on |
| * what accept4 wants for the child */ |
| open_flags |= (a4_flags & SOCK_CLOEXEC ? O_CLOEXEC : 0); |
| lcfd = open(listen, open_flags); |
| if (lcfd < 0) |
| return -1; |
| /* at this point, we have a new conversation, and lcfd is its |
| * ctl fd. nfd will be the FD for that conv's data file. |
| * sock_data will store our lcfd in the rock and return the data |
| * file fd. |
| * |
| * Note, we pass the listen socket's stype, but not it's sopts. |
| * The sopts (e.g. SOCK_NONBLOCK) apply to the original socket, |
| * not to the new one. Instead, we pass the accept4 flags, |
| * which are the sopts for the new socket. Note that this is |
| * just the sopts. Both the listen socket and the new socket |
| * have the same stype. */ |
| nfd = _sock_data(lcfd, net, r->domain, a4_flags | r->stype, |
| r->protocol, &nr); |
| if (nfd < 0) |
| return -1; |
| |
| /* get remote address */ |
| ip = (struct sockaddr_in *)&nr->raddr; |
| _sock_ingetaddr(nr, ip, &n, "remote"); |
| if (addr.__sockaddr__) { |
| memmove(addr.__sockaddr_in__, ip, |
| sizeof(struct sockaddr_in)); |
| *alen = sizeof(struct sockaddr_in); |
| } |
| |
| return nfd; |
| case PF_UNIX: |
| if (r->other >= 0) { |
| errno = EINVAL; // was EGREG |
| return -1; |
| } |
| |
| for (;;) { |
| /* read path to new connection */ |
| n = read(fd, name, sizeof(name) - 1); |
| if (n < 0) |
| return -1; |
| if (n == 0) |
| continue; |
| name[n] = 0; |
| |
| /* open new connection */ |
| _sock_srvname(file, name); |
| open_flags = O_RDWR; |
| /* This is for the listen - maybe don't block on open */ |
| open_flags |= (r->sopts & |
| SOCK_NONBLOCK ? O_NONBLOCK : 0); |
| /* This is for the ctl we get back - maybe CLOEXEC, |
| * based on what accept4 wants for the child */ |
| open_flags |= (a4_flags & SOCK_CLOEXEC ? O_CLOEXEC : 0); |
| nfd = open(file, open_flags); |
| if (nfd < 0) |
| continue; |
| |
| /* confirm opening on new connection */ |
| if (write(nfd, name, strlen(name)) > 0) |
| break; |
| |
| close(nfd); |
| } |
| |
| nr = _sock_newrock(nfd); |
| if (nr == 0) { |
| close(nfd); |
| return -1; |
| } |
| nr->domain = r->domain; |
| nr->stype = r->stype; |
| nr->sopts = a4_flags; |
| nr->protocol = r->protocol; |
| |
| return nfd; |
| default: |
| errno = EOPNOTSUPP; |
| return -1; |
| } |
| } |
| weak_alias(__libc_accept4, accept4) |