ref: d916a4c3823f55227ffae35738c2497256e307b5
dir: /appl/alphabet/auxi/rexecsrv.b/
implement Rexecsrv;
include "sys.m";
sys: Sys;
include "draw.m";
include "sh.m";
sh: Sh;
include "alphabet/endpoints.m";
endpoints: Endpoints;
Endpoint: import endpoints;
include "alphabet/reports.m";
reports: Reports;
Report: import reports;
include "alphabet.m";
alphabet: Alphabet;
Value: import alphabet;
include "alphabet/abc.m";
include "alphabet/abctypes.m";
include "string.m";
str: String;
Rexecsrv: module {
init: fn(nil: ref Draw->Context, argv: list of string);
};
drawctxt: ref Draw->Context;
init(ctxt: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
endpoints = load Endpoints Endpoints->PATH;
if(endpoints == nil)
fatal(sys->sprint("cannot load %s: %r", Endpoints->PATH));
endpoints->init();
sh = load Sh Sh->PATH;
if(sh == nil)
fatal(sys->sprint("cannot load %s: %r", Sh->PATH));
sh->initialise();
reports = load Reports Reports->PATH;
if(reports == nil)
fatal(sys->sprint("cannot load %s: %r", Reports->PATH));
str = load String String->PATH;
if(str == nil)
fatal(sys->sprint("cannot load %s: %r", String->PATH));
if(len argv != 3)
fatal("usage: rexecsrv dir {decls}");
drawctxt = ctxt;
if(sys->stat("/n/endpoint/local/clone").t0 == -1)
fatal("no local endpoints available");
dir := hd tl argv;
decls := parse(hd tl tl argv);
if(sys->bind("#s", dir, Sys->MREPL) == -1)
fatal(sys->sprint("cannot bind #s onto %q: %r", dir));
alphabet = declares(decls);
fio := sys->file2chan(dir, "exec");
sync := chan of int;
spawn rexecproc(sync, fio);
<-sync;
}
stderr(): ref Sys->FD
{
return sys->fildes(2);
}
# use one alphabet module to bootstrap another
# with the desired declarations that we can use to
# execute external commands.
declares(decls: ref Sh->Cmd): Alphabet
{
alphabet0 := load Alphabet Alphabet->PATH;
if(alphabet0 == nil)
fatal(sys->sprint("cannot load %s: %r", Alphabet->PATH));
alphabet0->init();
abctypes := load Abctypes Abctypes->PATH;
if(abctypes == nil)
fatal(sys->sprint("cannot load %s: %r", Abctypes->PATH));
Abccvt: import abctypes;
abc := load Abc Abc->PATH;
if(abc == nil)
fatal(sys->sprint("cannot load %s: %r", Abc->PATH));
abc->init();
Value: import abc;
(c, nil, abccvt) := abctypes->proxy0();
spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report);
r := <-reply;
if((err := alphabet0->loadtypeset("/abc", c, nil)) != nil)
fatal("cannot load typeset /abc: "+err);
alphabet0->setautodeclare(1);
spawn alphabet0->eval0(
parse("{(/cmd);"+
"/abc/abc |"+
"/abc/declares $1"+
"}"
),
"/abc/abc",
nil,
r,
r.start("evaldecls"),
ref (Alphabet->Value).Vc(decls) :: nil,
vc := chan of ref Alphabet->Value
);
r.enable();
av: ref Alphabet->Value;
wait:
for(;;)alt{
av = <-vc =>
;
msg := <-errorc =>
if(msg == nil)
break wait;
sys->fprint(stderr(), "rexecsrv: %s\n", msg);
}
if(av == nil)
fatal("declarations failed");
v := abccvt.ext2int(av).dup();
alphabet0->av.free(1);
pick xv := v {
VA =>
return xv.i.alphabet;
}
return nil;
}
parse(s: string): ref Sh->Cmd
{
(c, err) := sh->parse(s);
if(c== nil)
fatal(sys->sprint("cannot parse %q: %s", s, err));
return c;
}
lc(cmd: ref Sh->Cmd): ref Sh->Listnode
{
return ref Sh->Listnode(cmd, nil);
}
lw(word: string): ref Sh->Listnode
{
return ref Sh->Listnode(nil, word);
}
# write endpoints, cmd
# read endpoints
rexecproc(sync: chan of int, fio: ref Sys->FileIO)
{
sys->pctl(Sys->FORKNS, nil);
pending: list of (int, string);
sync <-= 1;
for(;;) alt {
(nil, data, fid, wc) := <-fio.write =>
if(wc == nil)
break;
req := string data;
l := str->unquoted(req);
if(len l != 2 || Endpoint.mk(hd l).addr == nil){
wc <-= (0, "bad request");
break;
}
pending = (fid, req) :: pending;
wc <-= (0, nil);
(offset, nil, fid, rc) := <-fio.read =>
if(rc == nil){
(pending, nil) = removefid(fid, pending);
break;
}
if(offset > 0){
rc <-= (nil, nil);
break;
}
req: string;
(pending, req) = removefid(fid, pending);
if(req == nil){
rc <-= (nil, "no pending exec");
break;
}
l := str->unquoted(req);
spawn exec(sync1 := chan of int, Endpoint.mk(hd l), hd tl l, rc);
<-sync1;
}
}
gather(errorc: chan of string)
{
s := "";
while((e := <-errorc) != nil)
s += e + "\n";
errorc <-= s;
}
exec(sync: chan of int, ep: Endpoint, expr: string,
rc: chan of (array of byte, string))
{
sys->pctl(Sys->FORKNS, nil);
sync <-= 1;
spawn gather(errorc := chan of string);
(c, err) := alphabet->parse(expr);
if(c == nil){
rc <-= (nil, "parse error: "+err);
return;
}
usage: string;
(c, usage) = alphabet->rewrite(c, "/fd", errorc);
errorc <-= nil;
err = <-errorc;
if(c == nil){
rc <-= (nil, err);
return;
}
if(!alphabet->typecompat("/fd -> /fd", usage).t0)
rc <-= (nil, "incompatible type: "+usage);
fd0: ref Sys->FD;
(fd0, err) = endpoints->open(nil, ep);
if(fd0 == nil){
rc <-= (nil, err);
return;
}
(fd1, ep1) := endpoints->create("local");
if(fd1 == nil){
rc <-= (nil, "cannot make endpoints: "+ep1.about);
return;
}
rc <-= (array of byte ep1.text(), nil);
runcmd(c, fd0, fd1);
}
fdproc(f: chan of ref Sys->FD, fd0: ref Sys->FD)
{
f <-= fd0;
fd1 := <-f;
if(fd1 == nil)
exit;
buf := array[Sys->ATOMICIO] of byte;
while((n := sys->read(fd0, buf, len buf)) > 0)
if(sys->write(fd1, buf, n) == -1)
break;
}
runcmd(c: ref Sh->Cmd, fd0, fd1: ref Sys->FD)
{
f := chan of ref Sys->FD;
spawn fdproc(f, fd0);
spawn reports->reportproc(errorc := chan of string, nil, reply := chan of ref Report);
r := <-reply;
spawn alphabet->eval0(
c,
"/fd",
drawctxt,
r,
r.start("evalcmd"),
ref (Alphabet->Value).Vf(f) :: nil,
vc := chan of ref Alphabet->Value
);
r.enable();
av: ref Alphabet->Value;
wait:
for(;;)alt{
av = <-vc =>
if(av == nil){
sys->fprint(stderr(), "rexecsrv: no value received\n");
break;
}
pick v := av {
Vf =>
<-v.i;
v.i <-= fd1;
* =>
sys->fprint(stderr(), "rexecsrv: can't happen: expression has wrong type '%c'\n",
alphabet->v.typec());
}
msg := <-errorc =>
if(msg == nil)
break wait;
# XXX could queue diagnostics back to caller here.
sys->fprint(stderr(), "rexecsrv: %s\n", msg);
}
sys->write(fd1, array[0] of byte, 0);
}
removefid(fid: int, l: list of (int, string)): (list of (int, string), string)
{
if(l == nil)
return (nil, nil);
if((hd l).t0 == fid)
return (removefid(fid, tl l).t0, (hd l).t1);
(rl, d) := removefid(fid, tl l);
return (hd l :: rl, d);
}
fatal(e: string)
{
sys->fprint(sys->fildes(2), "rexecsrv: %s\n", e);
raise "fail:error";
}