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
5 use std::thread::sleep;
6 use std::time::Duration;
7
8 use anyhow::anyhow;
9 use anyhow::Context;
10 use base::kill_process_group;
11 use base::reap_child;
12 use base::syslog;
13 use base::syslog::LogArgs;
14 use base::syslog::LogConfig;
15 use base::warn;
16 use devices::virtio::vhost::user::device::run_console_device;
17 use devices::virtio::vhost::user::device::run_fs_device;
18 use devices::virtio::vhost::user::device::run_vsock_device;
19 use devices::virtio::vhost::user::device::run_wl_device;
20
21 use crate::crosvm::sys::cmdline::Commands;
22 use crate::crosvm::sys::cmdline::DeviceSubcommand;
23 use crate::crosvm::sys::linux::start_devices;
24 use crate::CommandStatus;
25 use crate::Config;
26
start_device(command: DeviceSubcommand) -> anyhow::Result<()>27 pub(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.
wait_all_children() -> bool38 fn 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
cleanup()64 pub(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
get_library_watcher() -> std::io::Result<()>78 pub fn get_library_watcher() -> std::io::Result<()> {
79 Ok(())
80 }
81
run_command(command: Commands, _log_args: LogArgs) -> anyhow::Result<()>82 pub(crate) fn run_command(command: Commands, _log_args: LogArgs) -> anyhow::Result<()> {
83 match command {
84 Commands::Devices(cmd) => start_devices(cmd).context("start_devices subcommand failed"),
85 }
86 }
87
init_log(log_config: LogConfig, _cfg: &Config) -> anyhow::Result<()>88 pub(crate) fn init_log(log_config: LogConfig, _cfg: &Config) -> anyhow::Result<()> {
89 if let Err(e) = syslog::init_with(log_config) {
90 eprintln!("failed to initialize syslog: {}", e);
91 return Err(anyhow!("failed to initialize syslog: {}", e));
92 }
93 Ok(())
94 }
95
error_to_exit_code(_res: &std::result::Result<CommandStatus, anyhow::Error>) -> i3296 pub(crate) fn error_to_exit_code(_res: &std::result::Result<CommandStatus, anyhow::Error>) -> i32 {
97 1
98 }
99