Skip to content

Commit b755d9e

Browse files
authored
Merge pull request #522 from talex5/fd-passing
Eio_unix: add FD passing
2 parents bb47407 + 90af8f7 commit b755d9e

File tree

22 files changed

+360
-166
lines changed

22 files changed

+360
-166
lines changed

lib_eio/unix/eio_unix.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ module Ctf = Ctf_unix
3030

3131
module Process = Process
3232
module Net = Net
33+
module Pi = Pi
3334

3435
module Stdenv = struct
3536
type base = <

lib_eio/unix/eio_unix.mli

Lines changed: 2 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,37 +28,6 @@ module Resource : sig
2828
(** [fd_opt t] returns the FD being wrapped by a generic resource, if any.
2929
3030
This just probes [t] using {!extension-FD}. *)
31-
32-
module type FLOW = sig
33-
include Eio.Net.Pi.STREAM_SOCKET
34-
include Eio.File.Pi.WRITE with type t := t
35-
36-
val fd : t -> Fd.t
37-
end
38-
39-
val flow_handler :
40-
(module FLOW with type t = 't and type tag = 'tag) ->
41-
('t, [`Unix_fd | 'tag Eio.Net.stream_socket_ty | Eio.File.rw_ty]) Eio.Resource.handler
42-
43-
module type DATAGRAM_SOCKET = sig
44-
include Eio.Net.Pi.DATAGRAM_SOCKET
45-
46-
val fd : t -> Fd.t
47-
end
48-
49-
val datagram_handler :
50-
(module DATAGRAM_SOCKET with type t = 't and type tag = 'tag) ->
51-
('t, [`Unix_fd | 'tag Eio.Net.datagram_socket_ty]) Eio.Resource.handler
52-
53-
module type LISTENING_SOCKET = sig
54-
include Eio.Net.Pi.LISTENING_SOCKET
55-
56-
val fd : t -> Fd.t
57-
end
58-
59-
val listening_socket_handler :
60-
(module LISTENING_SOCKET with type t = 't and type tag = 'tag) ->
61-
('t, [`Unix_fd | 'tag Eio.Net.listening_socket_ty]) Eio.Resource.handler
6231
end
6332

6433
module Net = Net
@@ -129,3 +98,5 @@ module Private : sig
12998
end
13099

131100
module Ctf = Ctf_unix
101+
102+
module Pi = Pi

lib_eio/unix/fd.ml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ let of_unix ~sw ?blocking ?seekable ~close_unix fd =
4646
t.release_hook <- Switch.on_release_cancellable sw (fun () -> close t);
4747
t
4848

49+
let of_unix_list ~sw fds =
50+
match Switch.get_error sw with
51+
| Some e -> List.iter Unix.close fds; raise e
52+
| None -> List.map (of_unix ~sw ~close_unix:true) fds
53+
4954
external eio_is_blocking : Unix.file_descr -> bool = "eio_unix_is_blocking"
5055

5156
let is_blocking t =

lib_eio/unix/fd.mli

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ val of_unix : sw:Switch.t -> ?blocking:bool -> ?seekable:bool -> close_unix:bool
1414
@param seekable The value to be returned by {!is_seekable}. Defaults to probing if needed.
1515
@param close_unix Whether {!close} also closes [fd] (this should normally be [true]). *)
1616

17+
val of_unix_list : sw:Switch.t -> Unix.file_descr list -> t list
18+
(** [of_unix_list ~sw fds] is like [List.map (of_unix ~sw ~close_unix:true) fds],
19+
except that if [sw] is off then it closes all the FDs. *)
20+
1721
(** {2 Using FDs} *)
1822

1923
val use : t -> (Unix.file_descr -> 'a) -> if_closed:(unit -> 'a) -> 'a

lib_eio/unix/net.ml

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
open Eio.Std
22

