blob: 8dd3cd3c85cb1c3d5c6d31327c650ff00bc8b305 [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;
Jiyong Parkde13d402024-08-26 22:32:46 +090046use std::ffi::{c_char, c_int, c_void, CString};
David Brazdil46446062022-10-25 13:18:18 +010047use std::io::{self, Read};
Jiyong Parkde13d402024-08-26 22:32:46 +090048use std::os::fd::RawFd;
David Brazdil46446062022-10-25 13:18:18 +010049use std::process::Command;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000050use std::{
51 fmt::{self, Debug, Formatter},
52 fs::File,
Frederick Maylefbbcfcd2024-04-08 16:31:54 -070053 os::unix::io::{AsFd, AsRawFd, IntoRawFd, OwnedFd},
Andrew Walbrand0ef4002022-05-16 16:14:10 +000054 sync::Arc,
55 time::Duration,
56};
57
Inseob Kimf333ebc2024-09-13 14:01:05 +090058const EARLY_VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/early_virtmgr";
David Brazdil46446062022-10-25 13:18:18 +010059const VIRTMGR_PATH: &str = "/apex/com.android.virt/bin/virtmgr";
Alan Stokeseab0cdf2023-03-01 12:13:23 +000060const VIRTMGR_THREADS: usize = 2;
David Brazdil46446062022-10-25 13:18:18 +010061
62fn posix_pipe() -> Result<(OwnedFd, OwnedFd), io::Error> {
63 use nix::fcntl::OFlag;
64 use nix::unistd::pipe2;
65
66 // Create new POSIX pipe. Make it O_CLOEXEC to align with how Rust creates
67 // file descriptors (expected by SharedChild).
Frederick Maylefbbcfcd2024-04-08 16:31:54 -070068 Ok(pipe2(OFlag::O_CLOEXEC)?)
David Brazdil46446062022-10-25 13:18:18 +010069}
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).
Ludovic Barman696dd7e2023-11-30 14:21:52 +000077 Ok(socketpair(AddressFamily::Unix, SockType::Stream, None, SockFlag::SOCK_CLOEXEC)?)
David Brazdil46446062022-10-25 13:18:18 +010078}
79
Jiyong Parkde13d402024-08-26 22:32:46 +090080/// Error handling function for `get_virtualization_service`.
81///
82/// # Safety
83/// `message` shouldn't be used outside of the lifetime of the function. Management of `ctx` is
84/// entirely up to the function.
85pub type ErrorCallback =
86 unsafe extern "C" fn(code: c_int, message: *const c_char, ctx: *mut c_void);
87
88/// Spawns a new instance of virtmgr and rerturns a file descriptor for the socket connection to
89/// the service. When error occurs, it is reported via the ErrorCallback function along with the
90/// error message and any context that is set by the client.
91///
92/// # Safety
93/// `cb` should be null or a valid function pointer of type `ErrorCallback`
94#[no_mangle]
95pub unsafe extern "C" fn get_virtualization_service(
96 cb: Option<ErrorCallback>,
97 ctx: *mut c_void,
98) -> RawFd {
99 match VirtualizationService::new() {
100 Ok(vs) => vs.client_fd.into_raw_fd(),
101 Err(e) => {
102 if let Some(cb) = cb {
103 let code = e.raw_os_error().unwrap_or(-1);
104 let msg = CString::new(e.to_string()).unwrap();
105 // SAFETY: `cb` doesn't use `msg` outside of the lifetime of the function.
106 // msg's lifetime is longer than `cb` as it is bound to a local variable.
107 unsafe { cb(code, msg.as_ptr(), ctx) };
108 }
109 -1
110 }
111 }
112}
113
David Brazdil46446062022-10-25 13:18:18 +0100114/// A running instance of virtmgr which is hosting a VirtualizationService
115/// RpcBinder server.
116pub struct VirtualizationService {
117 /// Client FD for UDS connection to virtmgr's RpcBinder server. Closing it
118 /// will make virtmgr shut down.
119 client_fd: OwnedFd,
120}
121
122impl VirtualizationService {
123 /// Spawns a new instance of virtmgr, a child process that will host
124 /// the VirtualizationService AIDL service.
125 pub fn new() -> Result<VirtualizationService, io::Error> {
Inseob Kimf333ebc2024-09-13 14:01:05 +0900126 Self::new_with_path(VIRTMGR_PATH)
127 }
128
129 /// Spawns a new instance of early_virtmgr, a child process that will host
130 /// the VirtualizationService AIDL service for early VMs.
131 pub fn new_early() -> Result<VirtualizationService, io::Error> {
132 Self::new_with_path(EARLY_VIRTMGR_PATH)
133 }
134
135 fn new_with_path(virtmgr_path: &str) -> Result<VirtualizationService, io::Error> {
David Brazdil46446062022-10-25 13:18:18 +0100136 let (wait_fd, ready_fd) = posix_pipe()?;
137 let (client_fd, server_fd) = posix_socketpair()?;
138
Inseob Kimf333ebc2024-09-13 14:01:05 +0900139 let mut command = Command::new(virtmgr_path);
Jiyong Park69001a22024-08-16 20:45:34 +0900140 // Can't use BorrowedFd as it doesn't implement Display
David Brazdil46446062022-10-25 13:18:18 +0100141 command.arg("--rpc-server-fd").arg(format!("{}", server_fd.as_raw_fd()));
142 command.arg("--ready-fd").arg(format!("{}", ready_fd.as_raw_fd()));
Jiyong Park69001a22024-08-16 20:45:34 +0900143 command.preserved_fds(vec![server_fd, ready_fd]);
David Brazdil46446062022-10-25 13:18:18 +0100144
145 SharedChild::spawn(&mut command)?;
146
Jiyong Parkde13d402024-08-26 22:32:46 +0900147 // Wait for the child to signal that the RpcBinder server is read by closing its end of the
148 // pipe. Failing to read (especially EACCESS or EPERM) can happen if the client lacks the
149 // MANAGE_VIRTUAL_MACHINE permission. Therefore, such errors are propagated instead of
150 // being ignored.
151 let _ = File::from(wait_fd).read(&mut [0])?;
David Brazdil46446062022-10-25 13:18:18 +0100152 Ok(VirtualizationService { client_fd })
153 }
154
155 /// Connects to the VirtualizationService AIDL service.
156 pub fn connect(&self) -> Result<Strong<dyn IVirtualizationService>, io::Error> {
157 let session = RpcSession::new();
158 session.set_file_descriptor_transport_mode(FileDescriptorTransportMode::Unix);
159 session.set_max_incoming_threads(VIRTMGR_THREADS);
David Brazdil46446062022-10-25 13:18:18 +0100160 session
161 .setup_unix_domain_bootstrap_client(self.client_fd.as_fd())
162 .map_err(|_| io::Error::from(io::ErrorKind::ConnectionRefused))
163 }
164}
165
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000166/// A virtual machine which has been started by the VirtualizationService.
167pub struct VmInstance {
168 /// The `IVirtualMachine` Binder object representing the VM.
169 pub vm: Strong<dyn IVirtualMachine>,
170 cid: i32,
171 state: Arc<Monitor<VmState>>,
172 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
173 // is removed from the Binder when it's dropped.
174 _death_recipient: DeathRecipient,
175}
176
Alan Stokes0e82b502022-08-08 14:44:48 +0100177/// A trait to be implemented by clients to handle notification of significant changes to the VM
178/// state. Default implementations of all functions are provided so clients only need to handle the
179/// notifications they are interested in.
180#[allow(unused_variables)]
181pub trait VmCallback {
182 /// Called when the payload has been started within the VM. If present, `stream` is connected
183 /// to the stdin/stdout of the payload.
David Brazdil451cc962022-10-14 14:08:12 +0100184 fn on_payload_started(&self, cid: i32) {}
Alan Stokes0e82b502022-08-08 14:44:48 +0100185
186 /// Callend when the payload has notified Virtualization Service that it is ready to serve
187 /// clients.
188 fn on_payload_ready(&self, cid: i32) {}
189
190 /// Called when the payload has exited in the VM. `exit_code` is the exit code of the payload
191 /// process.
192 fn on_payload_finished(&self, cid: i32, exit_code: i32) {}
193
194 /// Called when an error has occurred in the VM. The `error_code` and `message` may give
195 /// further details.
Alan Stokes2bead0d2022-09-05 16:58:34 +0100196 fn on_error(&self, cid: i32, error_code: ErrorCode, message: &str) {}
Alan Stokes0e82b502022-08-08 14:44:48 +0100197
198 /// Called when the VM has exited, all resources have been freed, and any logs have been
199 /// written. `death_reason` gives an indication why the VM exited.
200 fn on_died(&self, cid: i32, death_reason: DeathReason) {}
201}
202
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000203impl VmInstance {
204 /// Creates (but doesn't start) a new VM with the given configuration.
205 pub fn create(
206 service: &dyn IVirtualizationService,
207 config: &VirtualMachineConfig,
Jiyong Parke6fb1672023-06-26 16:45:55 +0900208 console_out: Option<File>,
209 console_in: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000210 log: Option<File>,
Elie Kheirallah5c807a22024-09-23 20:40:42 +0000211 dump_dt: Option<File>,
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800212 callback: Option<Box<dyn VmCallback + Send + Sync>>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000213 ) -> BinderResult<Self> {
Jiyong Parke6fb1672023-06-26 16:45:55 +0900214 let console_out = console_out.map(ParcelFileDescriptor::new);
215 let console_in = console_in.map(ParcelFileDescriptor::new);
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000216 let log = log.map(ParcelFileDescriptor::new);
Elie Kheirallah5c807a22024-09-23 20:40:42 +0000217 let dump_dt = dump_dt.map(ParcelFileDescriptor::new);
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000218
Elie Kheirallah5c807a22024-09-23 20:40:42 +0000219 let vm = service.createVm(
220 config,
221 console_out.as_ref(),
222 console_in.as_ref(),
223 log.as_ref(),
224 dump_dt.as_ref(),
225 )?;
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000226
227 let cid = vm.getCid()?;
228
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800229 // Register callback before starting VM, in case it dies immediately.
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000230 let state = Arc::new(Monitor::new(VmState::default()));
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800231 let callback = BnVirtualMachineCallback::new_binder(
232 VirtualMachineCallback { state: state.clone(), client_callback: callback },
233 BinderFeatures::default(),
234 );
235 vm.registerCallback(&callback)?;
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000236 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
237
238 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
239 }
240
241 /// Starts the VM.
Chaitanya Cheemala (xWF)3da8a162025-01-21 08:57:09 -0800242 pub fn start(&self) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000243 self.vm.start()
244 }
245
Inseob Kimf3536de2024-11-22 14:00:57 +0900246 /// Stops the VM.
247 pub fn stop(&self) -> BinderResult<()> {
248 self.vm.stop()
249 }
250
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000251 /// Returns the CID used for vsock connections to the VM.
252 pub fn cid(&self) -> i32 {
253 self.cid
254 }
255
256 /// Returns the current lifecycle state of the VM.
257 pub fn state(&self) -> BinderResult<VirtualMachineState> {
258 self.vm.getState()
259 }
260
261 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
262 /// why it died.
263 pub fn wait_for_death(&self) -> DeathReason {
264 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
265 }
266
Alan Stokes71403772022-06-21 14:56:28 +0100267 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
268 /// Returns the reason why it died if it did so.
269 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
270 let (state, _timeout_result) =
271 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
272 // We don't care if it timed out - we just return the reason if there now is one
273 state.death_reason
274 }
275
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000276 /// Waits until the VM reports that it is ready.
277 ///
278 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
279 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
280 let (state, timeout_result) = self
281 .state
282 .wait_timeout_while(timeout, |state| {
283 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
284 })
285 .unwrap();
286 if timeout_result.timed_out() {
287 Err(VmWaitError::TimedOut)
288 } else if let Some(reason) = state.death_reason {
289 Err(VmWaitError::Died { reason })
290 } else if state.reported_state != VirtualMachineState::READY {
291 Err(VmWaitError::Finished)
292 } else {
293 Ok(())
294 }
295 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000296
297 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100298 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000299 &self,
300 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000301 ) -> Result<Strong<T>, StatusCode> {
David Brazdila2125dd2022-12-14 16:37:44 +0000302 RpcSession::new().setup_preconnected_client(|| {
Andrew Walbranc944fae2022-08-02 16:16:28 +0000303 match self.vm.connectVsock(port as i32) {
304 Ok(vsock) => {
305 // Ownership of the fd is transferred to binder
306 Some(vsock.into_raw_fd())
307 }
308 Err(e) => {
309 warn!("Vsock connection failed: {}", e);
310 None
311 }
312 }
313 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000314 }
Inseob Kim32e299c2024-12-09 15:27:40 +0900315
316 /// Opens a vsock connection to the CID of the VM on the given vsock port.
317 pub fn connect_vsock(&self, port: u32) -> BinderResult<ParcelFileDescriptor> {
318 self.vm.connectVsock(port as i32)
319 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000320}
321
322impl Debug for VmInstance {
323 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
324 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
325 }
326}
327
328/// Notify the VmState when the given Binder object dies.
329///
330/// If the returned DeathRecipient is dropped then this will no longer do anything.
331fn wait_for_binder_death(
332 binder: &mut impl IBinder,
333 state: Arc<Monitor<VmState>>,
334) -> BinderResult<DeathRecipient> {
335 let mut death_recipient = DeathRecipient::new(move || {
336 warn!("VirtualizationService unexpectedly died");
337 state.notify_death(DeathReason::VirtualizationServiceDied);
338 });
339 binder.link_to_death(&mut death_recipient)?;
340 Ok(death_recipient)
341}
342
343#[derive(Debug, Default)]
344struct VmState {
345 death_reason: Option<DeathReason>,
346 reported_state: VirtualMachineState,
347}
348
349impl Monitor<VmState> {
350 fn notify_death(&self, reason: DeathReason) {
351 let state = &mut *self.state.lock().unwrap();
352 // In case this method is called more than once, ignore subsequent calls.
353 if state.death_reason.is_none() {
354 state.death_reason.replace(reason);
355 self.cv.notify_all();
356 }
357 }
358
359 fn notify_state(&self, state: VirtualMachineState) {
360 self.state.lock().unwrap().reported_state = state;
361 self.cv.notify_all();
362 }
363}
364
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000365struct VirtualMachineCallback {
366 state: Arc<Monitor<VmState>>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100367 client_callback: Option<Box<dyn VmCallback + Send + Sync>>,
368}
369
370impl Debug for VirtualMachineCallback {
371 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
372 fmt.debug_struct("VirtualMachineCallback")
373 .field("state", &self.state)
374 .field(
375 "client_callback",
376 &if self.client_callback.is_some() { "Some(...)" } else { "None" },
377 )
378 .finish()
379 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000380}
381
382impl Interface for VirtualMachineCallback {}
383
384impl IVirtualMachineCallback for VirtualMachineCallback {
David Brazdil451cc962022-10-14 14:08:12 +0100385 fn onPayloadStarted(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000386 self.state.notify_state(VirtualMachineState::STARTED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100387 if let Some(ref callback) = self.client_callback {
David Brazdil451cc962022-10-14 14:08:12 +0100388 callback.on_payload_started(cid);
389 }
390 Ok(())
391 }
392
Alan Stokes0e82b502022-08-08 14:44:48 +0100393 fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000394 self.state.notify_state(VirtualMachineState::READY);
Alan Stokes0e82b502022-08-08 14:44:48 +0100395 if let Some(ref callback) = self.client_callback {
396 callback.on_payload_ready(cid);
397 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000398 Ok(())
399 }
400
Alan Stokes0e82b502022-08-08 14:44:48 +0100401 fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000402 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100403 if let Some(ref callback) = self.client_callback {
404 callback.on_payload_finished(cid, exit_code);
405 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000406 Ok(())
407 }
408
Alan Stokes2bead0d2022-09-05 16:58:34 +0100409 fn onError(&self, cid: i32, error_code: AidlErrorCode, message: &str) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000410 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100411 if let Some(ref callback) = self.client_callback {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100412 let error_code = error_code.into();
Alan Stokes0e82b502022-08-08 14:44:48 +0100413 callback.on_error(cid, error_code, message);
414 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000415 Ok(())
416 }
417
Alan Stokes0e82b502022-08-08 14:44:48 +0100418 fn onDied(&self, cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
419 let reason = reason.into();
420 self.state.notify_death(reason);
421 if let Some(ref callback) = self.client_callback {
422 callback.on_died(cid, reason);
423 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000424 Ok(())
425 }
426}