]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libc/9sys/dial.c
allow # netpath in dial string
[plan9front.git] / sys / src / libc / 9sys / dial.c
1 #include <u.h>
2 #include <libc.h>
3
4 typedef struct DS DS;
5
6 static int      call(char*, char*, DS*);
7 static int      csdial(DS*);
8 static void     _dial_string_parse(char*, DS*);
9
10 enum
11 {
12         Maxstring       = 128,
13         Maxpath         = 256,
14 };
15
16 struct DS {
17         /* dist string */
18         char    buf[Maxstring];
19         char    *netdir;
20         char    *proto;
21         char    *rem;
22
23         /* other args */
24         char    *local;
25         char    *dir;
26         int     *cfdp;
27 };
28
29
30 /*
31  *  the dialstring is of the form '[/net/]proto!dest'
32  */
33 int
34 dial(char *dest, char *local, char *dir, int *cfdp)
35 {
36         DS ds;
37         int rv;
38         char err[ERRMAX], alterr[ERRMAX];
39
40         ds.local = local;
41         ds.dir = dir;
42         ds.cfdp = cfdp;
43
44         _dial_string_parse(dest, &ds);
45         if(ds.netdir)
46                 return csdial(&ds);
47
48         ds.netdir = "/net";
49         rv = csdial(&ds);
50         if(rv >= 0)
51                 return rv;
52         err[0] = '\0';
53         errstr(err, sizeof err);
54         if(strstr(err, "refused") != 0){
55                 werrstr("%s", err);
56                 return rv;
57         }
58         ds.netdir = "/net.alt";
59         rv = csdial(&ds);
60         if(rv >= 0)
61                 return rv;
62
63         alterr[0] = 0;
64         errstr(alterr, sizeof alterr);
65         if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
66                 werrstr("%s", err);
67         else
68                 werrstr("%s", alterr);
69         return rv;
70 }
71
72 static int
73 csdial(DS *ds)
74 {
75         int n, fd, rv;
76         char *p, buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
77
78         /*
79          *  open connection server
80          */
81         snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
82         fd = open(buf, ORDWR);
83         if(fd < 0){
84                 /* no connection server, don't translate */
85                 snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
86                 return call(clone, ds->rem, ds);
87         }
88
89         /*
90          *  ask connection server to translate
91          */
92         snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
93         if(write(fd, buf, strlen(buf)) < 0){
94                 close(fd);
95                 return -1;
96         }
97
98         /*
99          *  loop through each address from the connection server till
100          *  we get one that works.
101          */
102         *besterr = 0;
103         rv = -1;
104         seek(fd, 0, 0);
105         while((n = read(fd, buf, sizeof(buf) - 1)) > 0){
106                 buf[n] = 0;
107                 p = strchr(buf, ' ');
108                 if(p == 0)
109                         continue;
110                 *p++ = 0;
111                 rv = call(buf, p, ds);
112                 if(rv >= 0)
113                         break;
114                 err[0] = '\0';
115                 errstr(err, sizeof err);
116                 if(strstr(err, "does not exist") == 0)
117                         strcpy(besterr, err);
118         }
119         close(fd);
120
121         if(rv < 0 && *besterr)
122                 werrstr("%s", besterr);
123         else
124                 werrstr("%s", err);
125         return rv;
126 }
127
128 static int
129 call(char *clone, char *dest, DS *ds)
130 {
131         int fd, cfd, n;
132         char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
133
134         /* because cs is in a different name space, replace the mount point */
135         if(*clone == '/' || *clone == '#'){
136                 p = strchr(clone+1, '/');
137                 if(p == nil)
138                         p = clone;
139                 else 
140                         p++;
141         } else
142                 p = clone;
143         snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
144
145         cfd = open(cname, ORDWR);
146         if(cfd < 0)
147                 return -1;
148
149         /* get directory name */
150         n = read(cfd, name, sizeof(name)-1);
151         if(n < 0){
152                 close(cfd);
153                 return -1;
154         }
155         name[n] = 0;
156         for(p = name; *p == ' '; p++)
157                 ;
158         snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
159         p = strrchr(cname, '/');
160         *p = 0;
161         if(ds->dir)
162                 snprint(ds->dir, NETPATHLEN, "%s/%s", cname, name);
163         snprint(data, sizeof(data), "%s/%s/data", cname, name);
164
165         /* connect */
166         if(ds->local)
167                 snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
168         else
169                 snprint(name, sizeof(name), "connect %s", dest);
170         if(write(cfd, name, strlen(name)) < 0){
171                 close(cfd);
172                 return -1;
173         }
174
175         /* open data connection */
176         fd = open(data, ORDWR);
177         if(fd < 0){
178                 close(cfd);
179                 return -1;
180         }
181         if(ds->cfdp)
182                 *ds->cfdp = cfd;
183         else
184                 close(cfd);
185         return fd;
186 }
187
188 /*
189  *  parse a dial string
190  */
191 static void
192 _dial_string_parse(char *str, DS *ds)
193 {
194         char *p, *p2;
195
196         strncpy(ds->buf, str, Maxstring);
197         ds->buf[Maxstring-1] = 0;
198
199         p = strchr(ds->buf, '!');
200         if(p == 0) {
201                 ds->netdir = 0;
202                 ds->proto = "net";
203                 ds->rem = ds->buf;
204         } else {
205                 p2 = ds->buf;
206                 if(*p2 == '#'){
207                         p2 = strchr(p2, '/');
208                         if(p2 == nil || p2 > p)
209                                 p2 = ds->buf;
210                 }
211                 if(*p2 != '/'){
212                         ds->netdir = 0;
213                         ds->proto = ds->buf;
214                 } else {
215                         for(p2 = p; *p2 != '/'; p2--)
216                                 ;
217                         *p2++ = 0;
218                         ds->netdir = ds->buf;
219                         ds->proto = p2;
220                 }
221                 *p = 0;
222                 ds->rem = p + 1;
223         }
224 }