Plan 9 version, nothing tweaked yet.
This commit is contained in:
parent
7763a61a35
commit
b8c14089d8
7 changed files with 2596 additions and 0 deletions
975
src/cmd/plumb/fsys.c
Normal file
975
src/cmd/plumb/fsys.c
Normal file
|
|
@ -0,0 +1,975 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <regexp.h>
|
||||
#include <thread.h>
|
||||
#include <auth.h>
|
||||
#include <fcall.h>
|
||||
#include <plumb.h>
|
||||
#include "plumber.h"
|
||||
|
||||
enum
|
||||
{
|
||||
Stack = 8*1024
|
||||
};
|
||||
|
||||
typedef struct Dirtab Dirtab;
|
||||
typedef struct Fid Fid;
|
||||
typedef struct Holdq Holdq;
|
||||
typedef struct Readreq Readreq;
|
||||
typedef struct Sendreq Sendreq;
|
||||
|
||||
struct Dirtab
|
||||
{
|
||||
char *name;
|
||||
uchar type;
|
||||
uint qid;
|
||||
uint perm;
|
||||
int nopen; /* #fids open on this port */
|
||||
Fid *fopen;
|
||||
Holdq *holdq;
|
||||
Readreq *readq;
|
||||
Sendreq *sendq;
|
||||
};
|
||||
|
||||
struct Fid
|
||||
{
|
||||
int fid;
|
||||
int busy;
|
||||
int open;
|
||||
int mode;
|
||||
Qid qid;
|
||||
Dirtab *dir;
|
||||
long offset; /* zeroed at beginning of each message, read or write */
|
||||
char *writebuf; /* partial message written so far; offset tells how much */
|
||||
Fid *next;
|
||||
Fid *nextopen;
|
||||
};
|
||||
|
||||
struct Readreq
|
||||
{
|
||||
Fid *fid;
|
||||
Fcall *fcall;
|
||||
uchar *buf;
|
||||
Readreq *next;
|
||||
};
|
||||
|
||||
struct Sendreq
|
||||
{
|
||||
int nfid; /* number of fids that should receive this message */
|
||||
int nleft; /* number left that haven't received it */
|
||||
Fid **fid; /* fid[nfid] */
|
||||
Plumbmsg *msg;
|
||||
char *pack; /* plumbpack()ed message */
|
||||
int npack; /* length of pack */
|
||||
Sendreq *next;
|
||||
};
|
||||
|
||||
struct Holdq
|
||||
{
|
||||
Plumbmsg *msg;
|
||||
Holdq *next;
|
||||
};
|
||||
|
||||
struct /* needed because incref() doesn't return value */
|
||||
{
|
||||
Lock;
|
||||
int ref;
|
||||
} rulesref;
|
||||
|
||||
enum
|
||||
{
|
||||
DEBUG = 0,
|
||||
NDIR = 50,
|
||||
Nhash = 16,
|
||||
|
||||
Qdir = 0,
|
||||
Qrules = 1,
|
||||
Qsend = 2,
|
||||
Qport = 3,
|
||||
NQID = Qport
|
||||
};
|
||||
|
||||
static Dirtab dir[NDIR] =
|
||||
{
|
||||
{ ".", QTDIR, Qdir, 0500|DMDIR },
|
||||
{ "rules", QTFILE, Qrules, 0600 },
|
||||
{ "send", QTFILE, Qsend, 0200 },
|
||||
};
|
||||
static int ndir = NQID;
|
||||
|
||||
static int srvfd;
|
||||
static int srvclosefd; /* rock for end of pipe to close */
|
||||
static int clockfd;
|
||||
static int clock;
|
||||
static Fid *fids[Nhash];
|
||||
static QLock readlock;
|
||||
static QLock queue;
|
||||
static char srvfile[128];
|
||||
static int messagesize = 8192+IOHDRSZ; /* good start */
|
||||
|
||||
static void fsysproc(void*);
|
||||
static void fsysrespond(Fcall*, uchar*, char*);
|
||||
static Fid* newfid(int);
|
||||
|
||||
static Fcall* fsysflush(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysversion(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysauth(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysattach(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsyswalk(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysopen(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsyscreate(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysread(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsyswrite(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysclunk(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysremove(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsysstat(Fcall*, uchar*, Fid*);
|
||||
static Fcall* fsyswstat(Fcall*, uchar*, Fid*);
|
||||
|
||||
Fcall* (*fcall[Tmax])(Fcall*, uchar*, Fid*) =
|
||||
{
|
||||
[Tflush] = fsysflush,
|
||||
[Tversion] = fsysversion,
|
||||
[Tauth] = fsysauth,
|
||||
[Tattach] = fsysattach,
|
||||
[Twalk] = fsyswalk,
|
||||
[Topen] = fsysopen,
|
||||
[Tcreate] = fsyscreate,
|
||||
[Tread] = fsysread,
|
||||
[Twrite] = fsyswrite,
|
||||
[Tclunk] = fsysclunk,
|
||||
[Tremove]= fsysremove,
|
||||
[Tstat] = fsysstat,
|
||||
[Twstat] = fsyswstat,
|
||||
};
|
||||
|
||||
char Ebadfcall[] = "bad fcall type";
|
||||
char Eperm[] = "permission denied";
|
||||
char Enomem[] = "malloc failed for buffer";
|
||||
char Enotdir[] = "not a directory";
|
||||
char Enoexist[] = "plumb file does not exist";
|
||||
char Eisdir[] = "file is a directory";
|
||||
char Ebadmsg[] = "bad plumb message format";
|
||||
char Enosuchport[] ="no such plumb port";
|
||||
char Enoport[] = "couldn't find destination for message";
|
||||
char Einuse[] = "file already open";
|
||||
|
||||
/*
|
||||
* Add new port. A no-op if port already exists or is the null string
|
||||
*/
|
||||
void
|
||||
addport(char *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(port == nil)
|
||||
return;
|
||||
for(i=NQID; i<ndir; i++)
|
||||
if(strcmp(port, dir[i].name) == 0)
|
||||
return;
|
||||
if(i == NDIR){
|
||||
fprint(2, "plumb: too many ports; max %d\n", NDIR);
|
||||
return;
|
||||
}
|
||||
ndir++;
|
||||
dir[i].name = estrdup(port);
|
||||
dir[i].qid = i;
|
||||
dir[i].perm = 0400;
|
||||
nports++;
|
||||
ports = erealloc(ports, nports*sizeof(char*));
|
||||
ports[nports-1] = dir[i].name;
|
||||
}
|
||||
|
||||
static ulong
|
||||
getclock(void)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
seek(clockfd, 0, 0);
|
||||
read(clockfd, buf, sizeof buf);
|
||||
return atoi(buf);
|
||||
}
|
||||
|
||||
void
|
||||
startfsys(void)
|
||||
{
|
||||
int p[2], fd;
|
||||
|
||||
fmtinstall('F', fcallfmt);
|
||||
clockfd = open("/dev/time", OREAD|OCEXEC);
|
||||
clock = getclock();
|
||||
if(pipe(p) < 0)
|
||||
error("can't create pipe: %r");
|
||||
/* 0 will be server end, 1 will be client end */
|
||||
srvfd = p[0];
|
||||
srvclosefd = p[1];
|
||||
sprint(srvfile, "/srv/plumb.%s.%d", user, getpid());
|
||||
if(putenv("plumbsrv", srvfile) < 0)
|
||||
error("can't write $plumbsrv: %r");
|
||||
fd = create(srvfile, OWRITE|OCEXEC|ORCLOSE, 0600);
|
||||
if(fd < 0)
|
||||
error("can't create /srv file: %r");
|
||||
if(fprint(fd, "%d", p[1]) <= 0)
|
||||
error("can't write /srv/file: %r");
|
||||
/* leave fd open; ORCLOSE will take care of it */
|
||||
|
||||
procrfork(fsysproc, nil, Stack, RFFDG);
|
||||
|
||||
close(p[0]);
|
||||
if(mount(p[1], -1, "/mnt/plumb", MREPL, "") < 0)
|
||||
error("can't mount /mnt/plumb: %r");
|
||||
close(p[1]);
|
||||
}
|
||||
|
||||
static void
|
||||
fsysproc(void*)
|
||||
{
|
||||
int n;
|
||||
Fcall *t;
|
||||
Fid *f;
|
||||
uchar *buf;
|
||||
|
||||
close(srvclosefd);
|
||||
srvclosefd = -1;
|
||||
t = nil;
|
||||
for(;;){
|
||||
buf = malloc(messagesize); /* avoid memset of emalloc */
|
||||
if(buf == nil)
|
||||
error("malloc failed: %r");
|
||||
qlock(&readlock);
|
||||
n = read9pmsg(srvfd, buf, messagesize);
|
||||
if(n <= 0){
|
||||
if(n < 0)
|
||||
error("i/o error on server channel");
|
||||
threadexitsall("unmounted");
|
||||
}
|
||||
if(readlock.head == nil) /* no other processes waiting to read; start one */
|
||||
proccreate(fsysproc, nil, Stack);
|
||||
qunlock(&readlock);
|
||||
if(t == nil)
|
||||
t = emalloc(sizeof(Fcall));
|
||||
if(convM2S(buf, n, t) != n)
|
||||
error("convert error in convM2S");
|
||||
if(DEBUG)
|
||||
fprint(2, "<= %F\n", t);
|
||||
if(fcall[t->type] == nil)
|
||||
fsysrespond(t, buf, Ebadfcall);
|
||||
else{
|
||||
if(t->type==Tversion || t->type==Tauth)
|
||||
f = nil;
|
||||
else
|
||||
f = newfid(t->fid);
|
||||
t = (*fcall[t->type])(t, buf, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
fsysrespond(Fcall *t, uchar *buf, char *err)
|
||||
{
|
||||
int n;
|
||||
|
||||
if(err){
|
||||
t->type = Rerror;
|
||||
t->ename = err;
|
||||
}else
|
||||
t->type++;
|
||||
if(buf == nil)
|
||||
buf = emalloc(messagesize);
|
||||
n = convS2M(t, buf, messagesize);
|
||||
if(n < 0)
|
||||
error("convert error in convS2M");
|
||||
if(write(srvfd, buf, n) != n)
|
||||
error("write error in respond");
|
||||
if(DEBUG)
|
||||
fprint(2, "=> %F\n", t);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static
|
||||
Fid*
|
||||
newfid(int fid)
|
||||
{
|
||||
Fid *f, *ff, **fh;
|
||||
|
||||
qlock(&queue);
|
||||
ff = nil;
|
||||
fh = &fids[fid&(Nhash-1)];
|
||||
for(f=*fh; f; f=f->next)
|
||||
if(f->fid == fid)
|
||||
goto Return;
|
||||
else if(ff==nil && !f->busy)
|
||||
ff = f;
|
||||
if(ff){
|
||||
ff->fid = fid;
|
||||
f = ff;
|
||||
goto Return;
|
||||
}
|
||||
f = emalloc(sizeof *f);
|
||||
f->fid = fid;
|
||||
f->next = *fh;
|
||||
*fh = f;
|
||||
Return:
|
||||
qunlock(&queue);
|
||||
return f;
|
||||
}
|
||||
|
||||
static uint
|
||||
dostat(Dirtab *dir, uchar *buf, uint nbuf, uint clock)
|
||||
{
|
||||
Dir d;
|
||||
|
||||
d.qid.type = dir->type;
|
||||
d.qid.path = dir->qid;
|
||||
d.qid.vers = 0;
|
||||
d.mode = dir->perm;
|
||||
d.length = 0; /* would be nice to do better */
|
||||
d.name = dir->name;
|
||||
d.uid = user;
|
||||
d.gid = user;
|
||||
d.muid = user;
|
||||
d.atime = clock;
|
||||
d.mtime = clock;
|
||||
return convD2M(&d, buf, nbuf);
|
||||
}
|
||||
|
||||
static void
|
||||
queuesend(Dirtab *d, Plumbmsg *m)
|
||||
{
|
||||
Sendreq *s, *t;
|
||||
Fid *f;
|
||||
int i;
|
||||
|
||||
s = emalloc(sizeof(Sendreq));
|
||||
s->nfid = d->nopen;
|
||||
s->nleft = s->nfid;
|
||||
s->fid = emalloc(s->nfid*sizeof(Fid*));
|
||||
i = 0;
|
||||
/* build array of fids open on this channel */
|
||||
for(f=d->fopen; f!=nil; f=f->nextopen)
|
||||
s->fid[i++] = f;
|
||||
s->msg = m;
|
||||
s->next = nil;
|
||||
/* link to end of queue; drainqueue() searches in sender order so this implements a FIFO */
|
||||
for(t=d->sendq; t!=nil; t=t->next)
|
||||
if(t->next == nil)
|
||||
break;
|
||||
if(t == nil)
|
||||
d->sendq = s;
|
||||
else
|
||||
t->next = s;
|
||||
}
|
||||
|
||||
static void
|
||||
queueread(Dirtab *d, Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
Readreq *r;
|
||||
|
||||
r = emalloc(sizeof(Readreq));
|
||||
r->fcall = t;
|
||||
r->buf = buf;
|
||||
r->fid = f;
|
||||
r->next = d->readq;
|
||||
d->readq = r;
|
||||
}
|
||||
|
||||
static void
|
||||
drainqueue(Dirtab *d)
|
||||
{
|
||||
Readreq *r, *nextr, *prevr;
|
||||
Sendreq *s, *nexts, *prevs;
|
||||
int i, n;
|
||||
|
||||
prevs = nil;
|
||||
for(s=d->sendq; s!=nil; s=nexts){
|
||||
nexts = s->next;
|
||||
for(i=0; i<s->nfid; i++){
|
||||
prevr = nil;
|
||||
for(r=d->readq; r!=nil; r=nextr){
|
||||
nextr = r->next;
|
||||
if(r->fid == s->fid[i]){
|
||||
/* pack the message if necessary */
|
||||
if(s->pack == nil)
|
||||
s->pack = plumbpack(s->msg, &s->npack);
|
||||
/* exchange the stuff... */
|
||||
r->fcall->data = s->pack+r->fid->offset;
|
||||
n = s->npack - r->fid->offset;
|
||||
if(n > messagesize-IOHDRSZ)
|
||||
n = messagesize-IOHDRSZ;
|
||||
if(n > r->fcall->count)
|
||||
n = r->fcall->count;
|
||||
r->fcall->count = n;
|
||||
fsysrespond(r->fcall, r->buf, nil);
|
||||
r->fid->offset += n;
|
||||
if(r->fid->offset >= s->npack){
|
||||
/* message transferred; delete this fid from send queue */
|
||||
r->fid->offset = 0;
|
||||
s->fid[i] = nil;
|
||||
s->nleft--;
|
||||
}
|
||||
/* delete read request from queue */
|
||||
if(prevr)
|
||||
prevr->next = r->next;
|
||||
else
|
||||
d->readq = r->next;
|
||||
free(r->fcall);
|
||||
free(r);
|
||||
break;
|
||||
}else
|
||||
prevr = r;
|
||||
}
|
||||
}
|
||||
/* if no fids left, delete this send from queue */
|
||||
if(s->nleft == 0){
|
||||
free(s->fid);
|
||||
plumbfree(s->msg);
|
||||
free(s->pack);
|
||||
if(prevs)
|
||||
prevs->next = s->next;
|
||||
else
|
||||
d->sendq = s->next;
|
||||
free(s);
|
||||
}else
|
||||
prevs = s;
|
||||
}
|
||||
}
|
||||
|
||||
/* can't flush a send because they are always answered synchronously */
|
||||
static void
|
||||
flushqueue(Dirtab *d, int oldtag)
|
||||
{
|
||||
Readreq *r, *prevr;
|
||||
|
||||
prevr = nil;
|
||||
for(r=d->readq; r!=nil; r=r->next){
|
||||
if(oldtag == r->fcall->tag){
|
||||
/* delete read request from queue */
|
||||
if(prevr)
|
||||
prevr->next = r->next;
|
||||
else
|
||||
d->readq = r->next;
|
||||
free(r->fcall);
|
||||
free(r->buf);
|
||||
free(r);
|
||||
return;
|
||||
}
|
||||
prevr = r;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove messages awaiting delivery to now-closing fid */
|
||||
static void
|
||||
removesenders(Dirtab *d, Fid *fid)
|
||||
{
|
||||
Sendreq *s, *nexts, *prevs;
|
||||
int i;
|
||||
|
||||
prevs = nil;
|
||||
for(s=d->sendq; s!=nil; s=nexts){
|
||||
nexts = s->next;
|
||||
for(i=0; i<s->nfid; i++)
|
||||
if(fid == s->fid[i]){
|
||||
/* delete this fid from send queue */
|
||||
s->fid[i] = nil;
|
||||
s->nleft--;
|
||||
break;
|
||||
}
|
||||
/* if no fids left, delete this send from queue */
|
||||
if(s->nleft == 0){
|
||||
free(s->fid);
|
||||
plumbfree(s->msg);
|
||||
free(s->pack);
|
||||
if(prevs)
|
||||
prevs->next = s->next;
|
||||
else
|
||||
d->sendq = s->next;
|
||||
free(s);
|
||||
}else
|
||||
prevs = s;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
hold(Plumbmsg *m, Dirtab *d)
|
||||
{
|
||||
Holdq *h, *q;
|
||||
|
||||
h = emalloc(sizeof(Holdq));
|
||||
h->msg = m;
|
||||
/* add to end of queue */
|
||||
if(d->holdq == nil)
|
||||
d->holdq = h;
|
||||
else{
|
||||
for(q=d->holdq; q->next!=nil; q=q->next)
|
||||
;
|
||||
q->next = h;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
queueheld(Dirtab *d)
|
||||
{
|
||||
Holdq *h;
|
||||
|
||||
while(d->holdq != nil){
|
||||
h = d->holdq;
|
||||
d->holdq = h->next;
|
||||
queuesend(d, h->msg);
|
||||
/* no need to drain queue because we know no-one is reading yet */
|
||||
free(h);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
dispose(Fcall *t, uchar *buf, Plumbmsg *m, Ruleset *rs, Exec *e)
|
||||
{
|
||||
int i;
|
||||
char *err;
|
||||
|
||||
qlock(&queue);
|
||||
err = nil;
|
||||
if(m->dst==nil || m->dst[0]=='\0'){
|
||||
err = Enoport;
|
||||
if(rs != nil)
|
||||
err = startup(rs, e);
|
||||
plumbfree(m);
|
||||
}else
|
||||
for(i=NQID; i<ndir; i++)
|
||||
if(strcmp(m->dst, dir[i].name) == 0){
|
||||
if(dir[i].nopen == 0){
|
||||
err = startup(rs, e);
|
||||
if(e!=nil && e->holdforclient)
|
||||
hold(m, &dir[i]);
|
||||
else
|
||||
plumbfree(m);
|
||||
}else{
|
||||
queuesend(&dir[i], m);
|
||||
drainqueue(&dir[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
freeexec(e);
|
||||
qunlock(&queue);
|
||||
fsysrespond(t, buf, err);
|
||||
free(t);
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysversion(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
if(t->msize < 256){
|
||||
fsysrespond(t, buf, "version: message size too small");
|
||||
return t;
|
||||
}
|
||||
if(t->msize < messagesize)
|
||||
messagesize = t->msize;
|
||||
t->msize = messagesize;
|
||||
if(strncmp(t->version, "9P2000", 6) != 0){
|
||||
fsysrespond(t, buf, "unrecognized 9P version");
|
||||
return t;
|
||||
}
|
||||
t->version = "9P2000";
|
||||
fsysrespond(t, buf, nil);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysauth(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
fsysrespond(t, buf, "plumber: authentication not required");
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysattach(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
Fcall out;
|
||||
|
||||
if(strcmp(t->uname, user) != 0){
|
||||
fsysrespond(&out, buf, Eperm);
|
||||
return t;
|
||||
}
|
||||
f->busy = 1;
|
||||
f->open = 0;
|
||||
f->qid.type = QTDIR;
|
||||
f->qid.path = Qdir;
|
||||
f->qid.vers = 0;
|
||||
f->dir = dir;
|
||||
memset(&out, 0, sizeof(Fcall));
|
||||
out.type = t->type;
|
||||
out.tag = t->tag;
|
||||
out.fid = f->fid;
|
||||
out.qid = f->qid;
|
||||
fsysrespond(&out, buf, nil);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysflush(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
int i;
|
||||
|
||||
qlock(&queue);
|
||||
for(i=NQID; i<ndir; i++)
|
||||
flushqueue(&dir[i], t->oldtag);
|
||||
qunlock(&queue);
|
||||
fsysrespond(t, buf, nil);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsyswalk(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
Fcall out;
|
||||
Fid *nf;
|
||||
ulong path;
|
||||
Dirtab *d, *dir;
|
||||
Qid q;
|
||||
int i;
|
||||
uchar type;
|
||||
char *err;
|
||||
|
||||
if(f->open){
|
||||
fsysrespond(t, buf, "clone of an open fid");
|
||||
return t;
|
||||
}
|
||||
|
||||
nf = nil;
|
||||
if(t->fid != t->newfid){
|
||||
nf = newfid(t->newfid);
|
||||
if(nf->busy){
|
||||
fsysrespond(t, buf, "clone to a busy fid");
|
||||
return t;
|
||||
}
|
||||
nf->busy = 1;
|
||||
nf->open = 0;
|
||||
nf->dir = f->dir;
|
||||
nf->qid = f->qid;
|
||||
f = nf; /* walk f */
|
||||
}
|
||||
|
||||
out.nwqid = 0;
|
||||
err = nil;
|
||||
dir = f->dir;
|
||||
q = f->qid;
|
||||
|
||||
if(t->nwname > 0){
|
||||
for(i=0; i<t->nwname; i++){
|
||||
if((q.type & QTDIR) == 0){
|
||||
err = Enotdir;
|
||||
break;
|
||||
}
|
||||
if(strcmp(t->wname[i], "..") == 0){
|
||||
type = QTDIR;
|
||||
path = Qdir;
|
||||
Accept:
|
||||
q.type = type;
|
||||
q.vers = 0;
|
||||
q.path = path;
|
||||
out.wqid[out.nwqid++] = q;
|
||||
continue;
|
||||
}
|
||||
d = dir;
|
||||
d++; /* skip '.' */
|
||||
for(; d->name; d++)
|
||||
if(strcmp(t->wname[i], d->name) == 0){
|
||||
type = d->type;
|
||||
path = d->qid;
|
||||
dir = d;
|
||||
goto Accept;
|
||||
}
|
||||
err = Enoexist;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
out.type = t->type;
|
||||
out.tag = t->tag;
|
||||
if(err!=nil || out.nwqid<t->nwname){
|
||||
if(nf)
|
||||
nf->busy = 0;
|
||||
}else if(out.nwqid == t->nwname){
|
||||
f->qid = q;
|
||||
f->dir = dir;
|
||||
}
|
||||
|
||||
fsysrespond(&out, buf, err);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysopen(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
int m, clearrules, mode;
|
||||
|
||||
clearrules = 0;
|
||||
if(t->mode & OTRUNC){
|
||||
if(f->qid.path != Qrules)
|
||||
goto Deny;
|
||||
clearrules = 1;
|
||||
}
|
||||
/* can't truncate anything, so just disregard */
|
||||
mode = t->mode & ~(OTRUNC|OCEXEC);
|
||||
/* can't execute or remove anything */
|
||||
if(mode==OEXEC || (mode&ORCLOSE))
|
||||
goto Deny;
|
||||
switch(mode){
|
||||
default:
|
||||
goto Deny;
|
||||
case OREAD:
|
||||
m = 0400;
|
||||
break;
|
||||
case OWRITE:
|
||||
m = 0200;
|
||||
break;
|
||||
case ORDWR:
|
||||
m = 0600;
|
||||
break;
|
||||
}
|
||||
if(((f->dir->perm&~(DMDIR|DMAPPEND))&m) != m)
|
||||
goto Deny;
|
||||
if(f->qid.path==Qrules && (mode==OWRITE || mode==ORDWR)){
|
||||
lock(&rulesref);
|
||||
if(rulesref.ref++ != 0){
|
||||
rulesref.ref--;
|
||||
unlock(&rulesref);
|
||||
fsysrespond(t, buf, Einuse);
|
||||
return t;
|
||||
}
|
||||
unlock(&rulesref);
|
||||
}
|
||||
if(clearrules){
|
||||
writerules(nil, 0);
|
||||
rules[0] = nil;
|
||||
}
|
||||
t->qid = f->qid;
|
||||
t->iounit = 0;
|
||||
qlock(&queue);
|
||||
f->mode = mode;
|
||||
f->open = 1;
|
||||
f->dir->nopen++;
|
||||
f->nextopen = f->dir->fopen;
|
||||
f->dir->fopen = f;
|
||||
queueheld(f->dir);
|
||||
qunlock(&queue);
|
||||
fsysrespond(t, buf, nil);
|
||||
return t;
|
||||
|
||||
Deny:
|
||||
fsysrespond(t, buf, Eperm);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsyscreate(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
fsysrespond(t, buf, Eperm);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysreadrules(Fcall *t, uchar *buf)
|
||||
{
|
||||
char *p;
|
||||
int n;
|
||||
|
||||
p = printrules();
|
||||
n = strlen(p);
|
||||
t->data = p;
|
||||
if(t->offset >= n)
|
||||
t->count = 0;
|
||||
else{
|
||||
t->data = p+t->offset;
|
||||
if(t->offset+t->count > n)
|
||||
t->count = n-t->offset;
|
||||
}
|
||||
fsysrespond(t, buf, nil);
|
||||
free(p);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysread(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
uchar *b;
|
||||
int i, n, o, e;
|
||||
uint len;
|
||||
Dirtab *d;
|
||||
uint clock;
|
||||
|
||||
if(f->qid.path != Qdir){
|
||||
if(f->qid.path == Qrules)
|
||||
return fsysreadrules(t, buf);
|
||||
/* read from port */
|
||||
if(f->qid.path < NQID){
|
||||
fsysrespond(t, buf, "internal error: unknown read port");
|
||||
return t;
|
||||
}
|
||||
qlock(&queue);
|
||||
queueread(f->dir, t, buf, f);
|
||||
drainqueue(f->dir);
|
||||
qunlock(&queue);
|
||||
return nil;
|
||||
}
|
||||
o = t->offset;
|
||||
e = t->offset+t->count;
|
||||
clock = getclock();
|
||||
b = malloc(messagesize-IOHDRSZ);
|
||||
if(b == nil){
|
||||
fsysrespond(t, buf, Enomem);
|
||||
return t;
|
||||
}
|
||||
n = 0;
|
||||
d = dir;
|
||||
d++; /* first entry is '.' */
|
||||
for(i=0; d->name!=nil && i<e; i+=len){
|
||||
len = dostat(d, b+n, messagesize-IOHDRSZ-n, clock);
|
||||
if(len <= BIT16SZ)
|
||||
break;
|
||||
if(i >= o)
|
||||
n += len;
|
||||
d++;
|
||||
}
|
||||
t->data = (char*)b;
|
||||
t->count = n;
|
||||
fsysrespond(t, buf, nil);
|
||||
free(b);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsyswrite(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
Plumbmsg *m;
|
||||
int i, n;
|
||||
long count;
|
||||
char *data;
|
||||
Exec *e;
|
||||
|
||||
switch((int)f->qid.path){
|
||||
case Qdir:
|
||||
fsysrespond(t, buf, Eisdir);
|
||||
return t;
|
||||
case Qrules:
|
||||
clock = getclock();
|
||||
fsysrespond(t, buf, writerules(t->data, t->count));
|
||||
return t;
|
||||
case Qsend:
|
||||
if(f->offset == 0){
|
||||
data = t->data;
|
||||
count = t->count;
|
||||
}else{
|
||||
/* partial message already assembled */
|
||||
f->writebuf = erealloc(f->writebuf, f->offset + t->count);
|
||||
memmove(f->writebuf+f->offset, t->data, t->count);
|
||||
data = f->writebuf;
|
||||
count = f->offset+t->count;
|
||||
}
|
||||
m = plumbunpackpartial(data, count, &n);
|
||||
if(m == nil){
|
||||
if(n == 0){
|
||||
f->offset = 0;
|
||||
free(f->writebuf);
|
||||
f->writebuf = nil;
|
||||
fsysrespond(t, buf, Ebadmsg);
|
||||
return t;
|
||||
}
|
||||
/* can read more... */
|
||||
if(f->offset == 0){
|
||||
f->writebuf = emalloc(t->count);
|
||||
memmove(f->writebuf, t->data, t->count);
|
||||
}
|
||||
/* else buffer has already been grown */
|
||||
f->offset += t->count;
|
||||
fsysrespond(t, buf, nil);
|
||||
return t;
|
||||
}
|
||||
/* release partial buffer */
|
||||
f->offset = 0;
|
||||
free(f->writebuf);
|
||||
f->writebuf = nil;
|
||||
for(i=0; rules[i]; i++)
|
||||
if((e=matchruleset(m, rules[i])) != nil){
|
||||
dispose(t, buf, m, rules[i], e);
|
||||
return nil;
|
||||
}
|
||||
if(m->dst != nil){
|
||||
dispose(t, buf, m, nil, nil);
|
||||
return nil;
|
||||
}
|
||||
fsysrespond(t, buf, "no matching plumb rule");
|
||||
return t;
|
||||
}
|
||||
fsysrespond(t, buf, "internal error: write to unknown file");
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysstat(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
t->stat = emalloc(messagesize-IOHDRSZ);
|
||||
t->nstat = dostat(f->dir, t->stat, messagesize-IOHDRSZ, clock);
|
||||
fsysrespond(t, buf, nil);
|
||||
free(t->stat);
|
||||
t->stat = nil;
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsyswstat(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
fsysrespond(t, buf, Eperm);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysremove(Fcall *t, uchar *buf, Fid*)
|
||||
{
|
||||
fsysrespond(t, buf, Eperm);
|
||||
return t;
|
||||
}
|
||||
|
||||
static Fcall*
|
||||
fsysclunk(Fcall *t, uchar *buf, Fid *f)
|
||||
{
|
||||
Fid *prev, *p;
|
||||
Dirtab *d;
|
||||
|
||||
qlock(&queue);
|
||||
if(f->open){
|
||||
d = f->dir;
|
||||
d->nopen--;
|
||||
if(d->qid==Qrules && (f->mode==OWRITE || f->mode==ORDWR)){
|
||||
/*
|
||||
* just to be sure last rule is parsed; error messages will be lost, though,
|
||||
* unless last write ended with a blank line
|
||||
*/
|
||||
writerules(nil, 0);
|
||||
lock(&rulesref);
|
||||
rulesref.ref--;
|
||||
unlock(&rulesref);
|
||||
}
|
||||
prev = nil;
|
||||
for(p=d->fopen; p; p=p->nextopen){
|
||||
if(p == f){
|
||||
if(prev)
|
||||
prev->nextopen = f->nextopen;
|
||||
else
|
||||
d->fopen = f->nextopen;
|
||||
removesenders(d, f);
|
||||
break;
|
||||
}
|
||||
prev = p;
|
||||
}
|
||||
}
|
||||
f->busy = 0;
|
||||
f->open = 0;
|
||||
f->offset = 0;
|
||||
if(f->writebuf != nil){
|
||||
free(f->writebuf);
|
||||
f->writebuf = nil;
|
||||
}
|
||||
qunlock(&queue);
|
||||
fsysrespond(t, buf, nil);
|
||||
return t;
|
||||
}
|
||||
463
src/cmd/plumb/match.c
Normal file
463
src/cmd/plumb/match.c
Normal file
|
|
@ -0,0 +1,463 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <regexp.h>
|
||||
#include <thread.h>
|
||||
#include <plumb.h>
|
||||
#include "plumber.h"
|
||||
|
||||
static char*
|
||||
nonnil(char *s)
|
||||
{
|
||||
if(s == nil)
|
||||
return "";
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
verbis(int obj, Plumbmsg *m, Rule *r)
|
||||
{
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'is' object %d\n", obj);
|
||||
break;
|
||||
case OData:
|
||||
return strcmp(m->data, r->qarg) == 0;
|
||||
case ODst:
|
||||
return strcmp(m->dst, r->qarg) == 0;
|
||||
case OType:
|
||||
return strcmp(m->type, r->qarg) == 0;
|
||||
case OWdir:
|
||||
return strcmp(m->wdir, r->qarg) == 0;
|
||||
case OSrc:
|
||||
return strcmp(m->src, r->qarg) == 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
setvar(Resub rs[10], char *match[10])
|
||||
{
|
||||
int i, n;
|
||||
|
||||
for(i=0; i<10; i++){
|
||||
free(match[i]);
|
||||
match[i] = nil;
|
||||
}
|
||||
for(i=0; i<10 && rs[i].sp!=nil; i++){
|
||||
n = rs[i].ep-rs[i].sp;
|
||||
match[i] = emalloc(n+1);
|
||||
memmove(match[i], rs[i].sp, n);
|
||||
match[i][n] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
clickmatch(Reprog *re, char *text, Resub rs[10], int click)
|
||||
{
|
||||
char *clickp;
|
||||
int i, w;
|
||||
Rune r;
|
||||
|
||||
/* click is in characters, not bytes */
|
||||
for(i=0; i<click && text[i]!='\0'; i+=w)
|
||||
w = chartorune(&r, text+i);
|
||||
clickp = text+i;
|
||||
for(i=0; i<=click; i++){
|
||||
memset(rs, 0, 10*sizeof(Resub));
|
||||
if(regexec(re, text+i, rs, 10))
|
||||
if(rs[0].sp<=clickp && clickp<=rs[0].ep)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
verbmatches(int obj, Plumbmsg *m, Rule *r, Exec *e)
|
||||
{
|
||||
Resub rs[10];
|
||||
char *clickval, *alltext;
|
||||
int p0, p1, ntext;
|
||||
|
||||
memset(rs, 0, sizeof rs);
|
||||
ntext = -1;
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'matches' object %d\n", obj);
|
||||
break;
|
||||
case OData:
|
||||
clickval = plumblookup(m->attr, "click");
|
||||
if(clickval == nil){
|
||||
alltext = m->data;
|
||||
ntext = m->ndata;
|
||||
goto caseAlltext;
|
||||
}
|
||||
if(!clickmatch(r->regex, m->data, rs, atoi(clickval)))
|
||||
break;
|
||||
p0 = rs[0].sp - m->data;
|
||||
p1 = rs[0].ep - m->data;
|
||||
if(e->p0 >=0 && !(p0==e->p0 && p1==e->p1))
|
||||
break;
|
||||
e->clearclick = 1;
|
||||
e->setdata = 1;
|
||||
e->p0 = p0;
|
||||
e->p1 = p1;
|
||||
setvar(rs, e->match);
|
||||
return 1;
|
||||
case ODst:
|
||||
alltext = m->dst;
|
||||
goto caseAlltext;
|
||||
case OType:
|
||||
alltext = m->type;
|
||||
goto caseAlltext;
|
||||
case OWdir:
|
||||
alltext = m->wdir;
|
||||
goto caseAlltext;
|
||||
case OSrc:
|
||||
alltext = m->src;
|
||||
/* fall through */
|
||||
caseAlltext:
|
||||
/* must match full text */
|
||||
if(ntext < 0)
|
||||
ntext = strlen(alltext);
|
||||
if(!regexec(r->regex, alltext, rs, 10) || rs[0].sp!=alltext || rs[0].ep!=alltext+ntext)
|
||||
break;
|
||||
setvar(rs, e->match);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
isfile(char *file, ulong maskon, ulong maskoff)
|
||||
{
|
||||
Dir *d;
|
||||
int mode;
|
||||
|
||||
d = dirstat(file);
|
||||
if(d == nil)
|
||||
return 0;
|
||||
mode = d->mode;
|
||||
free(d);
|
||||
if((mode & maskon) == 0)
|
||||
return 0;
|
||||
if(mode & maskoff)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
char*
|
||||
absolute(char *dir, char *file)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if(file[0] == '/')
|
||||
return estrdup(file);
|
||||
p = emalloc(strlen(dir)+1+strlen(file)+1);
|
||||
sprint(p, "%s/%s", dir, file);
|
||||
return cleanname(p);
|
||||
}
|
||||
|
||||
int
|
||||
verbisfile(int obj, Plumbmsg *m, Rule *r, Exec *e, ulong maskon, ulong maskoff, char **var)
|
||||
{
|
||||
char *file;
|
||||
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'isfile' object %d\n", obj);
|
||||
break;
|
||||
case OArg:
|
||||
file = absolute(m->wdir, expand(e, r->arg, nil));
|
||||
if(isfile(file, maskon, maskoff)){
|
||||
*var = file;
|
||||
return 1;
|
||||
}
|
||||
free(file);
|
||||
break;
|
||||
case OData:
|
||||
case OWdir:
|
||||
file = absolute(m->wdir, obj==OData? m->data : m->wdir);
|
||||
if(isfile(file, maskon, maskoff)){
|
||||
*var = file;
|
||||
return 1;
|
||||
}
|
||||
free(file);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
verbset(int obj, Plumbmsg *m, Rule *r, Exec *e)
|
||||
{
|
||||
char *new;
|
||||
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'is' object %d\n", obj);
|
||||
break;
|
||||
case OData:
|
||||
new = estrdup(expand(e, r->arg, nil));
|
||||
m->ndata = strlen(new);
|
||||
free(m->data);
|
||||
m->data = new;
|
||||
e->p0 = -1;
|
||||
e->p1 = -1;
|
||||
e->setdata = 0;
|
||||
return 1;
|
||||
case ODst:
|
||||
new = estrdup(expand(e, r->arg, nil));
|
||||
free(m->dst);
|
||||
m->dst = new;
|
||||
return 1;
|
||||
case OType:
|
||||
new = estrdup(expand(e, r->arg, nil));
|
||||
free(m->type);
|
||||
m->type = new;
|
||||
return 1;
|
||||
case OWdir:
|
||||
new = estrdup(expand(e, r->arg, nil));
|
||||
free(m->wdir);
|
||||
m->wdir = new;
|
||||
return 1;
|
||||
case OSrc:
|
||||
new = estrdup(expand(e, r->arg, nil));
|
||||
free(m->src);
|
||||
m->src = new;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
verbadd(int obj, Plumbmsg *m, Rule *r, Exec *e)
|
||||
{
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'add' object %d\n", obj);
|
||||
break;
|
||||
case OAttr:
|
||||
m->attr = plumbaddattr(m->attr, plumbunpackattr(expand(e, r->arg, nil)));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
verbdelete(int obj, Plumbmsg *m, Rule *r, Exec *e)
|
||||
{
|
||||
char *a;
|
||||
|
||||
switch(obj){
|
||||
default:
|
||||
fprint(2, "unimplemented 'delete' object %d\n", obj);
|
||||
break;
|
||||
case OAttr:
|
||||
a = expand(e, r->arg, nil);
|
||||
if(plumblookup(m->attr, a) == nil)
|
||||
break;
|
||||
m->attr = plumbdelattr(m->attr, a);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
matchpat(Plumbmsg *m, Exec *e, Rule *r)
|
||||
{
|
||||
switch(r->verb){
|
||||
default:
|
||||
fprint(2, "unimplemented verb %d\n", r->verb);
|
||||
break;
|
||||
case VAdd:
|
||||
return verbadd(r->obj, m, r, e);
|
||||
case VDelete:
|
||||
return verbdelete(r->obj, m, r, e);
|
||||
case VIs:
|
||||
return verbis(r->obj, m, r);
|
||||
case VIsdir:
|
||||
return verbisfile(r->obj, m, r, e, DMDIR, 0, &e->dir);
|
||||
case VIsfile:
|
||||
return verbisfile(r->obj, m, r, e, ~DMDIR, DMDIR, &e->file);
|
||||
case VMatches:
|
||||
return verbmatches(r->obj, m, r, e);
|
||||
case VSet:
|
||||
verbset(r->obj, m, r, e);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
freeexec(Exec *exec)
|
||||
{
|
||||
int i;
|
||||
|
||||
if(exec == nil)
|
||||
return;
|
||||
free(exec->dir);
|
||||
free(exec->file);
|
||||
for(i=0; i<10; i++)
|
||||
free(exec->match[i]);
|
||||
free(exec);
|
||||
}
|
||||
|
||||
Exec*
|
||||
newexec(Plumbmsg *m)
|
||||
{
|
||||
Exec *exec;
|
||||
|
||||
exec = emalloc(sizeof(Exec));
|
||||
exec->msg = m;
|
||||
exec->p0 = -1;
|
||||
exec->p1 = -1;
|
||||
return exec;
|
||||
}
|
||||
|
||||
void
|
||||
rewrite(Plumbmsg *m, Exec *e)
|
||||
{
|
||||
Plumbattr *a, *prev;
|
||||
|
||||
if(e->clearclick){
|
||||
prev = nil;
|
||||
for(a=m->attr; a!=nil; a=a->next){
|
||||
if(strcmp(a->name, "click") == 0){
|
||||
if(prev == nil)
|
||||
m->attr = a->next;
|
||||
else
|
||||
prev->next = a->next;
|
||||
free(a->name);
|
||||
free(a->value);
|
||||
free(a);
|
||||
break;
|
||||
}
|
||||
prev = a;
|
||||
}
|
||||
if(e->setdata){
|
||||
free(m->data);
|
||||
m->data = estrdup(expand(e, "$0", nil));
|
||||
m->ndata = strlen(m->data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char**
|
||||
buildargv(char *s, Exec *e)
|
||||
{
|
||||
char **av;
|
||||
int ac;
|
||||
|
||||
ac = 0;
|
||||
av = nil;
|
||||
for(;;){
|
||||
av = erealloc(av, (ac+1) * sizeof(char*));
|
||||
av[ac] = nil;
|
||||
while(*s==' ' || *s=='\t')
|
||||
s++;
|
||||
if(*s == '\0')
|
||||
break;
|
||||
av[ac++] = estrdup(expand(e, s, &s));
|
||||
}
|
||||
return av;
|
||||
}
|
||||
|
||||
Exec*
|
||||
matchruleset(Plumbmsg *m, Ruleset *rs)
|
||||
{
|
||||
int i;
|
||||
Exec *exec;
|
||||
|
||||
if(m->dst!=nil && m->dst[0]!='\0' && rs->port!=nil && strcmp(m->dst, rs->port)!=0)
|
||||
return nil;
|
||||
exec = newexec(m);
|
||||
for(i=0; i<rs->npat; i++)
|
||||
if(!matchpat(m, exec, rs->pat[i])){
|
||||
freeexec(exec);
|
||||
return nil;
|
||||
}
|
||||
if(rs->port!=nil && (m->dst==nil || m->dst[0]=='\0')){
|
||||
free(m->dst);
|
||||
m->dst = estrdup(rs->port);
|
||||
}
|
||||
rewrite(m, exec);
|
||||
return exec;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
NARGS = 100,
|
||||
NARGCHAR = 8*1024,
|
||||
EXECSTACK = 4096+(NARGS+1)*sizeof(char*)+NARGCHAR
|
||||
};
|
||||
|
||||
/* copy argv to stack and free the incoming strings, so we don't leak argument vectors */
|
||||
void
|
||||
stackargv(char **inargv, char *argv[NARGS+1], char args[NARGCHAR])
|
||||
{
|
||||
int i, n;
|
||||
char *s, *a;
|
||||
|
||||
s = args;
|
||||
for(i=0; i<NARGS; i++){
|
||||
a = inargv[i];
|
||||
if(a == nil)
|
||||
break;
|
||||
n = strlen(a)+1;
|
||||
if((s-args)+n >= NARGCHAR) /* too many characters */
|
||||
break;
|
||||
argv[i] = s;
|
||||
memmove(s, a, n);
|
||||
s += n;
|
||||
free(a);
|
||||
}
|
||||
argv[i] = nil;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
execproc(void *v)
|
||||
{
|
||||
char **av;
|
||||
char buf[1024], *args[NARGS+1], argc[NARGCHAR];
|
||||
|
||||
rfork(RFFDG);
|
||||
close(0);
|
||||
open("/dev/null", OREAD);
|
||||
av = v;
|
||||
stackargv(av, args, argc);
|
||||
free(av);
|
||||
procexec(nil, args[0], args);
|
||||
if(args[0][0]!='/' && strncmp(args[0], "./", 2)!=0 && strncmp(args[0], "../", 3)!=0)
|
||||
snprint(buf, sizeof buf, "/bin/%s", args[0]);
|
||||
procexec(nil, buf, args);
|
||||
threadexits("can't exec");
|
||||
}
|
||||
|
||||
char*
|
||||
startup(Ruleset *rs, Exec *e)
|
||||
{
|
||||
char **argv;
|
||||
int i;
|
||||
|
||||
if(rs != nil)
|
||||
for(i=0; i<rs->nact; i++){
|
||||
if(rs->act[i]->verb == VStart)
|
||||
goto Found;
|
||||
if(rs->act[i]->verb == VClient){
|
||||
if(e->msg->dst==nil || e->msg->dst[0]=='\0')
|
||||
return "no port for \"client\" rule";
|
||||
e->holdforclient = 1;
|
||||
goto Found;
|
||||
}
|
||||
}
|
||||
return "no start action for plumb message";
|
||||
|
||||
Found:
|
||||
argv = buildargv(rs->act[i]->arg, e);
|
||||
if(argv[0] == nil)
|
||||
return "empty argument list";
|
||||
proccreate(execproc, argv, EXECSTACK);
|
||||
return nil;
|
||||
}
|
||||
20
src/cmd/plumb/mkfile
Normal file
20
src/cmd/plumb/mkfile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
</$objtype/mkfile
|
||||
|
||||
TARG=plumber plumb
|
||||
|
||||
|
||||
BIN=/$objtype/bin
|
||||
</sys/src/cmd/mkmany
|
||||
|
||||
PLUMBER=plumber.$O fsys.$O match.$O rules.$O
|
||||
PLUMB=plumb.$O
|
||||
|
||||
$PLUMBER: $HFILES plumber.h
|
||||
$PLUMB: $HFILES
|
||||
|
||||
$O.plumb: $PLUMB
|
||||
$O.plumber: $PLUMBER
|
||||
|
||||
syms:V:
|
||||
8c -a plumber.c >syms
|
||||
8c -aa fsys.c match.c rules.c >>syms
|
||||
119
src/cmd/plumb/plumb.c
Normal file
119
src/cmd/plumb/plumb.c
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <plumb.h>
|
||||
|
||||
char *plumbfile = nil;
|
||||
Plumbmsg m;
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: plumb [-p plumbfile] [-a 'attr=value ...'] [-s src] [-d dst] [-t type] [-w wdir] -i | data1\n");
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
gather(void)
|
||||
{
|
||||
char buf[8192];
|
||||
int n;
|
||||
|
||||
m.ndata = 0;
|
||||
m.data = nil;
|
||||
while((n = read(0, buf, sizeof buf)) > 0){
|
||||
m.data = realloc(m.data, m.ndata+n);
|
||||
if(m.data == nil){
|
||||
fprint(2, "plumb: alloc failed: %r\n");
|
||||
exits("alloc");
|
||||
}
|
||||
memmove(m.data+m.ndata, buf, n);
|
||||
m.ndata += n;
|
||||
}
|
||||
if(n < 0){
|
||||
fprint(2, "plumb: i/o error on input: %r\n");
|
||||
exits("read");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
char buf[1024], *p;
|
||||
int fd, i, input;
|
||||
|
||||
input = 0;
|
||||
m.src = "plumb";
|
||||
m.dst = nil;
|
||||
m.wdir = getwd(buf, sizeof buf);
|
||||
m.type = "text";
|
||||
m.attr = nil;
|
||||
ARGBEGIN{
|
||||
case 'a':
|
||||
p = ARGF();
|
||||
if(p == nil)
|
||||
usage();
|
||||
m.attr = plumbaddattr(m.attr, plumbunpackattr(p));
|
||||
break;
|
||||
case 'd':
|
||||
m.dst = ARGF();
|
||||
if(m.dst == nil)
|
||||
usage();
|
||||
break;
|
||||
case 'i':
|
||||
input++;
|
||||
break;
|
||||
case 't':
|
||||
case 'k': /* for backwards compatibility */
|
||||
m.type = ARGF();
|
||||
if(m.type == nil)
|
||||
usage();
|
||||
break;
|
||||
case 'p':
|
||||
plumbfile = ARGF();
|
||||
if(plumbfile == nil)
|
||||
usage();
|
||||
break;
|
||||
case 's':
|
||||
m.src = ARGF();
|
||||
if(m.src == nil)
|
||||
usage();
|
||||
break;
|
||||
case 'w':
|
||||
m.wdir = ARGF();
|
||||
if(m.wdir == nil)
|
||||
usage();
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
if((input && argc>0) || (!input && argc<1))
|
||||
usage();
|
||||
if(plumbfile != nil)
|
||||
fd = open(plumbfile, OWRITE);
|
||||
else
|
||||
fd = plumbopen("send", OWRITE);
|
||||
if(fd < 0){
|
||||
fprint(2, "plumb: can't open plumb file: %r\n");
|
||||
exits("open");
|
||||
}
|
||||
if(input){
|
||||
gather();
|
||||
if(plumblookup(m.attr, "action") == nil)
|
||||
m.attr = plumbaddattr(m.attr, plumbunpackattr("action=showdata"));
|
||||
if(plumbsend(fd, &m) < 0){
|
||||
fprint(2, "plumb: can't send message: %r\n");
|
||||
exits("error");
|
||||
}
|
||||
exits(nil);
|
||||
}
|
||||
for(i=0; i<argc; i++){
|
||||
if(input == 0){
|
||||
m.data = argv[i];
|
||||
m.ndata = -1;
|
||||
}
|
||||
if(plumbsend(fd, &m) < 0){
|
||||
fprint(2, "plumb: can't send message: %r\n");
|
||||
exits("error");
|
||||
}
|
||||
}
|
||||
exits(nil);
|
||||
}
|
||||
147
src/cmd/plumb/plumber.c
Normal file
147
src/cmd/plumb/plumber.c
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <regexp.h>
|
||||
#include <thread.h>
|
||||
#include <plumb.h>
|
||||
#include <auth.h>
|
||||
#include <fcall.h>
|
||||
#include "plumber.h"
|
||||
|
||||
char *plumbfile;
|
||||
char *user;
|
||||
char *home;
|
||||
char *progname;
|
||||
Ruleset **rules;
|
||||
int printerrors=1;
|
||||
jmp_buf parsejmp;
|
||||
char *lasterror;
|
||||
|
||||
void
|
||||
makeports(Ruleset *rules[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; rules[i]; i++)
|
||||
addport(rules[i]->port);
|
||||
}
|
||||
|
||||
void
|
||||
mainproc(void *v)
|
||||
{
|
||||
Channel *c;
|
||||
|
||||
c = v;
|
||||
printerrors = 0;
|
||||
makeports(rules);
|
||||
startfsys();
|
||||
sendp(c, nil);
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char *argv[])
|
||||
{
|
||||
char buf[512];
|
||||
int fd;
|
||||
Channel *c;
|
||||
|
||||
progname = "plumber";
|
||||
|
||||
ARGBEGIN{
|
||||
case 'p':
|
||||
plumbfile = ARGF();
|
||||
break;
|
||||
}ARGEND
|
||||
|
||||
user = getenv("user");
|
||||
home = getenv("home");
|
||||
if(user==nil || home==nil)
|
||||
error("can't initialize $user or $home: %r");
|
||||
if(plumbfile == nil){
|
||||
sprint(buf, "%s/lib/plumbing", home);
|
||||
plumbfile = estrdup(buf);
|
||||
}
|
||||
|
||||
fd = open(plumbfile, OREAD);
|
||||
if(fd < 0)
|
||||
error("can't open rules file %s: %r", plumbfile);
|
||||
if(setjmp(parsejmp))
|
||||
error("parse error");
|
||||
|
||||
rules = readrules(plumbfile, fd);
|
||||
close(fd);
|
||||
|
||||
/*
|
||||
* Start all processes and threads from other proc
|
||||
* so we (main pid) can return to user.
|
||||
*/
|
||||
c = chancreate(sizeof(void*), 0);
|
||||
proccreate(mainproc, c, 8192);
|
||||
recvp(c);
|
||||
chanfree(c);
|
||||
threadexits(nil);
|
||||
}
|
||||
|
||||
void
|
||||
error(char *fmt, ...)
|
||||
{
|
||||
char buf[512];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vseprint(buf, buf+sizeof buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
fprint(2, "%s: %s\n", progname, buf);
|
||||
threadexitsall("error");
|
||||
}
|
||||
|
||||
void
|
||||
parseerror(char *fmt, ...)
|
||||
{
|
||||
char buf[512];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vseprint(buf, buf+sizeof buf, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(printerrors){
|
||||
printinputstack();
|
||||
fprint(2, "%s\n", buf);
|
||||
}
|
||||
do; while(popinput());
|
||||
lasterror = estrdup(buf);
|
||||
longjmp(parsejmp, 1);
|
||||
}
|
||||
|
||||
void*
|
||||
emalloc(long n)
|
||||
{
|
||||
void *p;
|
||||
|
||||
p = malloc(n);
|
||||
if(p == nil)
|
||||
error("malloc failed: %r");
|
||||
memset(p, 0, n);
|
||||
return p;
|
||||
}
|
||||
|
||||
void*
|
||||
erealloc(void *p, long n)
|
||||
{
|
||||
p = realloc(p, n);
|
||||
if(p == nil)
|
||||
error("realloc failed: %r");
|
||||
return p;
|
||||
}
|
||||
|
||||
char*
|
||||
estrdup(char *s)
|
||||
{
|
||||
char *t;
|
||||
|
||||
t = strdup(s);
|
||||
if(t == nil)
|
||||
error("estrdup failed: %r");
|
||||
return t;
|
||||
}
|
||||
93
src/cmd/plumb/plumber.h
Normal file
93
src/cmd/plumb/plumber.h
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
typedef struct Exec Exec;
|
||||
typedef struct Rule Rule;
|
||||
typedef struct Ruleset Ruleset;
|
||||
|
||||
/*
|
||||
* Object
|
||||
*/
|
||||
enum
|
||||
{
|
||||
OArg,
|
||||
OAttr,
|
||||
OData,
|
||||
ODst,
|
||||
OPlumb,
|
||||
OSrc,
|
||||
OType,
|
||||
OWdir,
|
||||
};
|
||||
|
||||
/*
|
||||
* Verbs
|
||||
*/
|
||||
enum
|
||||
{
|
||||
VAdd, /* apply to OAttr only */
|
||||
VClient,
|
||||
VDelete, /* apply to OAttr only */
|
||||
VIs,
|
||||
VIsdir,
|
||||
VIsfile,
|
||||
VMatches,
|
||||
VSet,
|
||||
VStart,
|
||||
VTo,
|
||||
};
|
||||
|
||||
struct Rule
|
||||
{
|
||||
int obj;
|
||||
int verb;
|
||||
char *arg; /* unparsed string of all arguments */
|
||||
char *qarg; /* quote-processed arg string */
|
||||
Reprog *regex;
|
||||
};
|
||||
|
||||
struct Ruleset
|
||||
{
|
||||
int npat;
|
||||
int nact;
|
||||
Rule **pat;
|
||||
Rule **act;
|
||||
char *port;
|
||||
};
|
||||
|
||||
struct Exec
|
||||
{
|
||||
Plumbmsg *msg;
|
||||
char *match[10];
|
||||
int p0; /* begin and end of match */
|
||||
int p1;
|
||||
int clearclick; /* click was expanded; remove attribute */
|
||||
int setdata; /* data should be set to $0 */
|
||||
int holdforclient; /* exec'ing client; keep message until port is opened */
|
||||
/* values of $variables */
|
||||
char *file;
|
||||
char *dir;
|
||||
};
|
||||
|
||||
void parseerror(char*, ...);
|
||||
void error(char*, ...);
|
||||
void* emalloc(long);
|
||||
void* erealloc(void*, long);
|
||||
char* estrdup(char*);
|
||||
Ruleset** readrules(char*, int);
|
||||
void startfsys(void);
|
||||
Exec* matchruleset(Plumbmsg*, Ruleset*);
|
||||
void freeexec(Exec*);
|
||||
char* startup(Ruleset*, Exec*);
|
||||
char* printrules(void);
|
||||
void addport(char*);
|
||||
char* writerules(char*, int);
|
||||
char* expand(Exec*, char*, char**);
|
||||
void makeports(Ruleset*[]);
|
||||
void printinputstack(void);
|
||||
int popinput(void);
|
||||
|
||||
Ruleset **rules;
|
||||
char *user;
|
||||
char *home;
|
||||
jmp_buf parsejmp;
|
||||
char *lasterror;
|
||||
char **ports;
|
||||
int nports;
|
||||
779
src/cmd/plumb/rules.c
Normal file
779
src/cmd/plumb/rules.c
Normal file
|
|
@ -0,0 +1,779 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <bio.h>
|
||||
#include <regexp.h>
|
||||
#include <thread.h>
|
||||
#include <ctype.h>
|
||||
#include <plumb.h>
|
||||
#include "plumber.h"
|
||||
|
||||
typedef struct Input Input;
|
||||
typedef struct Var Var;
|
||||
|
||||
struct Input
|
||||
{
|
||||
char *file; /* name of file */
|
||||
Biobuf *fd; /* input buffer, if from real file */
|
||||
uchar *s; /* input string, if from /mnt/plumb/rules */
|
||||
uchar *end; /* end of input string */
|
||||
int lineno;
|
||||
Input *next; /* file to read after EOF on this one */
|
||||
};
|
||||
|
||||
struct Var
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
char *qvalue;
|
||||
};
|
||||
|
||||
static int parsing;
|
||||
static int nvars;
|
||||
static Var *vars;
|
||||
static Input *input;
|
||||
|
||||
static char ebuf[4096];
|
||||
|
||||
char *badports[] =
|
||||
{
|
||||
".",
|
||||
"..",
|
||||
"send",
|
||||
nil
|
||||
};
|
||||
|
||||
char *objects[] =
|
||||
{
|
||||
"arg",
|
||||
"attr",
|
||||
"data",
|
||||
"dst",
|
||||
"plumb",
|
||||
"src",
|
||||
"type",
|
||||
"wdir",
|
||||
nil
|
||||
};
|
||||
|
||||
char *verbs[] =
|
||||
{
|
||||
"add",
|
||||
"client",
|
||||
"delete",
|
||||
"is",
|
||||
"isdir",
|
||||
"isfile",
|
||||
"matches",
|
||||
"set",
|
||||
"start",
|
||||
"to",
|
||||
nil
|
||||
};
|
||||
|
||||
static void
|
||||
printinputstackrev(Input *in)
|
||||
{
|
||||
if(in == nil)
|
||||
return;
|
||||
printinputstackrev(in->next);
|
||||
fprint(2, "%s:%d: ", in->file, in->lineno);
|
||||
}
|
||||
|
||||
void
|
||||
printinputstack(void)
|
||||
{
|
||||
printinputstackrev(input);
|
||||
}
|
||||
|
||||
static void
|
||||
pushinput(char *name, int fd, uchar *str)
|
||||
{
|
||||
Input *in;
|
||||
int depth;
|
||||
|
||||
depth = 0;
|
||||
for(in=input; in; in=in->next)
|
||||
if(depth++ >= 10) /* prevent deep C stack in plumber and bad include structure */
|
||||
parseerror("include stack too deep; max 10");
|
||||
|
||||
in = emalloc(sizeof(Input));
|
||||
in->file = estrdup(name);
|
||||
in->next = input;
|
||||
input = in;
|
||||
if(str)
|
||||
in->s = str;
|
||||
else{
|
||||
in->fd = emalloc(sizeof(Biobuf));
|
||||
if(Binit(in->fd, fd, OREAD) < 0)
|
||||
parseerror("can't initialize Bio for rules file: %r");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int
|
||||
popinput(void)
|
||||
{
|
||||
Input *in;
|
||||
|
||||
in = input;
|
||||
if(in == nil)
|
||||
return 0;
|
||||
input = in->next;
|
||||
if(in->fd){
|
||||
Bterm(in->fd);
|
||||
free(in->fd);
|
||||
}
|
||||
free(in);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
getc(void)
|
||||
{
|
||||
if(input == nil)
|
||||
return Beof;
|
||||
if(input->fd)
|
||||
return Bgetc(input->fd);
|
||||
if(input->s < input->end)
|
||||
return *(input->s)++;
|
||||
return -1;
|
||||
}
|
||||
|
||||
char*
|
||||
getline(void)
|
||||
{
|
||||
static int n = 0;
|
||||
static char *s, *incl;
|
||||
int c, i;
|
||||
|
||||
i = 0;
|
||||
for(;;){
|
||||
c = getc();
|
||||
if(c < 0)
|
||||
return nil;
|
||||
if(i == n){
|
||||
n += 100;
|
||||
s = erealloc(s, n);
|
||||
}
|
||||
if(c<0 || c=='\0' || c=='\n')
|
||||
break;
|
||||
s[i++] = c;
|
||||
}
|
||||
s[i] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
lookup(char *s, char *tab[])
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; tab[i]!=nil; i++)
|
||||
if(strcmp(s, tab[i])==0)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
Var*
|
||||
lookupvariable(char *s, int n)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i=0; i<nvars; i++)
|
||||
if(n==strlen(vars[i].name) && memcmp(s, vars[i].name, n)==0)
|
||||
return vars+i;
|
||||
return nil;
|
||||
}
|
||||
|
||||
char*
|
||||
variable(char *s, int n)
|
||||
{
|
||||
Var *var;
|
||||
|
||||
var = lookupvariable(s, n);
|
||||
if(var)
|
||||
return var->qvalue;
|
||||
return nil;
|
||||
}
|
||||
|
||||
void
|
||||
setvariable(char *s, int n, char *val, char *qval)
|
||||
{
|
||||
Var *var;
|
||||
|
||||
var = lookupvariable(s, n);
|
||||
if(var){
|
||||
free(var->value);
|
||||
free(var->qvalue);
|
||||
}else{
|
||||
vars = erealloc(vars, (nvars+1)*sizeof(Var));
|
||||
var = vars+nvars++;
|
||||
var->name = emalloc(n+1);
|
||||
memmove(var->name, s, n);
|
||||
}
|
||||
var->value = estrdup(val);
|
||||
var->qvalue = estrdup(qval);
|
||||
}
|
||||
|
||||
static char*
|
||||
nonnil(char *s)
|
||||
{
|
||||
if(s == nil)
|
||||
return "";
|
||||
return s;
|
||||
}
|
||||
|
||||
static char*
|
||||
filename(Exec *e, char *name)
|
||||
{
|
||||
static char *buf; /* rock to hold value so we don't leak the strings */
|
||||
|
||||
free(buf);
|
||||
/* if name is defined, used it */
|
||||
if(name!=nil && name[0]!='\0'){
|
||||
buf = estrdup(name);
|
||||
return cleanname(buf);
|
||||
}
|
||||
/* if data is an absolute file name, or wdir is empty, use it */
|
||||
if(e->msg->data[0]=='/' || e->msg->wdir==nil || e->msg->wdir[0]=='\0'){
|
||||
buf = estrdup(e->msg->data);
|
||||
return cleanname(buf);
|
||||
}
|
||||
buf = emalloc(strlen(e->msg->wdir)+1+strlen(e->msg->data)+1);
|
||||
sprint(buf, "%s/%s", e->msg->wdir, e->msg->data);
|
||||
return cleanname(buf);
|
||||
}
|
||||
|
||||
char*
|
||||
dollar(Exec *e, char *s, int *namelen)
|
||||
{
|
||||
int n;
|
||||
static char *abuf;
|
||||
char *t;
|
||||
|
||||
*namelen = 1;
|
||||
if(e!=nil && '0'<=s[0] && s[0]<='9')
|
||||
return nonnil(e->match[s[0]-'0']);
|
||||
|
||||
for(t=s; isalnum(*t); t++)
|
||||
;
|
||||
n = t-s;
|
||||
*namelen = n;
|
||||
|
||||
if(e != nil){
|
||||
if(n == 3){
|
||||
if(memcmp(s, "src", 3) == 0)
|
||||
return nonnil(e->msg->src);
|
||||
if(memcmp(s, "dst", 3) == 0)
|
||||
return nonnil(e->msg->dst);
|
||||
if(memcmp(s, "dir", 3) == 0)
|
||||
return filename(e, e->dir);
|
||||
}
|
||||
if(n == 4){
|
||||
if(memcmp(s, "attr", 4) == 0){
|
||||
free(abuf);
|
||||
abuf = plumbpackattr(e->msg->attr);
|
||||
return nonnil(abuf);
|
||||
}
|
||||
if(memcmp(s, "data", 4) == 0)
|
||||
return nonnil(e->msg->data);
|
||||
if(memcmp(s, "file", 4) == 0)
|
||||
return filename(e, e->file);
|
||||
if(memcmp(s, "type", 4) == 0)
|
||||
return nonnil(e->msg->type);
|
||||
if(memcmp(s, "wdir", 3) == 0)
|
||||
return nonnil(e->msg->wdir);
|
||||
}
|
||||
}
|
||||
|
||||
return variable(s, n);
|
||||
}
|
||||
|
||||
/* expand one blank-terminated string, processing quotes and $ signs */
|
||||
char*
|
||||
expand(Exec *e, char *s, char **ends)
|
||||
{
|
||||
char *p, *ep, *val;
|
||||
int namelen, quoting;
|
||||
|
||||
p = ebuf;
|
||||
ep = ebuf+sizeof ebuf-1;
|
||||
quoting = 0;
|
||||
while(p<ep && *s!='\0' && (quoting || (*s!=' ' && *s!='\t'))){
|
||||
if(*s == '\''){
|
||||
s++;
|
||||
if(!quoting)
|
||||
quoting = 1;
|
||||
else if(*s == '\''){
|
||||
*p++ = '\'';
|
||||
s++;
|
||||
}else
|
||||
quoting = 0;
|
||||
continue;
|
||||
}
|
||||
if(quoting || *s!='$'){
|
||||
*p++ = *s++;
|
||||
continue;
|
||||
}
|
||||
s++;
|
||||
val = dollar(e, s, &namelen);
|
||||
if(val == nil){
|
||||
*p++ = '$';
|
||||
continue;
|
||||
}
|
||||
if(ep-p < strlen(val))
|
||||
return "string-too-long";
|
||||
strcpy(p, val);
|
||||
p += strlen(val);
|
||||
s += namelen;
|
||||
}
|
||||
if(ends)
|
||||
*ends = s;
|
||||
*p = '\0';
|
||||
return ebuf;
|
||||
}
|
||||
|
||||
void
|
||||
regerror(char *msg)
|
||||
{
|
||||
if(parsing){
|
||||
parsing = 0;
|
||||
parseerror("%s", msg);
|
||||
}
|
||||
error("%s", msg);
|
||||
}
|
||||
|
||||
void
|
||||
parserule(Rule *r)
|
||||
{
|
||||
r->qarg = estrdup(expand(nil, r->arg, nil));
|
||||
switch(r->obj){
|
||||
case OArg:
|
||||
case OAttr:
|
||||
case OData:
|
||||
case ODst:
|
||||
case OType:
|
||||
case OWdir:
|
||||
case OSrc:
|
||||
if(r->verb==VClient || r->verb==VStart || r->verb==VTo)
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
if(r->obj!=OAttr && (r->verb==VAdd || r->verb==VDelete))
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
if(r->verb == VMatches){
|
||||
r->regex = regcomp(r->qarg);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case OPlumb:
|
||||
if(r->verb!=VClient && r->verb!=VStart && r->verb!=VTo)
|
||||
parseerror("%s not valid verb for object %s", verbs[r->verb], objects[r->obj]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
assignment(char *p)
|
||||
{
|
||||
char *var, *qval;
|
||||
int n;
|
||||
|
||||
if(!isalpha(p[0]))
|
||||
return 0;
|
||||
for(var=p; isalnum(*p); p++)
|
||||
;
|
||||
n = p-var;
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
if(*p++ != '=')
|
||||
return 0;
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
qval = expand(nil, p, nil);
|
||||
setvariable(var, n, p, qval);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
include(char *s)
|
||||
{
|
||||
char *t, *args[3], buf[128];
|
||||
int n, fd;
|
||||
|
||||
if(strncmp(s, "include", 7) != 0)
|
||||
return 0;
|
||||
/* either an include or an error */
|
||||
n = tokenize(s, args, nelem(args));
|
||||
if(n < 2)
|
||||
goto Err;
|
||||
if(strcmp(args[0], "include") != 0)
|
||||
goto Err;
|
||||
if(args[1][0] == '#')
|
||||
goto Err;
|
||||
if(n>2 && args[2][0] != '#')
|
||||
goto Err;
|
||||
t = args[1];
|
||||
fd = open(t, OREAD);
|
||||
if(fd<0 && t[0]!='/' && strncmp(t, "./", 2)!=0 && strncmp(t, "../", 3)!=0){
|
||||
snprint(buf, sizeof buf, "/sys/lib/plumb/%s", t);
|
||||
t = buf;
|
||||
fd = open(t, OREAD);
|
||||
}
|
||||
if(fd < 0)
|
||||
parseerror("can't open %s for inclusion", t);
|
||||
pushinput(t, fd, nil);
|
||||
return 1;
|
||||
|
||||
Err:
|
||||
parseerror("malformed include statement");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Rule*
|
||||
readrule(int *eof)
|
||||
{
|
||||
Rule *rp;
|
||||
char *line, *p;
|
||||
char *word;
|
||||
|
||||
Top:
|
||||
line = getline();
|
||||
if(line == nil){
|
||||
/*
|
||||
* if input is from string, and bytes remain (input->end is within string),
|
||||
* morerules() will pop input and save remaining data. otherwise pop
|
||||
* the stack here, and if there's more input, keep reading.
|
||||
*/
|
||||
if((input!=nil && input->end==nil) && popinput())
|
||||
goto Top;
|
||||
*eof = 1;
|
||||
return nil;
|
||||
}
|
||||
input->lineno++;
|
||||
|
||||
for(p=line; *p==' ' || *p=='\t'; p++)
|
||||
;
|
||||
if(*p=='\0' || *p=='#') /* empty or comment line */
|
||||
return nil;
|
||||
|
||||
if(include(p))
|
||||
goto Top;
|
||||
|
||||
if(assignment(p))
|
||||
return nil;
|
||||
|
||||
rp = emalloc(sizeof(Rule));
|
||||
|
||||
/* object */
|
||||
for(word=p; *p!=' ' && *p!='\t'; p++)
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
*p++ = '\0';
|
||||
rp->obj = lookup(word, objects);
|
||||
if(rp->obj < 0){
|
||||
if(strcmp(word, "kind") == 0) /* backwards compatibility */
|
||||
rp->obj = OType;
|
||||
else
|
||||
parseerror("unknown object %s", word);
|
||||
}
|
||||
|
||||
/* verb */
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
for(word=p; *p!=' ' && *p!='\t'; p++)
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
*p++ = '\0';
|
||||
rp->verb = lookup(word, verbs);
|
||||
if(rp->verb < 0)
|
||||
parseerror("unknown verb %s", word);
|
||||
|
||||
/* argument */
|
||||
while(*p==' ' || *p=='\t')
|
||||
p++;
|
||||
if(*p == '\0')
|
||||
parseerror("malformed rule");
|
||||
rp->arg = estrdup(p);
|
||||
|
||||
parserule(rp);
|
||||
|
||||
return rp;
|
||||
}
|
||||
|
||||
void
|
||||
freerule(Rule *r)
|
||||
{
|
||||
free(r->arg);
|
||||
free(r->qarg);
|
||||
free(r->regex);
|
||||
}
|
||||
|
||||
void
|
||||
freerules(Rule **r)
|
||||
{
|
||||
while(*r)
|
||||
freerule(*r++);
|
||||
}
|
||||
|
||||
void
|
||||
freeruleset(Ruleset *rs)
|
||||
{
|
||||
freerules(rs->pat);
|
||||
free(rs->pat);
|
||||
freerules(rs->act);
|
||||
free(rs->act);
|
||||
free(rs->port);
|
||||
free(rs);
|
||||
}
|
||||
|
||||
Ruleset*
|
||||
readruleset(void)
|
||||
{
|
||||
Ruleset *rs;
|
||||
Rule *r;
|
||||
int eof, inrule, i, ncmd;
|
||||
|
||||
Again:
|
||||
eof = 0;
|
||||
rs = emalloc(sizeof(Ruleset));
|
||||
rs->pat = emalloc(sizeof(Rule*));
|
||||
rs->act = emalloc(sizeof(Rule*));
|
||||
inrule = 0;
|
||||
ncmd = 0;
|
||||
for(;;){
|
||||
r = readrule(&eof);
|
||||
if(eof)
|
||||
break;
|
||||
if(r==nil){
|
||||
if(inrule)
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
inrule = 1;
|
||||
switch(r->obj){
|
||||
case OArg:
|
||||
case OAttr:
|
||||
case OData:
|
||||
case ODst:
|
||||
case OType:
|
||||
case OWdir:
|
||||
case OSrc:
|
||||
rs->npat++;
|
||||
rs->pat = erealloc(rs->pat, (rs->npat+1)*sizeof(Rule*));
|
||||
rs->pat[rs->npat-1] = r;
|
||||
rs->pat[rs->npat] = nil;
|
||||
break;
|
||||
case OPlumb:
|
||||
rs->nact++;
|
||||
rs->act = erealloc(rs->act, (rs->nact+1)*sizeof(Rule*));
|
||||
rs->act[rs->nact-1] = r;
|
||||
rs->act[rs->nact] = nil;
|
||||
if(r->verb == VTo){
|
||||
if(rs->npat>0 && rs->port != nil) /* npat==0 implies port declaration */
|
||||
parseerror("too many ports");
|
||||
if(lookup(r->qarg, badports) >= 0)
|
||||
parseerror("illegal port name %s", r->qarg);
|
||||
rs->port = estrdup(r->qarg);
|
||||
}else
|
||||
ncmd++; /* start or client rule */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(ncmd > 1){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset has more than one client or start action");
|
||||
}
|
||||
if(rs->npat>0 && rs->nact>0)
|
||||
return rs;
|
||||
if(rs->npat==0 && rs->nact==0){
|
||||
freeruleset(rs);
|
||||
return nil;
|
||||
}
|
||||
if(rs->nact==0 || rs->port==nil){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset must have patterns and actions");
|
||||
return nil;
|
||||
}
|
||||
|
||||
/* declare ports */
|
||||
for(i=0; i<rs->nact; i++)
|
||||
if(rs->act[i]->verb != VTo){
|
||||
freeruleset(rs);
|
||||
parseerror("ruleset must have actions");
|
||||
return nil;
|
||||
}
|
||||
for(i=0; i<rs->nact; i++)
|
||||
addport(rs->act[i]->qarg);
|
||||
freeruleset(rs);
|
||||
goto Again;
|
||||
}
|
||||
|
||||
Ruleset**
|
||||
readrules(char *name, int fd)
|
||||
{
|
||||
Ruleset *rs, **rules;
|
||||
int n;
|
||||
|
||||
parsing = 1;
|
||||
pushinput(name, fd, nil);
|
||||
rules = emalloc(sizeof(Ruleset*));
|
||||
for(n=0; (rs=readruleset())!=nil; n++){
|
||||
rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
|
||||
rules[n] = rs;
|
||||
rules[n+1] = nil;
|
||||
}
|
||||
popinput();
|
||||
parsing = 0;
|
||||
return rules;
|
||||
}
|
||||
|
||||
char*
|
||||
concat(char *s, char *t)
|
||||
{
|
||||
if(t == nil)
|
||||
return s;
|
||||
if(s == nil)
|
||||
s = estrdup(t);
|
||||
else{
|
||||
s = erealloc(s, strlen(s)+strlen(t)+1);
|
||||
strcat(s, t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printpat(Rule *r)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = emalloc(strlen(objects[r->obj])+1+strlen(verbs[r->verb])+1+strlen(r->arg)+1+1);
|
||||
sprint(s, "%s\t%s\t%s\n", objects[r->obj], verbs[r->verb], r->arg);
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printvar(Var *v)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = emalloc(strlen(v->name)+1+strlen(v->value)+2+1);
|
||||
sprint(s, "%s=%s\n\n", v->name, v->value);
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printrule(Ruleset *r)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
for(i=0; i<r->npat; i++)
|
||||
s = concat(s, printpat(r->pat[i]));
|
||||
for(i=0; i<r->nact; i++)
|
||||
s = concat(s, printpat(r->act[i]));
|
||||
s = concat(s, "\n");
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printport(char *port)
|
||||
{
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
s = concat(s, "plumb to ");
|
||||
s = concat(s, port);
|
||||
s = concat(s, "\n");
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
printrules(void)
|
||||
{
|
||||
int i;
|
||||
char *s;
|
||||
|
||||
s = nil;
|
||||
for(i=0; i<nvars; i++)
|
||||
s = concat(s, printvar(&vars[i]));
|
||||
for(i=0; i<nports; i++)
|
||||
s = concat(s, printport(ports[i]));
|
||||
s = concat(s, "\n");
|
||||
for(i=0; rules[i]; i++)
|
||||
s = concat(s, printrule(rules[i]));
|
||||
return s;
|
||||
}
|
||||
|
||||
char*
|
||||
stringof(char *s, int n)
|
||||
{
|
||||
char *t;
|
||||
|
||||
t = emalloc(n+1);
|
||||
memmove(t, s, n);
|
||||
return t;
|
||||
}
|
||||
|
||||
uchar*
|
||||
morerules(uchar *text, int done)
|
||||
{
|
||||
int n;
|
||||
Ruleset *rs;
|
||||
uchar *otext, *s, *endofrule;
|
||||
|
||||
pushinput("<rules input>", -1, text);
|
||||
if(done)
|
||||
input->end = text+strlen((char*)text);
|
||||
else{
|
||||
/*
|
||||
* Help user by sending any full rules to parser so any parse errors will
|
||||
* occur on write rather than close. A heuristic will do: blank line ends rule.
|
||||
*/
|
||||
endofrule = nil;
|
||||
for(s=text; *s!='\0'; s++)
|
||||
if(*s=='\n' && *++s=='\n')
|
||||
endofrule = s+1;
|
||||
if(endofrule == nil)
|
||||
return text;
|
||||
input->end = endofrule;
|
||||
}
|
||||
for(n=0; rules[n]; n++)
|
||||
;
|
||||
while((rs=readruleset()) != nil){
|
||||
rules = erealloc(rules, (n+2)*sizeof(Ruleset*));
|
||||
rules[n++] = rs;
|
||||
rules[n] = nil;
|
||||
}
|
||||
otext =text;
|
||||
if(input == nil)
|
||||
text = (uchar*)estrdup("");
|
||||
else
|
||||
text = (uchar*)estrdup((char*)input->end);
|
||||
popinput();
|
||||
free(otext);
|
||||
return text;
|
||||
}
|
||||
|
||||
char*
|
||||
writerules(char *s, int n)
|
||||
{
|
||||
static uchar *text;
|
||||
char *tmp;
|
||||
|
||||
free(lasterror);
|
||||
lasterror = nil;
|
||||
parsing = 1;
|
||||
if(setjmp(parsejmp) == 0){
|
||||
tmp = stringof(s, n);
|
||||
text = (uchar*)concat((char*)text, tmp);
|
||||
free(tmp);
|
||||
text = morerules(text, s==nil);
|
||||
}
|
||||
if(s == nil){
|
||||
free(text);
|
||||
text = nil;
|
||||
}
|
||||
parsing = 0;
|
||||
makeports(rules);
|
||||
return lasterror;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue