Plan 9 from Bell Labs’s /usr/web/sources/contrib/maht/blogger/actionfs.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// Put 

//	8c -w actionfs.c &&  8l actionfs.8  && mv 8.out /usr/maht/bin/386/actionfs 


#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include <regexp.h>
#include <stdio.h>

typedef struct Path Path;
struct Path
{
	Qid qid;
	char *name;
	Path *next;
};

Reprog  *freg;
Path *root = nil;
int nmatches;
int client = 0;
char *aread = "action-read";
char *awrite = "action-write";
char *astat = "action-stat";

static void
fsattach(Req *r)
{
	r->ofcall.qid = (Qid){0, ++client, QTDIR};
	r->fid->qid = r->ofcall.qid;
	respond(r, nil);
}

static void
print_qid(Qid *q) {
	print("p %x v %d f %x\n", q->path, q->vers, q->type);
}

static void
print_matches(Resub *matches) {
	if(!matches) {
		print("No match\n");
		return;
	}

	char *bit;
	int i, k;
	for(i = 0; i < nmatches; i++) {
		k = (matches[i].ep - matches[i].sp) + 1;
		bit = (char*)malloc(k);
		strecpy(bit, bit + k, matches[i].sp);
		free(bit);
	}
	print("\n");
}

static void
print_path(Path *p) {
	print("Name: %s\n", p->name);
	print_qid(&p->qid);
	print("Next: %x\n", p->next);
}

static Resub*
re(char *txt) {
	Resub* matches = (Resub*)calloc(nmatches, sizeof(Resub));
	if(regexec(freg, txt, matches, nmatches))
		return matches;
	free(matches);
	return nil;	
}

static Path*
find_path(Qid *qid) {
	Path *p;
	for(p = root; p; p = p->next) 
		if(qid->path == p->qid.path)
			break;
	return p;
}

static Path*
find_prev_path(Qid *qid) {
	Path *p;
	for(p = root; p; p = p->next) {
		if(p->next && (qid->path == p->next->qid.path))
			break;
	}
	return p;
}

static Qid*
find_qid(char *name) {
	Path *p;
	Resub *m;
	for(p = root; p; p = p->next)
		if(strcmp(name, p->name) == 0)
			return &p->qid;
	if(!(m = re(name)))
		return nil;
	free(m);

	p = (Path*)mallocz(sizeof(Path), 1);
	p->qid.path = root ? root->qid.path +1 : 1;
	p->qid.vers = 0;
	p->next = root;
	p->name = strdup(name);
	root = p;
	return &root->qid;
}

static char*
fswalk1(Fid *fid, char *name, Qid *qid)
{
	Qid *q;
	
	if(!(q = find_qid(name))) 
		return "Not Found";

	q->vers++;
	memcpy(qid, q, sizeof(Qid));
	memcpy(&fid->qid, q, sizeof(Qid));

	return nil;
}

static void
fsstat(Req *r)
{
	Path *p;
	p = find_path(&r->fid->qid);
	if(p) {
		Dir *d = &r->d;
		memset(d, 0, sizeof *d);
		d->uid = strdup("inband");
		d->gid = strdup("inband");
		d->name = strdup(p->name);
		d->mode = 0444;
		d->qid.path = r->fid->qid.path;
		d->qid.vers = r->fid->qid.vers;
		d->qid.type = r->fid->qid.type;
		d->length = 0;
		d->atime = d->mtime = time(0);
		respond(r, nil);
	} else {
		respond(r, "Not found");
	}
}

char **
build_argv(int fd, char *name) {
	char **argv = malloc(sizeof(char*) * (nmatches + 3));
	if(fd > 0)
		argv[0] = smprint("%s", aread);
	else
		argv[0] = smprint("%s", awrite);

	argv[1] = smprint("%d", abs(fd));

	Resub *matches = re(name);
	int i, j, k;
	for(i = 0, j = 2; i < nmatches; i++, j++) {
		k = (matches[i].ep - matches[i].sp) + 1;
		argv[j] = (char*)mallocz(k + 1, 1);
		strecpy(argv[j], argv[j] + k, matches[i].sp);
	}
	argv[j] = nil;

	return argv;
}

static char *
do_action(char *action, int fd, Path *p) {
	char **argv = build_argv(fd, p->name);
	char *error = nil;
	int i;

	switch(fork()) {
	case 0 :
		exec(action, argv);
		error = "exec failed";
		break;
	case -1 :
		error = "fork failed";
		break;
	default :
		wait();
		for(i = 0; i < nmatches+2; i++)
			free(argv[i]);
		free(argv);
		break;
	}

	return error;
}

