parlib: Expand our printf hacks
[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 <fcntl.h>
12 #include <iplib/iplib.h>
13 #include <parlib/parlib.h>
14 #include <signal.h>
15 #include <stdio.h>
16 #include <unistd.h>
17
18 static int nettrans(char *, char *, int na, char *, int);
19
20 enum {
21         Maxpath = 256,
22 };
23
24 /* Helper, given a net directory (translated from an addr/dialstring) by
25  * nettrans), clones a conversation, returns the ctl, and optionally fills in
26  * dir with the path of the directory (e.g. /net/tcp/1/).
27  *
28  * Returns the ctl FD, or -1 on error.  err_func is the function name to print
29  * for error output. */
30 static int __clone9(char *netdir, char *dir, char *err_func, int flags)
31 {
32         int ctl, n, m;
33         char buf[Maxpath];
34         char *cp;
35
36         /* get a control channel */
37         ctl = open(netdir, O_RDWR);
38         if (ctl < 0) {
39                 fprintf(stderr, "%s opening %s: %r\n", err_func, netdir);
40                 return -1;
41         }
42         cp = strrchr(netdir, '/');
43         if (cp == NULL) {
44                 fprintf(stderr, "%s arg format %s\n", err_func, netdir);
45                 close(ctl);
46                 return -1;
47         }
48         *cp = 0;
49
50         /* find out which line we have */
51         n = snprintf(buf, sizeof(buf), "%s/", netdir);
52         m = read(ctl, &buf[n], sizeof(buf) - n - 1);
53         if (m <= 0) {
54                 fprintf(stderr, "%s reading %s: %r\n", err_func, netdir);
55                 close(ctl);
56                 return -1;
57         }
58         buf[n + m] = 0;
59
60         /* return directory etc. */
61         if (dir) {
62                 strncpy(dir, buf, NETPATHLEN);
63                 dir[NETPATHLEN - 1] = 0;
64         }
65         return ctl;
66 }
67
68 /* Clones a new network connection for a given dialstring (e.g. tcp!*!22). */
69 int clone9(char *addr, char *dir, int flags)
70 {
71         char netdir[Maxpath];
72         char naddr[Maxpath];
73
74         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
75                 return -1;
76         return __clone9(addr, dir, "clone", flags);
77 }
78
79 /*
80  *  announce a network service.
81  */
82 int announce9(char *addr, char *dir, int flags)
83 {
84         int ctl, n;
85         char buf[Maxpath];
86         char netdir[Maxpath];
87         char naddr[Maxpath];
88
89         /*
90          *  translate the address
91          */
92         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
93                 return -1;
94
95         ctl = __clone9(netdir, dir, "announce", flags);
96         if (ctl < 0)
97                 return -1;
98
99         /*
100          *  make the call
101          */
102         n = snprintf(buf, sizeof(buf), "announce %s", naddr);
103         if (write(ctl, buf, n) != n) {
104                 fprintf(stderr, "announce writing %s: %r\n", netdir);
105                 close(ctl);
106                 return -1;
107         }
108
109         return ctl;
110 }
111
112 /* Gets a conversation and bypasses the protocol layer */
113 int bypass9(char *addr, char *conv_dir, int flags)
114 {
115         int ctl, n;
116         char buf[Maxpath];
117         char netdir[Maxpath];
118         char naddr[Maxpath];
119
120         if (nettrans(addr, naddr, sizeof(naddr), netdir, sizeof(netdir)) < 0)
121                 return -1;
122         ctl = __clone9(netdir, conv_dir, "bypass", flags);
123         if (ctl < 0)
124                 return -1;
125         n = snprintf(buf, sizeof(buf), "bypass %s", naddr);
126         if (write(ctl, buf, n) != n) {
127                 fprintf(stderr, "bypass writing %s: %r\n", netdir);
128                 close(ctl);
129                 return -1;
130         }
131         return ctl;
132 }
133
134 /*
135  *  listen for an incoming call
136  */
137 int listen9(char *dir, char *newdir, int flags)
138 {
139         int ctl, n, m;
140         char buf[Maxpath];
141         char *cp;
142
143         /*
144          *  open listen, wait for a call
145          */
146         snprintf(buf, sizeof(buf), "%s/listen", dir);
147         ctl = open(buf, O_RDWR | (flags & O_NONBLOCK));
148         if (ctl < 0) {
149                 if ((errno != EAGAIN) && (errno != EWOULDBLOCK))
150                         fprintf(stderr, "listen opening %s: %r\n", buf);
151                 return -1;
152         }
153
154         /*
155          *  find out which line we have
156          */
157         strncpy(buf, dir, sizeof(buf) - 1);
158         buf[sizeof(buf) - 1] = 0;
159         cp = strrchr(buf, '/');
160         if (cp == NULL) {
161                 close(ctl);
162                 fprintf(stderr, "listen arg format %s\n", dir);
163                 return -1;
164         }
165         *++cp = 0;
166         n = cp - buf;
167         m = read(ctl, cp, sizeof(buf) - n - 1);
168         if (m <= 0) {
169                 close(ctl);
170                 fprintf(stderr, "listen reading %s/listen: %r\n", dir);
171                 return -1;
172         }
173         buf[n + m] = 0;
174
175         /*
176          *  return directory etc.
177          */
178         if (newdir) {
179                 strncpy(newdir, buf, NETPATHLEN);
180                 newdir[NETPATHLEN - 1] = 0;
181         }
182         return ctl;
183 }
184
185 /*
186  *  accept a call, return an fd to the open data file
187  */
188 int accept9(int ctl, char *dir)
189 {
190         char buf[Maxpath];
191         char *num;
192         long n;
193
194         num = strrchr(dir, '/');
195         if (num == NULL)
196                 num = dir;
197         else
198                 num++;
199
200         n = snprintf(buf, sizeof(buf), "accept %s", num);
201         /* ignore return value, network might not need accepts */
202         write(ctl, buf, n);
203
204         snprintf(buf, sizeof(buf), "%s/data", dir);
205         return open(buf, O_RDWR);
206 }
207
208 /*
209  *  reject a call, tell device the reason for the rejection
210  */
211 int reject9(int ctl, char *dir, char *cause)
212 {
213         char buf[Maxpath];
214         char *num;
215         long n;
216
217         num = strrchr(dir, '/');
218         if (num == 0)
219                 num = dir;
220         else
221                 num++;
222         snprintf(buf, sizeof(buf), "reject %s %s", num, cause);
223         n = strlen(buf);
224         if (write(ctl, buf, n) != n)
225                 return -1;
226         return 0;
227 }
228
229 /*
230  *  perform the identity translation (in case we can't reach cs)
231  */
232 static int identtrans(char *netdir, char *addr, char *naddr, int na, char *file,
233                       int nf)
234 {
235         char proto[Maxpath];
236         char *p;
237
238         /* parse the protocol */
239         strncpy(proto, addr, sizeof(proto));
240         proto[sizeof(proto) - 1] = 0;
241         p = strchr(proto, '!');
242         if (p)
243                 *p++ = 0;
244
245         snprintf(file, nf, "%s/%s/clone", netdir, proto);
246         strncpy(naddr, p, na);
247         naddr[na - 1] = 0;
248
249         return 1;
250 }
251
252 /*
253  *  call up the connection server and get a translation
254  */
255 static int nettrans(char *addr, char *naddr, int na, char *file, int nf)
256 {
257         int i, fd;
258         char buf[Maxpath];
259         char netdir[Maxpath];
260         char *p, *p2;
261         long n;
262
263         /*
264          *  parse, get network directory
265          */
266         p = strchr(addr, '!');
267         if (p == 0) {
268                 fprintf(stderr, "bad dial string: %s\n", addr);
269                 return -1;
270         }
271         if (*addr != '/') {
272                 strncpy(netdir, "/net", sizeof(netdir));
273                 netdir[sizeof(netdir) - 1] = 0;
274         } else {
275                 for (p2 = p; *p2 != '/'; p2--)
276                         ;
277                 i = p2 - addr;
278                 if (i == 0 || i >= sizeof(netdir)) {
279                         fprintf(stderr, "bad dial string: %s\n", addr);
280                         return -1;
281                 }
282                 strncpy(netdir, addr, i);
283                 netdir[i] = 0;
284                 addr = p2 + 1;
285         }
286
287         /*
288          *  ask the connection server
289          */
290         snprintf(buf, sizeof(buf), "%s/cs", netdir);
291         fd = open(buf, O_RDWR);
292         if (fd < 0)
293                 return identtrans(netdir, addr, naddr, na, file, nf);
294         if (write(fd, addr, strlen(addr)) < 0) {
295                 close(fd);
296                 return -1;
297         }
298         lseek(fd, 0, 0);
299         n = read(fd, buf, sizeof(buf) - 1);
300         close(fd);
301         if (n <= 0)
302                 return -1;
303         buf[n] = 0;
304
305         /*
306          *  parse the reply
307          */
308         p = strchr(buf, ' ');
309         if (p == 0)
310                 return -1;
311         *p++ = 0;
312         strncpy(naddr, p, na);
313         naddr[na - 1] = 0;
314
315         if (buf[0] == '/') {
316                 p = strchr(buf + 1, '/');
317                 if (p == NULL)
318                         p = buf;
319                 else
320                         p++;
321         }
322         snprintf(file, nf, "%s/%s", netdir, p);
323         return 0;
324 }
325
326 int open_data_fd9(char *conv_dir, int flags)
327 {
328         char path_buf[MAX_PATH_LEN];
329
330         snprintf(path_buf, sizeof(path_buf), "%s/data", conv_dir);
331         return open(path_buf, O_RDWR | flags);
332 }
333
334 /* Given a conversation directory, return the "remote" or "local" port, passed
335  * as the string which.  Returns the port via *port and TRUE on success. */
336 bool get_port9(char *conv_dir, char *which, uint16_t *port)
337 {
338         /* We don't have a MAX_DIALSTRING, but MAX_PATH_LEN should be enough. */
339         char buf[MAX_PATH_LEN];
340         int local_fd;
341         int ret;
342         char *p;
343
344         snprintf(buf, sizeof(buf), "%s/%s", conv_dir, which);
345         local_fd = open(buf, O_RDONLY);
346         if (local_fd < 0)
347                 return FALSE;
348         ret = read(local_fd, buf, sizeof(buf));
349         close(local_fd);
350         if (ret <= 0)
351                 return FALSE;
352         p = strrchr(buf, '!');
353         if (!p)
354                 return FALSE;
355         p++;
356         *port = atoi(p);
357         return TRUE;
358 }