1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Safe, ergonomic Unix socket ancillary data (SCM_RIGHTS fd passing).
//!
//! This crate provides a safe Rust API for sending and receiving file
//! descriptors over Unix domain sockets via `SCM_RIGHTS`.
//!
//! # Design
//!
//! - **No `RawFd` in the public API** — `OwnedFd` and `BorrowedFd` only.
//! - **Automatic cleanup** — received fds are `OwnedFd`, closed on drop.
//! - **No fd leaks on truncation** — the high-level API sizes the receive
//! cmsg buffer past every Unix kernel's per-message fd cap, so the kernel
//! cannot deliver more fds than we can wrap. Surplus fds beyond the
//! caller's `N` are wrapped in `OwnedFd` and closed automatically.
//! - **CLOEXEC errors are surfaced** — on platforms without
//! `MSG_CMSG_CLOEXEC` (notably macOS) we set `FD_CLOEXEC` post-recv; if
//! that fails, every received fd is closed and the error is returned.
//!
//! # Truncation safety
//!
//! Linux caps a single `SCM_RIGHTS` message at `SCM_MAX_FD = 253` fds; other
//! Unix kernels enforce comparable caps. The high-level
//! [`UnixStreamExt::recv_fds`] / [`UnixDatagramExt::recv_fds`] always allocate
//! a cmsg buffer sized for that cap, regardless of the caller's `N`. Result:
//!
//! - The peer's kernel rejects oversized sends before they hit the wire.
//! - Every fd the kernel delivers becomes an `OwnedFd` we control.
//! - If the kernel still reports `MSG_CTRUNC` (defensive path; should never
//! fire), every fd we extracted is closed and an error is returned.
//!
//! Low-level callers using [`SocketAncillary`] manage their own buffer and
//! must size it appropriately — the [`is_truncated`](SocketAncillary::is_truncated)
//! flag is exposed for that path.
//!
//! # CLOEXEC race on macOS
//!
//! macOS lacks `MSG_CMSG_CLOEXEC` on `recvmsg`. This crate sets `FD_CLOEXEC`
//! via `fcntl` immediately after the syscall returns, but a concurrent
//! `fork`+`exec` between the two can leak the fd into the child. If your
//! workload forks concurrently with fd-receiving threads, hold a fork lock
//! around the receive.
//!
//! # Quick start
//!
//! ```no_run
//! use std::os::unix::net::UnixStream;
//! use unix_ancillary::UnixStreamExt;
//!
//! let (tx, rx) = UnixStream::pair().unwrap();
//!
//! let file = std::fs::File::open("/dev/null").unwrap();
//! tx.send_fds(b"hello", &[&file]).unwrap();
//!
//! let recv = rx.recv_fds::<1>().unwrap();
//! assert_eq!(&recv.data[..], b"hello");
//! assert_eq!(recv.fds.len(), 1);
//! ```
compile_error!;
pub use ;
pub use ;
use io;
use BorrowedFd;
/// Result returned by [`cmsg_recvmsg`].
/// Send data with ancillary control messages over a Unix socket.
///
/// Low-level API. Prefer [`UnixStreamExt::send_fds`] for convenience.
/// Receive data with ancillary control messages from a Unix socket.
///
/// Low-level API. Prefer [`UnixStreamExt::recv_fds`] for convenience.
///
/// On non-`MSG_CMSG_CLOEXEC` platforms, all received fds have `FD_CLOEXEC`
/// set before this function returns. If that fails for any fd, every
/// received fd is closed and the error is propagated.