1 /* 2 * dial - connect to a service (threaded parallel version) 3 */ 4 #include <u.h> 5 #include <libc.h> 6 #include <ctype.h> 7 #include <fcall.h> 8 #include <thread.h> 9 #include <9p.h> 10 11 typedef struct Conn Conn; 12 typedef struct Dest Dest; 13 typedef struct DS DS; 14 typedef struct Kidargs Kidargs; 15 typedef struct Restup Restup; 16 17 enum 18 { 19 Noblock, 20 Block, 21 22 Defstksize = 8192, 23 24 Maxstring = 128, 25 Maxpath = 256, 26 27 Maxcsreply = 64*80, /* this is probably overly generous */ 28 /* 29 * this should be a plausible slight overestimate for non-interactive 30 * use even if it's ridiculously long for interactive use. 31 */ 32 Maxconnms = 2*60*1000, /* 2 minutes */ 33 }; 34 35 struct DS { 36 /* dial string */ 37 char buf[Maxstring]; 38 char *netdir; 39 char *proto; 40 char *rem; 41 42 /* other args */ 43 char *local; 44 char *dir; 45 int *cfdp; 46 }; 47 48 struct Conn { 49 int cfd; 50 char dir[NETPATHLEN]; 51 }; 52 struct Dest { 53 DS *ds; 54 55 Channel *reschan; /* all callprocs send results on this */ 56 int nkid; 57 int kidthrids[64]; /* one per addr; ought to be enough */ 58 59 int windfd; 60 char err[ERRMAX]; 61 62 long oalarm; 63 64 int naddrs; 65 char *nextaddr; 66 char addrlist[Maxcsreply]; 67 }; 68 69 struct Kidargs { /* arguments to callproc */ 70 Dest *dp; 71 int thridsme; 72 char *clone; 73 char *dest; 74 }; 75 76 struct Restup { /* result tuple from callproc */ 77 int dfd; 78 int cfd; 79 char *err; 80 char *conndir; 81 }; 82 83 static int call(char*, char*, Dest*, Conn*); 84 static int call1(char*, char*, Dest*, Conn*); 85 static int csdial(DS*); 86 static void _dial_string_parse(char*, DS*); 87 88 /* 89 * the dialstring is of the form '[/net/]proto!dest' 90 */ 91 static int 92 dialimpl(char *dest, char *local, char *dir, int *cfdp) 93 { 94 DS ds; 95 int rv; 96 char err[ERRMAX], alterr[ERRMAX]; 97 98 ds.local = local; 99 ds.dir = dir; 100 ds.cfdp = cfdp; 101 102 _dial_string_parse(dest, &ds); 103 if(ds.netdir) 104 return csdial(&ds); 105 106 ds.netdir = "/net"; 107 rv = csdial(&ds); 108 if(rv >= 0) 109 return rv; 110 err[0] = '\0'; 111 errstr(err, sizeof err); 112 if(strstr(err, "refused") != 0){ 113 werrstr("%s", err); 114 return rv; 115 } 116 ds.netdir = "/net.alt"; 117 rv = csdial(&ds); 118 if(rv >= 0) 119 return rv; 120 121 alterr[0] = 0; 122 errstr(alterr, sizeof alterr); 123 if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) 124 werrstr("%s", err); 125 else 126 werrstr("%s", alterr); 127 return rv; 128 } 129 130 /* 131 * the thread library can't cope with rfork(RFMEM|RFPROC), 132 * so it must override _dial with this version of dial. 133 */ 134 int (*_dial)(char *, char *, char *, int *) = dialimpl; 135 136 int 137 dial(char *dest, char *local, char *dir, int *cfdp) 138 { 139 return (*_dial)(dest, local, dir, cfdp); 140 } 141 142 static void 143 freedest(Dest *dp) 144 { 145 if (dp) { 146 if (dp->oalarm >= 0) 147 alarm(dp->oalarm); 148 free(dp); 149 } 150 } 151 152 static void 153 closeopenfd(int *fdp) 154 { 155 if (*fdp >= 0) { 156 close(*fdp); 157 *fdp = -1; 158 } 159 } 160 161 static int 162 parsecs(Dest *dp, char **clonep, char **destp) 163 { 164 char *dest, *p; 165 166 dest = strchr(dp->nextaddr, ' '); 167 if(dest == nil) 168 return -1; 169 *dest++ = '\0'; 170 p = strchr(dest, '\n'); 171 if(p == nil) 172 return -1; 173 *p++ = '\0'; 174 *clonep = dp->nextaddr; 175 *destp = dest; 176 dp->nextaddr = p; /* advance to next line */ 177 return 0; 178 } 179 180 static void 181 pickuperr(char *besterr) 182 { 183 char err[ERRMAX]; 184 185 err[0] = '\0'; 186 errstr(err, ERRMAX); 187 if(strstr(err, "does not exist") == 0) 188 strcpy(besterr, err); 189 } 190 191 static int 192 catcher(void *, char *s) 193 { 194 return strstr(s, "alarm") != nil; 195 } 196 197 static void 198 callproc(void *p) 199 { 200 int dfd; 201 char besterr[ERRMAX]; 202 Conn lconn; 203 Conn *conn; 204 Kidargs *args; 205 Restup *tup; 206 207 threadnotify(catcher, 1); /* avoid atnotify callbacks in parent */ 208 209 conn = &lconn; 210 memset(conn, 0, sizeof *conn); 211 *besterr = '\0'; 212 args = (Kidargs *)p; 213 dfd = call(args->clone, args->dest, args->dp, conn); 214 if(dfd < 0) 215 pickuperr(besterr); 216 217 tup = (Restup *)emalloc9p(sizeof *tup); 218 *tup = (Restup){dfd, conn->cfd, nil, nil}; 219 if (dfd >= 0) 220 tup->conndir = strdup(conn->dir); 221 else 222 tup->err = strdup(besterr); 223 sendp(args->dp->reschan, tup); 224 225 args->dp->kidthrids[args->thridsme] = -1; 226 free(args); 227 threadexits(besterr); /* better be no atexit callbacks */ 228 } 229 230 /* interrupt all of our still-live kids */ 231 static void 232 intrcallprocs(Dest *dp) 233 { 234 int i; 235 236 for (i = 0; i < nelem(dp->kidthrids); i++) 237 if (dp->kidthrids[i] >= 0) 238 threadint(dp->kidthrids[i]); 239 } 240 241 static int 242 recvresults(Dest *dp, int block) 243 { 244 DS *ds; 245 Restup *tup; 246 247 for (; dp->nkid > 0; dp->nkid--) { 248 if (block) 249 tup = recvp(dp->reschan); 250 else 251 tup = nbrecvp(dp->reschan); 252 if (tup == nil) 253 break; 254 if (tup->dfd >= 0) /* connected? */ 255 if (dp->windfd < 0) { /* first connection? */ 256 ds = dp->ds; 257 dp->windfd = tup->dfd; 258 if (ds->cfdp) 259 *ds->cfdp = tup->cfd; 260 if (ds->dir) 261 strncpy(ds->dir, tup->conndir, 262 NETPATHLEN); 263 intrcallprocs(dp); 264 } else { 265 close(tup->dfd); 266 close(tup->cfd); 267 } 268 else if (dp->err[0] == '\0' && tup->err) 269 strncpy(dp->err, tup->err, ERRMAX); 270 free(tup->conndir); 271 free(tup->err); 272 free(tup); 273 } 274 return dp->windfd; 275 } 276 277 /* 278 * try all addresses in parallel and take the first one that answers; 279 * this helps when systems have ip v4 and v6 addresses but are 280 * only reachable from here on one (or some) of them. 281 */ 282 static int 283 dialmulti(Dest *dp) 284 { 285 int kidme; 286 char *clone, *dest; 287 Kidargs *argp; 288 289 dp->reschan = chancreate(sizeof(void *), 0); 290 dp->err[0] = '\0'; 291 dp->nkid = 0; 292 dp->windfd = -1; 293 /* if too many addresses for dp->kidthrids, ignore the last few */ 294 while(dp->windfd < 0 && dp->nkid < nelem(dp->kidthrids) && 295 *dp->nextaddr != '\0' && parsecs(dp, &clone, &dest) >= 0) { 296 kidme = dp->nkid++; 297 298 argp = (Kidargs *)emalloc9p(sizeof *argp); 299 *argp = (Kidargs){dp, kidme, clone, dest}; 300 301 dp->kidthrids[kidme] = proccreate(callproc, argp, Defstksize); 302 if (dp->kidthrids[kidme] < 0) 303 --dp->nkid; 304 } 305 306 recvresults(dp, Block); 307 assert(dp->nkid == 0); 308 309 chanclose(dp->reschan); 310 chanfree(dp->reschan); 311 if(dp->windfd < 0 && dp->err[0]) 312 werrstr("%s", dp->err); 313 return dp->windfd; 314 } 315 316 /* call a single address and pass back cfd & conn dir after */ 317 static int 318 call1(char *clone, char *rem, Dest *dp, Conn *conn) 319 { 320 int dfd; 321 DS *ds; 322 323 ds = dp->ds; 324 dfd = call(clone, rem, dp, conn); 325 if (dfd < 0) 326 return dfd; 327 328 if (ds->cfdp) 329 *ds->cfdp = conn->cfd; 330 if (ds->dir) 331 strncpy(ds->dir, conn->dir, NETPATHLEN); 332 return dfd; 333 } 334 335 static int 336 csdial(DS *ds) 337 { 338 int n, fd, dfd, addrs, bleft; 339 char c; 340 char *addrp, *clone2, *dest; 341 char buf[Maxstring], clone[Maxpath], besterr[ERRMAX]; 342 Conn lconn; 343 Conn *conn; 344 Dest *dp; 345 346 dp = mallocz(sizeof *dp, 1); 347 if(dp == nil) 348 return -1; 349 conn = &lconn; 350 memset(conn, 0, sizeof *conn); 351 dp->ds = ds; 352 if (ds->cfdp) 353 *ds->cfdp = -1; 354 if (ds->dir) 355 ds->dir[0] = '\0'; 356 dp->oalarm = alarm(0); 357 358 /* 359 * open connection server 360 */ 361 snprint(buf, sizeof(buf), "%s/cs", ds->netdir); 362 fd = open(buf, ORDWR); 363 if(fd < 0){ 364 /* no connection server, don't translate */ 365 snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto); 366 dfd = call1(clone, ds->rem, dp, conn); 367 freedest(dp); 368 return dfd; 369 } 370 371 /* 372 * ask connection server to translate 373 */ 374 snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem); 375 if(write(fd, buf, strlen(buf)) < 0){ 376 close(fd); 377 freedest(dp); 378 return -1; 379 } 380 381 /* 382 * read all addresses from the connection server. 383 */ 384 seek(fd, 0, 0); 385 addrs = 0; 386 addrp = dp->nextaddr = dp->addrlist; 387 bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */ 388 while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) { 389 if (addrp[n-1] != '\n') 390 addrp[n++] = '\n'; 391 addrs++; 392 addrp += n; 393 bleft -= n; 394 } 395 /* 396 * if we haven't read all of cs's output, assume the last line might 397 * have been truncated and ignore it. we really don't expect this 398 * to happen. 399 */ 400 if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1) 401 addrs--; 402 close(fd); 403 404 *besterr = 0; 405 dfd = -1; /* pessimistic default */ 406 dp->naddrs = addrs; 407 if (addrs == 0) 408 werrstr("no address to dial"); 409 else if (addrs == 1) { 410 /* common case: dial one address without forking */ 411 if (parsecs(dp, &clone2, &dest) >= 0 && 412 (dfd = call1(clone2, dest, dp, conn)) < 0) { 413 pickuperr(besterr); 414 werrstr("%s", besterr); 415 } 416 } else 417 dfd = dialmulti(dp); 418 419 freedest(dp); 420 return dfd; 421 } 422 423 /* returns dfd, stores cfd through cfdp */ 424 static int 425 call(char *clone, char *dest, Dest *dp, Conn *conn) 426 { 427 int fd, cfd, n, calleralarm, oalarm; 428 char cname[Maxpath], name[Maxpath], data[Maxpath], *p; 429 DS *ds; 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 ds = dp->ds; 441 snprint(cname, sizeof cname, "%s/%s", ds->netdir, p); 442 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 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 alarm(calleralarm? dp->oalarm: 0); 495 return fd; 496 } 497 498 /* 499 * assume p points at first '!' in dial string. st is start of dial string. 500 * there could be subdirs of the conn dirs (e.g., ssh/0) that must count as 501 * part of the proto string, so skip numeric components. 502 * returns pointer to delimiter after right-most non-numeric component. 503 */ 504 static char * 505 backoverchans(char *st, char *p) 506 { 507 char *sl; 508 509 for (sl = p; --p >= st && isascii(*p) && isdigit(*p); sl = p) { 510 while (--p >= st && isascii(*p) && isdigit(*p)) 511 ; 512 if (p < st || *p != '/') 513 break; /* "net.alt2" or ran off start */ 514 while (p > st && p[-1] == '/') /* skip runs of slashes */ 515 p--; 516 } 517 return sl; 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 p2 = backoverchans(ds->buf, p); 542 543 /* back over last component of netdir (proto) */ 544 while (--p2 > ds->buf && *p2 != '/') 545 ; 546 *p2++ = 0; 547 ds->netdir = ds->buf; 548 ds->proto = p2; 549 } 550 *p = 0; 551 ds->rem = p + 1; 552 } 553 } 554