Get odrefresh in composd working.
composd now keeps track of the running instance of the CompOS VM and
will proxy compilation requests to it. (I was going to return an
ICompOsService, but mixing RPC and normal binder isn't allowed.) This
avoids giving odrefresh any access to vsock_socket at all.
pvm_exec now connects to composd rather than directly to the VM if a
magic CID value is specified. (It also logs errors more volubly, which
was helpful.)
Modify pvm_exec
Bug: 186126194
Test: Run composd_cmd, artifacts generated
Change-Id: If80cf53287bd1bac9c97c992da7e121b1a64aaaa
diff --git a/compos/composd/src/compos_instance.rs b/compos/composd/src/compos_instance.rs
deleted file mode 100644
index e30a8b3..0000000
--- a/compos/composd/src/compos_instance.rs
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2021 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.
- */
-
-//! Starts and manages instances of the CompOS VM.
-
-use anyhow::{Context, Result};
-use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
-use compos_aidl_interface::binder::Strong;
-use compos_common::compos_client::VmInstance;
-use compos_common::{COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE};
-use std::fs;
-use std::path::PathBuf;
-
-#[allow(dead_code)]
-pub struct CompOsInstance {
- instance: VmInstance,
- service: Strong<dyn ICompOsService>,
-}
-
-impl CompOsInstance {
- pub fn start_current_instance() -> Result<CompOsInstance> {
- let instance_image: PathBuf =
- [COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE].iter().collect();
-
- let instance = VmInstance::start(&instance_image).context("Starting VM")?;
- let service = instance.get_service().context("Connecting to CompOS")?;
-
- let key_blob: PathBuf =
- [COMPOS_DATA_ROOT, CURRENT_DIR, PRIVATE_KEY_BLOB_FILE].iter().collect();
- let key_blob = fs::read(key_blob).context("Reading private key")?;
- service.initializeSigningKey(&key_blob).context("Loading key")?;
-
- Ok(CompOsInstance { instance, service })
- }
-
- pub fn cid(&self) -> i32 {
- self.instance.cid()
- }
-}
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index 33da889..71e8125 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -18,7 +18,7 @@
//! responsible for managing the lifecycle of the CompOS VM instances, providing key management for
//! them, and orchestrating trusted compilation.
-mod compos_instance;
+mod instance_manager;
mod odrefresh;
mod service;
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
new file mode 100644
index 0000000..5d2a7e8
--- /dev/null
+++ b/compos/composd/src/instance_manager.rs
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+//! Starts and manages instances of the CompOS VM. At most one instance should be running at
+//! a time.
+
+use anyhow::{bail, Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::binder::Strong;
+use compos_common::compos_client::VmInstance;
+use compos_common::{COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE};
+use std::fs;
+use std::path::PathBuf;
+use std::sync::{Arc, Mutex, Weak};
+
+pub struct CompOsInstance {
+ #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
+ vm_instance: VmInstance,
+ service: Strong<dyn ICompOsService>,
+}
+
+#[derive(Default)]
+pub struct InstanceManager(Mutex<State>);
+
+impl InstanceManager {
+ pub fn get_running_service(&self) -> Result<Strong<dyn ICompOsService>> {
+ let mut state = self.0.lock().unwrap();
+ let instance = state.get_running_instance().context("No running instance")?;
+ Ok(instance.service.clone())
+ }
+
+ pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
+ let mut state = self.0.lock().unwrap();
+ state.mark_starting()?;
+ // Don't hold the lock while we start the instance to avoid blocking other callers.
+ drop(state);
+
+ let instance = self.try_start_current_instance();
+
+ let mut state = self.0.lock().unwrap();
+ if let Ok(ref instance) = instance {
+ state.mark_started(instance)?;
+ } else {
+ state.mark_stopped();
+ }
+ instance
+ }
+
+ fn try_start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
+ // TODO: Create instance_image & keys if needed
+ // TODO: Hold on to an IVirtualizationService
+ let instance_image: PathBuf =
+ [COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE].iter().collect();
+
+ let vm_instance = VmInstance::start(&instance_image).context("Starting VM")?;
+ let service = vm_instance.get_service().context("Connecting to CompOS")?;
+
+ let key_blob: PathBuf =
+ [COMPOS_DATA_ROOT, CURRENT_DIR, PRIVATE_KEY_BLOB_FILE].iter().collect();
+ let key_blob = fs::read(key_blob).context("Reading private key")?;
+ service.initializeSigningKey(&key_blob).context("Loading key")?;
+
+ Ok(Arc::new(CompOsInstance { vm_instance, service }))
+ }
+}
+
+// Ensures we only run one instance at a time.
+// Valid states:
+// Starting: is_starting is true, running_instance is None.
+// Started: is_starting is false, running_instance is Some(x) and there is a strong ref to x.
+// Stopped: is_starting is false and running_instance is None or a weak ref to a dropped instance.
+#[derive(Default)]
+struct State {
+ running_instance: Option<Weak<CompOsInstance>>,
+ is_starting: bool,
+}
+
+impl State {
+ // Move to Starting iff we are Stopped.
+ fn mark_starting(&mut self) -> Result<()> {
+ if self.is_starting {
+ bail!("An instance is already starting");
+ }
+ if let Some(weak) = &self.running_instance {
+ if weak.strong_count() != 0 {
+ bail!("An instance is already running");
+ }
+ }
+ self.running_instance = None;
+ self.is_starting = true;
+ Ok(())
+ }
+
+ // Move from Starting to Stopped.
+ fn mark_stopped(&mut self) {
+ if !self.is_starting || self.running_instance.is_some() {
+ panic!("Tried to mark stopped when not starting");
+ }
+ self.is_starting = false;
+ }
+
+ // Move from Starting to Started.
+ fn mark_started(&mut self, instance: &Arc<CompOsInstance>) -> Result<()> {
+ if !self.is_starting {
+ panic!("Tried to mark started when not starting")
+ }
+ if self.running_instance.is_some() {
+ panic!("Attempted to mark started when already started");
+ }
+ self.is_starting = false;
+ self.running_instance = Some(Arc::downgrade(instance));
+ Ok(())
+ }
+
+ // Return the running instance if we are in the Started state.
+ fn get_running_instance(&mut self) -> Option<Arc<CompOsInstance>> {
+ if self.is_starting {
+ return None;
+ }
+ let instance = self.running_instance.as_ref()?.upgrade();
+ if instance.is_none() {
+ // No point keeping an orphaned weak reference
+ self.running_instance = None;
+ }
+ instance
+ }
+}
diff --git a/compos/composd/src/odrefresh.rs b/compos/composd/src/odrefresh.rs
index c0042f0..54da231 100644
--- a/compos/composd/src/odrefresh.rs
+++ b/compos/composd/src/odrefresh.rs
@@ -17,6 +17,7 @@
//! Handle the details of executing odrefresh to generate compiled artifacts.
use anyhow::{bail, Context, Result};
+use compos_common::VMADDR_CID_ANY;
use num_derive::FromPrimitive;
use num_traits::FromPrimitive;
use std::process::Command;
@@ -36,10 +37,10 @@
CleanupFailed = EX_MAX + 4,
}
-pub fn run_forced_compile(cid: i32) -> Result<ExitCode> {
+pub fn run_forced_compile() -> Result<ExitCode> {
// We don`t need to capture stdout/stderr - odrefresh writes to the log
let mut odrefresh = Command::new(ODREFRESH_BIN)
- .arg(format!("--use-compilation-os={}", cid))
+ .arg(format!("--use-compilation-os={}", VMADDR_CID_ANY))
.arg("--force-compile")
.spawn()
.context("Running odrefresh")?;
diff --git a/compos/composd/src/service.rs b/compos/composd/src/service.rs
index 7fc9ab0..e3a1be0 100644
--- a/compos/composd/src/service.rs
+++ b/compos/composd/src/service.rs
@@ -17,20 +17,26 @@
//! Implementation of IIsolatedCompilationService, called from system server when compilation is
//! desired.
-use crate::compos_instance::CompOsInstance;
+use crate::instance_manager::InstanceManager;
use crate::odrefresh;
use android_system_composd::aidl::android::system::composd::IIsolatedCompilationService::{
BnIsolatedCompilationService, IIsolatedCompilationService,
};
use android_system_composd::binder::{self, BinderFeatures, Interface, Status, Strong};
use anyhow::{bail, Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::{
+ CompilationResult::CompilationResult, FdAnnotation::FdAnnotation,
+};
use log::{error, info};
use std::ffi::CString;
-pub struct IsolatedCompilationService {}
+#[derive(Default)]
+pub struct IsolatedCompilationService {
+ instance_manager: InstanceManager,
+}
pub fn new_binder() -> Strong<dyn IIsolatedCompilationService> {
- let service = IsolatedCompilationService {};
+ let service = IsolatedCompilationService::default();
BnIsolatedCompilationService::new_binder(service, BinderFeatures::default())
}
@@ -38,8 +44,18 @@
impl IIsolatedCompilationService for IsolatedCompilationService {
fn runForcedCompile(&self) -> binder::Result<()> {
+ // TODO - check caller is system or shell/root?
to_binder_result(self.do_run_forced_compile())
}
+
+ fn compile(
+ &self,
+ args: &[String],
+ fd_annotation: &FdAnnotation,
+ ) -> binder::Result<CompilationResult> {
+ // TODO - check caller is odrefresh
+ to_binder_result(self.do_compile(args, fd_annotation))
+ }
}
fn to_binder_result<T>(result: Result<T>) -> binder::Result<T> {
@@ -53,16 +69,26 @@
fn do_run_forced_compile(&self) -> Result<()> {
info!("runForcedCompile");
- // TODO: Create instance if need be, handle instance failure, prevent
- // multiple instances running
- let comp_os = CompOsInstance::start_current_instance().context("Starting CompOS")?;
+ let comp_os = self.instance_manager.start_current_instance().context("Starting CompOS")?;
- let exit_code = odrefresh::run_forced_compile(comp_os.cid())?;
+ let exit_code = odrefresh::run_forced_compile()?;
if exit_code != odrefresh::ExitCode::CompilationSuccess {
bail!("Unexpected odrefresh result: {:?}", exit_code);
}
+ // The instance is needed until odrefresh is finished
+ drop(comp_os);
+
Ok(())
}
+
+ fn do_compile(
+ &self,
+ args: &[String],
+ fd_annotation: &FdAnnotation,
+ ) -> Result<CompilationResult> {
+ let compos = self.instance_manager.get_running_service()?;
+ compos.compile(args, fd_annotation).context("Compiling")
+ }
}