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