|  | /* 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) |