dial.thread.c 11 KB

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