dial.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  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] = '\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. conn->err[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. return -1;
  231. *dest++ = '\0';
  232. p = strchr(dest, '\n');
  233. if(p == nil)
  234. return -1;
  235. *p++ = '\0';
  236. *clonep = dp->nextaddr;
  237. *destp = dest;
  238. dp->nextaddr = p; /* advance to next line */
  239. return 0;
  240. }
  241. static void
  242. pickuperr(char *besterr, char *err)
  243. {
  244. err[0] = '\0';
  245. errstr(err, ERRMAX);
  246. if(strstr(err, "does not exist") == 0)
  247. strcpy(besterr, err);
  248. }
  249. static int
  250. catcher(void *, char *s)
  251. {
  252. return strstr(s, "alarm") != nil;
  253. }
  254. /*
  255. * try all addresses in parallel and take the first one that answers;
  256. * this helps when systems have ip v4 and v6 addresses but are
  257. * only reachable from here on one (or some) of them.
  258. */
  259. static int
  260. dialmulti(DS *ds, Dest *dp)
  261. {
  262. int rv, kid, kidme;
  263. char *clone, *dest;
  264. char err[ERRMAX], besterr[ERRMAX];
  265. dp->winner = -1;
  266. dp->nkid = 0;
  267. while(dp->winner < 0 && *dp->nextaddr != '\0' &&
  268. parsecs(dp, &clone, &dest) >= 0) {
  269. kidme = dp->nkid++; /* make private copy on stack */
  270. kid = rfork(RFPROC|RFMEM); /* spin off a call attempt */
  271. if (kid < 0)
  272. --dp->nkid;
  273. else if (kid == 0) {
  274. /* only in kid, to avoid atnotify callbacks in parent */
  275. atnotify(catcher, 1);
  276. *besterr = '\0';
  277. rv = call(clone, dest, ds, dp, &dp->conn[kidme]);
  278. if(rv < 0)
  279. pickuperr(besterr, err);
  280. _exits(besterr); /* avoid atexit callbacks */
  281. }
  282. }
  283. rv = connectwait(dp, besterr);
  284. if(rv < 0 && *besterr)
  285. werrstr("%s", besterr);
  286. else
  287. werrstr("%s", err);
  288. return rv;
  289. }
  290. static int
  291. csdial(DS *ds)
  292. {
  293. int n, fd, rv, addrs, bleft;
  294. char c;
  295. char *addrp, *clone2, *dest;
  296. char buf[Maxstring], clone[Maxpath], err[ERRMAX], besterr[ERRMAX];
  297. Dest *dp;
  298. dp = mallocz(sizeof *dp, 1);
  299. if(dp == nil)
  300. return -1;
  301. dp->winner = -1;
  302. dp->oalarm = alarm(0);
  303. if (connsalloc(dp, 1) < 0) { /* room for a single conn. */
  304. freedest(dp);
  305. return -1;
  306. }
  307. /*
  308. * open connection server
  309. */
  310. snprint(buf, sizeof(buf), "%s/cs", ds->netdir);
  311. fd = open(buf, ORDWR);
  312. if(fd < 0){
  313. /* no connection server, don't translate */
  314. snprint(clone, sizeof(clone), "%s/%s/clone", ds->netdir, ds->proto);
  315. rv = call(clone, ds->rem, ds, dp, &dp->conn[0]);
  316. fillinds(ds, dp);
  317. freedest(dp);
  318. return rv;
  319. }
  320. /*
  321. * ask connection server to translate
  322. */
  323. snprint(buf, sizeof(buf), "%s!%s", ds->proto, ds->rem);
  324. if(write(fd, buf, strlen(buf)) < 0){
  325. close(fd);
  326. freedest(dp);
  327. return -1;
  328. }
  329. /*
  330. * read all addresses from the connection server.
  331. */
  332. seek(fd, 0, 0);
  333. addrs = 0;
  334. addrp = dp->nextaddr = dp->addrlist;
  335. bleft = sizeof dp->addrlist - 2; /* 2 is room for \n\0 */
  336. while(bleft > 0 && (n = read(fd, addrp, bleft)) > 0) {
  337. if (addrp[n-1] != '\n')
  338. addrp[n++] = '\n';
  339. addrs++;
  340. addrp += n;
  341. bleft -= n;
  342. }
  343. /*
  344. * if we haven't read all of cs's output, assume the last line might
  345. * have been truncated and ignore it. we really don't expect this
  346. * to happen.
  347. */
  348. if (addrs > 0 && bleft <= 0 && read(fd, &c, 1) == 1)
  349. addrs--;
  350. close(fd);
  351. *besterr = 0;
  352. rv = -1; /* pessimistic default */
  353. dp->naddrs = addrs;
  354. if (addrs == 0)
  355. werrstr("no address to dial");
  356. else if (addrs == 1) {
  357. /* common case: dial one address without forking */
  358. if (parsecs(dp, &clone2, &dest) >= 0 &&
  359. (rv = call(clone2, dest, ds, dp, &dp->conn[0])) < 0) {
  360. pickuperr(besterr, err);
  361. werrstr("%s", besterr);
  362. }
  363. } else if (connsalloc(dp, addrs) >= 0)
  364. rv = dialmulti(ds, dp);
  365. /* fill in results */
  366. if (rv >= 0 && dp->winner >= 0)
  367. rv = fillinds(ds, dp);
  368. freedest(dp);
  369. return rv;
  370. }
  371. static int
  372. call(char *clone, char *dest, DS *ds, Dest *dp, Conn *conn)
  373. {
  374. int fd, cfd, n, calleralarm, oalarm;
  375. char cname[Maxpath], name[Maxpath], data[Maxpath], *p;
  376. /* because cs is in a different name space, replace the mount point */
  377. if(*clone == '/'){
  378. p = strchr(clone+1, '/');
  379. if(p == nil)
  380. p = clone;
  381. else
  382. p++;
  383. } else
  384. p = clone;
  385. snprint(cname, sizeof cname, "%s/%s", ds->netdir, p);
  386. conn->pid = getpid();
  387. conn->cfd = cfd = open(cname, ORDWR);
  388. if(cfd < 0)
  389. return -1;
  390. /* get directory name */
  391. n = read(cfd, name, sizeof(name)-1);
  392. if(n < 0){
  393. closeopenfd(&conn->cfd);
  394. return -1;
  395. }
  396. name[n] = 0;
  397. for(p = name; *p == ' '; p++)
  398. ;
  399. snprint(name, sizeof(name), "%ld", strtoul(p, 0, 0));
  400. p = strrchr(cname, '/');
  401. *p = 0;
  402. if(ds->dir)
  403. snprint(conn->dir, NETPATHLEN, "%s/%s", cname, name);
  404. snprint(data, sizeof(data), "%s/%s/data", cname, name);
  405. /* should be no alarm pending now; re-instate caller's alarm, if any */
  406. calleralarm = dp->oalarm > 0;
  407. if (calleralarm)
  408. alarm(dp->oalarm);
  409. else if (dp->naddrs > 1) /* in a sub-process? */
  410. alarm(Maxconnms);
  411. /* connect */
  412. if(ds->local)
  413. snprint(name, sizeof(name), "connect %s %s", dest, ds->local);
  414. else
  415. snprint(name, sizeof(name), "connect %s", dest);
  416. if(write(cfd, name, strlen(name)) < 0){
  417. closeopenfd(&conn->cfd);
  418. return -1;
  419. }
  420. oalarm = alarm(0); /* don't let alarm interrupt critical section */
  421. if (calleralarm)
  422. dp->oalarm = oalarm; /* time has passed, so update user's */
  423. /* open data connection */
  424. conn->dfd = fd = open(data, ORDWR);
  425. if(fd < 0){
  426. closeopenfd(&conn->cfd);
  427. alarm(dp->oalarm);
  428. return -1;
  429. }
  430. if(ds->cfdp == nil)
  431. closeopenfd(&conn->cfd);
  432. n = conn - dp->conn;
  433. if (dp->winner < 0) {
  434. qlock(&dp->winlck);
  435. if (dp->winner < 0 && conn < dp->connend)
  436. dp->winner = n;
  437. qunlock(&dp->winlck);
  438. }
  439. alarm(calleralarm? dp->oalarm: 0);
  440. return fd;
  441. }
  442. /*
  443. * parse a dial string
  444. */
  445. static void
  446. _dial_string_parse(char *str, DS *ds)
  447. {
  448. char *p, *p2;
  449. strncpy(ds->buf, str, Maxstring);
  450. ds->buf[Maxstring-1] = 0;
  451. p = strchr(ds->buf, '!');
  452. if(p == 0) {
  453. ds->netdir = 0;
  454. ds->proto = "net";
  455. ds->rem = ds->buf;
  456. } else {
  457. if(*ds->buf != '/' && *ds->buf != '#'){
  458. ds->netdir = 0;
  459. ds->proto = ds->buf;
  460. } else {
  461. for(p2 = p; *p2 != '/'; p2--)
  462. ;
  463. *p2++ = 0;
  464. ds->netdir = ds->buf;
  465. ds->proto = p2;
  466. }
  467. *p = 0;
  468. ds->rem = p + 1;
  469. }
  470. }