• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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