xref: /cloud-hypervisor/vmm/src/serial_manager.rs (revision b440cb7d2330770cd415b63544a371d4caa2db3a)
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 use crate::serial_buffer::SerialBuffer;
9 #[cfg(target_arch = "aarch64")]
10 use devices::legacy::Pl011;
11 #[cfg(target_arch = "x86_64")]
12 use devices::legacy::Serial;
13 use libc::EFD_NONBLOCK;
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::{Arc, Mutex};
19 use std::{io, result, thread};
20 use thiserror::Error;
21 use vmm_sys_util::eventfd::EventFd;
22 
23 #[derive(Debug, Error)]
24 pub enum Error {
25     /// Cannot clone File.
26     #[error("Error cloning File: {0}")]
27     FileClone(#[source] io::Error),
28 
29     /// Cannot create epoll context.
30     #[error("Error creating epoll context: {0}")]
31     Epoll(#[source] io::Error),
32 
33     /// Cannot handle the VM input stream.
34     #[error("Error handling VM input: {0:?}")]
35     ReadInput(#[source] io::Error),
36 
37     /// Cannot queue input to the serial device.
38     #[error("Error queuing input to the serial device: {0}")]
39     QueueInput(#[source] vmm_sys_util::errno::Error),
40 
41     /// Cannot flush output on the serial buffer.
42     #[error("Error flushing serial device's output buffer: {0}")]
43     FlushOutput(#[source] io::Error),
44 
45     /// Cannot make the file descriptor non-blocking.
46     #[error("Error making input file descriptor non-blocking: {0}")]
47     SetNonBlocking(#[source] io::Error),
48 
49     /// Cannot create EventFd.
50     #[error("Error creating EventFd: {0}")]
51     EventFd(#[source] io::Error),
52 
53     /// Cannot spawn SerialManager thread.
54     #[error("Error spawning SerialManager thread: {0}")]
55     SpawnSerialManager(#[source] io::Error),
56 }
57 pub type Result<T> = result::Result<T, Error>;
58 
59 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
60 #[repr(u64)]
61 pub enum EpollDispatch {
62     File = 0,
63     Kill = 1,
64     Unknown,
65 }
66 
67 impl From<u64> for EpollDispatch {
68     fn from(v: u64) -> Self {
69         use EpollDispatch::*;
70         match v {
71             0 => File,
72             1 => Kill,
73             _ => Unknown,
74         }
75     }
76 }
77 
78 pub struct SerialManager {
79     #[cfg(target_arch = "x86_64")]
80     serial: Arc<Mutex<Serial>>,
81     #[cfg(target_arch = "aarch64")]
82     serial: Arc<Mutex<Pl011>>,
83     epoll_file: File,
84     in_file: File,
85     kill_evt: EventFd,
86     handle: Option<thread::JoinHandle<()>>,
87 }
88 
89 impl SerialManager {
90     pub fn new(
91         #[cfg(target_arch = "x86_64")] serial: Arc<Mutex<Serial>>,
92         #[cfg(target_arch = "aarch64")] serial: Arc<Mutex<Pl011>>,
93         pty_pair: Option<Arc<Mutex<PtyPair>>>,
94         mode: ConsoleOutputMode,
95     ) -> Result<Option<Self>> {
96         let in_file = match mode {
97             ConsoleOutputMode::Pty => {
98                 if let Some(pty_pair) = pty_pair {
99                     pty_pair
100                         .lock()
101                         .unwrap()
102                         .main
103                         .try_clone()
104                         .map_err(Error::FileClone)?
105                 } else {
106                     return Ok(None);
107                 }
108             }
109             ConsoleOutputMode::Tty => {
110                 // If running on an interactive TTY then accept input
111                 if unsafe { libc::isatty(libc::STDIN_FILENO) == 1 } {
112                     let stdin_clone = unsafe { File::from_raw_fd(libc::dup(libc::STDIN_FILENO)) };
113                     let ret = unsafe {
114                         let mut flags = libc::fcntl(stdin_clone.as_raw_fd(), libc::F_GETFL);
115                         flags |= libc::O_NONBLOCK;
116                         libc::fcntl(stdin_clone.as_raw_fd(), libc::F_SETFL, flags)
117                     };
118 
119                     if ret < 0 {
120                         return Err(Error::SetNonBlocking(std::io::Error::last_os_error()));
121                     }
122 
123                     stdin_clone
124                 } else {
125                     return Ok(None);
126                 }
127             }
128             _ => return Ok(None),
129         };
130 
131         let epoll_fd = epoll::create(true).map_err(Error::Epoll)?;
132         let kill_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFd)?;
133 
134         epoll::ctl(
135             epoll_fd,
136             epoll::ControlOptions::EPOLL_CTL_ADD,
137             kill_evt.as_raw_fd(),
138             epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::Kill as u64),
139         )
140         .map_err(Error::Epoll)?;
141 
142         epoll::ctl(
143             epoll_fd,
144             epoll::ControlOptions::EPOLL_CTL_ADD,
145             in_file.as_raw_fd(),
146             epoll::Event::new(epoll::Events::EPOLLIN, EpollDispatch::File as u64),
147         )
148         .map_err(Error::Epoll)?;
149 
150         if mode == ConsoleOutputMode::Pty {
151             let writer = in_file.try_clone().map_err(Error::FileClone)?;
152             let mut buffer = SerialBuffer::new(Box::new(writer));
153             buffer.add_out_fd(in_file.as_raw_fd());
154             buffer.add_epoll_fd(epoll_fd);
155             serial.as_ref().lock().unwrap().set_out(Box::new(buffer));
156         }
157 
158         // Use 'File' to enforce closing on 'epoll_fd'
159         let epoll_file = unsafe { File::from_raw_fd(epoll_fd) };
160 
161         Ok(Some(SerialManager {
162             serial,
163             epoll_file,
164             in_file,
165             kill_evt,
166             handle: None,
167         }))
168     }
169 
170     pub fn start_thread(&mut self, exit_evt: EventFd) -> Result<()> {
171         // Don't allow this to be run if the handle exists
172         if self.handle.is_some() {
173             warn!("Tried to start multiple SerialManager threads, ignoring");
174             return Ok(());
175         }
176 
177         let epoll_fd = self.epoll_file.as_raw_fd();
178         let mut in_file = self.in_file.try_clone().map_err(Error::FileClone)?;
179         let serial = self.serial.clone();
180 
181         let thread = thread::Builder::new()
182             .name("serial-manager".to_string())
183             .spawn(move || {
184                 std::panic::catch_unwind(AssertUnwindSafe(move || {
185                     // 3 for File, Kill, and Unknown
186                     const EPOLL_EVENTS_LEN: usize = 3;
187 
188                     let mut events =
189                         vec![epoll::Event::new(epoll::Events::empty(), 0); EPOLL_EVENTS_LEN];
190 
191                     loop {
192                         let num_events = match epoll::wait(epoll_fd, -1, &mut events[..]) {
193                             Ok(res) => res,
194                             Err(e) => {
195                                 if e.kind() == io::ErrorKind::Interrupted {
196                                     // It's well defined from the epoll_wait() syscall
197                                     // documentation that the epoll loop can be interrupted
198                                     // before any of the requested events occurred or the
199                                     // timeout expired. In both those cases, epoll_wait()
200                                     // returns an error of type EINTR, but this should not
201                                     // be considered as a regular error. Instead it is more
202                                     // appropriate to retry, by calling into epoll_wait().
203                                     continue;
204                                 }
205                                 return Err(Error::Epoll(e));
206                             }
207                         };
208 
209                         for event in events.iter().take(num_events) {
210                             let dispatch_event: EpollDispatch = event.data.into();
211                             match dispatch_event {
212                                 EpollDispatch::Unknown => {
213                                     let event = event.data;
214                                     warn!("Unknown serial manager loop event: {}", event);
215                                 }
216                                 EpollDispatch::File => {
217                                     if event.events & libc::EPOLLOUT as u32 != 0 {
218                                         serial
219                                             .as_ref()
220                                             .lock()
221                                             .unwrap()
222                                             .flush_output()
223                                             .map_err(Error::FlushOutput)?;
224                                     }
225                                     if event.events & libc::EPOLLIN as u32 != 0 {
226                                         let mut input = [0u8; 64];
227                                         let count =
228                                             in_file.read(&mut input).map_err(Error::ReadInput)?;
229 
230                                         // Replace "\n" with "\r" to deal with Windows SAC (#1170)
231                                         if count == 1 && input[0] == 0x0a {
232                                             input[0] = 0x0d;
233                                         }
234 
235                                         serial
236                                             .as_ref()
237                                             .lock()
238                                             .unwrap()
239                                             .queue_input_bytes(&input[..count])
240                                             .map_err(Error::QueueInput)?;
241                                     }
242                                 }
243                                 EpollDispatch::Kill => {
244                                     info!("KILL event received, stopping epoll loop");
245                                     return Ok(());
246                                 }
247                             }
248                         }
249                     }
250                 }))
251                 .map_err(|_| {
252                     error!("serial-manager thread panicked");
253                     exit_evt.write(1).ok()
254                 })
255                 .ok();
256             })
257             .map_err(Error::SpawnSerialManager)?;
258         self.handle = Some(thread);
259         Ok(())
260     }
261 }
262 
263 impl Drop for SerialManager {
264     fn drop(&mut self) {
265         self.kill_evt.write(1).ok();
266         if let Some(handle) = self.handle.take() {
267             handle.join().ok();
268         }
269     }
270 }
271