Shells in OCaml
at main 176 lines 4.8 kB view raw
1let cwd () = Eio_unix.run_in_systhread ~label:"cwd" @@ fun () -> Unix.getcwd () 2 3let env () = 4 Eio_unix.run_in_systhread ~label:"env" @@ fun () -> 5 Unix.environment () 6 |> Array.map (Astring.String.cut ~sep:"=") 7 |> Array.to_list |> List.filter_map Fun.id 8 9let find_env k = env () |> List.assoc_opt k 10 11let put_env ~key ~value = 12 Eio_unix.run_in_systhread ~label:"put_env" @@ fun () -> Unix.putenv key value 13 14let get_user_and_host fs = 15 let passwd = 16 Eio.Path.(load (fs / "/etc/passwd")) 17 |> String.split_on_char '\n' 18 |> List.map (String.split_on_char ':') 19 in 20 let uid = Unix.getuid () in 21 let username = 22 List.find_map 23 (function 24 | name :: _ :: m :: _ -> 25 if int_of_string m = uid then Some name else None 26 | _ -> None) 27 passwd 28 in 29 let name = Option.value ~default:"?" username in 30 let host = Unix.gethostname () in 31 Fmt.str "%s@%s" name host 32 33external getpgrp : unit -> int = "caml_merry_getpgrp" 34external tcgetpgrp : Unix.file_descr -> int = "caml_merry_tcgetpgrp" 35external tcsetpgrp : int -> int -> int = "caml_merry_tcsetpgrp" 36 37let delegate_control ~pgid fn = 38 let shell_pid = Unix.getpid () in 39 Fun.protect 40 ~finally:(fun () -> 41 let _ : int = tcsetpgrp 0 shell_pid in 42 ()) 43 (fun () -> 44 match tcsetpgrp (Obj.magic Unix.stdin : int) pgid with 45 | 0 -> fn () 46 | n -> Fmt.failwith "tcsetpgrp: %i" n) 47 48external setpgrp : int -> int -> int = "caml_merry_setpgid" 49 50let make_process_group () = 51 let pgrp = getpgrp () in 52 let fg_prgp = tcgetpgrp Unix.stdin in 53 if Int.equal pgrp fg_prgp then 54 match setpgrp 0 0 with 55 | 0 -> () 56 | _ -> assert false 57 | exception Unix.Unix_error (Unix.EPERM, _, _) -> () 58 59let background () = 60 let _pgrid = Unix.getpid () in 61 () 62 63let fd_of_int (fd : int) : Unix.file_descr = Obj.magic fd 64 65let with_redirections (rdrs : Types.redirect list) fn = 66 let saved_stdin = Unix.dup Unix.stdin in 67 let saved_stdout = Unix.dup Unix.stdout in 68 let saved_stderr = Unix.dup Unix.stderr in 69 List.iter 70 (function 71 | Types.Redirect (i, fd, _) -> 72 Eio_unix.Fd.use_exn "with_redirections" fd @@ fun fd -> 73 if (Obj.magic fd : int) <> i then Unix.dup2 fd (fd_of_int i) 74 | Types.Close fd -> Eio_unix.Fd.close fd) 75 rdrs; 76 Fun.protect 77 ~finally:(fun () -> 78 Unix.dup2 saved_stdin (fd_of_int 0); 79 Unix.dup2 saved_stdout (fd_of_int 1); 80 Unix.dup2 saved_stderr (fd_of_int 2); 81 Unix.close saved_stdin; 82 Unix.close saved_stdout; 83 Unix.close saved_stderr) 84 fn 85 86let with_stdin_in_raw_mode fn = 87 let saved_tio = Unix.tcgetattr Unix.stdin in 88 let tio = 89 { 90 saved_tio with 91 (* input modes *) 92 c_ignpar = true; 93 c_istrip = false; 94 c_inlcr = false; 95 c_igncr = false; 96 c_ixon = false; 97 (* c_ixany = false; *) 98 (* c_iuclc = false; *) 99 c_ixoff = false; 100 (* output modes *) 101 c_opost = true; 102 (* control modes *) 103 c_isig = false; 104 c_icanon = false; 105 c_echo = false; 106 c_echoe = false; 107 c_echok = false; 108 c_echonl = false; 109 (* c_iexten = false; *) 110 111 (* special characters *) 112 c_vmin = 1; 113 c_vtime = 0; 114 } 115 in 116 Unix.tcsetattr Unix.stdin TCSADRAIN tio; 117 Fun.protect 118 ~finally:(fun () -> Unix.tcsetattr Unix.stdin TCSADRAIN saved_tio) 119 fn 120 121module Signals = struct 122 type t = 123 | Interrupt 124 | Quit 125 | Abort 126 | Kill 127 | Alarm 128 | Terminate 129 | Exit 130 | Stop 131 | Hup 132 [@@deriving to_yojson] 133 134 let of_int = function 135 | i when Int.equal i Sys.sigint -> Interrupt 136 | i when Int.equal i Sys.sigquit -> Quit 137 | i when Int.equal i Sys.sigabrt -> Abort 138 | i when Int.equal i Sys.sigkill -> Kill 139 | i when Int.equal i Sys.sigalrm -> Alarm 140 | i when Int.equal i Sys.sigterm -> Terminate 141 | i when Int.equal i Sys.sigstop -> Stop 142 | i when Int.equal i Sys.sighup -> Hup 143 (* From the manpages *) 144 | 1 -> Hup 145 | 2 -> Interrupt 146 | 3 -> Quit 147 | 6 -> Abort 148 | 9 -> Kill 149 | 14 -> Alarm 150 | 15 -> Terminate 151 | m -> Fmt.invalid_arg "Signal %i not supported yet." m 152 153 let to_int = function 154 | Interrupt -> Sys.sigint 155 | Quit -> Sys.sigquit 156 | Abort -> Sys.sigabrt 157 | Kill -> Sys.sigkill 158 | Alarm -> Sys.sigalrm 159 | Terminate | Exit -> Sys.sigterm 160 | Stop -> Sys.sigstop 161 | Hup -> Sys.sighup 162 163 let of_string s = 164 match String.uppercase_ascii s with 165 | "SIGINT" | "INT" -> Interrupt 166 | "SIGQUIT" | "QUIT" -> Quit 167 | "SIGABRT" | "ABRT" -> Abort 168 | "SIGKILL" | "KILL" -> Kill 169 | "SIGALRM" | "ALRM" -> Alarm 170 | "SIGTERM" | "TERM" -> Terminate 171 | "SIGSTOP" | "STOP" -> Stop 172 | "SIGHUP" | "HUP" -> Hup 173 | m -> Fmt.invalid_arg "Signal %s not supported or recognised." m 174 175 let raise v = Unix.kill (Unix.getpid ()) (to_int v) 176end