shithub: riscv

Download patch

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);
+}