blob: 077715d6cc5ee004ea1dd70642ae10a4b5a6e53f [file] [log] [blame]
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <errno.h>
#define err err9pserve /* Darwin x86 */
enum
{
STACK = 32768,
NHASH = 31,
MAXMSG = 64, /* per connection */
MAXMSGSIZE = 4*1024*1024
};
typedef struct Hash Hash;
typedef struct Fid Fid;
typedef struct Msg Msg;
typedef struct Conn Conn;
typedef struct Queue Queue;
struct Hash
{
Hash *next;
uint n;
void *v;
};
struct Fid
{
int fid;
int ref;
int cfid;
int openfd;
int offset;
int coffset;
int isdir;
Fid *next;
};
struct Msg
{
Conn *c;
int internal;
int sync;
int ref;
int ctag;
int tag;
int isopenfd;
Fcall tx;
Fcall rx;
Fid *fid;
Fid *newfid;
Fid *afid;
Msg *oldm;
Msg *next;
uchar *tpkt;
uchar *rpkt;
};
struct Conn
{
int fd;
int fdmode;
Fid *fdfid;
int nmsg;
int nfid;
Channel *inc;
Channel *internal;
int inputstalled;
char dir[40];
Hash *tag[NHASH];
Hash *fid[NHASH];
Queue *outq;
Queue *inq;
Channel *outqdead;
};
char *xaname;
char *addr;
int afd;
char adir[40];
int isunix;
Queue *outq;
Queue *inq;
int verbose = 0;
int logging = 0;
int msize = 8192;
u32int xafid = NOFID;
int attached;
int versioned;
int noauth;
void *gethash(Hash**, uint);
int puthash(Hash**, uint, void*);
int delhash(Hash**, uint, void*);
Msg *mread9p(Ioproc*, int);
int mwrite9p(Ioproc*, int, uchar*);
uchar *read9ppkt(Ioproc*, int);
int write9ppkt(int, uchar*);
Msg *msgnew(int);
void msgput(Msg*);
void msgclear(Msg*);
Msg *msgget(int);
void msgincref(Msg*);
Fid *fidnew(int);
void fidput(Fid*);
void *emalloc(int);
void *erealloc(void*, int);
Queue *qalloc(void);
int sendq(Queue*, void*);
void *recvq(Queue*);
void connthread(void*);
void connoutthread(void*);
void listenthread(void*);
void outputthread(void*);
void inputthread(void*);
void rewritehdr(Fcall*, uchar*);
void repack(Fcall*, uchar**);
int tlisten(char*, char*);
int taccept(int, char*);
int iolisten(Ioproc*, char*, char*);
int ioaccept(Ioproc*, int, char*);
int iorecvfd(Ioproc*, int);
int iosendfd(Ioproc*, int, int);
void mainproc(void*);
int ignorepipe(void*, char*);
int timefmt(Fmt*);
void dorootstat(void);
void
usage(void)
{
fprint(2, "usage: 9pserve [-lnv] [-A aname afid] [-c addr] [-M msize] address\n");
fprint(2, "\treads/writes 9P messages on stdin/stdout\n");
threadexitsall("usage");
}
uchar vbuf[128];
extern int _threaddebuglevel;
void
threadmain(int argc, char **argv)
{
char *file, *x, *addr;
int fd;
rfork(RFNOTEG);
x = getenv("verbose9pserve");
if(x){
verbose = atoi(x);
fprint(2, "verbose9pserve %s => %d\n", x, verbose);
}
ARGBEGIN{
default:
usage();
case 'A':
attached = 1;
xaname = EARGF(usage());
xafid = atoi(EARGF(usage()));
break;
case 'M':
versioned = 1;
msize = atoi(EARGF(usage()));
break;
case 'c':
addr = netmkaddr(EARGF(usage()), "net", "9fs");
if((fd = dial(addr, nil, nil, nil)) < 0)
sysfatal("dial %s: %r", addr);
dup(fd, 0);
dup(fd, 1);
if(fd > 1)
close(fd);
break;
case 'n':
noauth = 1;
break;
case 'v':
verbose++;
break;
case 'u':
isunix++;
break;
case 'l':
logging++;
break;
}ARGEND
if(attached && !versioned){
fprint(2, "-A must be used with -M\n");
usage();
}
if(argc != 1)
usage();
addr = argv[0];
fmtinstall('T', timefmt);
if((afd = announce(addr, adir)) < 0)
sysfatal("announce %s: %r", addr);
if(logging){
if(strncmp(addr, "unix!", 5) == 0)
addr += 5;
file = smprint("%s.log", addr);
if(file == nil)
sysfatal("smprint log: %r");
if((fd = create(file, OWRITE, 0666)) < 0)
sysfatal("create %s: %r", file);
dup(fd, 2);
if(fd > 2)
close(fd);
}
if(verbose) fprint(2, "%T 9pserve running\n");
proccreate(mainproc, nil, STACK);
}
void
mainproc(void *v)
{
int n, nn;
Fcall f;
USED(v);
atnotify(ignorepipe, 1);
fmtinstall('D', dirfmt);
fmtinstall('M', dirmodefmt);
fmtinstall('F', fcallfmt);
fmtinstall('H', encodefmt);
outq = qalloc();
inq = qalloc();
if(!versioned){
f.type = Tversion;
f.version = "9P2000";
f.msize = msize;
f.tag = NOTAG;
n = convS2M(&f, vbuf, sizeof vbuf);
if(n <= BIT16SZ)
sysfatal("convS2M conversion error");
if(verbose > 1) fprint(2, "%T * <- %F\n", &f);
nn = write(1, vbuf, n);
if(n != nn)
sysfatal("error writing Tversion: %r\n");
n = read9pmsg(0, vbuf, sizeof vbuf);
if(n < 0)
sysfatal("read9pmsg failure");
if(convM2S(vbuf, n, &f) != n)
sysfatal("convM2S failure");
if(f.msize < msize)
msize = f.msize;
if(verbose > 1) fprint(2, "%T * -> %F\n", &f);
}
threadcreate(inputthread, nil, STACK);
threadcreate(outputthread, nil, STACK);
/* if(rootfid) */
/* dorootstat(); */
threadcreate(listenthread, nil, STACK);
threadexits(0);
}
int
ignorepipe(void *v, char *s)
{
USED(v);
if(strcmp(s, "sys: write on closed pipe") == 0)
return 1;
if(strcmp(s, "sys: tstp") == 0)
return 1;
if(strcmp(s, "sys: window size change") == 0)
return 1;
fprint(2, "9pserve %s: %T note: %s\n", addr, s);
return 0;
}
void
listenthread(void *arg)
{
Conn *c;
Ioproc *io;
io = ioproc();
USED(arg);
threadsetname("listen %s", adir);
for(;;){
c = emalloc(sizeof(Conn));
c->fd = iolisten(io, adir, c->dir);
if(c->fd < 0){
if(verbose) fprint(2, "%T listen: %r\n");
close(afd);
free(c);
return;
}
c->inc = chancreate(sizeof(void*), 0);
c->internal = chancreate(sizeof(void*), 0);
c->inq = qalloc();
c->outq = qalloc();
c->outqdead = chancreate(sizeof(void*), 0);
if(verbose) fprint(2, "%T incoming call on %s\n", c->dir);
threadcreate(connthread, c, STACK);
}
}
void
send9pmsg(Msg *m)
{
int n, nn;
n = sizeS2M(&m->rx);
m->rpkt = emalloc(n);
nn = convS2M(&m->rx, m->rpkt, n);
if(nn <= BIT16SZ)
sysfatal("convS2M conversion error");
if(nn != n)
sysfatal("sizeS2M and convS2M disagree");
sendq(m->c->outq, m);
}
void
sendomsg(Msg *m)
{
int n, nn;
n = sizeS2M(&m->tx);
m->tpkt = emalloc(n);
nn = convS2M(&m->tx, m->tpkt, n);
if(nn <= BIT16SZ)
sysfatal("convS2M conversion error");
if(nn != n)
sysfatal("sizeS2M and convS2M disagree");
sendq(outq, m);
}
void
err(Msg *m, char *ename)
{
m->rx.type = Rerror;
m->rx.ename = ename;
m->rx.tag = m->tx.tag;
send9pmsg(m);
}
char*
estrdup(char *s)
{
char *t;
t = emalloc(strlen(s)+1);
strcpy(t, s);
return t;
}
void
connthread(void *arg)
{
int i, fd;
Conn *c;
Hash *h, *hnext;
Msg *m, *om, *mm, sync;
Fid *f;
Ioproc *io;
c = arg;
threadsetname("conn %s", c->dir);
io = ioproc();
fd = ioaccept(io, c->fd, c->dir);
if(fd < 0){
if(verbose) fprint(2, "%T accept %s: %r\n", c->dir);
goto out;
}
close(c->fd);
c->fd = fd;
threadcreate(connoutthread, c, STACK);
while((m = mread9p(io, c->fd)) != nil){
if(verbose > 1) fprint(2, "%T fd#%d -> %F\n", c->fd, &m->tx);
m->c = c;
m->ctag = m->tx.tag;
c->nmsg++;
if(verbose > 1) fprint(2, "%T fd#%d: new msg %p\n", c->fd, m);
if(puthash(c->tag, m->tx.tag, m) < 0){
err(m, "duplicate tag");
continue;
}
msgincref(m);
switch(m->tx.type){
case Tversion:
m->rx.tag = m->tx.tag;
m->rx.msize = m->tx.msize;
if(m->rx.msize > msize)
m->rx.msize = msize;
m->rx.version = "9P2000";
m->rx.type = Rversion;
send9pmsg(m);
continue;
case Tflush:
if((m->oldm = gethash(c->tag, m->tx.oldtag)) == nil){
m->rx.tag = m->tx.tag;
m->rx.type = Rflush;
send9pmsg(m);
continue;
}
msgincref(m->oldm);
break;
case Tattach:
m->afid = nil;
if(m->tx.afid != NOFID
&& (m->afid = gethash(c->fid, m->tx.afid)) == nil){
err(m, "unknown fid");
continue;
}
if(m->afid)
m->afid->ref++;
m->fid = fidnew(m->tx.fid);
if(puthash(c->fid, m->tx.fid, m->fid) < 0){
err(m, "duplicate fid");
continue;
}
m->fid->ref++;
if(attached && m->afid==nil){
if(m->tx.aname[0] && strcmp(xaname, m->tx.aname) != 0){
err(m, "invalid attach name");
continue;
}
m->tx.afid = xafid;
m->tx.aname = xaname;
m->tx.uname = getuser(); /* what srv.c used */
repack(&m->tx, &m->tpkt);
}
break;
case Twalk:
if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
err(m, "unknown fid");
continue;
}
m->fid->ref++;
if(m->tx.newfid == m->tx.fid){
m->fid->ref++;
m->newfid = m->fid;
}else{
m->newfid = fidnew(m->tx.newfid);
if(puthash(c->fid, m->tx.newfid, m->newfid) < 0){
err(m, "duplicate fid");
continue;
}
m->newfid->ref++;
}
break;
case Tauth:
if(attached){
err(m, "authentication not required");
continue;
}
if(noauth){
err(m, "authentication rejected");
continue;
}
m->afid = fidnew(m->tx.afid);
if(puthash(c->fid, m->tx.afid, m->afid) < 0){
err(m, "duplicate fid");
continue;
}
m->afid->ref++;
break;
case Tcreate:
if(m->tx.perm&(DMSYMLINK|DMDEVICE|DMNAMEDPIPE|DMSOCKET)){
err(m, "unsupported file type");
continue;
}
goto caseTopen;
case Topenfd:
if(m->tx.mode&~(OTRUNC|3)){
err(m, "bad openfd mode");
continue;
}
m->isopenfd = 1;
m->tx.type = Topen;
m->tpkt[4] = Topen;
/* fall through */
caseTopen:
case Topen:
case Tclunk:
case Tread:
case Twrite:
case Tremove:
case Tstat:
case Twstat:
if((m->fid = gethash(c->fid, m->tx.fid)) == nil){
err(m, "unknown fid");
continue;
}
m->fid->ref++;
break;
}
/* have everything - translate and send */
m->c = c;
m->ctag = m->tx.tag;
m->tx.tag = m->tag;
if(m->fid)
m->tx.fid = m->fid->fid;
if(m->newfid)
m->tx.newfid = m->newfid->fid;
if(m->afid)
m->tx.afid = m->afid->fid;
if(m->oldm)
m->tx.oldtag = m->oldm->tag;
/* reference passes to outq */
sendq(outq, m);
while(c->nmsg >= MAXMSG){
c->inputstalled = 1;
recvp(c->inc);
}
}
if(verbose) fprint(2, "%T fd#%d eof; flushing conn\n", c->fd);
/* flush all outstanding messages */
for(i=0; i<NHASH; i++){
while((h = c->tag[i]) != nil){
om = h->v;
msgincref(om); /* for us */
m = msgnew(0);
m->internal = 1;
m->c = c;
c->nmsg++;
m->tx.type = Tflush;
m->tx.tag = m->tag;
m->tx.oldtag = om->tag;
m->oldm = om;
msgincref(om);
msgincref(m); /* for outq */
sendomsg(m);
mm = recvp(c->internal);
assert(mm == m);
msgput(m); /* got from recvp */
msgput(m); /* got from msgnew */
if(delhash(c->tag, om->ctag, om) == 0)
msgput(om); /* got from hash table */
msgput(om); /* got from msgincref */
}
}
/*
* outputthread has written all its messages
* to the remote connection (because we've gotten all the replies!),
* but it might not have gotten a chance to msgput
* the very last one. sync up to make sure.
*/
memset(&sync, 0, sizeof sync);
sync.sync = 1;
sync.c = c;
sendq(outq, &sync);
recvp(c->outqdead);
/* everything is quiet; can close the local output queue. */
sendq(c->outq, nil);
recvp(c->outqdead);
/* should be no messages left anywhere. */
assert(c->nmsg == 0);
/* clunk all outstanding fids */
for(i=0; i<NHASH; i++){
for(h=c->fid[i]; h; h=hnext){
f = h->v;
m = msgnew(0);
m->internal = 1;
m->c = c;
c->nmsg++;
m->tx.type = Tclunk;
m->tx.tag = m->tag;
m->tx.fid = f->fid;
m->fid = f;
f->ref++;
msgincref(m);
sendomsg(m);
mm = recvp(c->internal);
assert(mm == m);
msgclear(m);
msgput(m); /* got from recvp */
msgput(m); /* got from msgnew */
fidput(f); /* got from hash table */
hnext = h->next;
free(h);
}
}
out:
closeioproc(io);
assert(c->nmsg == 0);
assert(c->nfid == 0);
close(c->fd);
chanfree(c->internal);
c->internal = 0;
chanfree(c->inc);
c->inc = 0;
free(c->inq);
c->inq = 0;
free(c);
}
static void
openfdthread(void *v)
{
Conn *c;
Fid *fid;
Msg *m;
int n;
vlong tot;
Ioproc *io;
char buf[1024];
c = v;
fid = c->fdfid;
io = ioproc();
threadsetname("openfd %s", c->fdfid);
tot = 0;
m = nil;
if(c->fdmode == OREAD){
for(;;){
if(verbose) fprint(2, "%T tread...");
m = msgnew(0);
m->internal = 1;
m->c = c;
m->tx.type = Tread;
m->tx.count = msize - IOHDRSZ;
m->tx.fid = fid->fid;
m->tx.tag = m->tag;
m->tx.offset = tot;
m->fid = fid;
fid->ref++;
msgincref(m);
sendomsg(m);
recvp(c->internal);
if(m->rx.type == Rerror){
/* fprint(2, "%T read error: %s\n", m->rx.ename); */
break;
}
if(m->rx.count == 0)
break;
tot += m->rx.count;
if(iowrite(io, c->fd, m->rx.data, m->rx.count) != m->rx.count){
/* fprint(2, "%T pipe write error: %r\n"); */
break;
}
msgput(m);
msgput(m);
m = nil;
}
}else{
for(;;){
if(verbose) fprint(2, "%T twrite...");
n = sizeof buf;
if(n > msize)
n = msize;
if((n=ioread(io, c->fd, buf, n)) <= 0){
if(n < 0)
fprint(2, "%T pipe read error: %r\n");
break;
}
m = msgnew(0);
m->internal = 1;
m->c = c;
m->tx.type = Twrite;
m->tx.fid = fid->fid;
m->tx.data = buf;
m->tx.count = n;
m->tx.tag = m->tag;
m->tx.offset = tot;
m->fid = fid;
fid->ref++;
msgincref(m);
sendomsg(m);
recvp(c->internal);
if(m->rx.type == Rerror){
/* fprint(2, "%T write error: %s\n", m->rx.ename); */
}
tot += n;
msgput(m);
msgput(m);
m = nil;
}
}
if(verbose) fprint(2, "%T eof on %d fid %d\n", c->fd, fid->fid);
close(c->fd);
closeioproc(io);
if(m){
msgput(m);
msgput(m);
}
if(verbose) fprint(2, "%T eof on %d fid %d ref %d\n", c->fd, fid->fid, fid->ref);
if(--fid->openfd == 0){
m = msgnew(0);
m->internal = 1;
m->c = c;
m->tx.type = Tclunk;
m->tx.tag = m->tag;
m->tx.fid = fid->fid;
m->fid = fid;
fid->ref++;
msgincref(m);
sendomsg(m);
recvp(c->internal);
msgput(m);
msgput(m);
}
fidput(fid);
c->fdfid = nil;
chanfree(c->internal);
c->internal = 0;
free(c);
}
int
xopenfd(Msg *m)
{
char errs[ERRMAX];
int n, p[2];
Conn *nc;
if(pipe(p) < 0){
rerrstr(errs, sizeof errs);
err(m, errs);
/* XXX return here? */
}
if(verbose) fprint(2, "%T xopen pipe %d %d...", p[0], p[1]);
/* now we're committed. */
/* a new connection for this fid */
nc = emalloc(sizeof(Conn));
nc->internal = chancreate(sizeof(void*), 0);
/* a ref for us */
nc->fdfid = m->fid;
m->fid->ref++;
nc->fdfid->openfd++;
nc->fdmode = m->tx.mode;
nc->fd = p[0];
/* a thread to tend the pipe */
threadcreate(openfdthread, nc, STACK);
/* if mode is ORDWR, that openfdthread will write; start a reader */
if((m->tx.mode&3) == ORDWR){
nc = emalloc(sizeof(Conn));
nc->internal = chancreate(sizeof(void*), 0);
nc->fdfid = m->fid;
m->fid->ref++;
nc->fdfid->openfd++;
nc->fdmode = OREAD;
nc->fd = dup(p[0], -1);
threadcreate(openfdthread, nc, STACK);
}
/* steal fid from other connection */
if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
fidput(m->fid);
/* rewrite as Ropenfd */
m->rx.type = Ropenfd;
n = GBIT32(m->rpkt);
m->rpkt = erealloc(m->rpkt, n+4);
PBIT32(m->rpkt+n, p[1]);
n += 4;
PBIT32(m->rpkt, n);
m->rpkt[4] = Ropenfd;
m->rx.unixfd = p[1];
return 0;
}
void
connoutthread(void *arg)
{
int err;
Conn *c;
Msg *m, *om;
Ioproc *io;
c = arg;
io = ioproc();
threadsetname("connout %s", c->dir);
while((m = recvq(c->outq)) != nil){
err = m->tx.type+1 != m->rx.type;
if(!err && m->isopenfd)
if(xopenfd(m) < 0)
continue;
switch(m->tx.type){
case Tflush:
om = m->oldm;
if(om)
if(delhash(om->c->tag, om->ctag, om) == 0)
msgput(om);
break;
case Tclunk:
case Tremove:
if(m->fid)
if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
fidput(m->fid);
break;
case Tauth:
if(err && m->afid){
if(verbose) fprint(2, "%T auth error\n");
if(delhash(m->c->fid, m->afid->cfid, m->afid) == 0)
fidput(m->afid);
}
break;
case Tattach:
if(err && m->fid)
if(delhash(m->c->fid, m->fid->cfid, m->fid) == 0)
fidput(m->fid);
break;
case Twalk:
if(err || m->rx.nwqid < m->tx.nwname)
if(m->tx.fid != m->tx.newfid && m->newfid)
if(delhash(m->c->fid, m->newfid->cfid, m->newfid) == 0)
fidput(m->newfid);
break;
case Tread:
break;
case Tstat:
break;
case Topen:
case Tcreate:
m->fid->isdir = (m->rx.qid.type & QTDIR);
break;
}
if(delhash(m->c->tag, m->ctag, m) == 0)
msgput(m);
if(verbose > 1) fprint(2, "%T fd#%d <- %F\n", c->fd, &m->rx);
rewritehdr(&m->rx, m->rpkt);
if(mwrite9p(io, c->fd, m->rpkt) < 0)
if(verbose) fprint(2, "%T write error: %r\n");
msgput(m);
if(c->inputstalled && c->nmsg < MAXMSG)
nbsendp(c->inc, 0);
}
closeioproc(io);
free(c->outq);
c->outq = nil;
sendp(c->outqdead, nil);
}
void
outputthread(void *arg)
{
Msg *m;
Ioproc *io;
USED(arg);
io = ioproc();
threadsetname("output");
while((m = recvq(outq)) != nil){
if(m->sync){
sendp(m->c->outqdead, nil);
continue;
}
if(verbose > 1) fprint(2, "%T * <- %F\n", &m->tx);
rewritehdr(&m->tx, m->tpkt);
if(mwrite9p(io, 1, m->tpkt) < 0)
sysfatal("output error: %r");
msgput(m);
}
closeioproc(io);
fprint(2, "%T output eof\n");
threadexitsall(0);
}
void
inputthread(void *arg)
{
uchar *pkt;
int n, nn, tag;
Msg *m;
Ioproc *io;
threadsetname("input");
if(verbose) fprint(2, "%T input thread\n");
io = ioproc();
USED(arg);
while((pkt = read9ppkt(io, 0)) != nil){
n = GBIT32(pkt);
if(n < 7){
fprint(2, "%T short 9P packet from server\n");
free(pkt);
continue;
}
if(verbose > 2) fprint(2, "%T read %.*H\n", n, pkt);
tag = GBIT16(pkt+5);
if((m = msgget(tag)) == nil){
fprint(2, "%T unexpected 9P response tag %d\n", tag);
free(pkt);
continue;
}
if((nn = convM2S(pkt, n, &m->rx)) != n){
fprint(2, "%T bad packet - convM2S %d but %d\n", nn, n);
free(pkt);
msgput(m);
continue;
}
if(verbose > 1) fprint(2, "%T * -> %F%s\n", &m->rx,
m->internal ? " (internal)" : "");
m->rpkt = pkt;
m->rx.tag = m->ctag;
if(m->internal)
sendp(m->c->internal, m);
else if(m->c->outq)
sendq(m->c->outq, m);
else
msgput(m);
}
closeioproc(io);
/*fprint(2, "%T input eof\n"); */
threadexitsall(0);
}
void*
gethash(Hash **ht, uint n)
{
Hash *h;
for(h=ht[n%NHASH]; h; h=h->next)
if(h->n == n)
return h->v;
return nil;
}
int
delhash(Hash **ht, uint n, void *v)
{
Hash *h, **l;
for(l=&ht[n%NHASH]; h=*l; l=&h->next)
if(h->n == n){
if(h->v != v){
if(verbose) fprint(2, "%T delhash %d got %p want %p\n", n, h->v, v);
return -1;
}
*l = h->next;
free(h);
return 0;
}
return -1;
}
int
puthash(Hash **ht, uint n, void *v)
{
Hash *h;
if(gethash(ht, n))
return -1;
h = emalloc(sizeof(Hash));
h->next = ht[n%NHASH];
h->n = n;
h->v = v;
ht[n%NHASH] = h;
return 0;
}
Fid **fidtab;
int nfidtab;
Fid *freefid;
Fid*
fidnew(int cfid)
{
Fid *f;
if(freefid == nil){
fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
if(nfidtab == xafid){
fidtab[nfidtab++] = nil;
fidtab = erealloc(fidtab, (nfidtab+1)*sizeof(fidtab[0]));
}
fidtab[nfidtab] = emalloc(sizeof(Fid));
freefid = fidtab[nfidtab];
freefid->fid = nfidtab++;
}
f = freefid;
freefid = f->next;
f->cfid = cfid;
f->ref = 1;
f->offset = 0;
f->coffset = 0;
f->isdir = -1;
return f;
}
void
fidput(Fid *f)
{
if(f == nil)
return;
assert(f->ref > 0);
if(--f->ref > 0)
return;
f->next = freefid;
f->cfid = -1;
freefid = f;
}
Msg **msgtab;
int nmsgtab;
int nmsg;
Msg *freemsg;
void
msgincref(Msg *m)
{
if(verbose > 1) fprint(2, "%T msgincref @0x%lux %p tag %d/%d ref %d=>%d\n",
getcallerpc(&m), m, m->tag, m->ctag, m->ref, m->ref+1);
m->ref++;
}
Msg*
msgnew(int x)
{
Msg *m;
if(freemsg == nil){
msgtab = erealloc(msgtab, (nmsgtab+1)*sizeof(msgtab[0]));
msgtab[nmsgtab] = emalloc(sizeof(Msg));
freemsg = msgtab[nmsgtab];
freemsg->tag = nmsgtab++;
}
m = freemsg;
freemsg = m->next;
m->ref = 1;
if(verbose > 1) fprint(2, "%T msgnew @0x%lux %p tag %d ref %d\n",
getcallerpc(&x), m, m->tag, m->ref);
nmsg++;
return m;
}
/*
* Clear data associated with connections, so that
* if all msgs have been msgcleared, the connection
* can be freed. Note that this does *not* free the tpkt
* and rpkt; they are freed in msgput with the msg itself.
* The io write thread might still be holding a ref to msg
* even once the connection has finished with it.
*/
void
msgclear(Msg *m)
{
if(m->c){
m->c->nmsg--;
m->c = nil;
}
if(m->oldm){
msgput(m->oldm);
m->oldm = nil;
}
if(m->fid){
fidput(m->fid);
m->fid = nil;
}
if(m->afid){
fidput(m->afid);
m->afid = nil;
}
if(m->newfid){
fidput(m->newfid);
m->newfid = nil;
}
if(m->rx.type == Ropenfd && m->rx.unixfd >= 0){
close(m->rx.unixfd);
m->rx.unixfd = -1;
}
}
void
msgput(Msg *m)
{
if(m == nil)
return;
if(verbose > 1) fprint(2, "%T msgput 0x%lux %p tag %d/%d ref %d\n",
getcallerpc(&m), m, m->tag, m->ctag, m->ref);
assert(m->ref > 0);
if(--m->ref > 0)
return;
nmsg--;
msgclear(m);
if(m->tpkt){
free(m->tpkt);
m->tpkt = nil;
}
if(m->rpkt){
free(m->rpkt);
m->rpkt = nil;
}
m->isopenfd = 0;
m->internal = 0;
m->next = freemsg;
freemsg = m;
}
Msg*
msgget(int n)
{
Msg *m;
if(n < 0 || n >= nmsgtab)
return nil;
m = msgtab[n];
if(m->ref == 0)
return nil;
if(verbose) fprint(2, "%T msgget %d = %p\n", n, m);
msgincref(m);
return m;
}
void*
emalloc(int n)
{
void *v;
v = mallocz(n, 1);
if(v == nil){
abort();
sysfatal("out of memory allocating %d", n);
}
return v;
}
void*
erealloc(void *v, int n)
{
v = realloc(v, n);
if(v == nil){
abort();
sysfatal("out of memory reallocating %d", n);
}
return v;
}
typedef struct Qel Qel;
struct Qel
{
Qel *next;
void *p;
};
struct Queue
{
QLock lk;
Rendez r;
Qel *head;
Qel *tail;
};
Queue*
qalloc(void)
{
Queue *q;
q = mallocz(sizeof(Queue), 1);
if(q == nil)
return nil;
q->r.l = &q->lk;
return q;
}
int
sendq(Queue *q, void *p)
{
Qel *e;
e = emalloc(sizeof(Qel));
qlock(&q->lk);
e->p = p;
e->next = nil;
if(q->head == nil)
q->head = e;
else
q->tail->next = e;
q->tail = e;
rwakeup(&q->r);
qunlock(&q->lk);
return 0;
}
void*
recvq(Queue *q)
{
void *p;
Qel *e;
qlock(&q->lk);
while(q->head == nil)
rsleep(&q->r);
e = q->head;
q->head = e->next;
qunlock(&q->lk);
p = e->p;
free(e);
return p;
}
uchar*
read9ppkt(Ioproc *io, int fd)
{
uchar buf[4], *pkt;
int n, nn;
n = ioreadn(io, fd, buf, 4);
if(n != 4)
return nil;
n = GBIT32(buf);
if(n > MAXMSGSIZE)
return nil;
pkt = emalloc(n);
PBIT32(pkt, n);
nn = ioreadn(io, fd, pkt+4, n-4);
if(nn != n-4){
free(pkt);
return nil;
}
/* would do this if we ever got one of these, but we only generate them
if(pkt[4] == Ropenfd){
newfd = iorecvfd(io, fd);
PBIT32(pkt+n-4, newfd);
}
*/
return pkt;
}
Msg*
mread9p(Ioproc *io, int fd)
{
int n, nn;
uchar *pkt;
Msg *m;
if((pkt = read9ppkt(io, fd)) == nil)
return nil;
m = msgnew(0);
m->tpkt = pkt;
n = GBIT32(pkt);
nn = convM2S(pkt, n, &m->tx);
if(nn != n){
fprint(2, "%T read bad packet from %d\n", fd);
return nil;
}
return m;
}
int
mwrite9p(Ioproc *io, int fd, uchar *pkt)
{
int n, nfd;
n = GBIT32(pkt);
if(verbose > 2) fprint(2, "%T write %d %d %.*H\n", fd, n, n, pkt);
if(verbose > 1) fprint(2, "%T before iowrite\n");
if(iowrite(io, fd, pkt, n) != n){
fprint(2, "%T write error: %r\n");
return -1;
}
if(verbose > 1) fprint(2, "%T after iowrite\n");
if(pkt[4] == Ropenfd){
nfd = GBIT32(pkt+n-4);
if(iosendfd(io, fd, nfd) < 0){
fprint(2, "%T send fd error: %r\n");
return -1;
}
}
return 0;
}
void
restring(uchar *pkt, int pn, char *s)
{
int n;
if(s < (char*)pkt || s >= (char*)pkt+pn)
return;
n = strlen(s);
memmove(s+1, s, n);
PBIT16((uchar*)s-1, n);
}
void
repack(Fcall *f, uchar **ppkt)
{
uint n, nn;
uchar *pkt;
pkt = *ppkt;
n = GBIT32(pkt);
nn = sizeS2M(f);
if(nn > n){
free(pkt);
pkt = emalloc(nn);
*ppkt = pkt;
}
n = convS2M(f, pkt, nn);
if(n <= BIT16SZ)
sysfatal("convS2M conversion error");
if(n != nn)
sysfatal("convS2M and sizeS2M disagree");
}
void
rewritehdr(Fcall *f, uchar *pkt)
{
int i, n;
n = GBIT32(pkt);
PBIT16(pkt+5, f->tag);
switch(f->type){
case Tversion:
case Rversion:
restring(pkt, n, f->version);
break;
case Tauth:
PBIT32(pkt+7, f->afid);
restring(pkt, n, f->uname);
restring(pkt, n, f->aname);
break;
case Tflush:
PBIT16(pkt+7, f->oldtag);
break;
case Tattach:
restring(pkt, n, f->uname);
restring(pkt, n, f->aname);
PBIT32(pkt+7, f->fid);
PBIT32(pkt+11, f->afid);
break;
case Twalk:
PBIT32(pkt+7, f->fid);
PBIT32(pkt+11, f->newfid);
for(i=0; i<f->nwname; i++)
restring(pkt, n, f->wname[i]);
break;
case Tcreate:
restring(pkt, n, f->name);
/* fall through */
case Topen:
case Tclunk:
case Tremove:
case Tstat:
case Twstat:
case Twrite:
PBIT32(pkt+7, f->fid);
break;
case Tread:
PBIT32(pkt+7, f->fid);
PBIT64(pkt+11, f->offset);
break;
case Rerror:
restring(pkt, n, f->ename);
break;
}
}
static long
_iolisten(va_list *arg)
{
char *a, *b;
a = va_arg(*arg, char*);
b = va_arg(*arg, char*);
return listen(a, b);
}
int
iolisten(Ioproc *io, char *a, char *b)
{
return iocall(io, _iolisten, a, b);
}
static long
_ioaccept(va_list *arg)
{
int fd;
char *dir;
fd = va_arg(*arg, int);
dir = va_arg(*arg, char*);
return accept(fd, dir);
}
int
ioaccept(Ioproc *io, int fd, char *dir)
{
return iocall(io, _ioaccept, fd, dir);
}
int
timefmt(Fmt *fmt)
{
static char *mon[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
vlong ns;
Tm tm;
ns = nsec();
tm = *localtime(time(0));
return fmtprint(fmt, "%s %2d %02d:%02d:%02d.%03d",
mon[tm.mon], tm.mday, tm.hour, tm.min, tm.sec,
(int)(ns%1000000000)/1000000);
}