Add a close callback for socket Rocks (XCC)
[akaros.git] / tools / compilers / gcc-glibc / glibc-2.19-akaros / sysdeps / akaros / plan9_sockets.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 #include <assert.h>
18
19 /* bsd extensions */
20 #include <sys/uio.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <sys/un.h>
24 #include <arpa/inet.h>
25
26 #include "plan9_sockets.h"
27
28 void
29 _sock_ingetaddr(Rock * r, struct sockaddr_in *ip, socklen_t * alen,
30                                 const char *a)
31 {
32         int n, fd;
33         char *p;
34         char name[Ctlsize];
35
36         /* get remote address */
37         strcpy(name, r->ctl);
38         p = strrchr(name, '/');
39         strcpy(p + 1, a);
40         fd = open(name, O_RDONLY);
41         if (fd >= 0) {
42                 n = read(fd, name, sizeof(name) - 1);
43                 if (n > 0) {
44                         name[n] = 0;
45                         p = strchr(name, '!');
46                         if (p) {
47                                 *p++ = 0;
48                                 ip->sin_family = AF_INET;
49                                 ip->sin_port = htons(atoi(p));
50                                 ip->sin_addr.s_addr = inet_addr(name);
51                                 if (alen)
52                                         *alen = sizeof(struct sockaddr_in);
53                         }
54                 }
55                 close(fd);
56         }
57
58 }
59
60 /*
61  *  return ndb attribute type of an ip name
62  */
63 int _sock_ipattr(const char *name)
64 {
65         const char *p;
66         int dot = 0;
67         int alpha = 0;
68
69         for (p = name; *p; p++) {
70                 if (isdigit(*p)) ;
71                 else if (isalpha(*p) || *p == '-')
72                         alpha = 1;
73                 else if (*p == '.')
74                         dot = 1;
75                 else
76                         return Tsys;
77         }
78
79         if (alpha) {
80                 if (dot)
81                         return Tdom;
82                 else
83                         return Tsys;
84         }
85
86         if (dot)
87                 return Tip;
88         else
89                 return Tsys;
90 }
91
92 /* we can't avoid overrunning npath because we don't know how big it is. */
93 void _sock_srvname(char *npath, char *path)
94 {
95         char *p;
96
97         strcpy(npath, "/srv/UD.");
98         p = strrchr(path, '/');
99         if (p == 0)
100                 p = path;
101         else
102                 p++;
103         strcat(npath, p);
104 }
105
106 int _sock_srv(char *path, int fd)
107 {
108         int sfd;
109         char msg[8 + 256 + 1];
110
111         /* change the path to something in srv */
112         _sock_srvname(msg, path);
113
114         /* remove any previous instance */
115         unlink(msg);
116
117         /* put the fd in /srv and then close it */
118         sfd = creat(msg, 0666);
119         if (sfd < 0) {
120                 close(fd);
121                 return -1;
122         }
123         snprintf(msg, sizeof msg, "%d", fd);
124         if (write(sfd, msg, strlen(msg)) < 0) {
125                 close(sfd);
126                 close(fd);
127                 return -1;
128         }
129         close(sfd);
130         close(fd);
131         return 0;
132 }
133
134 #warning "Not threadsafe!"
135 Rock *_sock_rock;
136
137 Rock *_sock_findrock(int fd, struct stat * dp)
138 {
139         Rock *r;
140         struct stat d;
141
142         /* Skip the fstat if there are no socket rocks */
143         if (!_sock_rock)
144                 return 0;
145         /* If they pass us a struct stat, then they already did an fstat */
146         if (dp == 0) {
147                 dp = &d;
148                 fstat(fd, dp);
149         }
150         for (r = _sock_rock; r; r = r->next) {
151                 if (r->inode == dp->st_ino && r->dev == dp->st_dev)
152                         break;
153         }
154         return r;
155 }
156
157 Rock *_sock_newrock(int fd)
158 {
159         Rock *r;
160         struct stat d;
161
162         fstat(fd, &d);
163         r = _sock_findrock(fd, &d);
164         if (r == 0) {
165                 r = malloc(sizeof(Rock));
166                 if (r == 0)
167                         return 0;
168                 r->dev = d.st_dev;
169                 r->inode = d.st_ino;
170                 /* TODO: this is not thread-safe! */
171                 r->next = _sock_rock;
172                 _sock_rock = r;
173         }
174         assert(r->dev == d.st_dev);
175         assert(r->inode == d.st_ino);
176         r->domain = 0;
177         r->stype = 0;
178         r->sopts = 0;
179         r->protocol = 0;
180         memset(&r->addr, 0, sizeof(r->addr_stor));
181         r->reserved = 0;
182         memset(&r->raddr, 0, sizeof(r->raddr_stor));
183         r->ctl[0] = '\0';
184         r->other = -1;
185         r->is_listener = FALSE;
186         r->listen_fd = -1;
187         return r;
188 }
189
190 void _sock_fd_closed(int fd)
191 {
192 }
193
194 /* For a ctlfd and a few other settings, it opens and returns the corresponding
195  * datafd.  This will close cfd for you. */
196 int
197 _sock_data(int cfd, char *net, int domain, int type, int protocol, Rock ** rp)
198 {
199         int n, fd;
200         Rock *r;
201         char name[Ctlsize];
202
203         /* get the data file name */
204         n = read(cfd, name, sizeof(name) - 1);
205         if (n < 0) {
206                 close(cfd);
207                 errno = ENOBUFS;
208                 return -1;
209         }
210         name[n] = 0;
211         n = strtoul(name, 0, 0);
212         snprintf(name, sizeof name, "/net/%s/%d/data", net, n);
213
214         /* open data file */
215         fd = open(name, O_RDWR);
216         close(cfd);     /* close this no matter what */
217         if (fd < 0) {
218                 errno = ENOBUFS;
219                 return -1;
220         }
221
222         /* hide stuff under the rock */
223         snprintf(name, sizeof name, "/net/%s/%d/ctl", net, n);
224         r = _sock_newrock(fd);
225         if (r == 0) {
226                 errno = ENOBUFS;
227                 close(fd);
228                 return -1;
229         }
230         if (rp)
231                 *rp = r;
232         memset(&r->raddr, 0, sizeof(r->raddr_stor));
233         memset(&r->addr, 0, sizeof(r->addr_stor));
234         r->domain = domain;
235         r->stype = _sock_strip_opts(type);
236         r->sopts = _sock_get_opts(type);
237         r->protocol = protocol;
238         strcpy(r->ctl, name);
239         return fd;
240 }
241
242 /* Takes network-byte ordered IPv4 addr and writes it into buf, in the plan 9 IP
243  * addr format */
244 void naddr_to_plan9addr(uint32_t sin_addr, uint8_t * buf)
245 {
246         uint8_t *sin_bytes = (uint8_t *) & sin_addr;
247         memset(buf, 0, 10);
248         buf += 10;
249         buf[0] = 0xff;
250         buf[1] = 0xff;
251         buf += 2;
252         buf[0] = sin_bytes[0];  /* e.g. 192 */
253         buf[1] = sin_bytes[1];  /* e.g. 168 */
254         buf[2] = sin_bytes[2];  /* e.g.   0 */
255         buf[3] = sin_bytes[3];  /* e.g.   1 */
256 }
257
258 /* does v4 only */
259 uint32_t plan9addr_to_naddr(uint8_t * buf)
260 {
261         buf += 12;
262         return (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0);
263 }
264
265 /* Returns a rock* if the socket exists and is UDP */
266 Rock *udp_sock_get_rock(int fd)
267 {
268         Rock *r = _sock_findrock(fd, 0);
269         if (!r) {
270                 errno = ENOTSOCK;
271                 return 0;
272         }
273         if ((r->domain == PF_INET) && (r->stype == SOCK_DGRAM))
274                 return r;
275         else
276                 return 0;
277 }
278
279 /* In Linux, socket options are multiplexed in the socket type. */
280 int _sock_strip_opts(int type)
281 {
282         return type & ~(SOCK_NONBLOCK | SOCK_CLOEXEC);
283 }
284
285 int _sock_get_opts(int type)
286 {
287         return type & (SOCK_NONBLOCK | SOCK_CLOEXEC);
288 }
289
290 /* Used by user/iplib (e.g. epoll).  Opens and returns the FD for the
291  * conversation's listen fd, which the caller needs to close.  Returns -1 if the
292  * FD is not a listener. */
293 int _sock_get_listen_fd(int sock_fd)
294 {
295         char listen_file[Ctlsize + 3];
296         char *x, *last_ctl;
297         Rock *r = _sock_findrock(sock_fd, 0);
298         int ret;
299
300         if (!r)
301                 return -1;
302         if (!r->is_listener)
303                 return -1;
304         /* We want an FD for the "listen" file.  This is for epoll.  We
305          * could optimize and only do this on demand, but whatever. */
306         strncpy(listen_file, r->ctl, sizeof(listen_file));
307         /* We want the conversation directory.  We can find the last "ctl"
308          * in the CTL name (they could have mounted at /ctlfoo/net/) */
309         x = listen_file;
310         do {
311                 last_ctl = x;
312                 x++;    /* move forward enough to not find the same "ctl" */
313                 x = strstr(x, "ctl");
314         } while (x);
315         /* last_ctl is either listen_file (if we never found ctl, which should never
316          * happen) or it points at the 'c' in the last "ctl". */
317         assert(last_ctl != listen_file);
318         strcpy(last_ctl, "listen");
319         ret = open(listen_file, O_PATH);
320         /* Probably a bug in the rock code (or the kernel!) if we couldn't walk to
321          * our listen. */
322         assert(ret >= 0);
323         r->listen_fd = ret;
324         return ret;
325 }
326
327 /* Used by user/iplib (e.g. epoll).  Looks up a previously opened FD for the
328  * listen file for this conversation.  Returns -1 if the FD is not a listener
329  * with an already-opened listen FD. */
330 int _sock_lookup_listen_fd(int sock_fd)
331 {
332         Rock *r = _sock_findrock(sock_fd, 0);
333
334         if (!r)
335                 return -1;
336         if (!r->is_listener)
337                 return -1;
338         return r->listen_fd;
339 }