<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/geoff/3cmdfs.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/geoff/3cmdfs.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
/*
 * 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 &lt;u.h&gt;
#include &lt;libc.h&gt;
#include &lt;bio.h&gt;
#include &lt;auth.h&gt;
#include &lt;fcall.h&gt;
#include &lt;thread.h&gt;
#include &lt;9p.h&gt;

#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(&amp;f-&gt;ref);
	while (f &amp;&amp; (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(&amp;f-&gt;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(&amp;f-&gt;ref);
		return nf;
	}
	/* another race */
	decref(&amp;f-&gt;ref);
	return fcreate(f, name, u, g, CHEXCL|m);
}

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

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

// run cmd with fd0 &amp; 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) &lt; 0)
		sysfatal("can't make a pipe: %r");
	aux = emalloc(sizeof(Aux));
	aux-&gt;pid = connect(cmd, pipefds[Rd], dup(1, -1), pipefds[Wr]);
	aux-&gt;fd = pipefds[Wr];
	aux-&gt;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-&gt;file, *root = filetree-&gt;root;

	if (qid-&gt;path&amp;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-&gt;file = file =
			fcreate(root, name, root-&gt;uid, root-&gt;gid, CHEXCL|perm);
	}
	assert(file != nil);
	*qid = fid-&gt;qid = file-&gt;qid;

	if (file-&gt;aux == nil &amp;&amp; !(perm&amp;CHDIR) &amp;&amp; (omode&amp;OMASK) == OWRITE)
		file-&gt;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-&gt;qid.path&amp;CHDIR) {
		// Dir d;
		// convD2M(&amp;d, &amp;fid-&gt;file-&gt;Dir);
		(void) fdirread(fid-&gt;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-&gt;file;

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

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

	if (file != nil)
		aux = file-&gt;aux;
	if (aux == nil)
		return;
	assert(aux-&gt;magic == Auxmag);
	if (verbose)
		fprint(2, "fileclunkaux: freeing aux %lux for file %lux\n",
			(ulong)aux, (ulong)fid-&gt;file);
	close(aux-&gt;fd);		/* let cmd run to completion */
	aux-&gt;fd = -1;
	aux-&gt;pid = -1;		/* we don't care when it finishes */
	aux-&gt;magic = 0;
	free(aux);
	file-&gt;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(&amp;filesrv, nil, mntpt, flag);
	exits(0);
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:webmaster@plan9.bell-labs.com">webmaster@plan9.bell-labs.com</a>.</span>
</font></center>
</body>
</html>