net: Fixup iplib based on the O_NONBLOCK rules
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / accept.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9 /* posix */
10 #include <sys/types.h>
11 #include <unistd.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <errno.h>
16 #include <string.h>
17
18 /* bsd extensions */
19 #include <sys/uio.h>
20 #include <sys/socket.h>
21 #include <netinet/in.h>
22 #include <sys/un.h>
23
24 #include "plan9_sockets.h"
25
26 /* Await a connection on socket FD.
27    When a connection arrives, open a new socket to communicate with it,
28    set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
29    peer and *ADDR_LEN to the address's actual length, and return the
30    new socket's descriptor, or -1 for errors.  */
31 int accept(int fd, __SOCKADDR_ARG addr, socklen_t * __restrict alen)
32 {
33         int nfd, lcfd;
34         socklen_t n;
35         Rock *r, *nr;
36         struct sockaddr_in *ip;
37         char name[Ctlsize];
38         char file[8 + Ctlsize + 1];
39         char *p;
40         const char *net = 0;
41         char listen[Ctlsize];
42
43         r = _sock_findrock(fd, 0);
44         if (r == 0) {
45                 errno = ENOTSOCK;
46                 return -1;
47         }
48
49         switch (r->domain) {
50                 case PF_INET:
51                         switch (r->stype) {
52                                 case SOCK_DGRAM:
53                                         net = "udp";
54                                         break;
55                                 case SOCK_STREAM:
56                                         net = "tcp";
57                                         break;
58                         }
59                         /* at this point, our FD is for the data file.  we need to open the
60                          * listen file.  The line is stored in r->ctl (e.g. /net/tcp/666/ctl) */
61                         strcpy(listen, r->ctl);
62                         p = strrchr(listen, '/');
63                         if (p == 0)
64                                 return -1;
65                         strcpy(p + 1, "listen");
66
67                         lcfd = open(listen, O_RDWR);
68                         if (lcfd < 0)
69                                 return -1;
70                         /* at this point, we have a new conversation, and lcfd is its ctl fd.
71                          * nfd will be the FD for that conv's data file.  sock_data will trade
72                          * our lcfd for the data file fd.  even if it fails, sock_data will
73                          * close our lcfd for us.  when it succeeds, it'll open the data file
74                          * before closing lcfd, which will keep the converstation alive.
75                          *
76                          * Note, we pass the listen socket's stype, but not it's sopts.  If
77                          * we implement something like accept4, that's where those sopts
78                          * (e.g. O_CLOEXEC, O_NONBLOCK) will come from. */
79                         nfd = _sock_data(lcfd, net, r->domain, r->stype, r->protocol, &nr);
80                         if (nfd < 0)
81                                 return -1;
82
83                         /* get remote address */
84                         ip = (struct sockaddr_in *)&nr->raddr;
85                         _sock_ingetaddr(nr, ip, &n, "remote");
86                         if (addr.__sockaddr__) {
87                                 memmove(addr.__sockaddr_in__, ip, sizeof(struct sockaddr_in));
88                                 *alen = sizeof(struct sockaddr_in);
89                         }
90
91                         return nfd;
92                 case PF_UNIX:
93                         if (r->other >= 0) {
94                                 errno = EINVAL; // was EGREG
95                                 return -1;
96                         }
97
98                         for (;;) {
99                                 /* read path to new connection */
100                                 n = read(fd, name, sizeof(name) - 1);
101                                 if (n < 0)
102                                         return -1;
103                                 if (n == 0)
104                                         continue;
105                                 name[n] = 0;
106
107                                 /* open new connection */
108                                 _sock_srvname(file, name);
109                                 nfd = open(file, O_RDWR);
110                                 if (nfd < 0)
111                                         continue;
112
113                                 /* confirm opening on new connection */
114                                 if (write(nfd, name, strlen(name)) > 0)
115                                         break;
116
117                                 close(nfd);
118                         }
119
120                         nr = _sock_newrock(nfd);
121                         if (nr == 0) {
122                                 close(nfd);
123                                 return -1;
124                         }
125                         nr->domain = r->domain;
126                         nr->stype = r->stype;
127                         nr->protocol = r->protocol;
128
129                         return nfd;
130                 default:
131                         errno = EOPNOTSUPP;
132                         return -1;
133         }
134 }
135
136 libc_hidden_def(accept)