Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions lib_eio/process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,15 @@ module Env = struct
Printf.sprintf "%s=%s" name value

let get_opt name t =
Name.validate name;
let prefix = name ^ "=" in
Array.find_opt (Name.starts_with ~prefix) t
|> Option.map (fun e ->
let i = String.length prefix in
String.sub e i (String.length e - i)
)
if name = "" then None
else (
let prefix = name ^ "=" in
Array.find_opt (Name.starts_with ~prefix) t
|> Option.map (fun e ->
let i = String.length prefix in
String.sub e i (String.length e - i)
)
)

let override bindings t =
List.iter validate_binding bindings;
Expand Down Expand Up @@ -163,6 +165,9 @@ module Pi = struct
type tag
type t

val environment : t -> Env.t
val getenv_opt : t -> string -> string option

val pipe :
t ->
sw:Switch.t ->
Expand Down Expand Up @@ -221,6 +226,16 @@ let signal (type tag) (t : [> tag ty] r) s =
let module X = (val (Resource.get ops Pi.Process)) in
X.signal v s

let environment (type tag) (t : [> tag mgr_ty] r) =
let (Resource.T (v, ops)) = t in
let module X = (val (Resource.get ops Pi.Mgr)) in
X.environment v

let getenv_opt (type tag) (t : [> tag mgr_ty] r) =
let (Resource.T (v, ops)) = t in
let module X = (val (Resource.get ops Pi.Mgr)) in
X.getenv_opt v

let spawn (type tag) ~sw (t : [> tag mgr_ty] r) ?cwd ?stdin ?stdout ?stderr ?env ?executable args : tag ty r =
let (Resource.T (v, ops)) = t in
let module X = (val (Resource.get ops Pi.Mgr)) in
Expand Down
17 changes: 14 additions & 3 deletions lib_eio/process.mli
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ type 'a mgr = 'a r
constraint 'a = [> [> `Generic] mgr_ty]
(** A process manager capable of spawning new processes. *)

(** {2 Environment variables} *)

module Env : sig
(** A list of environment variable entries.

Expand Down Expand Up @@ -79,9 +81,8 @@ module Env : sig
This just adds [xs] to {!empty} using {!override}. *)

val get_opt : string -> t -> string option
(** [get_opt name t] is the value of [name] in [t], or [None] if there is no such binding.

@raise Invalid_argument if [name] is not a valid name. *)
(** [get_opt name t] is the value of [name] in [t], or [None] if there is no such binding
(or if [name] is not a valid name). *)

