MODULE DswHostProcs; IMPORT SYSTEM, DswProcs, HostLang, unistd := PosixCunistd, stdlib := PosixCstdlib, signal := PosixCsignal, sys_wait := PosixCsys_wait, macro := PosixCmacro, errno := PosixCerrno, sys_stat := PosixCsys_stat; TYPE Directory = POINTER TO RECORD (DswProcs.Directory) END; String = POINTER TO ARRAY OF SHORTCHAR; SString = POINTER TO ARRAY OF String; Process = POINTER TO RECORD (DswProcs.Process) argv: SString; pid: unistd.pid_t; code: unistd.int; END; PROCEDURE ToHost (IN s: ARRAY OF CHAR): String; VAR ss: String; res: INTEGER; BEGIN NEW(ss, LEN(s$) * 3 + 1); ASSERT(ss # NIL, 100); HostLang.StringToHost(s, ss, HostLang.pep383x, res); ASSERT(res = 0, 101); RETURN ss END ToHost; PROCEDURE IsRegular (mode: sys_stat.mode_t): BOOLEAN; BEGIN RETURN BITS(mode) * BITS(sys_stat.S_IFMT) = BITS(sys_stat.S_IFREG) END IsRegular; PROCEDURE (d: Directory) New* (): Process; VAR p: Process; BEGIN NEW(p); p.argv := NIL; p.pid := -1; p.code := 0; RETURN p END New; PROCEDURE (d: Directory) GetPath (IN name: ARRAY OF CHAR): DswProcs.String; VAR ch: CHAR; v: POINTER [untagged] TO ARRAY [untagged] OF SHORTCHAR; s, e, ret: POINTER TO ARRAY OF CHAR; w: String; cres: sys_stat.int; i, j, k, len, res: INTEGER; buf: sys_stat.struct_stat; found: BOOLEAN; BEGIN v := stdlib.getenv("PATH"); IF v # NIL THEN NEW(s, LEN(v$) * 3 + 1); HostLang.HostToString(v$, s, HostLang.pep383x, res); ASSERT(res = 0, 100); len := LEN(name$); NEW(e, LEN(s$) + len + 2); i := 0; REPEAT j := -1; found := FALSE; REPEAT ch := s[i]; INC(i); INC(j); e[j] := ch UNTIL (ch = ":") OR (ch = 0X); IF e[0] = "/" THEN e[j] := "/"; INC(j); FOR k := 0 TO len DO e[j + k] := name[k] END; e[j + k] := 0X; w := ToHost(e); cres := macro.stat(w, buf); found := (cres = 0) & IsRegular(buf.st_mode) & (unistd.access(w, unistd.X_OK) = 0) END UNTIL found OR (ch = 0X); IF ~found THEN e := NIL END END; RETURN e END GetPath; PROCEDURE (p: Process) Program* (IN exe: ARRAY OF CHAR); VAR argv: SString; BEGIN ASSERT(p.pid = -1, 20); NEW(argv, 1); argv[0] := ToHost(exe); p.argv := argv END Program; PROCEDURE (p: Process) PutParam* (IN par: ARRAY OF CHAR); VAR i: INTEGER; argv: SString; BEGIN ASSERT(p.pid = -1, 20); ASSERT(p.argv # NIL, 21); NEW(argv, LEN(p.argv) + 1); FOR i := 0 TO LEN(p.argv) - 1 DO argv[i] := p.argv[i] END; argv[i] := ToHost(par); p.argv := argv END PutParam; PROCEDURE (p: Process) Execute* (OUT ok: BOOLEAN); TYPE UString = POINTER [untagged] TO ARRAY [untagged] OF SHORTCHAR; SUString = POINTER TO ARRAY OF UString; VAR i: INTEGER; pid: unistd.pid_t; res: unistd.int; argv: SUString; s: String; BEGIN ASSERT(p.pid = -1, 20); ASSERT(p.argv # NIL, 21); NEW(argv, LEN(p.argv) + 2); FOR i := 0 TO LEN(p.argv) - 1 DO s := p.argv[i]; argv[i] := SYSTEM.VAL(UString, SYSTEM.ADR(s[0])) END; argv[i] := NIL; pid := unistd.fork(); IF pid = 0 THEN res := unistd.execv(argv[0], argv); unistd._exit(127); (* system() and most utils exit with 126/127 *) ok := FALSE ELSIF pid = -1 THEN ok := FALSE ELSE p.argv := NIL; (* or save it for debugging and reuse? *) p.pid := pid; ok := TRUE END END Execute; PROCEDURE (p: Process) Terminate* (OUT ok: BOOLEAN); VAR res, e: signal.int; BEGIN ASSERT(p.pid > 0, 20); res := signal.kill(p.pid, signal.SIGTERM); IF res = 0 THEN p.Wait; ok := TRUE ELSIF res = -1 THEN e := macro.errno(); CASE e OF | errno.EINVAL: HALT(100) (* wat *) | errno.EPERM: ok := FALSE (* process can not be killed *) | errno.ESRCH: p.code := 0; p.pid := -1; ok := TRUE (* process already killed *) END ELSE HALT(101) (* wat *) END END Terminate; PROCEDURE (p: Process) IsTerminated* (): BOOLEAN; VAR pid: unistd.pid_t; e: unistd.int; BEGIN IF p.pid # -1 THEN ASSERT(p.pid > 0, 100); (* wat *) pid := sys_wait.waitpid(p.pid, p.code, sys_wait.WNOHANG); IF pid = 0 THEN (* process still executes *) ELSIF pid = p.pid THEN p.pid := -1 (* process returns code *) ELSIF pid = -1 THEN e := macro.errno(); CASE e OF | errno.ECHILD: p.code := 0; p.pid := -1 (* process was killed externally *) | errno.EINVAL: HALT(101) (* wat *) | errno.EINTR: (* assume that process still executes *) END ELSE HALT(102) (* wat *) END END; RETURN p.pid = -1 END IsTerminated; PROCEDURE (p: Process) Wait*; VAR pid: unistd.pid_t; e: unistd.int; BEGIN ASSERT(p.pid > 0, 20); REPEAT pid := sys_wait.waitpid(p.pid, p.code, 0); IF pid = p.pid THEN p.pid := -1 (* was terminated *) ELSIF pid = -1 THEN e := macro.errno(); CASE e OF | errno.ECHILD: p.code := 0; p.pid := -1 (* process was killed externally *) | errno.EINVAL: HALT(100) (* wat *) | errno.EINTR: (* continue wait for process termination *) END ELSE HALT(101) (* wat *) END UNTIL p.pid = -1 END Wait; PROCEDURE (p: Process) Result (): INTEGER; BEGIN IF p.pid # -1 THEN p.Wait END; (* RETURN SYSTEM.CONV(INTEGER, sys_wait.WEXITSTATUS(p.code)) *) (* !!! *) RETURN SYSTEM.VAL(INTEGER, p.code) DIV 256 MOD 128 END Result; PROCEDURE Init; VAR d: Directory; BEGIN NEW(d); DswProcs.SetDir(d) END Init; BEGIN Init END DswHostProcs.