#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <mach.h>
#define Extern extern
#include "acid.h"
#include "y.tab.h"

static void install(int);

void
nocore(void)
{
       int i;

       if(cormap == 0)
               return;

       for (i = 0; i < cormap->nsegs; i++)
               if (cormap->seg[i].inuse && cormap->seg[i].fd >= 0)
                       close(cormap->seg[i].fd);
       free(cormap);
       cormap = 0;
}

void
sproc(int pid)
{
       Lsym *s;
       char buf[64];
       int i, fcor;

       if(symmap == 0)
               error("no map");

       snprint(buf, sizeof(buf), "/proc/%d/mem", pid);
       fcor = open(buf, ORDWR);
       if(fcor < 0)
               error("setproc: open %s: %r", buf);

       checkqid(symmap->seg[0].fd, pid);

       s = look("pid");
       s->v->ival = pid;

       nocore();
       cormap = attachproc(pid, kernel, fcor, &fhdr);
       if (cormap == 0)
               error("setproc: can't make coremap: %r");
       i = findseg(cormap, "text");
       if (i > 0)
               cormap->seg[i].name = "*text";
       i = findseg(cormap, "data");
       if (i > 0)
               cormap->seg[i].name = "*data";
       install(pid);
}

int
nproc(char **argv)
{
       char buf[128];
       int pid, i, fd;

       pid = fork();
       switch(pid) {
       case -1:
               error("new: fork %r");
       case 0:
               rfork(RFNAMEG|RFNOTEG);

               snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
               fd = open(buf, ORDWR);
               if(fd < 0)
                       fatal("new: open %s: %r", buf);
               write(fd, "hang", 4);
               close(fd);

               close(0);
               close(1);
               close(2);
               for(i = 3; i < NFD; i++)
                       close(i);

               open("/dev/cons", OREAD);
               open("/dev/cons", OWRITE);
               open("/dev/cons", OWRITE);
               exec(argv[0], argv);
               fatal("new: exec %s: %r", argv[0]);
       default:
               install(pid);
               msg(pid, "waitstop");
               notes(pid);
               sproc(pid);
               dostop(pid);
               break;
       }

       return pid;
}

void
notes(int pid)
{
       Lsym *s;
       Value *v;
       int i, fd;
       char buf[128];
       List *l, **tail;

       s = look("notes");
       if(s == 0)
               return;
       v = s->v;

       snprint(buf, sizeof(buf), "/proc/%d/note", pid);
       fd = open(buf, OREAD);
       if(fd < 0)
               error("pid=%d: open note: %r", pid);

       v->set = 1;
       v->type = TLIST;
       v->l = 0;
       tail = &v->l;
       for(;;) {
               i = read(fd, buf, sizeof(buf));
               if(i <= 0)
                       break;
               buf[i] = '\0';
               l = al(TSTRING);
               l->string = strnode(buf);
               l->fmt = 's';
               *tail = l;
               tail = &l->next;
       }
       close(fd);
}

void
dostop(int pid)
{
       Lsym *s;
       Node *np, *p;

       s = look("stopped");
       if(s && s->proc) {
               np = an(ONAME, ZN, ZN);
               np->sym = s;
               np->fmt = 'D';
               np->type = TINT;
               p = con(pid);
               p->fmt = 'D';
               np = an(OCALL, np, p);
               execute(np);
       }
}

static void
install(int pid)
{
       Lsym *s;
       List *l;
       char buf[128];
       int i, fd, new, p;

       new = -1;
       for(i = 0; i < Maxproc; i++) {
               p = ptab[i].pid;
               if(p == pid)
                       return;
               if(p == 0 && new == -1)
                       new = i;
       }
       if(new == -1)
               error("no free process slots");

       snprint(buf, sizeof(buf), "/proc/%d/ctl", pid);
       fd = open(buf, OWRITE);
       if(fd < 0)
               error("pid=%d: open ctl: %r", pid);
       ptab[new].pid = pid;
       ptab[new].ctl = fd;

       s = look("proclist");
       l = al(TINT);
       l->fmt = 'D';
       l->ival = pid;
       l->next = s->v->l;
       s->v->l = l;
       s->v->set = 1;

       callhook("procattach");
}

void
deinstall(int pid)
{
       int i;
       Lsym *s;
       List *f, **d;

       for(i = 0; i < Maxproc; i++) {
               if(ptab[i].pid == pid) {
                       close(ptab[i].ctl);
                       ptab[i].pid = 0;
                       s = look("proclist");
                       d = &s->v->l;
                       for(f = *d; f; f = f->next) {
                               if(f->ival == pid) {
                                       *d = f->next;
                                       break;
                               }
                       }
                       s = look("pid");
                       if(s->v->ival == pid)
                               s->v->ival = 0;
                       return;
               }
       }
}

void
msg(int pid, char *msg)
{
       int i;
       int l;
       char err[ERRMAX];

       for(i = 0; i < Maxproc; i++) {
               if(ptab[i].pid == pid) {
                       l = strlen(msg);
                       if(write(ptab[i].ctl, msg, l) != l) {
                               errstr(err, sizeof err);
                               if(strcmp(err, "process exited") == 0)
                                       deinstall(pid);
                               error("msg: pid=%d %s: %s", pid, msg, err);
                       }
                       return;
               }
       }
       error("msg: pid=%d: not found for %s", pid, msg);
}

char *
getstatus(int pid)
{
       int fd, n;
       char *argv[16], buf[64];
       static char status[128];

       snprint(buf, sizeof(buf), "/proc/%d/status", pid);
       fd = open(buf, OREAD);
       if(fd < 0)
               error("open %s: %r", buf);

       n = read(fd, status, sizeof(status)-1);
       close(fd);
       if(n <= 0)
               error("read %s: %r", buf);
       status[n] = '\0';

       if(tokenize(status, argv, nelem(argv)-1) < 3)
               error("tokenize %s: %r", buf);

       return argv[2];
}

Waitmsg*
waitfor(int pid)
{
       Waitmsg *w;

       for(;;) {
               if((w = wait()) == nil)
                       error("wait %r");
               if(w->pid == pid)
                       return w;
               free(w);
       }
}