summaryrefslogtreecommitdiff
path: root/appl/cmd/styxlisten.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/styxlisten.b')
-rw-r--r--appl/cmd/styxlisten.b262
1 files changed, 262 insertions, 0 deletions
diff --git a/appl/cmd/styxlisten.b b/appl/cmd/styxlisten.b
new file mode 100644
index 00000000..2147e619
--- /dev/null
+++ b/appl/cmd/styxlisten.b
@@ -0,0 +1,262 @@
+implement Styxlisten;
+include "sys.m";
+ sys: Sys;
+include "draw.m";
+include "keyring.m";
+ keyring: Keyring;
+include "security.m";
+ auth: Auth;
+include "arg.m";
+include "sh.m";
+
+Styxlisten: module {
+ init: fn(nil: ref Draw->Context, argv: list of string);
+};
+
+badmodule(p: string)
+{
+ sys->fprint(stderr(), "styxlisten: cannot load %s: %r\n", p);
+ raise "fail:bad module";
+}
+
+verbose := 0;
+passhostnames := 0;
+
+init(ctxt: ref Draw->Context, argv: list of string)
+{
+ sys = load Sys Sys->PATH;
+ auth = load Auth Auth->PATH;
+ if (auth == nil)
+ badmodule(Auth->PATH);
+ if ((e := auth->init()) != nil)
+ error("auth init failed: " + e);
+ keyring = load Keyring Keyring->PATH;
+ if (keyring == nil)
+ badmodule(Keyring->PATH);
+
+ arg := load Arg Arg->PATH;
+ if (arg == nil)
+ badmodule(Arg->PATH);
+
+ arg->init(argv);
+ arg->setusage("styxlisten [-a alg]... [-Atsv] [-k keyfile] address cmd [arg...]");
+
+ algs: list of string;
+ doauth := 1;
+ synchronous := 0;
+ trusted := 0;
+ keyfile := "";
+
+ while ((opt := arg->opt()) != 0) {
+ case opt {
+ 'v' =>
+ verbose = 1;
+ 'a' =>
+ algs = arg->earg() :: algs;
+ 'f' or
+ 'k' =>
+ keyfile = arg->earg();
+ if (! (keyfile[0] == '/' || (len keyfile > 2 && keyfile[0:2] == "./")))
+ keyfile = "/usr/" + user() + "/keyring/" + keyfile;
+ 'h' =>
+ passhostnames = 1;
+ 't' =>
+ trusted = 1;
+ 's' =>
+ synchronous = 1;
+ 'A' =>
+ doauth = 0;
+ * =>
+ arg->usage();
+ }
+ }
+ argv = arg->argv();
+ if (len argv < 2)
+ arg->usage();
+ arg = nil;
+ if (doauth && algs == nil)
+ algs = getalgs();
+ addr := netmkaddr(hd argv, "tcp", "styx");
+ cmd := tl argv;
+
+ authinfo: ref Keyring->Authinfo;
+ if (doauth) {
+ if (keyfile == nil)
+ keyfile = "/usr/" + user() + "/keyring/default";
+ authinfo = keyring->readauthinfo(keyfile);
+ if (authinfo == nil)
+ error(sys->sprint("cannot read %s: %r", keyfile));
+ }
+
+ (ok, c) := sys->announce(addr);
+ if (ok == -1)
+ error(sys->sprint("cannot announce on %s: %r", addr));
+ if(!trusted){
+ sys->unmount(nil, "/mnt/keys"); # should do for now
+ # become none?
+ }
+
+ lsync := chan[1] of int;
+ if(synchronous)
+ listener(c, popen(ctxt, cmd, lsync), authinfo, algs, lsync);
+ else
+ spawn listener(c, popen(ctxt, cmd, lsync), authinfo, algs, lsync);
+}
+
+listener(c: Sys->Connection, mfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, algs: list of string, lsync: chan of int)
+{
+ lsync <-= sys->pctl(0, nil);
+ for (;;) {
+ (n, nc) := sys->listen(c);
+ if (n == -1)
+ error(sys->sprint("listen failed: %r"));
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: got connection from %s",
+ readfile(nc.dir + "/remote"));
+ dfd := sys->open(nc.dir + "/data", Sys->ORDWR);
+ if (dfd != nil) {
+ if(nc.cfd != nil)
+ sys->fprint(nc.cfd, "keepalive");
+ hostname: string;
+ if(passhostnames){
+ hostname = readfile(nc.dir + "/remote");
+ if(hostname != nil)
+ hostname = hostname[0:len hostname - 1];
+ }
+ if (algs == nil) {
+ sync := chan of int;
+ spawn exportproc(sync, mfd, nil, hostname, dfd);
+ <-sync;
+ } else
+ spawn authenticator(dfd, authinfo, mfd, algs, hostname);
+ }
+ }
+}
+
+# authenticate a connection and set the user id.
+authenticator(dfd: ref Sys->FD, authinfo: ref Keyring->Authinfo, mfd: ref Sys->FD,
+ algs: list of string, hostname: string)
+{
+ # authenticate and change user id appropriately
+ (fd, err) := auth->server(algs, authinfo, dfd, 1);
+ if (fd == nil) {
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: authentication failed: %s\n", err);
+ return;
+ }
+ if (verbose)
+ sys->fprint(stderr(), "styxlisten: client authenticated as %s\n", err);
+ sync := chan of int;
+ spawn exportproc(sync, mfd, err, hostname, fd);
+ <-sync;
+}
+
+exportproc(sync: chan of int, fd: ref Sys->FD, uname, hostname: string, dfd: ref Sys->FD)
+{
+ sys->pctl(Sys->NEWFD | Sys->NEWNS, 2 :: fd.fd :: dfd.fd :: nil);
+ fd = sys->fildes(fd.fd);
+ dfd = sys->fildes(dfd.fd);
+ sync <-= 1;
+
+ # XXX unfortunately we cannot pass through the aname from
+ # the original attach, an inherent shortcoming of this scheme.
+ if (sys->mount(fd, nil, "/", Sys->MREPL|Sys->MCREATE, hostname) == -1)
+ error(sys->sprint("cannot mount for user '%s': %r\n", uname));
+
+ sys->export(dfd, "/", Sys->EXPWAIT);
+}
+
+error(e: string)
+{
+ sys->fprint(stderr(), "styxlisten: %s\n", e);
+ raise "fail:error";
+}
+
+
+popen(ctxt: ref Draw->Context, argv: list of string, lsync: chan of int): ref Sys->FD
+{
+ sync := chan of int;
+ fds := array[2] of ref Sys->FD;
+ sys->pipe(fds);
+ spawn runcmd(ctxt, argv, fds[0], sync, lsync);
+ <-sync;
+ return fds[1];
+}
+
+runcmd(ctxt: ref Draw->Context, argv: list of string, stdin: ref Sys->FD,
+ sync: chan of int, lsync: chan of int)
+{
+ sys->pctl(Sys->FORKFD, nil);
+ sys->dup(stdin.fd, 0);
+ stdin = nil;
+ sync <-= 0;
+ sh := load Sh Sh->PATH;
+ e := sh->run(ctxt, argv);
+ kill(<-lsync, "kill"); # kill listener, as command has exited
+ if(verbose){
+ if(e != nil)
+ sys->fprint(stderr(), "styxlisten: command exited with error: %s\n", e);
+ else
+ sys->fprint(stderr(), "styxlisten: command exited\n");
+ }
+}
+
+kill(pid: int, how: string)
+{
+ sys->fprint(sys->open("/prog/"+string pid+"/ctl", Sys->OWRITE), "%s", how);
+}
+
+user(): string
+{
+ if ((s := readfile("/dev/user")) == nil)
+ return "none";
+ return s;
+}
+
+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];
+}
+
+getalgs(): list of string
+{
+ sslctl := readfile("#D/clone");
+ if (sslctl == nil) {
+ sslctl = readfile("#D/ssl/clone");
+ if (sslctl == nil)
+ return nil;
+ sslctl = "#D/ssl/" + sslctl;
+ } else
+ sslctl = "#D/" + sslctl;
+ (nil, algs) := sys->tokenize(readfile(sslctl + "/encalgs") + " " + readfile(sslctl + "/hashalgs"), " \t\n");
+ return "none" :: algs;
+}
+
+stderr(): ref Sys->FD
+{
+ return sys->fildes(2);
+}
+
+netmkaddr(addr, net, svc: string): string
+{
+ if(net == nil)
+ net = "net";
+ (n, l) := 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);
+}