Subversion Repositories planix.SVN

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

#include        "u.h"
#include        "lib.h"
#include        "dat.h"
#include        "fns.h"
#include        "error.h"

#include        "user.h"
#undef open
#undef mount
#undef read
#undef write
#undef seek
#undef stat
#undef wstat
#undef remove
#undef close
#undef fstat
#undef fwstat

/*
 * The sys*() routines needn't poperror() as they return directly to syscall().
 */

static void
unlockfgrp(Fgrp *f)
{
        int ex;

        ex = f->exceed;
        f->exceed = 0;
        unlock(&f->ref.lk);
        if(ex)
                pprint("warning: process exceeds %d file descriptors\n", ex);
}

int
growfd(Fgrp *f, int fd) /* fd is always >= 0 */
{
        Chan **newfd, **oldfd;

        if(fd < f->nfd)
                return 0;
        if(fd >= f->nfd+DELTAFD)
                return -1;      /* out of range */
        /*
         * Unbounded allocation is unwise; besides, there are only 16 bits
         * of fid in 9P
         */
        if(f->nfd >= 5000){
    Exhausted:
                print("no free file descriptors\n");
                return -1;
        }
        newfd = malloc((f->nfd+DELTAFD)*sizeof(Chan*));
        if(newfd == 0)
                goto Exhausted;
        oldfd = f->fd;
        memmove(newfd, oldfd, f->nfd*sizeof(Chan*));
        f->fd = newfd;
        free(oldfd);
        f->nfd += DELTAFD;
        if(fd > f->maxfd){
                if(fd/100 > f->maxfd/100)
                        f->exceed = (fd/100)*100;
                f->maxfd = fd;
        }
        return 1;
}

/*
 *  this assumes that the fgrp is locked
 */
int
findfreefd(Fgrp *f, int start)
{
        int fd;

        for(fd=start; fd<f->nfd; fd++)
                if(f->fd[fd] == 0)
                        break;
        if(fd >= f->nfd && growfd(f, fd) < 0)
                return -1;
        return fd;
}

int
newfd(Chan *c)
{
        int fd;
        Fgrp *f;

        f = up->fgrp;
        lock(&f->ref.lk);
        fd = findfreefd(f, 0);
        if(fd < 0){
                unlockfgrp(f);
                return -1;
        }
        if(fd > f->maxfd)
                f->maxfd = fd;
        f->fd[fd] = c;
        unlockfgrp(f);
        return fd;
}

int
newfd2(int fd[2], Chan *c[2])
{
        Fgrp *f;

        f = up->fgrp;
        lock(&f->ref.lk);
        fd[0] = findfreefd(f, 0);
        if(fd[0] < 0){
                unlockfgrp(f);
                return -1;
        }
        fd[1] = findfreefd(f, fd[0]+1);
        if(fd[1] < 0){
                unlockfgrp(f);
                return -1;
        }
        if(fd[1] > f->maxfd)
                f->maxfd = fd[1];
        f->fd[fd[0]] = c[0];
        f->fd[fd[1]] = c[1];
        unlockfgrp(f);

        return 0;
}

Chan*
fdtochan(int fd, int mode, int chkmnt, int iref)
{
        Chan *c;
        Fgrp *f;

        c = 0;
        f = up->fgrp;

        lock(&f->ref.lk);
        if(fd<0 || f->nfd<=fd || (c = f->fd[fd])==0) {
                unlock(&f->ref.lk);
                error(Ebadfd);
        }
        if(iref)
                incref(&c->ref);
        unlock(&f->ref.lk);

        if(chkmnt && (c->flag&CMSG)) {
                if(iref)
                        cclose(c);
                error(Ebadusefd);
        }

        if(mode<0 || c->mode==ORDWR)
                return c;

        if((mode&OTRUNC) && c->mode==OREAD) {
                if(iref)
                        cclose(c);
                error(Ebadusefd);
        }

        if((mode&~OTRUNC) != c->mode) {
                if(iref)
                        cclose(c);
                error(Ebadusefd);
        }

        return c;
}

