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