Skip to content

Commit f31c4b9

Browse files
committed
virtio-console: Basic support for passign SIGINT to guest
This works by writing ascii 'ETX' into the guest console on SIGINT. This curently only works when the guest input is redirected (otherwise pressing ^C should work, but SIGINT to the host libkrun process won't work. Furthermore init process needs to fork itself, because pid 1 cannot receive SIGINT signal, unless it instals a handler, but we cannot expect all programs to do that. Signed-off-by: Matej Hrica <[email protected]>
1 parent b509d1b commit f31c4b9

File tree

5 files changed

+105
-24
lines changed

5 files changed

+105
-24
lines changed

init/init.c

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <string.h>
99
#include <time.h>
1010
#include <dirent.h>
11+
#include <termios.h>
1112

1213
#include <net/if.h>
1314
#include <sys/ioctl.h>
@@ -913,15 +914,23 @@ int main(int argc, char **argv)
913914
}
914915
#endif
915916

916-
if (setup_redirects() < 0) {
917-
exit(-4);
917+
// We need to fork ourselves, because pid 1 cannot doesn't receive SIGINT signal
918+
int pid = fork();
919+
if (pid < 0) {
920+
perror("fork");
921+
exit(-3);
922+
} if (pid == 0) { // child
923+
if (setup_redirects() < 0) {
924+
exit(-4);
925+
}
926+
if (execvp(exec_argv[0], exec_argv) < 0) {
927+
printf("Couldn't execute '%s' inside the vm: %s\n", exec_argv[0], strerror(errno));
928+
exit(-3);
929+
}
930+
} else { // parent
931+
// wait for children since we can't exit init
932+
waitpid(pid, NULL, 0);
918933
}
919934

920-
if (execvp(exec_argv[0], exec_argv) < 0) {
921-
//TODO: maybe print this msg to the console not redirected stdout?
922-
printf("Couldn't execute '%s' inside the vm: %s\n", exec_argv[0], strerror(errno));
923-
exit(-3);
924-
}
925-
926935
return 0;
927936
}

src/devices/src/virtio/console/port_io.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
use std::io::{self, ErrorKind};
22
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
33

4-
use libc::{fcntl, F_GETFL, F_SETFL, O_NONBLOCK, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO};
4+
use libc::{
5+
fcntl, EFD_NONBLOCK, F_GETFL, F_SETFL, O_NONBLOCK, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO,
6+
};
57
use log::Level;
68
use nix::errno::Errno;
79
use nix::poll::{poll, PollFd, PollFlags};
810
use nix::unistd::dup;
11+
use utils::eventfd::EventFd;
912
use vm_memory::bitmap::Bitmap;
1013
use vm_memory::{VolatileMemoryError, VolatileSlice, WriteVolatile};
1114

@@ -179,3 +182,40 @@ impl PortOutput for PortOutputLog {
179182

180183
fn wait_until_writable(&self) {}
181184
}
185+
186+
pub struct PortInputSigInt {
187+
sigint_evt: EventFd,
188+
}
189+
190+
impl PortInputSigInt {
191+
pub fn new() -> Self {
192+
PortInputSigInt {
193+
sigint_evt: EventFd::new(EFD_NONBLOCK)
194+
.expect("Failed to create EventFd for SIGINT signaling"),
195+
}
196+
}
197+
198+
pub fn sigint_evt(&self) -> &EventFd {
199+
&self.sigint_evt
200+
}
201+
}
202+
203+
impl Default for PortInputSigInt {
204+
fn default() -> Self {
205+
Self::new()
206+
}
207+
}
208+
209+
impl PortInput for PortInputSigInt {
210+
fn read_volatile(&mut self, buf: &mut VolatileSlice) -> Result<usize, io::Error> {
211+
self.sigint_evt.read()?;
212+
log::trace!("SIGINT received");
213+
buf.copy_from(&[3u8]); //ASCII 'ETX' -> generates SIGINIT in a terminal
214+
Ok(1)
215+
}
216+
217+
fn wait_until_readable(&self) {
218+
let mut poll_fds = [PollFd::new(self.sigint_evt.as_raw_fd(), PollFlags::POLLIN)];
219+
poll(&mut poll_fds, -1).expect("Failed to poll");
220+
}
221+
}

src/vmm/src/builder.rs

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crossbeam_channel::unbounded;
88
use std::fmt::{Display, Formatter};
99
use std::io;
10+
use std::os::fd::AsRawFd;
1011
use std::sync::{Arc, Mutex};
1112

