devices/sys/linux/
serial_device.rs1use std::borrow::Cow;
6use std::fs::OpenOptions;
7use std::io;
8use std::io::ErrorKind;
9use std::io::Write;
10use std::os::unix::net::UnixDatagram;
11use std::os::unix::net::UnixStream;
12use std::path::Path;
13use std::path::PathBuf;
14use std::thread;
15use std::time::Duration;
16
17use base::error;
18use base::info;
19use base::read_raw_stdin;
20use base::AsRawDescriptor;
21use base::Event;
22use base::FileSync;
23use base::RawDescriptor;
24use base::ReadNotifier;
25use hypervisor::ProtectionType;
26
27use crate::serial_device::Error;
28use crate::serial_device::SerialInput;
29use crate::serial_device::SerialOptions;
30use crate::serial_device::SerialParameters;
31
32pub const SYSTEM_SERIAL_TYPE_NAME: &str = "UnixSocket";
33
34pub struct ConsoleInput(std::io::Stdin);
37
38impl ConsoleInput {
39 pub fn new() -> Self {
40 Self(std::io::stdin())
41 }
42}
43
44impl io::Read for ConsoleInput {
45 fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
46 read_raw_stdin(out).map_err(|e| e.into())
47 }
48}
49
50impl ReadNotifier for ConsoleInput {
51 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
52 &self.0
53 }
54}
55
56impl SerialInput for ConsoleInput {}
57
58pub trait SerialDevice {
61 fn new(
62 protection_type: ProtectionType,
63 interrupt_evt: Event,
64 input: Option<Box<dyn SerialInput>>,
65 output: Option<Box<dyn io::Write + Send>>,
66 sync: Option<Box<dyn FileSync + Send>>,
67 options: SerialOptions,
68 keep_rds: Vec<RawDescriptor>,
69 ) -> Self;
70}
71
72pub const MAX_SOCKET_PATH_LENGTH: usize = 108;
75
76struct WriteSocket {
77 sock: UnixDatagram,
78 buf: Vec<u8>,
79}
80
81const BUF_CAPACITY: usize = 1024;
82
83impl WriteSocket {
84 pub fn new(s: UnixDatagram) -> WriteSocket {
85 WriteSocket {
86 sock: s,
87 buf: Vec::with_capacity(BUF_CAPACITY),
88 }
89 }
90
91 pub fn send_buf(&self, buf: &[u8]) -> io::Result<usize> {
92 const SEND_RETRY: usize = 2;
93 let mut sent = 0;
94 for _ in 0..SEND_RETRY {
95 match self.sock.send(buf) {
96 Ok(bytes_sent) => {
97 sent = bytes_sent;
98 break;
99 }
100 Err(e) => info!("Send error: {:?}", e),
101 }
102 }
103 Ok(sent)
104 }
105}
106
107impl io::Write for WriteSocket {
108 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
109 let last_newline_idx = match buf.iter().rposition(|&x| x == b'\n') {
110 Some(newline_idx) => Some(self.buf.len() + newline_idx),
111 None => None,
112 };
113 self.buf.extend_from_slice(buf);
114
115 match last_newline_idx {
116 Some(last_newline_idx) => {
117 for line in (self.buf[..last_newline_idx]).split(|&x| x == b'\n') {
118 let send_line = match line.split_last() {
120 Some((b'\r', trimmed)) => trimmed,
121 _ => line,
122 };
123 if self.send_buf(send_line).is_err() {
124 break;
125 }
126 }
127 self.buf.drain(..=last_newline_idx);
128 }
129 None => {
130 if self.buf.len() >= BUF_CAPACITY {
131 if let Err(e) = self.send_buf(&self.buf) {
132 info!("Couldn't send full buffer. {:?}", e);
133 }
134 self.buf.clear();
135 }
136 }
137 }
138 Ok(buf.len())
139 }
140
141 fn flush(&mut self) -> io::Result<()> {
142 Ok(())
143 }
144}
145
146pub(crate) fn create_system_type_serial_device<T: SerialDevice>(
147 param: &SerialParameters,
148 protection_type: ProtectionType,
149 evt: Event,
150 input: Option<Box<dyn SerialInput>>,
151 keep_rds: &mut Vec<RawDescriptor>,
152) -> std::result::Result<T, Error> {
153 match ¶m.path {
154 Some(path) => {
155 let mut path_cow = Cow::<Path>::Borrowed(path);
162 let mut _dir_fd = None;
163 if path.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
164 let mut short_path = PathBuf::with_capacity(MAX_SOCKET_PATH_LENGTH);
165 short_path.push("/proc/self/fd/");
166
167 let parent_path = path
168 .parent()
169 .ok_or_else(|| Error::InvalidPath(path.clone()))?;
170 let file_name = path
171 .file_name()
172 .ok_or_else(|| Error::InvalidPath(path.clone()))?;
173
174 let dir = OpenOptions::new()
180 .read(true)
181 .open(parent_path)
182 .map_err(|e| Error::FileOpen(e, parent_path.into()))?;
183
184 short_path.push(dir.as_raw_descriptor().to_string());
185 short_path.push(file_name);
186 path_cow = Cow::Owned(short_path);
187 _dir_fd = Some(dir);
188 }
189
190 if path_cow.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
193 return Err(Error::InvalidPath(path_cow.into()));
194 }
195
196 let sock = UnixDatagram::unbound().map_err(Error::SocketCreate)?;
201 loop {
202 match sock.connect(&path_cow) {
203 Ok(_) => break,
204 Err(e) => {
205 match e.kind() {
206 ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
207 thread::sleep(Duration::from_millis(10))
211 }
212 _ => {
213 error!("Unexpected error connecting to logging socket: {:?}", e);
214 return Err(Error::SocketConnect(e));
215 }
216 }
217 }
218 };
219 }
220 keep_rds.push(sock.as_raw_descriptor());
221 let output: Option<Box<dyn Write + Send>> = Some(Box::new(WriteSocket::new(sock)));
222 Ok(T::new(
223 protection_type,
224 evt,
225 input,
226 output,
227 None,
228 Default::default(),
229 keep_rds.to_vec(),
230 ))
231 }
232 None => Err(Error::PathRequired),
233 }
234}
235
236pub(crate) fn create_unix_stream_serial_device<T: SerialDevice>(
238 param: &SerialParameters,
239 protection_type: ProtectionType,
240 evt: Event,
241 keep_rds: &mut Vec<RawDescriptor>,
242) -> std::result::Result<T, Error> {
243 let path = param.path.as_ref().ok_or(Error::PathRequired)?;
244 let input = UnixStream::connect(path).map_err(Error::SocketConnect)?;
245 let output = input.try_clone().map_err(Error::CloneUnixStream)?;
246 keep_rds.push(input.as_raw_descriptor());
247 keep_rds.push(output.as_raw_descriptor());
248
249 Ok(T::new(
250 protection_type,
251 evt,
252 Some(Box::new(input)),
253 Some(Box::new(output)),
254 None,
255 SerialOptions {
256 name: param.name.clone(),
257 out_timestamp: param.out_timestamp,
258 console: param.console,
259 pci_address: param.pci_address,
260 max_queue_sizes: param.max_queue_sizes.clone(),
261 },
262 keep_rds.to_vec(),
263 ))
264}