1 // Copyright 2021, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! Functions for running instances of `crosvm`.
16 
17 use crate::aidl::VirtualMachineCallbacks;
18 use crate::config::VmConfig;
19 use crate::Cid;
20 use anyhow::Error;
21 use log::{error, info};
22 use shared_child::SharedChild;
23 use std::fs::File;
24 use std::process::Command;
25 use std::sync::atomic::{AtomicBool, Ordering};
26 use std::sync::Arc;
27 use std::thread;
28 
29 const CROSVM_PATH: &str = "/apex/com.android.virt/bin/crosvm";
30 
31 /// Information about a particular instance of a VM which is running.
32 #[derive(Debug)]
33 pub struct VmInstance {
34     /// The crosvm child process.
35     child: SharedChild,
36     /// The CID assigned to the VM for vsock communication.
37     pub cid: Cid,
38     /// The UID of the process which requested the VM.
39     pub requester_uid: u32,
40     /// The SID of the process which requested the VM.
41     pub requester_sid: String,
42     /// The PID of the process which requested the VM. Note that this process may no longer exist
43     /// and the PID may have been reused for a different process, so this should not be trusted.
44     pub requester_debug_pid: i32,
45     /// Whether the VM is still running.
46     running: AtomicBool,
47     /// Callbacks to clients of the VM.
48     pub callbacks: VirtualMachineCallbacks,
49 }
50 
51 impl VmInstance {
52     /// Create a new `VmInstance` for the given process.
53     fn new(
54         child: SharedChild,
55         cid: Cid,
56         requester_uid: u32,
57         requester_sid: String,
58         requester_debug_pid: i32,
59     ) -> VmInstance {
60         VmInstance {
61             child,
62             cid,
63             requester_uid,
64             requester_sid,
65             requester_debug_pid,
66             running: AtomicBool::new(true),
67             callbacks: Default::default(),
68         }
69     }
70 
71     /// Start an instance of `crosvm` to manage a new VM. The `crosvm` instance will be killed when
72     /// the `VmInstance` is dropped.
73     pub fn start(
74         config: &VmConfig,
75         cid: Cid,
76         log_fd: Option<File>,
77         requester_uid: u32,
78         requester_sid: String,
79         requester_debug_pid: i32,
80     ) -> Result<Arc<VmInstance>, Error> {
81         let child = run_vm(config, cid, log_fd)?;
82         let instance = Arc::new(VmInstance::new(
83             child,
84             cid,
85             requester_uid,
86             requester_sid,
87             requester_debug_pid,
88         ));
89 
90         let instance_clone = instance.clone();
91         thread::spawn(move || {
92             instance_clone.monitor();
93         });
94 
95         Ok(instance)
96     }
97 
98     /// Wait for the crosvm child process to finish, then mark the VM as no longer running and call
99     /// any callbacks.
100     fn monitor(&self) {
101         match self.child.wait() {
102             Err(e) => error!("Error waiting for crosvm instance to die: {}", e),
103             Ok(status) => info!("crosvm exited with status {}", status),
104         }
105         self.running.store(false, Ordering::Release);
106         self.callbacks.callback_on_died(self.cid);
107     }
108 
109     /// Return whether `crosvm` is still running the VM.
110     pub fn running(&self) -> bool {
111         self.running.load(Ordering::Acquire)
112     }
113 
114     /// Kill the crosvm instance.
115     pub fn kill(&self) {
116         // TODO: Talk to crosvm to shutdown cleanly.
117         if let Err(e) = self.child.kill() {
118             error!("Error killing crosvm instance: {}", e);
119         }
120     }
121 }
122 
123 /// Start an instance of `crosvm` to manage a new VM.
124 fn run_vm(config: &VmConfig, cid: Cid, log_fd: Option<File>) -> Result<SharedChild, Error> {
125     config.validate()?;
126 
127     let mut command = Command::new(CROSVM_PATH);
128     // TODO(qwandor): Remove --disable-sandbox.
129     command.arg("run").arg("--disable-sandbox").arg("--cid").arg(cid.to_string());
130     if let Some(log_fd) = log_fd {
131         command.stdout(log_fd);
132     } else {
133         // Ignore console output.
134         command.arg("--serial=type=sink");
135     }
136     if let Some(bootloader) = &config.bootloader {
137         command.arg("--bios").arg(bootloader);
138     }
139     if let Some(initrd) = &config.initrd {
140         command.arg("--initrd").arg(initrd);
141     }
142     if let Some(params) = &config.params {
143         command.arg("--params").arg(params);
144     }
145     for disk in &config.disks {
146         command.arg(if disk.writable { "--rwdisk" } else { "--disk" }).arg(&disk.image);
147     }
148     if let Some(kernel) = &config.kernel {
149         command.arg(kernel);
150     }
151     info!("Running {:?}", command);
152     Ok(SharedChild::spawn(&mut command)?)
153 }
154