static void
fsopen(Req *r)
{
	int fd;
	switch(r->ifcall.mode & 1) { // discard OTRUNC etc.
	case OREAD :
		fd = create(tmpnam(nil), ORDWR|ORCLOSE, 0600);
		if(fd < 1)  { // assume fd 0 is taken !
			respond(r, "/tmp/$file create failed");
			return;
		}
		break;
	case OWRITE :
		fd = create(tmpnam(nil), ORDWR|ORCLOSE, 0600);
		if(fd < 1)  { // assume fd 0 is taken !
			respond(r, "/tmp/$file create failed");
			return;
		}
		fd = -fd;
		break;
	default :
		respond(r, "permission denied");
		return;
	}

	r->fid->aux = (void*)fd;


	Path *p = find_path(&r->fid->qid);
	char *error = nil;
	if(fd > 0)
		error = do_action(aread, fd, p);
	respond(r, error);
}

static void
remove_path(Path *p) {
	Path *pp;
	pp = find_prev_path(&p->qid);
	if(pp)
		pp->next = p->next;
	else
		root = nil;
	free(p->name);
	free(p);
}

static void
fsclose(Fid *fid) {
	if(fid->aux)
		close(abs((int)fid->aux));

	Path *p = find_path(&fid->qid);
	if(p && p->qid.path)  // p *should* always be non null
		if(--p->qid.vers == 0)
			remove_path(p);
}

static void
fsclunk(Fid *fid) {
	Path *p = find_path(&fid->qid);

	if((int)fid->aux < 0) {
		seek(abs((int)fid->aux), 0, 0);
		do_action(awrite, (int)fid->aux, p);
	}
	if(fid->aux)
		close(abs((int)fid->aux));

	if(p && p->qid.path)  // p *should* always be non null
		if(--p->qid.vers == 0)
			remove_path(p);
}

static void
fsread(Req *r)
{
	seek((int)r->fid->aux, r->ifcall.offset, 0);
	int k = read((int)r->fid->aux, r->ofcall.data, r->ifcall.count);
	if(k < 0)
		respond(r, "Read failed");
	r->ofcall.count = k;
	respond(r, nil);
}

static void
fswrite(Req *r)
{
	seek(abs((int)r->fid->aux), r->ifcall.offset, 0);
	int k = write(abs((int)r->fid->aux), r->ifcall.data, r->ifcall.count);
	if(k < 0)
		respond(r, "Write failed");
	r->ofcall.count = k;
	respond(r, nil);
}

Srv numsrv = {
.attach=	fsattach,
.walk1=	fswalk1,
.open=	fsopen,
.read=	fsread,
.write=	fswrite,
.stat=	fsstat,
.destroyfid = fsclunk,
};

static int
num_matches(char *txt){
	int i = 0;
	char *p;
	for(p = txt; p ; i++) {
		p = strchr(p, '(');
		if(p) p++;
	}
	return i ? i : 1;
}

extern int chatty9p;

void
usage(void)
{
	fprint(2, "usage: actionfs [-v servicename] [-m mountpoint] [-t tmpdir] [-r file] [-w file] [-s file] regex\n");
	exits("usage");
}

void
main(int argc, char **argv)
{
	char *reg;
	char *tmp = "/tmp";
	char *service = "actionfs";
	char *mtpt = nil;


	ARGBEGIN{
	case 'D':
		chatty9p++;
		break;
	case 'r' :
		aread = EARGF(usage());
		break;
	case 'w':
		awrite = EARGF(usage());
		break;
	case 's' :
		astat = EARGF(usage());
		break;
	case 'v' : 
		service = EARGF(usage());
		if(mtpt == nil)
			mtpt = smprint("/n/%s", service);
		break;
	case 'm' :
		mtpt = smprint("%s", EARGF(usage()));
		break;
	case 't' :
		tmp = EARGF(usage());
		break;
	}ARGEND

	if(argc == 1)
		reg = argv[0];
	else
		reg = ".*";

	nmatches = num_matches(reg);
	freg = regcomp(reg);
	
	if(mtpt == nil)
		mtpt=(smprint("/n/actionfs"));
	chdir(tmp);
	postmountsrv(&numsrv, service, mtpt, MREPL);
	free(mtpt);
	exits(nil);
}

/*

action fs is invoked with a regular expression

when reads & writes are performed on /n/actionfs/ the actions specified  [default /bin/action-read /bin/action-write /bin/action-stat] are executed command line args : fd matches

/bin/tst 
#!/bin/rc
{
	date
	echo $* 
	echo hello
} > /fd/$1


rm /srv/actionfs; actionfs -r /bin/tst [0-9]+; cat /n/actionfs/41df
Sun Aug 16 08:09:55 GMT 2009
3 41
hello

for reads, the command is executed on open
for writes, the command is executed on clunk


3 3  Zx 3   x

which is 'match[0], matched text, match[0], match[1]'

BUGS

/bin/action-stat not implemented

*/

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.