implement Cpu;
include "sys.m";
sys: Sys;
include "draw.m";
include "styx.m";
include "iauth.m";
iauth: Iauth;
include "string.m";
str: String;
include "fdrun.m";
fdrun: FDrun;
include "wmlib.m";
include "arg.m";
Cpu: module {
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
keyspec: string;
MAXCMD: con 128*1024;
init(ctxt: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
iauth = load Iauth Iauth->PATH;
if(iauth == nil)
badmodule(Iauth->PATH);
iauth->init();
str = load String String->PATH;
if(str == nil)
badmodule(String->PATH);
arg := load Arg Arg->PATH;
fdrun = load FDrun FDrun->PATH;
if(fdrun == nil)
badmodule(FDrun->PATH);
fdrun->init();
arg->init(argv);
arg->setusage("cpu [-RA] [-h system] [-k keyspec] [cmd [arg...]]");
system := "$cpu";
doauth := 1;
while((opt := arg->opt()) != 0){
case opt {
'R' =>
remotesideproc(ctxt);
exit;
'h' =>
system = arg->earg();
'k' =>
keyspec = arg->earg();
'A' =>
doauth = 0;
* =>
arg->usage();
}
}
argv = arg->argv();
if(argv == nil)
argv = "/dis/sh" :: "-i" :: nil;
sys->pctl(Sys->FORKNS, nil);
na := netmkaddr(system, nil, "rstyx");
(ok, c) := sys->dial(na, nil);
if(ok == -1)
fatal(sys->sprint("cannot dial %q: %r", na));
fd: ref Sys->FD;
if(doauth){
(afd, err) := iauth->auth("proto=infauth role=client "+keyspec, c.dfd, 0);
if(afd == nil)
fatal(sys->sprint("cannot authenticate: %s", err));
fd = afd;
}else
fd = c.dfd;
c.dfd = nil;
{
b := str->quoted(argv);
t := array of byte sys->sprint("%d\n%s\n", len (array of byte b)+1, b);
if(sys->write(fd, t, len t) != len t)
fatal("cannot send command");
}
if(ctxt != nil && sys->stat("/mnt/wm/clone").t0 == -1){
sys->pipe(p := array[2] of ref Sys->FD);
fdrun->run(ctxt, "wmexport"::nil, "0--", array[] of {p[0]}, chan[1] of string);
p[0] = nil;
if(sys->mount(p[1], nil, "/mnt/wm", Sys->MREPL, nil) == -1)
sys->fprint(sys->fildes(2), "cpu: warning: cannot mount wmexport: %r\n");
}
# servefile("cwd", sys->fd2path(sys->open(".", Sys->OREAD)));
sys->export(fd, "/", Sys->EXPWAIT);
}
# called with stdin connected to client side, post authentication.
remotesideproc(ctxt: ref Draw->Context)
{
sys->pctl(Sys->NEWPGRP|Sys->FORKNS, nil);
argv := readcmd(sys->fildes(0));
fd := sys->open("#M0/data", Sys->ORDWR);
# make sure buffers are big by doing fversion explicitly; pick a large number; other side will trim
(rc, nil) := sys->fversion(fd, 64*1024, Styx->VERSION);
if(rc == -1)
fatal(sys->sprint("fversion failed: %r"));
if(sys->mount(fd, nil, "/n/local", Sys->MREPL|Sys->MCREATE, nil) == -1)
fatal(sys->sprint("mount failed: %r"));
fd = nil;
fds := array[2] of ref Sys->FD;
if((fds[0] = sys->open("/n/local/dev/cons", Sys->OREAD)) == nil)
fatal(sys->sprint("cannot open stdin: %r"));
if((fds[1] = sys->open("/n/local/dev/cons", Sys->OWRITE)) == nil)
fatal(sys->sprint("cannot open stdout: %r"));
if(sys->stat("/n/local/mnt/wm/clone").t0 != -1){
wmlib := load Wmlib Wmlib->PATH;
wmlib->init();
(ectxt, err) := wmlib->importdrawcontext("/n/local/dev", "/n/local/mnt/wm");
if(ectxt == nil)
sys->fprint(fds[1], "cpu: warning: failed to import draw context: %s\n", err);
else{
sys->fprint(fds[1], "cpu: imported draw context ok\n");
ctxt = ectxt;
sys->pctl(Sys->FORKNS, nil);
spawn watchdogproc(fdc := chan of ref Sys->FD);
if(sys->mount(<-fdc, nil, "/root", Sys->MREPL, nil) == -1)
sys->fprint(sys->fildes(2), "failed to mount watchdog: %r\n");
}
}else
sys->fprint(sys->fildes(2), "no wm context\n");
fdrun->run(ctxt, argv, "011", fds, chan[1] of string);
}
# wait until all clients of the namespace leave,
# whereupon we kill the current process group.
# this terminates wmlib->importdrawcontext, which can't know
# when no more clients might arrive.
watchdogproc(fdc: chan of ref Sys->FD)
{
sys->pipe(p := array[2] of ref Sys->FD);
sys->pctl(Sys->NEWNS, nil);
fdc <-= p[1];
p[1] = nil;
sys->export(p[0], "#//dev", Sys->EXPWAIT);
sys->fprint(sys->open("#p/"+string sys->pctl(0, nil)+"/ctl", Sys->OWRITE), "killgrp");
}
readcmd(fd: ref Sys->FD): list of string
{
b := array[1] of byte;
nb := 0;
while((n := sys->read(fd, b, 1)) > 0 && (c := int b[0]) != '\n')
if(c >= '0' && c <= '9')
nb = nb * 10 + (c - '0');
if(n <= 0)
fatal("protocol botch; premature EOF1");
# size sanity check:
if(nb > MAXCMD)
fatal("command too large");
buf := b = array[nb] of byte;
while(nb > 0){
n = sys->read(fd, b, len b);
if(n <= 0)
fatal("protocol botch; premature EOF2");
b = b[n:];
nb -= n;
}
return str->unquoted(string buf);
}
netmkaddr(addr, net, svc: string): string
{
if(net == nil)
net = "net";
(n, nil) := sys->tokenize(addr, "!");
if(n <= 1){
if(svc== nil)
return sys->sprint("%s!%s", net, addr);
return sys->sprint("%s!%s!%s", net, addr, svc);
}
if(svc == nil || n > 2)
return addr;
return sys->sprint("%s!%s", addr, svc);
}
badmodule(p: string)
{
fatal(sys->sprint("cannot load %q: %r", p));
}
fatal(e: string)
{
sys->fprint(sys->fildes(2), "cpu: %s\n", e);
raise "fail:error";
}
|