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