Anyway, it's here:
% mk install
% watchfs main.c
"cat /n/watch/ctl" will block until a change has been made
to /n/watch/data/main.c.
KNOWN BUGS:
- it doesn't care about Tflush yet.
- it only accepts files in the current directory.
# To unbundle, run this file
echo mkfile
sed 's/.//' >mkfile <<'//GO.SYSIN DD mkfile'
-</$objtype/mkfile
-BIN=$home/bin/$objtype
-
-TARG=watchfs
-OFILES=\
- main.$O\
-
-</sys/src/cmd/mkone
//GO.SYSIN DD mkfile
echo main.c
sed 's/.//' >main.c <<'//GO.SYSIN DD main.c'
-#include <u.h>
-#include <libc.h>
-#include <auth.h>
-#include <fcall.h>
-#include <thread.h>
-#include <9p.h>
-
-int dbg;
-
-char *uname;
-Channel *cctl, *cctlwait;
-Channel *pctl, *pctlwait;
-Channel *creq;
-
-typedef struct Watchfile Watchfile;
-struct Watchfile {
- Ref;
- Qid qid;
- int fd;
- char *name;
- int dirty;
-};
-
-Watchfile wtab[10];
-int nw;
-
-void wtopen(Req*);
-void wtread(Req*);
-void wtwrite(Req*);
-void wtdestroyfid(Fid*);
-void takedown(Srv*);
-
-enum{
- Qdata= 0,
- Qctl= 1,
-
- STACK = 8192,
-};
-
-int chatty9p;
-Srv fs = {
- .open= wtopen,
- .read= wtread,
- .write= wtwrite,
- .destroyfid = wtdestroyfid,
- .end= takedown,
-};
-
-static void
-dumpQid(Dir *d)
-{
- ulong path;
- path = d->qid.path;
- fprint(2, "name=%s qid=%lux\n", d->name, path);
-}
-
-static void
-dumpWF(void)
-{
- int i;
-
- for(i=0; i<nw; i++)
- fprint(2, "dumpWF: %s %llud\n", wtab[i].name, wtab[i].qid.path);
-}
-
-Watchfile *
-findtab(ulong path)
-{
- int i;
- for(i=0; i<nw; i++)
- if(wtab[i].qid.path == path)
- return &wtab[i];
- return nil;
-}
-
-void
-wtopen(Req *r)
-{
- ulong path;
- Watchfile *wf;
-
- path = r->fid->qid.path;
- switch(path){
- case Qctl:
- case Qdata:
- respond(r, nil);
- return;
- }
-
- /* data/... */
- wf = findtab(path);
- if(wf == nil){
- respond(r, "no file");
- return;
- }
- if(dbg)
- fprint(2, "wtopen: %s %ld fd %d\n", wf->name, wf->ref, wf->fd);
-
- incref(wf);
- if(wf->fd < 0){
- wf->fd = open(wf->name, r->ifcall.mode);
- wf->dirty = 0;
- }
- if(r->ifcall.mode&OTRUNC)
- r->fid->file->length = 0;
- respond(r, nil);
-}
-
-void
-wtread(Req *r)
-{
- char buf[512];
- int n;
- ulong path;
- Watchfile *wf;
-
- path = r->fid->qid.path;
- switch(path){
- case Qdata:
- case Qctl:
- sendp(creq, r);
- return;
- }
-
- /* data/... */
- wf = findtab(r->fid->qid.path);
- if(wf == nil){
- respond(r, "no such qid");
- return;
- }
- if(wf->fd < 0){
- respond(r, "no fd");
- return;
- }
- if(dbg)
- fprint(2, "wtread: %s ref %ld fd %d\n", wf->name, wf->ref,
wf->fd);
- n = pread(wf->fd, r->ofcall.data, r->ifcall.count, r->ifcall.offset);
- if(n < 0){
- rerrstr(buf, sizeof buf);
- respond(r, buf);
- return;
- }
- r->ofcall.count = n;
- respond(r, nil);
-}
-
-void
-wtwrite(Req *r)
-{
- char buf[512];
- int n;
- ulong path;
- vlong offset;
- Watchfile *wf;
-
- path = r->fid->qid.path;
- offset = r->ifcall.offset;
-
- switch(path){
- case Qdata:
- case Qctl:
- snprint(buf, sizeof buf, "not yet %lux", path);
- respond(r, buf);
- return;
- }
-
- /* data/... */
- wf = findtab(path);
- if(wf == nil){
- respond(r, "no such qid");
- return;
- }
- if(dbg)
- fprint(2, "wtwrite: %s ref %ld fd %d\n", wf->name, wf->ref,
wf->fd);
-
- if(wf->fd < 0){
- respond(r, "no fd");
- return;
- }
-
- n = pwrite(wf->fd, r->ifcall.data, r->ifcall.count, r->ifcall.offset);
- if(n < 0){
- rerrstr(buf, sizeof buf);
- respond(r, buf);
- return;
- }
- if(n+offset >= r->fid->file->length)
- r->fid->file->length = n+offset;
- wf->dirty++;
- r->ofcall.count = n;
- respond(r, nil);
-
-}
-
-void
-wtdestroyfid(Fid *fid)
-{
- ulong path;
- Watchfile *wf;
-
- path = fid->qid.path;
- wf = findtab(path);
- if(wf == nil)
- return;
-
- if(dbg)
- fprint(2, "wtdestroyfid: %s ref %ld fd %d\n", wf->name,
wf->ref, wf->fd);
-
- if(wf->fd >= 0){
- if(decref(wf) == 0){
- if(wf->dirty){
- sendp(cctl, wf);
- recvp(cctlwait);
- }
- close(wf->fd);
- wf->fd = -1;
- wf->dirty = 0;
- }
- }
-}
-
-Tree *
-inittree(char **argv)
-{
- Tree *tree;
- File *df;
-
- tree = alloctree(uname, uname, DMDIR|0555, nil);
- df = createfile(tree->root, "data", uname, DMDIR|0555, nil);
- createfile(tree->root, "ctl", uname, 0660, nil);
-
- for(;*argv;argv++){
- File *f;
- Dir *d;
- d = dirstat(*argv);
- if(d == nil)
- continue;
- if(dbg)
- fprint(2, "dir: name %s len %lld\n", d->name,
d->length);
- f = createfile(df, *argv, uname, 0660, nil);
- wtab[nw].qid = f->qid;
- wtab[nw].name = estrdup9p(*argv);
- wtab[nw].fd = -1;
- f->length = d->length;
- nw++;
- }
-
- return tree;
-}
-
-static void
-pollthread(void*)
-{
- for(;;){
- sleep(1);
- }
-}
-
-static void
-ctlthread(void*)
-{
- int i, np = 0;
- Alt a[4];
- Req *r, *rtab[10];
- Watchfile *wf;
-
- threadsetname("ctlthread");
-
- a[0].op = CHANRCV;
- a[0].c = creq;
- a[0].v = &r;
- a[1].op = CHANRCV;
- a[1].c = cctl;
- a[1].v = &wf;
- a[2].op = CHANRCV;
- a[2].c = pctl;
- a[2].v = &wf;
- a[3].op = CHANEND;
-
- for(;;){
- switch(alt(a)){
- case 0: /* creq: someone tries to read ctl */
- rtab[np++] = r;
- break;
-
- case 1: /* cctl: ready to serve ctl */
- for(i=0; i<np; i++){
- r = rtab[i];
- snprint(r->ofcall.data, r->ifcall.count,
- "%s %llud\n", wf->name, wf->qid.path);
- r->ofcall.count = strlen(r->ofcall.data);
- respond(r, nil);
- }
- np = 0;
- sendp(cctlwait, 0);
- break;
-
- case 2: /* pctl: poll says modified */
- for(i=0; i<np; i++){
- r = rtab[i];
- snprint(r->ofcall.data, r->ifcall.count,
- "%s %llud\n", wf->name, wf->qid.path);
- r->ofcall.count = strlen(r->ofcall.data);
- respond(r, nil);
- }
- np = 0;
- sendp(pctlwait, 0);
- break;
-
- default:
- break;
- }
- }
-}
-
-void
-initctl(void)
-{
- cctl = chancreate(sizeof(void*), 0);
- cctlwait = chancreate(sizeof(void*), 0);
- pctl = chancreate(sizeof(void*), 0);
- pctlwait = chancreate(sizeof(void*), 0);
- creq = chancreate(sizeof(void*), 0);
- procrfork(ctlthread, nil, STACK, RFNAMEG);
- procrfork(pollthread, nil, STACK, RFNAMEG);
-}
-
-void
-usage(void)
-{
- fprint(2, "usage: watchfs files...\n");
-}
-
-void
-threadmain(int argc, char *argv[])
-{
-
- ARGBEGIN{
- case 'd':
- dbg++;
- break;
- case 'D':
- chatty9p++;
- break;
- }ARGEND;
-
- uname = getuser();
-
- fs.tree = inittree(&argv[0]);
- if(dbg)
- dumpWF();
-
- initctl();
- threadpostmountsrv(&fs, nil, "/n/watch", MREPL|MCREATE);
- threadexits(0);
-}
-
-void
-takedown(Srv*)
-{
- threadexitsall("done");
-}
-
-
-
-
//GO.SYSIN DD main.c