#include "all.h"
#include        "/sys/include/fcall.h"

static int
readmsg(Chan *c, void *abuf, int n, int *ninep)
{
       int fd, len;
       uchar *buf;

       buf = abuf;
       fd = c->chan;
       qlock(&c->rlock);
       if(readn(fd, buf, 3) != 3){
               qunlock(&c->rlock);
               print("readn(3) fails: %r\n");
               return -1;
       }
       if((50 <= buf[0] && buf[0] <= 87 && (buf[0]&1)==0 && GBIT16(buf+1) == 0xFFFF)
       || buf[0] == 86 /* Tattach */){
               *ninep = 1;
               /* assume message boundaries */
               n = read(fd, buf+3, n-3);
               if(n < 0){
                       qunlock(&c->rlock);
                       return -1;
               }
               return n+3;
       }

       *ninep = 2;
       if(read(fd, buf+3, 1) != 1){
               qunlock(&c->rlock);
               print("read(1) fails: %r\n");
               return -1;
       }
       len = GBIT32(buf);
       if(len > n){
               print("msg too large\n");
               qunlock(&c->rlock);
               return -1;
       }
       if(readn(fd, buf+4, len-4) != len-4){
               print("readn(%d) fails: %r\n", len-4);
               qunlock(&c->rlock);
               return -1;
       }
       qunlock(&c->rlock);
       return len;
}

void
startserveproc(void (*f)(Chan*, uchar*, int), char *name, Chan *c, uchar *b, int nb)
{
       switch(rfork(RFMEM|RFFDG|RFPROC)){
       case -1:
               panic("can't fork");
       case 0:
               break;
       default:
               return;
       }
       procname = name;
       f(c, b, nb);
       _exits(nil);
}

void
serve(Chan *chan)
{
       int i, nin, p9;
       uchar inbuf[1024];
       void (*s)(Chan*, uchar*, int);

       p9 = 0;
       if((nin = readmsg(chan, inbuf, sizeof inbuf, &p9)) < 0)
               return;

       switch(p9){
       default:
               print("unknown 9P type\n");
               return;
       case 1:
               s = serve9p1;
               break;
       case 2:
               s = serve9p2;
               break;
       }

       for(i=1; i<conf.nserve; i++)
               startserveproc(s, "srv", chan, nil, 0);
       (*s)(chan, inbuf, nin);
}