Plan 9 from Bell Labs’s /usr/web/sources/contrib/geoff/3cmdfs.c

Copyright © 2009 Alcatel-Lucent.
Distributed under the Lucent Public License version 1.02.
Download the Plan 9 distribution.


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2009 Alcatel-Lucent. All Rights Reserved.
Comments to webmaster@plan9.bell-labs.com.