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