diff --git a/sys/src/cmd/sshnet.c b/sys/src/cmd/sshnet.c new file mode 100755 index 000000000..a74a9e326 --- /dev/null +++ b/sys/src/cmd/sshnet.c @@ -0,0 +1,1286 @@ +/* + * SSH network file system. + * Presents remote TCP stack as /net-style file system. + */ + +#include +#include +#include +#include +#include +#include +#include <9p.h> + +typedef struct Client Client; +typedef struct Msg Msg; + +enum +{ + Qroot, + Qcs, + Qtcp, + Qclone, + Qn, + Qctl, + Qdata, + Qlocal, + Qremote, + Qstatus, +}; + +#define PATH(type, n) ((type)|((n)<<8)) +#define TYPE(path) ((int)(path) & 0xFF) +#define NUM(path) ((uint)(path)>>8) + +Channel *sshmsgchan; /* chan(Msg*) */ +Channel *fsreqchan; /* chan(Req*) */ +Channel *fsreqwaitchan; /* chan(nil) */ +Channel *fsclunkchan; /* chan(Fid*) */ +Channel *fsclunkwaitchan; /* chan(nil) */ +ulong time0; + +enum +{ + Closed, + Dialing, + Established, + Teardown, +}; + +char *statestr[] = { + "Closed", + "Dialing", + "Established", + "Teardown", +}; + +struct Client +{ + int ref; + int state; + int num; + int servernum; + char *connect; + + int sendpkt; + int sendwin; + int recvwin; + int recvacc; + + Req *wq; + Req **ewq; + + Req *rq; + Req **erq; + + Msg *mq; + Msg **emq; +}; + +enum { + MSG_CHANNEL_OPEN = 90, + MSG_CHANNEL_OPEN_CONFIRMATION, + MSG_CHANNEL_OPEN_FAILURE, + MSG_CHANNEL_WINDOW_ADJUST, + MSG_CHANNEL_DATA, + MSG_CHANNEL_EXTENDED_DATA, + MSG_CHANNEL_EOF, + MSG_CHANNEL_CLOSE, + MSG_CHANNEL_REQUEST, + MSG_CHANNEL_SUCCESS, + MSG_CHANNEL_FAILURE, + + MaxPacket = 1<<15, + WinPackets = 8, +}; + +struct Msg +{ + Msg *link; + + uchar *rp; + uchar *wp; + uchar *ep; + uchar buf[MaxPacket]; +}; + +#define PUT4(p, u) (p)[0] = (u)>>24, (p)[1] = (u)>>16, (p)[2] = (u)>>8, (p)[3] = (u) +#define GET4(p) (u32int)(p)[3] | (u32int)(p)[2]<<8 | (u32int)(p)[1]<<16 | (u32int)(p)[0]<<24 + +int nclient; +Client **client; +char *mtpt; +int sshfd; +int localport; +char localip[] = "::"; + +int +vpack(uchar *p, int n, char *fmt, va_list a) +{ + uchar *p0 = p, *e = p+n; + u32int u; + void *s; + int c; + + for(;;){ + switch(c = *fmt++){ + case '\0': + return p - p0; + case '_': + if(++p > e) goto err; + break; + case '.': + *va_arg(a, void**) = p; + break; + case 'b': + if(p >= e) goto err; + *p++ = va_arg(a, int); + break; + case '[': + case 's': + s = va_arg(a, void*); + u = va_arg(a, int); + if(c == 's'){ + if(p+4 > e) goto err; + PUT4(p, u), p += 4; + } + if(u > e-p) goto err; + memmove(p, s, u); + p += u; + break; + case 'u': + u = va_arg(a, int); + if(p+4 > e) goto err; + PUT4(p, u), p += 4; + break; + } + } +err: + return -1; +} + +int +vunpack(uchar *p, int n, char *fmt, va_list a) +{ + uchar *p0 = p, *e = p+n; + u32int u; + void *s; + + for(;;){ + switch(*fmt++){ + case '\0': + return p - p0; + case '_': + if(++p > e) goto err; + break; + case '.': + *va_arg(a, void**) = p; + break; + case 'b': + if(p >= e) goto err; + *va_arg(a, int*) = *p++; + break; + case 's': + if(p+4 > e) goto err; + u = GET4(p), p += 4; + if(u > e-p) goto err; + *va_arg(a, void**) = p; + *va_arg(a, int*) = u; + p += u; + break; + case '[': + s = va_arg(a, void*); + u = va_arg(a, int); + if(u > e-p) goto err; + memmove(s, p, u); + p += u; + break; + case 'u': + if(p+4 > e) goto err; + u = GET4(p); + *va_arg(a, int*) = u; + p += 4; + break; + } + } +err: + return -1; +} + +Msg* +allocmsg(void) +{ + Msg *m; + + m = emalloc9p(sizeof(Msg)); + m->link = nil; + m->rp = m->wp = m->buf; + m->ep = m->rp + sizeof(m->buf); + return m; +} + +Msg* +pack(Msg *m, char *fmt, ...) +{ + va_list a; + int n; + + if(m == nil) + m = allocmsg(); + va_start(a, fmt); + n = vpack(m->wp, m->ep - m->wp, fmt, a); + if(n < 0) + sysfatal("pack faild"); + m->wp += n; + va_end(a); + return m; +} + +int +unpack(Msg *m, char *fmt, ...) +{ + va_list a; + int n; + + va_start(a, fmt); + n = vunpack(m->rp, m->wp - m->rp, fmt, a); + if(n > 0) + m->rp += n; + va_end(a); + return n; +} + +void +sendmsg(Msg *m) +{ + int n; + + if(m == nil) + return; + n = m->wp - m->rp; + if(n > 0){ + if(write(sshfd, m->rp, n) != n) + sysfatal("write to ssh failed: %r"); + } + free(m); +} + +int +newclient(void) +{ + int i; + Client *c; + + for(i=0; iref==0 && client[i]->state == Closed) + return i; + + if(nclient%16 == 0) + client = erealloc9p(client, (nclient+16)*sizeof(client[0])); + + c = emalloc9p(sizeof(Client)); + memset(c, 0, sizeof(*c)); + c->num = nclient; + client[nclient++] = c; + return c->num; +} + +Client* +getclient(int num) +{ + if(num < 0 || num >= nclient) + return nil; + return client[num]; +} + +void +adjustwin(Client *c, int len) +{ + c->recvacc += len; + if(c->recvacc >= MaxPacket*WinPackets/2 || c->recvwin < MaxPacket){ + sendmsg(pack(nil, "buu", MSG_CHANNEL_WINDOW_ADJUST, c->servernum, c->recvacc)); + c->recvacc = 0; + } + c->recvwin += len; +} + +void +senddata(Client *c, void *data, int len) +{ + sendmsg(pack(nil, "bus", MSG_CHANNEL_DATA, c->servernum, (char*)data, len)); + c->sendwin -= len; +} + +void +queuerreq(Client *c, Req *r) +{ + if(c->rq==nil) + c->erq = &c->rq; + *c->erq = r; + r->aux = nil; + c->erq = (Req**)&r->aux; +} + +void +queuermsg(Client *c, Msg *m) +{ + if(c->mq==nil) + c->emq = &c->mq; + *c->emq = m; + m->link = nil; + c->emq = (Msg**)&m->link; +} + +void +matchrmsgs(Client *c) +{ + Req *r; + Msg *m; + int n, rm; + + while(c->rq != nil && c->mq != nil){ + r = c->rq; + c->rq = r->aux; + + rm = 0; + m = c->mq; + n = r->ifcall.count; + if(n >= m->wp - m->rp){ + n = m->wp - m->rp; + c->mq = m->link; + rm = 1; + } + memmove(r->ofcall.data, m->rp, n); + if(rm) + free(m); + else + m->rp += n; + r->ofcall.count = n; + respond(r, nil); + adjustwin(c, n); + } +} + +void +queuewreq(Client *c, Req *r) +{ + if(c->wq==nil) + c->ewq = &c->wq; + *c->ewq = r; + r->aux = nil; + c->ewq = (Req**)&r->aux; +} + +void +procwreqs(Client *c) +{ + Req *r; + int n; + + while((r = c->wq) != nil && (n = c->sendwin) > 0){ + if(n > c->sendpkt) + n = c->sendpkt; + if(r->ifcall.count > n){ + senddata(c, r->ifcall.data, n); + r->ifcall.count -= n; + memmove(r->ifcall.data, (char*)r->ifcall.data + n, r->ifcall.count); + continue; + } + c->wq = (Req*)r->aux; + r->aux = nil; + senddata(c, r->ifcall.data, r->ifcall.count); + r->ofcall.count = r->ifcall.count; + respond(r, nil); + } +} + +Req* +findreq(Client *c, Req *r) +{ + Req **l; + + for(l=&c->rq; *l; l=(Req**)&(*l)->aux){ + if(*l == r){ + *l = r->aux; + if(*l == nil) + c->erq = l; + return r; + } + } + for(l=&c->wq; *l; l=(Req**)&(*l)->aux){ + if(*l == r){ + *l = r->aux; + if(*l == nil) + c->ewq = l; + return r; + } + } + return nil; +} + +void +dialedclient(Client *c) +{ + Req *r; + + if(r=c->wq){ + if(r->aux != nil) + sysfatal("more than one outstanding dial request (BUG)"); + if(c->state == Established) + respond(r, nil); + else + respond(r, "connect failed"); + } + c->wq = nil; +} + +void +teardownclient(Client *c) +{ + c->state = Teardown; + sendmsg(pack(nil, "bu", MSG_CHANNEL_EOF, c->servernum)); +} + +void +hangupclient(Client *c) +{ + Req *r, *next; + Msg *m, *mnext; + + c->state = Closed; + for(m=c->mq; m; m=mnext){ + mnext = m->link; + free(m); + } + c->mq = nil; + for(r=c->rq; r; r=next){ + next = r->aux; + respond(r, "hangup on network connection"); + } + c->rq = nil; + for(r=c->wq; r; r=next){ + next = r->aux; + respond(r, "hangup on network connection"); + } + c->wq = nil; +} + +void +closeclient(Client *c) +{ + Msg *m, *next; + + if(--c->ref) + return; + + if(c->rq != nil || c->wq != nil) + sysfatal("ref count reached zero with requests pending (BUG)"); + + for(m=c->mq; m; m=next){ + next = m->link; + free(m); + } + c->mq = nil; + + if(c->state != Closed) + teardownclient(c); +} + + +void +sshreadproc(void*) +{ + Msg *m; + int n; + + for(;;){ + m = allocmsg(); + n = read(sshfd, m->rp, m->ep - m->rp); + if(n <= 0) + sysfatal("eof on ssh connection"); + m->wp += n; + sendp(sshmsgchan, m); + } +} + +typedef struct Tab Tab; +struct Tab +{ + char *name; + ulong mode; +}; + +Tab tab[] = +{ + "/", DMDIR|0555, + "cs", 0666, + "tcp", DMDIR|0555, + "clone", 0666, + nil, DMDIR|0555, + "ctl", 0666, + "data", 0666, + "local", 0444, + "remote", 0444, + "status", 0444, +}; + +static void +fillstat(Dir *d, uvlong path) +{ + Tab *t; + + memset(d, 0, sizeof(*d)); + d->uid = estrdup9p("ssh"); + d->gid = estrdup9p("ssh"); + d->qid.path = path; + d->atime = d->mtime = time0; + t = &tab[TYPE(path)]; + if(t->name) + d->name = estrdup9p(t->name); + else{ + d->name = smprint("%ud", NUM(path)); + if(d->name == nil) + sysfatal("out of memory"); + } + d->qid.type = t->mode>>24; + d->mode = t->mode; +} + +static void +fsattach(Req *r) +{ + if(r->ifcall.aname && r->ifcall.aname[0]){ + respond(r, "invalid attach specifier"); + return; + } + r->fid->qid.path = PATH(Qroot, 0); + r->fid->qid.type = QTDIR; + r->fid->qid.vers = 0; + r->ofcall.qid = r->fid->qid; + respond(r, nil); +} + +static void +fsstat(Req *r) +{ + fillstat(&r->d, r->fid->qid.path); + respond(r, nil); +} + +static int +rootgen(int i, Dir *d, void*) +{ + i += Qroot+1; + if(i <= Qtcp){ + fillstat(d, i); + return 0; + } + return -1; +} + +static int +tcpgen(int i, Dir *d, void*) +{ + i += Qtcp+1; + if(i < Qn){ + fillstat(d, i); + return 0; + } + i -= Qn; + if(i < nclient){ + fillstat(d, PATH(Qn, i)); + return 0; + } + return -1; +} + +static int +clientgen(int i, Dir *d, void *aux) +{ + Client *c; + + c = aux; + i += Qn+1; + if(i <= Qstatus){ + fillstat(d, PATH(i, c->num)); + return 0; + } + return -1; +} + +static char* +fswalk1(Fid *fid, char *name, Qid *qid) +{ + int i, n; + char buf[32]; + ulong path; + + path = fid->qid.path; + if(!(fid->qid.type&QTDIR)) + return "walk in non-directory"; + + if(strcmp(name, "..") == 0){ + switch(TYPE(path)){ + case Qn: + qid->path = PATH(Qtcp, NUM(path)); + qid->type = tab[Qtcp].mode>>24; + return nil; + case Qtcp: + qid->path = PATH(Qroot, 0); + qid->type = tab[Qroot].mode>>24; + return nil; + case Qroot: + return nil; + default: + return "bug in fswalk1"; + } + } + + i = TYPE(path)+1; + for(; ipath = PATH(i, n); + qid->type = tab[i].mode>>24; + return nil; + } + break; + } + if(strcmp(name, tab[i].name) == 0){ + qid->path = PATH(i, NUM(path)); + qid->type = tab[i].mode>>24; + return nil; + } + if(tab[i].mode&DMDIR) + break; + } + return "directory entry not found"; +} + +typedef struct Cs Cs; +struct Cs +{ + char *resp; + int isnew; +}; + +static int +ndbfindport(char *p) +{ + char *s, *port; + int n; + static Ndb *db; + + if(*p == '\0') + return -1; + + n = strtol(p, &s, 0); + if(*s == '\0') + return n; + + if(db == nil){ + db = ndbopen("/lib/ndb/common"); + if(db == nil) + return -1; + } + + port = ndbgetvalue(db, nil, "tcp", p, "port", nil); + if(port == nil) + return -1; + n = atoi(port); + free(port); + + return n; +} + +static void +csread(Req *r) +{ + Cs *cs; + + cs = r->fid->aux; + if(cs->resp==nil){ + respond(r, "cs read without write"); + return; + } + if(r->ifcall.offset==0){ + if(!cs->isnew){ + r->ofcall.count = 0; + respond(r, nil); + return; + } + cs->isnew = 0; + } + readstr(r, cs->resp); + respond(r, nil); +} + +static void +cswrite(Req *r) +{ + int port, nf; + char err[ERRMAX], *f[4], *s, *ns; + Cs *cs; + + cs = r->fid->aux; + s = emalloc9p(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + + nf = getfields(s, f, nelem(f), 0, "!"); + if(nf != 3){ + free(s); + respond(r, "can't translate"); + return; + } + if(strcmp(f[0], "tcp") != 0 && strcmp(f[0], "net") != 0){ + free(s); + respond(r, "unknown protocol"); + return; + } + port = ndbfindport(f[2]); + if(port <= 0){ + free(s); + respond(r, "no translation found"); + return; + } + + ns = smprint("%s/tcp/clone %s!%d", mtpt, f[1], port); + if(ns == nil){ + free(s); + rerrstr(err, sizeof err); + respond(r, err); + return; + } + free(s); + free(cs->resp); + cs->resp = ns; + cs->isnew = 1; + r->ofcall.count = r->ifcall.count; + respond(r, nil); +} + +static void +ctlread(Req *r, Client *c) +{ + char buf[32]; + + sprint(buf, "%d", c->num); + readstr(r, buf); + respond(r, nil); +} + +static void +ctlwrite(Req *r, Client *c) +{ + char *f[3], *s; + int nf; + + s = emalloc9p(r->ifcall.count+1); + memmove(s, r->ifcall.data, r->ifcall.count); + s[r->ifcall.count] = '\0'; + + nf = tokenize(s, f, 3); + if(nf == 0){ + free(s); + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + } + + if(strcmp(f[0], "hangup") == 0){ + if(c->state != Established) + goto Badarg; + if(nf != 1) + goto Badarg; + teardownclient(c); + r->ofcall.count = r->ifcall.count; + respond(r, nil); + }else if(strcmp(f[0], "connect") == 0){ + if(c->state != Closed) + goto Badarg; + if(nf != 2) + goto Badarg; + c->connect = estrdup9p(f[1]); + nf = getfields(f[1], f, nelem(f), 0, "!"); + if(nf != 2){ + free(c->connect); + c->connect = nil; + goto Badarg; + } + c->sendwin = MaxPacket; + c->recvwin = WinPackets * MaxPacket; + c->recvacc = 0; + c->state = Dialing; + queuewreq(c, r); + + sendmsg(pack(nil, "bsuuususu", MSG_CHANNEL_OPEN, + "direct-tcpip", 12, + c->num, c->recvwin, MaxPacket, + f[0], strlen(f[0]), ndbfindport(f[1]), + localip, strlen(localip), localport)); + }else{ + Badarg: + respond(r, "bad or inappropriate tcp control message"); + } + free(s); +} + +static void +dataread(Req *r, Client *c) +{ + if(c->state != Established){ + respond(r, "not connected"); + return; + } + queuerreq(c, r); + matchrmsgs(c); +} + +static void +datawrite(Req *r, Client *c) +{ + if(c->state != Established){ + respond(r, "not connected"); + return; + } + if(r->ifcall.count == 0){ + r->ofcall.count = r->ifcall.count; + respond(r, nil); + return; + } + queuewreq(c, r); + procwreqs(c); +} + +static void +localread(Req *r) +{ + char buf[128]; + + snprint(buf, sizeof buf, "%s!%d\n", localip, localport); + readstr(r, buf); + respond(r, nil); +} + +static void +remoteread(Req *r, Client *c) +{ + char *s; + char buf[128]; + + s = c->connect; + if(s == nil) + s = "::!0"; + snprint(buf, sizeof buf, "%s\n", s); + readstr(r, buf); + respond(r, nil); +} + +static void +statusread(Req *r, Client *c) +{ + char *s; + + s = statestr[c->state]; + readstr(r, s); + respond(r, nil); +} + +static void +fsread(Req *r) +{ + char e[ERRMAX]; + ulong path; + + path = r->fid->qid.path; + switch(TYPE(path)){ + default: + snprint(e, sizeof e, "bug in fsread path=%lux", path); + respond(r, e); + break; + + case Qroot: + dirread9p(r, rootgen, nil); + respond(r, nil); + break; + + case Qcs: + csread(r); + break; + + case Qtcp: + dirread9p(r, tcpgen, nil); + respond(r, nil); + break; + + case Qn: + dirread9p(r, clientgen, client[NUM(path)]); + respond(r, nil); + break; + + case Qctl: + ctlread(r, client[NUM(path)]); + break; + + case Qdata: + dataread(r, client[NUM(path)]); + break; + + case Qlocal: + localread(r); + break; + + case Qremote: + remoteread(r, client[NUM(path)]); + break; + + case Qstatus: + statusread(r, client[NUM(path)]); + break; + } +} + +static void +fswrite(Req *r) +{ + ulong path; + char e[ERRMAX]; + + path = r->fid->qid.path; + switch(TYPE(path)){ + default: + snprint(e, sizeof e, "bug in fswrite path=%lux", path); + respond(r, e); + break; + + case Qcs: + cswrite(r); + break; + + case Qctl: + ctlwrite(r, client[NUM(path)]); + break; + + case Qdata: + datawrite(r, client[NUM(path)]); + break; + } +} + +static void +fsopen(Req *r) +{ + static int need[4] = { 4, 2, 6, 1 }; + ulong path; + int n; + Tab *t; + Cs *cs; + + /* + * lib9p already handles the blatantly obvious. + * we just have to enforce the permissions we have set. + */ + path = r->fid->qid.path; + t = &tab[TYPE(path)]; + n = need[r->ifcall.mode&3]; + if((n&t->mode) != n){ + respond(r, "permission denied"); + return; + } + + switch(TYPE(path)){ + case Qcs: + cs = emalloc9p(sizeof(Cs)); + r->fid->aux = cs; + respond(r, nil); + break; + case Qclone: + n = newclient(); + path = PATH(Qctl, n); + r->fid->qid.path = path; + r->ofcall.qid.path = path; + if(chatty9p) + fprint(2, "open clone => path=%lux\n", path); + t = &tab[Qctl]; + /* fall through */ + default: + if(t-tab >= Qn) + client[NUM(path)]->ref++; + respond(r, nil); + break; + } +} + +static void +fsflush(Req *r) +{ + int i; + + for(i=0; ioldreq)) + respond(r->oldreq, "interrupted"); + respond(r, nil); +} + +static void +handlemsg(Msg *m) +{ + int chan, win, pkt, n; + Client *c; + char *s; + + switch(m->rp[0]){ + case MSG_CHANNEL_WINDOW_ADJUST: + if(unpack(m, "_uu", &chan, &n) < 0) + break; + c = getclient(chan); + if(c != nil && c->state==Established){ + c->sendwin += n; + procwreqs(c); + } + break; + case MSG_CHANNEL_DATA: + if(unpack(m, "_us", &chan, &s, &n) < 0) + break; + c = getclient(chan); + if(c != nil && c->state==Established){ + c->recvwin -= n; + m->rp = (uchar*)s; + queuermsg(c, m); + matchrmsgs(c); + return; + } + break; + case MSG_CHANNEL_EOF: + if(unpack(m, "_u", &chan) < 0) + break; + c = getclient(chan); + if(c != nil){ + hangupclient(c); + m->rp = m->wp = m->buf; + sendmsg(pack(m, "bu", MSG_CHANNEL_CLOSE, c->servernum)); + return; + } + break; + case MSG_CHANNEL_CLOSE: + if(unpack(m, "_u", &chan) < 0) + break; + c = getclient(chan); + if(c != nil) + hangupclient(c); + break; + case MSG_CHANNEL_OPEN_CONFIRMATION: + if(unpack(m, "_uuuu", &chan, &n, &win, &pkt) < 0) + break; + c = getclient(chan); + if(c == nil || c->state != Dialing) + break; + if(pkt <= 0 || pkt > MaxPacket) + pkt = MaxPacket; + c->sendpkt = pkt; + c->sendwin = win; + c->servernum = n; + c->state = Established; + dialedclient(c); + break; + case MSG_CHANNEL_OPEN_FAILURE: + if(unpack(m, "_uu", &chan, &n) < 0) + break; + c = getclient(chan); + if(c == nil || c->state != Dialing) + break; + c->servernum = n; + c->state = Closed; + dialedclient(c); + break; + } + free(m); +} + +void +fsnetproc(void*) +{ + ulong path; + Alt a[4]; + Cs *cs; + Fid *fid; + Req *r; + Msg *m; + + threadsetname("fsthread"); + + a[0].op = CHANRCV; + a[0].c = fsclunkchan; + a[0].v = &fid; + a[1].op = CHANRCV; + a[1].c = fsreqchan; + a[1].v = &r; + a[2].op = CHANRCV; + a[2].c = sshmsgchan; + a[2].v = &m; + a[3].op = CHANEND; + + for(;;){ + switch(alt(a)){ + case 0: + path = fid->qid.path; + switch(TYPE(path)){ + case Qcs: + cs = fid->aux; + if(cs){ + free(cs->resp); + free(cs); + } + break; + } + if(fid->omode != -1 && TYPE(path) >= Qn) + closeclient(client[NUM(path)]); + sendp(fsclunkwaitchan, nil); + break; + case 1: + switch(r->ifcall.type){ + case Tattach: + fsattach(r); + break; + case Topen: + fsopen(r); + break; + case Tread: + fsread(r); + break; + case Twrite: + fswrite(r); + break; + case Tstat: + fsstat(r); + break; + case Tflush: + fsflush(r); + break; + default: + respond(r, "bug in fsthread"); + break; + } + sendp(fsreqwaitchan, 0); + break; + case 2: + handlemsg(m); + break; + } + } +} + +static void +fssend(Req *r) +{ + sendp(fsreqchan, r); + recvp(fsreqwaitchan); /* avoids need to deal with spurious flushes */ +} + +static void +fsdestroyfid(Fid *fid) +{ + sendp(fsclunkchan, fid); + recvp(fsclunkwaitchan); +} + +void +takedown(Srv*) +{ + threadexitsall("done"); +} + +Srv fs = +{ +.attach= fssend, +.destroyfid= fsdestroyfid, +.walk1= fswalk1, +.open= fssend, +.read= fssend, +.write= fssend, +.stat= fssend, +.flush= fssend, +.end= takedown, +}; + +int pfd[2]; +int sshargc; +char **sshargv; + +void +startssh(void *) +{ + char *f; + + close(pfd[0]); + dup(pfd[1], 0); + dup(pfd[1], 1); + close(pfd[1]); + if(strncmp(sshargv[0], "./", 2) != 0) + f = smprint("/bin/%s", sshargv[0]); + else + f = sshargv[0]; + procexec(nil, f, sshargv); + sysfatal("exec: %r"); +} + +void +usage(void) +{ + fprint(2, "usage: sshnet [-m mtpt] [ssh options]\n"); + exits("usage"); +} + +void +threadmain(int argc, char **argv) +{ + char *service; + + fmtinstall('H', encodefmt); + + mtpt = "/net"; + service = nil; + ARGBEGIN{ + case 'D': + chatty9p++; + break; + case 'm': + mtpt = EARGF(usage()); + break; + case 's': + service = EARGF(usage()); + break; + default: + usage(); + }ARGEND + + if(argc == 0) + usage(); + + sshargc = argc + 2; + sshargv = emalloc9p(sizeof(char *) * (sshargc + 1)); + sshargv[0] = "ssh"; + sshargv[1] = "-X"; + memcpy(sshargv + 2, argv, argc * sizeof(char *)); + + pipe(pfd); + sshfd = pfd[0]; + procrfork(startssh, nil, mainstacksize, RFFDG|RFNOTEG|RFNAMEG); + close(pfd[1]); + + time0 = time(0); + sshmsgchan = chancreate(sizeof(Msg*), 16); + fsreqchan = chancreate(sizeof(Req*), 0); + fsreqwaitchan = chancreate(sizeof(void*), 0); + fsclunkchan = chancreate(sizeof(Fid*), 0); + fsclunkwaitchan = chancreate(sizeof(void*), 0); + + procrfork(sshreadproc, nil, mainstacksize, RFNAMEG|RFNOTEG); + procrfork(fsnetproc, nil, mainstacksize, RFNAMEG|RFNOTEG); + + threadpostmountsrv(&fs, service, mtpt, MREPL); + exits(0); +}