summaryrefslogtreecommitdiff
path: root/appl/cmd/mash/exec.b
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 17:07:39 +0000
commit37da2899f40661e3e9631e497da8dc59b971cbd0 (patch)
treecbc6d4680e347d906f5fa7fca73214418741df72 /appl/cmd/mash/exec.b
parent54bc8ff236ac10b3eaa928fd6bcfc0cdb2ba46ae (diff)
20060303a
Diffstat (limited to 'appl/cmd/mash/exec.b')
-rw-r--r--appl/cmd/mash/exec.b401
1 files changed, 401 insertions, 0 deletions
diff --git a/appl/cmd/mash/exec.b b/appl/cmd/mash/exec.b
new file mode 100644
index 00000000..faa003b3
--- /dev/null
+++ b/appl/cmd/mash/exec.b
@@ -0,0 +1,401 @@
+#
+# Manage the execution of a command.
+#
+
+srv: string; # srv file proto
+nsrv: int = 0; # srv file unique id
+
+#
+# Return error string.
+#
+errstr(): string
+{
+ return sys->sprint("%r");
+}
+
+#
+# Server thread for servefd.
+#
+server(c: ref Sys->FileIO, fd: ref Sys->FD, write: int)
+{
+ a: array of byte;
+ if (!write)
+ a = array[Sys->ATOMICIO] of byte;
+ for (;;) {
+ alt {
+ (nil, b, nil, wc) := <- c.write =>
+ if (wc == nil)
+ return;
+ if (!write) {
+ wc <- = (0, EPIPE);
+ return;
+ }
+ r := sys->write(fd, b, len b);
+ if (r < 0) {
+ wc <- = (0, errstr());
+ return;
+ }
+ wc <- = (r, nil);
+ (nil, n, nil, rc) := <- c.read =>
+ if (rc == nil)
+ return;
+ if (write) {
+ rc <- = (array[0] of byte, nil);
+ return;
+ }
+ if (n > Sys->ATOMICIO)
+ n = Sys->ATOMICIO;
+ r := sys->read(fd, a, n);
+ if (r < 0) {
+ rc <- = (nil, errstr());
+ return;
+ }
+ rc <- = (a[0:r], nil);
+ }
+ }
+}
+
+#
+# Serve FD as a #s file. Used to implement generators.
+#
+Env.servefd(e: self ref Env, fd: ref Sys->FD, write: int): string
+{
+ (s, c) := e.servefile(nil);
+ spawn server(c, fd, write);
+ return s;
+}
+
+#
+# Generate name and FileIO adt for a served filed.
+#
+Env.servefile(e: self ref Env, n: string): (string, ref Sys->FileIO)
+{
+ c: ref Sys->FileIO;
+ s: string;
+ if (srv == nil) {
+ (ok, d) := sys->stat(CHAN);
+ if (ok < 0)
+ e.couldnot("stat", CHAN);
+ if (d.dtype != 's') {
+ if (sys->bind("#s", CHAN, Sys->MBEFORE) < 0)
+ e.couldnot("bind", CHAN);
+ }
+ srv = "mash." + string sys->pctl(0, nil);
+ }
+ retry := 0;
+ for (;;) {
+ if (retry || n == nil)
+ s = srv + "." + string nsrv++;
+ else
+ s = n;
+ c = sys->file2chan(CHAN, s);
+ s = CHAN + "/" + s;
+ if (c == nil) {
+ if (retry || n == nil || errstr() != EEXISTS)
+ e.couldnot("file2chan", s);
+ retry = 1;
+ continue;
+ }
+ break;
+ }
+ if (n != nil)
+ n = CHAN + "/" + n;
+ else
+ n = s;
+ if (retry && sys->bind(s, n, Sys->MREPL) < 0)
+ e.couldnot("bind", n);
+ return (n, c);
+}
+
+#
+# Shorthand for string output.
+#
+Env.output(e: self ref Env, s: string)
+{
+ if (s == nil)
+ return;
+ out := e.outfile();
+ if (out == nil)
+ return;
+ out.puts(s);
+ out.close();
+}
+
+#
+# Return Iobuf for stdout.
+#
+Env.outfile(e: self ref Env): ref Bufio->Iobuf
+{
+ fd := e.out;
+ if (fd == nil)
+ fd = sys->fildes(1);
+ out := bufio->fopen(fd, Bufio->OWRITE);
+ if (out == nil)
+ e.report(sys->sprint("fopen failed: %r"));
+ return out;
+}
+
+#
+# Return FD for /dev/null.
+#
+Env.devnull(e: self ref Env): ref Sys->FD
+{
+ fd := sys->open(DEVNULL, Sys->OREAD);
+ if (fd == nil)
+ e.couldnot("open", DEVNULL);
+ return fd;
+}
+
+#
+# Make a pipe.
+#
+Env.pipe(e: self ref Env): array of ref Sys->FD
+{
+ fds := array[2] of ref Sys->FD;
+ if (sys->pipe(fds) < 0) {
+ e.report(sys->sprint("pipe failed: %r"));
+ return nil;
+ }
+ return fds;
+}
+
+#
+# Open wait file for an env.
+#
+waitfd(e: ref Env)
+{
+ w := "#p/" + string sys->pctl(0, nil) + "/wait";
+ fd := sys->open(w, sys->OREAD);
+ if (fd == nil)
+ e.couldnot("open", w);
+ e.wait = fd;
+}
+
+#
+# Wait for a thread. Perhaps propagate exception or exit.
+#
+waitfor(e: ref Env, pid: int, wc: chan of int, ec, xc: chan of string)
+{
+ if (ec != nil || xc != nil) {
+ spawn waiter(e, pid, wc);
+ if (ec == nil)
+ ec = chan of string;
+ if (xc == nil)
+ xc = chan of string;
+ alt {
+ <-wc =>
+ return;
+ x := <-ec =>
+ <-wc;
+ exitmash();
+ x := <-xc =>
+ <-wc;
+ s := x;
+ if (len s < FAILLEN || s[0:FAILLEN] != FAIL)
+ s = FAIL + s;
+ raise s;
+ }
+ } else
+ waiter(e, pid, nil);
+}
+
+#
+# Wait for a specific pid.
+#
+waiter(e: ref Env, pid: int, wc: chan of int)
+{
+ buf := array[sys->WAITLEN] of byte;
+ for(;;) {
+ n := sys->read(e.wait, buf, len buf);
+ if (n < 0) {
+ e.report(sys->sprint("read wait: %r\n"));
+ break;
+ }
+ status := string buf[0:n];
+ if (status[len status - 1] != ':')
+ sys->fprint(e.stderr, "%s\n", status);
+ who := int status;
+ if (who != 0 && who == pid)
+ break;
+ }
+ if (wc != nil)
+ wc <-= 0;
+}
+
+#
+# Preparse IO for a new thread.
+# Make a new FD group and redirect stdin/stdout.
+#
+prepareio(in, out: ref sys->FD): (int, ref Sys->FD)
+{
+ fds := list of { 0, 1, 2};
+ if (in != nil)
+ fds = in.fd :: fds;
+ if (out != nil)
+ fds = out.fd :: fds;
+ pid := sys->pctl(sys->NEWFD, fds);
+ console := sys->fildes(2);
+ if (in != nil) {
+ sys->dup(in.fd, 0);
+ in = nil;
+ }
+ if (out != nil) {
+ sys->dup(out.fd, 1);
+ out = nil;
+ }
+ return (pid, console);
+}
+
+#
+# Add ".dis" to a command if missing.
+#
+dis(s: string): string
+{
+ if (len s < 4 || s[len s - 4:] != ".dis")
+ return s + ".dis";
+ return s;
+}
+
+#
+# Load a builtin.
+#
+Env.doload(e: self ref Env, s: string)
+{
+ file := dis(s);
+ l := load Mashbuiltin file;
+ if (l == nil) {
+ err := errstr();
+ if (nonexistent(err) && file[0] != '/' && file[0:2] != "./") {
+ l = load Mashbuiltin LIB + file;
+ if (l == nil)
+ err = errstr();
+ }
+ if (l == nil) {
+ e.report(s + ": " + err);
+ return;
+ }
+ }
+ l->mashinit("load" :: s :: nil, lib, l, e);
+}
+
+#
+# Execute a spawned thread (dis module or builtin).
+#
+mkprog(args: list of string, e: ref Env, in, out: ref Sys->FD, wc: chan of int, ec, xc: chan of string)
+{
+ (pid, console) := prepareio(in, out);
+ wc <-= pid;
+ if (pid < 0)
+ return;
+ cmd := hd args;
+ {
+ b := e.builtin(cmd);
+ if (b != nil) {
+ e = e.copy();
+ e.in = in;
+ e.out = out;
+ e.stderr = console;
+ e.wait = nil;
+ b->mashcmd(e, args);
+ } else {
+ file := dis(cmd);
+ c := load Command file;
+ if (c == nil) {
+ err := errstr();
+ if (nonexistent(err) && file[0] != '/' && file[0:2] != "./") {
+ c = load Command "/dis/" + file;
+ if (c == nil)
+ err = errstr();
+ }
+ if (c == nil) {
+ sys->fprint(console, "%s: %s\n", file, err);
+ return;
+ }
+ }
+ c->init(gctxt, args);
+ }
+ }exception x{
+ FAILPAT =>
+ if (xc != nil)
+ xc <-= x;
+ # the command failure should be propagated silently to
+ # a higher level, where $status can be set.. - wrtp.
+ #else
+ # sys->fprint(console, "%s: %s\n", cmd, x.name);
+ exit;
+ EPIPE =>
+ if (xc != nil)
+ xc <-= x;
+ #else
+ # sys->fprint(console, "%s: %s\n", cmd, x.name);
+ exit;
+ EXIT =>
+ if (ec != nil)
+ ec <-= x;
+ exit;
+ }
+}
+
+#
+# Open/create files for redirection.
+#
+redirect(e: ref Env, f: array of string, in, out: ref Sys->FD): (int, ref Sys->FD, ref Sys->FD)
+{
+ s: string;
+ err := 0;
+ if (f[Rinout] != nil) {
+ s = f[Rinout];
+ in = sys->open(s, Sys->ORDWR);
+ if (in == nil) {
+ sys->fprint(e.stderr, "%s: %r\n", s);
+ err = 1;
+ }
+ out = in;
+ } else if (f[Rin] != nil) {
+ s = f[Rin];
+ in = sys->open(s, Sys->OREAD);
+ if (in == nil) {
+ sys->fprint(e.stderr, "%s: %r\n", s);
+ err = 1;
+ }
+ }
+ if (f[Rout] != nil || f[Rappend] != nil) {
+ if (f[Rappend] != nil) {
+ s = f[Rappend];
+ out = sys->open(s, Sys->OWRITE);
+ if (out != nil)
+ sys->seek(out, big 0, Sys->SEEKEND);
+ } else {
+ s = f[Rout];
+ out = nil;
+ }
+ if (out == nil) {
+ out = sys->create(s, Sys->OWRITE, 8r666);
+ if (out == nil) {
+ sys->fprint(e.stderr, "%s: %r\n", s);
+ err = 1;
+ }
+ }
+ }
+ if (err)
+ return (0, nil, nil);
+ return (1, in, out);
+}
+
+#
+# Spawn a command and maybe wait for it.
+#
+exec(a: list of string, e: ref Env, infd, outfd: ref Sys->FD, wait: int)
+{
+ if (wait && e.wait == nil)
+ waitfd(e);
+ wc := chan of int;
+ if (wait && (e.flags & ERaise))
+ xc := chan of string;
+ if (wait && (e.flags & ETop))
+ ec := chan of string;
+ spawn mkprog(a, e, infd, outfd, wc, ec, xc);
+ pid := <-wc;
+ if (wait)
+ waitfor(e, pid, wc, ec, xc);
+}