# shfs1 - naively presents a filesystem of the output of commands passed to new or written to n
# I say naive because it's just the first working version
# there's no way to see what the command text is
# the ambition is to learn how to present a file as both a file and a directory
# but, one step at a time
# This code is based on man page examples and code supplied by Rog & Chris at vitanuova.com
# The rest M Heath, 2003
# feel free to use of it / do with it what you will but dont use it for a heart monitor and them blame me it it all goes wrong
#
# see http://www.topica.com/lists/inferno/ for further discussion
# bugs - command length might be limited to one styx packet payload length
# limits - the command will only be run once as I've not learned how to unlink yet
# send this to see it work
# cd; limbo shfs1.b; mount {shfs1} /n/remote; cd /n/remote; echo date > new; echo 'ls / | grep lib' > new; ls -l ; cat [0-9]*
implement shfs1;
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;
shfs1: 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, Qdata: con big iota; # qtypes
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) {
Qdata => {
i := int(f.path >> bitshift);
commands[i].stdout = commands[i].perform();
reply = styxservers->readbytes(m, commands[i].stdout);
}
}
}
Write => {
f := srv.getfid(m.fid);
qtype := big int(f.path);
case (qtype) {
Qdata => {
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);
}
}
}
}
if(reply == nil) {
srv.default(gm);
} else {
srv.reply(reply);
reply = nil;
}
}
tree.quit();
}
process_m_data(data : array of byte) : string { # take up to first \n
(cmd, nl) := str->splitl(string data, "\n");
return cmd;
}
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);
qid := big command_count;
qid = qid << bitshift;
qid += Qdata;
tree.create(dir_qid, dir(sys->sprint("%d", command_count), 8r666, 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);
}
|