add ptrap
This commit is contained in:
parent
d06196ab87
commit
721b141438
75
sys/man/4/ptrap
Normal file
75
sys/man/4/ptrap
Normal file
|
@ -0,0 +1,75 @@
|
|||
.TH PTRAP 4
|
||||
.SH NAME
|
||||
ptrap \- \fIplumber\fR(4) filter
|
||||
.SH SYNOPSIS
|
||||
.B ptrap
|
||||
.I port
|
||||
[\fB!\fR]\fIregexp\fR
|
||||
[
|
||||
.I port
|
||||
[\fB!\fR]\fIregexp\fR ...
|
||||
]
|
||||
.SH DESCRIPTION
|
||||
.I Ptrap
|
||||
is a program that mounts itself over a
|
||||
.IR plumber (4)
|
||||
service mounted at
|
||||
.B /mnt/plumb
|
||||
and filters incoming messages according to the rules provided on the command line.
|
||||
.PP
|
||||
.I Ptrap
|
||||
accepts an arbitrary number of argument pairs; each pair consists of a port name
|
||||
.I port
|
||||
and a regular expression
|
||||
.I regexp
|
||||
(see
|
||||
.IR regexp (6)).
|
||||
Each incoming message that does not match
|
||||
.I regexp
|
||||
is discarded.
|
||||
The
|
||||
.I regexp
|
||||
can be optionally prefixed by
|
||||
.B !
|
||||
to indicate logical inversion (i.e. messages matching the regexp are discarded).
|
||||
.SH EXAMPLES
|
||||
Start a
|
||||
.IR sam (1)
|
||||
instance dedicated to editing kernel source code:
|
||||
.IP
|
||||
.EX
|
||||
ptrap edit '^/sys/src/9/'
|
||||
sam
|
||||
.EE
|
||||
.PP
|
||||
In another window, start a second
|
||||
.IR sam (1)
|
||||
instance for all other editing jobs:
|
||||
.IP
|
||||
.EX
|
||||
ptrap edit '!^/sys/src/9/'
|
||||
sam
|
||||
.EE
|
||||
.SH SOURCE
|
||||
.B /sys/src/cmd/ptrap.c
|
||||
.SH SEE ALSO
|
||||
.IR plumber (4),
|
||||
.IR plumb (6)
|
||||
.SH BUGS
|
||||
Multiple filters specified on the same port ignore all but the last one.
|
||||
.PP
|
||||
.I Ptrap
|
||||
would be more useful if it could inhibit sending the message to other clients.
|
||||
.PP
|
||||
As far as
|
||||
.IR plumber (4)
|
||||
is concerned, even messages dropped by
|
||||
.I ptrap
|
||||
are "accepted", which means rules that are supposed to apply to messages not accepted by clients are not invoked (e.g. a rule starting an editor if no one is listening to the
|
||||
.I edit
|
||||
port will not work if there is a
|
||||
.I ptrap
|
||||
on that port).
|
||||
.SH HISTORY
|
||||
.I Ptrap
|
||||
first appeared in 9front (February, 2018).
|
375
sys/src/cmd/ptrap.c
Normal file
375
sys/src/cmd/ptrap.c
Normal file
|
@ -0,0 +1,375 @@
|
|||
#include <u.h>
|
||||
#include <libc.h>
|
||||
#include <thread.h>
|
||||
#include <fcall.h>
|
||||
#include <9p.h>
|
||||
#include <plumb.h>
|
||||
#include <regexp.h>
|
||||
|
||||
typedef struct IOProc IOProc;
|
||||
typedef struct PFilter PFilter;
|
||||
typedef struct PFid PFid;
|
||||
|
||||
struct IOProc {
|
||||
int id;
|
||||
Channel *ch;
|
||||
IOProc *next;
|
||||
};
|
||||
QLock freellock;
|
||||
IOProc *freel;
|
||||
|
||||
struct PFid {
|
||||
char *name;
|
||||
PFilter *filter;
|
||||
int fd;
|
||||
char *msg;
|
||||
int msgn, msgp;
|
||||
};
|
||||
Qid rootqid = {.type QTDIR};
|
||||
|
||||
struct PFilter {
|
||||
char *name;
|
||||
Reprog *filt;
|
||||
PFilter *next;
|
||||
int invert;
|
||||
};
|
||||
PFilter *filters;
|
||||
|
||||
static char *
|
||||
fname(char *n)
|
||||
{
|
||||
static char *last;
|
||||
|
||||
if(last != nil){
|
||||
free(last);
|
||||
last = nil;
|
||||
}
|
||||
if(n == nil)
|
||||
return "/mnt/plumb";
|
||||
last = smprint("/mnt/plumb/%s", n);
|
||||
return last;
|
||||
}
|
||||
|
||||
static void theioproc(void *);
|
||||
static IOProc *
|
||||
getioproc(void)
|
||||
{
|
||||
IOProc *p;
|
||||
|
||||
qlock(&freellock);
|
||||
p = freel;
|
||||
if(p != nil)
|
||||
freel = freel->next;
|
||||
qunlock(&freellock);
|
||||
if(p == nil){
|
||||
p = emalloc9p(sizeof(IOProc));
|
||||
p->ch = chancreate(sizeof(Req*), 0);
|
||||
p->id = proccreate(theioproc, p, 4096);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
static void
|
||||
putioproc(IOProc *p)
|
||||
{
|
||||
qlock(&freellock);
|
||||
p->next = freel;
|
||||
freel = p;
|
||||
qunlock(&freellock);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapattach(Req *r)
|
||||
{
|
||||
PFid *pf;
|
||||
|
||||
pf = emalloc9p(sizeof(PFid));
|
||||
pf->fd = -1;
|
||||
r->fid->aux = pf;
|
||||
r->ofcall.qid = rootqid;
|
||||
r->fid->qid = rootqid;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static char *
|
||||
ptrapclone(Fid *old, Fid *new)
|
||||
{
|
||||
PFid *pf;
|
||||
|
||||
pf = emalloc9p(sizeof(PFid));
|
||||
memcpy(pf, old->aux, sizeof(PFid));
|
||||
new->aux = pf;
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapdestroyfid(Fid *f)
|
||||
{
|
||||
PFid *pf;
|
||||
|
||||
pf = f->aux;
|
||||
if(pf == nil) return;
|
||||
free(pf->name);
|
||||
if(pf->fd >= 0)
|
||||
close(pf->fd);
|
||||
free(pf);
|
||||
f->aux = nil;
|
||||
}
|
||||
|
||||
static char *
|
||||
ptrapwalk1(Fid *fid, char *name, Qid *qid)
|
||||
{
|
||||
PFid *pf;
|
||||
Dir *d;
|
||||
static char err[ERRMAX];
|
||||
|
||||
pf = fid->aux;
|
||||
if(pf->name != nil)
|
||||
return "phase error";
|
||||
d = dirstat(fname(name));
|
||||
if(d == nil){
|
||||
rerrstr(err, ERRMAX);
|
||||
return err;
|
||||
}
|
||||
pf->name = strdup(name);
|
||||
fid->qid = d->qid;
|
||||
*qid = d->qid;
|
||||
free(d);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapopen(Req *r)
|
||||
{
|
||||
PFid* pf;
|
||||
PFilter *f;
|
||||
|
||||
pf = r->fid->aux;
|
||||
pf->fd = open(fname(pf->name), r->ifcall.mode);
|
||||
if(pf->fd < 0){
|
||||
responderror(r);
|
||||
return;
|
||||
}
|
||||
if(pf->name == nil){
|
||||
respond(r, nil);
|
||||
return;
|
||||
}
|
||||
for(f = filters; f != nil; f = f->next)
|
||||
if(strcmp(f->name, pf->name) == 0)
|
||||
break;
|
||||
pf->filter = f;
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static int
|
||||
filterread(Req *r, PFid *pf)
|
||||
{
|
||||
int rc, len, more;
|
||||
char *buf;
|
||||
Plumbmsg *pm;
|
||||
|
||||
for(;;){
|
||||
if(pf->msg != nil){
|
||||
rc = r->ifcall.count;
|
||||
if(pf->msgp + rc >= pf->msgn)
|
||||
rc = pf->msgn - pf->msgp;
|
||||
r->ofcall.count = rc;
|
||||
memmove(r->ofcall.data, pf->msg + pf->msgp, rc);
|
||||
pf->msgp += rc;
|
||||
if(pf->msgp >= pf->msgn){
|
||||
free(pf->msg);
|
||||
pf->msg = nil;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
buf = emalloc9p(4096);
|
||||
rc = read(pf->fd, buf, 4096);
|
||||
if(rc < 0) goto err;
|
||||
len = rc;
|
||||
while(pm = plumbunpackpartial(buf, len, &more), pm == nil){
|
||||
if(more == 0) goto err;
|
||||
buf = erealloc9p(buf, len + more);
|
||||
rc = readn(pf->fd, buf + len, more);
|
||||
if(rc < 0) goto err;
|
||||
len += rc;
|
||||
}
|
||||
free(buf);
|
||||
if(regexec(pf->filter->filt, pm->data, nil, 0) ^ pf->filter->invert){
|
||||
pf->msg = plumbpack(pm, &pf->msgn);
|
||||
pf->msgp = 0;
|
||||
}
|
||||
plumbfree(pm);
|
||||
}
|
||||
err:
|
||||
free(buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void
|
||||
theioproc(void *iopp)
|
||||
{
|
||||
Req *r;
|
||||
PFid *pf;
|
||||
IOProc *iop;
|
||||
char *buf;
|
||||
int fd, rc;
|
||||
|
||||
rfork(RFNOTEG);
|
||||
|
||||
buf = smprint("/proc/%d/ctl", getpid());
|
||||
fd = open(buf, OWRITE);
|
||||
free(buf);
|
||||
|
||||
iop = iopp;
|
||||
for(;;){
|
||||
if(fd >= 0)
|
||||
write(fd, "nointerrupt", 11);
|
||||
r = recvp(iop->ch);
|
||||
r->aux = iop;
|
||||
pf = r->fid->aux;
|
||||
switch(r->ifcall.type){
|
||||
case Tread:
|
||||
if(!pf->filter){
|
||||
rc = pread(pf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
|
||||
if(rc < 0){
|
||||
responderror(r);
|
||||
break;
|
||||
}
|
||||
r->ofcall.count = rc;
|
||||
respond(r, nil);
|
||||
break;
|
||||
}
|
||||
if(filterread(r, pf) < 0)
|
||||
responderror(r);
|
||||
else
|
||||
respond(r, nil);
|
||||
break;
|
||||
case Twrite:
|
||||
rc = pwrite(pf->fd, r->ifcall.data, r->ifcall.count, r->ifcall.offset);
|
||||
if(rc < 0)
|
||||
responderror(r);
|
||||
else{
|
||||
r->ofcall.count = rc;
|
||||
respond(r, nil);
|
||||
}
|
||||
break;
|
||||
}
|
||||
putioproc(iop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapread(Req *r)
|
||||
{
|
||||
IOProc *iop;
|
||||
|
||||
iop = getioproc();
|
||||
send(iop->ch, &r);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapwrite(Req *r)
|
||||
{
|
||||
IOProc *iop;
|
||||
|
||||
iop = getioproc();
|
||||
send(iop->ch, &r);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapstat(Req *r)
|
||||
{
|
||||
PFid *pf;
|
||||
Dir *d;
|
||||
|
||||
pf = r->fid->aux;
|
||||
if(pf->fd >= 0)
|
||||
d = dirfstat(pf->fd);
|
||||
else
|
||||
d = dirstat(fname(pf->name));
|
||||
if(d == nil){
|
||||
responderror(r);
|
||||
return;
|
||||
}
|
||||
memmove(&r->d, d, sizeof(Dir));
|
||||
r->d.name = strdup(d->name);
|
||||
r->d.uid = strdup(d->uid);
|
||||
r->d.muid = strdup(d->muid);
|
||||
r->d.gid = strdup(d->gid);
|
||||
free(d);
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapwstat(Req *r)
|
||||
{
|
||||
PFid *pf;
|
||||
int rc;
|
||||
|
||||
pf = r->fid->aux;
|
||||
if(pf->fd >= 0)
|
||||
rc = dirfwstat(pf->fd, &r->d);
|
||||
else
|
||||
rc = dirwstat(fname(pf->name), &r->d);
|
||||
if(rc < 0)
|
||||
responderror(r);
|
||||
else
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
static void
|
||||
ptrapflush(Req *r)
|
||||
{
|
||||
if(r->oldreq->aux != nil)
|
||||
threadint(((IOProc*)r->oldreq->aux)->id);
|
||||
respond(r, nil);
|
||||
}
|
||||
|
||||
Srv ptrapsrv = {
|
||||
.attach = ptrapattach,
|
||||
.clone = ptrapclone,
|
||||
.destroyfid = ptrapdestroyfid,
|
||||
.walk1 = ptrapwalk1,
|
||||
.open = ptrapopen,
|
||||
.read = ptrapread,
|
||||
.write = ptrapwrite,
|
||||
.stat = ptrapstat,
|
||||
.wstat = ptrapwstat,
|
||||
.flush = ptrapflush,
|
||||
};
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
fprint(2, "usage: %s port regex [ port regex ... ]\n", argv0);
|
||||
exits("usage");
|
||||
}
|
||||
|
||||
void
|
||||
threadmain(int argc, char **argv)
|
||||
{
|
||||
PFilter *f;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
ARGBEGIN{
|
||||
default: usage();
|
||||
}ARGEND;
|
||||
|
||||
if(argc % 2) usage();
|
||||
for(i = 0; i < argc; i += 2){
|
||||
f = emalloc9p(sizeof(PFilter));
|
||||
f->name = strdup(argv[i]);
|
||||
p = argv[i+1];
|
||||
if(*p == '!'){
|
||||
p++;
|
||||
f->invert = 1;
|
||||
}
|
||||
f->filt = regcomp(p);
|
||||
if(f->filt == nil)
|
||||
sysfatal("%r");
|
||||
f->next = filters;
|
||||
filters = f;
|
||||
}
|
||||
threadpostmountsrv(&ptrapsrv, nil, "/mnt/plumb", MREPL | MCREATE);
|
||||
}
|
Loading…
Reference in a new issue