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.
new( child: SharedChild, cid: Cid, requester_uid: u32, requester_sid: String, requester_debug_pid: i32, ) -> VmInstance53 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.
start( config: &VmConfig, cid: Cid, log_fd: Option<File>, requester_uid: u32, requester_sid: String, requester_debug_pid: i32, ) -> Result<Arc<VmInstance>, Error>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.
monitor(&self)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.
running(&self) -> bool110 pub fn running(&self) -> bool {
111 self.running.load(Ordering::Acquire)
112 }
113
114 /// Kill the crosvm instance.
kill(&self)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.
run_vm(config: &VmConfig, cid: Cid, log_fd: Option<File>) -> Result<SharedChild, Error>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