implement Mount;
# Exactly like mount(1), but can insert delays to pretend the
# link to the file server has a bad latency, for testing other services.
include "sys.m";
sys: Sys;
include "draw.m";
include "keyring.m";
include "security.m";
include "factotum.m";
include "styxconv.m";
include "styxpersist.m";
include "arg.m";
include "sh.m";
Mount: module
{
init: fn(nil: ref Draw->Context, nil: list of string);
};
verbose := 0;
doauth := 1;
do9 := 0;
oldstyx := 0;
persist := 0;
showstyx := 0;
quiet := 0;
alg := "none";
keyfile: string;
spec: string;
addr: string;
latency := 0;
fail(status, msg: string)
{
sys->fprint(sys->fildes(2), "mount: %s\n", msg);
raise "fail:"+status;
}
nomod(mod: string)
{
fail("load", sys->sprint("can't load %s: %r", mod));
}
init(ctxt: ref Draw->Context, args: list of string)
{
sys = load Sys Sys->PATH;
arg := load Arg Arg->PATH;
if(arg == nil)
nomod(Arg->PATH);
arg->init(args);
arg->setusage("o/lmount [-L lat] [-a|-b] [-coA9] [-C cryptoalg] [-k keyfile] [-q] net!addr|file|{command} mountpoint [spec]");
flags := 0;
while((o := arg->opt()) != 0){
case o {
'a' =>
flags |= Sys->MAFTER;
'b' =>
flags |= Sys->MBEFORE;
'c' =>
flags |= Sys->MCREATE;
'C' =>
alg = arg->earg();
'k' or
'f' =>
keyfile = arg->earg();
'A' =>
doauth = 0;
'9' =>
doauth = 0;
do9 = 1;
'o' =>
oldstyx = 1;
'v' =>
verbose = 1;
'P' =>
persist = 1;
'S' =>
showstyx = 1;
'q' =>
quiet = 1;
'L' =>
latency = int arg->earg();
* =>
arg->usage();
}
}
args = arg->argv();
if(len args != 2){
if(len args != 3)
arg->usage();
spec = hd tl tl args;
}
arg = nil;
addr = hd args;
mountpoint := hd tl args;
if(oldstyx && do9)
fail("usage", "cannot combine -o and -9 options");
fd := connect(ctxt, addr);
ok: int;
if(do9){
fd = styxlog(fd);
factotum := load Factotum Factotum->PATH;
if(factotum == nil)
nomod(Factotum->PATH);
factotum->init();
ok = factotum->mount(fd, mountpoint, flags, spec, nil).t0;
}else{
err: string;
if(!persist){
(fd, err) = authcvt(fd);
if(fd == nil)
fail("error", err);
}
fd = styxlog(fd);
ok = sys->mount(fd, nil, mountpoint, flags, spec);
}
if(ok < 0 && !quiet)
fail("mount failed", sys->sprint("mount failed: %r"));
}
connect(ctxt: ref Draw->Context, dest: string): ref Sys->FD
{
if(dest != nil && dest[0] == '{' && dest[len dest - 1] == '}'){
if(persist)
fail("usage", "cannot persistently mount a command");
doauth = 0;
return popen(ctxt, dest :: nil);
}
(n, nil) := sys->tokenize(dest, "!");
if(n == 1){
fd := sys->open(dest, Sys->ORDWR);
if(fd != nil){
if(persist)
fail("usage", "cannot persistently mount a file");
return fd;
}
if(dest[0] == '/')
fail("open failed", sys->sprint("can't open %s: %r", dest));
}
svc := "styx";
if(do9)
svc = "9fs";
dest = netmkaddr(dest, "net", svc);
if(persist){
styxpersist := load Styxpersist Styxpersist->PATH;
if(styxpersist == nil)
fail("load", sys->sprint("cannot load %s: %r", Styxpersist->PATH));
sys->pipe(p := array[2] of ref Sys->FD);
(c, err) := styxpersist->init(p[0], do9, nil);
if(c == nil)
fail("error", "styxpersist: "+err);
spawn dialler(c, dest);
return p[1];
}
(ok, c) := sys->dial(dest, nil);
if(ok < 0)
fail("dial failed", sys->sprint("can't dial %s: %r", dest));
return c.dfd;
}
dialler(dialc: chan of chan of ref Sys->FD, dest: string)
{
while((reply := <-dialc) != nil){
if(verbose)
sys->print("dialling %s\n", addr);
(ok, c) := sys->dial(dest, nil);
if(ok == -1){
reply <-= nil;
continue;
}
(fd, err) := authcvt(c.dfd);
if(fd == nil && verbose)
sys->print("%s\n", err);
# XXX could check that user at the other end is still the same.
reply <-= fd;
}
}
authcvt(fd: ref Sys->FD): (ref Sys->FD, string)
{
err: string;
if(doauth){
(fd, err) = authenticate(keyfile, alg, fd, addr);
if(fd == nil)
return (nil, err);
if(verbose)
sys->print("remote username is %s\n", err);
}
if(oldstyx)
return cvstyx(fd);
return (fd, nil);
}
popen(ctxt: ref Draw->Context, argv: list of string): ref Sys->FD
{
sh := load Sh Sh->PATH;
if(sh == nil)
nomod(Sh->PATH);
sync := chan of int;
fds := array[2] of ref Sys->FD;
sys->pipe(fds);
spawn runcmd(sh, ctxt, argv, fds[0], sync);
<-sync;
return fds[1];
}
runcmd(sh: Sh, ctxt: ref Draw->Context, argv: list of string, stdin: ref Sys->FD, sync: chan of int)
{
sys->pctl(Sys->FORKFD, nil);
sys->dup(stdin.fd, 0);
stdin = nil;
sync <-= 0;
sh->run(ctxt, argv);
}
cvstyx(fd: ref Sys->FD): (ref Sys->FD, string)
{
styxconv := load Styxconv Styxconv->PATH;
if(styxconv == nil)
return (nil, sys->sprint("cannot load %s: %r", Styxconv->PATH));
styxconv->init();
p := array[2] of ref Sys->FD;
if(sys->pipe(p) < 0)
return (nil, sys->sprint("can't create pipe: %r"));
pidc := chan of int;
spawn styxconv->styxconv(p[1], fd, pidc);
p[1] = nil;
<-pidc;
return (p[0], nil);
}
authenticate(keyfile, alg: string, dfd: ref Sys->FD, addr: string): (ref Sys->FD, string)
{
cert : string;
kr := load Keyring Keyring->PATH;
if(kr == nil)
return (nil, sys->sprint("cannot load %s: %r", Keyring->PATH));
kd := "/usr/" + user() + "/keyring/";
if(keyfile == nil) {
cert = kd + netmkaddr(addr, "tcp", "");
(ok, nil) := sys->stat(cert);
if (ok < 0)
cert = kd + "default";
}
else if(len keyfile > 0 && keyfile[0] != '/')
cert = kd + keyfile;
else
cert = keyfile;
ai := kr->readauthinfo(cert);
if(ai == nil)
return (nil, sys->sprint("cannot read %s: %r", cert));
auth := load Auth Auth->PATH;
if(auth == nil)
nomod(Auth->PATH);
err := auth->init();
if(err != nil)
return (nil, "cannot init auth: "+err);
fd: ref Sys->FD;
(fd, err) = auth->client(alg, ai, dfd);
if(fd == nil)
return (nil, "authentication failed: "+err);
return (fd, err);
}
user(): string
{
fd := sys->open("/dev/user", sys->OREAD);
if(fd == nil)
return "";
buf := array[Sys->NAMEMAX] of byte;
n := sys->read(fd, buf, len buf);
if(n < 0)
return "";
return string buf[0:n];
}
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);
}
kill(pid: int)
{
if ((fd := sys->open("#p/" + string pid + "/ctl", Sys->OWRITE)) != nil)
sys->fprint(fd, "kill");
}
include "styx.m";
styx: Styx;
Rmsg, Tmsg: import styx;
styxlog(fd: ref Sys->FD): ref Sys->FD
{
sys->pipe(p := array[2] of ref Sys->FD);
styx = load Styx Styx->PATH;
styx->init();
spawn tmsgreader(p[0], fd, p1 := chan[1] of int, p2 := chan[1] of int);
spawn rmsgreader(fd, p[0], p2, p1);
fd = p[1];
return fd;
}
tmsgwriter(sfd: ref Sys->FD, tc: chan of array of byte)
{
while((a := <-tc) != nil)
sys->write(sfd, a, len a);
}
tmsgdelay(d: array of byte, l: int, tc: chan of array of byte)
{
sys->sleep(l);
tc <-= d;
}
tmsgreader(cfd, sfd: ref Sys->FD, p1, p2: chan of int)
{
p1 <-= sys->pctl(0, nil);
m: ref Tmsg;
tc:= chan of array of byte;
spawn tmsgwriter(sfd, tc);
do{
m = Tmsg.read(cfd, 9000);
if (showstyx)
sys->print("%s\n", m.text());
pick mm := m {
Create =>
l := len "!!LAT=";
if (len mm.name >= l && mm.name[0:l] == "!!LAT="){
latency = int mm.name[l:];
sys->fprint(sys->fildes(2), "o/lmount: latency %d ms\n", latency);
}
}
d := m.pack();
if (latency > 0)
spawn tmsgdelay(d, latency, tc);
else
tc <-= d;
} while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
tc <-= nil;
kill(<-p2);
}
rmsgreader(sfd, cfd: ref Sys->FD, p1, p2: chan of int)
{
p1 <-= sys->pctl(0, nil);
m: ref Rmsg;
do{
m = Rmsg.read(sfd, 9000);
if (showstyx)
sys->print("%s\n", m.text());
d := m.pack();
if(sys->write(cfd, d, len d) != len d)
sys->print("rmsg write error: %r\n");
} while(m != nil && tagof(m) != tagof(Tmsg.Readerror));
kill(<-p2);
}
|