diff --git a/src/lib/built_ins.ml b/src/lib/built_ins.ml --- a/src/lib/built_ins.ml +++ b/src/lib/built_ins.ml @@ -87,6 +87,7 @@ params : string list; } type t = + | Msh_info (* Built-in Actions *) | Cd of { path : string option } | Pwd @@ -114,6 +115,7 @@ let reserved = [ "fg"; "bg"; "jobs" ] let pp_args = Fmt.(list ~sep:(Fmt.any " ") string) let to_string = function + | Msh_info -> "msh-info" | Cd { path = None } -> "cd" | Cd { path = Some path } -> Fmt.str "cd %s" path | Pwd -> "pwd" @@ -668,6 +670,7 @@ | "umask" :: _ as cmd -> exec_cmd cmd Umask.t | "shift" :: _ as cmd -> exec_cmd cmd Shift.t | "read" :: _ as cmd -> exec_cmd cmd Read.t | "getopts" :: _ as cmd -> exec_cmd cmd Getopts.t + | "msh-info" :: _ -> Some (Ok Msh_info) | cmd :: _ -> if List.mem cmd reserved then begin Debug.Log.err (fun f -> f "Unimplemented built-in: %s" cmd); diff --git a/src/lib/built_ins.mli b/src/lib/built_ins.mli --- a/src/lib/built_ins.mli +++ b/src/lib/built_ins.mli @@ -46,6 +46,7 @@ params : string list; } type t = + | Msh_info (* For debugging the internals of the shell. *) | Cd of { path : string option } (** Change directory to a path (if [None] then it should be [HOME]) *) | Pwd diff --git a/src/lib/eval.ml b/src/lib/eval.ml --- a/src/lib/eval.ml +++ b/src/lib/eval.ml @@ -130,10 +130,10 @@ | `Double -> Fmt.string ppf "double" | `Single -> Fmt.string ppf "single" in (Fmt.braces - @@ Fmt.record + @@ Fmt.record ~sep:Fmt.comma [ Fmt.field "quotes" (fun t -> t.quotes) pp_quotes; - Fmt.field "rdrs" (fun t -> t.rdrs) Fmt.(lst Types.pp_redirect); + Fmt.field "fd-table" (fun t -> t.fd_table) Fd_table.pp; Fmt.field "argv" (fun t -> Array.to_list t.argv) Fmt.(lst string); Fmt.field "functions" (fun t -> t.functions |> List.map fst) @@ -662,11 +662,30 @@ Fmt.(list Types.pp_redirect) rdrs); Eunix.with_redirections ~restore:false rdrs Fun.id; + let fd_table = + List.fold_left + (fun tb -> function + | Types.Redirect (true, fd, efd, _) -> + Eio_unix.Fd.use_exn "fd-table-rdr" efd + @@ fun u -> + Fd_table.add_fd ~src:fd + ~tgt:(Obj.magic u : int) + tb + | Types.Redirect (false, _, _, _) -> tb + | Types.Close efd -> + Eio_unix.Fd.use_exn "fd-table-close" + efd + @@ fun u -> + Fd_table.remove (Obj.magic u : int) tb) + ctx.fd_table rdrs + in if args <> [] then Unix.execve (Option.get prog) (Array.of_list args) (get_env ~extra:ctx.local_state ctx) - else job + else + handle_job job + (`Rdr (Exit.zero { ctx with fd_table })) | ":" -> job | _ -> ( let saved_ctx = ctx in @@ -1968,7 +1987,11 @@ let rdrs = make_child_rdrs_for_parent rdrs in Eunix.with_redirections ~restore:true rdrs @@ fun () -> Debug.Log.debug (fun f -> f "built-in: %s" (Built_ins.to_string v)); match v with - | Built_ins.Cd { path } -> + | Built_ins.Msh_info -> + let str = Fmt.str "msh-info\n%a\n" dump_ctx ctx in + Eio.Flow.copy_string str ctx.stdout; + Exit.zero ctx + | Cd { path } -> let cwd = S.cwd ctx.state in let+ state = match path with diff --git a/src/lib/fd_table.ml b/src/lib/fd_table.ml --- a/src/lib/fd_table.ml +++ b/src/lib/fd_table.ml @@ -48,3 +48,7 @@ f "FD %i disagreement %a %a" fd pp_resource v1 pp_resource v2); Some v2) let empty = M.empty + +let pp ppf t = + let bindings = M.bindings t |> List.filter (fun (i, _) -> i > 2) in + Fmt.(lst (pair ~sep:(any " -> ") int pp_resource)) ppf bindings diff --git a/src/lib/fd_table.mli b/src/lib/fd_table.mli --- a/src/lib/fd_table.mli +++ b/src/lib/fd_table.mli @@ -23,3 +23,6 @@ The second FD table takes precedence in the case of conflict. *) val empty : t (** The empty file descriptor table. *) + +val pp : t Fmt.t +(** A pretty printer for FD tables. *) diff --git a/test/fd_table.t b/test/fd_table.t new file mode 100644 --- /dev/null +++ b/test/fd_table.t @@ -0,0 +1,14 @@ +Keeping track of the file descriptors in-memory (not just the process itself). + + $ cat > test.sh << EOF + > exec 4>&1 + > msh-info + > EOF + + $ msh test.sh + msh-info + {quotes: none, + fd-table: + [4 -> FD-1, 455 -> Path-pipe:[818672], 456 -> Path-pipe:[818672]], + argv: [test.sh], + functions: []}