dial.c 11 KB

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