19a747e4fSDavid du Colombier #include <u.h>
29a747e4fSDavid du Colombier #include <libc.h>
39a747e4fSDavid du Colombier #include <fcall.h>
49a747e4fSDavid du Colombier #include <thread.h>
59a747e4fSDavid du Colombier #include <9p.h>
69a747e4fSDavid du Colombier #include "dat.h"
79a747e4fSDavid du Colombier
89a747e4fSDavid du Colombier int nclient;
99a747e4fSDavid du Colombier Client **client;
109a747e4fSDavid du Colombier #define Zmsg ((Msg*)~0)
119a747e4fSDavid du Colombier char nocmd[] = "";
129a747e4fSDavid du Colombier
139a747e4fSDavid du Colombier static void readthread(void*);
149a747e4fSDavid du Colombier static void writethread(void*);
159a747e4fSDavid du Colombier static void kickwriter(Client*);
169a747e4fSDavid du Colombier
179a747e4fSDavid du Colombier int
newclient(void)189a747e4fSDavid du Colombier newclient(void)
199a747e4fSDavid du Colombier {
209a747e4fSDavid du Colombier int i;
219a747e4fSDavid du Colombier Client *c;
229a747e4fSDavid du Colombier
239a747e4fSDavid du Colombier for(i=0; i<nclient; i++)
249a747e4fSDavid du Colombier if(client[i]->ref==0 && !client[i]->moribund)
259a747e4fSDavid du Colombier return i;
269a747e4fSDavid du Colombier
279a747e4fSDavid du Colombier c = emalloc(sizeof(Client));
289a747e4fSDavid du Colombier c->writerkick = chancreate(sizeof(void*), 1);
299a747e4fSDavid du Colombier c->execpid = chancreate(sizeof(ulong), 0);
309a747e4fSDavid du Colombier c->cmd = nocmd;
319a747e4fSDavid du Colombier
329a747e4fSDavid du Colombier c->readerproc = ioproc();
339a747e4fSDavid du Colombier c->writerproc = ioproc();
349a747e4fSDavid du Colombier c->num = nclient;
359a747e4fSDavid du Colombier if(nclient%16 == 0)
369a747e4fSDavid du Colombier client = erealloc(client, (nclient+16)*sizeof(client[0]));
379a747e4fSDavid du Colombier client[nclient++] = c;
389a747e4fSDavid du Colombier return nclient-1;
399a747e4fSDavid du Colombier }
409a747e4fSDavid du Colombier
419a747e4fSDavid du Colombier void
die(Client * c)429a747e4fSDavid du Colombier die(Client *c)
439a747e4fSDavid du Colombier {
449a747e4fSDavid du Colombier Msg *m, *next;
459a747e4fSDavid du Colombier Req *r, *rnext;
469a747e4fSDavid du Colombier
479a747e4fSDavid du Colombier c->moribund = 1;
489a747e4fSDavid du Colombier kickwriter(c);
493ff48bf5SDavid du Colombier iointerrupt(c->readerproc);
503ff48bf5SDavid du Colombier iointerrupt(c->writerproc);
519a747e4fSDavid du Colombier if(--c->activethread == 0){
529a747e4fSDavid du Colombier if(c->cmd != nocmd){
539a747e4fSDavid du Colombier free(c->cmd);
549a747e4fSDavid du Colombier c->cmd = nocmd;
559a747e4fSDavid du Colombier }
569a747e4fSDavid du Colombier c->pid = 0;
579a747e4fSDavid du Colombier c->moribund = 0;
589a747e4fSDavid du Colombier c->status = Closed;
599a747e4fSDavid du Colombier for(m=c->mq; m && m != Zmsg; m=next){
609a747e4fSDavid du Colombier next = m->link;
619a747e4fSDavid du Colombier free(m);
629a747e4fSDavid du Colombier }
639a747e4fSDavid du Colombier c->mq = nil;
649a747e4fSDavid du Colombier if(c->rq != nil){
659a747e4fSDavid du Colombier for(r=c->rq; r; r=rnext){
669a747e4fSDavid du Colombier rnext = r->aux;
679a747e4fSDavid du Colombier respond(r, "hangup");
689a747e4fSDavid du Colombier }
699a747e4fSDavid du Colombier c->rq = nil;
709a747e4fSDavid du Colombier }
719a747e4fSDavid du Colombier if(c->wq != nil){
729a747e4fSDavid du Colombier for(r=c->wq; r; r=rnext){
739a747e4fSDavid du Colombier rnext = r->aux;
749a747e4fSDavid du Colombier respond(r, "hangup");
759a747e4fSDavid du Colombier }
769a747e4fSDavid du Colombier c->wq = nil;
779a747e4fSDavid du Colombier }
789a747e4fSDavid du Colombier c->rq = nil;
799a747e4fSDavid du Colombier c->wq = nil;
809a747e4fSDavid du Colombier c->emq = nil;
819a747e4fSDavid du Colombier c->erq = nil;
829a747e4fSDavid du Colombier c->ewq = nil;
839a747e4fSDavid du Colombier }
849a747e4fSDavid du Colombier }
859a747e4fSDavid du Colombier
869a747e4fSDavid du Colombier void
closeclient(Client * c)879a747e4fSDavid du Colombier closeclient(Client *c)
889a747e4fSDavid du Colombier {
899a747e4fSDavid du Colombier if(--c->ref == 0){
909a747e4fSDavid du Colombier if(c->pid > 0)
919a747e4fSDavid du Colombier postnote(PNPROC, c->pid, "kill");
929a747e4fSDavid du Colombier c->status = Hangup;
939a747e4fSDavid du Colombier close(c->fd[0]);
949a747e4fSDavid du Colombier c->fd[0] = c->fd[1] = -1;
959a747e4fSDavid du Colombier c->moribund = 1;
969a747e4fSDavid du Colombier kickwriter(c);
973ff48bf5SDavid du Colombier iointerrupt(c->readerproc);
983ff48bf5SDavid du Colombier iointerrupt(c->writerproc);
999a747e4fSDavid du Colombier c->activethread++;
1009a747e4fSDavid du Colombier die(c);
1019a747e4fSDavid du Colombier }
1029a747e4fSDavid du Colombier }
1039a747e4fSDavid du Colombier
1049a747e4fSDavid du Colombier void
queuerdreq(Client * c,Req * r)1059a747e4fSDavid du Colombier queuerdreq(Client *c, Req *r)
1069a747e4fSDavid du Colombier {
1079a747e4fSDavid du Colombier if(c->rq==nil)
1089a747e4fSDavid du Colombier c->erq = &c->rq;
1099a747e4fSDavid du Colombier *c->erq = r;
1109a747e4fSDavid du Colombier r->aux = nil;
1119a747e4fSDavid du Colombier c->erq = (Req**)&r->aux;
1129a747e4fSDavid du Colombier }
1139a747e4fSDavid du Colombier
1149a747e4fSDavid du Colombier void
queuewrreq(Client * c,Req * r)1159a747e4fSDavid du Colombier queuewrreq(Client *c, Req *r)
1169a747e4fSDavid du Colombier {
1179a747e4fSDavid du Colombier if(c->wq==nil)
1189a747e4fSDavid du Colombier c->ewq = &c->wq;
1199a747e4fSDavid du Colombier *c->ewq = r;
1209a747e4fSDavid du Colombier r->aux = nil;
1219a747e4fSDavid du Colombier c->ewq = (Req**)&r->aux;
1229a747e4fSDavid du Colombier }
1239a747e4fSDavid du Colombier
1249a747e4fSDavid du Colombier void
queuemsg(Client * c,Msg * m)1259a747e4fSDavid du Colombier queuemsg(Client *c, Msg *m)
1269a747e4fSDavid du Colombier {
1279a747e4fSDavid du Colombier if(c->mq==nil)
1289a747e4fSDavid du Colombier c->emq = &c->mq;
1299a747e4fSDavid du Colombier *c->emq = m;
1309a747e4fSDavid du Colombier if(m != Zmsg){
1319a747e4fSDavid du Colombier m->link = nil;
1329a747e4fSDavid du Colombier c->emq = (Msg**)&m->link;
1339a747e4fSDavid du Colombier }else
1349a747e4fSDavid du Colombier c->emq = nil;
1359a747e4fSDavid du Colombier }
1369a747e4fSDavid du Colombier
1379a747e4fSDavid du Colombier void
matchmsgs(Client * c)1389a747e4fSDavid du Colombier matchmsgs(Client *c)
1399a747e4fSDavid du Colombier {
1409a747e4fSDavid du Colombier Req *r;
1419a747e4fSDavid du Colombier Msg *m;
1429a747e4fSDavid du Colombier int n, rm;
1439a747e4fSDavid du Colombier
1449a747e4fSDavid du Colombier while(c->rq && c->mq){
1459a747e4fSDavid du Colombier r = c->rq;
1469a747e4fSDavid du Colombier c->rq = r->aux;
1479a747e4fSDavid du Colombier
1489a747e4fSDavid du Colombier rm = 0;
1499a747e4fSDavid du Colombier m = c->mq;
1509a747e4fSDavid du Colombier if(m == Zmsg){
1519a747e4fSDavid du Colombier respond(r, "execnet: no more data");
1529a747e4fSDavid du Colombier break;
1539a747e4fSDavid du Colombier }
1549a747e4fSDavid du Colombier n = r->ifcall.count;
1559a747e4fSDavid du Colombier if(n >= m->ep - m->rp){
1569a747e4fSDavid du Colombier n = m->ep - m->rp;
1579a747e4fSDavid du Colombier c->mq = m->link;
1589a747e4fSDavid du Colombier rm = 1;
1599a747e4fSDavid du Colombier }
1609a747e4fSDavid du Colombier if(n)
1619a747e4fSDavid du Colombier memmove(r->ofcall.data, m->rp, n);
1629a747e4fSDavid du Colombier if(rm)
1639a747e4fSDavid du Colombier free(m);
1649a747e4fSDavid du Colombier else
1659a747e4fSDavid du Colombier m->rp += n;
1669a747e4fSDavid du Colombier r->ofcall.count = n;
1679a747e4fSDavid du Colombier respond(r, nil);
1689a747e4fSDavid du Colombier }
1699a747e4fSDavid du Colombier }
1709a747e4fSDavid du Colombier
171*6b6b9ac8SDavid du Colombier void
findrdreq(Client * c,Req * r)1729a747e4fSDavid du Colombier findrdreq(Client *c, Req *r)
1739a747e4fSDavid du Colombier {
1749a747e4fSDavid du Colombier Req **l;
1759a747e4fSDavid du Colombier
1769a747e4fSDavid du Colombier for(l=&c->rq; *l; l=(Req**)&(*l)->aux){
1779a747e4fSDavid du Colombier if(*l == r){
1789a747e4fSDavid du Colombier *l = r->aux;
1799a747e4fSDavid du Colombier if(*l == nil)
1809a747e4fSDavid du Colombier c->erq = l;
181*6b6b9ac8SDavid du Colombier respond(r, "flushed");
182*6b6b9ac8SDavid du Colombier break;
1839a747e4fSDavid du Colombier }
1849a747e4fSDavid du Colombier }
1859a747e4fSDavid du Colombier }
1869a747e4fSDavid du Colombier
187*6b6b9ac8SDavid du Colombier void
findwrreq(Client * c,Req * r)1889a747e4fSDavid du Colombier findwrreq(Client *c, Req *r)
1899a747e4fSDavid du Colombier {
1909a747e4fSDavid du Colombier Req **l;
1919a747e4fSDavid du Colombier
1929a747e4fSDavid du Colombier for(l=&c->wq; *l; l=(Req**)&(*l)->aux){
1939a747e4fSDavid du Colombier if(*l == r){
1949a747e4fSDavid du Colombier *l = r->aux;
1959a747e4fSDavid du Colombier if(*l == nil)
1969a747e4fSDavid du Colombier c->ewq = l;
197*6b6b9ac8SDavid du Colombier respond(r, "flushed");
198*6b6b9ac8SDavid du Colombier return;
1999a747e4fSDavid du Colombier }
2009a747e4fSDavid du Colombier }
2019a747e4fSDavid du Colombier }
2029a747e4fSDavid du Colombier
2039a747e4fSDavid du Colombier void
dataread(Req * r,Client * c)2049a747e4fSDavid du Colombier dataread(Req *r, Client *c)
2059a747e4fSDavid du Colombier {
2069a747e4fSDavid du Colombier queuerdreq(c, r);
2079a747e4fSDavid du Colombier matchmsgs(c);
2089a747e4fSDavid du Colombier }
2099a747e4fSDavid du Colombier
2109a747e4fSDavid du Colombier static void
readthread(void * a)2119a747e4fSDavid du Colombier readthread(void *a)
2129a747e4fSDavid du Colombier {
2139a747e4fSDavid du Colombier uchar *buf;
2149a747e4fSDavid du Colombier int n;
2159a747e4fSDavid du Colombier Client *c;
2169a747e4fSDavid du Colombier Ioproc *io;
2179a747e4fSDavid du Colombier Msg *m;
2189a747e4fSDavid du Colombier char tmp[32];
2199a747e4fSDavid du Colombier
2209a747e4fSDavid du Colombier c = a;
2219a747e4fSDavid du Colombier snprint(tmp, sizeof tmp, "read%d", c->num);
2229a747e4fSDavid du Colombier threadsetname(tmp);
2239a747e4fSDavid du Colombier
2249a747e4fSDavid du Colombier buf = emalloc(8192);
2259a747e4fSDavid du Colombier io = c->readerproc;
2263ff48bf5SDavid du Colombier while((n = ioread(io, c->fd[0], buf, 8192)) >= 0){
2279a747e4fSDavid du Colombier m = emalloc(sizeof(Msg)+n);
2289a747e4fSDavid du Colombier m->rp = (uchar*)&m[1];
2299a747e4fSDavid du Colombier m->ep = m->rp + n;
2309a747e4fSDavid du Colombier if(n)
2319a747e4fSDavid du Colombier memmove(m->rp, buf, n);
2329a747e4fSDavid du Colombier queuemsg(c, m);
2339a747e4fSDavid du Colombier matchmsgs(c);
2349a747e4fSDavid du Colombier }
2359a747e4fSDavid du Colombier queuemsg(c, Zmsg);
2369a747e4fSDavid du Colombier free(buf);
2379a747e4fSDavid du Colombier die(c);
2389a747e4fSDavid du Colombier }
2399a747e4fSDavid du Colombier
2409a747e4fSDavid du Colombier static void
kickwriter(Client * c)2419a747e4fSDavid du Colombier kickwriter(Client *c)
2429a747e4fSDavid du Colombier {
2439a747e4fSDavid du Colombier nbsendp(c->writerkick, nil);
2449a747e4fSDavid du Colombier }
2459a747e4fSDavid du Colombier
2469a747e4fSDavid du Colombier void
clientflush(Req * or,Client * c)2479a747e4fSDavid du Colombier clientflush(Req *or, Client *c)
2489a747e4fSDavid du Colombier {
2499a747e4fSDavid du Colombier if(or->ifcall.type == Tread)
2509a747e4fSDavid du Colombier findrdreq(c, or);
2519a747e4fSDavid du Colombier else{
2529a747e4fSDavid du Colombier if(c->execreq == or){
2539a747e4fSDavid du Colombier c->execreq = nil;
2543ff48bf5SDavid du Colombier iointerrupt(c->writerproc);
2559a747e4fSDavid du Colombier }
2569a747e4fSDavid du Colombier findwrreq(c, or);
2579a747e4fSDavid du Colombier if(c->curw == or){
2589a747e4fSDavid du Colombier c->curw = nil;
2593ff48bf5SDavid du Colombier iointerrupt(c->writerproc);
2609a747e4fSDavid du Colombier kickwriter(c);
2619a747e4fSDavid du Colombier }
2629a747e4fSDavid du Colombier }
2639a747e4fSDavid du Colombier }
2649a747e4fSDavid du Colombier
2659a747e4fSDavid du Colombier void
datawrite(Req * r,Client * c)2669a747e4fSDavid du Colombier datawrite(Req *r, Client *c)
2679a747e4fSDavid du Colombier {
2689a747e4fSDavid du Colombier queuewrreq(c, r);
2699a747e4fSDavid du Colombier kickwriter(c);
2709a747e4fSDavid du Colombier }
2719a747e4fSDavid du Colombier
2729a747e4fSDavid du Colombier static void
writethread(void * a)2739a747e4fSDavid du Colombier writethread(void *a)
2749a747e4fSDavid du Colombier {
2759a747e4fSDavid du Colombier char e[ERRMAX];
2769a747e4fSDavid du Colombier uchar *buf;
2779a747e4fSDavid du Colombier int n;
2789a747e4fSDavid du Colombier Ioproc *io;
2799a747e4fSDavid du Colombier Req *r;
2809a747e4fSDavid du Colombier Client *c;
2819a747e4fSDavid du Colombier char tmp[32];
2829a747e4fSDavid du Colombier
2839a747e4fSDavid du Colombier c = a;
2849a747e4fSDavid du Colombier snprint(tmp, sizeof tmp, "write%d", c->num);
2859a747e4fSDavid du Colombier threadsetname(tmp);
2869a747e4fSDavid du Colombier
2879a747e4fSDavid du Colombier buf = emalloc(8192);
2889a747e4fSDavid du Colombier io = c->writerproc;
2899a747e4fSDavid du Colombier for(;;){
2909a747e4fSDavid du Colombier while(c->wq == nil){
2919a747e4fSDavid du Colombier if(c->moribund)
2929a747e4fSDavid du Colombier goto Out;
2939a747e4fSDavid du Colombier recvp(c->writerkick);
2949a747e4fSDavid du Colombier if(c->moribund)
2959a747e4fSDavid du Colombier goto Out;
2969a747e4fSDavid du Colombier }
2979a747e4fSDavid du Colombier r = c->wq;
2989a747e4fSDavid du Colombier c->wq = r->aux;
2999a747e4fSDavid du Colombier c->curw = r;
3003ff48bf5SDavid du Colombier n = iowrite(io, c->fd[1], r->ifcall.data, r->ifcall.count);
3019a747e4fSDavid du Colombier if(chatty9p)
3029a747e4fSDavid du Colombier fprint(2, "io->write returns %d\n", n);
3039a747e4fSDavid du Colombier if(n >= 0){
3049a747e4fSDavid du Colombier r->ofcall.count = n;
3059a747e4fSDavid du Colombier respond(r, nil);
3069a747e4fSDavid du Colombier }else{
3079a747e4fSDavid du Colombier rerrstr(e, sizeof e);
3089a747e4fSDavid du Colombier respond(r, e);
3099a747e4fSDavid du Colombier }
3109a747e4fSDavid du Colombier }
3119a747e4fSDavid du Colombier Out:
3129a747e4fSDavid du Colombier free(buf);
3139a747e4fSDavid du Colombier die(c);
3149a747e4fSDavid du Colombier }
3159a747e4fSDavid du Colombier
3169a747e4fSDavid du Colombier static void
execproc(void * a)3179a747e4fSDavid du Colombier execproc(void *a)
3189a747e4fSDavid du Colombier {
3199a747e4fSDavid du Colombier int i, fd;
3209a747e4fSDavid du Colombier Client *c;
3219a747e4fSDavid du Colombier char tmp[32];
3229a747e4fSDavid du Colombier
3239a747e4fSDavid du Colombier c = a;
3249a747e4fSDavid du Colombier snprint(tmp, sizeof tmp, "execproc%d", c->num);
3259a747e4fSDavid du Colombier threadsetname(tmp);
3269a747e4fSDavid du Colombier if(pipe(c->fd) < 0){
3279a747e4fSDavid du Colombier rerrstr(c->err, sizeof c->err);
3289a747e4fSDavid du Colombier sendul(c->execpid, -1);
3299a747e4fSDavid du Colombier return;
3309a747e4fSDavid du Colombier }
3319a747e4fSDavid du Colombier rfork(RFFDG);
3329a747e4fSDavid du Colombier fd = c->fd[1];
3339a747e4fSDavid du Colombier close(c->fd[0]);
3349a747e4fSDavid du Colombier dup(fd, 0);
3359a747e4fSDavid du Colombier dup(fd, 1);
3369a747e4fSDavid du Colombier for(i=3; i<100; i++) /* should do better */
3379a747e4fSDavid du Colombier close(i);
3389a747e4fSDavid du Colombier strcpy(c->err, "exec failed");
3399a747e4fSDavid du Colombier procexecl(c->execpid, "/bin/rc", "rc", "-c", c->cmd, nil);
3409a747e4fSDavid du Colombier }
3419a747e4fSDavid du Colombier
3429a747e4fSDavid du Colombier static void
execthread(void * a)3439a747e4fSDavid du Colombier execthread(void *a)
3449a747e4fSDavid du Colombier {
3459a747e4fSDavid du Colombier Client *c;
3469a747e4fSDavid du Colombier int p;
3479a747e4fSDavid du Colombier char tmp[32];
3489a747e4fSDavid du Colombier
3499a747e4fSDavid du Colombier c = a;
3509a747e4fSDavid du Colombier snprint(tmp, sizeof tmp, "exec%d", c->num);
3519a747e4fSDavid du Colombier threadsetname(tmp);
3529a747e4fSDavid du Colombier c->execpid = chancreate(sizeof(ulong), 0);
3539a747e4fSDavid du Colombier proccreate(execproc, c, STACK);
3549a747e4fSDavid du Colombier p = recvul(c->execpid);
3559a747e4fSDavid du Colombier chanfree(c->execpid);
3569a747e4fSDavid du Colombier c->execpid = nil;
3579a747e4fSDavid du Colombier close(c->fd[1]);
3589a747e4fSDavid du Colombier c->fd[1] = c->fd[0];
3599a747e4fSDavid du Colombier if(p != -1){
3609a747e4fSDavid du Colombier c->pid = p;
3619a747e4fSDavid du Colombier c->activethread = 2;
3629a747e4fSDavid du Colombier threadcreate(readthread, c, STACK);
3639a747e4fSDavid du Colombier threadcreate(writethread, c, STACK);
3649a747e4fSDavid du Colombier if(c->execreq)
3659a747e4fSDavid du Colombier respond(c->execreq, nil);
3669a747e4fSDavid du Colombier }else{
3679a747e4fSDavid du Colombier if(c->execreq)
3689a747e4fSDavid du Colombier respond(c->execreq, c->err);
3699a747e4fSDavid du Colombier }
3709a747e4fSDavid du Colombier }
3719a747e4fSDavid du Colombier
3729a747e4fSDavid du Colombier void
ctlwrite(Req * r,Client * c)3739a747e4fSDavid du Colombier ctlwrite(Req *r, Client *c)
3749a747e4fSDavid du Colombier {
3759a747e4fSDavid du Colombier char *f[3], *s, *p;
3769a747e4fSDavid du Colombier int nf;
3779a747e4fSDavid du Colombier
3789a747e4fSDavid du Colombier s = emalloc(r->ifcall.count+1);
3799a747e4fSDavid du Colombier memmove(s, r->ifcall.data, r->ifcall.count);
3809a747e4fSDavid du Colombier s[r->ifcall.count] = '\0';
3819a747e4fSDavid du Colombier
3829a747e4fSDavid du Colombier f[0] = s;
3839a747e4fSDavid du Colombier p = strchr(s, ' ');
3849a747e4fSDavid du Colombier if(p == nil)
3859a747e4fSDavid du Colombier nf = 1;
3869a747e4fSDavid du Colombier else{
3879a747e4fSDavid du Colombier *p++ = '\0';
3889a747e4fSDavid du Colombier f[1] = p;
3899a747e4fSDavid du Colombier nf = 2;
3909a747e4fSDavid du Colombier }
3919a747e4fSDavid du Colombier
3929a747e4fSDavid du Colombier if(f[0][0] == '\0'){
3939a747e4fSDavid du Colombier free(s);
3949a747e4fSDavid du Colombier respond(r, nil);
3959a747e4fSDavid du Colombier return;
3969a747e4fSDavid du Colombier }
3979a747e4fSDavid du Colombier
3989a747e4fSDavid du Colombier r->ofcall.count = r->ifcall.count;
3999a747e4fSDavid du Colombier if(strcmp(f[0], "hangup") == 0){
4009a747e4fSDavid du Colombier if(c->pid == 0){
4019a747e4fSDavid du Colombier respond(r, "connection already hung up");
4029a747e4fSDavid du Colombier goto Out;
4039a747e4fSDavid du Colombier }
4049a747e4fSDavid du Colombier postnote(PNPROC, c->pid, "kill");
4059a747e4fSDavid du Colombier respond(r, nil);
4069a747e4fSDavid du Colombier goto Out;
4079a747e4fSDavid du Colombier }
4089a747e4fSDavid du Colombier
4099a747e4fSDavid du Colombier if(strcmp(f[0], "connect") == 0){
4109a747e4fSDavid du Colombier if(c->cmd != nocmd){
4119a747e4fSDavid du Colombier respond(r, "already have connection");
4129a747e4fSDavid du Colombier goto Out;
4139a747e4fSDavid du Colombier }
4149a747e4fSDavid du Colombier if(nf == 1){
4159a747e4fSDavid du Colombier respond(r, "need argument to connect");
4169a747e4fSDavid du Colombier goto Out;
4179a747e4fSDavid du Colombier }
4189a747e4fSDavid du Colombier c->status = Exec;
4199a747e4fSDavid du Colombier if(p = strrchr(f[1], '!'))
4209a747e4fSDavid du Colombier *p = '\0';
4219a747e4fSDavid du Colombier c->cmd = emalloc(4+1+strlen(f[1])+1);
4229a747e4fSDavid du Colombier strcpy(c->cmd, "exec ");
4239a747e4fSDavid du Colombier strcat(c->cmd, f[1]);
4249a747e4fSDavid du Colombier c->execreq = r;
4259a747e4fSDavid du Colombier threadcreate(execthread, c, STACK);
4269a747e4fSDavid du Colombier goto Out;
4279a747e4fSDavid du Colombier }
4289a747e4fSDavid du Colombier
4299a747e4fSDavid du Colombier respond(r, "bad or inappropriate control message");
4309a747e4fSDavid du Colombier Out:
4319a747e4fSDavid du Colombier free(s);
4329a747e4fSDavid du Colombier }
433