blob: 5d2a7e8236d28c1919902ccc1b2e205da6e57a2a [file] [log] [blame]
Alan Stokesa2869d22021-09-22 09:06:41 +01001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Starts and manages instances of the CompOS VM. At most one instance should be running at
18//! a time.
19
20use anyhow::{bail, Context, Result};
21use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
22use compos_aidl_interface::binder::Strong;
23use compos_common::compos_client::VmInstance;
24use compos_common::{COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE};
25use std::fs;
26use std::path::PathBuf;
27use std::sync::{Arc, Mutex, Weak};
28
29pub struct CompOsInstance {
30 #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
31 vm_instance: VmInstance,
32 service: Strong<dyn ICompOsService>,
33}
34
35#[derive(Default)]
36pub struct InstanceManager(Mutex<State>);
37
38impl InstanceManager {
39 pub fn get_running_service(&self) -> Result<Strong<dyn ICompOsService>> {
40 let mut state = self.0.lock().unwrap();
41 let instance = state.get_running_instance().context("No running instance")?;
42 Ok(instance.service.clone())
43 }
44
45 pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
46 let mut state = self.0.lock().unwrap();
47 state.mark_starting()?;
48 // Don't hold the lock while we start the instance to avoid blocking other callers.
49 drop(state);
50
51 let instance = self.try_start_current_instance();
52
53 let mut state = self.0.lock().unwrap();
54 if let Ok(ref instance) = instance {
55 state.mark_started(instance)?;
56 } else {
57 state.mark_stopped();
58 }
59 instance
60 }
61
62 fn try_start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
63 // TODO: Create instance_image & keys if needed
64 // TODO: Hold on to an IVirtualizationService
65 let instance_image: PathBuf =
66 [COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE].iter().collect();
67
68 let vm_instance = VmInstance::start(&instance_image).context("Starting VM")?;
69 let service = vm_instance.get_service().context("Connecting to CompOS")?;
70
71 let key_blob: PathBuf =
72 [COMPOS_DATA_ROOT, CURRENT_DIR, PRIVATE_KEY_BLOB_FILE].iter().collect();
73 let key_blob = fs::read(key_blob).context("Reading private key")?;
74 service.initializeSigningKey(&key_blob).context("Loading key")?;
75
76 Ok(Arc::new(CompOsInstance { vm_instance, service }))
77 }
78}
79
80// Ensures we only run one instance at a time.
81// Valid states:
82// Starting: is_starting is true, running_instance is None.
83// Started: is_starting is false, running_instance is Some(x) and there is a strong ref to x.
84// Stopped: is_starting is false and running_instance is None or a weak ref to a dropped instance.
85#[derive(Default)]
86struct State {
87 running_instance: Option<Weak<CompOsInstance>>,
88 is_starting: bool,
89}
90
91impl State {
92 // Move to Starting iff we are Stopped.
93 fn mark_starting(&mut self) -> Result<()> {
94 if self.is_starting {
95 bail!("An instance is already starting");
96 }
97 if let Some(weak) = &self.running_instance {
98 if weak.strong_count() != 0 {
99 bail!("An instance is already running");
100 }
101 }
102 self.running_instance = None;
103 self.is_starting = true;
104 Ok(())
105 }
106
107 // Move from Starting to Stopped.
108 fn mark_stopped(&mut self) {
109 if !self.is_starting || self.running_instance.is_some() {
110 panic!("Tried to mark stopped when not starting");
111 }
112 self.is_starting = false;
113 }
114
115 // Move from Starting to Started.
116 fn mark_started(&mut self, instance: &Arc<CompOsInstance>) -> Result<()> {
117 if !self.is_starting {
118 panic!("Tried to mark started when not starting")
119 }
120 if self.running_instance.is_some() {
121 panic!("Attempted to mark started when already started");
122 }
123 self.is_starting = false;
124 self.running_instance = Some(Arc::downgrade(instance));
125 Ok(())
126 }
127
128 // Return the running instance if we are in the Started state.
129 fn get_running_instance(&mut self) -> Option<Arc<CompOsInstance>> {
130 if self.is_starting {
131 return None;
132 }
133 let instance = self.running_instance.as_ref()?.upgrade();
134 if instance.is_none() {
135 // No point keeping an orphaned weak reference
136 self.running_instance = None;
137 }
138 instance
139 }
140}