#include #include enum { Maxconcurr = 4, Maxstring = 128, }; typedef struct DS DS; typedef struct Conn Conn; struct DS { /* dist string */ char *netdir; char *proto; char *rem; /* other args */ char *local; char *dir; int *cfdp; }; struct Conn { Conn *next; int pid; int cfd; int dfd; char dest[Maxstring]; char dir[NETPATHLEN]; }; static Conn* openconn(char *clone, char *dest, char *netdir) { char *x, *p, *e; Conn *c; int n; if((c = malloc(sizeof(Conn))) == nil) return nil; c->next = nil; c->pid = 0; c->cfd = -1; c->dfd = -1; snprint(c->dest, sizeof c->dest, "%s", dest); if(netdir){ if(*clone == '/' && (p = strchr(clone+1, '/'))) clone = ++p; snprint(c->dir, sizeof c->dir, "%s/%s", netdir, clone); } else snprint(c->dir, sizeof c->dir, "%s", clone); e = c->dir + sizeof c->dir; if((p = strrchr(c->dir, '/')) == nil) goto err; if((c->cfd = open(c->dir, ORDWR)) < 0) goto err; if((n = (e - p)-1) <= 0) goto err; if((n = read(c->cfd, p, n)) <= 0) goto err; p[n] = 0; for(x = p; *x == ' '; x++) ; snprint(p, e - p, "/%ld/data", strtoul(x, 0, 0)); if((c->dfd = open(c->dir, ORDWR)) < 0) goto err; if(p = strrchr(c->dir, '/')) *p = 0; return c; err: if(c->cfd >= 0) close(c->cfd); if(c->dfd >= 0) close(c->dfd); free(c); return nil; } static char* readcs(int cs, char *buf, int nbuf, char **destp) { char *p; int n; if((n = read(cs, buf, nbuf-1)) <= 0) return nil; if(buf[n-1] == '\n') n--; buf[n] = 0; if((p = strchr(buf, ' ')) == nil) return nil; *p++ = 0; if(destp) *destp = p; return buf; } static Conn* getconn(int cs, char *buf, int nbuf, char *netdir) { char *clone, *dest; if(clone = readcs(cs, buf, nbuf, &dest)) return openconn(clone, dest, netdir); return nil; } static int connect(Conn *c, char *local) { if(local) return fprint(c->cfd, "connect %s %s", c->dest, local) > 0; else return fprint(c->cfd, "connect %s", c->dest) > 0; } static int aconnect(Conn *c, char *local) { if((c->pid = rfork(RFPROC)) < 0) return 0; else if(c->pid > 0) return 1; notify(nil); if(connect(c, local)) _exits(nil); _exits("%r"); return -1; } static int canfork(char *buf, int nbuf) { int fd; snprint(buf, nbuf, "/proc/%d/note", getpid()); if((fd = open(buf, OWRITE)) >= 0) close(fd); return fd >= 0; } static int csdial(DS *ds) { char buf[Maxstring+NETPATHLEN+4]; Conn *conns, *winner, *c; int cs, ret, kids, more; conns = winner = nil; snprint(buf, sizeof buf, "%s/cs", ds->netdir); if((cs = open(buf, ORDWR)) < 0){ snprint(buf, sizeof buf, "%s/%s/clone", ds->netdir, ds->proto); if((conns = openconn(buf, ds->rem, nil)) == nil) goto out; if(connect(conns, ds->local)) winner = conns; goto out; } if(fprint(cs, "%s!%s", ds->proto, ds->rem) < 0) goto out; seek(cs, 0, 0); if((conns = getconn(cs, buf, sizeof buf, ds->netdir)) == nil){ werrstr("no address to dial"); goto out; } conns->next = getconn(cs, buf, sizeof buf, ds->netdir); if(conns->next == nil || !canfork(buf, sizeof buf)){ if(connect(c = conns, ds->local)){ winner = c; goto out; } if((c = c->next) == nil) goto out; if(connect(c, ds->local)){ winner = c; goto out; } while(c->next = getconn(cs, buf, sizeof buf, ds->netdir)){ if(connect(c = c->next, ds->local)){ winner = c; goto out; } } goto out; } more = 1; kids = 0; if(aconnect(conns, ds->local)) kids++; if(aconnect(conns->next, ds->local)) kids++; for(;;){ Waitmsg *m; while(more && kids < Maxconcurr){ if((c = getconn(cs, buf, sizeof buf, ds->netdir)) == nil){ more = 0; break; } c->next = conns; conns = c; if(aconnect(c, ds->local)) kids++; } if(kids == 0) break; if(m = wait()){ for(c = conns; c; c = c->next){ if(c->pid != m->pid) continue; c->pid = 0; --kids; if(m->msg[0]){ char *p; if(p = strchr(m->msg, ':')) p++; else p = m->msg; while(*p == ' ') p++; werrstr("%s", p); } else if(winner) fprint(c->cfd, "hangup"); else winner = c; break; } free(m); } if(winner || m == nil){ more = 0; for(c = conns; c; c = c->next) if(c->pid) postnote(PNPROC, c->pid, "die"); } } out: if(cs >= 0) close(cs); if(c = winner){ if(ds->dir) strncpy(ds->dir, c->dir, NETPATHLEN); if(ds->cfdp) *ds->cfdp = c->cfd; else close(c->cfd); ret = c->dfd; } else ret = -1; while(c = conns){ conns = c->next; if(c != winner){ close(c->cfd); close(c->dfd); } free(c); } return ret; } int dial(char *dest, char *local, char *dir, int *cfdp) { char buf[Maxstring], *p, *x; int ret; DS ds; ds.local = local; ds.dir = dir; ds.cfdp = cfdp; snprint(buf, sizeof buf, "%s", dest); if((p = strchr(buf, '!')) == 0) { ds.netdir = 0; ds.proto = "net"; ds.rem = buf; } else { if(buf[0] != '/' && buf[0] != '#'){ ds.netdir = 0; ds.proto = buf; } else { for(x = p; *x != '/'; x--) ; *x++ = 0; ds.netdir = buf; ds.proto = x; } *p++ = 0; ds.rem = p; } if(ds.netdir) return csdial(&ds); ds.netdir = "/net"; if((ret = csdial(&ds)) < 0){ char err[ERRMAX]; rerrstr(err, sizeof err); if(strstr(err, "refused")) return ret; ds.netdir = "/net.alt"; if((ret = csdial(&ds)) < 0){ char alterr[ERRMAX]; /* use previous error if /net.alt was not available */ rerrstr(alterr, sizeof alterr); if(strstr(alterr, "translate") || strstr(alterr, "does not exist")) werrstr("%s", err); } } return ret; }