int
openmode(ulong o)
{
        o &= ~(OTRUNC|OCEXEC|ORCLOSE);
        if(o > OEXEC)
                error(Ebadarg);
        if(o == OEXEC)
                return OREAD;
        return o;
}

long
_sysfd2path(int fd, char *buf, uint nbuf)
{
        Chan *c;

        c = fdtochan(fd, -1, 0, 1);

        if(c->name == nil)
                snprint(buf, nbuf, "<null>");
        else
                snprint(buf, nbuf, "%s", c->name->s);
        cclose(c);
        return 0;
}

long
_syspipe(int fd[2])
{
        Chan *c[2];
        Dev *d;
        static char *datastr[] = {"data", "data1"};

        d = devtab[devno('|', 0)];
        c[0] = namec("#|", Atodir, 0, 0);
        c[1] = 0;
        fd[0] = -1;
        fd[1] = -1;

        if(waserror()){
                cclose(c[0]);
                if(c[1])
                        cclose(c[1]);
                nexterror();
        }
        c[1] = cclone(c[0]);
        if(walk(&c[0], datastr+0, 1, 1, nil) < 0)
                error(Egreg);
        if(walk(&c[1], datastr+1, 1, 1, nil) < 0)
                error(Egreg);
        c[0] = d->open(c[0], ORDWR);
        c[1] = d->open(c[1], ORDWR);
        if(newfd2(fd, c) < 0)
                error(Enofd);
        poperror();

        return 0;
}

long
_sysdup(int fd0, int fd1)
{
        int fd;
        Chan *c, *oc;
        Fgrp *f = up->fgrp;

        /*
         * Close after dup'ing, so date > #d/1 works
         */
        c = fdtochan(fd0, -1, 0, 1);
        fd = fd1;
        if(fd != -1){
                lock(&f->ref.lk);
                if(fd<0 || growfd(f, fd)<0) {
                        unlockfgrp(f);
                        cclose(c);
                        error(Ebadfd);
                }
                if(fd > f->maxfd)
                        f->maxfd = fd;

                oc = f->fd[fd];
                f->fd[fd] = c;
                unlockfgrp(f);
                if(oc)
                        cclose(oc);
        }else{
                if(waserror()) {
                        cclose(c);
                        nexterror();
                }
                fd = newfd(c);
                if(fd < 0)
                        error(Enofd);
                poperror();
        }

        return fd;
}

long
_sysopen(char *name, int mode)
{
        int fd;
        Chan *c = 0;

        openmode(mode); /* error check only */
        if(waserror()){
                if(c)
                        cclose(c);
                nexterror();
        }
        c = namec(name, Aopen, mode, 0);
        fd = newfd(c);
        if(fd < 0)
                error(Enofd);
        poperror();
        return fd;
}

void
fdclose(int fd, int flag)
{
        int i;
        Chan *c;
        Fgrp *f = up->fgrp;

        lock(&f->ref.lk);
        c = f->fd[fd];
        if(c == 0){
                /* can happen for users with shared fd tables */
                unlock(&f->ref.lk);
                return;
        }
        if(flag){
                if(c==0 || !(c->flag&flag)){
                        unlock(&f->ref.lk);
                        return;
                }
        }
        f->fd[fd] = 0;
        if(fd == f->maxfd)
                for(i=fd; --i>=0 && f->fd[i]==0; )
                        f->maxfd = i;

        unlock(&f->ref.lk);
        cclose(c);
}

long
_sysclose(int fd)
{
        fdtochan(fd, -1, 0, 0);
        fdclose(fd, 0);

        return 0;
}

