ref: 721b141438589bbf0521832760e28a2f496ef83f
parent: d06196ab8760ec567a9f043b757a789755a6aed7
author: aiju <devnull@localhost>
date: Mon Feb 5 04:38:59 EST 2018
add ptrap
--- /dev/null
+++ b/sys/man/4/ptrap
@@ -1,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).
--- /dev/null
+++ b/sys/src/cmd/ptrap.c
@@ -1,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);
+}