# cd; limbo shfs.b; mount {shfs} /n/remote; cd /n/remote; echo date > new; echo 'ls / | grep lib' > new; # % ls -l # d-rwxrwxrwx M 84 inferno inferno 0 Jan 01 1970 0 # d-rwxrwxrwx M 84 inferno inferno 0 Jan 01 1970 1 # --rw-rw-rw- M 84 inferno inferno 0 Jan 01 1970 new # % ls 0 # 0/cmd # 0/stdout # % cat 0/stdout # Fri Jun 27 01:07:16 BST 2003 # % cat 0/stdout # Fri Jun 27 01:07:16 BST 2003 # % rm 0/stdout # % cat 0/stdout # Fri Jun 27 01:07:30 BST 2003 # % cat 1/cmd # ls / | grep lib implement shfs2; include "sys.m"; include "draw.m"; include "styx.m"; include "styxservers.m"; include "sh.m"; include "string.m"; sys: Sys; sh: Sh; styx : Styx; str : String; Context: import sh; Rmsg : import styx; styxservers: Styxservers; Styxserver, Navigator: import styxservers; nametree: Nametree; Tree: import nametree; tree : ref Tree; bitshift : con 32; shfs2: module { init: fn(nil: ref Draw->Context, argv: list of string); shell_cmd: adt { cmd : string; stdout : array of byte; perform : fn (this : self shell_cmd) : array of byte; }; }; shell_cmd.perform(this : self shell_cmd) : array of byte { # not quite how I wanted it # I wanted to return void and set this.stdout but it seems to be working on a copy if (this.stdout != nil) return this.stdout; bchan := chan of array of byte; spawn child(bchan, this.cmd); stdout := <-bchan; return stdout; } Qroot, Qnew, Qfolder, Qcommand, Qstdout: con big iota; # paths commands : array of shell_cmd; command_count := 0; init(nil: ref Draw->Context, nil: list of string) { sys = load Sys Sys->PATH; styx = load Styx Styx->PATH; sh = load Sh Sh->PATH; str = load String String->PATH; treeop : chan of ref Styxservers->Navop; styx->init(); styxservers = load Styxservers Styxservers->PATH; styxservers->init(styx); nametree = load Nametree Nametree->PATH; nametree->init(); sys->pctl(Sys->FORKNS, nil); (tree, treeop) = nametree->start(); tree.create(Qroot, dir(".", 8r555|Sys->DMDIR, Qroot)); tree.create(Qroot, dir("new", 8r666, Qnew)); (tchan, srv) := Styxserver.new(sys->fildes(0),Navigator.new(treeop), Qroot); reply : ref Rmsg; while((gm := <-tchan) != nil) { pick m := gm { Read => { f := srv.getfid(m.fid); qtype := big int(f.path); case (qtype) { Qstdout => { i := int(f.path >> bitshift); commands[i].stdout = commands[i].perform(); reply = styxservers->readbytes(m, commands[i].stdout); } Qcommand => { i := int(f.path >> bitshift); reply = styxservers->readbytes(m, array of byte commands[i].cmd); } } } Write => { f := srv.getfid(m.fid); qtype := big int(f.path); case (qtype) { Qcommand => { i := int(f.path >> bitshift); commands[i].cmd = string m.data; commands[i].stdout = nil; reply = ref Rmsg.Write(m.tag, len m.data); } Qnew => { add_command(Qroot, string m.data); reply = ref Rmsg.Write(m.tag, len m.data); } } } Remove => { f := srv.getfid(m.fid); qtype := big int(f.path); case (qtype) { Qcommand => { i := int(f.path >> bitshift); commands[i].cmd =""; commands[i].stdout = nil; reply = ref Rmsg.Remove(m.tag); } Qstdout => { i := int(f.path >> bitshift); commands[i].stdout = nil; reply = ref Rmsg.Remove(m.tag); } } if (reply != nil) { srv.delfid(f); } } } if(reply == nil) { srv.default(gm); } else { srv.reply(reply); reply = nil; } } tree.quit(); } extend_commands() { # grow the commands array quarter := 5 + len commands / 4; # formula plucked from thin air new_commands := array[len commands + quarter] of shell_cmd; if (len commands > 0) new_commands[0:] = commands; commands = new_commands; } add_command (dir_qid : big, cmd : string) { if (command_count == len commands) extend_commands(); sh_cmd := shell_cmd (cmd, nil); f_qid := big command_count; f_qid = f_qid << bitshift; f_qid += Qfolder; tree.create(dir_qid, dir(sys->sprint("%d", command_count), 8r777 | Sys->DMDIR, f_qid)); qid := big command_count; qid = qid << bitshift; qid += Qcommand; tree.create(f_qid, dir("cmd", 8r666, qid)); qid = big command_count; qid = qid << bitshift; qid += Qstdout; tree.create(f_qid, dir("stdout", 8r444, qid)); commands[command_count++] = sh_cmd; } dir(name: string, perm: int, qid: big): Sys->Dir { d := sys->zerodir; d.name = name; d.uid = "inferno"; d.gid = "inferno"; d.qid.path = qid; if (perm & Sys->DMDIR) d.qid.qtype = Sys->QTDIR; else d.qid.qtype = Sys->QTFILE; d.mode = perm; return d; } child(pidc : chan of array of byte, cmd : string) { res := array [1024] of byte; fds := array[2] of ref Sys->FD; sys->pipe(fds); sync := chan of int; spawn async(sync, fds[1].fd, cmd); <- sync; fds[1] = nil; got := 0; for (;;) { n := sys->read(fds[0] , res[got:], len res - got); if (n <= 0) break; got += n; if (n == len res) res = (array [2* len res] of byte)[:] = res; } pidc <-= res[:got]; } async(sync: chan of int, outfd: int, cmd: string) { sys->pctl(Sys->NEWFD|Sys->FORKNS|Sys->NEWPGRP, outfd::nil); sys->dup(outfd, 1); sync <-= 1; sh->system(nil, cmd); }