9fans - fans of the OS Plan 9 from Bell Labs
 help / color / mirror / Atom feed
* [9fans] command file server
@ 2002-04-20  9:58 Geoff Collyer
  0 siblings, 0 replies; only message in thread
From: Geoff Collyer @ 2002-04-20  9:58 UTC (permalink / raw)
  To: 9fans

You run it, it serves a directory containing one file, named "pipe".
You can then bind "pipe" anywhere you want.  Every time a program
opens pipe, writes data into it, and closes it, cmdfs starts a new
invocation of a named command and copies the data to its standard
input.

My first use for it is to bind "pipe" over the mailbox where
upas/filter drops anything that looks like spam (that the smtpd spam
filters missed).  I'll have cmdfs start "mail spamcop" so that when a
spam message arrives, it will be immediately forwarded to spamcop.


# To unbundle, run this file
echo cmdfs.c
sed 's/^X//' >cmdfs.c <<'!'
X/*
X * cmdfs mntpt cmd - mount this file server, writes into which become
X *	writes to a pipe to cmd.  a new cmd is started for each open
X *	of mntpt/pipe, but only one is permitted at a time.
X */
X#include <u.h>
X#include <libc.h>
X#include <bio.h>
X#include <auth.h>
X#include <fcall.h>
X#include <thread.h>
X#include <9p.h>

X#define TEMPDIR "/n/temp"
X#define TEMPFILE "pipe"

enum {
X	Rd, Wr,
X	Auxmag = 0xb1ffd00d,	/* none genuine without it */
X};
enum {
X	Open, Create,
X};

X/*
X * I think Aux should contain a reference count, but we're getting away
X * without one for now.
X */
typedef struct {
X	int	fd;		/* write here to reach cmd */
X	int	pid;		/* of cmd */
X	ulong	magic;
X} Aux;

static Tree *filetree;
static int verbose;
static char *mntpt = TEMPDIR, *cmd = "";

static void*
emalloc(long sz)
X{
X	void *v = mallocz(sz, 1);

X	if (v == nil)
X		sysfatal("malloc %lud fails\n", sz);
X	return v;
X}

static char*
estrdup(char *s)
X{
X	s = strdup(s);
X	if (s == nil)
X		sysfatal("strdup (%.10s) fails\n", s);
X	return s;
X}

static File*
fcreatewalk(File *f, char *name, char *u, char *g, ulong m)
X{
X	char elem[NAMELEN];
X	char *p;
X	File *nf;

X	if (verbose)
X		fprint(2, "fcreatewalk %s\n", name);
X	incref(&f->ref);
X	while (f && (p = strchr(name, '/'))) {
X		memmove(elem, name, p-name);
X		elem[p-name] = '\0';
X		name = p+1;
X		if (strcmp(elem, "") == 0)
X			continue;
X		/* this would be a race if we were multithreaded */
X		if (verbose)
X			fprint(2, "fcreatewalk: fwalk/fcreate1 %s\n", elem);
X		decref(&f->ref);
X		nf = fwalk(f, elem);
X		if (nf == nil)
X			nf = fcreate(f, elem, u, g, CHDIR|0775);
X		f = nf;
X	}
X	if (f == nil)
X		return nil;

X	if (verbose)
X		fprint(2, "fcreatewalk: fwalk/fcreate2 %s\n", name);
X	if (nf = fwalk(f, name)) {
X		decref(&f->ref);
X		return nf;
X	}
X	/* another race */
X	decref(&f->ref);
X	return fcreate(f, name, u, g, CHEXCL|m);
X}

static void
createfile(char *name)
X{
X	File *root = filetree->root;
X	File *f = fcreatewalk(root, name, root->uid, root->gid, 0664);

X	if (f == nil)
X		sysfatal("creating %s: %r", name);
X	f->aux = nil;			// unused
X	f->mtime = f->atime = time(nil);
X	f->length = 0;
X	decref(&f->ref);
X}

X// run cmd with fd0 & fd1 as stdin and stdout (then close them in parent)
int
connect(char *cmd, int fd0, int fd1, int closeme)
X{
X	int pid = rfork(RFFDG|RFREND|RFPROC|RFNOWAIT);

X	switch (pid) {
X	case -1:
X		sysfatal("fork %s: %r", cmd);
X		return -1;
X	default:
X		close(fd0);
X		close(fd1);
X		return pid;
X	case 0:
X		close(closeme);
X		dup(fd0, 0);
X		dup(fd1, 1);
X		close(fd0);
X		close(fd1);
X		execl("/bin/rc", "rc", "-c", cmd, nil);
X		sysfatal("exec %s: %r", cmd);
X		return 0;
X	}
X}

static Aux *
newconn(char *cmd)
X{
X	int pipefds[2];
X	Aux *aux;

X	if (pipe(pipefds) < 0)
X		sysfatal("can't make a pipe: %r");
X	aux = emalloc(sizeof(Aux));
X	aux->pid = connect(cmd, pipefds[Rd], dup(1, -1), pipefds[Wr]);
X	aux->fd = pipefds[Wr];
X	aux->magic = Auxmag;
X	close(pipefds[Rd]);
X	if (verbose)
X		fprint(2, "newconn: allocated new aux %lux\n", (ulong)aux);
X	return aux;
X}

