xref: /cloud-hypervisor/vmm/src/serial_manager.rs (revision eea9bcea38e0c5649f444c829f3a4f9c22aa486c)
1 // Copyright © 2021 Intel Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use crate::config::ConsoleOutputMode;
7 use crate::device_manager::PtyPair;
8 #[cfg(target_arch = "aarch64")]
9 use devices::legacy::Pl011;
10 #[cfg(target_arch = "x86_64")]
11 use devices::legacy::Serial;
12 use libc::EFD_NONBLOCK;
13 use serial_buffer::SerialBuffer;
14 use std::fs::File;
15 use std::io::Read;
16 use std::os::unix::io::{AsRawFd, FromRawFd};
17 use std::panic::AssertUnwindSafe;
18 use std::sync::atomic::{AtomicBool, Ordering};
19 use std::sync::{Arc, Mutex};
20 use std::{io, result, thread};
21 use thiserror::Error;
22 use vmm_sys_util::eventfd::EventFd;
23 
24 #[derive(Debug, Error)]
25 pub enum Error {
26     /// Cannot clone File.
27     #[error("Error cloning File: {0}")]
28     FileClone(#[source] io::Error),
29 
30     /// Cannot create epoll context.
31     #[error("Error creating epoll context: {0}")]
32     Epoll(#[source] io::Error),
33 
34     /// Cannot handle the VM input stream.
35     #[error("Error handling VM input: {0:?}")]
36     ReadInput(#[source] io::Error),
37 
38     /// Cannot queue input to the serial device.
39     #[error("Error queuing input to the serial device: {0}")]
40     QueueInput(#[source] vmm_sys_util::errno::Error),
41 
42     /// Cannot flush output on the serial buffer.
43     #[error("Error flushing serial device's output buffer: {0}")]
44     FlushOutput(#[source] io::Error),
45 
46     /// Cannot make the file descriptor non-blocking.
47     #[error("Error making input file descriptor non-blocking: {0}")]
48     SetNonBlocking(#[source] io::Error),
49 
50     /// Cannot create EventFd.
51     #[error("Error creating EventFd: {0}")]
52     EventFd(#[source] io::Error),
53 
54     /// Cannot spawn SerialManager thread.
55     #[error("Error spawning SerialManager thread: {0}")]
56     SpawnSerialManager(#[source] io::Error),
57 }
58 pub type Result<T> = result::Result<T, Error>;
59 
60 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
61 #[repr(u64)]
62 pub enum EpollDispatch {
63     File = 0,
64     Kill = 1,
65     Unknown,
66 }
67 
68 impl From<u64> for EpollDispatch {
69     fn from(v: u64) -> Self {
70         use EpollDispatch::*;
71         match v {
72             0 => File,
73             1 => Kill,
74             _ => Unknown,
75         }
76     }
77 }
78 
79 pub struct SerialManager {
80     #[cfg(target_arch = "x86_64")]
81     serial: Arc<Mutex<Serial>>,
82     #[cfg(target_arch = "aarch64")]
83     serial: Arc<Mutex<Pl011>>,
84     epoll_file: File,
85     in_file: File,
86     kill_evt: EventFd,
87     handle: Option<thread::JoinHandle<()>>,
88     pty_write_out: Option<Arc<AtomicBool>>,
89 }
90 
91 impl SerialManager {
92     pub fn new(
93         #[cfg(target_arch = "x86_64")] serial: Arc<Mutex<Serial>>,
94         #[cfg(target_arch = "aarch64")] serial: Arc<Mutex<Pl011>>,
95         pty_pair: Option<Arc<Mutex<PtyPair>>>,
96         mode: ConsoleOutputMode,
97     ) -> Result<Option<Self>> {
98         let in_file = match mode {
99             ConsoleOutputMode::Pty => {
100                 if let Some(pty_pair) = pty_pair {
101                     pty_pair
102                         .lock()
103                         .unwrap()
104                         .main
105                         .try_clone()
106                         .map_err(Error::FileClone)?
107                 } else {
108                     return Ok(None);
109                 }
110             }
111             ConsoleOutputMode::Tty => {
112                 // If running on an interactive TTY then accept input
113                 if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } {
114                     let stdin_clone = unsafe { File::from_raw_fd(libc::dup(libc::STDIN_FILENO)) };
115                     let ret = unsafe {
116                         let mut flags = libc::fcntl(stdin_clone.as_raw_fd(), libc::F_GETFL);
117                         flags |= libc::O_NONBLOCK;
118                         libc::fcntl(stdin_clone.as_raw_fd(), libc::F_SETFL, flags)
119                     };
120 
121                     if ret < 0 {
122                         return Err(Error::SetNonBlocking(std::io::Error::last_os_error()));
123                     }
124 
125                     stdin_clone
126                 } else {
127                     return Ok(None);
128                 }
129             }
130             _ => return Ok(None),
131         };
132 
133         let epoll_fd = epoll::create(true).map_err(Error::Epoll)?;
134         let kill_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?;
135 
136         epoll::ctl(
137             epoll_fd,
138             epoll::ControlOptions::EPOLL_CTL_ADD,
139             kill_evt.as_raw_fd(),
140             epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::Kill as u64),
141         )
142         .map_err(Error::Epoll)?;
143 
144         epoll::ctl(
145             epoll_fd,
146             epoll::ControlOptions::EPOLL_CTL_ADD,
147             in_file.as_raw_fd(),
148             epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::File as u64),
149         )
150         .map_err(Error::Epoll)?;
151 
152         let mut pty_write_out = None;
153         if mode == ConsoleOutputMode::Pty {
154             let write_out = Arc::new(AtomicBool::new(false));
155             pty_write_out = Some(write_out.clone());
156             let writer = in_file.try_clone().map_err(Error::FileClone)?;
157             let buffer = SerialBuffer::new(Box::new(writer), write_out);
158             serial.as_ref().lock().unwrap().set_out(Box::new(buffer));
159         }
160 
161         // Use 'File' to enforce closing on 'epoll_fd'
162         let epoll_file = unsafe { File::from_raw_fd(epoll_fd) };
163 
164         Ok(Some(SerialManager {
165             serial,
166             epoll_file,
167             in_file,
168             kill_evt,
169             handle: None,
170             pty_write_out,
171         }))
172     }
173 
174     // This function should be called when the other end of the PTY is
175     // connected. It verifies if this is the first time it's been invoked
176     // after the connection happened, and if that's the case it flushes
177     // all output from the serial to the PTY. Otherwise, it's a no-op.
178     fn trigger_pty_flush(
179         #[cfg(target_arch = "x86_64")] serial: &Arc<Mutex<Serial>>,
180         #[cfg(target_arch = "aarch64")] serial: &Arc<Mutex<Pl011>>,
181         pty_write_out: Option<&Arc<AtomicBool>>,
182     ) -> Result<()> {
183         if let Some(pty_write_out) = &pty_write_out {
184             if pty_write_out.load(Ordering::Acquire) {
185                 return Ok(());
186             }
187 
188             pty_write_out.store(true, Ordering::Release);
189 
190             serial
191                 .lock()
192                 .unwrap()
193                 .flush_output()
194                 .map_err(Error::FlushOutput)?;
195         }
196 
197         Ok(())
198     }
199 
200     pub fn start_thread(&mut self, exit_evt: EventFd) -> Result<()> {
201         // Don't allow this to be run if the handle exists
202         if self.handle.is_some() {
203             warn!("Tried to start multiple SerialManager threads, ignoring");
204             return Ok(());
205         }
206 
207         let epoll_fd = self.epoll_file.as_raw_fd();
208         let mut in_file = self.in_file.try_clone().map_err(Error::FileClone)?;
209         let serial = self.serial.clone();
210         let pty_write_out = self.pty_write_out.clone();
211 
212         // In case of PTY, we want to be able to detect a connection on the
213         // other end of the PTY. This is done by detecting there's no event
214         // triggered on the epoll, which is the reason why we want the
215         // epoll_wait() function to return after the timeout expired.
216         // In case of TTY, we don't expect to detect such behavior, which is
217         // why we can afford to block until an actual event is triggered.
218         let timeout = if pty_write_out.is_some() { 500 } else { -1 };
219 
220         let thread = thread::Builder::new()
221             .name("serial-manager".to_string())
222             .spawn(move || {
223                 std::panic::catch_unwind(AssertUnwindSafe(move || {
224                     // 3 for File, Kill, and Unknown
225                     const EPOLL_EVENTS_LEN: usize = 3;
226 
227                     let mut events =
228                         vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
229 
230                     loop {
231                         let num_events = match epoll::wait(epoll_fd, timeout, &mut events[..]) {
232                             Ok(res) => res,
233                             Err(e) => {
234                                 if e.kind() == io::ErrorKind::Interrupted {
235                                     // It's well defined from the epoll_wait() syscall
236                                     // documentation that the epoll loop can be interrupted
237                                     // before any of the requested events occurred or the
238                                     // timeout expired. In both those cases, epoll_wait()
239                                     // returns an error of type EINTR, but this should not
240                                     // be considered as a regular error. Instead it is more
241                                     // appropriate to retry, by calling into epoll_wait().
242                                     continue;
243                                 } else {
244                                     return Err(Error::Epoll(e));
245                                 }
246                             }
247                         };
248 
249                         if num_events == 0 {
250                             // This very specific case happens when the serial is connected
251                             // to a PTY. We know EPOLLHUP is always present when there's nothing
252                             // connected at the other end of the PTY. That's why getting no event
253                             // means we can flush the output of the serial through the PTY.
254                             Self::trigger_pty_flush(&serial, pty_write_out.as_ref())?;
255                             continue;
256                         }
257 
258                         for event in events.iter().take(num_events) {
259                             let dispatch_event: EpollDispatch = event.data.into();
260                             match dispatch_event {
261                                 EpollDispatch::Unknown => {
262                                     let event = event.data;
263                                     warn!("Unknown serial manager loop event: {}", event);
264                                 }
265                                 EpollDispatch::File => {
266                                     if event.events & libc::EPOLLIN as u32 != 0 {
267                                         let mut input = [0u8; 64];
268                                         let count =
269                                             in_file.read(&mut input).map_err(Error::ReadInput)?;
270 
271                                         // Replace "\n" with "\r" to deal with Windows SAC (#1170)
272                                         if count == 1 && input[0] == 0x0a {
273                                             input[0] = 0x0d;
274                                         }
275 
276                                         serial
277                                             .as_ref()
278                                             .lock()
279                                             .unwrap()
280                                             .queue_input_bytes(&input[..count])
281                                             .map_err(Error::QueueInput)?;
282                                     }
283                                     if event.events & libc::EPOLLHUP as u32 != 0 {
284                                         if let Some(pty_write_out) = &pty_write_out {
285                                             pty_write_out.store(false, Ordering::Release);
286                                         }
287                                         // It's really important to sleep here as this will prevent
288                                         // the current thread from consuming 100% of the CPU cycles
289                                         // when waiting for someone to connect to the PTY.
290                                         std::thread::sleep(std::time::Duration::from_millis(500));
291                                     } else {
292                                         // If the EPOLLHUP flag is not up on the associated event, we
293                                         // can assume the other end of the PTY is connected and therefore
294                                         // we can flush the output of the serial to it.
295                                         Self::trigger_pty_flush(&serial, pty_write_out.as_ref())?;
296                                     }
297                                 }
298                                 EpollDispatch::Kill => {
299                                     info!("KILL event received, stopping epoll loop");
300                                     return Ok(());
301                                 }
302                             }
303                         }
304                     }
305                 }))
306                 .map_err(|_| {
307                     error!("serial-manager thread panicked");
308                     exit_evt.write(1).ok()
309                 })
310                 .ok();
311             })
312             .map_err(Error::SpawnSerialManager)?;
313         self.handle = Some(thread);
314         Ok(())
315     }
316 }
317 
318 impl Drop for SerialManager {
319     fn drop(&mut self) {
320         self.kill_evt.write(1).ok();
321         if let Some(handle) = self.handle.take() {
322             handle.join().ok();
323         }
324     }
325 }
326