| // Copyright 2022, The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! Android Virtualization Manager |
| |
| mod aidl; |
| mod atom; |
| mod composite; |
| mod crosvm; |
| mod debug_config; |
| mod dt_overlay; |
| mod payload; |
| mod selinux; |
| |
| use crate::aidl::{GLOBAL_SERVICE, VirtualizationService}; |
| use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService; |
| use anyhow::{bail, Context, Result}; |
| use binder::{BinderFeatures, ProcessState}; |
| use lazy_static::lazy_static; |
| use log::{info, LevelFilter}; |
| use rpcbinder::{FileDescriptorTransportMode, RpcServer}; |
| use std::os::unix::io::{AsFd, FromRawFd, OwnedFd, RawFd}; |
| use clap::Parser; |
| use nix::fcntl::{fcntl, F_GETFD, F_SETFD, FdFlag}; |
| use nix::unistd::{write, Pid, Uid}; |
| use std::os::unix::raw::{pid_t, uid_t}; |
| |
| const LOG_TAG: &str = "virtmgr"; |
| |
| lazy_static! { |
| static ref PID_CURRENT: Pid = Pid::this(); |
| static ref PID_PARENT: Pid = Pid::parent(); |
| static ref UID_CURRENT: Uid = Uid::current(); |
| } |
| |
| fn get_this_pid() -> pid_t { |
| // Return the process ID of this process. |
| PID_CURRENT.as_raw() |
| } |
| |
| fn get_calling_pid() -> pid_t { |
| // The caller is the parent of this process. |
| PID_PARENT.as_raw() |
| } |
| |
| fn get_calling_uid() -> uid_t { |
| // The caller and this process share the same UID. |
| UID_CURRENT.as_raw() |
| } |
| |
| #[derive(Parser)] |
| struct Args { |
| /// File descriptor inherited from the caller to run RpcBinder server on. |
| /// This should be one end of a socketpair() compatible with RpcBinder's |
| /// UDS bootstrap transport. |
| #[clap(long)] |
| rpc_server_fd: RawFd, |
| /// File descriptor inherited from the caller to signal RpcBinder server |
| /// readiness. This should be one end of pipe() and the caller should be |
| /// waiting for HUP on the other end. |
| #[clap(long)] |
| ready_fd: RawFd, |
| } |
| |
| fn take_fd_ownership(raw_fd: RawFd, owned_fds: &mut Vec<RawFd>) -> Result<OwnedFd, anyhow::Error> { |
| // Basic check that the integer value does correspond to a file descriptor. |
| fcntl(raw_fd, F_GETFD).with_context(|| format!("Invalid file descriptor {raw_fd}"))?; |
| |
| // The file descriptor had CLOEXEC disabled to be inherited from the parent. |
| // Re-enable it to make sure it is not accidentally inherited further. |
| fcntl(raw_fd, F_SETFD(FdFlag::FD_CLOEXEC)) |
| .with_context(|| format!("Could not set CLOEXEC on file descriptor {raw_fd}"))?; |
| |
| // Creating OwnedFd for stdio FDs is not safe. |
| if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) { |
| bail!("File descriptor {raw_fd} is standard I/O descriptor"); |
| } |
| |
| // Reject RawFds that already have a corresponding OwnedFd. |
| if owned_fds.contains(&raw_fd) { |
| bail!("File descriptor {raw_fd} already owned"); |
| } |
| owned_fds.push(raw_fd); |
| |
| // SAFETY: Initializing OwnedFd for a RawFd provided in cmdline arguments. |
| // We checked that the integer value corresponds to a valid FD and that this |
| // is the first argument to claim its ownership. |
| Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) }) |
| } |
| |
| fn check_vm_support() -> Result<()> { |
| if hypervisor_props::is_any_vm_supported()? { |
| Ok(()) |
| } else { |
| // This should never happen, it indicates a misconfigured device where the virt APEX |
| // is present but VMs are not supported. If it does happen, fail fast to avoid wasting |
| // resources trying. |
| bail!("Device doesn't support protected or non-protected VMs") |
| } |
| } |
| |
| fn main() { |
| android_logger::init_once( |
| android_logger::Config::default() |
| .with_tag(LOG_TAG) |
| .with_max_level(LevelFilter::Info) |
| .with_log_buffer(android_logger::LogId::System), |
| ); |
| |
| check_vm_support().unwrap(); |
| |
| let args = Args::parse(); |
| |
| let mut owned_fds = vec![]; |
| let rpc_server_fd = take_fd_ownership(args.rpc_server_fd, &mut owned_fds) |
| .expect("Failed to take ownership of rpc_server_fd"); |
| let ready_fd = take_fd_ownership(args.ready_fd, &mut owned_fds) |
| .expect("Failed to take ownership of ready_fd"); |
| |
| // Start thread pool for kernel Binder connection to VirtualizationServiceInternal. |
| ProcessState::start_thread_pool(); |
| |
| GLOBAL_SERVICE.removeMemlockRlimit().expect("Failed to remove memlock rlimit"); |
| |
| let service = VirtualizationService::init(); |
| let service = |
| BnVirtualizationService::new_binder(service, BinderFeatures::default()).as_binder(); |
| |
| let server = RpcServer::new_unix_domain_bootstrap(service, rpc_server_fd) |
| .expect("Failed to start RpcServer"); |
| server.set_supported_file_descriptor_transport_modes(&[FileDescriptorTransportMode::Unix]); |
| |
| info!("Started VirtualizationService RpcServer. Ready to accept connections"); |
| |
| // Signal readiness to the caller by closing our end of the pipe. |
| write(ready_fd.as_fd(), "o".as_bytes()) |
| .expect("Failed to write a single character through ready_fd"); |
| drop(ready_fd); |
| |
| server.join(); |
| info!("Shutting down VirtualizationService RpcServer"); |
| } |