static void
setupfile(int what, Req *req, Fid *fid, char *name, int omode, ulong perm,
X	Qid *qid)
X{
X	File *file = fid->file, *root = filetree->root;

X	if (qid->path&CHDIR)
X		perm |= CHDIR|0111;
X	if (verbose)
X		fprint(2, "setupfile: %s file %lux\n", name, (ulong)file);
X	if (what == Create) {
X		if (file != nil)
X			fclose(file);
X		/*
X		 * fcreate assigns qids, starting paths at zero for both
X		 * dir.s and plain files, so we just have to have faith
X		 * despite the bogus-looking qids we get at first from it.
X		 */
X		fid->file = file =
X			fcreate(root, name, root->uid, root->gid, CHEXCL|perm);
X	}
X	assert(file != nil);
X	*qid = fid->qid = file->qid;

X	if (file->aux == nil && !(perm&CHDIR) && (omode&OMASK) == OWRITE)
X		file->aux = newconn(cmd);
X	respond(req, nil);
X}

X/*
X * "pipe" is created by hand in main(), so fileopen will be called for it,
X * thus some setup will have to be done in setupfile in either case.
X */
static void
fileopen(Req *req, Fid *fid, int omode, Qid *qid)
X{
X	setupfile(Open, req, fid, TEMPFILE, omode, 0664, qid);
X}

static void
filecreate(Req *req, Fid *fid, char *name, int omode, ulong perm, Qid *qid)
X{
X	setupfile(Create, req, fid, name, omode, perm, qid);
X}

X/* shouldn't be called; paranoia */
static void
fileread(Req *r, Fid *fid, void *buf, long *count, vlong offset)
X{
X	char err[ERRLEN];

X	if (fid->qid.path&CHDIR) {
X		// Dir d;
X		// convD2M(&d, &fid->file->Dir);
X		(void) fdirread(fid->file, buf, count, offset);
X		respond(r, nil);
X	}
X	strncpy(err, "can't read from commands", ERRLEN);
X	respond(r, err);
X}

static void
filewrite(Req *r, Fid *fid, void *buf, long *count, vlong offset)
X{
X	long n;
X	char err[ERRLEN];
X	Aux *aux = nil;
X	File *file = fid->file;

X	USED(offset);
X	werrstr("");
X	if (file != nil)
X		aux = file->aux;
X	assert(aux == nil || aux->magic == Auxmag);
X	if (aux == nil || (n = write(aux->fd, buf, *count)) != *count) {
X		err[0] = '\0';
X		errstr(err);
X		respond(r, err);
X	} else {
X		file->qid.vers++;
X		file->mtime = time(nil);
X		*count = n;
X		respond(r, nil);
X	}
X}

static void
fileclunkaux(Fid *fid)
X{
X	File *file = fid->file;
X	Aux *aux = nil;

X	if (file != nil)
X		aux = file->aux;
X	if (aux == nil)
X		return;
X	assert(aux->magic == Auxmag);
X	if (verbose)
X		fprint(2, "fileclunkaux: freeing aux %lux for file %lux\n",
X			(ulong)aux, (ulong)fid->file);
X	close(aux->fd);		/* let cmd run to completion */
X	aux->fd = -1;
X	aux->pid = -1;		/* we don't care when it finishes */
X	aux->magic = 0;
X	free(aux);
X	file->aux = nil;
X}

Srv filesrv = {
X	.open=	fileopen,
X	.create=filecreate,
X	.read=	fileread,
X	.write=	filewrite,
X	.clunkaux=fileclunkaux,
X};

static void
usage(void)
X{
X	fprint(2, "usage: %s [-abcC] [-m mntpt] cmd\n", argv0);
X	exits("usage");
X}

void
main(int argc, char **argv)
X{
X	char *user = getuser();
X	ulong flag = MBEFORE;

X	ARGBEGIN {
X	case 'a':
X		flag |= MAFTER;
X		break;
X	case 'b':
X		flag |= MBEFORE;
X		break;
X	case 'c':
X		flag |= MCREATE;
X		break;
X	case 'C':
X		flag |= MCACHE;
X		break;
X	case 'm':
X		mntpt = ARGF();
X		break;
X	case 'v':
X		verbose = 1;
X		break;
X	default:
X		usage();
X		break;
X	} ARGEND;

X	if (argc != 1)
X		usage();
X	cmd = argv[0];

X	filetree = filesrv.tree = mktree(user, user, CHDIR|0775);
X	createfile(TEMPFILE);

X	postmountsrv(&filesrv, nil, mntpt, flag);
X	exits(0);
X}
!


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2002-04-20  9:58 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2002-04-20  9:58 [9fans] command file server Geoff Collyer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).