Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

#include <u.h>
#include <libc.h>
#include <venti.h>

static char *okvers[] = {
        "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;
}