#include <u.h>
#include <libc.h>
#include <bio.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
#include "snap.h"

typedef struct PD PD;
struct PD {
       int isproc;
       union {
               Proc *p;
               Data *d;
       };
};

PD*
PDProc(Proc *p)
{
       PD *pd;

       pd = emalloc(sizeof(*pd));
       pd->isproc = 1;
       pd->p = p;
       return pd;
}

PD*
PDData(Data *d)
{
       PD *pd;

       pd = emalloc(sizeof(*pd));
       pd->isproc = 0;
       pd->d = d;
       return pd;
}

void
usage(void)
{
       fprint(2, "usage: snapfs [-a] [-m mtpt] file\n");
       exits("usage");
}

char*
memread(Proc *p, File *f, void *buf, long *count, vlong offset)
{
       Page *pg;
       int po;

       po = offset%Pagesize;
       if(!(pg = findpage(p, p->pid, f->name[0], offset-po)))
               return "address not mapped";

       if(*count > Pagesize-po)
               *count = Pagesize-po;

       memmove(buf, pg->data+po, *count);
       return nil;
}

char*
dataread(Data *d, void *buf, long *count, vlong offset)
{
       assert(d != nil);

       if(offset >= d->len) {
               *count = 0;
               return nil;
       }

       if(offset+*count >= d->len)
               *count = d->len - offset;

       memmove(buf, d->data+offset, *count);
       return nil;
}

void
fsread(Req *r)
{
       char *e;
       PD *pd;
       Fid *fid;
       void *data;
       vlong offset;
       long count;

       fid = r->fid;
       data = r->ofcall.data;
       offset = r->ifcall.offset;
       count = r->ifcall.count;
       pd = fid->file->aux;

       if(pd->isproc)
               e = memread(pd->p, fid->file, data, &count, offset);
       else
               e = dataread(pd->d, data, &count, offset);

       if(e == nil)
               r->ofcall.count = count;
       respond(r, e);
}

Srv fs = {
       .read = fsread,
};

File*
ecreatefile(File *a, char *b, char *c, ulong d, void *e)
{
       File *f;

       f = createfile(a, b, c, d, e);
       if(f == nil)
               sysfatal("error creating snap tree: %r");
       return f;
}

void
main(int argc, char **argv)
{
       Biobuf *b;
       Data *d;
       File *fdir, *f;
       Proc *p, *plist;
       Tree *tree;
       char *mtpt, buf[32];
       int i, mflag;

       mtpt = "/proc";
       mflag = MBEFORE;

       ARGBEGIN{
       case 'D':
               chatty9p++;
               break;
       case 'd':
               debug++;
               break;
       case 'a':
               mflag = MAFTER;
               break;
       case 'm':
               mtpt = ARGF();
               break;
       default:
               usage();
       }ARGEND

       if(argc != 1)
               usage();

       if((b = Bopen(argv[0], OREAD)) == nil)
               sysfatal("cannot open \"%s\": %r", argv[0]);

       if((plist = readsnap(b)) == nil)
               sysfatal("readsnap fails");

       tree = alloctree(nil, nil, DMDIR|0555, nil);
       fs.tree = tree;

       for(p=plist; p; p=p->link) {
               print("process %ld %.*s\n", p->pid, 28, p->d[Pstatus] ? p->d[Pstatus]->data : "");

               snprint(buf, sizeof buf, "%ld", p->pid);
               fdir = ecreatefile(tree->root, buf, nil, DMDIR|0555, nil);
               ecreatefile(fdir, "ctl", nil, 0777, nil);
               if(p->text)
                       ecreatefile(fdir, "text", nil, 0777, PDProc(p));

               ecreatefile(fdir, "mem", nil, 0666, PDProc(p));
               for(i=0; i<Npfile; i++) {
                       if(d = p->d[i]) {
                               f = ecreatefile(fdir, pfile[i], nil, 0666, PDData(d));
                               f->length = d->len;
                       }
               }
       }

       postmountsrv(&fs, nil, mtpt, mflag);
       exits(0);
}