blob: 3920e4d6220279773f2d09e000a669dbe9205055 [file] [log] [blame]
// Copyright 2022 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::thread::sleep;
use std::time::Duration;
use anyhow::anyhow;
use anyhow::Context;
use base::kill_process_group;
use base::reap_child;
use base::syslog;
use base::syslog::LogConfig;
use base::warn;
use devices::virtio::vhost::user::device::run_console_device;
use devices::virtio::vhost::user::device::run_fs_device;
#[cfg(feature = "gpu")]
use devices::virtio::vhost::user::device::run_gpu_device;
#[cfg(feature = "audio")]
use devices::virtio::vhost::user::device::run_snd_device;
use devices::virtio::vhost::user::device::run_vsock_device;
use devices::virtio::vhost::user::device::run_wl_device;
use crate::crosvm::sys::cmdline::Commands;
use crate::crosvm::sys::cmdline::DeviceSubcommand;
use crate::crosvm::sys::unix::start_devices;
use crate::CommandStatus;
use crate::Config;
pub(crate) fn start_device(command: DeviceSubcommand) -> anyhow::Result<()> {
match command {
DeviceSubcommand::Console(cfg) => run_console_device(cfg),
#[cfg(feature = "audio")]
DeviceSubcommand::Snd(cfg) => run_snd_device(cfg),
DeviceSubcommand::Fs(cfg) => run_fs_device(cfg),
#[cfg(feature = "gpu")]
DeviceSubcommand::Gpu(cfg) => run_gpu_device(cfg),
DeviceSubcommand::Vsock(cfg) => run_vsock_device(cfg),
DeviceSubcommand::Wl(cfg) => run_wl_device(cfg),
}
}
// Wait for all children to exit. Return true if they have all exited, false
// otherwise.
fn wait_all_children() -> bool {
const CHILD_WAIT_MAX_ITER: isize = 100;
const CHILD_WAIT_MS: u64 = 10;
for _ in 0..CHILD_WAIT_MAX_ITER {
loop {
match reap_child() {
Ok(0) => break,
// We expect ECHILD which indicates that there were no children left.
Err(e) if e.errno() == libc::ECHILD => return true,
Err(e) => {
warn!("error while waiting for children: {}", e);
return false;
}
// We reaped one child, so continue reaping.
_ => {}
}
}
// There's no timeout option for waitpid which reap_child calls internally, so our only
// recourse is to sleep while waiting for the children to exit.
sleep(Duration::from_millis(CHILD_WAIT_MS));
}
// If we've made it to this point, not all of the children have exited.
false
}
pub(crate) fn cleanup() {
// Reap exit status from any child device processes. At this point, all devices should have been
// dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
// take some time for the processes to shut down.
if !wait_all_children() {
// We gave them a chance, and it's too late.
warn!("not all child processes have exited; sending SIGKILL");
if let Err(e) = kill_process_group() {
// We're now at the mercy of the OS to clean up after us.
warn!("unable to kill all child processes: {}", e);
}
}
}
pub fn get_library_watcher() -> std::io::Result<()> {
Ok(())
}
pub(crate) fn run_command(command: Commands) -> anyhow::Result<()> {
match command {
Commands::Devices(cmd) => start_devices(cmd).context("start_devices subcommand failed"),
}
}
pub(crate) fn init_log<F: 'static>(log_config: LogConfig<F>, _cfg: &Config) -> anyhow::Result<()>
where
F: Fn(&mut syslog::fmt::Formatter, &log::Record<'_>) -> std::io::Result<()> + Sync + Send,
{
if let Err(e) = syslog::init_with(log_config) {
eprintln!("failed to initialize syslog: {}", e);
return Err(anyhow!("failed to initialize syslog: {}", e));
}
Ok(())
}
pub(crate) fn error_to_exit_code(_res: &std::result::Result<CommandStatus, anyhow::Error>) -> i32 {
1
}