427 lines
6.5 KiB
C
427 lines
6.5 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <fcall.h>
|
|
#include <thread.h>
|
|
#include <9p.h>
|
|
#include "dat.h"
|
|
|
|
int nclient;
|
|
Client **client;
|
|
#define Zmsg ((Msg*)~0)
|
|
char nocmd[] = "";
|
|
|
|
static void readthread(void*);
|
|
static void writethread(void*);
|
|
static void kickwriter(Client*);
|
|
|
|
int
|
|
newclient(void)
|
|
{
|
|
int i;
|
|
Client *c;
|
|
|
|
for(i=0; i<nclient; i++)
|
|
if(client[i]->ref==0 && !client[i]->moribund)
|
|
return i;
|
|
|
|
c = emalloc(sizeof(Client));
|
|
c->writerkick = chancreate(sizeof(void*), 1);
|
|
c->execpid = chancreate(sizeof(ulong), 0);
|
|
c->cmd = nocmd;
|
|
|
|
c->readerproc = ioproc();
|
|
c->writerproc = ioproc();
|
|
c->num = nclient;
|
|
if(nclient%16 == 0)
|
|
client = erealloc(client, (nclient+16)*sizeof(client[0]));
|
|
client[nclient++] = c;
|
|
return nclient-1;
|
|
}
|
|
|
|
void
|
|
die(Client *c)
|
|
{
|
|
Msg *m, *next;
|
|
Req *r, *rnext;
|
|
|
|
c->moribund = 1;
|
|
kickwriter(c);
|
|
iointerrupt(c->readerproc);
|
|
iointerrupt(c->writerproc);
|
|
if(--c->activethread == 0){
|
|
if(c->cmd != nocmd){
|
|
free(c->cmd);
|
|
c->cmd = nocmd;
|
|
}
|
|
c->pid = 0;
|
|
c->moribund = 0;
|
|
c->status = Closed;
|
|
for(m=c->mq; m && m != Zmsg; m=next){
|
|
next = m->link;
|
|
free(m);
|
|
}
|
|
c->mq = nil;
|
|
if(c->rq != nil){
|
|
for(r=c->rq; r; r=rnext){
|
|
rnext = r->aux;
|
|
respond(r, "hangup");
|
|
}
|
|
c->rq = nil;
|
|
}
|
|
if(c->wq != nil){
|
|
for(r=c->wq; r; r=rnext){
|
|
rnext = r->aux;
|
|
respond(r, "hangup");
|
|
}
|
|
c->wq = nil;
|
|
}
|
|
c->rq = nil;
|
|
c->wq = nil;
|
|
c->emq = nil;
|
|
c->erq = nil;
|
|
c->ewq = nil;
|
|
}
|
|
}
|
|
|
|
void
|
|
closeclient(Client *c)
|
|
{
|
|
if(--c->ref == 0){
|
|
if(c->pid > 0)
|
|
postnote(PNPROC, c->pid, "kill");
|
|
c->status = Hangup;
|
|
close(c->fd[0]);
|
|
c->fd[0] = c->fd[1] = -1;
|
|
c->moribund = 1;
|
|
kickwriter(c);
|
|
iointerrupt(c->readerproc);
|
|
iointerrupt(c->writerproc);
|
|
c->activethread++;
|
|
die(c);
|
|
}
|
|
}
|
|
|
|
void
|
|
queuerdreq(Client *c, Req *r)
|
|
{
|
|
if(c->rq==nil)
|
|
c->erq = &c->rq;
|
|
*c->erq = r;
|
|
r->aux = nil;
|
|
c->erq = (Req**)&r->aux;
|
|
}
|
|
|
|
void
|
|
queuewrreq(Client *c, Req *r)
|
|
{
|
|
if(c->wq==nil)
|
|
c->ewq = &c->wq;
|
|
*c->ewq = r;
|
|
r->aux = nil;
|
|
c->ewq = (Req**)&r->aux;
|
|
}
|
|
|
|
void
|
|
queuemsg(Client *c, Msg *m)
|
|
{
|
|
if(c->mq==nil)
|
|
c->emq = &c->mq;
|
|
*c->emq = m;
|
|
if(m != Zmsg){
|
|
m->link = nil;
|
|
c->emq = (Msg**)&m->link;
|
|
}else
|
|
c->emq = nil;
|
|
}
|
|
|
|
void
|
|
matchmsgs(Client *c)
|
|
{
|
|
Req *r;
|
|
Msg *m;
|
|
int n, rm;
|
|
|
|
while(c->rq && c->mq){
|
|
r = c->rq;
|
|
c->rq = r->aux;
|
|
|
|
rm = 0;
|
|
m = c->mq;
|
|
if(m == Zmsg){
|
|
respond(r, "execnet: no more data");
|
|
break;
|
|
}
|
|
n = r->ifcall.count;
|
|
if(n >= m->ep - m->rp){
|
|
n = m->ep - m->rp;
|
|
c->mq = m->link;
|
|
rm = 1;
|
|
}
|
|
if(n)
|
|
memmove(r->ofcall.data, m->rp, n);
|
|
if(rm)
|
|
free(m);
|
|
else
|
|
m->rp += n;
|
|
r->ofcall.count = n;
|
|
respond(r, nil);
|
|
}
|
|
}
|
|
|
|
void
|
|
findrdreq(Client *c, Req *r)
|
|
{
|
|
Req **l;
|
|
|
|
for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
|
|
if(*l == r){
|
|
*l = r->aux;
|
|
if(*l == nil)
|
|
c->erq = l;
|
|
respond(r, "interrupted");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
findwrreq(Client *c, Req *r)
|
|
{
|
|
Req **l;
|
|
|
|
for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
|
|
if(*l == r){
|
|
*l = r->aux;
|
|
if(*l == nil)
|
|
c->ewq = l;
|
|
respond(r, "interrupted");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
dataread(Req *r, Client *c)
|
|
{
|
|
queuerdreq(c, r);
|
|
matchmsgs(c);
|
|
}
|
|
|
|
static void
|
|
readthread(void *a)
|
|
{
|
|
uchar *buf;
|
|
int n;
|
|
Client *c;
|
|
Ioproc *io;
|
|
Msg *m;
|
|
|
|
c = a;
|
|
threadsetname("read%d", c->num);
|
|
|
|
buf = emalloc(8192);
|
|
io = c->readerproc;
|
|
while((n = ioread(io, c->fd[0], buf, 8192)) >= 0){
|
|
m = emalloc(sizeof(Msg)+n);
|
|
m->rp = (uchar*)&m[1];
|
|
m->ep = m->rp + n;
|
|
if(n)
|
|
memmove(m->rp, buf, n);
|
|
queuemsg(c, m);
|
|
matchmsgs(c);
|
|
}
|
|
queuemsg(c, Zmsg);
|
|
free(buf);
|
|
die(c);
|
|
}
|
|
|
|
static void
|
|
kickwriter(Client *c)
|
|
{
|
|
nbsendp(c->writerkick, nil);
|
|
}
|
|
|
|
void
|
|
clientflush(Req *or, Client *c)
|
|
{
|
|
if(or->ifcall.type == Tread)
|
|
findrdreq(c, or);
|
|
else{
|
|
if(c->execreq == or){
|
|
c->execreq = nil;
|
|
iointerrupt(c->writerproc);
|
|
ioflush(c->writerproc);
|
|
}
|
|
findwrreq(c, or);
|
|
if(c->curw == or){
|
|
c->curw = nil;
|
|
iointerrupt(c->writerproc);
|
|
kickwriter(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
datawrite(Req *r, Client *c)
|
|
{
|
|
queuewrreq(c, r);
|
|
kickwriter(c);
|
|
}
|
|
|
|
static void
|
|
writethread(void *a)
|
|
{
|
|
char e[ERRMAX];
|
|
uchar *buf;
|
|
int n;
|
|
Ioproc *io;
|
|
Req *r;
|
|
Client *c;
|
|
|
|
c = a;
|
|
threadsetname("write%d", c->num);
|
|
|
|
buf = emalloc(8192);
|
|
io = c->writerproc;
|
|
for(;;){
|
|
while(c->wq == nil){
|
|
if(c->moribund)
|
|
goto Out;
|
|
recvp(c->writerkick);
|
|
if(c->moribund)
|
|
goto Out;
|
|
}
|
|
r = c->wq;
|
|
c->wq = r->aux;
|
|
c->curw = r;
|
|
n = iowrite(io, c->fd[1], r->ifcall.data, r->ifcall.count);
|
|
c->curw = nil;
|
|
if(chatty9p)
|
|
fprint(2, "io->write returns %d\n", n);
|
|
if(n >= 0){
|
|
r->ofcall.count = n;
|
|
respond(r, nil);
|
|
}else{
|
|
rerrstr(e, sizeof e);
|
|
respond(r, e);
|
|
}
|
|
}
|
|
Out:
|
|
free(buf);
|
|
die(c);
|
|
}
|
|
|
|
static void
|
|
execproc(void *a)
|
|
{
|
|
int i, fd;
|
|
Client *c;
|
|
|
|
c = a;
|
|
threadsetname("execproc%d", c->num);
|
|
if(pipe(c->fd) < 0){
|
|
rerrstr(c->err, sizeof c->err);
|
|
sendul(c->execpid, -1);
|
|
return;
|
|
}
|
|
rfork(RFFDG);
|
|
fd = c->fd[1];
|
|
close(c->fd[0]);
|
|
dup(fd, 0);
|
|
dup(fd, 1);
|
|
for(i=3; i<100; i++) /* should do better */
|
|
close(i);
|
|
strcpy(c->err, "exec failed");
|
|
procexecl(c->execpid, "/bin/rc", "rc", "-c", c->cmd, nil);
|
|
}
|
|
|
|
static void
|
|
execthread(void *a)
|
|
{
|
|
Client *c;
|
|
int p;
|
|
|
|
c = a;
|
|
threadsetname("exec%d", c->num);
|
|
c->execpid = chancreate(sizeof(ulong), 0);
|
|
proccreate(execproc, c, STACK);
|
|
p = recvul(c->execpid);
|
|
chanfree(c->execpid);
|
|
c->execpid = nil;
|
|
close(c->fd[1]);
|
|
c->fd[1] = c->fd[0];
|
|
if(p != -1){
|
|
c->pid = p;
|
|
c->activethread = 2;
|
|
threadcreate(readthread, c, STACK);
|
|
threadcreate(writethread, c, STACK);
|
|
if(c->execreq)
|
|
respond(c->execreq, nil);
|
|
}else{
|
|
if(c->execreq)
|
|
respond(c->execreq, c->err);
|
|
}
|
|
}
|
|
|
|
void
|
|
ctlwrite(Req *r, Client *c)
|
|
{
|
|
char *f[3], *s, *p;
|
|
int nf;
|
|
|
|
s = emalloc(r->ifcall.count+1);
|
|
memmove(s, r->ifcall.data, r->ifcall.count);
|
|
s[r->ifcall.count] = '\0';
|
|
|
|
f[0] = s;
|
|
p = strchr(s, ' ');
|
|
if(p == nil)
|
|
nf = 1;
|
|
else{
|
|
*p++ = '\0';
|
|
f[1] = p;
|
|
nf = 2;
|
|
}
|
|
|
|
if(f[0][0] == '\0'){
|
|
free(s);
|
|
respond(r, nil);
|
|
return;
|
|
}
|
|
|
|
r->ofcall.count = r->ifcall.count;
|
|
if(strcmp(f[0], "hangup") == 0){
|
|
if(c->pid == 0){
|
|
respond(r, "connection already hung up");
|
|
goto Out;
|
|
}
|
|
postnote(PNPROC, c->pid, "kill");
|
|
respond(r, nil);
|
|
goto Out;
|
|
}
|
|
|
|
if(strcmp(f[0], "connect") == 0){
|
|
if(c->cmd != nocmd){
|
|
respond(r, "already have connection");
|
|
goto Out;
|
|
}
|
|
if(nf == 1){
|
|
respond(r, "need argument to connect");
|
|
goto Out;
|
|
}
|
|
c->status = Exec;
|
|
if(p = strrchr(f[1], '!'))
|
|
*p = '\0';
|
|
c->cmd = emalloc(4+1+strlen(f[1])+1);
|
|
strcpy(c->cmd, "exec ");
|
|
strcat(c->cmd, f[1]);
|
|
c->execreq = r;
|
|
threadcreate(execthread, c, STACK);
|
|
goto Out;
|
|
}
|
|
|
|
respond(r, "bad or inappropriate control message");
|
|
Out:
|
|
free(s);
|
|
}
|