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