virtio-devices: determine tty size in console

This prepares us to be able to handle console resizes in the console
device's epoll loop, which we'll have to do if the output is a pty,
since we won't get SIGWINCH from it.

Signed-off-by: Alyssa Ross <hi@alyssa.is>
This commit is contained in:
Alyssa Ross 2021-09-10 11:27:08 +00:00 committed by Rob Bradford
parent 68e6a14deb
commit 28382a1491
4 changed files with 47 additions and 44 deletions

View File

@ -11,7 +11,7 @@ use crate::seccomp_filters::Thread;
use crate::thread_helper::spawn_virtio_thread; use crate::thread_helper::spawn_virtio_thread;
use crate::GuestMemoryMmap; use crate::GuestMemoryMmap;
use crate::VirtioInterrupt; use crate::VirtioInterrupt;
use libc::EFD_NONBLOCK; use libc::{EFD_NONBLOCK, TIOCGWINSZ};
use seccompiler::SeccompAction; use seccompiler::SeccompAction;
use std::cmp; use std::cmp;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -46,7 +46,7 @@ const FILE_EVENT: u16 = EPOLL_HELPER_EVENT_LAST + 5;
//Console size feature bit //Console size feature bit
const VIRTIO_CONSOLE_F_SIZE: u64 = 0; const VIRTIO_CONSOLE_F_SIZE: u64 = 0;
#[derive(Copy, Clone, Debug, Default, Versionize)] #[derive(Copy, Clone, Debug, Versionize)]
#[repr(C, packed)] #[repr(C, packed)]
pub struct VirtioConsoleConfig { pub struct VirtioConsoleConfig {
cols: u16, cols: u16,
@ -55,6 +55,17 @@ pub struct VirtioConsoleConfig {
emerg_wr: u32, emerg_wr: u32,
} }
impl Default for VirtioConsoleConfig {
fn default() -> Self {
VirtioConsoleConfig {
cols: 0,
rows: 0,
max_nr_ports: 1,
emerg_wr: 0,
}
}
}
// Safe because it only has data and has no implicit padding. // Safe because it only has data and has no implicit padding.
unsafe impl ByteValued for VirtioConsoleConfig {} unsafe impl ByteValued for VirtioConsoleConfig {}
@ -287,12 +298,13 @@ pub struct ConsoleResizer {
} }
impl ConsoleResizer { impl ConsoleResizer {
pub fn update_console_size(&self, cols: u16, rows: u16) { pub fn update_console_size(&self) {
if self if self
.acked_features .acked_features
.fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel) .fetch_and(1u64 << VIRTIO_CONSOLE_F_SIZE, Ordering::AcqRel)
!= 0 != 0
{ {
let (cols, rows) = get_win_size();
self.config.lock().unwrap().update_console_size(cols, rows); self.config.lock().unwrap().update_console_size(cols, rows);
//Send the interrupt to the driver //Send the interrupt to the driver
let _ = self.config_evt.write(1); let _ = self.config_evt.write(1);
@ -301,15 +313,6 @@ impl ConsoleResizer {
} }
impl VirtioConsoleConfig { impl VirtioConsoleConfig {
pub fn new(cols: u16, rows: u16) -> Self {
VirtioConsoleConfig {
cols,
rows,
max_nr_ports: 1u32,
emerg_wr: 0u32,
}
}
pub fn update_console_size(&mut self, cols: u16, rows: u16) { pub fn update_console_size(&mut self, cols: u16, rows: u16) {
self.cols = cols; self.cols = cols;
self.rows = rows; self.rows = rows;
@ -336,6 +339,24 @@ pub struct ConsoleState {
in_buffer: Vec<u8>, in_buffer: Vec<u8>,
} }
fn get_win_size() -> (u16, u16) {
#[repr(C)]
#[derive(Default)]
struct WindowSize {
rows: u16,
cols: u16,
xpixel: u16,
ypixel: u16,
}
let ws: WindowSize = WindowSize::default();
unsafe {
libc::ioctl(0, TIOCGWINSZ, &ws);
}
(ws.cols, ws.rows)
}
impl VersionMapped for ConsoleState {} impl VersionMapped for ConsoleState {}
impl Console { impl Console {
@ -343,8 +364,6 @@ impl Console {
pub fn new( pub fn new(
id: String, id: String,
endpoint: Endpoint, endpoint: Endpoint,
cols: u16,
rows: u16,
iommu: bool, iommu: bool,
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
exit_evt: EventFd, exit_evt: EventFd,
@ -356,13 +375,15 @@ impl Console {
} }
let config_evt = EventFd::new(EFD_NONBLOCK).unwrap(); let config_evt = EventFd::new(EFD_NONBLOCK).unwrap();
let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::new(cols, rows))); let console_config = Arc::new(Mutex::new(VirtioConsoleConfig::default()));
let resizer = Arc::new(ConsoleResizer { let resizer = Arc::new(ConsoleResizer {
config_evt, config_evt,
config: console_config.clone(), config: console_config.clone(),
acked_features: AtomicU64::new(0), acked_features: AtomicU64::new(0),
}); });
resizer.update_console_size();
Ok(( Ok((
Console { Console {
common: VirtioCommon { common: VirtioCommon {

View File

@ -48,6 +48,7 @@ macro_rules! or {
} }
// See include/uapi/asm-generic/ioctls.h in the kernel code. // See include/uapi/asm-generic/ioctls.h in the kernel code.
const TIOCGWINSZ: u64 = 0x5413;
const FIONBIO: u64 = 0x5421; const FIONBIO: u64 = 0x5421;
// See include/uapi/linux/vfio.h in the kernel code. // See include/uapi/linux/vfio.h in the kernel code.
@ -57,6 +58,10 @@ const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72;
// See include/uapi/linux/if_tun.h in the kernel code. // See include/uapi/linux/if_tun.h in the kernel code.
const TUNSETOFFLOAD: u64 = 0x4004_54d0; const TUNSETOFFLOAD: u64 = 0x4004_54d0;
fn create_virtio_console_ioctl_seccomp_rule() -> Vec<SeccompRule> {
or![and![Cond::new(1, ArgLen::Dword, Eq, TIOCGWINSZ).unwrap()]]
}
fn create_virtio_iommu_ioctl_seccomp_rule() -> Vec<SeccompRule> { fn create_virtio_iommu_ioctl_seccomp_rule() -> Vec<SeccompRule> {
or![ or![
and![Cond::new(1, ArgLen::Dword, Eq, VFIO_IOMMU_MAP_DMA).unwrap()], and![Cond::new(1, ArgLen::Dword, Eq, VFIO_IOMMU_MAP_DMA).unwrap()],
@ -99,6 +104,7 @@ fn virtio_block_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
fn virtio_console_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> { fn virtio_console_thread_rules() -> Vec<(i64, Vec<SeccompRule>)> {
vec![ vec![
(libc::SYS_ioctl, create_virtio_console_ioctl_seccomp_rule()),
(libc::SYS_mprotect, vec![]), (libc::SYS_mprotect, vec![]),
(libc::SYS_prctl, vec![]), (libc::SYS_prctl, vec![]),
(libc::SYS_sched_getaffinity, vec![]), (libc::SYS_sched_getaffinity, vec![]),

View File

@ -62,7 +62,7 @@ use hypervisor::DeviceFd;
use hypervisor::IoEventAddress; use hypervisor::IoEventAddress;
use libc::{ use libc::{
isatty, tcgetattr, tcsetattr, termios, ECHO, ICANON, ISIG, MAP_NORESERVE, MAP_PRIVATE, isatty, tcgetattr, tcsetattr, termios, ECHO, ICANON, ISIG, MAP_NORESERVE, MAP_PRIVATE,
MAP_SHARED, O_TMPFILE, PROT_READ, PROT_WRITE, TCSANOW, TIOCGWINSZ, MAP_SHARED, O_TMPFILE, PROT_READ, PROT_WRITE, TCSANOW,
}; };
use pci::VfioPciDevice; use pci::VfioPciDevice;
use pci::{ use pci::{
@ -450,24 +450,6 @@ type VirtioDeviceArc = Arc<Mutex<dyn virtio_devices::VirtioDevice>>;
#[cfg(feature = "acpi")] #[cfg(feature = "acpi")]
const DEVICE_MANAGER_ACPI_SIZE: usize = 0x10; const DEVICE_MANAGER_ACPI_SIZE: usize = 0x10;
pub fn get_win_size() -> (u16, u16) {
#[repr(C)]
#[derive(Default)]
struct WindowSize {
rows: u16,
cols: u16,
xpixel: u16,
ypixel: u16,
}
let ws: WindowSize = WindowSize::default();
unsafe {
libc::ioctl(0, TIOCGWINSZ, &ws);
}
(ws.cols, ws.rows)
}
const TIOCSPTLCK: libc::c_int = 0x4004_5431; const TIOCSPTLCK: libc::c_int = 0x4004_5431;
const TIOCGTPEER: libc::c_int = 0x5441; const TIOCGTPEER: libc::c_int = 0x5441;
@ -543,9 +525,9 @@ impl Console {
Ok(()) Ok(())
} }
pub fn update_console_size(&self, cols: u16, rows: u16) { pub fn update_console_size(&self) {
if let Some(resizer) = self.console_resizer.as_ref() { if let Some(resizer) = self.console_resizer.as_ref() {
resizer.update_console_size(cols, rows) resizer.update_console_size()
} }
} }
} }
@ -1716,14 +1698,11 @@ impl DeviceManager {
ConsoleOutputMode::Null => Endpoint::Null, ConsoleOutputMode::Null => Endpoint::Null,
ConsoleOutputMode::Off => return Ok(None), ConsoleOutputMode::Off => return Ok(None),
}; };
let (col, row) = get_win_size();
let id = String::from(CONSOLE_DEVICE_NAME); let id = String::from(CONSOLE_DEVICE_NAME);
let (virtio_console_device, console_resizer) = virtio_devices::Console::new( let (virtio_console_device, console_resizer) = virtio_devices::Console::new(
id.clone(), id.clone(),
endpoint, endpoint,
col,
row,
self.force_iommu | console_config.iommu, self.force_iommu | console_config.iommu,
self.seccomp_action.clone(), self.seccomp_action.clone(),
self.exit_evt self.exit_evt

View File

@ -17,9 +17,7 @@ use crate::config::{
ConsoleOutputMode, DeviceConfig, DiskConfig, FsConfig, HotplugMethod, NetConfig, PmemConfig, ConsoleOutputMode, DeviceConfig, DiskConfig, FsConfig, HotplugMethod, NetConfig, PmemConfig,
UserDeviceConfig, ValidationError, VmConfig, VsockConfig, UserDeviceConfig, ValidationError, VmConfig, VsockConfig,
}; };
use crate::device_manager::{ use crate::device_manager::{self, Console, DeviceManager, DeviceManagerError, PtyPair};
self, get_win_size, Console, DeviceManager, DeviceManagerError, PtyPair,
};
use crate::device_tree::DeviceTree; use crate::device_tree::DeviceTree;
use crate::memory_manager::{Error as MemoryManagerError, MemoryManager}; use crate::memory_manager::{Error as MemoryManagerError, MemoryManager};
use crate::migration::{get_vm_snapshot, url_to_path, VM_SNAPSHOT_FILE}; use crate::migration::{get_vm_snapshot, url_to_path, VM_SNAPSHOT_FILE};
@ -1602,8 +1600,7 @@ impl Vm {
for signal in signals.forever() { for signal in signals.forever() {
match signal { match signal {
SIGWINCH => { SIGWINCH => {
let (col, row) = get_win_size(); console_input_clone.update_console_size();
console_input_clone.update_console_size(col, row);
} }
SIGTERM | SIGINT => { SIGTERM | SIGINT => {
if on_tty { if on_tty {