various tweeks for corner cases, etc. Reference: /n/atom/patch/applied2013/ssh2upd Date: Wed Jun 19 04:20:48 CES 2013 Signed-off-by: quanstro@quanstro.net --- /sys/src/cmd/ssh2/ssh.c Wed Jun 19 04:20:22 2013 +++ /sys/src/cmd/ssh2/ssh.c Wed Jun 19 04:20:22 2013 @@ -319,9 +319,9 @@ if(iflag){ consfd = open("/dev/cons", ORDWR); cctlfd = open("/dev/consctl", OWRITE); + if(!cooked && subsystem == nil) + fprint(cctlfd, "rawon"); } - if(iflag && !cooked && subsystem == nil) - fprint(cctlfd, "rawon"); if (doauth(cfd1, whichkey) < 0) goto bail; @@ -392,6 +392,7 @@ p = buf + n; write(1, buf, p-buf); } + fprint(cfd1, "hangup"); postnote(PNPROC, kbdpid, "kill"); fprint(2, "Connection closed by server\n"); break; @@ -428,6 +429,7 @@ lstart = (buf[n-1] == '\n' || buf[n-1] == '\r'); write(dfd2, buf, n); } + fprint(cfd1, "hangup"); postnote(PNPROC, netpid, "kill"); fprint(2, "EOF on client side\n"); break; @@ -444,6 +446,7 @@ close(dfd2); close(dfd1); close(cfd2); + fprint(cfd1, "hangup"); close(cfd1); write(notefd, "kill", 4); close(notefd); --- /sys/src/cmd/ssh2/sshsession.c Wed Jun 19 04:20:22 2013 +++ /sys/src/cmd/ssh2/sshsession.c Wed Jun 19 04:20:22 2013 @@ -2,18 +2,21 @@ #include #include #include +#include void newchannel(int, char *, int); char *get_string(char *, char *); char *confine(char *, char *); void runcmd(int, int, char *, char *, char *, char *); +Ref nchan; int errfd, slfd, toppid, sflag, tflag, prevent; char *shell; char *restdir; char *srvpt; char *nsfile = nil; char *uname; +char *netdir; void usage(void) @@ -24,8 +27,8 @@ main(int argc, char *argv[]) { - char *netdir, *filnam, *p, *q; - int ctlfd, fd, n; + char *filnam, *p, *q; + int ctlfd, topctl, fd, n; char buf[128]; rfork(RFNOTEG); @@ -62,10 +65,18 @@ uname = "none"; netdir = getenv("net"); fprint(errfd, "net is %s\n", netdir); + filnam = smprint("%s/ctl", netdir); + topctl = open(filnam, OWRITE); + if (topctl < 0) { + fprint(errfd, "could not open control file: %r\n"); + exits(nil); + } + free(filnam); filnam = smprint("%s/clone", netdir); ctlfd = open(filnam, ORDWR); if (ctlfd < 0) { fprint(errfd, "could not clone: %s: %r\n", filnam); + fprint(topctl, "hangup"); exits(nil); } free(filnam); @@ -73,7 +84,7 @@ fd = open(filnam, OREAD); if (fd < 0) { fprint(errfd, "Couldn't open data: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } n = read(fd, buf, 128); @@ -81,7 +92,7 @@ free(filnam); if (n < 0) { fprint(errfd, "Read error for cap: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } else if (n > 0) { @@ -90,12 +101,12 @@ fd = open("#ยค/capuse", OWRITE); if (fd < 0) { fprint(errfd, "Couldn't open capuse: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } if (write(fd, buf, n) < 0) { fprint(errfd, "Write to capuse failed: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } close(fd); @@ -124,7 +135,7 @@ fd = open(p, ORDWR); if (fd < 0) { fprint(errfd, "srv open failed; %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } mount(fd, -1, "/net", MBEFORE, ""); @@ -133,26 +144,35 @@ fd = open(filnam, ORDWR); if (fd < 0) { fprint(errfd, "listen failed: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } n = read(fd, buf, 128); fprint(errfd, "read from listen file returned %d\n", n); if (n <= 0) { fprint(errfd, "read on listen failed: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); } buf[n] = '\0'; fprint(errfd, "read %s\n", buf); switch (fork()) { case 0: - close(ctlfd); + incref(&nchan); newchannel(fd, netdir, atoi(buf)); + /* + * Normally newchannel doesn't return, but in case it does... + */ + if (decref(&nchan) == 0) { + fprint(topctl, "hangup"); + } + close(fd); + close(ctlfd); + exits(nil); break; case -1: fprint(errfd, "fork failed: %r\n"); - fprint(ctlfd, "hangup"); + fprint(topctl, "hangup"); exits(nil); break; default: @@ -167,32 +187,36 @@ { char *p, *q, *reqfile, *datafile; int n, reqfd, datafd, motdfd, want_reply, already_done; - char buf[32768], buf2[10240], cmd[1024]; + char buf[32769], buf2[10240], cmd[1024]; - close(fd); already_done = 0; reqfile = smprint("%s/%d/request", conndir, channum); reqfd = open(reqfile, ORDWR); if (reqfd < 0) { fprint(errfd, "Couldn't open request file: %r\n"); - exits(nil); + return; } datafile = smprint("%s/%d/data", conndir, channum); datafd = open(datafile, ORDWR); if (datafd < 0) { fprint(errfd, "Couldn't open data file: %r\n"); - exits(nil); + return; } while (1) { n = read(reqfd, buf, 32768); - fprint(errfd, "read from request file returned %d\n", n); if (n == 0) { - exits(nil); + if (already_done) /* let the child dec the ref */ + exits(nil); + return; } else if (n < 0) { fprint(errfd, "Read failed: %r\n"); - exits(nil); + if (already_done) + exits(nil); + return; } + buf[n] = 0; + fprint(errfd, "read from request file returned %d %s\n", n, buf); for (p = buf; p < buf + n && *p != ' '; ++p) ; *p = '\0'; ++p; @@ -217,6 +241,7 @@ } switch (fork()) { case 0: + close(fd); if (sflag) snprint(cmd, 1024, "-s%s", shell); else @@ -245,7 +270,7 @@ if (want_reply) fprint(reqfd, "failure"); fprint(2, "Cannot fork: %r\n"); - exits(nil); + return; break; default: already_done = 1; @@ -262,6 +287,7 @@ } switch (fork()) { case 0: + close(fd); if (restdir) chdir(restdir); if (!prevent || (q = getenv("sshsession")) && strcmp(q, "allow") == 0) @@ -290,6 +316,7 @@ else if (strcmp(buf, "subsystem") == 0) { if (want_reply) fprint(reqfd, "failure"); + return; } else if (strcmp(buf, "window-change") == 0) { if (want_reply) @@ -399,10 +426,15 @@ syslog(1, "ssh", "closing ssh session for %s", uname); fprint(errfd, "closing connection\n"); write(reqfd, "close", 5); - p = smprint("/proc/%d/notepg", toppid); - fd = open(p, OWRITE); - write(fd, "interrupt", 3); - close(fd); + if (decref(&nchan) == 0) { + p = smprint("%s/ctl", netdir); + fd = open(p, OWRITE); + if (fd >= 0) { + fprint(fd, "hangup"); + close(fd); + } + free(p); + } break; } exits(nil); --- /sys/src/cmd/ssh2/sshtun.c Wed Jun 19 04:20:22 2013 +++ /sys/src/cmd/ssh2/sshtun.c Wed Jun 19 04:20:22 2013 @@ -102,8 +102,6 @@ int slfd; char uid[32]; MBox keymbox; -QLock availlck; -Rendez availrend; void usage(void) @@ -152,8 +150,7 @@ close(fd); } - keymbox.mchan = chancreate(4, 0); - availrend.l = &availlck; + keymbox.mchan = chancreate(sizeof(ulong), 0); dh_init(pkas); if (rfork(RFNOTEG) < 0) @@ -164,6 +161,7 @@ close(1); server(mntpt, srvpt); + threadexits(0); } Ioproc *io9p; @@ -395,26 +393,22 @@ threadexits(nil); } sc = c->chans[fnum & ConnMask]; - qlock(&c->l); - sc->lreq = r; for (i = 0; i < c->nchan; ++i) if (c->chans[i] && c->chans[i]->state == Opening && c->chans[i]->ann && strcmp(c->chans[i]->ann, sc->ann) == 0) break; if (i >= c->nchan) { sc->state = Listening; - rsleep(&sc->r); + recvul(sc->synch); i = sc->waker; if (i < 0) { - qunlock(&c->l); r->aux = 0; responderror(r); threadexits(nil); } } else - rwakeup(&c->chans[i]->r); - qunlock(&c->l); + nbsendul(c->chans[i]->synch, 1); if (c->state == Closed || c->state == Closing || c->state == Eof) { r->aux = 0; respond(r, "Listen on a closed connection"); @@ -634,6 +628,8 @@ cnum = fnum & ConnMask; sc = c->chans[cnum]; } + if (debug) + fprint(2, "readreqrem: %x %d %d\n", fnum, lev, xconn); switch (lev) { case 0: if (r->ifcall.offset == 0 && keymbox.state != Empty) { @@ -732,6 +728,9 @@ threadexits(nil); } } + if (debug) + fprint(2, "readreqrem: ifcall=%d m=%s\n", + r->ifcall.count, (char *)sc->reqq->st); n = r->ifcall.count; if (sc->reqq->rem < n) n = sc->reqq->rem; @@ -772,19 +771,30 @@ } cnum = fnum & ConnMask; sc = c->chans[cnum]; + if ((sc->state == Closed || sc->state == Closing || sc->state == Eof) && sc->dataq == nil) { + if (debug) + fprint(2, "Sending EOF1 to channel listener\n"); + r->aux = 0; + respond(r, nil); + threadexits(nil); + } + if (sc->dataq != nil) { + getdata(c, sc, r); + threadexits(nil); + } while (sc->dataq == nil) { - if (sc->state == Closed || sc->state == Closing || sc->state == Eof) { + if (recv(sc->inchan, nil) < 0) { if (debug) - fprint(2, "Sending EOF2 to channel listener\n"); + fprint(2, "Got interrupt/error in readdata %r\n"); r->aux = 0; - respond(r, nil); + respond(r, "interrupted"); threadexits(nil); } - if (recv(sc->inchan, nil) < 0) { + if ((sc->state == Closed || sc->state == Closing || sc->state == Eof) && sc->dataq == nil) { if (debug) - fprint(2, "Got intterrupt/error in readdata %r\n"); + fprint(2, "Sending EOF2 to channel listener\n"); r->aux = 0; - respond(r, "interrupted"); + respond(r, nil); threadexits(nil); } } @@ -803,11 +813,10 @@ lev = fnum >> LEVSHIFT; xconn = (fnum >> CONNSHIFT) & ConnMask; c = connections[xconn]; - if (c == nil) { + if (lev != 0 && c == nil) { respond(r, "Invalid connection"); return; } - ch = c->chans[fnum & ConnMask]; switch (fnum & FileMask) { case CloneFile: case CtlFile: @@ -819,6 +828,7 @@ respond(r, nil); break; } + ch = c->chans[fnum & ConnMask]; if (c->state == Closed || c->state == Closing || ch->state == Closed || ch->state == Closing) { respond(r, nil); break; @@ -834,6 +844,20 @@ } } +int +openchans(Conn *c) +{ + SSHChan *ch; + int i, n; + + for (n = 0, i = 0; i < MAXCONN; ++i) { + ch = c->chans[i]; + if (ch && ch->state != Empty && ch->state != Closed && ch->state != Closing) + ++n; + } + return n; +} + void writectl(void *a) { @@ -843,18 +867,17 @@ SSHChan *ch; char *q, *buf, *toks[16],*attrs[5]; int n, ntok, lev, fnum, xconn; - char path[40], buf2[10]; + char path[40], buf2[32]; r = a; fnum = (uintptr)r->fid->file->aux; lev = fnum >> LEVSHIFT; xconn = (fnum >> CONNSHIFT) & ConnMask; c = connections[xconn]; - if (c == nil) { + if (lev != 0 && c == nil) { respond(r, "Invalid connection"); threadexits(nil); } - ch = c->chans[fnum & ConnMask]; if (r->ifcall.count <= 10) buf = emalloc9p(11); else @@ -862,8 +885,26 @@ memmove(buf, r->ifcall.data, r->ifcall.count); buf[r->ifcall.count] = '\0'; ntok = tokenize(buf, toks, nelem(toks)); + if (debug) + fprint(2, "Got ctl message on fnum:%x lev:%d xconn:%d cmd:%s\n", fnum, lev, xconn, toks[0]); switch (lev) { case 0: + if (strcmp(toks[0], "debug") == 0) { + if (ntok != 2) { + r->aux = 0; + free(buf); + respond(r, "debug {on|off|9p}"); + threadexits(nil); + } + if (strcmp(toks[1], "off") == 0) { + debug = 0; + chatty9p = 0; + } + else if (strcmp(toks[1], "on") == 0) + debug = 1; + else if(strcmp(toks[1], "9p") == 0) + chatty9p = 1; + } break; case 1: if (strcmp(toks[0], "connect") == 0) { @@ -894,7 +935,7 @@ nbsendul(keymbox.mchan, 1); break; } - if (c->state == Closed || c->state == Closing) { + if (c->state == Closed || (c->state == Closing && strcmp(toks[0], "hangup"))) { r->aux = 0; respond(r, "connection closed"); free(buf); @@ -980,10 +1021,8 @@ threadexits(nil); } } - qlock(&c->l); if (c->state != Established) - rsleep(&c->r); - qunlock(&c->l); + recvul(c->synch); if (c->state != Established) { r->aux = 0; respond(r, "Authentication failed"); @@ -1002,10 +1041,14 @@ } else if (strcmp(toks[0], "announce") == 0) { if (debug) - print("Got %s argument for announce\n", toks[1]); + fprint(2, "Got %s argument for announce\n", toks[1]); write(c->ctlfd, r->ifcall.data, r->ifcall.count); } else if (strcmp(toks[0], "accept") == 0) { + if (debug) { + fd2path(c->ctlfd, buf2, sizeof buf2); + fprint(2, "Got accept on xconn %d: %s\n", xconn, buf2); + } memset(buf2, '\0', sizeof(buf2)); pread(c->ctlfd, buf2, 10, 0); fprint(c->ctlfd, "accept %s", buf2); @@ -1044,6 +1087,7 @@ } break; case 2: + ch = c->chans[fnum & ConnMask]; if (c->state == Closed || c->state == Closing) { r->aux = 0; respond(r, "connection closed"); @@ -1064,10 +1108,8 @@ n = finish_packet(p); iowrite(c->dio, c->datafd, p->nlength, n); free(p); - qlock(&c->l); if (ch->otherid == -1) - rsleep(&ch->r); - qunlock(&c->l); + recvul(ch->synch); break; } else if (strcmp(toks[0], "global") == 0) { @@ -1083,16 +1125,11 @@ iowrite(c->dio, c->datafd, p->nlength, n); free(p); } - qlock(&c->l); - rwakeup(&ch->r); - qunlock(&c->l); + nbsendul(ch->synch, 1); nbsendul(ch->inchan, 1); nbsendul(ch->reqchan, 1); } - for (n = 0; n < MAXCONN - && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closing - || c->chans[n]->state == Closed); ++n) ; - if (n >= MAXCONN) { + if (openchans(c) == 0) { if (c->rpid >= 0) { threadint(c->rpid); } @@ -1101,7 +1138,7 @@ } else if (strcmp(toks[0], "announce") == 0) { if (debug) - print("Got %s argument for announce\n", toks[1]); + fprint(2, "Got %s argument for announce\n", toks[1]); if (ch->ann) free(ch->ann); ch->ann = estrdup9p(toks[1]); @@ -1175,6 +1212,26 @@ ch->state = Closing; add_byte(p, SSH_MSG_CHANNEL_CLOSE); add_uint32(p, ch->otherid); + n = finish_packet(p); + iowrite(c->dio, c->datafd, p->nlength, n); + free(p); + + for (n = time(0); time(0) - n < 5 && ch->state == Closing; ) + yield(); + r->aux = 0; + if (ch->state == Closed) + respond(r, nil); + else { + respond(r, "no response to close"); + ch->state = Closed; + nbsendul(ch->synch, 1); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + chanclose(ch->inchan); + chanclose(ch->reqchan); + } + free(buf); + threadexits(nil); } else if (strcmp(toks[0], "shell") == 0) { ch->state = Established; @@ -1298,10 +1355,8 @@ free(p); threadexits(nil); } - qlock(&ch->xmtlock); while (ch->sent + p->rlength > ch->twindow) - rsleep(&ch->xmtrendez); - qunlock(&ch->xmtlock); + recvul(ch->xmtsync); iowrite(c->dio, c->datafd, p->nlength, n); free(p); r->aux = 0; @@ -1310,17 +1365,38 @@ } /* - * Although this is named stclunk, it's attached to the destroyfid - * member of the Srv struct. It turns out there's no member - * called clunk. But if there are no other references, a 9P Tclunk - * will end up calling destroyfid. + * This is a sort of catch-all. If we find things are all closed but + * haven't gotten the normal hangup/close sequence, we go ahead + * and shut down the channel/connection. The strategy is that + * if we're clunking a channel file and all the others have no references, + * we close the channel. If we're closing a connection file and all + * other connection files have no references and all its channels are + * closed, then we close the connection. + * + * The tricky bit is knowing the baseline reference count so we know + * when there are no more references. Createfile() does two incref()s + * on the file being created: one for the pointer that's returned which + * we store in the structure, and one for the reference in the file tree. + * So every file should always have a ref count of at least 2. Furthermore, + * directories have an additional ref for each entry. For a channel + * directory, this means the baseline count will be 7 (two for the dir + * itself and one for each of five entries). The total over all of a channel + * will be 17. For a connection, it will depend on how many channels + * have ever been created for that connection. However, if we ignore + * the directory itself, then we have a baseline of 14 (two for each of + * seven regular files. + * + * Because destroyfid gets called before the reference gets decremented, + * we know this the last of a set if the sum of references in the set + * is equal to 1 more than the baseline. Hence the comparisons to 15 + * and 18. */ void stclunk(Fid *f) { Packet *p; Conn *c; - SSHChan *sc; + SSHChan *ch; int n, fnum, lev, cnum, chnum; if (f == nil || f->file == nil) @@ -1331,69 +1407,68 @@ chnum = fnum & ConnMask; if (debug) fprint(2, "Got destroy fid on file: %x %d %d %d: %s\n", fnum, lev, cnum, chnum, f->file->name); - if (lev == 0 && fnum == ReqRemFile) { - if (keymbox.state != Empty) { - keymbox.state = Empty; - //nbsendul(keymbox.mchan, 1); + switch (lev) { + case 0: + if (fnum == ReqRemFile) { + if (keymbox.state != Empty) { + keymbox.state = Empty; + //nbsendul(keymbox.mchan, 1); + } + keymbox.msg = nil; } - keymbox.msg = nil; - return; - } - c = connections[cnum]; - if (c == nil) - return; - if (lev == 1 && (fnum & FileMask) == CtlFile - && (c->state == Opening || c->state == Negotiating - || c->state == Authing)) { - for (n = 0; n < MAXCONN - && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closed || c->chans[n]->state == Closing); ++n) ; - if (n >= MAXCONN) { + break; + case 1: + c = connections[cnum]; + if (c == nil) // shouldn't be possible, but just in case + return; + n = c->clonefile->ref + c->ctlfile->ref + c->datafile->ref + + c->listenfile->ref + c->localfile->ref + c->remotefile->ref + c->statusfile->ref; + if (debug) + fprint(2, "ref sum is %d\n", n); + if (n == 15 && c->state != Empty && c->state != Closing && c->state != Closed && openchans(c) == 0) { if (c->rpid >= 0) { threadint(c->rpid); } shutdown(c); } - return; - } - sc = c->chans[chnum]; - if (lev == 2) { - if ((fnum & FileMask) == ListenFile && sc->state == Listening) { - qlock(&c->l); - if (sc->state != Closed) { - sc->state = Closed; - chanclose(sc->inchan); - chanclose(sc->reqchan); - } - qunlock(&c->l); - } - else if ((fnum & FileMask) == DataFile && sc->state != Empty - && sc->state != Closed && sc->state != Closing) { - if (f->file != sc->data && f->file != sc->request) { - fprint(2, "Great evil is upon us destroying a fid we didn't create\n"); - return; - } + break; + case 2: + c = connections[cnum]; + if (c == nil) // shouldn't be possible, but just in case + return; + ch = c->chans[chnum]; + if (ch == nil) + return; + n = ch->dir->ref + ch->ctl->ref + ch->data->ref + ch->listen->ref + + ch->request->ref + ch->status->ref; + if (debug) + fprint(2, "ref sum is %d: %ld %ld %ld %ld %ld %ld\n", n, ch->dir->ref, ch->ctl->ref, + ch->data->ref, ch->listen->ref, ch->request->ref, ch->status->ref); + if (n == 18 && ch->state != Empty && ch->state != Closing && ch->state != Closed) { p = new_packet(c); add_byte(p, SSH_MSG_CHANNEL_CLOSE); - hnputl(p->payload+1, sc->otherid); + hnputl(p->payload+1, ch->otherid); p->rlength += 4; n = finish_packet(p); - sc->state = Closing; + ch->state = Closing; iowrite(c->dio, c->datafd, p->nlength, n); free(p); - qlock(&c->l); - rwakeup(&sc->r); - qunlock(&c->l); - nbsendul(sc->inchan, 1); - nbsendul(sc->reqchan, 1); - } - for (n = 0; n < MAXCONN - && (!c->chans[n] || c->chans[n]->state == Empty || c->chans[n]->state == Closed || c->chans[n]->state == Closing); ++n) ; - if (n >= MAXCONN) { - if (c->rpid >= 0) { - threadint(c->rpid); + nbsendul(ch->synch, 1); + nbsendul(ch->inchan, 1); + nbsendul(ch->reqchan, 1); + yield(); + n = c->clonefile->ref + c->ctlfile->ref + c->datafile->ref + + c->listenfile->ref + c->localfile->ref + c->remotefile->ref + c->statusfile->ref; + if (debug) + fprint(2, "ref sum is %d\n", n); + if (n == 14 && openchans(c) == 0) { + if (c->rpid >= 0) { + threadint(c->rpid); + } + shutdown(c); } - shutdown(c); } + break; } } @@ -1404,7 +1479,9 @@ fnum = (uintptr)r->oldreq->fid->file->aux; if (debug) - fprint(2, "Got flush on file %x %d %d %d: %s %p\n", fnum, fnum >> LEVSHIFT, (fnum >> CONNSHIFT) & ConnMask, fnum & ConnMask, r->oldreq->fid->file->name, r->oldreq->aux); + fprint(2, "Got flush on file %x %d %d %d: %s %p\n", fnum, fnum >> LEVSHIFT, + (fnum >> CONNSHIFT) & ConnMask, fnum & ConnMask, + r->oldreq->fid->file->name, r->oldreq->aux); if (r->oldreq->aux) { if (r->oldreq->ifcall.type == Topen && (fnum & FileMask) == ListenFile && (fnum >> LEVSHIFT) == 1) { threadint((uintptr)r->oldreq->aux); @@ -1468,9 +1545,10 @@ } sconn = i << CONNSHIFT; c = connections[i]; - memset(&c->r, '\0', sizeof(Rendez)); - c->r.l = &c->l; c->dio = ioproc(); + if (c->synch) + chanfree(c->synch); + c->synch = chancreate(sizeof (ulong), 0); c->rio = nil; c->state = Allocated; c->role = Server; @@ -1547,8 +1625,6 @@ } sc = c->chans[cnum]; snprint(buf, 10, "%d", cnum); - memset(&sc->r, '\0', sizeof(Rendez)); - sc->r.l = &c->l; sc->id = cnum; sc->otherid = -1; sc->state = Empty; @@ -1559,7 +1635,6 @@ sc->rwindow = 0; sc->inrqueue = 0; sc->ann = nil; - sc->lreq = nil; slev = 2 << LEVSHIFT; sconn = c->id << CONNSHIFT; if (sc->dir == nil) { @@ -1581,14 +1656,18 @@ free(p); } sc->reqtl = nil; + if (sc->synch) + chanfree(sc->synch); + sc->synch = chancreate(sizeof (ulong), 0); + if (sc->xmtsync) + chanfree(sc->xmtsync); + sc->xmtsync = chancreate(sizeof (ulong), 0); if (sc->inchan) chanfree(sc->inchan); - sc->inchan = chancreate(4, 0); + sc->inchan = chancreate(sizeof (ulong), 0); if (sc->reqchan) chanfree(sc->reqchan); - sc->reqchan = chancreate(4, 0); - memset(&sc->xmtrendez, '\0', sizeof(Rendez)); - sc->xmtrendez.l = &sc->xmtlock; + sc->reqchan = chancreate(sizeof (ulong), 0); qunlock(&c->l); return sc; } @@ -1619,6 +1698,7 @@ fd = ioopen(io, path, ORDWR); if (fd < 0) { closeioproc(io); + shutdown(c); return -1; } c->datafd = fd; @@ -1643,6 +1723,7 @@ iowrite(io, fd, "hangup", 6); ioclose(io, fd); closeioproc(io); + shutdown(c); return -1; } *p = 0; @@ -1655,6 +1736,7 @@ iowrite(io, fd, "hangup", 6); ioclose(io, fd); closeioproc(io); + shutdown(c); return -1; } closeioproc(io); @@ -1678,10 +1760,8 @@ send_kexinit(c); - qlock(&c->l); if ((c->role == Client && c->state != Negotiating) || (c->role == Server && c->state != Established)) - rsleep(&c->r); - qunlock(&c->l); + recvul(c->synch); if (c->role == Server && c->state != Established) return -1; if (c->role == Client && c->state != Negotiating) @@ -1782,6 +1862,8 @@ if (debug) fprint(2, "calling read for connection %d, state %d, nb %d, dc %d\n", c->id, c->state, nb, c->decrypt); + if (c->rio == nil) + goto bail; if ((nl = ioreadn(c->rio, c->datafd, p->nlength, nb)) != nb) { if (debug) fprint(2, "Reader for connection %d exiting, nl=%d: %r\n", c->id, nl); @@ -1797,6 +1879,8 @@ fprint(2, "Absurd packet length: %ld, unrecoverable decrypt failure\n", p->rlength); goto bail; } + if (c->rio == nil) + goto bail; np = ioreadn(c->rio, c->datafd, p->nlength+nb, p->rlength + 4 - nb); if (c->inmac != -1) nm = ioreadn(c->rio, c->datafd, p->nlength + p->rlength + 4, 20); @@ -1898,13 +1982,11 @@ if (debug) fprint(2, "Using %s for encryption and %s for decryption\n", cryptos[c->encrypt]->name, cryptos[c->decrypt]->name); - qlock(&c->l); if (i != -1) c->state = Established; if (c->role == Client) { - rwakeup(&c->r); + nbsendul(c->synch, 1); } - qunlock(&c->l); break; case SSH_MSG_KEXDH_INIT: kexes[c->kexalg]->serverkex(c, p); @@ -1916,9 +1998,7 @@ add_byte(p2, SSH_MSG_NEWKEYS); n = finish_packet(p2); iowrite(c->rio, c->datafd, p2->nlength, n); - qlock(&c->l); - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); } else{ add_byte(p2, SSH_MSG_DISCONNECT); @@ -1933,9 +2013,7 @@ closeioproc(c->rio); c->rio = nil; c->rpid = -1; - qlock(&c->l); - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); threadexits(nil); } break; @@ -1979,9 +2057,7 @@ n = client_auth(c, c->rio); c->state = Authing; if (n < 0) { - qlock(&c->l); - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); } break; default: @@ -2002,10 +2078,8 @@ case SSH_MSG_USERAUTH_REQUEST: switch (auth_req(p, c)) { case 0: - qlock(&c->l); c->state = Established; - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); break; case -1: break; @@ -2015,15 +2089,11 @@ } break; case SSH_MSG_USERAUTH_FAILURE: - qlock(&c->l); - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); break; case SSH_MSG_USERAUTH_SUCCESS: - qlock(&c->l); c->state = Established; - rwakeup(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); break; case SSH_MSG_USERAUTH_BANNER: break; @@ -2094,7 +2164,6 @@ } if (debug) fprint(2, "alloced channel %d for listener\n", ch->id); - qlock(&c->l); ch->otherid = nhgetl(q); ch->twindow = nhgetl(q+4); if (debug) @@ -2112,31 +2181,26 @@ ch->ann = estrdup9p(buf); if (debug) fprint(2, "Waiting for someone to announce %s\n", ch->ann); - rsleep(&ch->r); + recvul(ch->synch); } else{ if (debug) fprint(2, "Found listener on channel %d\n", ch->id); c->chans[i]->waker = ch->id; - rwakeup(&c->chans[i]->r); + nbsendul(c->chans[i]->synch, 1); } - qunlock(&c->l); break; case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: cnum = nhgetl(p->payload + 1); ch = c->chans[cnum]; - qlock(&c->l); ch->otherid = nhgetl(p->payload+5); ch->twindow = nhgetl(p->payload+9); - rwakeup(&ch->r); - qunlock(&c->l); + nbsendul(ch->synch, 1); break; case SSH_MSG_CHANNEL_OPEN_FAILURE: cnum = nhgetl(p->payload + 1); ch = c->chans[cnum]; - qlock(&c->l); - rwakeup(&ch->r); - qunlock(&c->l); + nbsendul(ch->synch, 1); goto bail; break; case SSH_MSG_CHANNEL_WINDOW_ADJUST: @@ -2145,9 +2209,7 @@ ch->twindow += nhgetl(p->payload + 5); if (debug) fprint(2, "New twindow for channel: %d: %lud\n", cnum, ch->twindow); - qlock(&ch->xmtlock); - rwakeup(&ch->xmtrendez); - qunlock(&ch->xmtlock); + nbsendul(ch->xmtsync, 1); break; case SSH_MSG_CHANNEL_DATA: case SSH_MSG_CHANNEL_EXTENDED_DATA: @@ -2184,6 +2246,8 @@ break; case SSH_MSG_CHANNEL_CLOSE: cnum = nhgetl(p->payload + 1); + if (debug) + fprint(2, "Got channel close on cnum %d\n", cnum); ch = c->chans[cnum]; if (ch->state != Closed && ch->state != Closing) { init_packet(p2); @@ -2194,16 +2258,14 @@ n = finish_packet(p2); iowrite(c->rio, c->datafd, p2->nlength, n); } - qlock(&c->l); if (ch->state != Closed) { ch->state = Closed; - rwakeup(&ch->r); + nbsendul(ch->synch, 1); nbsendul(ch->inchan, 1); nbsendul(ch->reqchan, 1); chanclose(ch->inchan); chanclose(ch->reqchan); } - qunlock(&c->l); for (i = 0; i < MAXCONN && (!c->chans[i] || c->chans[i]->state == Empty || c->chans[i]->state == Closed); ++i) ; if (i >= MAXCONN) { goto bail; @@ -2795,8 +2857,7 @@ fprint(2, "Shutting down connection %d\n", c->id); ostate = c->state; if (c->clonefile->ref <= 2 && c->ctlfile->ref <= 2 && c->datafile->ref <= 2 - && c->listenfile->ref <= 2 && c->localfile->ref <= 2 - && c->remotefile->ref <= 2 && c->statusfile->ref <= 2) + && c->listenfile->ref <= 2) c->state = Closed; else { if (c->state != Closed) @@ -2823,6 +2884,11 @@ closeioproc(c->dio); c->dio = nil; } + yield(); /* give everyone a chance to respond to the closes */ + if (c->rpid != -1) { + threadint(c->rpid); + c->rpid = -1; + } c->decrypt = -1; c->inmac = -1; c->ctlfd = -1; @@ -2857,10 +2923,12 @@ free(c->service); c->service = nil; } + if (c->cap) { + free(c->cap); + c->cap = nil; + } c->otherid = nil; - qlock(&c->l); - rwakeupall(&c->r); - qunlock(&c->l); + nbsendul(c->synch, 1); for (i = 0; i < MAXCONN; ++i) { sc = c->chans[i]; if (sc == nil) @@ -2871,7 +2939,6 @@ } if (sc->state != Empty && sc->state != Closed) { sc->state = Closed; - sc->lreq = nil; while (sc->dataq != nil) { p = sc->dataq; sc->dataq = p->next; @@ -2884,18 +2951,14 @@ free(p->pack); free(p); } - qlock(&c->l); - rwakeupall(&sc->r); + nbsendul(sc->synch, 1); nbsendul(sc->inchan, 1); nbsendul(sc->reqchan, 1); chanclose(sc->inchan); chanclose(sc->reqchan); - qunlock(&c->l); } } - qlock(&availlck); - rwakeup(&availrend); - qunlock(&availlck); + c->state = Closed; if (debug) fprint(2, "Done processing shutdown of connection %d\n", c->id); } --- /sys/src/cmd/ssh2/sshtun.h Wed Jun 19 04:20:22 2013 +++ /sys/src/cmd/ssh2/sshtun.h Wed Jun 19 04:20:22 2013 @@ -140,24 +140,22 @@ }; struct SSHChan { - Rendez r; + Channel *synch; int id, otherid, state; int waker; int conn; ulong rwindow, twindow; ulong sent, inrqueue; char *ann; - Req *lreq; File *dir, *ctl, *data, *listen, *request, *status; Plist *dataq, *datatl, *reqq, *reqtl; Channel *inchan, *reqchan; - QLock xmtlock; - Rendez xmtrendez; + Channel *xmtsync; }; struct Conn { QLock l; - Rendez r; + Channel *synch; Ioproc *dio, *cio, *rio; int state; int role;