xref: /plan9/sys/src/cmd/ssh2/dial.c (revision 63afb9a5d3f910047231762bcce0ee49fed3d07c)
1*63afb9a5SDavid du Colombier /*
2*63afb9a5SDavid du Colombier  * dial - connect to a service (parallel version)
3*63afb9a5SDavid du Colombier  */
4*63afb9a5SDavid du Colombier #include <u.h>
5*63afb9a5SDavid du Colombier #include <libc.h>
6*63afb9a5SDavid du Colombier #include <ctype.h>
7*63afb9a5SDavid du Colombier 
8*63afb9a5SDavid du Colombier typedef struct Conn Conn;
9*63afb9a5SDavid du Colombier typedef struct Dest Dest;
10*63afb9a5SDavid du Colombier typedef struct DS DS;
11*63afb9a5SDavid du Colombier 
12*63afb9a5SDavid du Colombier enum
13*63afb9a5SDavid du Colombier {
14*63afb9a5SDavid du Colombier 	Maxstring	= 128,
15*63afb9a5SDavid du Colombier 	Maxpath		= 256,
16*63afb9a5SDavid du Colombier 
17*63afb9a5SDavid du Colombier 	Maxcsreply	= 64*80,	/* this is probably overly generous */
18*63afb9a5SDavid du Colombier 	/*
19*63afb9a5SDavid du Colombier 	 * this should be a plausible slight overestimate for non-interactive
20*63afb9a5SDavid du Colombier 	 * use even if it's ridiculously long for interactive use.
21*63afb9a5SDavid du Colombier 	 */
22*63afb9a5SDavid du Colombier 	Maxconnms	= 2*60*1000,	/* 2 minutes */
23*63afb9a5SDavid du Colombier };
24*63afb9a5SDavid du Colombier 
25*63afb9a5SDavid du Colombier struct DS {
26*63afb9a5SDavid du Colombier 	/* dist string */
27*63afb9a5SDavid du Colombier 	char	buf[Maxstring];
28*63afb9a5SDavid du Colombier 	char	*netdir;		/* e.g., /net.alt */
29*63afb9a5SDavid du Colombier 	char	*proto;			/* e.g., tcp */
30*63afb9a5SDavid du Colombier 	char	*rem;			/* e.g., host!service */
31*63afb9a5SDavid du Colombier 
32*63afb9a5SDavid du Colombier 	/* other args */
33*63afb9a5SDavid du Colombier 	char	*local;
34*63afb9a5SDavid du Colombier 	char	*dir;
35*63afb9a5SDavid du Colombier 	int	*cfdp;
36*63afb9a5SDavid du Colombier };
37*63afb9a5SDavid du Colombier 
38*63afb9a5SDavid du Colombier /*
39*63afb9a5SDavid du Colombier  * malloc these; they need to be writable by this proc & all children.
40*63afb9a5SDavid du Colombier  * the stack is private to each proc, and static allocation in the data
41*63afb9a5SDavid du Colombier  * segment would not permit concurrent dials within a multi-process program.
42*63afb9a5SDavid du Colombier  */
43*63afb9a5SDavid du Colombier struct Conn {
44*63afb9a5SDavid du Colombier 	int	pid;
45*63afb9a5SDavid du Colombier 	int	dead;
46*63afb9a5SDavid du Colombier 
47*63afb9a5SDavid du Colombier 	int	dfd;
48*63afb9a5SDavid du Colombier 	int	cfd;
49*63afb9a5SDavid du Colombier 	char	dir[NETPATHLEN];
50*63afb9a5SDavid du Colombier 	char	err[ERRMAX];
51*63afb9a5SDavid du Colombier };
52*63afb9a5SDavid du Colombier struct Dest {
53*63afb9a5SDavid du Colombier 	Conn	*conn;			/* allocated array */
54*63afb9a5SDavid du Colombier 	Conn	*connend;
55*63afb9a5SDavid du Colombier 	int	nkid;
56*63afb9a5SDavid du Colombier 
57*63afb9a5SDavid du Colombier 	long	oalarm;
58*63afb9a5SDavid du Colombier 	int	naddrs;
59*63afb9a5SDavid du Colombier 
60*63afb9a5SDavid du Colombier 	QLock	winlck;
61*63afb9a5SDavid du Colombier 	int	winner;			/* index into conn[] */
62*63afb9a5SDavid du Colombier 
63*63afb9a5SDavid du Colombier 	char	*nextaddr;
64*63afb9a5SDavid du Colombier 	char	addrlist[Maxcsreply];
65*63afb9a5SDavid du Colombier };
66*63afb9a5SDavid du Colombier 
67*63afb9a5SDavid du Colombier static int	call(char*, char*, DS*, Dest*, Conn*);
68*63afb9a5SDavid du Colombier static int	csdial(DS*);
69*63afb9a5SDavid du Colombier static void	_dial_string_parse(char*, DS*);
70*63afb9a5SDavid du Colombier 
71*63afb9a5SDavid du Colombier 
72*63afb9a5SDavid du Colombier /*
73*63afb9a5SDavid du Colombier  *  the dialstring is of the form '[/net/]proto!dest'
74*63afb9a5SDavid du Colombier  */
75*63afb9a5SDavid du Colombier static int
76*63afb9a5SDavid du Colombier dialimpl(char *dest, char *local, char *dir, int *cfdp)
77*63afb9a5SDavid du Colombier {
78*63afb9a5SDavid du Colombier 	DS ds;
79*63afb9a5SDavid du Colombier 	int rv;
80*63afb9a5SDavid du Colombier 	char err[ERRMAX], alterr[ERRMAX];
81*63afb9a5SDavid du Colombier 
82*63afb9a5SDavid du Colombier 	ds.local = local;
83*63afb9a5SDavid du Colombier 	ds.dir = dir;
84*63afb9a5SDavid du Colombier 	ds.cfdp = cfdp;
85*63afb9a5SDavid du Colombier 
86*63afb9a5SDavid du Colombier 	_dial_string_parse(dest, &ds);
87*63afb9a5SDavid du Colombier 	if(ds.netdir)
88*63afb9a5SDavid du Colombier 		return csdial(&ds);
89*63afb9a5SDavid du Colombier 
90*63afb9a5SDavid du Colombier 	ds.netdir = "/net";
91*63afb9a5SDavid du Colombier 	rv = csdial(&ds);
92*63afb9a5SDavid du Colombier 	if(rv >= 0)
93*63afb9a5SDavid du Colombier 		return rv;
94*63afb9a5SDavid du Colombier 	err[0] = '\0';
95*63afb9a5SDavid du Colombier 	errstr(err, sizeof err);
96*63afb9a5SDavid du Colombier 	if(strstr(err, "refused") != 0){
97*63afb9a5SDavid du Colombier 		werrstr("%s", err);
98*63afb9a5SDavid du Colombier 		return rv;
99*63afb9a5SDavid du Colombier 	}
100*63afb9a5SDavid du Colombier 	ds.netdir = "/net.alt";
101*63afb9a5SDavid du Colombier 	rv = csdial(&ds);
102*63afb9a5SDavid du Colombier 	if(rv >= 0)
103*63afb9a5SDavid du Colombier 		return rv;
104*63afb9a5SDavid du Colombier 
105*63afb9a5SDavid du Colombier 	alterr[0] = 0;
106*63afb9a5SDavid du Colombier 	errstr(alterr, sizeof alterr);
107*63afb9a5SDavid du Colombier 	if(strstr(alterr, "translate") || strstr(alterr, "does not exist"))
108*63afb9a5SDavid du Colombier 		werrstr("%s", err);
109*63afb9a5SDavid du Colombier 	else
110*63afb9a5SDavid du Colombier 		werrstr("%s", alterr);
111*63afb9a5SDavid du Colombier 	return rv;
112*63afb9a5SDavid du Colombier }
113*63afb9a5SDavid du Colombier 
114*63afb9a5SDavid du Colombier /*
115*63afb9a5SDavid du Colombier  * the thread library can't cope with rfork(RFMEM|RFPROC),
116*63afb9a5SDavid du Colombier  * so it must override this with a private version of dial.
117*63afb9a5SDavid du Colombier  */
118*63afb9a5SDavid du Colombier int (*_dial)(char *, char *, char *, int *) = dialimpl;
119*63afb9a5SDavid du Colombier 
120*63afb9a5SDavid du Colombier int
121*63afb9a5SDavid du Colombier dial(char *dest, char *local, char *dir, int *cfdp)
122*63afb9a5SDavid du Colombier {
123*63afb9a5SDavid du Colombier 	return (*_dial)(dest, local, dir, cfdp);
124*63afb9a5SDavid du Colombier }
125*63afb9a5SDavid du Colombier 
126*63afb9a5SDavid du Colombier static int
127*63afb9a5SDavid du Colombier connsalloc(Dest *dp, int addrs)
128*63afb9a5SDavid du Colombier {
129*63afb9a5SDavid du Colombier 	Conn *conn;
130*63afb9a5SDavid du Colombier 
131*63afb9a5SDavid du Colombier 	free(dp->conn);
132*63afb9a5SDavid du Colombier 	dp->connend = nil;
133*63afb9a5SDavid du Colombier 	assert(addrs > 0);
134*63afb9a5SDavid du Colombier 
135*63afb9a5SDavid du Colombier 	dp->conn = mallocz(addrs * sizeof *dp->conn, 1);
136*63afb9a5SDavid du Colombier 	if(dp->conn == nil)
137*63afb9a5SDavid du Colombier 		return -1;
138*63afb9a5SDavid du Colombier 	dp->connend = dp->conn + addrs;
139*63afb9a5SDavid du Colombier 	for(conn = dp->conn; conn < dp->connend; conn++)
140*63afb9a5SDavid du Colombier 		conn->cfd = conn->dfd = -1;
141*63afb9a5SDavid du Colombier 	return 0;
142*63afb9a5SDavid du Colombier }
143*63afb9a5SDavid du Colombier 
144*63afb9a5SDavid du Colombier static void
145*63afb9a5SDavid du Colombier freedest(Dest *dp)
146*63afb9a5SDavid du Colombier {
147*63afb9a5SDavid du Colombier 	long oalarm;
148*63afb9a5SDavid du Colombier 
149*63afb9a5SDavid du Colombier 	if (dp == nil)
150*63afb9a5SDavid du Colombier 		return;
151*63afb9a5SDavid du Colombier 	oalarm = dp->oalarm;
152*63afb9a5SDavid du Colombier 	free(dp->conn);
153*63afb9a5SDavid du Colombier 	free(dp);
154*63afb9a5SDavid du Colombier 	if (oalarm >= 0)
155*63afb9a5SDavid du Colombier 		alarm(oalarm);
156*63afb9a5SDavid du Colombier }
157*63afb9a5SDavid du Colombier 
158*63afb9a5SDavid du Colombier static void
159*63afb9a5SDavid du Colombier closeopenfd(int *fdp)
160*63afb9a5SDavid du Colombier {
161*63afb9a5SDavid du Colombier 	if (*fdp >= 0) {
162*63afb9a5SDavid du Colombier 		close(*fdp);
163*63afb9a5SDavid du Colombier 		*fdp = -1;
164*63afb9a5SDavid du Colombier 	}
165*63afb9a5SDavid du Colombier }
166*63afb9a5SDavid du Colombier 
167*63afb9a5SDavid du Colombier static void
168*63afb9a5SDavid du Colombier notedeath(Dest *dp, char *exitsts)
169*63afb9a5SDavid du Colombier {
170*63afb9a5SDavid du Colombier 	int i, n, pid;
171*63afb9a5SDavid du Colombier 	char *fields[5];			/* pid + 3 times + error */
172*63afb9a5SDavid du Colombier 	Conn *conn;
173*63afb9a5SDavid du Colombier 
174*63afb9a5SDavid du Colombier 	for (i = 0; i < nelem(fields); i++)
175*63afb9a5SDavid du Colombier 		fields[i] = "";
176*63afb9a5SDavid du Colombier 	n = tokenize(exitsts, fields, nelem(fields));
177*63afb9a5SDavid du Colombier 	if (n < 4)
178*63afb9a5SDavid du Colombier 		return;
179*63afb9a5SDavid du Colombier 	pid = atoi(fields[0]);
180*63afb9a5SDavid du Colombier 	if (pid <= 0)
181*63afb9a5SDavid du Colombier 		return;
182*63afb9a5SDavid du Colombier 	for (conn = dp->conn; conn < dp->connend; conn++)
183*63afb9a5SDavid du Colombier 		if (conn->pid == pid && !conn->dead) {  /* it's one we know? */
184*63afb9a5SDavid du Colombier 			if (conn - dp->conn != dp->winner) {
185*63afb9a5SDavid du Colombier 				closeopenfd(&conn->dfd);
186*63afb9a5SDavid du Colombier 				closeopenfd(&conn->cfd);
187*63afb9a5SDavid du Colombier 			}
188*63afb9a5SDavid du Colombier 			strncpy(conn->err, fields[4], sizeof conn->err);
189*63afb9a5SDavid du Colombier 			conn->dead = 1;
190*63afb9a5SDavid du Colombier 			return;
191*63afb9a5SDavid du Colombier 		}
192*63afb9a5SDavid du Colombier 	/* not a proc that we forked */
193*63afb9a5SDavid du Colombier }
194*63afb9a5SDavid du Colombier 
195*63afb9a5SDavid du Colombier static int
196*63afb9a5SDavid du Colombier outstandingprocs(Dest *dp)
197*63afb9a5SDavid du Colombier {
198*63afb9a5SDavid du Colombier 	Conn *conn;
199*63afb9a5SDavid du Colombier 
200*63afb9a5SDavid du Colombier 	for (conn = dp->conn; conn < dp->connend; conn++)
201*63afb9a5SDavid du Colombier 		if (!conn->dead)
202*63afb9a5SDavid du Colombier 			return 1;
203*63afb9a5SDavid du Colombier 	return 0;
204*63afb9a5SDavid du Colombier }
205*63afb9a5SDavid du Colombier 
206*63afb9a5SDavid du Colombier static int
207*63afb9a5SDavid du Colombier reap(Dest *dp)
208*63afb9a5SDavid du Colombier {
209*63afb9a5SDavid du Colombier 	char exitsts[2*ERRMAX];
210*63afb9a5SDavid du Colombier 
211*63afb9a5SDavid du Colombier 	if (outstandingprocs(dp) && await(exitsts, sizeof exitsts) >= 0) {
212*63afb9a5SDavid du Colombier 		notedeath(dp, exitsts);
213*63afb9a5SDavid du Colombier 		return 0;
214*63afb9a5SDavid du Colombier 	}
215*63afb9a5SDavid du Colombier 	return -1;
216*63afb9a5SDavid du Colombier }
217*63afb9a5SDavid du Colombier 
218*63afb9a5SDavid du Colombier static int
219*63afb9a5SDavid du Colombier fillinds(DS *ds, Dest *dp)
220*63afb9a5SDavid du Colombier {
221*63afb9a5SDavid du Colombier 	Conn *conn;
222*63afb9a5SDavid du Colombier 
223*63afb9a5SDavid du Colombier 	if (dp->winner < 0)
224*63afb9a5SDavid du Colombier 		return -1;
225*63afb9a5SDavid du Colombier 	conn = &dp->conn[dp->winner];
226*63afb9a5SDavid du Colombier 	if (ds->cfdp)
227*63afb9a5SDavid du Colombier 		*ds->cfdp = conn->cfd;
228*63afb9a5SDavid du Colombier 	if (ds->dir)
229*63afb9a5SDavid du Colombier 		strncpy(ds->dir, conn->dir, NETPATHLEN);
230*63afb9a5SDavid du Colombier 	return conn->dfd;
231*63afb9a5SDavid du Colombier }
232*63afb9a5SDavid du Colombier 
233*63afb9a5SDavid du Colombier static int
234*63afb9a5SDavid du Colombier connectwait(Dest *dp, char *besterr)
235*63afb9a5SDavid du Colombier {
236*63afb9a5SDavid du Colombier 	Conn *conn;
237*63afb9a5SDavid du Colombier 
238*63afb9a5SDavid du Colombier 	/* wait for a winner or all attempts to time out */
239*63afb9a5SDavid du Colombier 	while (dp->winner < 0 && reap(dp) >= 0)
240*63afb9a5SDavid du Colombier 		;
241*63afb9a5SDavid du Colombier 
242*63afb9a5SDavid du Colombier 	/* kill all of our still-live kids & reap them */
243*63afb9a5SDavid du Colombier 	for (conn = dp->conn; conn < dp->connend; conn++)
244*63afb9a5SDavid du Colombier 		if (!conn->dead)
245*63afb9a5SDavid du Colombier 			postnote(PNPROC, conn->pid, "alarm");
246*63afb9a5SDavid du Colombier 	while (reap(dp) >= 0)
247*63afb9a5SDavid du Colombier 		;
248*63afb9a5SDavid du Colombier 
249*63afb9a5SDavid du Colombier 	/* rummage about and report some error string */
250*63afb9a5SDavid du Colombier 	for (conn = dp->conn; conn < dp->connend; conn++)
251*63afb9a5SDavid du Colombier 		if (conn - dp->conn != dp->winner && conn->dead &&
252*63afb9a5SDavid du Colombier 		    conn->err[0]) {
253*63afb9a5SDavid du Colombier 			strncpy(besterr, conn->err, ERRMAX);
254*63afb9a5SDavid du Colombier 			break;
255*63afb9a5SDavid du Colombier 		}
256*63afb9a5SDavid du Colombier 	return dp->winner;
257*63afb9a5SDavid du Colombier }
258*63afb9a5SDavid du Colombier 
259*63afb9a5SDavid du Colombier static int
260*63afb9a5SDavid du Colombier parsecs(Dest *dp, char **clonep, char **destp)
261*63afb9a5SDavid du Colombier {
262*63afb9a5SDavid du Colombier 	char *dest, *p;
263*63afb9a5SDavid du Colombier 
264*63afb9a5SDavid du Colombier 	dest = strchr(dp->nextaddr, ' ');
265*63afb9a5SDavid du Colombier 	if(dest == nil)
266*63afb9a5SDavid du Colombier 		return -1;
267*63afb9a5SDavid du Colombier 	*dest++ = '\0';
268*63afb9a5SDavid du Colombier 	p = strchr(dest, '\n');
269*63afb9a5SDavid du Colombier 	if(p == nil)
270*63afb9a5SDavid du Colombier 		return -1;
271*63afb9a5SDavid du Colombier 	*p++ = '\0';
272*63afb9a5SDavid du Colombier 	*clonep = dp->nextaddr;
273*63afb9a5SDavid du Colombier 	*destp = dest;
274*63afb9a5SDavid du Colombier 	dp->nextaddr = p;		/* advance to next line */
275*63afb9a5SDavid du Colombier 	return 0;
276*63afb9a5SDavid du Colombier }
277*63afb9a5SDavid du Colombier 
278*63afb9a5SDavid du Colombier static void
279*63afb9a5SDavid du Colombier pickuperr(char *besterr, char *err)
280*63afb9a5SDavid du Colombier {
281*63afb9a5SDavid du Colombier 	err[0] = '\0';
282*63afb9a5SDavid du Colombier 	errstr(err, ERRMAX);
283*63afb9a5SDavid du Colombier 	if(strstr(err, "does not exist") == 0)
284*63afb9a5SDavid du Colombier 		strcpy(besterr, err);
285*63afb9a5SDavid du Colombier }
286*63afb9a5SDavid du Colombier 
287*63afb9a5SDavid du Colombier static void
288*63afb9a5SDavid du Colombier catcher(void *, char *s)
289*63afb9a5SDavid du Colombier {
290*63afb9a5SDavid du Colombier 	if (strstr(s, "alarm") != nil)
291*63afb9a5SDavid du Colombier 		noted(NCONT);
292*63afb9a5SDavid du Colombier 	else
293*63afb9a5SDavid du Colombier 		noted(NDFLT);
294*63afb9a5SDavid du Colombier }
295*63afb9a5SDavid du Colombier 
296*63afb9a5SDavid du Colombier /*
297*63afb9a5SDavid du Colombier  * try all addresses in parallel and take the first one that answers;
298*63afb9a5SDavid du Colombier  * this helps when systems have ip v4 and v6 addresses but are
299*63afb9a5SDavid du Colombier  * only reachable from here on one (or some) of them.
300*63afb9a5SDavid du Colombier  */
301*63afb9a5SDavid du Colombier static int
302*63afb9a5SDavid du Colombier dialmulti(DS *ds, Dest *dp)
303*63afb9a5SDavid du Colombier {
304*63afb9a5SDavid du Colombier 	int rv, kid, kidme;
305*63afb9a5SDavid du Colombier 	char *clone, *dest;
306*63afb9a5SDavid du Colombier 	char err[ERRMAX], besterr[ERRMAX];
307*63afb9a5SDavid du Colombier 
308*63afb9a5SDavid du Colombier 	dp->winner = -1;
309*63afb9a5SDavid du Colombier 	dp->nkid = 0;
310*63afb9a5SDavid du Colombier 	while(dp->winner < 0 && *dp->nextaddr != '\0' &&
311*63afb9a5SDavid du Colombier 	    parsecs(dp, &clone, &dest) >= 0) {
312*63afb9a5SDavid du Colombier 		kidme = dp->nkid++;		/* make private copy on stack */
313*63afb9a5SDavid du Colombier 		kid = rfork(RFPROC|RFMEM);	/* spin off a call attempt */
314*63afb9a5SDavid du Colombier 		if (kid < 0)
315*63afb9a5SDavid du Colombier 			--dp->nkid;
316*63afb9a5SDavid du Colombier 		else if (kid == 0) {
317*63afb9a5SDavid du Colombier 			/* only in kid, to avoid atnotify callbacks in parent */
318*63afb9a5SDavid du Colombier 			notify(catcher);
319*63afb9a5SDavid du Colombier 
320*63afb9a5SDavid du Colombier 			*besterr = '\0';
321*63afb9a5SDavid du Colombier 			rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
322*63afb9a5SDavid du Colombier 			if(rv < 0)
323*63afb9a5SDavid du Colombier 				pickuperr(besterr, err);
324*63afb9a5SDavid du Colombier 			_exits(besterr);	/* avoid atexit callbacks */
325*63afb9a5SDavid du Colombier 		}
326*63afb9a5SDavid du Colombier 	}
327*63afb9a5SDavid du Colombier 	rv = connectwait(dp, besterr);
328*63afb9a5SDavid du Colombier 	if(rv < 0 && *besterr)
329*63afb9a5SDavid du Colombier 		werrstr("%s", besterr);
330*63afb9a5SDavid du Colombier 	else
331*63afb9a5SDavid du Colombier 		werrstr("%s", err);
332*63afb9a5SDavid du Colombier 	return rv;
333*63afb9a5SDavid du Colombier }
334*63afb9a5SDavid du Colombier 
335*63afb9a5SDavid du Colombier static int
336*63afb9a5SDavid du Colombier csdial(DS *ds)
337*63afb9a5SDavid du Colombier {
338*63afb9a5SDavid du Colombier 	int n, fd, rv, addrs, bleft;
339*63afb9a5SDavid du Colombier 	char c;
340*63afb9a5SDavid du Colombier 	char *addrp, *clone2, *dest;
341*63afb9a5SDavid du Colombier 	char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
342*63afb9a5SDavid du Colombier 	Dest *dp;
343*63afb9a5SDavid du Colombier 
344*63afb9a5SDavid du Colombier 	dp = mallocz(sizeof *dp, 1);
345*63afb9a5SDavid du Colombier 	if(dp == nil)
346*63afb9a5SDavid du Colombier 		return -1;
347*63afb9a5SDavid du Colombier 	dp->winner = -1;
348*63afb9a5SDavid du Colombier 	dp->oalarm = alarm(0);
349*63afb9a5SDavid du Colombier 	if (connsalloc(dp, 1) < 0) {		/* room for a single conn. */
350*63afb9a5SDavid du Colombier 		freedest(dp);
351*63afb9a5SDavid du Colombier 		return -1;
352*63afb9a5SDavid du Colombier 	}
353*63afb9a5SDavid du Colombier 
354*63afb9a5SDavid du Colombier 	/*
355*63afb9a5SDavid du Colombier 	 *  open connection server
356*63afb9a5SDavid du Colombier 	 */
357*63afb9a5SDavid du Colombier 	snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
358*63afb9a5SDavid du Colombier 	fd = open(buf, ORDWR);
359*63afb9a5SDavid du Colombier 	if(fd < 0){
360*63afb9a5SDavid du Colombier 		/* no connection server, don't translate */
361*63afb9a5SDavid du Colombier 		snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
362*63afb9a5SDavid du Colombier 		rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
363*63afb9a5SDavid du Colombier 		fillinds(ds, dp);
364*63afb9a5SDavid du Colombier 		freedest(dp);
365*63afb9a5SDavid du Colombier 		return rv;
366*63afb9a5SDavid du Colombier 	}
367*63afb9a5SDavid du Colombier 
368*63afb9a5SDavid du Colombier 	/*
369*63afb9a5SDavid du Colombier 	 *  ask connection server to translate
370*63afb9a5SDavid du Colombier 	 */
371*63afb9a5SDavid du Colombier 	snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
372*63afb9a5SDavid du Colombier 	if(write(fd, buf, strlen(buf)) < 0){
373*63afb9a5SDavid du Colombier 		close(fd);
374*63afb9a5SDavid du Colombier 		freedest(dp);
375*63afb9a5SDavid du Colombier 		return -1;
376*63afb9a5SDavid du Colombier 	}
377*63afb9a5SDavid du Colombier 
378*63afb9a5SDavid du Colombier 	/*
379*63afb9a5SDavid du Colombier 	 *  read all addresses from the connection server.
380*63afb9a5SDavid du Colombier 	 */
381*63afb9a5SDavid du Colombier 	seek(fd, 0, 0);
382*63afb9a5SDavid du Colombier 	addrs = 0;
383*63afb9a5SDavid du Colombier 	addrp = dp->nextaddr = dp->addrlist;
384*63afb9a5SDavid du Colombier 	bleft = sizeof dp->addrlist - 2;	/* 2 is room for \n\0 */
385*63afb9a5SDavid du Colombier 	while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
386*63afb9a5SDavid du Colombier 		if (addrp[n-1] != '\n')
387*63afb9a5SDavid du Colombier 			addrp[n++] = '\n';
388*63afb9a5SDavid du Colombier 		addrs++;
389*63afb9a5SDavid du Colombier 		addrp += n;
390*63afb9a5SDavid du Colombier 		bleft -= n;
391*63afb9a5SDavid du Colombier 	}
392*63afb9a5SDavid du Colombier 	/*
393*63afb9a5SDavid du Colombier 	 * if we haven't read all of cs's output, assume the last line might
394*63afb9a5SDavid du Colombier 	 * have been truncated and ignore it.  we really don't expect this
395*63afb9a5SDavid du Colombier 	 * to happen.
396*63afb9a5SDavid du Colombier 	 */
397*63afb9a5SDavid du Colombier 	if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
398*63afb9a5SDavid du Colombier 		addrs--;
399*63afb9a5SDavid du Colombier 	close(fd);
400*63afb9a5SDavid du Colombier 
401*63afb9a5SDavid du Colombier 	*besterr = 0;
402*63afb9a5SDavid du Colombier 	rv = -1;				/* pessimistic default */
403*63afb9a5SDavid du Colombier 	dp->naddrs = addrs;
404*63afb9a5SDavid du Colombier 	if (addrs == 0)
405*63afb9a5SDavid du Colombier 		werrstr("no address to dial");
406*63afb9a5SDavid du Colombier 	else if (addrs == 1) {
407*63afb9a5SDavid du Colombier 		/* common case: dial one address without forking */
408*63afb9a5SDavid du Colombier 		if (parsecs(dp, &clone2, &dest) >= 0 &&
409*63afb9a5SDavid du Colombier 		    (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
410*63afb9a5SDavid du Colombier 			pickuperr(besterr, err);
411*63afb9a5SDavid du Colombier 			werrstr("%s", besterr);
412*63afb9a5SDavid du Colombier 		}
413*63afb9a5SDavid du Colombier 	} else if (connsalloc(dp, addrs) >= 0)
414*63afb9a5SDavid du Colombier 		rv = dialmulti(ds, dp);
415*63afb9a5SDavid du Colombier 
416*63afb9a5SDavid du Colombier 	/* fill in results */
417*63afb9a5SDavid du Colombier 	if (rv >= 0 && dp->winner >= 0)
418*63afb9a5SDavid du Colombier 		rv = fillinds(ds, dp);
419*63afb9a5SDavid du Colombier 
420*63afb9a5SDavid du Colombier 	freedest(dp);
421*63afb9a5SDavid du Colombier 	return rv;
422*63afb9a5SDavid du Colombier }
423*63afb9a5SDavid du Colombier 
424*63afb9a5SDavid du Colombier static int
425*63afb9a5SDavid du Colombier call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
426*63afb9a5SDavid du Colombier {
427*63afb9a5SDavid du Colombier 	int fd, cfd, n, calleralarm, oalarm;
428*63afb9a5SDavid du Colombier 	char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
429*63afb9a5SDavid du Colombier 
430*63afb9a5SDavid du Colombier 	/* because cs is in a different name space, replace the mount point */
431*63afb9a5SDavid du Colombier 	if(*clone == '/'){
432*63afb9a5SDavid du Colombier 		p = strchr(clone+1, '/');
433*63afb9a5SDavid du Colombier 		if(p == nil)
434*63afb9a5SDavid du Colombier 			p = clone;
435*63afb9a5SDavid du Colombier 		else
436*63afb9a5SDavid du Colombier 			p++;
437*63afb9a5SDavid du Colombier 	} else
438*63afb9a5SDavid du Colombier 		p = clone;
439*63afb9a5SDavid du Colombier 	snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
440*63afb9a5SDavid du Colombier 
441*63afb9a5SDavid du Colombier 	conn->pid = getpid();
442*63afb9a5SDavid du Colombier 	conn->cfd = cfd = open(cname, ORDWR);
443*63afb9a5SDavid du Colombier 	if(cfd < 0)
444*63afb9a5SDavid du Colombier 		return -1;
445*63afb9a5SDavid du Colombier 
446*63afb9a5SDavid du Colombier 	/* get directory name */
447*63afb9a5SDavid du Colombier 	n = read(cfd, name, sizeof(name)-1);
448*63afb9a5SDavid du Colombier 	if(n < 0){
449*63afb9a5SDavid du Colombier 		closeopenfd(&conn->cfd);
450*63afb9a5SDavid du Colombier 		return -1;
451*63afb9a5SDavid du Colombier 	}
452*63afb9a5SDavid du Colombier 	name[n] = 0;
453*63afb9a5SDavid du Colombier 	for(p = name; *p == ' '; p++)
454*63afb9a5SDavid du Colombier 		;
455*63afb9a5SDavid du Colombier 	snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
456*63afb9a5SDavid du Colombier 	p = strrchr(cname, '/');
457*63afb9a5SDavid du Colombier 	*p = 0;
458*63afb9a5SDavid du Colombier 	if(ds->dir)
459*63afb9a5SDavid du Colombier 		snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
460*63afb9a5SDavid du Colombier 	snprint(data, sizeof(data), "%s/%s/data", cname, name);
461*63afb9a5SDavid du Colombier 
462*63afb9a5SDavid du Colombier 	/* should be no alarm pending now; re-instate caller's alarm, if any */
463*63afb9a5SDavid du Colombier 	calleralarm = dp->oalarm > 0;
464*63afb9a5SDavid du Colombier 	if (calleralarm)
465*63afb9a5SDavid du Colombier 		alarm(dp->oalarm);
466*63afb9a5SDavid du Colombier 	else if (dp->naddrs > 1)	/* in a sub-process? */
467*63afb9a5SDavid du Colombier 		alarm(Maxconnms);
468*63afb9a5SDavid du Colombier 
469*63afb9a5SDavid du Colombier 	/* connect */
470*63afb9a5SDavid du Colombier 	if(ds->local)
471*63afb9a5SDavid du Colombier 		snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
472*63afb9a5SDavid du Colombier 	else
473*63afb9a5SDavid du Colombier 		snprint(name, sizeof(name), "connect %s", dest);
474*63afb9a5SDavid du Colombier 	if(write(cfd, name, strlen(name)) < 0){
475*63afb9a5SDavid du Colombier 		closeopenfd(&conn->cfd);
476*63afb9a5SDavid du Colombier 		return -1;
477*63afb9a5SDavid du Colombier 	}
478*63afb9a5SDavid du Colombier 
479*63afb9a5SDavid du Colombier 	oalarm = alarm(0);	/* don't let alarm interrupt critical section */
480*63afb9a5SDavid du Colombier 	if (calleralarm)
481*63afb9a5SDavid du Colombier 		dp->oalarm = oalarm;	/* time has passed, so update user's */
482*63afb9a5SDavid du Colombier 
483*63afb9a5SDavid du Colombier 	/* open data connection */
484*63afb9a5SDavid du Colombier 	conn->dfd = fd = open(data, ORDWR);
485*63afb9a5SDavid du Colombier 	if(fd < 0){
486*63afb9a5SDavid du Colombier 		closeopenfd(&conn->cfd);
487*63afb9a5SDavid du Colombier 		alarm(dp->oalarm);
488*63afb9a5SDavid du Colombier 		return -1;
489*63afb9a5SDavid du Colombier 	}
490*63afb9a5SDavid du Colombier 	if(ds->cfdp == nil)
491*63afb9a5SDavid du Colombier 		closeopenfd(&conn->cfd);
492*63afb9a5SDavid du Colombier 
493*63afb9a5SDavid du Colombier 	n = conn - dp->conn;
494*63afb9a5SDavid du Colombier 	if (dp->winner < 0) {
495*63afb9a5SDavid du Colombier 		qlock(&dp->winlck);
496*63afb9a5SDavid du Colombier 		if (dp->winner < 0 && conn < dp->connend)
497*63afb9a5SDavid du Colombier 			dp->winner = n;
498*63afb9a5SDavid du Colombier 		qunlock(&dp->winlck);
499*63afb9a5SDavid du Colombier 	}
500*63afb9a5SDavid du Colombier 	alarm(calleralarm? dp->oalarm: 0);
501*63afb9a5SDavid du Colombier 	return fd;
502*63afb9a5SDavid du Colombier }
503*63afb9a5SDavid du Colombier 
504*63afb9a5SDavid du Colombier /*
505*63afb9a5SDavid du Colombier  * assume p points at first '!' in dial string.  st is start of dial string.
506*63afb9a5SDavid du Colombier  * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as
507*63afb9a5SDavid du Colombier  * part of the proto string, so skip numeric components.
508*63afb9a5SDavid du Colombier  * returns pointer to delimiter after right-most non-numeric component.
509*63afb9a5SDavid du Colombier  */
510*63afb9a5SDavid du Colombier static char *
511*63afb9a5SDavid du Colombier backoverchans(char *st, char *p)
512*63afb9a5SDavid du Colombier {
513*63afb9a5SDavid du Colombier 	char *sl;
514*63afb9a5SDavid du Colombier 
515*63afb9a5SDavid du Colombier 	for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) {
516*63afb9a5SDavid du Colombier 		while (--p >= st && isascii(*p) && isdigit(*p))
517*63afb9a5SDavid du Colombier 			;
518*63afb9a5SDavid du Colombier 		if (p < st || *p != '/')
519*63afb9a5SDavid du Colombier 			break;			/* "net.alt2" or ran off start */
520*63afb9a5SDavid du Colombier 		while (p > st && p[-1] == '/')	/* skip runs of slashes */
521*63afb9a5SDavid du Colombier 			p--;
522*63afb9a5SDavid du Colombier 	}
523*63afb9a5SDavid du Colombier 	return sl;
524*63afb9a5SDavid du Colombier }
525*63afb9a5SDavid du Colombier 
526*63afb9a5SDavid du Colombier /*
527*63afb9a5SDavid du Colombier  *  parse a dial string
528*63afb9a5SDavid du Colombier  */
529*63afb9a5SDavid du Colombier static void
530*63afb9a5SDavid du Colombier _dial_string_parse(char *str, DS *ds)
531*63afb9a5SDavid du Colombier {
532*63afb9a5SDavid du Colombier 	char *p, *p2;
533*63afb9a5SDavid du Colombier 
534*63afb9a5SDavid du Colombier 	strncpy(ds->buf, str, Maxstring);
535*63afb9a5SDavid du Colombier 	ds->buf[Maxstring-1] = 0;
536*63afb9a5SDavid du Colombier 
537*63afb9a5SDavid du Colombier 	p = strchr(ds->buf, '!');
538*63afb9a5SDavid du Colombier 	if(p == 0) {
539*63afb9a5SDavid du Colombier 		ds->netdir = 0;
540*63afb9a5SDavid du Colombier 		ds->proto = "net";
541*63afb9a5SDavid du Colombier 		ds->rem = ds->buf;
542*63afb9a5SDavid du Colombier 	} else {
543*63afb9a5SDavid du Colombier 		if(*ds->buf != '/' && *ds->buf != '#'){
544*63afb9a5SDavid du Colombier 			ds->netdir = 0;
545*63afb9a5SDavid du Colombier 			ds->proto = ds->buf;
546*63afb9a5SDavid du Colombier 		} else {
547*63afb9a5SDavid du Colombier 			p2 = backoverchans(ds->buf, p);
548*63afb9a5SDavid du Colombier 
549*63afb9a5SDavid du Colombier 			/* back over last component of netdir (proto) */
550*63afb9a5SDavid du Colombier 			while (--p2 > ds->buf && *p2 != '/')
551*63afb9a5SDavid du Colombier 				;
552*63afb9a5SDavid du Colombier 			*p2++ = 0;
553*63afb9a5SDavid du Colombier 			ds->netdir = ds->buf;
554*63afb9a5SDavid du Colombier 			ds->proto = p2;
555*63afb9a5SDavid du Colombier 		}
556*63afb9a5SDavid du Colombier 		*p = 0;
557*63afb9a5SDavid du Colombier 		ds->rem = p + 1;
558*63afb9a5SDavid du Colombier 	}
559*63afb9a5SDavid du Colombier }
560