(* * Copyright (c) 2015-present Thomas Gazagnaire * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *) open Lwt.Infix let src = Logs.Src.create "mirage-flow-unix" module Log = (val Logs.src_log src : Logs.LOG) module Make (F: Mirage_flow.S) = struct let reader t = let frag = ref (Cstruct.create 0) in let rec aux buf ofs len = if len = 0 then Lwt.return 0 else let available = Cstruct.length !frag in if available = 0 then begin F.read t >>= function | Ok (`Data b) -> frag := b; aux buf ofs len | Ok `Eof -> Lwt.return 0 | Error e -> Lwt.fail_with @@ Fmt.str "Lwt_io_flow.reader: %a" F.pp_error e end else begin let n = min available len in Cstruct.blit !frag 0 (Cstruct.of_bigarray buf) ofs n; frag := Cstruct.shift !frag n; Lwt.return n end in aux let writer t buf ofs len = let b = Cstruct.sub (Cstruct.of_bigarray buf) ofs len in F.write t b >>= function | Ok () -> Lwt.return len | Error `Closed -> Lwt.return 0 | Error e -> Lwt.fail_with @@ Fmt.str "Lwt_io_flow.writer: %a" F.pp_write_error e let ic ?(buffer_size=1024) ?(close=true) t = let close () = if close then F.close t else Lwt.return_unit in let buffer = Lwt_bytes.create buffer_size in Lwt_io.make ~buffer ~mode:Lwt_io.input ~close (reader t) let oc ?(buffer_size=1024) ?(close=false) t = let close () = if close then F.close t else Lwt.return_unit in let buffer = Lwt_bytes.create buffer_size in Lwt_io.make ~buffer ~mode:Lwt_io.output ~close (writer t) end module Fd = struct type error = [`Msg of string] type write_error = [ Mirage_flow.write_error | error ] let pp_error ppf (`Msg s) = Fmt.string ppf s let pp_write_error ppf = function | #Mirage_flow.write_error as e -> Mirage_flow.pp_write_error ppf e | #error as e -> pp_error ppf e type flow = Lwt_unix.file_descr let err e = Lwt.return (Error (`Msg (Printexc.to_string e))) let failf fmt = Fmt.kstr Lwt.fail_with fmt let pp_fd ppf (t:Lwt_unix.file_descr) = Fmt.int ppf (Obj.magic (Lwt_unix.unix_file_descr t): int) let rec really_write fd buf off len = match len with | 0 -> Lwt.return_unit | len -> Log.debug (fun l -> l "really_write %a off=%d len=%d" pp_fd fd off len); Lwt_unix.write fd buf off len >>= fun n -> if n = 0 then Lwt.fail_with "write 0" else really_write fd buf (off+n) (len-n) let write_all fd buf = really_write fd buf 0 (Bytes.length buf) let read_all fd = Log.debug (fun l -> l "read_all %a" pp_fd fd); let len = 16 * 1024 in let buf = Bytes.create len in let rec loop acc = Lwt_unix.read fd buf 0 len >>= fun n -> if n = 0 then failf "read %a: 0" pp_fd fd else let acc = Bytes.sub buf 0 n :: acc in if n <= len then Lwt.return (List.rev acc) else loop acc in loop [] >|= fun bufs -> Bytes.concat (Bytes.create 0) bufs let read t = Lwt.catch (fun () -> read_all t >|= fun buf -> Ok (`Data (Cstruct.of_bytes buf)) ) (function Failure _ -> Lwt.return (Ok `Eof) | e -> err e) let write t b = Lwt.catch (fun () -> write_all t (Cstruct.to_bytes b) >|= fun () -> Ok () ) (fun e -> err e) let close t = Lwt_unix.close t let shutdown t mode = let cmd = Lwt_unix.(match mode with | `read -> SHUTDOWN_RECEIVE | `write -> SHUTDOWN_SEND | `read_write -> SHUTDOWN_ALL) in Lwt.return (Lwt_unix.shutdown t cmd) let writev t bs = Lwt.catch (fun () -> Lwt_list.iter_s (fun b -> write_all t (Cstruct.to_bytes b)) bs >|= fun () -> Ok () ) (fun e -> err e) end