* [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).