dial.c 9.9 KB

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