implement Listen;
include "sys.m";
sys: Sys;
include "draw.m";
include "arg.m";
include "iauth.m";
iauth: Iauth;
include "sh.m";
sh: Sh;
Context: import sh;
Listen: module{
init: fn(nil: ref Draw->Context, argv: list of string);
};
badmodule(p: string)
{
sys->fprint(stderr(), "listen: cannot load %s: %r\n", p);
raise "fail:bad module";
}
keyspec: string;
setuid := 0;
verbose := 0;
initscript: string;
addr: string;
cmd: list of string;
doauth := 1;
trusted := 0;
init(drawctxt: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
sh = load Sh Sh->PATH;
if(sh == nil)
badmodule(Sh->PATH);
iauth = load Iauth Iauth->PATH;
if(iauth == nil)
badmodule(Iauth->PATH);
iauth->init();
arg := load Arg Arg->PATH;
if(arg == nil)
badmodule(Arg->PATH);
arg->init(argv);
synchronous := 0;
arg->setusage("listen [-i {initscript}] [-Ast] [-k keyfile] [-a alg]... addr command [arg...]");
while((opt := arg->opt()) != 0){
case opt{
'A' =>
doauth = 0;
'k' =>
keyspec = arg->earg();
'i' =>
initscript = arg->earg();
'v' =>
verbose = 1;
's' =>
synchronous = 1;
't' =>
trusted = 1;
'p' =>
setuid = 1;
* =>
arg->usage();
}
}
if(!trusted){
sys->unmount(nil, "/mnt/keys");
# sys->unmount(nil, "/mnt/factotum");
# become none?
}
argv = arg->argv();
n := len argv;
if(n < 2)
arg->usage();
arg = nil;
addr = hd argv;
cmd = tl argv;
argv = nil;
sync := chan[1] of string;
spawn listen(drawctxt, sync);
e := <-sync;
if(e != nil)
raise "fail:" + e;
if(synchronous){
e = <-sync;
if(e != nil)
raise "fail:" + e;
}
}
listen(drawctxt: ref Draw->Context, sync: chan of string)
{
{
listen1(drawctxt, sync);
} exception e{
"fail:*" =>
sync <-= e;
}
}
listen1(drawctxt: ref Draw->Context, sync: chan of string)
{
sys->pctl(Sys->FORKFD, nil);
ctxt := Context.new(drawctxt);
(ok, acon) := sys->announce(addr);
if(ok == -1){
sys->fprint(stderr(), "listen: failed to announce on '%s': %r\n", addr);
sync <-= "cannot announce";
exit;
}
ctxt.set("user", nil);
if(initscript != nil){
ctxt.setlocal("net", ref Sh->Listnode(nil, acon.dir) :: nil);
ctxt.run(ref Sh->Listnode(nil, initscript) :: nil, 0);
initscript = nil;
}
# make sure the shell command is parsed only once;
# also checks for syntax errors.
shargv := sh->stringlist2list(cmd);
if((hd cmd) != nil && (hd cmd)[0] == '{'){
(c1, e) := sh->parse(hd cmd);
if(c1 == nil){
sys->fprint(stderr(), "listen: %s\n", e);
sync <-= "parse error";
exit;
}
shargv = ref Sh->Listnode(c1, hd cmd) :: tl shargv;
}
sync <-= nil;
listench := chan of (int, Sys->Connection);
authch := chan of (string, Sys->Connection);
spawn listener(listench, acon, addr);
for(;;){
user := "";
ccon: Sys->Connection;
alt{
(lok, c) := <-listench =>
if(lok == -1){
sync <-= "listen";
exit;
}
if(doauth){
spawn authenticator(authch, c, addr);
continue;
}
ccon = c;
(user, ccon) = <-authch =>
;
}
if(user != nil)
ctxt.set("user", sh->stringlist2list(user :: nil));
ctxt.set("net", ref Sh->Listnode(nil, ccon.dir) :: nil);
# XXX could do this in a separate process too, to
# allow new connections to arrive and start authenticating
# while the shell command is still running.
sys->dup(ccon.dfd.fd, 0);
sys->dup(ccon.dfd.fd, 1);
ccon.dfd = ccon.cfd = nil;
ctxt.run(shargv, 0);
sys->dup(2, 0);
sys->dup(2, 1);
}
}
listener(listench: chan of (int, Sys->Connection), c: Sys->Connection, addr: string)
{
for(;;){
(ok, nc) := sys->listen(c);
if(ok == -1){
sys->fprint(stderr(), "listen: listen error on '%s': %r\n", addr);
listench <-= (-1, nc);
exit;
}
if(verbose)
sys->fprint(stderr(), "listen: got connection on %s from %s",
addr, readfile(nc.dir + "/remote"));
nc.dfd = sys->open(nc.dir + "/data", Sys->ORDWR);
if(nc.dfd == nil)
sys->fprint(stderr(), "listen: cannot open %s: %r\n", nc.dir + "/data");
else{
if(nc.cfd != nil)
sys->fprint(nc.cfd, "keepalive");
listench <-= (ok, nc);
}
}
}
authenticator(authch: chan of (string, Sys->Connection),
c: Sys->Connection, addr: string)
{
err: string;
(c.dfd, err) = iauth->auth(keyspec+" proto=infauth role=server", c.dfd, setuid);
if(c.dfd == nil){
sys->fprint(stderr(), "listen: auth on %s failed: %s\n", addr, err);
return;
}
if(verbose)
sys->fprint(stderr(), "listen: authenticated on %s as %s\n", addr, err);
authch <-= (err, c);
}
stderr(): ref Sys->FD
{
return sys->fildes(2);
}
readfile(f: string): string
{
fd := sys->open(f, sys->OREAD);
if(fd == nil)
return nil;
buf := array[1024] of byte;
n := sys->read(fd, buf, len buf);
if(n < 0)
return nil;
return string buf[0:n];
}
|