long
unionread(Chan *c, void *va, long n)
{
        int i;
        long nr;
        Mhead *m;
        Mount *mount;

        qlock(&c->umqlock);
        m = c->umh;
        rlock(&m->lock);
        mount = m->mount;
        /* bring mount in sync with c->uri and c->umc */
        for(i = 0; mount != nil && i < c->uri; i++)
                mount = mount->next;

        nr = 0;
        while(mount != nil) {
                /* Error causes component of union to be skipped */
                if(mount->to && !waserror()) {
                        if(c->umc == nil){
                                c->umc = cclone(mount->to);
                                c->umc = devtab[c->umc->type]->open(c->umc, OREAD);
                        }
        
                        nr = devtab[c->umc->type]->read(c->umc, va, n, c->umc->offset);
                        c->umc->offset += nr;
                        poperror();
                }
                if(nr > 0)
                        break;

                /* Advance to next element */
                c->uri++;
                if(c->umc) {
                        cclose(c->umc);
                        c->umc = nil;
                }
                mount = mount->next;
        }
        runlock(&m->lock);
        qunlock(&c->umqlock);
        return nr;
}

static long
kread(int fd, void *buf, long n, vlong *offp)
{
        int dir;
        Chan *c;
        vlong off;

        c = fdtochan(fd, OREAD, 1, 1);

        if(waserror()) {
                cclose(c);
                nexterror();
        }

        dir = c->qid.type&QTDIR;
        /*
         * The offset is passed through on directories, normally. sysseek complains but
         * pread is used by servers and e.g. exportfs that shouldn't need to worry about this issue.
         */

        if(offp == nil) /* use and maintain channel's offset */
                off = c->offset;
        else
                off = *offp;

        if(off < 0)
                error(Enegoff);

        if(dir && c->umh)
                n = unionread(c, buf, n);
        else
                n = devtab[c->type]->read(c, buf, n, off);

        if(offp == nil){
                lock(&c->ref.lk);
                c->offset += n;
                unlock(&c->ref.lk);
        }

        poperror();
        cclose(c);

        return n;
}

/* name conflicts with netbsd
long
_sys_read(int fd, void *buf, long n)
{
        return kread(fd, buf, n, nil);
}
*/

long
_syspread(int fd, void *buf, long n, vlong off)
{
        if(off == ((uvlong) ~0))
                return kread(fd, buf, n, nil);
        return kread(fd, buf, n, &off);
}

static long
kwrite(int fd, void *buf, long nn, vlong *offp)
{
        Chan *c;
        long m, n;
        vlong off;

        n = 0;
        c = fdtochan(fd, OWRITE, 1, 1);
        if(waserror()) {
                if(offp == nil){
                        lock(&c->ref.lk);
                        c->offset -= n;
                        unlock(&c->ref.lk);
                }
                cclose(c);
                nexterror();
        }

        if(c->qid.type & QTDIR)
                error(Eisdir);

        n = nn;

        if(offp == nil){        /* use and maintain channel's offset */
                lock(&c->ref.lk);
                off = c->offset;
                c->offset += n;
                unlock(&c->ref.lk);
        }else
                off = *offp;

        if(off < 0)
                error(Enegoff);

        m = devtab[c->type]->write(c, buf, n, off);

        if(offp == nil && m < n){
                lock(&c->ref.lk);
                c->offset -= n - m;
                unlock(&c->ref.lk);
        }

        poperror();
        cclose(c);

        return m;
}

long
sys_write(int fd, void *buf, long n)
{
        return kwrite(fd, buf, n, nil);
}

long
_syspwrite(int fd, void *buf, long n, vlong off)
{
        if(off == ((uvlong) ~0))
                return kwrite(fd, buf, n, nil);
        return kwrite(fd, buf, n, &off);
}

static vlong
_sysseek(int fd, vlong off, int whence)
{
        Chan *c;
        uchar buf[sizeof(Dir)+100];
        Dir dir;
        int n;

        c = fdtochan(fd, -1, 1, 1);
        if(waserror()){
                cclose(c);
                nexterror();
        }
        if(devtab[c->type]->dc == '|')
                error(Eisstream);

        switch(whence){
        case 0:
                if((c->qid.type & QTDIR) && off != 0)
                        error(Eisdir);
                if(off < 0)
                        error(Enegoff);
                c->offset = off;
                break;

        case 1:
                if(c->qid.type & QTDIR)
                        error(Eisdir);
                lock(&c->ref.lk);       /* lock for read/write update */
                off = off + c->offset;
                if(off < 0)
                        error(Enegoff);
                c->offset = off;
                unlock(&c->ref.lk);
                break;

        case 2:
                if(c->qid.type & QTDIR)
                        error(Eisdir);
                n = devtab[c->type]->stat(c, buf, sizeof buf);
                if(convM2D(buf, n, &dir, nil) == 0)
                        error("internal error: stat error in seek");
                off = dir.length + off;
                if(off < 0)
                        error(Enegoff);
                c->offset = off;
                break;

        default:
                error(Ebadarg);
        }
        c->uri = 0;
        c->dri = 0;
        cclose(c);
        poperror();
        return off;
}

