blob: 9f1d7d1be3417d3ec7ea4f4dde5598f560265730 [file] [log] [blame]
Andrew Walbrand0ef4002022-05-16 16:14:10 +00001// Copyright 2022, 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//! Client library for VirtualizationService.
16
17mod death_reason;
Alan Stokes2bead0d2022-09-05 16:58:34 +010018mod error_code;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000019mod errors;
20mod sync;
21
22pub use crate::death_reason::DeathReason;
Alan Stokes2bead0d2022-09-05 16:58:34 +010023pub use crate::error_code::ErrorCode;
Andrew Walbranc944fae2022-08-02 16:16:28 +000024pub use crate::errors::VmWaitError;
25use crate::sync::Monitor;
David Brazdil49f96f52022-12-16 21:29:13 +000026use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
27 DeathReason::DeathReason as AidlDeathReason, ErrorCode::ErrorCode as AidlErrorCode,
28};
Andrew Walbrand0ef4002022-05-16 16:14:10 +000029use android_system_virtualizationservice::{
30 aidl::android::system::virtualizationservice::{
Andrew Walbrand0ef4002022-05-16 16:14:10 +000031 IVirtualMachine::IVirtualMachine,
32 IVirtualMachineCallback::{BnVirtualMachineCallback, IVirtualMachineCallback},
33 IVirtualizationService::IVirtualizationService,
34 VirtualMachineConfig::VirtualMachineConfig,
35 VirtualMachineState::VirtualMachineState,
36 },
37 binder::{
Seungjae Yoo81821f62023-09-13 14:30:11 +090038 BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface, ParcelFileDescriptor,
39 Result as BinderResult, StatusCode, Strong,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000040 },
41};
David Brazdil46446062022-10-25 13:18:18 +010042use command_fds::CommandFdExt;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000043use log::warn;
David Brazdil46446062022-10-25 13:18:18 +010044use rpcbinder::{FileDescriptorTransportMode, RpcSession};
45use shared_child::SharedChild;
46use std::io::{self, Read};
47use std::process::Command;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000048use std::{
49 fmt::{self, Debug, Formatter},
50 fs::File,
David Brazdil46446062022-10-25 13:18:18 +010051 os::unix::io::{AsFd, AsRawFd, FromRawFd, IntoRawFd, OwnedFd},
Andrew Walbrand0ef4002022-05-16 16:14:10 +000052 sync::Arc,
53 time::Duration,
54};
55
David Brazdil46446062022-10-25 13:18:18 +010056const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
Alan Stokeseab0cdf2023-03-01 12:13:23 +000057const VIRTMGR_THREADS: usize = 2;
David Brazdil46446062022-10-25 13:18:18 +010058
59fn posix_pipe() -> Result<(OwnedFd, OwnedFd), io::Error> {
60 use nix::fcntl::OFlag;
61 use nix::unistd::pipe2;
62
63 // Create new POSIX pipe. Make it O_CLOEXEC to align with how Rust creates
64 // file descriptors (expected by SharedChild).
65 let (raw1, raw2) = pipe2(OFlag::O_CLOEXEC)?;
66
Andrew Walbranb58d1b42023-07-07 13:54:49 +010067 // SAFETY: Taking ownership of brand new FDs.
David Brazdil46446062022-10-25 13:18:18 +010068 unsafe { Ok((OwnedFd::from_raw_fd(raw1), OwnedFd::from_raw_fd(raw2))) }
69}
70
71fn posix_socketpair() -> Result<(OwnedFd, OwnedFd), io::Error> {
72 use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
73
74 // Create new POSIX socketpair, suitable for use with RpcBinder UDS bootstrap
75 // transport. Make it O_CLOEXEC to align with how Rust creates file
76 // descriptors (expected by SharedChild).
77 let (raw1, raw2) =
78 socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?;
79
Andrew Walbranb58d1b42023-07-07 13:54:49 +010080 // SAFETY: Taking ownership of brand new FDs.
David Brazdil46446062022-10-25 13:18:18 +010081 unsafe { Ok((OwnedFd::from_raw_fd(raw1), OwnedFd::from_raw_fd(raw2))) }
82}
83
84/// A running instance of virtmgr which is hosting a VirtualizationService
85/// RpcBinder server.
86pub struct VirtualizationService {
87 /// Client FD for UDS connection to virtmgr's RpcBinder server. Closing it
88 /// will make virtmgr shut down.
89 client_fd: OwnedFd,
90}
91
92impl VirtualizationService {
93 /// Spawns a new instance of virtmgr, a child process that will host
94 /// the VirtualizationService AIDL service.
95 pub fn new() -> Result<VirtualizationService, io::Error> {
96 let (wait_fd, ready_fd) = posix_pipe()?;
97 let (client_fd, server_fd) = posix_socketpair()?;
98
99 let mut command = Command::new(VIRTMGR_PATH);
100 command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
101 command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
102 command.preserved_fds(vec![server_fd.as_raw_fd(), ready_fd.as_raw_fd()]);
103
104 SharedChild::spawn(&mut command)?;
105
106 // Drop FDs that belong to virtmgr.
107 drop(server_fd);
108 drop(ready_fd);
109
110 // Wait for the child to signal that the RpcBinder server is ready
111 // by closing its end of the pipe.
Dan Albert04e5dbd2023-05-08 22:49:40 +0000112 let _ignored = File::from(wait_fd).read(&mut [0]);
David Brazdil46446062022-10-25 13:18:18 +0100113
114 Ok(VirtualizationService { client_fd })
115 }
116
117 /// Connects to the VirtualizationService AIDL service.
118 pub fn connect(&self) -> Result<Strong<dyn IVirtualizationService>, io::Error> {
119 let session = RpcSession::new();
120 session.set_file_descriptor_transport_mode(FileDescriptorTransportMode::Unix);
121 session.set_max_incoming_threads(VIRTMGR_THREADS);
David Brazdil46446062022-10-25 13:18:18 +0100122 session
123 .setup_unix_domain_bootstrap_client(self.client_fd.as_fd())
124 .map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))
125 }
126}
127
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000128/// A virtual machine which has been started by the VirtualizationService.
129pub struct VmInstance {
130 /// The `IVirtualMachine` Binder object representing the VM.
131 pub vm: Strong<dyn IVirtualMachine>,
132 cid: i32,
133 state: Arc<Monitor<VmState>>,
134 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
135 // is removed from the Binder when it's dropped.
136 _death_recipient: DeathRecipient,
137}
138
Alan Stokes0e82b502022-08-08 14:44:48 +0100139/// A trait to be implemented by clients to handle notification of significant changes to the VM
140/// state. Default implementations of all functions are provided so clients only need to handle the
141/// notifications they are interested in.
142#[allow(unused_variables)]
143pub trait VmCallback {
144 /// Called when the payload has been started within the VM. If present, `stream` is connected
145 /// to the stdin/stdout of the payload.
David Brazdil451cc962022-10-14 14:08:12 +0100146 fn on_payload_started(&self, cid: i32) {}
Alan Stokes0e82b502022-08-08 14:44:48 +0100147
148 /// Callend when the payload has notified Virtualization Service that it is ready to serve
149 /// clients.
150 fn on_payload_ready(&self, cid: i32) {}
151
152 /// Called when the payload has exited in the VM. `exit_code` is the exit code of the payload
153 /// process.
154 fn on_payload_finished(&self, cid: i32, exit_code: i32) {}
155
156 /// Called when an error has occurred in the VM. The `error_code` and `message` may give
157 /// further details.
Alan Stokes2bead0d2022-09-05 16:58:34 +0100158 fn on_error(&self, cid: i32, error_code: ErrorCode, message: &str) {}
Alan Stokes0e82b502022-08-08 14:44:48 +0100159
160 /// Called when the VM has exited, all resources have been freed, and any logs have been
161 /// written. `death_reason` gives an indication why the VM exited.
162 fn on_died(&self, cid: i32, death_reason: DeathReason) {}
163}
164
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000165impl VmInstance {
166 /// Creates (but doesn't start) a new VM with the given configuration.
167 pub fn create(
168 service: &dyn IVirtualizationService,
169 config: &VirtualMachineConfig,
Jiyong Parke6fb1672023-06-26 16:45:55 +0900170 console_out: Option<File>,
171 console_in: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000172 log: Option<File>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100173 callback: Option<Box<dyn VmCallback + Send + Sync>>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000174 ) -> BinderResult<Self> {
Jiyong Parke6fb1672023-06-26 16:45:55 +0900175 let console_out = console_out.map(ParcelFileDescriptor::new);
176 let console_in = console_in.map(ParcelFileDescriptor::new);
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000177 let log = log.map(ParcelFileDescriptor::new);
178
Jiyong Parke6fb1672023-06-26 16:45:55 +0900179 let vm =
180 service.createVm(config, console_out.as_ref(), console_in.as_ref(), log.as_ref())?;
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000181
182 let cid = vm.getCid()?;
183
184 // Register callback before starting VM, in case it dies immediately.
185 let state = Arc::new(Monitor::new(VmState::default()));
186 let callback = BnVirtualMachineCallback::new_binder(
Alan Stokes0e82b502022-08-08 14:44:48 +0100187 VirtualMachineCallback { state: state.clone(), client_callback: callback },
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000188 BinderFeatures::default(),
189 );
190 vm.registerCallback(&callback)?;
191 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
192
193 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
194 }
195
196 /// Starts the VM.
197 pub fn start(&self) -> BinderResult<()> {
198 self.vm.start()
199 }
200
201 /// Returns the CID used for vsock connections to the VM.
202 pub fn cid(&self) -> i32 {
203 self.cid
204 }
205
206 /// Returns the current lifecycle state of the VM.
207 pub fn state(&self) -> BinderResult<VirtualMachineState> {
208 self.vm.getState()
209 }
210
211 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
212 /// why it died.
213 pub fn wait_for_death(&self) -> DeathReason {
214 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
215 }
216
Alan Stokes71403772022-06-21 14:56:28 +0100217 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
218 /// Returns the reason why it died if it did so.
219 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
220 let (state, _timeout_result) =
221 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
222 // We don't care if it timed out - we just return the reason if there now is one
223 state.death_reason
224 }
225
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000226 /// Waits until the VM reports that it is ready.
227 ///
228 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
229 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
230 let (state, timeout_result) = self
231 .state
232 .wait_timeout_while(timeout, |state| {
233 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
234 })
235 .unwrap();
236 if timeout_result.timed_out() {
237 Err(VmWaitError::TimedOut)
238 } else if let Some(reason) = state.death_reason {
239 Err(VmWaitError::Died { reason })
240 } else if state.reported_state != VirtualMachineState::READY {
241 Err(VmWaitError::Finished)
242 } else {
243 Ok(())
244 }
245 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000246
247 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100248 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000249 &self,
250 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000251 ) -> Result<Strong<T>, StatusCode> {
David Brazdila2125dd2022-12-14 16:37:44 +0000252 RpcSession::new().setup_preconnected_client(|| {
Andrew Walbranc944fae2022-08-02 16:16:28 +0000253 match self.vm.connectVsock(port as i32) {
254 Ok(vsock) => {
255 // Ownership of the fd is transferred to binder
256 Some(vsock.into_raw_fd())
257 }
258 Err(e) => {
259 warn!("Vsock connection failed: {}", e);
260 None
261 }
262 }
263 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000264 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000265}
266
267impl Debug for VmInstance {
268 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
269 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
270 }
271}
272
273/// Notify the VmState when the given Binder object dies.
274///
275/// If the returned DeathRecipient is dropped then this will no longer do anything.
276fn wait_for_binder_death(
277 binder: &mut impl IBinder,
278 state: Arc<Monitor<VmState>>,
279) -> BinderResult<DeathRecipient> {
280 let mut death_recipient = DeathRecipient::new(move || {
281 warn!("VirtualizationService unexpectedly died");
282 state.notify_death(DeathReason::VirtualizationServiceDied);
283 });
284 binder.link_to_death(&mut death_recipient)?;
285 Ok(death_recipient)
286}
287
288#[derive(Debug, Default)]
289struct VmState {
290 death_reason: Option<DeathReason>,
291 reported_state: VirtualMachineState,
292}
293
294impl Monitor<VmState> {
295 fn notify_death(&self, reason: DeathReason) {
296 let state = &mut *self.state.lock().unwrap();
297 // In case this method is called more than once, ignore subsequent calls.
298 if state.death_reason.is_none() {
299 state.death_reason.replace(reason);
300 self.cv.notify_all();
301 }
302 }
303
304 fn notify_state(&self, state: VirtualMachineState) {
305 self.state.lock().unwrap().reported_state = state;
306 self.cv.notify_all();
307 }
308}
309
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000310struct VirtualMachineCallback {
311 state: Arc<Monitor<VmState>>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100312 client_callback: Option<Box<dyn VmCallback + Send + Sync>>,
313}
314
315impl Debug for VirtualMachineCallback {
316 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
317 fmt.debug_struct("VirtualMachineCallback")
318 .field("state", &self.state)
319 .field(
320 "client_callback",
321 &if self.client_callback.is_some() { "Some(...)" } else { "None" },
322 )
323 .finish()
324 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000325}
326
327impl Interface for VirtualMachineCallback {}
328
329impl IVirtualMachineCallback for VirtualMachineCallback {
David Brazdil451cc962022-10-14 14:08:12 +0100330 fn onPayloadStarted(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000331 self.state.notify_state(VirtualMachineState::STARTED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100332 if let Some(ref callback) = self.client_callback {
David Brazdil451cc962022-10-14 14:08:12 +0100333 callback.on_payload_started(cid);
334 }
335 Ok(())
336 }
337
Alan Stokes0e82b502022-08-08 14:44:48 +0100338 fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000339 self.state.notify_state(VirtualMachineState::READY);
Alan Stokes0e82b502022-08-08 14:44:48 +0100340 if let Some(ref callback) = self.client_callback {
341 callback.on_payload_ready(cid);
342 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000343 Ok(())
344 }
345
Alan Stokes0e82b502022-08-08 14:44:48 +0100346 fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000347 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100348 if let Some(ref callback) = self.client_callback {
349 callback.on_payload_finished(cid, exit_code);
350 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000351 Ok(())
352 }
353
Alan Stokes2bead0d2022-09-05 16:58:34 +0100354 fn onError(&self, cid: i32, error_code: AidlErrorCode, message: &str) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000355 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100356 if let Some(ref callback) = self.client_callback {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100357 let error_code = error_code.into();
Alan Stokes0e82b502022-08-08 14:44:48 +0100358 callback.on_error(cid, error_code, message);
359 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000360 Ok(())
361 }
362
Alan Stokes0e82b502022-08-08 14:44:48 +0100363 fn onDied(&self, cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
364 let reason = reason.into();
365 self.state.notify_death(reason);
366 if let Some(ref callback) = self.client_callback {
367 callback.on_died(cid, reason);
368 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000369 Ok(())
370 }
371}