/*
* 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);
}
|