3-
type stream_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.stream_socket_ty]
4-
type datagram_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.datagram_socket_ty]
5-
type listening_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.listening_socket_ty]
3+
type stream_socket_ty = [`Generic | `Unix] Eio.Net.stream_socket_ty
4+
type datagram_socket_ty = [`Generic | `Unix] Eio.Net.datagram_socket_ty
5+
type listening_socket_ty = [`Generic | `Unix] Eio.Net.listening_socket_ty
66
type 'a stream_socket = ([> stream_socket_ty] as 'a) r
77
type 'a datagram_socket = ([> datagram_socket_ty] as 'a) r
88
type 'a listening_socket = ([> listening_socket_ty] as 'a) r
@@ -30,6 +30,20 @@ let sockaddr_of_unix_datagram = function
3030
let host = Ipaddr.of_unix host in
3131
`Udp (host, port)
3232

33+
let send_msg (Eio.Resource.T (t, ops)) ?(fds=[]) bufs =
34+
let module X = (val (Eio.Resource.get ops Pi.Stream_socket)) in
35+
let rec aux ~fds bufs =
36+
let sent = X.send_msg t ~fds bufs in
37+
match Cstruct.shiftv bufs sent with
38+
| [] -> ()
39+
| bufs -> aux bufs ~fds:[]
40+
in
41+
aux ~fds bufs
42+
43+
let recv_msg_with_fds (Eio.Resource.T (t, ops)) ~sw ~max_fds bufs =
44+
let module X = (val (Eio.Resource.get ops Pi.Stream_socket)) in
45+
X.recv_msg_with_fds t ~sw ~max_fds bufs
46+
3347
let getnameinfo (sockaddr : Eio.Net.Sockaddr.t) =
3448
let options =
3549
match sockaddr with
@@ -46,15 +60,15 @@ type t = [`Generic | `Unix] Eio.Net.ty r
4660
[@@@alert "-unstable"]
4761

4862
type _ Effect.t +=
49-
| Import_socket_stream : Switch.t * bool * Unix.file_descr -> stream_socket_ty r Effect.t
50-
| Import_socket_datagram : Switch.t * bool * Unix.file_descr -> datagram_socket_ty r Effect.t
63+
| Import_socket_stream : Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t
64+
| Import_socket_datagram : Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t
5165
| Socketpair_stream : Switch.t * Unix.socket_domain * int ->
52-
(stream_socket_ty r * stream_socket_ty r) Effect.t
66+
([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t
5367
| Socketpair_datagram : Switch.t * Unix.socket_domain * int ->
54-
(datagram_socket_ty r * datagram_socket_ty r) Effect.t
68+
([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t
5569

56-
let open_stream s = (s : _ stream_socket :> [< stream_socket_ty] r)
57-
let open_datagram s = (s : _ datagram_socket :> [< datagram_socket_ty] r)
70+
let open_stream s = (s : _ stream_socket :> [< `Unix_fd | stream_socket_ty] r)
71+
let open_datagram s = (s : _ datagram_socket :> [< `Unix_fd | datagram_socket_ty] r)
5872

5973
let import_socket_stream ~sw ~close_unix fd =
6074
open_stream @@ Effect.perform (Import_socket_stream (sw, close_unix, fd))
@@ -68,3 +82,6 @@ let socketpair_stream ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
6882

6983
let socketpair_datagram ~sw ?(domain=Unix.PF_UNIX) ?(protocol=0) () =
7084
Effect.perform (Socketpair_datagram (sw, domain, protocol))
85+
86+
let fd socket =
87+
Option.get (Resource.fd_opt socket)

lib_eio/unix/net.mli

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,36 @@ open Eio.Std
44
55
These extend the types in {!Eio.Net} with support for file descriptors. *)
66

7-
type stream_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.stream_socket_ty]
8-
type datagram_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.datagram_socket_ty]
9-
type listening_socket_ty = [`Unix_fd | [`Generic | `Unix] Eio.Net.listening_socket_ty]
7+
type stream_socket_ty = [`Generic | `Unix] Eio.Net.stream_socket_ty
8+
type datagram_socket_ty = [`Generic | `Unix] Eio.Net.datagram_socket_ty
9+
type listening_socket_ty = [`Generic | `Unix] Eio.Net.listening_socket_ty
1010
type 'a stream_socket = ([> stream_socket_ty] as 'a) r
1111
type 'a datagram_socket = ([> datagram_socket_ty] as 'a) r
1212
type 'a listening_socket = ([> listening_socket_ty] as 'a) r
1313