void
validstat(uchar *s, int n)
{
        int m;
        char buf[64];

        if(statcheck(s, n) < 0)
                error(Ebadstat);
        /* verify that name entry is acceptable */
        s += STATFIXLEN - 4*BIT16SZ;    /* location of first string */
        /*
         * s now points at count for first string.
         * if it's too long, let the server decide; this is
         * only for his protection anyway. otherwise
         * we'd have to allocate and waserror.
         */
        m = GBIT16(s);
        s += BIT16SZ;
        if(m+1 > sizeof buf)
                return;
        memmove(buf, s, m);
        buf[m] = '\0';
        /* name could be '/' */
        if(strcmp(buf, "/") != 0)
                validname(buf, 0);
}

long
_sysfstat(int fd, void *buf, long n)
{
        Chan *c;
        uint l;

        l = n;
        validaddr(buf, l, 1);
        c = fdtochan(fd, -1, 0, 1);
        if(waserror()) {
                cclose(c);
                nexterror();
        }
        l = devtab[c->type]->stat(c, buf, l);
        poperror();
        cclose(c);
        return l;
}

long
_sysstat(char *name, void *buf, long n)
{
        Chan *c;
        uint l;

        l = n;
        validaddr(buf, l, 1);
        validaddr(name, 1, 0);
        c = namec(name, Aaccess, 0, 0);
        if(waserror()){
                cclose(c);
                nexterror();
        }
        l = devtab[c->type]->stat(c, buf, l);
        poperror();
        cclose(c);
        return l;
}

long
_syschdir(char *name)
{
        Chan *c;

        validaddr(name, 1, 0);

        c = namec(name, Atodir, 0, 0);
        cclose(up->dot);
        up->dot = c;
        return 0;
}

long
bindmount(int ismount, int fd, int afd, char* arg0, char* arg1, ulong flag, char* spec)
{
        int ret;
        Chan *c0, *c1, *ac, *bc;
        struct{
                Chan    *chan;
                Chan    *authchan;
                char    *spec;
                int     flags;
        }bogus;

        if((flag&~MMASK) || (flag&MORDER)==(MBEFORE|MAFTER))
                error(Ebadarg);

        bogus.flags = flag & MCACHE;

        if(ismount){
                if(up->pgrp->noattach)
                        error(Enoattach);

                ac = nil;
                bc = fdtochan(fd, ORDWR, 0, 1);
                if(waserror()) {
                        if(ac)
                                cclose(ac);
                        cclose(bc);
                        nexterror();
                }

                if(afd >= 0)
                        ac = fdtochan(afd, ORDWR, 0, 1);

                bogus.chan = bc;
                bogus.authchan = ac;

                validaddr((ulong)spec, 1, 0);
                bogus.spec = spec;
                if(waserror())
                        error(Ebadspec);
                validname(spec, 1);
                poperror();

                ret = devno('M', 0);
                c0 = devtab[ret]->attach((char*)&bogus);

                poperror();
                if(ac)
                        cclose(ac);
                cclose(bc);
        }else{
                bogus.spec = 0;
                validaddr((ulong)arg0, 1, 0);
                c0 = namec(arg0, Abind, 0, 0);
        }

        if(waserror()){
                cclose(c0);
                nexterror();
        }

        validaddr((ulong)arg1, 1, 0);
        c1 = namec(arg1, Amount, 0, 0);
        if(waserror()){
                cclose(c1);
                nexterror();
        }

        ret = cmount(&c0, c1, flag, bogus.spec);

        poperror();
        cclose(c1);
        poperror();
        cclose(c0);
        if(ismount)
                fdclose(fd, 0);

        return ret;
}

