Files
hak/t/sysproc-01.hak

115 lines
3.6 KiB
Plaintext

## child processes: sys.popen / pwait / pkill / pclose over the handle table
fun chk(ok msg) {
if ok { printf "OK: %s\n" msg } \
else { printf "ERROR: %s\n" msg }
}
iosem := (core.sem-new)
tmo := (core.sem-new)
sg := (core.semgr-new)
core.semgr-add sg iosem
core.semgr-add sg tmo
fun waitin(h secs) {
| s |
core.sem-signal tmo secs 0
core.sem-signal-on-input iosem h
s := (core.semgr-wait sg)
core.sem-unsignal iosem
core.sem-unsignal tmo
if (eqv? s tmo) { return 0 } else { return 1 }
}
## read until data arrives, retrying on -1
fun rd(h buf) {
| n |
while true {
n := (sys.read h buf)
if (>= n 0) { return n }
if (= (waitin h 5) 0) { return -1 }
}
}
## reap without blocking the VM, waiting on a short timer between looks
fun reap(proc) {
| n k |
k := 0
while (< k 300) {
n := (sys.pwait proc)
if (not (= n 256)) { return n }
core.sem-signal tmo 0 20000000
core.semgr-wait sg
core.sem-unsignal tmo
k := (+ k 1)
}
return 256
}
buf := (core.basicNew ByteArray 64)
## --- stdout and the exit status ---
p := (sys.popen "printf abc; exit 3" "r")
chk (array? p) "sys.popen returns a handle array"
chk (integer? (core.basicAt p 0)) "slot 0 is the process handle"
chk (nil? (core.basicAt p 1)) "stdin is nil when 'w' was not requested"
chk (integer? (core.basicAt p 2)) "stdout is a handle when 'r' was requested"
chk (nil? (core.basicAt p 3)) "stderr is nil when 'e' was not requested"
chk (= (rd (core.basicAt p 2) buf) 3) "the child's stdout is readable"
chk (= (reap (core.basicAt p 0)) 3) "the exit status comes through"
sys.pclose (core.basicAt p 0)
## --- bidirectional: write to stdin, read stdout ---
p := (sys.popen "tr a-z A-Z" "rw")
inh := (core.basicAt p 1)
outh := (core.basicAt p 2)
chk (integer? inh) "stdin is a handle when 'w' was requested"
wb := (core.basicNew ByteArray 3)
core.basicAtPut wb 0 120
core.basicAtPut wb 1 121
core.basicAtPut wb 2 122
chk (= (sys.write inh wb) 3) "writing to the child's stdin works"
## closing the child's stdin must really send eof, or tr never flushes
sys.close inh
chk (= (rd outh buf) 3) "the child answered after stdin was closed"
chk (= (core.basicAt buf 0) 88) "the child transformed the data (x -> X)"
sys.pclose (core.basicAt p 0)
## --- stderr kept separate from stdout ---
p := (sys.popen "printf OUT; printf ERRR 1>&2" "re")
chk (= (rd (core.basicAt p 2) buf) 3) "stdout has its own stream"
chk (= (rd (core.basicAt p 3) buf) 4) "stderr has its own stream"
sys.pclose (core.basicAt p 0)
## --- a signalled child reports 256 + signo ---
p := (sys.popen "kill -TERM $$" "r")
chk (= (reap (core.basicAt p 0)) 271) "a signalled child reports 256 + SIGTERM"
sys.pclose (core.basicAt p 0)
## --- sys.pkill ---
p := (sys.popen "sleep 60" "r")
chk (= (sys.pwait (core.basicAt p 0)) 256) "a running child reports 256"
sys.pkill (core.basicAt p 0)
chk (= (reap (core.basicAt p 0)) 265) "after pkill the child reports 256 + SIGKILL"
sys.pclose (core.basicAt p 0)
## --- pclose tears the whole group down ---
p := (sys.popen "sleep 60" "r")
outh := (core.basicAt p 2)
sys.pclose (core.basicAt p 0)
chk true "pclose released the group"
## --- the exit handle ---
## it is muxable but carries no bytes. it exists only where the platform has
## pidfd_open(); elsewhere sys.popen answers nil for it, so guard the check
## rather than assuming Linux.
p := (sys.popen "true" "r")
xh := (core.basicAt p 4)
if (nil? xh) { printf "OK: no exit handle where the platform has no pidfd\n" } \
else {
raised := false
try { sys.read xh (core.basicNew ByteArray 4) } catch (e) { raised := true }
chk raised "the exit handle carries no bytes"
}
sys.pclose (core.basicAt p 0)