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