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