1 | use std::net::Shutdown; |
2 | use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}; |
3 | use std::os::unix::net::{self, SocketAddr}; |
4 | use std::path::Path; |
5 | use std::{fmt, io}; |
6 | |
7 | use crate::io_source::IoSource; |
8 | use crate::{event, sys, Interest, Registry, Token}; |
9 | |
10 | /// A Unix datagram socket. |
11 | pub struct UnixDatagram { |
12 | inner: IoSource<net::UnixDatagram>, |
13 | } |
14 | |
15 | impl UnixDatagram { |
16 | /// Creates a Unix datagram socket bound to the given path. |
17 | pub fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { |
18 | let addr = SocketAddr::from_pathname(path)?; |
19 | UnixDatagram::bind_addr(&addr) |
20 | } |
21 | |
22 | /// Creates a new `UnixDatagram` bound to the specified socket `address`. |
23 | pub fn bind_addr(address: &SocketAddr) -> io::Result<UnixDatagram> { |
24 | sys::uds::datagram::bind_addr(address).map(UnixDatagram::from_std) |
25 | } |
26 | |
27 | /// Creates a new `UnixDatagram` from a standard `net::UnixDatagram`. |
28 | /// |
29 | /// This function is intended to be used to wrap a Unix datagram from the |
30 | /// standard library in the Mio equivalent. The conversion assumes nothing |
31 | /// about the underlying datagram; it is left up to the user to set it in |
32 | /// non-blocking mode. |
33 | pub fn from_std(socket: net::UnixDatagram) -> UnixDatagram { |
34 | UnixDatagram { |
35 | inner: IoSource::new(socket), |
36 | } |
37 | } |
38 | |
39 | /// Connects the socket to the specified address. |
40 | /// |
41 | /// This may return a `WouldBlock` in which case the socket connection |
42 | /// cannot be completed immediately. |
43 | pub fn connect<P: AsRef<Path>>(&self, path: P) -> io::Result<()> { |
44 | self.inner.connect(path) |
45 | } |
46 | |
47 | /// Creates a Unix Datagram socket which is not bound to any address. |
48 | pub fn unbound() -> io::Result<UnixDatagram> { |
49 | sys::uds::datagram::unbound().map(UnixDatagram::from_std) |
50 | } |
51 | |
52 | /// Create an unnamed pair of connected sockets. |
53 | pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { |
54 | sys::uds::datagram::pair().map(|(socket1, socket2)| { |
55 | ( |
56 | UnixDatagram::from_std(socket1), |
57 | UnixDatagram::from_std(socket2), |
58 | ) |
59 | }) |
60 | } |
61 | |
62 | /// Returns the address of this socket. |
63 | pub fn local_addr(&self) -> io::Result<SocketAddr> { |
64 | self.inner.local_addr() |
65 | } |
66 | |
67 | /// Returns the address of this socket's peer. |
68 | /// |
69 | /// The `connect` method will connect the socket to a peer. |
70 | pub fn peer_addr(&self) -> io::Result<SocketAddr> { |
71 | self.inner.peer_addr() |
72 | } |
73 | |
74 | /// Receives data from the socket. |
75 | /// |
76 | /// On success, returns the number of bytes read and the address from |
77 | /// whence the data came. |
78 | pub fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { |
79 | self.inner.do_io(|inner| inner.recv_from(buf)) |
80 | } |
81 | |
82 | /// Receives data from the socket. |
83 | /// |
84 | /// On success, returns the number of bytes read. |
85 | pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { |
86 | self.inner.do_io(|inner| inner.recv(buf)) |
87 | } |
88 | |
89 | /// Sends data on the socket to the specified address. |
90 | /// |
91 | /// On success, returns the number of bytes written. |
92 | pub fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { |
93 | self.inner.do_io(|inner| inner.send_to(buf, path)) |
94 | } |
95 | |
96 | /// Sends data on the socket to the socket's peer. |
97 | /// |
98 | /// The peer address may be set by the `connect` method, and this method |
99 | /// will return an error if the socket has not already been connected. |
100 | /// |
101 | /// On success, returns the number of bytes written. |
102 | pub fn send(&self, buf: &[u8]) -> io::Result<usize> { |
103 | self.inner.do_io(|inner| inner.send(buf)) |
104 | } |
105 | |
106 | /// Returns the value of the `SO_ERROR` option. |
107 | pub fn take_error(&self) -> io::Result<Option<io::Error>> { |
108 | self.inner.take_error() |
109 | } |
110 | |
111 | /// Shut down the read, write, or both halves of this connection. |
112 | /// |
113 | /// This function will cause all pending and future I/O calls on the |
114 | /// specified portions to immediately return with an appropriate value |
115 | /// (see the documentation of `Shutdown`). |
116 | pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { |
117 | self.inner.shutdown(how) |
118 | } |
119 | |
120 | /// Execute an I/O operation ensuring that the socket receives more events |
121 | /// if it hits a [`WouldBlock`] error. |
122 | /// |
123 | /// # Notes |
124 | /// |
125 | /// This method is required to be called for **all** I/O operations to |
126 | /// ensure the user will receive events once the socket is ready again after |
127 | /// returning a [`WouldBlock`] error. |
128 | /// |
129 | /// [`WouldBlock`]: io::ErrorKind::WouldBlock |
130 | /// |
131 | /// # Examples |
132 | /// |
133 | /// ``` |
134 | /// # use std::error::Error; |
135 | /// # |
136 | /// # fn main() -> Result<(), Box<dyn Error>> { |
137 | /// use std::io; |
138 | /// use std::os::fd::AsRawFd; |
139 | /// use mio::net::UnixDatagram; |
140 | /// |
141 | /// let (dgram1, dgram2) = UnixDatagram::pair()?; |
142 | /// |
143 | /// // Wait until the dgram is writable... |
144 | /// |
145 | /// // Write to the dgram using a direct libc call, of course the |
146 | /// // `io::Write` implementation would be easier to use. |
147 | /// let buf = b"hello" ; |
148 | /// let n = dgram1.try_io(|| { |
149 | /// let buf_ptr = &buf as *const _ as *const _; |
150 | /// let res = unsafe { libc::send(dgram1.as_raw_fd(), buf_ptr, buf.len(), 0) }; |
151 | /// if res != -1 { |
152 | /// Ok(res as usize) |
153 | /// } else { |
154 | /// // If EAGAIN or EWOULDBLOCK is set by libc::send, the closure |
155 | /// // should return `WouldBlock` error. |
156 | /// Err(io::Error::last_os_error()) |
157 | /// } |
158 | /// })?; |
159 | /// eprintln!("write {} bytes" , n); |
160 | /// |
161 | /// // Wait until the dgram is readable... |
162 | /// |
163 | /// // Read from the dgram using a direct libc call, of course the |
164 | /// // `io::Read` implementation would be easier to use. |
165 | /// let mut buf = [0; 512]; |
166 | /// let n = dgram2.try_io(|| { |
167 | /// let buf_ptr = &mut buf as *mut _ as *mut _; |
168 | /// let res = unsafe { libc::recv(dgram2.as_raw_fd(), buf_ptr, buf.len(), 0) }; |
169 | /// if res != -1 { |
170 | /// Ok(res as usize) |
171 | /// } else { |
172 | /// // If EAGAIN or EWOULDBLOCK is set by libc::recv, the closure |
173 | /// // should return `WouldBlock` error. |
174 | /// Err(io::Error::last_os_error()) |
175 | /// } |
176 | /// })?; |
177 | /// eprintln!("read {} bytes" , n); |
178 | /// # Ok(()) |
179 | /// # } |
180 | /// ``` |
181 | pub fn try_io<F, T>(&self, f: F) -> io::Result<T> |
182 | where |
183 | F: FnOnce() -> io::Result<T>, |
184 | { |
185 | self.inner.do_io(|_| f()) |
186 | } |
187 | } |
188 | |
189 | impl event::Source for UnixDatagram { |
190 | fn register( |
191 | &mut self, |
192 | registry: &Registry, |
193 | token: Token, |
194 | interests: Interest, |
195 | ) -> io::Result<()> { |
196 | self.inner.register(registry, token, interests) |
197 | } |
198 | |
199 | fn reregister( |
200 | &mut self, |
201 | registry: &Registry, |
202 | token: Token, |
203 | interests: Interest, |
204 | ) -> io::Result<()> { |
205 | self.inner.reregister(registry, token, interests) |
206 | } |
207 | |
208 | fn deregister(&mut self, registry: &Registry) -> io::Result<()> { |
209 | self.inner.deregister(registry) |
210 | } |
211 | } |
212 | |
213 | impl fmt::Debug for UnixDatagram { |
214 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
215 | self.inner.fmt(f) |
216 | } |
217 | } |
218 | |
219 | impl IntoRawFd for UnixDatagram { |
220 | fn into_raw_fd(self) -> RawFd { |
221 | self.inner.into_inner().into_raw_fd() |
222 | } |
223 | } |
224 | |
225 | impl AsRawFd for UnixDatagram { |
226 | fn as_raw_fd(&self) -> RawFd { |
227 | self.inner.as_raw_fd() |
228 | } |
229 | } |
230 | |
231 | impl FromRawFd for UnixDatagram { |
232 | /// Converts a `RawFd` to a `UnixDatagram`. |
233 | /// |
234 | /// # Notes |
235 | /// |
236 | /// The caller is responsible for ensuring that the socket is in |
237 | /// non-blocking mode. |
238 | unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { |
239 | UnixDatagram::from_std(socket:FromRawFd::from_raw_fd(fd)) |
240 | } |
241 | } |
242 | |
243 | impl From<UnixDatagram> for net::UnixDatagram { |
244 | fn from(datagram: UnixDatagram) -> Self { |
245 | // Safety: This is safe since we are extracting the raw fd from a well-constructed |
246 | // mio::net::uds::UnixListener which ensures that we actually pass in a valid file |
247 | // descriptor/socket |
248 | unsafe { net::UnixDatagram::from_raw_fd(datagram.into_raw_fd()) } |
249 | } |
250 | } |
251 | |
252 | impl From<UnixDatagram> for OwnedFd { |
253 | fn from(unix_datagram: UnixDatagram) -> Self { |
254 | unix_datagram.inner.into_inner().into() |
255 | } |
256 | } |
257 | |
258 | impl AsFd for UnixDatagram { |
259 | fn as_fd(&self) -> BorrowedFd<'_> { |
260 | self.inner.as_fd() |
261 | } |
262 | } |
263 | |
264 | impl From<OwnedFd> for UnixDatagram { |
265 | fn from(fd: OwnedFd) -> Self { |
266 | UnixDatagram::from_std(socket:From::from(fd)) |
267 | } |
268 | } |
269 | |