• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::borrow::Cow;
6 use std::fs::OpenOptions;
7 use std::io;
8 use std::io::ErrorKind;
9 use std::io::Write;
10 use std::os::unix::net::UnixDatagram;
11 use std::path::Path;
12 use std::path::PathBuf;
13 use std::thread;
14 use std::time::Duration;
15 
16 use base::error;
17 use base::info;
18 use base::read_raw_stdin;
19 use base::AsRawDescriptor;
20 use base::Event;
21 use base::FileSync;
22 use base::RawDescriptor;
23 use base::ReadNotifier;
24 use hypervisor::ProtectionType;
25 
26 use crate::serial_device::Error;
27 use crate::serial_device::SerialInput;
28 use crate::serial_device::SerialOptions;
29 use crate::serial_device::SerialParameters;
30 
31 pub const SYSTEM_SERIAL_TYPE_NAME: &str = "UnixSocket";
32 
33 // This wrapper is used in place of the libstd native version because we don't want
34 // buffering for stdin.
35 pub struct ConsoleInput(std::io::Stdin);
36 
37 impl ConsoleInput {
new() -> Self38     pub fn new() -> Self {
39         Self(std::io::stdin())
40     }
41 }
42 
43 impl io::Read for ConsoleInput {
read(&mut self, out: &mut [u8]) -> io::Result<usize>44     fn read(&mut self, out: &mut [u8]) -> io::Result<usize> {
45         read_raw_stdin(out).map_err(|e| e.into())
46     }
47 }
48 
49 impl ReadNotifier for ConsoleInput {
get_read_notifier(&self) -> &dyn AsRawDescriptor50     fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
51         &self.0
52     }
53 }
54 
55 impl SerialInput for ConsoleInput {}
56 
57 /// Abstraction over serial-like devices that can be created given an event and optional input and
58 /// output streams.
59 pub trait SerialDevice {
new( protection_type: ProtectionType, interrupt_evt: Event, input: Option<Box<dyn SerialInput>>, output: Option<Box<dyn io::Write + Send>>, sync: Option<Box<dyn FileSync + Send>>, options: SerialOptions, keep_rds: Vec<RawDescriptor>, ) -> Self60     fn new(
61         protection_type: ProtectionType,
62         interrupt_evt: Event,
63         input: Option<Box<dyn SerialInput>>,
64         output: Option<Box<dyn io::Write + Send>>,
65         sync: Option<Box<dyn FileSync + Send>>,
66         options: SerialOptions,
67         keep_rds: Vec<RawDescriptor>,
68     ) -> Self;
69 }
70 
71 // The maximum length of a path that can be used as the address of a
72 // unix socket. Note that this includes the null-terminator.
73 pub const MAX_SOCKET_PATH_LENGTH: usize = 108;
74 
75 struct WriteSocket {
76     sock: UnixDatagram,
77     buf: Vec<u8>,
78 }
79 
80 const BUF_CAPACITY: usize = 1024;
81 
82 impl WriteSocket {
new(s: UnixDatagram) -> WriteSocket83     pub fn new(s: UnixDatagram) -> WriteSocket {
84         WriteSocket {
85             sock: s,
86             buf: Vec::with_capacity(BUF_CAPACITY),
87         }
88     }
89 
send_buf(&self, buf: &[u8]) -> io::Result<usize>90     pub fn send_buf(&self, buf: &[u8]) -> io::Result<usize> {
91         const SEND_RETRY: usize = 2;
92         let mut sent = 0;
93         for _ in 0..SEND_RETRY {
94             match self.sock.send(buf) {
95                 Ok(bytes_sent) => {
96                     sent = bytes_sent;
97                     break;
98                 }
99                 Err(e) => info!("Send error: {:?}", e),
100             }
101         }
102         Ok(sent)
103     }
104 }
105 
106 impl io::Write for WriteSocket {
write(&mut self, buf: &[u8]) -> io::Result<usize>107     fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
108         let last_newline_idx = match buf.iter().rposition(|&x| x == b'\n') {
109             Some(newline_idx) => Some(self.buf.len() + newline_idx),
110             None => None,
111         };
112         self.buf.extend_from_slice(buf);
113 
114         match last_newline_idx {
115             Some(last_newline_idx) => {
116                 for line in (self.buf[..last_newline_idx]).split(|&x| x == b'\n') {
117                     // Also drop CR+LF line endings.
118                     let send_line = match line.split_last() {
119                         Some((b'\r', trimmed)) => trimmed,
120                         _ => line,
121                     };
122                     if self.send_buf(send_line).is_err() {
123                         break;
124                     }
125                 }
126                 self.buf.drain(..=last_newline_idx);
127             }
128             None => {
129                 if self.buf.len() >= BUF_CAPACITY {
130                     if let Err(e) = self.send_buf(&self.buf) {
131                         info!("Couldn't send full buffer. {:?}", e);
132                     }
133                     self.buf.clear();
134                 }
135             }
136         }
137         Ok(buf.len())
138     }
139 
flush(&mut self) -> io::Result<()>140     fn flush(&mut self) -> io::Result<()> {
141         Ok(())
142     }
143 }
144 
create_system_type_serial_device<T: SerialDevice>( param: &SerialParameters, protection_type: ProtectionType, evt: Event, input: Option<Box<dyn SerialInput>>, keep_rds: &mut Vec<RawDescriptor>, ) -> std::result::Result<T, Error>145 pub(crate) fn create_system_type_serial_device<T: SerialDevice>(
146     param: &SerialParameters,
147     protection_type: ProtectionType,
148     evt: Event,
149     input: Option<Box<dyn SerialInput>>,
150     keep_rds: &mut Vec<RawDescriptor>,
151 ) -> std::result::Result<T, Error> {
152     match &param.path {
153         Some(path) => {
154             // If the path is longer than 107 characters,
155             // then we won't be able to connect directly
156             // to it. Instead we can shorten the path by
157             // opening the containing directory and using
158             // /proc/self/fd/*/ to access it via a shorter
159             // path.
160             let mut path_cow = Cow::<Path>::Borrowed(path);
161             let mut _dir_fd = None;
162             if path.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
163                 let mut short_path = PathBuf::with_capacity(MAX_SOCKET_PATH_LENGTH);
164                 short_path.push("/proc/self/fd/");
165 
166                 let parent_path = path
167                     .parent()
168                     .ok_or_else(|| Error::InvalidPath(path.clone()))?;
169                 let file_name = path
170                     .file_name()
171                     .ok_or_else(|| Error::InvalidPath(path.clone()))?;
172 
173                 // We don't actually want to open this
174                 // directory for reading, but the stdlib
175                 // requires all files be opened as at
176                 // least one of readable, writeable, or
177                 // appeandable.
178                 let dir = OpenOptions::new()
179                     .read(true)
180                     .open(parent_path)
181                     .map_err(|e| Error::FileOpen(e, parent_path.into()))?;
182 
183                 short_path.push(dir.as_raw_descriptor().to_string());
184                 short_path.push(file_name);
185                 path_cow = Cow::Owned(short_path);
186                 _dir_fd = Some(dir);
187             }
188 
189             // The shortened path may still be too long,
190             // in which case we must give up here.
191             if path_cow.as_os_str().len() >= MAX_SOCKET_PATH_LENGTH {
192                 return Err(Error::InvalidPath(path_cow.into()));
193             }
194 
195             // There's a race condition between
196             // vmlog_forwarder making the logging socket and
197             // crosvm starting up, so we loop here until it's
198             // available.
199             let sock = UnixDatagram::unbound().map_err(Error::SocketCreate)?;
200             loop {
201                 match sock.connect(&path_cow) {
202                     Ok(_) => break,
203                     Err(e) => {
204                         match e.kind() {
205                             ErrorKind::NotFound | ErrorKind::ConnectionRefused => {
206                                 // logging socket doesn't
207                                 // exist yet, sleep for 10 ms
208                                 // and try again.
209                                 thread::sleep(Duration::from_millis(10))
210                             }
211                             _ => {
212                                 error!("Unexpected error connecting to logging socket: {:?}", e);
213                                 return Err(Error::SocketConnect(e));
214                             }
215                         }
216                     }
217                 };
218             }
219             keep_rds.push(sock.as_raw_descriptor());
220             let output: Option<Box<dyn Write + Send>> = Some(Box::new(WriteSocket::new(sock)));
221             Ok(T::new(
222                 protection_type,
223                 evt,
224                 input,
225                 output,
226                 None,
227                 Default::default(),
228                 keep_rds.to_vec(),
229             ))
230         }
231         None => Err(Error::PathRequired),
232     }
233 }
234