crosvm/sys/linux/
main.rs

1// Copyright 2022 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::thread::sleep;
6use std::time::Duration;
7
8use anyhow::anyhow;
9use anyhow::Context;
10use base::kill_process_group;
11use base::reap_child;
12use base::syslog;
13use base::syslog::LogArgs;
14use base::syslog::LogConfig;
15use base::warn;
16use device_virtio_console::vhost_user::run_console_device;
17use devices::virtio::vhost_user_backend::run_fs_device;
18use devices::virtio::vhost_user_backend::run_vsock_device;
19#[cfg(feature = "virtio_wl")]
20use devices::virtio::vhost_user_backend::run_wl_device;
21
22use crate::crosvm::sys::cmdline::Commands;
23use crate::crosvm::sys::cmdline::DeviceSubcommand;
24use crate::crosvm::sys::linux::start_devices;
25use crate::CommandStatus;
26use crate::Config;
27
28pub(crate) fn start_device(command: DeviceSubcommand) -> anyhow::Result<()> {
29    match command {
30        DeviceSubcommand::Console(cfg) => run_console_device(cfg),
31        DeviceSubcommand::Fs(cfg) => run_fs_device(cfg),
32        DeviceSubcommand::Vsock(cfg) => run_vsock_device(cfg),
33        #[cfg(feature = "virtio_wl")]
34        DeviceSubcommand::Wl(cfg) => run_wl_device(cfg),
35    }
36}
37
38// Wait for all children to exit. Return true if they have all exited, false
39// otherwise.
40fn wait_all_children() -> bool {
41    const CHILD_WAIT_MAX_ITER: isize = 100;
42    const CHILD_WAIT_MS: u64 = 10;
43    for _ in 0..CHILD_WAIT_MAX_ITER {
44        loop {
45            match reap_child() {
46                Ok(0) => break,
47                // We expect ECHILD which indicates that there were no children left.
48                Err(e) if e.errno() == libc::ECHILD => return true,
49                Err(e) => {
50                    warn!("error while waiting for children: {}", e);
51                    return false;
52                }
53                // We reaped one child, so continue reaping.
54                _ => {}
55            }
56        }
57        // There's no timeout option for waitpid which reap_child calls internally, so our only
58        // recourse is to sleep while waiting for the children to exit.
59        sleep(Duration::from_millis(CHILD_WAIT_MS));
60    }
61
62    // If we've made it to this point, not all of the children have exited.
63    false
64}
65
66pub(crate) fn cleanup() {
67    // Reap exit status from any child device processes. At this point, all devices should have been
68    // dropped in the main process and told to shutdown. Try over a period of 100ms, since it may
69    // take some time for the processes to shut down.
70    if !wait_all_children() {
71        // We gave them a chance, and it's too late.
72        warn!("not all child processes have exited; sending SIGKILL");
73        if let Err(e) = kill_process_group() {
74            // We're now at the mercy of the OS to clean up after us.
75            warn!("unable to kill all child processes: {}", e);
76        }
77    }
78}
79
80pub(crate) fn run_command(command: Commands, _log_args: LogArgs) -> anyhow::Result<()> {
81    match command {
82        Commands::Devices(cmd) => start_devices(cmd).context("start_devices subcommand failed"),
83    }
84}
85
86pub(crate) fn init_log(log_config: LogConfig, _cfg: &Config) -> anyhow::Result<()> {
87    if let Err(e) = syslog::init_with(log_config) {
88        eprintln!("failed to initialize syslog: {e}");
89        return Err(anyhow!("failed to initialize syslog: {}", e));
90    }
91    Ok(())
92}
93
94pub(crate) fn error_to_exit_code(_res: &std::result::Result<CommandStatus, anyhow::Error>) -> i32 {
95    1
96}