long
_sysbind(char *old, char *new, int flag)
{
        return bindmount(0, -1, -1, old, new, flag, nil);
}

long
_sysmount(int fd, int afd, char *new, int flag, char *spec)
{
        return bindmount(1, fd, afd, nil, new, flag, spec);
}

long
_sysunmount(char *old, char *new)
{
        Chan *cmount, *cmounted;

        cmounted = 0;

        cmount = namec(new, Amount, 0, 0);

        if(old) {
                if(waserror()) {
                        cclose(cmount);
                        nexterror();
                }
                validaddr(old, 1, 0);
                /*
                 * This has to be namec(..., Aopen, ...) because
                 * if arg[0] is something like /srv/cs or /fd/0,
                 * opening it is the only way to get at the real
                 * Chan underneath.
                 */
                cmounted = namec(old, Aopen, OREAD, 0);
                poperror();
        }

        if(waserror()) {
                cclose(cmount);
                if(cmounted)
                        cclose(cmounted);
                nexterror();
        }

        cunmount(cmount, cmounted);
        cclose(cmount);
        if(cmounted)
                cclose(cmounted);
        poperror();
        return 0;
}

long
_syscreate(char *name, int mode, ulong perm)
{
        int fd;
        Chan *c = 0;

        openmode(mode&~OEXCL);  /* error check only; OEXCL okay here */
        if(waserror()) {
                if(c)
                        cclose(c);
                nexterror();
        }
        validaddr(name, 1, 0);
        c = namec(name, Acreate, mode, perm);
        fd = newfd(c);
        if(fd < 0)
                error(Enofd);
        poperror();
        return fd;
}

long
_sysremove(char *name)
{
        Chan *c;

        c = namec(name, Aremove, 0, 0);
        if(waserror()){
                c->type = 0;    /* see below */
                cclose(c);
                nexterror();
        }
        devtab[c->type]->remove(c);
        /*
         * Remove clunks the fid, but we need to recover the Chan
         * so fake it up.  rootclose() is known to be a nop.
         */
        c->type = 0;
        poperror();
        cclose(c);
        return 0;
}

long
_syswstat(char *name, void *buf, long n)
{
        Chan *c;
        uint l;

        l = n;
        validstat(buf, l);
        validaddr(name, 1, 0);
        c = namec(name, Aaccess, 0, 0);
        if(waserror()){
                cclose(c);
                nexterror();
        }
        l = devtab[c->type]->wstat(c, buf, l);
        poperror();
        cclose(c);
        return l;
}

long
_sysfwstat(int fd, void *buf, long n)
{
        Chan *c;
        uint l;

        l = n;
        validaddr(buf, l, 0);
        validstat(buf, l);
        c = fdtochan(fd, -1, 1, 1);
        if(waserror()) {
                cclose(c);
                nexterror();
        }
        l = devtab[c->type]->wstat(c, buf, l);
        poperror();
        cclose(c);
        return l;
}


static void
starterror(void)
{
        assert(up->nerrlab == 0);
}

static void
_syserror(void)
{
        char *p;

        p = up->syserrstr;
        up->syserrstr = up->errstr;
        up->errstr = p;
}

static void
enderror(void)
{
        assert(up->nerrlab == 1);
        poperror();
}

int
sysbind(char *old, char *new, int flag)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysbind(old, new, flag);
        enderror();
        return n;
}

int
syschdir(char *path)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syschdir(path);
        enderror();
        return n;
}

int
sysclose(int fd)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysclose(fd);
        enderror();
        return n;
}

int
syscreate(char *name, int mode, ulong perm)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syscreate(name, mode, perm);
        enderror();
        return n;
}

int
sysdup(int fd0, int fd1)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysdup(fd0, fd1);
        enderror();
        return n;
}

int
sysfstat(int fd, uchar *buf, int n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysfstat(fd, buf, n);
        enderror();
        return n;
}

