Create new virtmgr binary from virtualizationservice
To bootstrap separation of virtualizationservice into two binaries
(global service and user instance), start by compiling a second binary
out of the same source code, just using a diffrent main source file.
The new binary has a command line interface that takes two file
descriptors - a socketpair() fd to start RpcBinder server on, and
a pipe() fd to use for signalling when the server has been started.
The process registers a death notification to kill itself when the
parent process has died.
Bug: 245727626
Test: builds, not used in tests yet
Change-Id: I0c4c7892af91b6deb5ece60e0d1b49a2b4356d35
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index da56f76..d0dde42 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -5,7 +5,6 @@
rust_defaults {
name: "virtualizationservice_defaults",
crate_name: "virtualizationservice",
- srcs: ["src/main.rs"],
edition: "2021",
// Only build on targets which crosvm builds on.
enabled: false,
@@ -67,11 +66,23 @@
rust_binary {
name: "virtualizationservice",
defaults: ["virtualizationservice_defaults"],
+ srcs: ["src/main.rs"],
+ apex_available: ["com.android.virt"],
+}
+
+rust_binary {
+ name: "virtmgr",
+ defaults: ["virtualizationservice_defaults"],
+ srcs: ["src/virtmgr.rs"],
+ rustlibs: [
+ "libclap",
+ ],
apex_available: ["com.android.virt"],
}
rust_test {
name: "virtualizationservice_device_test",
+ srcs: ["src/main.rs"],
defaults: ["virtualizationservice_defaults"],
rustlibs: [
"libtempfile",
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 3e7eca1..d7c7125 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,6 +14,7 @@
//! Implementation of the AIDL interface of the VirtualizationService.
+use crate::{get_calling_pid, get_calling_uid};
use crate::atom::{
forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom,
write_vm_booted_stats, write_vm_creation_stats};
@@ -55,7 +56,7 @@
use apkverify::{HashAlgorithm, V4Signature};
use binder::{
self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, ParcelFileDescriptor,
- Status, StatusCode, Strong, ThreadState,
+ Status, StatusCode, Strong,
};
use disk::QcowFile;
use lazy_static::lazy_static;
@@ -82,8 +83,6 @@
/// The unique ID of a VM used (together with a port number) for vsock communication.
pub type Cid = u32;
-pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
-
/// Directory in which to write disk image files used while running VMs.
pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
@@ -550,8 +549,8 @@
let state = &mut *self.state.lock().unwrap();
let console_fd = console_fd.map(clone_file).transpose()?;
let log_fd = log_fd.map(clone_file).transpose()?;
- let requester_uid = ThreadState::get_calling_uid();
- let requester_debug_pid = ThreadState::get_calling_pid();
+ let requester_uid = get_calling_uid();
+ let requester_debug_pid = get_calling_pid();
// Counter to generate unique IDs for temporary image files.
let mut next_temporary_image_id = 0;
@@ -877,8 +876,8 @@
/// Checks whether the caller has a specific permission
fn check_permission(perm: &str) -> binder::Result<()> {
- let calling_pid = ThreadState::get_calling_pid();
- let calling_uid = ThreadState::get_calling_uid();
+ let calling_pid = get_calling_pid();
+ let calling_uid = get_calling_uid();
// Root can do anything
if calling_uid == 0 {
return Ok(());
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
index 698dbca..e430c74 100644
--- a/virtualizationservice/src/atom.rs
+++ b/virtualizationservice/src/atom.rs
@@ -16,6 +16,7 @@
use crate::aidl::{clone_file, GLOBAL_SERVICE};
use crate::crosvm::VmMetric;
+use crate::get_calling_uid;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
IVirtualMachine::IVirtualMachine,
@@ -29,7 +30,7 @@
AtomVmExited::AtomVmExited,
};
use anyhow::{anyhow, Result};
-use binder::{ParcelFileDescriptor, ThreadState};
+use binder::ParcelFileDescriptor;
use log::{trace, warn};
use microdroid_payload_config::VmPayloadConfig;
use rustutils::system_properties;
@@ -112,7 +113,7 @@
};
let atom = AtomVmCreationRequested {
- uid: ThreadState::get_calling_uid() as i32,
+ uid: get_calling_uid() as i32,
vmIdentifier: vm_identifier,
isProtected: is_protected,
creationSucceeded: creation_succeeded,
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 714bcfd..9272e65 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -21,16 +21,27 @@
mod payload;
mod selinux;
-use crate::aidl::{VirtualizationService, BINDER_SERVICE_IDENTIFIER, TEMPORARY_DIRECTORY};
+use crate::aidl::{VirtualizationService, TEMPORARY_DIRECTORY};
use android_logger::{Config, FilterBuilder};
use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
use anyhow::{bail, Context, Error};
-use binder::{register_lazy_service, BinderFeatures, ProcessState};
+use binder::{register_lazy_service, BinderFeatures, ProcessState, ThreadState};
use log::{info, Level};
use std::fs::{remove_dir_all, remove_file, read_dir};
+use std::os::unix::raw::{pid_t, uid_t};
const LOG_TAG: &str = "VirtualizationService";
+const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
+
+fn get_calling_pid() -> pid_t {
+ ThreadState::get_calling_pid()
+}
+
+fn get_calling_uid() -> uid_t {
+ ThreadState::get_calling_uid()
+}
+
fn main() {
android_logger::init_once(
Config::default()
diff --git a/virtualizationservice/src/virtmgr.rs b/virtualizationservice/src/virtmgr.rs
new file mode 100644
index 0000000..1aa3df9
--- /dev/null
+++ b/virtualizationservice/src/virtmgr.rs
@@ -0,0 +1,120 @@
+// 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 payload;
+mod selinux;
+
+use crate::aidl::VirtualizationService;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
+use anyhow::{bail, Context};
+use binder::BinderFeatures;
+use lazy_static::lazy_static;
+use log::{info, Level};
+use rpcbinder::{FileDescriptorTransportMode, RpcServer};
+use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
+use clap::Parser;
+use nix::unistd::{Pid, Uid};
+use std::os::unix::raw::{pid_t, uid_t};
+
+const LOG_TAG: &str = "virtmgr";
+
+lazy_static! {
+ static ref PID_PARENT: Pid = Pid::parent();
+ static ref UID_CURRENT: Uid = Uid::current();
+}
+
+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.
+ nix::fcntl::fcntl(raw_fd, nix::fcntl::F_GETFD)
+ .with_context(|| format!("Invalid 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 main() {
+ android_logger::init_once(
+ android_logger::Config::default()
+ .with_tag(LOG_TAG)
+ .with_min_level(Level::Info)
+ .with_log_id(android_logger::LogId::System),
+ );
+
+ 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");
+
+ 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.
+ drop(ready_fd);
+
+ server.join();
+ info!("Shutting down VirtualizationService RpcServer");
+}