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"; }