ref: e3843e8fa26afff64728ffe93ef77f0b10a1f109
dir: /lib/std/spork.myr/
use "die" use "errno" use "execvp" use "fmt" use "result" use "syswrap" use "wait" pkg std = const run : (cmd : byte[:][:] -> waitstatus) const spork : (cmd : byte[:][:] -> result((pid, fd, fd), errno)) const espork : (cmd : byte[:][:] -> result((pid, fd, fd, fd), errno)) const filterfd : (fd : fd, cmd : byte[:][:] -> result(pid, errno)) ;; const run = {cmd var pid pid = fork() /* error */ if pid < 0 -> `Waiterror elif pid == 0 execvp(cmd[0], cmd) suicide() else -> wait(pid) ;; } const spork = {cmd var infds : fd[2], outfds : fd[2] var err /* init for safe close */ infds = [-1, -1] outfds = [-1, -1] /* open up pipes */ err = pipe(&infds) if err != Enone goto sporkerr ;; err = pipe(&outfds) if err != Enone goto sporkerr ;; match sporkfd(cmd, infds, outfds, [-1, 2]) | `Ok pid: /* close unused fd ends */ close(infds[0]); close(outfds[1]); -> `Ok (pid, infds[1], outfds[0]) | `Err m: err = m goto sporkerr ;; :sporkerr close(infds[0]) close(infds[1]) close(outfds[0]) close(outfds[1]) -> `Err err } const espork = {cmd var infds : fd[2], outfds : fd[2], errfds : fd[2] var err /* init for safe close */ infds = [-1, -1] outfds = [-1, -1] errfds = [-1, -1] /* open up pipes */ err = pipe(&infds) if err != Enone goto sporkerr ;; err = pipe(&outfds) if err != Enone goto sporkerr ;; err = pipe(&errfds) if err != Enone goto sporkerr ;; match sporkfd(cmd, infds, outfds, errfds) | `Ok pid: /* close unused fd ends */ close(infds[0]); close(outfds[1]); close(errfds[1]); -> `Ok (pid, infds[1], outfds[0], errfds[0]) | `Err m: err = m goto sporkerr ;; :sporkerr /* close on a bad fd is ok. */ close(infds[0]) close(infds[1]) close(outfds[0]) close(outfds[1]) close(errfds[0]) close(errfds[1]) -> `Err err } const filterfd = {fd, cmd var outfds : fd[2] var err err = pipe(&outfds) if err != Enone -> `Err err ;; match sporkfd(cmd, [fd, -1], outfds, [-1, 2]) | `Ok pid: dup2(outfds[0], fd) close(outfds[0]); close(outfds[1]); -> `Ok pid | `Err e: -> `Err e ;; } const sporkfd = {cmd, infds, outfds, errfds var pid pid = fork() /* error */ if pid < 0 /* so we don't leak fds on error, close the child's ends. The parent handles closing its fds. */ if infds[1] != 0 close(infds[1]); ;; if outfds[0] != 1 close(outfds[0]); ;; if errfds[0] != 2 close(errfds[0]); ;; -> `Err (pid : errno) /* child */ elif pid == 0 /* stdin/stdout for our communication. */ match dup2(infds[0], 0) | `Ok _: /* nothing */ | `Err e: -> `Err e ;; match dup2(outfds[1], 1) | `Ok _: /* nothing */ | `Err e: -> `Err e ;; match dup2(errfds[1], 2) | `Ok _: /* nothing */ | `Err e: -> `Err e ;; /* close the fds we duped */ if infds[0] != 0 close(infds[0]) ;; if outfds[1] != 1 close(outfds[1]) ;; if errfds[1] != 2 close(errfds[1]) ;; /* close the unused ends */ close(infds[1]) close(outfds[0]) close(errfds[0]) execvp(cmd[0], cmd) /* if fork succeeds, we never return */ suicide() /* parent */ else -> `Ok pid ;; }