| 251 | } |
| 252 | |
| 253 | pub fn start_thread(&mut self, exit_evt: EventFd) -> Result<()> { |
| 254 | // Don't allow this to be run if the handle exists |
| 255 | if self.handle.is_some() { |
| 256 | warn!("Tried to start multiple SerialManager threads, ignoring"); |
| 257 | return Ok(()); |
| 258 | } |
| 259 | |
| 260 | let epoll_fd = self.epoll_fd.try_clone().map_err(Error::Epoll)?; |
| 261 | let transport = self.transport.clone(); |
| 262 | let serial = self.serial.clone(); |
| 263 | let pty_write_out = self.pty_write_out.clone(); |
| 264 | let mut reader: Option<UnixStream> = None; |
| 265 | |
| 266 | // In case of PTY, we want to be able to detect a connection on the |
| 267 | // other end of the PTY. This is done by detecting there's no event |
| 268 | // triggered on the epoll, which is the reason why we want the |
| 269 | // epoll_wait() function to return after the timeout expired. |
| 270 | // In case of TTY, we don't expect to detect such behavior, which is |
| 271 | // why we can afford to block until an actual event is triggered. |
| 272 | let timeout = if pty_write_out.is_some() { 500 } else { -1 }; |
| 273 | |
| 274 | let thread = thread::Builder::new() |
| 275 | .name("serial-manager".to_string()) |
| 276 | .spawn(move || { |
| 277 | std::panic::catch_unwind(AssertUnwindSafe(move || { |
| 278 | let mut events = |
| 279 | [epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN]; |
| 280 | |
| 281 | loop { |
| 282 | let num_events = |
| 283 | match epoll::wait(epoll_fd.as_raw_fd(), timeout, &mut events[..]) { |
| 284 | Ok(res) => res, |
| 285 | Err(e) => { |
| 286 | if e.kind() == io::ErrorKind::Interrupted { |
| 287 | // It's well defined from the epoll_wait() syscall |
| 288 | // documentation that the epoll loop can be interrupted |
| 289 | // before any of the requested events occurred or the |
| 290 | // timeout expired. In both those cases, epoll_wait() |
| 291 | // returns an error of type EINTR, but this should not |
| 292 | // be considered as a regular error. Instead it is more |
| 293 | // appropriate to retry, by calling into epoll_wait(). |
| 294 | continue; |
| 295 | } |
| 296 | return Err(Error::Epoll(e)); |
| 297 | } |
| 298 | }; |
| 299 | |
| 300 | if matches!(transport, ConsoleTransport::Pty(_)) && num_events == 0 { |
| 301 | // This very specific case happens when the serial is connected |
| 302 | // to a PTY. We know EPOLLHUP is always present when there's nothing |
| 303 | // connected at the other end of the PTY. That's why getting no event |
| 304 | // means we can flush the output of the serial through the PTY. |
| 305 | Self::trigger_pty_flush(&serial, pty_write_out.as_ref())?; |
| 306 | continue; |
| 307 | } |
| 308 | |
| 309 | for event in events.iter().take(num_events) { |
| 310 | let dispatch_event: EpollDispatch = event.data.into(); |