1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
open Eio.Std
let resolve_program name =
if Filename.is_implicit name then (
Sys.getenv_opt "PATH"
|> Option.value ~default:"/bin:/usr/bin"
|> String.split_on_char ':'
|> List.find_map (fun dir ->
let p = Filename.concat dir name in
if Sys.file_exists p then Some p else None
)
) else if Sys.file_exists name then (
Some name
) else None
let read_of_fd ~sw ~default ~to_close = function
| None -> default
| Some f ->
match Resource.fd_opt f with
| Some fd -> fd
| None ->
let r, w = Private.pipe sw in
Fiber.fork ~sw (fun () ->
Eio.Flow.copy f w;
Eio.Flow.close w
);
let r = Resource.fd r in
to_close := r :: !to_close;
r
let write_of_fd ~sw ~default ~to_close = function
| None -> default
| Some f ->
match Resource.fd_opt f with
| Some fd -> fd
| None ->
let r, w = Private.pipe sw in
Fiber.fork ~sw (fun () ->
Eio.Flow.copy r f;
Eio.Flow.close r
);
let w = Resource.fd w in
to_close := w :: !to_close;
w
let with_close_list fn =
let to_close = ref [] in
let close () =
List.iter Fd.close !to_close
in
match fn to_close with
| x -> close (); x
| exception ex ->
let bt = Printexc.get_raw_backtrace () in
close ();
Printexc.raise_with_backtrace ex bt
let get_executable ~args = function
| Some exe -> exe
| None ->
match args with
| [] -> invalid_arg "Arguments list is empty and no executable given!"
| (x :: _) ->
match resolve_program x with
| Some x -> x
| None -> raise (Eio.Process.err (Executable_not_found x))
let get_env = function
| Some e -> e
| None -> Unix.environment ()
type ty = [ `Generic | `Unix ] Eio.Process.ty
type 'a t = ([> ty] as 'a) r
type mgr_ty = [`Generic | `Unix] Eio.Process.mgr_ty
type 'a mgr = ([> mgr_ty] as 'a) r
module Pi = struct
module type MGR = sig
include Eio.Process.Pi.MGR
val spawn_unix :
t ->
sw:Switch.t ->
?cwd:Eio.Fs.dir_ty Eio.Path.t ->
env:string array ->
fds:(int * Fd.t * Fork_action.blocking) list ->
executable:string ->
string list ->
ty r
end
type (_, _, _) Eio.Resource.pi +=
| Mgr_unix : ('t, (module MGR with type t = 't), [> mgr_ty]) Eio.Resource.pi
let mgr_unix (type t tag) (module X : MGR with type t = t and type tag = tag) =
Eio.Resource.handler [
H (Eio.Process.Pi.Mgr, (module X));
H (Mgr_unix, (module X));
]
end
module Make_mgr (X : sig
type t
val spawn_unix :
t ->
sw:Switch.t ->
?cwd:Eio.Fs.dir_ty Eio.Path.t ->
env:string array ->
fds:(int * Fd.t * Fork_action.blocking) list ->
executable:string ->
string list ->
ty r
end) = struct
type t = X.t
type tag = [ `Generic | `Unix ]
let pipe _ ~sw =
(Private.pipe sw :> ([Eio.Resource.close_ty | Eio.Flow.source_ty] r *
[Eio.Resource.close_ty | Eio.Flow.sink_ty] r))
let spawn v ~sw ?cwd ?stdin ?stdout ?stderr ?env ?executable args =
let executable = get_executable executable ~args in
let env = get_env env in
with_close_list @@ fun to_close ->
let stdin_fd = read_of_fd ~sw stdin ~default:Fd.stdin ~to_close in
let stdout_fd = write_of_fd ~sw stdout ~default:Fd.stdout ~to_close in
let stderr_fd = write_of_fd ~sw stderr ~default:Fd.stderr ~to_close in
let fds = [
0, stdin_fd, `Blocking;
1, stdout_fd, `Blocking;
2, stderr_fd, `Blocking;
] in
X.spawn_unix v ~sw ?cwd ~env ~fds ~executable args
let spawn_unix = X.spawn_unix
end
let spawn_unix ~sw (Eio.Resource.T (v, ops)) ?cwd ~fds ?env ?executable args =
let module X = (val (Eio.Resource.get ops Pi.Mgr_unix)) in
let executable = get_executable executable ~args in
let env = get_env env in
X.spawn_unix v ~sw ?cwd ~fds ~env ~executable args
let sigchld = Eio.Condition.create ()
let install_sigchld_handler () =
Sys.(set_signal sigchld) (Signal_handle (fun (_:int) -> Eio.Condition.broadcast sigchld))
|