1 // Copyright 2020 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 std::fmt::{self, Display};
6 use std::fs::OpenOptions;
7 use std::io::{self, stdin, stdout};
8 use std::path::PathBuf;
9
10 use base::{error, open_file, syslog, AsRawDescriptor, Event, FileSync, RawDescriptor};
11 use hypervisor::ProtectionType;
12 use remain::sorted;
13 use serde::{Deserialize, Serialize};
14 use thiserror::Error as ThisError;
15
16 pub use crate::sys::serial_device::SerialDevice;
17 use crate::sys::serial_device::*;
18
19 #[sorted]
20 #[derive(ThisError, Debug)]
21 pub enum Error {
22 #[error("Unable to clone an Event: {0}")]
23 CloneEvent(base::Error),
24 #[error("Unable to open/create file: {0}")]
25 FileError(std::io::Error),
26 #[error("Serial device path is invalid")]
27 InvalidPath,
28 #[error("Invalid serial hardware: {0}")]
29 InvalidSerialHardware(String),
30 #[error("Invalid serial type: {0}")]
31 InvalidSerialType(String),
32 #[error("Serial device type file requires a path")]
33 PathRequired,
34 #[error("Failed to create unbound socket")]
35 SocketCreateFailed,
36 #[error("Unable to open system type serial: {0}")]
37 SystemTypeError(std::io::Error),
38 #[error("Serial device type {0} not implemented")]
39 Unimplemented(SerialType),
40 }
41
42 /// Enum for possible type of serial devices
43 #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
44 #[serde(rename_all = "kebab-case")]
45 pub enum SerialType {
46 File,
47 Stdout,
48 Sink,
49 Syslog,
50 #[cfg_attr(unix, serde(rename = "unix"))]
51 #[cfg_attr(windows, serde(rename = "namedpipe"))]
52 SystemSerialType,
53 }
54
55 impl Default for SerialType {
default() -> Self56 fn default() -> Self {
57 Self::Sink
58 }
59 }
60
61 impl Display for SerialType {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 let s = match &self {
64 SerialType::File => "File".to_string(),
65 SerialType::Stdout => "Stdout".to_string(),
66 SerialType::Sink => "Sink".to_string(),
67 SerialType::Syslog => "Syslog".to_string(),
68 SerialType::SystemSerialType => SYSTEM_SERIAL_TYPE_NAME.to_string(),
69 };
70
71 write!(f, "{}", s)
72 }
73 }
74
75 /// Serial device hardware types
76 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
77 #[serde(rename_all = "kebab-case")]
78 pub enum SerialHardware {
79 Serial, // Standard PC-style (8250/16550 compatible) UART
80 VirtioConsole, // virtio-console device
81 }
82
83 impl Default for SerialHardware {
default() -> Self84 fn default() -> Self {
85 Self::Serial
86 }
87 }
88
89 impl Display for SerialHardware {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 let s = match &self {
92 SerialHardware::Serial => "serial".to_string(),
93 SerialHardware::VirtioConsole => "virtio-console".to_string(),
94 };
95
96 write!(f, "{}", s)
97 }
98 }
99
serial_parameters_default_num() -> u8100 fn serial_parameters_default_num() -> u8 {
101 1
102 }
103
104 #[derive(Clone, Debug, Default, PartialEq, Deserialize)]
105 #[serde(deny_unknown_fields, default)]
106 pub struct SerialParameters {
107 #[serde(rename = "type")]
108 pub type_: SerialType,
109 pub hardware: SerialHardware,
110 pub path: Option<PathBuf>,
111 pub input: Option<PathBuf>,
112 #[serde(default = "serial_parameters_default_num")]
113 pub num: u8,
114 pub console: bool,
115 pub earlycon: bool,
116 pub stdin: bool,
117 pub out_timestamp: bool,
118 }
119
120 impl SerialParameters {
121 /// Helper function to create a serial device from the defined parameters.
122 ///
123 /// # Arguments
124 /// * `evt` - event used for interrupt events
125 /// * `keep_rds` - Vector of FDs required by this device if it were sandboxed in a child
126 /// process. `evt` will always be added to this vector by this function.
create_serial_device<T: SerialDevice>( &self, protected_vm: ProtectionType, evt: &Event, keep_rds: &mut Vec<RawDescriptor>, ) -> std::result::Result<T, Error>127 pub fn create_serial_device<T: SerialDevice>(
128 &self,
129 protected_vm: ProtectionType,
130 evt: &Event,
131 keep_rds: &mut Vec<RawDescriptor>,
132 ) -> std::result::Result<T, Error> {
133 let evt = evt.try_clone().map_err(Error::CloneEvent)?;
134 keep_rds.push(evt.as_raw_descriptor());
135 let input: Option<Box<dyn io::Read + Send>> = if let Some(input_path) = &self.input {
136 let input_path = input_path.as_path();
137
138 let input_file = open_file(input_path, OpenOptions::new().read(true))
139 .map_err(|e| Error::FileError(e.into()))?;
140
141 keep_rds.push(input_file.as_raw_descriptor());
142 Some(Box::new(input_file))
143 } else if self.stdin {
144 keep_rds.push(stdin().as_raw_descriptor());
145 Some(Box::new(ConsoleInput))
146 } else {
147 None
148 };
149 let (output, sync): (
150 Option<Box<dyn io::Write + Send>>,
151 Option<Box<dyn FileSync + Send>>,
152 ) = match self.type_ {
153 SerialType::Stdout => {
154 keep_rds.push(stdout().as_raw_descriptor());
155 (Some(Box::new(stdout())), None)
156 }
157 SerialType::Sink => (None, None),
158 SerialType::Syslog => {
159 syslog::push_descriptors(keep_rds);
160 (
161 Some(Box::new(syslog::Syslogger::new(
162 syslog::Priority::Info,
163 syslog::Facility::Daemon,
164 ))),
165 None,
166 )
167 }
168 SerialType::File => match &self.path {
169 Some(path) => {
170 let file = open_file(path, OpenOptions::new().append(true).create(true))
171 .map_err(|e| Error::FileError(e.into()))?;
172 let sync = file.try_clone().map_err(Error::FileError)?;
173
174 keep_rds.push(file.as_raw_descriptor());
175 keep_rds.push(sync.as_raw_descriptor());
176
177 (Some(Box::new(file)), Some(Box::new(sync)))
178 }
179 None => return Err(Error::PathRequired),
180 },
181 SerialType::SystemSerialType => {
182 return create_system_type_serial_device(self, protected_vm, evt, input, keep_rds);
183 }
184 };
185 Ok(T::new(
186 protected_vm,
187 evt,
188 input,
189 output,
190 sync,
191 self.out_timestamp,
192 keep_rds.to_vec(),
193 ))
194 }
195 }
196
197 #[cfg(test)]
198 mod tests {
199 use super::*;
200 use serde_keyvalue::*;
201
from_serial_arg(options: &str) -> Result<SerialParameters, ParseError>202 fn from_serial_arg(options: &str) -> Result<SerialParameters, ParseError> {
203 from_key_values(options)
204 }
205
206 #[test]
params_from_key_values()207 fn params_from_key_values() {
208 // Defaults
209 let params = from_serial_arg("").unwrap();
210 assert_eq!(
211 params,
212 SerialParameters {
213 type_: SerialType::Sink,
214 hardware: SerialHardware::Serial,
215 path: None,
216 input: None,
217 num: 1,
218 console: false,
219 earlycon: false,
220 stdin: false,
221 out_timestamp: false,
222 }
223 );
224
225 // type parameter
226 let params = from_serial_arg("type=file").unwrap();
227 assert_eq!(params.type_, SerialType::File);
228 let params = from_serial_arg("type=stdout").unwrap();
229 assert_eq!(params.type_, SerialType::Stdout);
230 let params = from_serial_arg("type=sink").unwrap();
231 assert_eq!(params.type_, SerialType::Sink);
232 let params = from_serial_arg("type=syslog").unwrap();
233 assert_eq!(params.type_, SerialType::Syslog);
234 #[cfg(unix)]
235 let opt = "type=unix";
236 #[cfg(window)]
237 let opt = "type=namedpipe";
238 let params = from_serial_arg(opt).unwrap();
239 assert_eq!(params.type_, SerialType::SystemSerialType);
240 let params = from_serial_arg("type=foobar");
241 assert!(params.is_err());
242
243 // hardware parameter
244 let params = from_serial_arg("hardware=serial").unwrap();
245 assert_eq!(params.hardware, SerialHardware::Serial);
246 let params = from_serial_arg("hardware=virtio-console").unwrap();
247 assert_eq!(params.hardware, SerialHardware::VirtioConsole);
248 let params = from_serial_arg("hardware=foobar");
249 assert!(params.is_err());
250
251 // path parameter
252 let params = from_serial_arg("path=/test/path").unwrap();
253 assert_eq!(params.path, Some("/test/path".into()));
254 let params = from_serial_arg("path");
255 assert!(params.is_err());
256
257 // input parameter
258 let params = from_serial_arg("input=/path/to/input").unwrap();
259 assert_eq!(params.input, Some("/path/to/input".into()));
260 let params = from_serial_arg("input");
261 assert!(params.is_err());
262
263 // console parameter
264 let params = from_serial_arg("console").unwrap();
265 assert!(params.console);
266 let params = from_serial_arg("console=true").unwrap();
267 assert!(params.console);
268 let params = from_serial_arg("console=false").unwrap();
269 assert!(!params.console);
270 let params = from_serial_arg("console=foobar");
271 assert!(params.is_err());
272
273 // earlycon parameter
274 let params = from_serial_arg("earlycon").unwrap();
275 assert!(params.earlycon);
276 let params = from_serial_arg("earlycon=true").unwrap();
277 assert!(params.earlycon);
278 let params = from_serial_arg("earlycon=false").unwrap();
279 assert!(!params.earlycon);
280 let params = from_serial_arg("earlycon=foobar");
281 assert!(params.is_err());
282
283 // stdin parameter
284 let params = from_serial_arg("stdin").unwrap();
285 assert!(params.stdin);
286 let params = from_serial_arg("stdin=true").unwrap();
287 assert!(params.stdin);
288 let params = from_serial_arg("stdin=false").unwrap();
289 assert!(!params.stdin);
290 let params = from_serial_arg("stdin=foobar");
291 assert!(params.is_err());
292
293 // all together
294 let params = from_serial_arg("type=stdout,path=/some/path,hardware=virtio-console,num=5,earlycon,console,stdin,input=/some/input,out_timestamp").unwrap();
295 assert_eq!(
296 params,
297 SerialParameters {
298 type_: SerialType::Stdout,
299 hardware: SerialHardware::VirtioConsole,
300 path: Some("/some/path".into()),
301 input: Some("/some/input".into()),
302 num: 5,
303 console: true,
304 earlycon: true,
305 stdin: true,
306 out_timestamp: true,
307 }
308 );
309
310 // invalid field
311 let params = from_serial_arg("type=stdout,foo=bar");
312 assert!(params.is_err());
313 }
314 }
315