dial.c 11 KB

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