int
sysfwstat(int fd, uchar *buf, int n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysfwstat(fd, buf, n);
        enderror();
        return n;
}

int
sysmount(int fd, int afd, char *new, int flag, char *spec)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysmount(fd, afd, new, flag, spec);
        enderror();
        return n;
}

int
sysunmount(char *old, char *new)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysunmount(old, new);
        enderror();
        return n;
}

int
sysopen(char *name, int mode)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysopen(name, mode);
        enderror();
        return n;
}

int
syspipe(int *fd)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syspipe(fd);
        enderror();
        return n;
}

long
syspread(int fd, void *buf, long n, vlong off)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syspread(fd, buf, n, off);
        enderror();
        return n;
}

long
syspwrite(int fd, void *buf, long n, vlong off)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syspwrite(fd, buf, n, off);
        enderror();
        return n;
}

long
sysread(int fd, void *buf, long n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syspread(fd, buf, n, (uvlong) ~0);
        enderror();
        return n;
}

int
sysremove(char *path)
{
        int n;

        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysremove(path);
        enderror();
        return n;
}

vlong
sysseek(int fd, vlong off, int whence)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        off = _sysseek(fd, off, whence);
        enderror();
        return off;
}

int
sysstat(char *name, uchar *buf, int n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _sysstat(name, buf, n);
        enderror();
        return n;
}

long
syswrite(int fd, void *buf, long n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syspwrite(fd, buf, n, (uvlong) ~0);
        enderror();
        return n;
}

int
syswstat(char *name, uchar *buf, int n)
{
        starterror();
        if(waserror()){
                _syserror();
                return -1;
        }
        n = _syswstat(name, buf, n);
        enderror();
        return n;
}

void
werrstr(char *f, ...)
{
        char buf[ERRMAX];
        va_list arg;

        va_start(arg, f);
        vsnprint(buf, sizeof buf, f, arg);
        va_end(arg);

        if(up->nerrlab)
                strecpy(up->errstr, up->errstr+ERRMAX, buf);
        else
                strecpy(up->syserrstr, up->syserrstr+ERRMAX, buf);
}

int
__errfmt(Fmt *fmt)
{
        if(up->nerrlab)
                return fmtstrcpy(fmt, up->errstr);
        else
                return fmtstrcpy(fmt, up->syserrstr);
}

int
errstr(char *buf, uint n)
{
        char tmp[ERRMAX];
        char *p;

        p = up->nerrlab ? up->errstr : up->syserrstr;
        memmove(tmp, p, ERRMAX);
        utfecpy(p, p+ERRMAX, buf);
        utfecpy(buf, buf+n, tmp);
        return strlen(buf);
}

int
rerrstr(char *buf, uint n)
{
        char *p;

        p = up->nerrlab ? up->errstr : up->syserrstr;
        utfecpy(buf, buf+n, p);
        return strlen(buf);
}

void*
_sysrendezvous(void* arg0, void* arg1)
{
        void *tag, *val;
        Proc *p, **l;

        tag = arg0;
        l = &REND(up->rgrp, (uintptr)tag);
        up->rendval = (void*)~0;

        lock(&up->rgrp->ref.lk);
        for(p = *l; p; p = p->rendhash) {
                if(p->rendtag == tag) {
                        *l = p->rendhash;
                        val = p->rendval;
                        p->rendval = arg1;

                        while(p->mach != 0)
                                ;
                        procwakeup(p);
                        unlock(&up->rgrp->ref.lk);
                        return val;
                }
                l = &p->rendhash;
        }

        /* Going to sleep here */
        up->rendtag = tag;
        up->rendval = arg1;
        up->rendhash = *l;
        *l = up;
        up->state = Rendezvous;
        unlock(&up->rgrp->ref.lk);

        procsleep();

        return up->rendval;
}

void*
sysrendezvous(void *tag, void *val)
{
        void *n;

        starterror();
        if(waserror()){
                _syserror();
                return (void*)~0;
        }
        n = _sysrendezvous(tag, val);
        enderror();
        return n;
}