diff options
Diffstat (limited to 'src/main.rs')
-rw-r--r-- | src/main.rs | 681 |
1 files changed, 8 insertions, 673 deletions
diff --git a/src/main.rs b/src/main.rs index b4d3cda..d3c80f7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,151 +20,28 @@ extern crate data_model; pub mod argument; pub mod kernel_cmdline; pub mod device_manager; +pub mod linux; -use std::ffi::{CString, CStr}; -use std::fmt; -use std::fs::{File, OpenOptions, remove_file}; -use std::io::{stdin, stdout}; use std::net; use std::os::unix::net::UnixDatagram; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::string::String; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, Mutex, Barrier}; -use std::thread; -use std::thread::{sleep, JoinHandle}; +use std::thread::sleep; use std::time::Duration; -use io_jail::Minijail; -use kvm::*; -use sys_util::{GuestAddress, GuestMemory, EventFd, TempDir, Terminal, Poller, Pollable, Scm, - register_signal_handler, Killable, SignalFd, chown, getpid, geteuid, getegid, - get_user_id, get_group_id, kill_process_group, reap_child, syslog}; - +use sys_util::{Scm, getpid, kill_process_group, reap_child, syslog}; use argument::{Argument, set_arguments, print_help}; -use device_manager::*; -use vm_control::{VmRequest, VmResponse}; - -enum Error { - OpenKernel(PathBuf, std::io::Error), - Socket(std::io::Error), - Disk(std::io::Error), - BlockDeviceNew(sys_util::Error), - VhostNetDeviceNew(devices::virtio::vhost::Error), - NetDeviceNew(devices::virtio::NetError), - NoVarEmpty, - VhostVsockDeviceNew(devices::virtio::vhost::Error), - DeviceJail(io_jail::Error), - DevicePivotRoot(io_jail::Error), - RegisterBlock(device_manager::Error), - RegisterNet(device_manager::Error), - RegisterWayland(device_manager::Error), - RegisterVsock(device_manager::Error), - Cmdline(kernel_cmdline::Error), - GetWaylandGroup(sys_util::Error), - SettingUidMap(io_jail::Error), - SettingGidMap(io_jail::Error), - ChownWaylandRoot(sys_util::Error), - RegisterIrqfd(sys_util::Error), - RegisterRng(device_manager::Error), - RngDeviceNew(devices::virtio::RngError), - KernelLoader(kernel_loader::Error), - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - ConfigureSystem(x86_64::Error), - EventFd(sys_util::Error), - SignalFd(sys_util::SignalFdError), - Kvm(sys_util::Error), - Vm(sys_util::Error), - Vcpu(sys_util::Error), - SpawnVcpu(std::io::Error), - Sys(sys_util::Error), -} - -impl std::convert::From<kernel_loader::Error> for Error { - fn from(e: kernel_loader::Error) -> Error { - Error::KernelLoader(e) - } -} - -#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] -impl std::convert::From<x86_64::Error> for Error { - fn from(e: x86_64::Error) -> Error { - Error::ConfigureSystem(e) - } -} - -impl std::convert::From<sys_util::Error> for Error { - fn from(e: sys_util::Error) -> Error { - Error::Sys(e) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &Error::OpenKernel(ref p, ref e) => write!(f, "failed to open kernel image {:?}: {}", p, e), - &Error::Socket(ref e) => write!(f, "failed to create socket: {}", e), - &Error::Disk(ref e) => write!(f, "failed to load disk image: {}", e), - &Error::BlockDeviceNew(ref e) => write!(f, "failed to create block device: {:?}", e), - &Error::RegisterBlock(ref e) => write!(f, "error registering block device: {:?}", e), - &Error::VhostNetDeviceNew(ref e) => write!(f, "failed to set up vhost networking: {:?}", e), - &Error::RegisterVsock(ref e) => write!(f, "error registering virtual socket device: {:?}", e), - &Error::NetDeviceNew(ref e) => write!(f, "failed to set up virtio networking: {:?}", e), - &Error::NoVarEmpty => write!(f, "/var/empty doesn't exist, can't jail devices."), - &Error::DeviceJail(ref e) => write!(f, "failed to jail device: {}", e), - &Error::DevicePivotRoot(ref e) => write!(f, "failed to pivot root device: {}", e), - &Error::VhostVsockDeviceNew(ref e) => write!(f, "failed to set up virtual socket device: {:?}", e), - &Error::RegisterNet(ref e) => write!(f, "error registering net device: {:?}", e), - &Error::RegisterRng(ref e) => write!(f, "error registering rng device: {:?}", e), - &Error::RngDeviceNew(ref e) => write!(f, "failed to set up rng: {:?}", e), - &Error::RegisterWayland(ref e) => write!(f, "error registering wayland device: {}", e), - &Error::SettingUidMap(ref e) => write!(f, "error setting UID map: {}", e), - &Error::SettingGidMap(ref e) => write!(f, "error setting GID map: {}", e), - &Error::ChownWaylandRoot(ref e) => write!(f, "error chowning wayland root directory: {:?}", e), - &Error::Cmdline(ref e) => write!(f, "the given kernel command line was invalid: {}", e), - &Error::GetWaylandGroup(ref e) => write!(f, "could not find gid for wayland group: {:?}", e), - &Error::RegisterIrqfd(ref e) => write!(f, "error registering irqfd: {:?}", e), - &Error::KernelLoader(ref e) => write!(f, "error loading kernel: {:?}", e), - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - &Error::ConfigureSystem(ref e) => write!(f, "error configuring system: {:?}", e), - &Error::EventFd(ref e) => write!(f, "error creating EventFd: {:?}", e), - &Error::SignalFd(ref e) => write!(f, "error with SignalFd: {:?}", e), - &Error::Kvm(ref e) => write!(f, "error creating Kvm: {:?}", e), - &Error::Vm(ref e) => write!(f, "error creating Vm: {:?}", e), - &Error::Vcpu(ref e) => write!(f, "error creating Vcpu: {:?}", e), - &Error::SpawnVcpu(ref e) => write!(f, "error creating spawning Vcpu: {}", e), - &Error::Sys(ref e) => write!(f, "error with system call: {:?}", e), - } - } -} +use vm_control::VmRequest; -type Result<T> = std::result::Result<T, Error>; - -struct UnlinkUnixDatagram(UnixDatagram); -impl AsRef<UnixDatagram> for UnlinkUnixDatagram { - fn as_ref(&self) -> &UnixDatagram{ - &self.0 - } -} -impl Drop for UnlinkUnixDatagram { - fn drop(&mut self) { - if let Ok(addr) = self.0.local_addr() { - if let Some(path) = addr.as_pathname() { - if let Err(e) = remove_file(path) { - warn!("failed to remove control socket file: {:?}", e); - } - } - } - } -} +static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm"; struct DiskOption { path: PathBuf, writable: bool, } -struct Config { +pub struct Config { disks: Vec<DiskOption>, vcpu_count: Option<u32>, memory: Option<usize>, @@ -204,38 +81,6 @@ impl Default for Config { } } -const KERNEL_START_OFFSET: usize = 0x200000; -const CMDLINE_OFFSET: usize = 0x20000; -const CMDLINE_MAX_SIZE: usize = KERNEL_START_OFFSET - CMDLINE_OFFSET; -const BASE_DEV_MEMORY_PFN: u64 = 1u64 << 26; - -static SECCOMP_POLICY_DIR: &'static str = "/usr/share/policy/crosvm"; - -fn create_base_minijail(root: &Path, seccomp_policy: &Path) -> Result<Minijail> { - // All child jails run in a new user namespace without any users mapped, - // they run as nobody unless otherwise configured. - let mut j = Minijail::new().map_err(|e| Error::DeviceJail(e))?; - j.namespace_pids(); - j.namespace_user(); - j.namespace_user_disable_setgroups(); - // Don't need any capabilities. - j.use_caps(0); - // Create a new mount namespace with an empty root FS. - j.namespace_vfs(); - j.enter_pivot_root(root) - .map_err(|e| Error::DevicePivotRoot(e))?; - // Run in an empty network namespace. - j.namespace_net(); - // Apply the block device seccomp policy. - j.no_new_privs(); - j.parse_seccomp_filters(seccomp_policy) - .map_err(|e| Error::DeviceJail(e))?; - j.use_seccomp_filter(); - // Don't do init setup. - j.run_as_init(); - Ok(j) -} - // Wait for all children to exit. Return true if they have all exited, false // otherwise. fn wait_all_children() -> bool { @@ -264,516 +109,6 @@ fn wait_all_children() -> bool { return false; } -fn run_config(cfg: Config) -> Result<()> { - static DEFAULT_PIVOT_ROOT: &'static str = "/var/empty"; - - if cfg.multiprocess { - // Printing something to the syslog before entering minijail so that libc's syslogger has a - // chance to open files necessary for its operation, like `/etc/localtime`. After jailing, - // access to those files will not be possible. - info!("crosvm entering multiprocess mode"); - } - - let kernel_image = File::open(cfg.kernel_path.as_path()) - .map_err(|e| Error::OpenKernel(cfg.kernel_path.clone(), e))?; - - let mut control_sockets = Vec::new(); - if let Some(ref path) = cfg.socket_path { - let path = Path::new(path); - let control_socket = UnixDatagram::bind(path).map_err(|e| Error::Socket(e))?; - control_sockets.push(UnlinkUnixDatagram(control_socket)); - } - - let mem_size = cfg.memory.unwrap_or(256) << 20; - #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - let arch_mem_regions = vec![(GuestAddress(0), mem_size)]; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - let arch_mem_regions = x86_64::arch_memory_regions(mem_size); - let guest_mem = - GuestMemory::new(&arch_mem_regions).expect("new mmap failed"); - - let mut cmdline = kernel_cmdline::Cmdline::new(CMDLINE_MAX_SIZE); - cmdline - .insert_str("console=ttyS0 noacpi reboot=k panic=1 pci=off") - .unwrap(); - - let mut device_manager = DeviceManager::new(guest_mem.clone(), 0x1000, 0xd0000000, 5); - - // An empty directory for jailed device's pivot root. - let empty_root_path = Path::new(DEFAULT_PIVOT_ROOT); - if cfg.multiprocess && !empty_root_path.exists() { - return Err(Error::NoVarEmpty); - } - - for disk in cfg.disks { - let disk_image = OpenOptions::new() - .read(true) - .write(disk.writable) - .open(disk.path) - .map_err(|e| Error::Disk(e))?; - - let block_box = Box::new(devices::virtio::Block::new(disk_image) - .map_err(|e| Error::BlockDeviceNew(e))?); - let jail = if cfg.multiprocess { - let policy_path: PathBuf = cfg.seccomp_policy_dir.join("block_device.policy"); - Some(create_base_minijail(empty_root_path, &policy_path)?) - } - else { - None - }; - - device_manager.register_mmio(block_box, jail, &mut cmdline) - .map_err(Error::RegisterBlock)?; - } - - let rng_box = Box::new(devices::virtio::Rng::new().map_err(Error::RngDeviceNew)?); - let rng_jail = if cfg.multiprocess { - let policy_path: PathBuf = cfg.seccomp_policy_dir.join("rng_device.policy"); - Some(create_base_minijail(empty_root_path, &policy_path)?) - } else { - None - }; - device_manager.register_mmio(rng_box, rng_jail, &mut cmdline) - .map_err(Error::RegisterRng)?; - - // We checked above that if the IP is defined, then the netmask is, too. - if let Some(host_ip) = cfg.host_ip { - if let Some(netmask) = cfg.netmask { - let net_box: Box<devices::virtio::VirtioDevice> = if cfg.vhost_net { - Box::new(devices::virtio::vhost::Net::new(host_ip, netmask, &guest_mem) - .map_err(|e| Error::VhostNetDeviceNew(e))?) - } else { - Box::new(devices::virtio::Net::new(host_ip, netmask) - .map_err(|e| Error::NetDeviceNew(e))?) - }; - - let jail = if cfg.multiprocess { - let policy_path: PathBuf = if cfg.vhost_net { - cfg.seccomp_policy_dir.join("vhost_net_device.policy") - } else { - cfg.seccomp_policy_dir.join("net_device.policy") - }; - - Some(create_base_minijail(empty_root_path, &policy_path)?) - } - else { - None - }; - - device_manager.register_mmio(net_box, jail, &mut cmdline).map_err(Error::RegisterNet)?; - } - } - - let wl_root = TempDir::new(&PathBuf::from("/tmp/wl_root"))?; - if let Some(wayland_socket_path) = cfg.wayland_socket_path { - let jailed_wayland_path = Path::new("/wayland-0"); - - let (host_socket, device_socket) = UnixDatagram::pair().map_err(Error::Socket)?; - control_sockets.push(UnlinkUnixDatagram(host_socket)); - let wl_box = Box::new(devices::virtio::Wl::new(if cfg.multiprocess { - &jailed_wayland_path - } else { - wayland_socket_path.as_path() - }, - device_socket)?); - - let jail = if cfg.multiprocess { - let wl_root_path = wl_root.as_path().unwrap(); // Won't fail if new succeeded. - let policy_path: PathBuf = cfg.seccomp_policy_dir.join("wl_device.policy"); - let mut jail = create_base_minijail(wl_root_path, &policy_path)?; - - // Bind mount the wayland socket into jail's root. This is necessary since each - // new wayland context must open() the socket. - jail.mount_bind(wayland_socket_path.as_path(), jailed_wayland_path, true) - .unwrap(); - - // Set the uid/gid for the jailed process, and give a basic id map. This - // is required for the above bind mount to work. - let wayland_group = cfg.wayland_group.unwrap_or(String::from("wayland")); - let wayland_cstr = CString::new(wayland_group.into_bytes()).unwrap(); - let wayland_gid = get_group_id(&wayland_cstr) - .map_err(Error::GetWaylandGroup)?; - - let crosvm_user_group = CStr::from_bytes_with_nul(b"crosvm\0").unwrap(); - let crosvm_uid = match get_user_id(&crosvm_user_group) { - Ok(u) => u, - Err(e) => { - warn!("falling back to current user id for Wayland: {:?}", e); - geteuid() - } - }; - let crosvm_gid = match get_group_id(&crosvm_user_group) { - Ok(u) => u, - Err(e) => { - warn!("falling back to current group id for Wayland: {:?}", e); - getegid() - } - }; - jail.change_uid(crosvm_uid); - jail.change_gid(wayland_gid); - jail.uidmap(&format!("{0} {0} 1", crosvm_uid)) - .map_err(Error::SettingUidMap)?; - jail.gidmap(&format!("{0} {0} 1", wayland_gid)) - .map_err(Error::SettingGidMap)?; - - // chown the root directory for the jail so we can actually bind mount the socket. - let wayland_root_cstr = CString::new(wl_root_path.as_os_str().to_str().unwrap()) - .unwrap(); - chown(&wayland_root_cstr, crosvm_uid, crosvm_gid) - .map_err(Error::ChownWaylandRoot)?; - - Some(jail) - } else { - None - }; - device_manager - .register_mmio(wl_box, jail, &mut cmdline) - .map_err(Error::RegisterWayland)?; - } - - if let Some(cid) = cfg.cid { - let vsock_box = Box::new(devices::virtio::vhost::Vsock::new(cid, &guest_mem) - .map_err(|e| Error::VhostVsockDeviceNew(e))?); - - let jail = if cfg.multiprocess { - let policy_path: PathBuf = cfg.seccomp_policy_dir.join("vhost_vsock_device.policy"); - - Some(create_base_minijail(empty_root_path, &policy_path)?) - } else { - None - }; - - device_manager.register_mmio(vsock_box, jail, &mut cmdline).map_err(Error::RegisterVsock)?; - } - - if !cfg.params.is_empty() { - cmdline - .insert_str(cfg.params) - .map_err(|e| Error::Cmdline(e))?; - } - - run_kvm(device_manager.vm_requests, - kernel_image, - &CString::new(cmdline).unwrap(), - cfg.vcpu_count.unwrap_or(1), - guest_mem, - &device_manager.bus, - control_sockets) -} - -fn run_kvm(requests: Vec<VmRequest>, - mut kernel_image: File, - cmdline: &CStr, - vcpu_count: u32, - guest_mem: GuestMemory, - mmio_bus: &devices::Bus, - control_sockets: Vec<UnlinkUnixDatagram>) - -> Result<()> { - let kvm = Kvm::new().map_err(Error::Kvm)?; - let kernel_start_addr = GuestAddress(KERNEL_START_OFFSET); - let cmdline_addr = GuestAddress(CMDLINE_OFFSET); - - let mut vm = Vm::new(&kvm, guest_mem).map_err(Error::Vm)?; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - { - let tss_addr = GuestAddress(0xfffbd000); - vm.set_tss_addr(tss_addr).expect("set tss addr failed"); - vm.create_pit().expect("create pit failed"); - } - vm.create_irq_chip().expect("create irq chip failed"); - - let mut next_dev_pfn = BASE_DEV_MEMORY_PFN; - for request in requests { - let mut running = false; - if let VmResponse::Err(e) = request.execute(&mut vm, &mut next_dev_pfn, &mut running) { - return Err(Error::Vm(e)); - } - if !running { - info!("configuration requested exit"); - return Ok(()); - } - } - - kernel_loader::load_kernel(vm.get_memory(), kernel_start_addr, &mut kernel_image)?; - kernel_loader::load_cmdline(vm.get_memory(), cmdline_addr, cmdline)?; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - x86_64::configure_system(vm.get_memory(), - kernel_start_addr, - cmdline_addr, - cmdline.to_bytes().len() + 1, - vcpu_count as u8)?; - - let mut io_bus = devices::Bus::new(); - - let exit_evt = EventFd::new().expect("failed to create exit eventfd"); - - // Masking signals is inherently dangerous, since this can persist across - // clones/execs. Do this after any jailed devices have been spawned, but - // before the vcpus spawn so they also inherit the masking for SIGCHLD. - let sigchld_fd = SignalFd::new(libc::SIGCHLD) - .expect("failed to create child signalfd"); - - struct NoDevice; - impl devices::BusDevice for NoDevice {} - - let com_evt_1_3 = EventFd::new().map_err(Error::EventFd)?; - let com_evt_2_4 = EventFd::new().map_err(Error::EventFd)?; - let stdio_serial = - Arc::new(Mutex::new( - devices::Serial::new_out(com_evt_1_3.try_clone().map_err(Error::EventFd)?, - Box::new(stdout())))); - let nul_device = Arc::new(Mutex::new(NoDevice)); - io_bus.insert(stdio_serial.clone(), 0x3f8, 0x8).unwrap(); - io_bus - .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_2_4 - .try_clone() - .map_err(Error::EventFd)?))), - 0x2f8, - 0x8) - .unwrap(); - io_bus - .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_1_3 - .try_clone() - .map_err(Error::EventFd)?))), - 0x3e8, - 0x8) - .unwrap(); - io_bus - .insert(Arc::new(Mutex::new(devices::Serial::new_sink(com_evt_2_4 - .try_clone() - .map_err(Error::EventFd)?))), - 0x2e8, - 0x8) - .unwrap(); - io_bus - .insert(Arc::new(Mutex::new(devices::Cmos::new())), 0x70, 0x2) - .unwrap(); - io_bus - .insert(Arc::new(Mutex::new(devices::I8042Device::new(exit_evt - .try_clone() - .map_err(Error::EventFd)?))), - 0x061, - 0x4) - .unwrap(); - io_bus.insert(nul_device.clone(), 0x040, 0x8).unwrap(); // ignore pit - io_bus.insert(nul_device.clone(), 0x0ed, 0x1).unwrap(); // most likely this one does nothing - io_bus.insert(nul_device.clone(), 0x0f0, 0x2).unwrap(); // ignore fpu - io_bus.insert(nul_device.clone(), 0xcf8, 0x8).unwrap(); // ignore pci - - vm.register_irqfd(&com_evt_1_3, 4) - .map_err(Error::RegisterIrqfd)?; - vm.register_irqfd(&com_evt_2_4, 3) - .map_err(Error::RegisterIrqfd)?; - - let kill_signaled = Arc::new(AtomicBool::new(false)); - let mut vcpu_handles = Vec::with_capacity(vcpu_count as usize); - let vcpu_thread_barrier = Arc::new(Barrier::new((vcpu_count + 1) as usize)); - for cpu_id in 0..vcpu_count { - let mmio_bus = mmio_bus.clone(); - let io_bus = io_bus.clone(); - let kill_signaled = kill_signaled.clone(); - let vcpu_thread_barrier = vcpu_thread_barrier.clone(); - let vcpu_exit_evt = exit_evt.try_clone().map_err(Error::EventFd)?; - let vcpu = Vcpu::new(cpu_id as libc::c_ulong, &kvm, &vm).map_err(Error::Vcpu)?; - #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - x86_64::configure_vcpu(vm.get_memory(), - kernel_start_addr, - &kvm, - &vcpu, - cpu_id as u64, - vcpu_count as u64)?; - vcpu_handles.push(thread::Builder::new() - .name(format!("crosvm_vcpu{}", cpu_id)) - .spawn(move || { - unsafe { - extern "C" fn handle_signal() {} - // Our signal handler does nothing and is trivially async signal safe. - register_signal_handler(0, handle_signal) - .expect("failed to register vcpu signal handler"); - } - - vcpu_thread_barrier.wait(); - loop { - let run_res = vcpu.run(); - match run_res { - Ok(run) => { - match run { - VcpuExit::IoIn(addr, data) => { - io_bus.read(addr as u64, data); - } - VcpuExit::IoOut(addr, data) => { - io_bus.write(addr as u64, data); - } - VcpuExit::MmioRead(addr, data) => { - mmio_bus.read(addr, data); - } - VcpuExit::MmioWrite(addr, data) => { - mmio_bus.write(addr, data); - } - VcpuExit::Hlt => break, - VcpuExit::Shutdown => break, - r => warn!("unexpected vcpu exit: {:?}", r), - } - } - Err(e) => { - match e.errno() { - libc::EAGAIN | libc::EINTR => {}, - _ => { - error!("vcpu hit unknown error: {:?}", e); - break; - } - } - } - } - if kill_signaled.load(Ordering::SeqCst) { - break; - } - } - vcpu_exit_evt - .write(1) - .expect("failed to signal vcpu exit eventfd"); - }).map_err(Error::SpawnVcpu)?); - } - - vcpu_thread_barrier.wait(); - - run_control(vm, - control_sockets, - next_dev_pfn, - stdio_serial, - exit_evt, - sigchld_fd, - kill_signaled, - vcpu_handles) -} - -fn run_control(mut vm: Vm, - control_sockets: Vec<UnlinkUnixDatagram>, - mut next_dev_pfn: u64, - stdio_serial: Arc<Mutex<devices::Serial>>, - exit_evt: EventFd, - sigchld_fd: SignalFd, - kill_signaled: Arc<AtomicBool>, - vcpu_handles: Vec<JoinHandle<()>>) - -> Result<()> { - const MAX_VM_FD_RECV: usize = 1; - - const EXIT: u32 = 0; - const STDIN: u32 = 1; - const CHILD_SIGNAL: u32 = 2; - const VM_BASE: u32 = 3; - - let stdin_handle = stdin(); - let stdin_lock = stdin_handle.lock(); - stdin_lock - .set_raw_mode() - .expect("failed to set terminal raw mode"); - - let mut pollables = Vec::new(); - pollables.push((EXIT, &exit_evt as &Pollable)); - pollables.push((STDIN, &stdin_lock as &Pollable)); - pollables.push((CHILD_SIGNAL, &sigchld_fd as &Pollable)); - for (i, socket) in control_sockets.iter().enumerate() { - pollables.push((VM_BASE + i as u32, socket.as_ref() as &Pollable)); - } - - let mut poller = Poller::new(pollables.len()); - let mut scm = Scm::new(MAX_VM_FD_RECV); - - 'poll: loop { - let tokens = { - match poller.poll(&pollables[..]) { - Ok(v) => v, - Err(e) => { - error!("failed to poll: {:?}", e); - break; - } - } - }; - for &token in tokens { - match token { - EXIT => { - info!("vcpu requested shutdown"); - break 'poll; - } - STDIN => { - let mut out = [0u8; 64]; - match stdin_lock.read_raw(&mut out[..]) { - Ok(0) => { - // Zero-length read indicates EOF. Remove from pollables. - pollables.retain(|&pollable| pollable.0 != STDIN); - }, - Err(e) => { - warn!("error while reading stdin: {:?}", e); - pollables.retain(|&pollable| pollable.0 != STDIN); - }, - Ok(count) => { - stdio_serial - .lock() - .unwrap() - .queue_input_bytes(&out[..count]) - .expect("failed to queue bytes into serial port"); - }, - } - } - CHILD_SIGNAL => { - // Print all available siginfo structs, then exit the loop. - loop { - let result = sigchld_fd.read().map_err(Error::SignalFd)?; - if let Some(siginfo) = result { - error!("child {} died: signo {}, status {}, code {}", - siginfo.ssi_pid, - siginfo.ssi_signo, - siginfo.ssi_status, - siginfo.ssi_code); - } - break 'poll; - } - } - t if t >= VM_BASE && t < VM_BASE + (control_sockets.len() as u32) => { - let socket = &control_sockets[(t - VM_BASE) as usize]; - match VmRequest::recv(&mut scm, socket.as_ref()) { - Ok(request) => { - let mut running = true; - let response = - request.execute(&mut vm, &mut next_dev_pfn, &mut running); - if let Err(e) = response.send(&mut scm, socket.as_ref()) { - error!("failed to send VmResponse: {:?}", e); - } - if !running { - info!("control socket requested exit"); - break 'poll; - } - } - Err(e) => error!("failed to recv VmRequest: {:?}", e), - } - } - _ => {} - } - } - } - - // vcpu threads MUST see the kill signaled flag, otherwise they may - // re-enter the VM. - kill_signaled.store(true, Ordering::SeqCst); - for handle in vcpu_handles { - match handle.kill(0) { - Ok(_) => { - if let Err(e) = handle.join() { - error!("failed to join vcpu thread: {:?}", e); - } - } - Err(e) => error!("failed to kill vcpu thread: {:?}", e), - } - } - - stdin_lock - .set_canon_mode() - .expect("failed to restore canonical mode for terminal"); - - Ok(()) -} - fn set_argument(cfg: &mut Config, name: &str, value: Option<&str>) -> argument::Result<()> { match name { "" => { @@ -1014,7 +349,7 @@ fn run_vm(args: std::env::Args) { match match_res { Ok(_) => { - match run_config(cfg) { + match linux::run_config(cfg) { Ok(_) => info!("crosvm has exited normally"), Err(e) => error!("{}", e), } |