Skip to content

Commit c85b286

Browse files
committed
allow redirecting guest serial output to a file
Add a new field, `serial_out_path`, to the logger configuration (available both via API and CLI) which can be set to the path of a file into which the output of the guest's serial console should be dumped. Have the file behave identically to our log file (e.g. it must already exist, Firecracker does not create it). Signed-off-by: Patrick Roy <[email protected]>
1 parent 04456ec commit c85b286

File tree

8 files changed

+48
-2
lines changed

8 files changed

+48
-2
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ and this project adheres to
3535
requests to `/mmds/config` to enforce MMDS to always respond plain text
3636
contents in the IMDS format regardless of the `Accept` header in requests.
3737
Users need to regenerate snapshots.
38+
- [#5350](https://github.com/firecracker-microvm/firecracker/pull/5350): Added a
39+
`--serial-out-path` CLI option and a `serial_out_path` field to `PUT /logger`,
40+
which allows specifying a file into which Firecracker should redirect guest
41+
serial console output instead of stdout.
3842

3943
### Changed
4044

src/firecracker/src/api_server/request/logger.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod tests {
3636

3737
let expected_config = LoggerConfig {
3838
log_path: Some(PathBuf::from("log")),
39+
serial_out_path: None,
3940
level: Some(LevelFilter::Warn),
4041
show_level: Some(false),
4142
show_log_origin: Some(false),
@@ -55,6 +56,7 @@ mod tests {
5556

5657
let expected_config = LoggerConfig {
5758
log_path: Some(PathBuf::from("log")),
59+
serial_out_path: None,
5860
level: Some(LevelFilter::Debug),
5961
show_level: Some(false),
6062
show_log_origin: Some(false),

src/firecracker/src/main.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,11 @@ fn main_exec() -> Result<(), MainError> {
208208
.takes_value(true)
209209
.help("Path to a fifo or a file used for configuring the logger on startup."),
210210
)
211+
.arg(
212+
Argument::new("serial-out-path")
213+
.takes_value(true)
214+
.help("Path to a fifo or a file used for serial output."),
215+
)
211216
.arg(
212217
Argument::new("level")
213218
.takes_value(true)
@@ -300,6 +305,7 @@ fn main_exec() -> Result<(), MainError> {
300305
.set(String::from(instance_id))
301306
.unwrap();
302307
let log_path = arguments.single_value("log-path").map(PathBuf::from);
308+
let serial_out_path = arguments.single_value("serial-out-path").map(PathBuf::from);
303309
let level = arguments
304310
.single_value("level")
305311
.map(|s| vmm::logger::LevelFilter::from_str(s))
@@ -311,6 +317,7 @@ fn main_exec() -> Result<(), MainError> {
311317
LOGGER
312318
.update(LoggerConfig {
313319
log_path,
320+
serial_out_path,
314321
level,
315322
show_level,
316323
show_log_origin,

src/firecracker/swagger/firecracker.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,11 @@ definitions:
10221022
type: string
10231023
description: The module path to filter log messages by.
10241024
example: api_server::request
1025+
serial_out_path:
1026+
type: string
1027+
description:
1028+
Path to the named pipe of file for guest serial console output. If not specified,
1029+
serial console output will be printed to stdout.
10251030

10261031
MachineConfiguration:
10271032
type: object

src/vmm/src/device_manager/mod.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use std::convert::Infallible;
99
use std::fmt::Debug;
10+
use std::os::unix::prelude::OpenOptionsExt;
1011
use std::sync::{Arc, Mutex};
1112

1213
use acpi::ACPIDeviceManager;
@@ -32,6 +33,7 @@ use crate::devices::legacy::{IER_RDA_BIT, IER_RDA_OFFSET, SerialDevice};
3233
use crate::devices::pseudo::BootTimer;
3334
use crate::devices::virtio::device::VirtioDevice;
3435
use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport};
36+
use crate::logger::LOGGER;
3537
use crate::resources::VmResources;
3638
use crate::snapshot::Persist;
3739
use crate::vstate::memory::GuestMemoryMmap;
@@ -126,11 +128,23 @@ impl DeviceManager {
126128
fn setup_serial_device(
127129
event_manager: &mut EventManager,
128130
) -> Result<Arc<Mutex<SerialDevice>>, std::io::Error> {
129-
Self::set_stdout_nonblocking();
131+
let serial_out = match LOGGER.0.lock().expect("logger poisoned").serial_out_path {
132+
Some(ref path) => std::fs::OpenOptions::new()
133+
.custom_flags(libc::O_NONBLOCK)
134+
.read(true)
135+
.write(true)
136+
.open(path)
137+
.map(SerialOut::File)?,
138+
None => {
139+
Self::set_stdout_nonblocking();
140+
141+
SerialOut::Stdout(std::io::stdout())
142+
}
143+
};
130144

131145
let serial = Arc::new(Mutex::new(SerialDevice::new(
132146
Some(std::io::stdin()),
133-
SerialOut::Stdout(std::io::stdout()),
147+
serial_out,
134148
)?));
135149
event_manager.add_subscriber(serial.clone());
136150
Ok(serial)

src/vmm/src/devices/legacy/serial.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
//! Implements a wrapper over an UART serial device.
99
use std::fmt::Debug;
10+
use std::fs::File;
1011
use std::io::{self, Read, Stdin, Write};
1112
use std::os::unix::io::{AsRawFd, RawFd};
1213
use std::sync::{Arc, Barrier};
@@ -129,18 +130,21 @@ impl SerialEvents for SerialEventsWrapper {
129130
pub enum SerialOut {
130131
Sink,
131132
Stdout(std::io::Stdout),
133+
File(File),
132134
}
133135
impl std::io::Write for SerialOut {
134136
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
135137
match self {
136138
Self::Sink => Ok(buf.len()),
137139
Self::Stdout(stdout) => stdout.write(buf),
140+
Self::File(file) => file.write(buf),
138141
}
139142
}
140143
fn flush(&mut self) -> std::io::Result<()> {
141144
match self {
142145
Self::Sink => Ok(()),
143146
Self::Stdout(stdout) => stdout.flush(),
147+
Self::File(file) => file.flush(),
144148
}
145149
}
146150
}

src/vmm/src/logger/logging.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub static INSTANCE_ID: OnceLock<String> = OnceLock::new();
2828
/// Default values matching the swagger specification (`src/firecracker/swagger/firecracker.yaml`).
2929
pub static LOGGER: Logger = Logger(Mutex::new(LoggerConfiguration {
3030
target: None,
31+
serial_out_path: None,
3132
filter: LogFilter { module: None },
3233
format: LogFormat {
3334
show_level: false,
@@ -72,6 +73,10 @@ impl Logger {
7273
guard.target = Some(file);
7374
};
7475

76+
if let Some(serial_out_path) = config.serial_out_path {
77+
guard.serial_out_path = Some(serial_out_path);
78+
}
79+
7580
if let Some(show_level) = config.show_level {
7681
guard.format.show_level = show_level;
7782
}
@@ -104,6 +109,7 @@ pub struct LogFormat {
104109
#[derive(Debug)]
105110
pub struct LoggerConfiguration {
106111
pub target: Option<std::fs::File>,
112+
pub serial_out_path: Option<PathBuf>,
107113
pub filter: LogFilter,
108114
pub format: LogFormat,
109115
}
@@ -186,6 +192,8 @@ impl Log for Logger {
186192
pub struct LoggerConfig {
187193
/// Named pipe or file used as output for logs.
188194
pub log_path: Option<PathBuf>,
195+
/// Named pipe of file used as output for guest serial console.
196+
pub serial_out_path: Option<PathBuf>,
189197
/// The level of the Logger.
190198
pub level: Option<LevelFilter>,
191199
/// Whether to show the log level in the log.
@@ -366,6 +374,7 @@ mod tests {
366374
// Create logger.
367375
let logger = Logger(Mutex::new(LoggerConfiguration {
368376
target: Some(target),
377+
serial_out_path: None,
369378
filter: LogFilter {
370379
module: Some(String::from("module")),
371380
},

src/vmm/src/rpc_interface.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1185,6 +1185,7 @@ mod tests {
11851185
)));
11861186
check_unsupported(runtime_request(VmmAction::ConfigureLogger(LoggerConfig {
11871187
log_path: Some(PathBuf::new()),
1188+
serial_out_path: None,
11881189
level: Some(crate::logger::LevelFilter::Debug),
11891190
show_level: Some(false),
11901191
show_log_origin: Some(false),

0 commit comments

Comments
 (0)