1414
type t = [`Generic | `Unix] Eio.Net.ty r
1515

16+
(** {2 Passing file descriptors} *)
17+
18+
val send_msg :
19+
[> `Platform of [>`Unix] | `Socket | `Stream] r ->
20+
?fds:Fd.t list ->
21+
Cstruct.t list -> unit
22+
(** Like {!Eio.Flow.write}, but allows passing file descriptors (for Unix-domain sockets). *)
23+
24+
val recv_msg_with_fds :
25+
[> `Platform of [>`Unix] | `Socket | `Stream] r ->
26+
sw:Switch.t ->
27+
max_fds:int ->
28+
Cstruct.t list ->
29+
int * Fd.t list
30+
(** Like {!Eio.Flow.single_read}, but also allows receiving file descriptors (for Unix-domain sockets).
31+
32+
@param max_fds The maximum number of file descriptors to accept (additional ones will be closed). *)
33+
34+
val fd : [> `Platform of [> `Unix] | `Socket] r -> Fd.t
35+
(** [fd socket] is the underlying FD of [socket]. *)
36+
1637
(** {2 Unix address conversions}
1738
1839
Note: OCaml's {!Unix.sockaddr} type considers e.g. TCP port 80 and UDP port
@@ -34,15 +55,15 @@ end
3455

3556
(** {2 Creating or importing sockets} *)
3657

37-
val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> stream_socket_ty r
58+
val import_socket_stream : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | stream_socket_ty] r
3859
(** [import_socket_stream ~sw ~close_unix:true fd] is an Eio flow that uses [fd].
3960
4061
It can be cast to e.g. {!source} for a one-way flow.
4162
The socket object will be closed when [sw] finishes.
4263
4364
The [close_unix] and [sw] arguments are passed to {!Fd.of_unix}. *)
4465

45-
val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> datagram_socket_ty r
66+
val import_socket_datagram : sw:Switch.t -> close_unix:bool -> Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r
4667
(** [import_socket_datagram ~sw ~close_unix:true fd] is an Eio datagram socket that uses [fd].
4768
4869
The socket object will be closed when [sw] finishes.
@@ -54,7 +75,7 @@ val socketpair_stream :
5475
?domain:Unix.socket_domain ->
5576
?protocol:int ->
5677
unit ->
57-
stream_socket_ty r * stream_socket_ty r
78+
[`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r
5879
(** [socketpair_stream ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
5980
6081
This creates OS-level resources using [socketpair(2)].
@@ -65,7 +86,7 @@ val socketpair_datagram :
6586
?domain:Unix.socket_domain ->
6687
?protocol:int ->
6788
unit ->
68-
datagram_socket_ty r * datagram_socket_ty r
89+
[`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r
6990
(** [socketpair_datagram ~sw ()] returns a connected pair of flows, such that writes to one can be read by the other.
7091
7192
This creates OS-level resources using [socketpair(2)].
@@ -78,11 +99,11 @@ val getnameinfo : Eio.Net.Sockaddr.t -> (string * string)
7899

79100
type _ Effect.t +=
80101
| Import_socket_stream :
81-
Switch.t * bool * Unix.file_descr -> stream_socket_ty r Effect.t (** See {!import_socket_stream} *)
102+
Switch.t * bool * Unix.file_descr -> [`Unix_fd | stream_socket_ty] r Effect.t (** See {!import_socket_stream} *)
82103
| Import_socket_datagram :
83-
Switch.t * bool * Unix.file_descr -> datagram_socket_ty r Effect.t (** See {!import_socket_datagram} *)
104+
Switch.t * bool * Unix.file_descr -> [`Unix_fd | datagram_socket_ty] r Effect.t (** See {!import_socket_datagram} *)
84105
| Socketpair_stream : Eio.Switch.t * Unix.socket_domain * int ->
85-
(stream_socket_ty r * stream_socket_ty r) Effect.t (** See {!socketpair_stream} *)
106+
([`Unix_fd | stream_socket_ty] r * [`Unix_fd | stream_socket_ty] r) Effect.t (** See {!socketpair_stream} *)
86107
| Socketpair_datagram : Eio.Switch.t * Unix.socket_domain * int ->
87-
(datagram_socket_ty r * datagram_socket_ty r) Effect.t (** See {!socketpair_datagram} *)
108+
([`Unix_fd | datagram_socket_ty] r * [`Unix_fd | datagram_socket_ty] r) Effect.t (** See {!socketpair_datagram} *)
88109
[@@alert "-unstable"]

lib_eio/unix/pi.ml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
open Eio.Std
2+
3+
module type STREAM_SOCKET = sig
4+
include Eio.Net.Pi.STREAM_SOCKET
5+
6+
val send_msg : t -> fds:Fd.t list -> Cstruct.t list -> int
7+
val recv_msg_with_fds : t -> sw:Switch.t -> max_fds:int -> Cstruct.t list -> int * Fd.t list
8+
9+
val fd : t -> Fd.t
10+
end
11+
12+
type (_, _, _) Eio.Resource.pi +=
13+
| Stream_socket : ('t, (module STREAM_SOCKET with type t = 't), [> `Platform of [> `Unix] | `Socket | `Stream]) Eio.Resource.pi
14+
15+
module type FLOW = sig
16+
include Eio.File.Pi.WRITE
17+
include STREAM_SOCKET with type t := t
18+
end
19+
20+
let flow_handler (type t tag) (module X : FLOW with type t = t and type tag = tag) : (t, _) Eio.Resource.handler =
21+
Eio.Resource.handler @@
22+
Eio.Resource.bindings (Eio.Net.Pi.stream_socket (module X)) @
23+
Eio.Resource.bindings (Eio.File.Pi.rw (module X)) @ [
24+
H (Resource.T, X.fd);
25+
H (Stream_socket, (module X));
26+
]
27+
28+
module type DATAGRAM_SOCKET = sig
29+
include Eio.Net.Pi.DATAGRAM_SOCKET
30+
31+
val fd : t -> Fd.t
32+
end
33+
34+
let datagram_handler (type t tag) (module X : DATAGRAM_SOCKET with type t = t and type tag = tag) : (t, _) Eio.Resource.handler =
35+
Eio.Resource.handler @@
36+
Eio.Resource.bindings (Eio.Net.Pi.datagram_socket (module X)) @ [
37+
H (Resource.T, X.fd);
38+
]
39+
40+
module type LISTENING_SOCKET = sig
41+
include Eio.Net.Pi.LISTENING_SOCKET
42+
43+
val fd : t -> Fd.t
44+
end
45+
46+
let listening_socket_handler (type t tag) (module X : LISTENING_SOCKET with type t = t and type tag = tag)
47+
: (t, _) Eio.Resource.handler =
48+
Eio.Resource.handler @@
49+
Eio.Resource.bindings (Eio.Net.Pi.listening_socket (module X)) @ [
50+
H (Resource.T, X.fd);
51+
]

lib_eio/unix/pi.mli

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
open Eio.Std
2+
3+
module type STREAM_SOCKET = sig
4+
include Eio.Net.Pi.STREAM_SOCKET
5+
6+
val send_msg : t -> fds:Fd.t list -> Cstruct.t list -> int
7+
val recv_msg_with_fds : t -> sw:Switch.t -> max_fds:int -> Cstruct.t list -> int * Fd.t list
8+
9+
val fd : t -> Fd.t
10+
end
11+
12+
type (_, _, _) Eio.Resource.pi +=
13+
| Stream_socket : ('t, (module STREAM_SOCKET with type t = 't), [> `Platform of [> `Unix] | `Socket | `Stream]) Eio.Resource.pi
14+
15+
module type FLOW = sig
16+
include Eio.File.Pi.WRITE
17+
include STREAM_SOCKET with type t := t
18+
end
19+
20+
val flow_handler :
21+
(module FLOW with type t = 't and type tag = 'tag) ->
22+
('t, [`Unix_fd | 'tag Eio.Net.stream_socket_ty | Eio.File.rw_ty]) Eio.Resource.handler
23+
24+
module type DATAGRAM_SOCKET = sig
25+
include Eio.Net.Pi.DATAGRAM_SOCKET
26+
27+
val fd : t -> Fd.t
28+
end
29+
30+
val datagram_handler :
31+
(module DATAGRAM_SOCKET with type t = 't and type tag = 'tag) ->
32+
('t, [`Unix_fd | 'tag Eio.Net.datagram_socket_ty]) Eio.Resource.handler
33+
34+
module type LISTENING_SOCKET = sig
35+
include Eio.Net.Pi.LISTENING_SOCKET
36+
37+
val fd : t -> Fd.t
38+
end
39+
40+
val listening_socket_handler :
41+
(module LISTENING_SOCKET with type t = 't and type tag = 'tag) ->
42+
('t, [`Unix_fd | 'tag Eio.Net.listening_socket_ty]) Eio.Resource.handler

lib_eio/unix/resource.ml

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,42 +7,3 @@ let fd_opt (Eio.Resource.T (t, ops)) =
77
match Eio.Resource.get_opt ops T with
88
| Some f -> Some (f t)
99
| None -> None
10-
11-
module type FLOW = sig
12-
include Eio.Net.Pi.STREAM_SOCKET
13-
include Eio.File.Pi.WRITE with type t := t
14-
15-
val fd : t -> Fd.t
16-
end
17-
18-
let flow_handler (type t tag) (module X : FLOW with type t = t and type tag = tag) : (t, _) Eio.Resource.handler =
19-
Eio.Resource.handler @@
20-
Eio.Resource.bindings (Eio.Net.Pi.stream_socket (module X)) @
21-
Eio.Resource.bindings (Eio.File.Pi.rw (module X)) @ [
22-
H (T, X.fd);
23-
]
24-
25-
module type DATAGRAM_SOCKET = sig
26-
include Eio.Net.Pi.DATAGRAM_SOCKET
27-
28-
val fd : t -> Fd.t
29-
end
30-
31-
let datagram_handler (type t tag) (module X : DATAGRAM_SOCKET with type t = t and type tag = tag) : (t, _) Eio.Resource.handler =
32-
Eio.Resource.handler @@
33-
Eio.Resource.bindings (Eio.Net.Pi.datagram_socket (module X)) @ [
34-
H (T, X.fd);
35-
]
36-
37-
module type LISTENING_SOCKET = sig
38-
include Eio.Net.Pi.LISTENING_SOCKET
39-
40-
val fd : t -> Fd.t
41-
end
42-
43-
let listening_socket_handler (type t tag) (module X : LISTENING_SOCKET with type t = t and type tag = tag)
44-
: (t, _) Eio.Resource.handler =
45-
Eio.Resource.handler @@
46-
Eio.Resource.bindings (Eio.Net.Pi.listening_socket (module X)) @ [
47-
H (T, X.fd);
48-
]

0 commit comments

Comments
 (0)