blob: 99eddfc9a293b8263d5fdaf4ce622367744a45d4 [file] [log] [blame]
Victor Hsieh272aa242021-02-01 14:19:20 -08001/*
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
Victor Hsieh9ed27182021-08-25 15:52:42 -070017//! pvm_exec is a proxy/wrapper command to run compilation task remotely. The most important task
Victor Hsieh272aa242021-02-01 14:19:20 -080018//! for this program is to run a `fd_server` that serves remote file read/write requests.
19//!
Victor Hsieh9ed27182021-08-25 15:52:42 -070020//! It currently works as a command line wrapper to make it easy to schedule an existing dex2oat
21//! task to run in the VM.
Victor Hsieh272aa242021-02-01 14:19:20 -080022//!
Victor Hsieh9ed27182021-08-25 15:52:42 -070023//! Example:
24//! $ adb shell exec 3</input/dex 4<>/output/oat ... pvm_exec --in-fd 3 --out-fd 4 -- dex2oat64 ...
25//!
26//! Note the immediate argument "dex2oat64" right after "--" is not really used. It is only for
27//! ergonomics.
Victor Hsieh272aa242021-02-01 14:19:20 -080028
29use anyhow::{bail, Context, Result};
Victor Hsieha7d37862021-06-04 17:14:20 -070030use binder::unstable_api::{new_spibinder, AIBinder};
31use binder::FromIBinder;
Victor Hsieh9ed27182021-08-25 15:52:42 -070032use log::{debug, error, warn};
Victor Hsieh272aa242021-02-01 14:19:20 -080033use minijail::Minijail;
34use nix::fcntl::{fcntl, FcntlArg::F_GETFD};
35use nix::sys::stat::fstat;
36use std::os::unix::io::RawFd;
37use std::path::Path;
38use std::process::exit;
39
40use compos_aidl_interface::aidl::com::android::compos::{
Victor Hsieha64194b2021-08-06 17:43:36 -070041 ICompOsService::ICompOsService, InputFdAnnotation::InputFdAnnotation, Metadata::Metadata,
Victor Hsieh272aa242021-02-01 14:19:20 -080042 OutputFdAnnotation::OutputFdAnnotation,
43};
44use compos_aidl_interface::binder::Strong;
45
Victor Hsieha7d37862021-06-04 17:14:20 -070046mod common;
47use common::{SERVICE_NAME, VSOCK_PORT};
Victor Hsieh272aa242021-02-01 14:19:20 -080048
Victor Hsieha7d37862021-06-04 17:14:20 -070049const FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
50
Victor Hsieha64194b2021-08-06 17:43:36 -070051fn get_local_service() -> Result<Strong<dyn ICompOsService>> {
Victor Hsieha7d37862021-06-04 17:14:20 -070052 compos_aidl_interface::binder::get_interface(SERVICE_NAME).context("get local binder")
53}
54
Victor Hsieha64194b2021-08-06 17:43:36 -070055fn get_rpc_binder(cid: u32) -> Result<Strong<dyn ICompOsService>> {
Victor Hsieha7d37862021-06-04 17:14:20 -070056 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
57 // safely taken by new_spibinder.
58 let ibinder = unsafe {
59 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, VSOCK_PORT) as *mut AIBinder)
60 };
61 if let Some(ibinder) = ibinder {
Victor Hsieha64194b2021-08-06 17:43:36 -070062 <dyn ICompOsService>::try_from(ibinder).context("Cannot connect to RPC service")
Victor Hsieha7d37862021-06-04 17:14:20 -070063 } else {
64 bail!("Invalid raw AIBinder")
65 }
Victor Hsieh272aa242021-02-01 14:19:20 -080066}
67
68fn spawn_fd_server(metadata: &Metadata, debuggable: bool) -> Result<Minijail> {
69 let mut inheritable_fds = if debuggable {
70 vec![1, 2] // inherit/redirect stdout/stderr for debugging
71 } else {
72 vec![]
73 };
74
Victor Hsieha7d37862021-06-04 17:14:20 -070075 let mut args = vec![FD_SERVER_BIN.to_string(), "--rpc-binder".to_string()];
Victor Hsieh272aa242021-02-01 14:19:20 -080076 for metadata in &metadata.input_fd_annotations {
77 args.push("--ro-fds".to_string());
78 args.push(metadata.fd.to_string());
79 inheritable_fds.push(metadata.fd);
80 }
81 for metadata in &metadata.output_fd_annotations {
82 args.push("--rw-fds".to_string());
83 args.push(metadata.fd.to_string());
84 inheritable_fds.push(metadata.fd);
85 }
86
87 let jail = Minijail::new()?;
88 let _pid = jail.run(Path::new(FD_SERVER_BIN), &inheritable_fds, &args)?;
89 Ok(jail)
90}
91
92fn is_fd_valid(fd: RawFd) -> Result<bool> {
93 let retval = fcntl(fd, F_GETFD)?;
94 Ok(retval >= 0)
95}
96
97fn parse_arg_fd(arg: &str) -> Result<RawFd> {
98 let fd = arg.parse::<RawFd>()?;
99 if !is_fd_valid(fd)? {
100 bail!("Bad FD: {}", fd);
101 }
102 Ok(fd)
103}
104
105struct Config {
106 args: Vec<String>,
107 metadata: Metadata,
Victor Hsieha7d37862021-06-04 17:14:20 -0700108 cid: Option<u32>,
Victor Hsieh272aa242021-02-01 14:19:20 -0800109 debuggable: bool,
110}
111
112fn parse_args() -> Result<Config> {
113 #[rustfmt::skip]
114 let matches = clap::App::new("pvm_exec")
115 .arg(clap::Arg::with_name("in-fd")
116 .long("in-fd")
117 .takes_value(true)
118 .multiple(true)
119 .use_delimiter(true))
120 .arg(clap::Arg::with_name("out-fd")
121 .long("out-fd")
122 .takes_value(true)
123 .multiple(true)
124 .use_delimiter(true))
Victor Hsieha7d37862021-06-04 17:14:20 -0700125 .arg(clap::Arg::with_name("cid")
126 .takes_value(true)
127 .long("cid"))
Victor Hsieh272aa242021-02-01 14:19:20 -0800128 .arg(clap::Arg::with_name("debug")
129 .long("debug"))
130 .arg(clap::Arg::with_name("args")
131 .last(true)
132 .required(true)
133 .multiple(true))
134 .get_matches();
135
136 let results: Result<Vec<_>> = matches
137 .values_of("in-fd")
138 .unwrap_or_default()
139 .map(|arg| {
140 let fd = parse_arg_fd(arg)?;
141 let file_size = fstat(fd)?.st_size;
142 Ok(InputFdAnnotation { fd, file_size })
143 })
144 .collect();
145 let input_fd_annotations = results?;
146
147 let results: Result<Vec<_>> = matches
148 .values_of("out-fd")
149 .unwrap_or_default()
150 .map(|arg| {
151 let fd = parse_arg_fd(arg)?;
152 Ok(OutputFdAnnotation { fd })
153 })
154 .collect();
155 let output_fd_annotations = results?;
156
157 let args: Vec<_> = matches.values_of("args").unwrap().map(|s| s.to_string()).collect();
Victor Hsieha7d37862021-06-04 17:14:20 -0700158 let cid =
159 if let Some(arg) = matches.value_of("cid") { Some(arg.parse::<u32>()?) } else { None };
Victor Hsieh272aa242021-02-01 14:19:20 -0800160 let debuggable = matches.is_present("debug");
161
162 Ok(Config {
163 args,
164 metadata: Metadata { input_fd_annotations, output_fd_annotations },
Victor Hsieha7d37862021-06-04 17:14:20 -0700165 cid,
Victor Hsieh272aa242021-02-01 14:19:20 -0800166 debuggable,
167 })
168}
169
170fn main() -> Result<()> {
Victor Hsieh4a654592021-08-19 09:08:19 -0700171 let debuggable = env!("TARGET_BUILD_VARIANT") != "user";
172 let log_level = if debuggable { log::Level::Trace } else { log::Level::Info };
173 android_logger::init_once(
174 android_logger::Config::default().with_tag("pvm_exec").with_min_level(log_level),
175 );
176
Victor Hsieh272aa242021-02-01 14:19:20 -0800177 // 1. Parse the command line arguments for collect execution data.
Victor Hsieha7d37862021-06-04 17:14:20 -0700178 let Config { args, metadata, cid, debuggable } = parse_args()?;
Victor Hsieh272aa242021-02-01 14:19:20 -0800179
180 // 2. Spawn and configure a fd_server to serve remote read/write requests.
181 let fd_server_jail = spawn_fd_server(&metadata, debuggable)?;
182 let fd_server_lifetime = scopeguard::guard(fd_server_jail, |fd_server_jail| {
183 if let Err(e) = fd_server_jail.kill() {
184 if !matches!(e, minijail::Error::Killed(_)) {
185 warn!("Failed to kill fd_server: {}", e);
186 }
187 }
188 });
189
190 // 3. Send the command line args to the remote to execute.
Victor Hsieha7d37862021-06-04 17:14:20 -0700191 let service = if let Some(cid) = cid { get_rpc_binder(cid) } else { get_local_service() }?;
Victor Hsieh9ed27182021-08-25 15:52:42 -0700192 let result = service.compile(&args, &metadata).context("Binder call failed")?;
193
194 // TODO: store/use the signature
195 debug!(
196 "Signature length: oat {}, vdex {}, image {}",
197 result.oatSignature.len(),
198 result.vdexSignature.len(),
199 result.imageSignature.len()
200 );
Victor Hsieh272aa242021-02-01 14:19:20 -0800201
202 // Be explicit about the lifetime, which should last at least until the task is finished.
203 drop(fd_server_lifetime);
204
Victor Hsieh9ed27182021-08-25 15:52:42 -0700205 if result.exitCode > 0 {
206 error!("remote execution failed with exit code {}", result.exitCode);
207 exit(result.exitCode as i32);
Victor Hsieh272aa242021-02-01 14:19:20 -0800208 }
209 Ok(())
210}