| #include <u.h> |
| #include <libc.h> |
| #include <venti.h> |
| |
| static char *okvers[] = { |
| "04", |
| "02", |
| nil, |
| }; |
| |
| /* |
| static char EBigString[] = "string too long"; |
| static char EBigPacket[] = "packet too long"; |
| static char ENullString[] = "missing string"; |
| */ |
| static char EBadVersion[] = "bad format in version string"; |
| |
| static int |
| vtreadversion(VtConn *z, char *q, char *v, int nv) |
| { |
| int n; |
| |
| for(;;){ |
| if(nv <= 1){ |
| werrstr("version too long"); |
| return -1; |
| } |
| n = read(z->infd, v, 1); |
| if(n <= 0){ |
| if(n == 0) |
| werrstr("unexpected eof"); |
| return -1; |
| } |
| if(*v == '\n'){ |
| *v = 0; |
| break; |
| } |
| if((uchar)*v < ' ' || (uchar)*v > 0x7f || (*q && *v != *q)){ |
| werrstr(EBadVersion); |
| return -1; |
| } |
| v++; |
| nv--; |
| if(*q) |
| q++; |
| } |
| return 0; |
| } |
| |
| int |
| vtversion(VtConn *z) |
| { |
| char buf[VtMaxStringSize], *p, *ep, *prefix, *pp; |
| int i; |
| |
| qlock(&z->lk); |
| if(z->state != VtStateAlloc){ |
| werrstr("bad session state"); |
| qunlock(&z->lk); |
| return -1; |
| } |
| |
| qlock(&z->inlk); |
| qlock(&z->outlk); |
| |
| p = buf; |
| ep = buf + sizeof buf; |
| prefix = "venti-"; |
| p = seprint(p, ep, "%s", prefix); |
| p += strlen(p); |
| for(i=0; okvers[i]; i++) |
| p = seprint(p, ep, "%s%s", i ? ":" : "", okvers[i]); |
| p = seprint(p, ep, "-libventi\n"); |
| assert(p-buf < sizeof buf); |
| |
| if(write(z->outfd, buf, p-buf) != p-buf) |
| goto Err; |
| vtdebug(z, "version string out: %s", buf); |
| |
| if(vtreadversion(z, prefix, buf, sizeof buf) < 0) |
| goto Err; |
| vtdebug(z, "version string in: %s", buf); |
| |
| p = buf+strlen(prefix); |
| for(; *p; p=pp){ |
| if(*p == ':' || *p == '-') |
| p++; |
| pp = strpbrk(p, ":-"); |
| if(pp == nil) |
| pp = p+strlen(p); |
| for(i=0; okvers[i]; i++) |
| if(strlen(okvers[i]) == pp-p && memcmp(okvers[i], p, pp-p) == 0){ |
| *pp = 0; |
| z->version = vtstrdup(p); |
| goto Okay; |
| } |
| } |
| werrstr("unable to negotiate version"); |
| goto Err; |
| |
| Okay: |
| z->state = VtStateConnected; |
| qunlock(&z->inlk); |
| qunlock(&z->outlk); |
| qunlock(&z->lk); |
| return 0; |
| |
| Err: |
| werrstr("vtversion: %r"); |
| if(z->infd >= 0) |
| close(z->infd); |
| if(z->outfd >= 0 && z->outfd != z->infd) |
| close(z->outfd); |
| z->infd = -1; |
| z->outfd = -1; |
| z->state = VtStateClosed; |
| qunlock(&z->inlk); |
| qunlock(&z->outlk); |
| qunlock(&z->lk); |
| return -1; |
| } |