val override : (string * string option) list -> t -> t
(** [override bindings t] is a new environment which is like [t]
Expand Down Expand Up @@ -109,6 +110,13 @@ module Env : sig
val pp : t Fmt.t
end

val environment : _ mgr -> Env.t
(** [environment t] returns a snapshot of this process's current environment. *)

val getenv_opt : _ mgr -> string -> string option
(** [getenv_opt t name] will get the environment variable called [name].
Returns [None] if [name] does not exist. *)

(** {2 Processes} *)

val pid : _ t -> int
Expand Down Expand Up @@ -228,6 +236,9 @@ module Pi : sig
type tag
type t

val environment : t -> Env.t
val getenv_opt : t -> string -> string option

val pipe :
t ->
sw:Switch.t ->
Expand Down
3 changes: 3 additions & 0 deletions lib_eio/unix/process.ml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ end) = struct

type tag = [ `Generic | `Unix ]

let environment _t = Unix.environment () |> Eio.Process.Env.of_array
let getenv_opt _t name = Sys.getenv_opt name

let pipe _ ~sw =
(Private.pipe sw :> ([Eio.Resource.close_ty | Eio.Flow.source_ty] r *
[Eio.Resource.close_ty | Eio.Flow.sink_ty] r))
Expand Down
17 changes: 10 additions & 7 deletions lib_eio_linux/tests/spawn.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ open Eio.Std

module Env = Eio.Process.Env
module Process = Eio_linux.Low_level.Process

let default_env = Unix.environment () |> Env.of_array
```

## Spawning processes
Expand All @@ -31,7 +29,8 @@ FOO=bar
Changing directory:

```ocaml
# Eio_linux.run @@ fun _env ->
# Eio_linux.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child = Process.spawn ~sw Process.Fork_action.[
chdir "/";
Expand All @@ -47,7 +46,8 @@ Changing directory:
Changing directory using a file descriptor:

```ocaml
# Eio_linux.run @@ fun _env ->
# Eio_linux.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let root =
Eio_linux.Low_level.openat2 ~sw "/"
Expand All @@ -71,7 +71,8 @@ Changing directory using a file descriptor:
Exit status:

```ocaml
# Eio_linux.run @@ fun _env ->
# Eio_linux.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child = Process.spawn ~sw Process.Fork_action.[
execve "/usr/bin/env"
Expand All @@ -85,7 +86,8 @@ Exit status:
Failure starting child:

```ocaml
# Eio_linux.run @@ fun _env ->
# Eio_linux.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
Process.spawn ~sw Process.Fork_action.[
chdir "/idontexist";
Expand All @@ -99,7 +101,8 @@ Exception: Unix.Unix_error(Unix.ENOENT, "chdir", "")
Signalling a running child:

```ocaml
# Eio_linux.run @@ fun _env ->
# Eio_linux.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child =
Process.spawn ~sw Process.Fork_action.[
Expand Down
23 changes: 14 additions & 9 deletions lib_eio_posix/test/spawn.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ open Eio.Std

module Env = Eio.Process.Env
module Process = Eio_posix.Low_level.Process

let default_env = Unix.environment () |> Env.of_array
```

## Spawning processes
Expand All @@ -31,7 +29,8 @@ FOO=bar
Changing directory:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child = Process.spawn ~sw Process.Fork_action.[
chdir "/";
Expand All @@ -47,7 +46,8 @@ Changing directory:
Changing directory using a file descriptor:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let root = Eio_posix.Low_level.openat ~sw ~mode:0 Fs "/" Eio_posix.Low_level.Open_flags.(rdonly + directory) in
let child = Process.spawn ~sw Process.Fork_action.[
Expand All @@ -64,7 +64,8 @@ Changing directory using a file descriptor:
Exit status:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child = Process.spawn ~sw Process.Fork_action.[
execve "/usr/bin/env"
Expand All @@ -78,7 +79,8 @@ Exit status:
Failure starting child:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
Process.spawn ~sw Process.Fork_action.[
chdir "/idontexist";
Expand All @@ -92,7 +94,8 @@ Exception: Unix.Unix_error(Unix.ENOENT, "chdir", "")
Signalling a running child:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let child =
Process.spawn ~sw Process.Fork_action.[
Expand Down Expand Up @@ -163,7 +166,8 @@ let read_all pipe =
Swapping FDs (note: plain sh can't handle multi-digit FDs!):

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let pipe1_r, pipe1_w = Eio_unix.pipe sw in
let pipe2_r, pipe2_w = Eio_unix.pipe sw in
Expand Down Expand Up @@ -208,7 +212,8 @@ Swapping FDs (note: plain sh can't handle multi-digit FDs!):
Keeping an FD open:

```ocaml
# Eio_posix.run @@ fun _env ->
# Eio_posix.run @@ fun env ->
let default_env = Eio.Stdenv.process_mgr env |> Eio.Process.environment in
Switch.run @@ fun sw ->
let pipe1_r, pipe1_w = Eio_unix.pipe sw in
let child =
Expand Down
39 changes: 35 additions & 4 deletions tests/process.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,16 @@ A custom environment:
- : string = ":2"
```

Using the parent's environment explicitly:

```ocaml
# run @@ fun mgr env ->
Unix.putenv "DISPLAY" ":1";
let env = Eio.Process.environment mgr in
Process.parse_out ~env mgr Eio.Buf_read.line ["sh"; "-c"; "echo $DISPLAY"];;
- : string = ":1"
```

Eio's child reaping code doesn't interfere with OCaml's process spawning:

```ocaml
Expand Down Expand Up @@ -293,10 +303,8 @@ Invalid environment variable name "k="
# e |> Env.override ["k", Some "v=1"] |> Env.get_opt "k";;
- : string option = Some "v=1"

# try Env.(get_opt "" empty) |> ignore
with Invalid_argument x -> print_endline x;;
Invalid environment variable name ""
- : unit = ()
# Env.(get_opt "" (of_array [| "=foo" |]));;
- : string option = None

# try e |> Env.override ["", None] |> ignore
with Invalid_argument x -> print_endline x;;
Expand All @@ -320,3 +328,26 @@ val e : Env.t = [""
"c=5"
"c=6"]
```

Using the environment capability:

```ocaml
# run @@ fun mgr _env ->
Unix.putenv "DISPLAY" ":1";
Eio.Process.getenv_opt mgr "DISPLAY";;
- : string option = Some ":1"
```

```ocaml
# run @@ fun mgr _env ->
Eio.Process.getenv_opt mgr "THIS_VAR_PROBABLY_WILL_NOT_EXIST";;
- : string option = None
```

```ocaml
# run @@ fun mgr _env ->
Unix.putenv "DISPLAY" ":1";
let env = Eio.Process.environment mgr in
Eio.Process.Env.get_opt "DISPLAY" env;;
- : string option = Some ":1"
```
Loading