iplib: Support non-blocking listens
[akaros.git] / user / iplib / announce.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 #include <stdlib.h>
10
11 #include <stdio.h>
12 #include <fcntl.h>
13 #include <parlib/parlib.h>
14 #include <unistd.h>
15 #include <signal.h>
16 #include <iplib/iplib.h>
17
18 static int      nettrans(char*, char*, int na, char*, int);
19
20 enum
21 {
22         Maxpath=        256,
23 };
24
25 /*
26  *  announce a network service.
27  */
28 int
29 announce(char *addr, char *dir)
30 {
31         int ctl, n, m;
32         char buf[Maxpath];
33         char buf2[Maxpath];
34         char netdir[Maxpath];
35         char naddr[Maxpath];
36         char *cp;
37
38         /*
39          *  translate the address
40          */
41         if(nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
42                 return -1;
43
44         /*
45          * get a control channel
46          */
47         ctl = open(netdir, O_RDWR);
48         if(ctl<0){
49                 fprintf(stderr,"announce opening %s: %r\n", netdir);
50                 return -1;
51         }
52         cp = strrchr(netdir, '/');
53         if(cp == NULL){
54                 fprintf(stderr,"announce arg format %s\n", netdir);
55                 close(ctl);
56                 return -1;
57         }
58         *cp = 0;
59
60         /*
61          *  find out which line we have
62          */
63         n = snprintf(buf, sizeof(buf), "%s/", netdir);
64         m = read(ctl, &buf[n], sizeof(buf)-n-1);
65         if(m <= 0){
66                 fprintf(stderr,"announce reading %s: %r\n", netdir);
67                 close(ctl);
68                 return -1;
69         }
70         buf[n+m] = 0;
71
72         /*
73          *  make the call
74          */
75         n = snprintf(buf2, sizeof(buf2), "announce %s", naddr);
76         if(write(ctl, buf2, n)!=n){
77                 fprintf(stderr,"announce writing %s: %r\n", netdir);
78                 close(ctl);
79                 return -1;
80         }
81
82         /*
83          *  return directory etc.
84          */
85         if(dir){
86                 strncpy(dir, buf, NETPATHLEN);
87                 dir[NETPATHLEN-1] = 0;
88         }
89         return ctl;
90 }
91
92 /*
93  *  listen for an incoming call
94  */
95 int
96 listen(char *dir, char *newdir)
97 {
98         int ctl, n, m;
99         char buf[Maxpath];
100         char *cp;
101
102         /*
103          *  open listen, wait for a call
104          */
105         snprintf(buf, sizeof(buf), "%s/listen", dir);
106         ctl = open(buf, O_RDWR);
107         if(ctl < 0){
108                 if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
109                         fprintf(stderr,"listen opening %s: %r\n", buf);
110                 return -1;
111         }
112
113         /*
114          *  find out which line we have
115          */
116         strncpy(buf, dir, sizeof(buf) - 1);
117         buf[sizeof(buf) - 1] = 0;
118         cp = strrchr(buf, '/');
119         if(cp == NULL){
120                 close(ctl);
121                 fprintf(stderr,"listen arg format %s\n", dir);
122                 return -1;
123         }
124         *++cp = 0;
125         n = cp-buf;
126         m = read(ctl, cp, sizeof(buf) - n - 1);
127         if(m <= 0){
128                 close(ctl);
129                 fprintf(stderr,"listen reading %s/listen: %r\n", dir);
130                 return -1;
131         }
132         buf[n+m] = 0;
133
134         /*
135          *  return directory etc.
136          */
137         if(newdir){
138                 strncpy(newdir, buf, NETPATHLEN);
139                 newdir[NETPATHLEN-1] = 0;
140         }
141         return ctl;
142
143 }
144
145 /*
146  *  accept a call, return an fd to the open data file
147  */
148 int
149 accept(int ctl, char *dir)
150 {
151         char buf[Maxpath];
152         char *num;
153         long n;
154
155         num = strrchr(dir, '/');
156         if(num == NULL)
157                 num = dir;
158         else
159                 num++;
160
161         n = snprintf(buf, sizeof(buf), "accept %s", num);
162         write(ctl, buf, n); /* ignore return value, network might not need accepts */
163
164         snprintf(buf, sizeof(buf), "%s/data", dir);
165         return open(buf, O_RDWR);
166 }
167
168 /*
169  *  reject a call, tell device the reason for the rejection
170  */
171 int
172 reject(int ctl, char *dir, char *cause)
173 {
174         char buf[Maxpath];
175         char *num;
176         long n;
177
178         num = strrchr(dir, '/');
179         if(num == 0)
180                 num = dir;
181         else
182                 num++;
183         snprintf(buf, sizeof(buf), "reject %s %s", num, cause);
184         n = strlen(buf);
185         if(write(ctl, buf, n) != n)
186                 return -1;
187         return 0;
188 }
189
190 /*
191  *  perform the identity translation (in case we can't reach cs)
192  */
193 static int
194 identtrans(char *netdir, char *addr, char *naddr, int na, char *file, int nf)
195 {
196         char proto[Maxpath];
197         char *p;
198
199         /* parse the protocol */
200         strncpy(proto, addr, sizeof(proto));
201         proto[sizeof(proto)-1] = 0;
202         p = strchr(proto, '!');
203         if(p)
204                 *p++ = 0;
205
206         snprintf(file, nf, "%s/%s/clone", netdir, proto);
207         strncpy(naddr, p, na);
208         naddr[na-1] = 0;
209
210         return 1;
211 }
212
213 /*
214  *  call up the connection server and get a translation
215  */
216 static int
217 nettrans(char *addr, char *naddr, int na, char *file, int nf)
218 {
219         int i, fd;
220         char buf[Maxpath];
221         char netdir[Maxpath];
222         char *p, *p2;
223         long n;
224
225         /*
226          *  parse, get network directory
227          */
228         p = strchr(addr, '!');
229         if(p == 0){
230                 fprintf(stderr,"bad dial string: %s\n", addr);
231                 return -1;
232         }
233         if(*addr != '/'){
234                 strncpy(netdir, "/net", sizeof(netdir));
235                 netdir[sizeof(netdir) - 1] = 0;
236         } else {
237                 for(p2 = p; *p2 != '/'; p2--)
238                         ;
239                 i = p2 - addr;
240                 if(i == 0 || i >= sizeof(netdir)){
241                         fprintf(stderr,"bad dial string: %s\n", addr);
242                         return -1;
243                 }
244                 strncpy(netdir, addr, i);
245                 netdir[i] = 0;
246                 addr = p2 + 1;
247         }
248
249         /*
250          *  ask the connection server
251          */
252         snprintf(buf, sizeof(buf), "%s/cs", netdir);
253         fd = open(buf, O_RDWR);
254         if(fd < 0)
255                 return identtrans(netdir, addr, naddr, na, file, nf);
256         if(write(fd, addr, strlen(addr)) < 0){
257                 close(fd);
258                 return -1;
259         }
260         lseek(fd, 0, 0);
261         n = read(fd, buf, sizeof(buf)-1);
262         close(fd);
263         if(n <= 0)
264                 return -1;
265         buf[n] = 0;
266
267         /*
268          *  parse the reply
269          */
270         p = strchr(buf, ' ');
271         if(p == 0)
272                 return -1;
273         *p++ = 0;
274         strncpy(naddr, p, na);
275         naddr[na-1] = 0;
276
277         if(buf[0] == '/'){
278                 p = strchr(buf+1, '/');
279                 if(p == NULL)
280                         p = buf;
281                 else 
282                         p++;
283         }
284         snprintf(file, nf, "%s/%s", netdir, p);
285         return 0;
286 }