1213
use super::{Error, Vmm};
@@ -28,6 +29,7 @@ use kbs_types::Tee;
2829
use crate::device_manager;
2930
#[cfg(feature = "tee")]
3031
use crate::resources::TeeConfig;
32+
use crate::signal_handler::register_sigint_handler;
3133
#[cfg(target_os = "linux")]
3234
use crate::signal_handler::register_sigwinch_handler;
3335
use crate::terminal::term_set_raw_mode;
@@ -1040,9 +1042,6 @@ fn attach_console_devices(
10401042
intc: Option<Arc<Mutex<Gic>>>,
10411043
) -> std::result::Result<(), StartMicrovmError> {
10421044
use self::StartMicrovmError::*;
1043-
if let Err(e) = term_set_raw_mode(false) {
1044-
log::error!("Failed to set terminal to raw mode: {e}")
1045-
}
10461045

10471046
let stdin_is_terminal = isatty(STDIN_FILENO).unwrap_or(false);
10481047
let stdout_is_terminal = isatty(STDOUT_FILENO).unwrap_or(false);
@@ -1052,17 +1051,26 @@ fn attach_console_devices(
10521051
log::error!("Failed to set terminal to raw mode: {e}")
10531052
}
10541053

1054+
let console_input = if stdin_is_terminal {
1055+
Some(port_io::stdin().unwrap())
1056+
} else {
1057+
let sigint_input = port_io::PortInputSigInt::new();
1058+
let sigint_input_fd = sigint_input.sigint_evt().as_raw_fd();
1059+
1060+
register_sigint_handler(sigint_input_fd).map_err(RegisterFsSigwinch)?;
1061+
1062+
Some(Box::new(sigint_input) as _)
1063+
};
1064+
1065+
let console_output = if stdout_is_terminal {
1066+
Some(port_io::stdout().unwrap())
1067+
} else {
1068+
Some(port_io::output_to_log_as_err())
1069+
};
1070+
10551071
let mut ports = vec![PortDescription::Console {
1056-
input: if stdin_is_terminal {
1057-
Some(port_io::stdin().unwrap())
1058-
} else {
1059-
None
1060-
},
1061-
output: if stdout_is_terminal {
1062-
Some(port_io::stdout().unwrap())
1063-
} else {
1064-
Some(port_io::output_to_log_as_err())
1065-
},
1072+
input: console_input,
1073+
output: console_output,
10661074
}];
10671075

10681076
if !stdin_is_terminal {

src/vmm/src/signal_handler.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use std::os::unix::io::RawFd;
55
use std::sync::atomic::{AtomicI32, Ordering};
66

7-
use libc::{_exit, c_int, c_void, siginfo_t, SIGBUS, SIGSEGV, SIGSYS, SIGWINCH};
7+
use libc::{_exit, c_int, c_void, siginfo_t, SIGBUS, SIGINT, SIGSEGV, SIGSYS, SIGWINCH};
88
use utils::signal::register_signal_handler;
99

1010
// The offset of `si_syscall` (offending syscall identifier) within the siginfo structure
@@ -17,6 +17,7 @@ const SI_OFF_SYSCALL: isize = 6;
1717
const SYS_SECCOMP_CODE: i32 = 1;
1818

1919
static CONSOLE_SIGWINCH_FD: AtomicI32 = AtomicI32::new(-1);
20+
static CONSOLE_SIGINT_FD: AtomicI32 = AtomicI32::new(-1);
2021

2122
/// Signal handler for `SIGSYS`.
2223
///
@@ -94,6 +95,21 @@ extern "C" fn sigwinch_handler(num: c_int, info: *mut siginfo_t, _unused: *mut c
9495
let _ = unsafe { libc::write(console_fd, &val as *const _ as *const c_void, 8) };
9596
}
9697

98+
extern "C" fn sigint_handler(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) {
99+
// Safe because we're just reading some fields from a supposedly valid argument.
100+
let si_signo = unsafe { (*info).si_signo };
101+
102+
// Sanity check. The condition should never be true.
103+
if num != si_signo || num != SIGINT {
104+
// Safe because we're terminating the process anyway.
105+
unsafe { _exit(i32::from(super::FC_EXIT_CODE_UNEXPECTED_ERROR)) };
106+
}
107+
108+
let val: u64 = 1;
109+
let console_fd = CONSOLE_SIGINT_FD.load(Ordering::Relaxed);
110+
let _ = unsafe { libc::write(console_fd, &val as *const _ as *const c_void, 8) };
111+
}
112+
97113
pub fn register_sigwinch_handler(console_fd: RawFd) -> utils::errno::Result<()> {
98114
CONSOLE_SIGWINCH_FD.store(console_fd, Ordering::Relaxed);
99115

@@ -102,6 +118,14 @@ pub fn register_sigwinch_handler(console_fd: RawFd) -> utils::errno::Result<()>
102118
Ok(())
103119
}
104120

121+
pub fn register_sigint_handler(sigint_fd: RawFd) -> utils::errno::Result<()> {
122+
CONSOLE_SIGINT_FD.store(sigint_fd, Ordering::Relaxed);
123+
124+
register_signal_handler(SIGINT, sigint_handler)?;
125+
126+
Ok(())
127+
}
128+
105129
/// Registers all the required signal handlers.
106130
///
107131
/// Custom handlers are installed for: `SIGBUS`, `SIGSEGV`, `SIGSYS`.

src/vmm/src/terminal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ pub fn term_fd_set_raw_mode(
2626
let mut termios = tcgetattr(term)?;
2727

2828
let mut mask = LocalFlags::ECHO | LocalFlags::ICANON;
29-
if handle_signals_by_terminal {
29+
if !handle_signals_by_terminal {
3030
mask |= LocalFlags::ISIG
3131
}
3232

0 commit comments

Comments
 (0)