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 ¶m.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