blob: 2218d1065e65c4a42f4ff0ec1f26299b3add6c75 [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
17//! pvm_exec is a proxy/wrapper command to run a command remotely. It does not transport the
18//! program and just pass the command line arguments to compsvc to execute. The most important task
19//! for this program is to run a `fd_server` that serves remote file read/write requests.
20//!
21//! Example:
22//! $ adb shell exec 3</dev/zero 4<>/dev/null pvm_exec --in-fd 3 --out-fd 4 -- sleep 10
23//!
24//! Note the immediate argument right after "--" (e.g. "sleep" in the example above) is not really
25//! used. It is only for ergonomics.
26
27use anyhow::{bail, Context, Result};
Victor Hsieha7d37862021-06-04 17:14:20 -070028use binder::unstable_api::{new_spibinder, AIBinder};
29use binder::FromIBinder;
Victor Hsieh272aa242021-02-01 14:19:20 -080030use log::{error, warn};
31use minijail::Minijail;
32use nix::fcntl::{fcntl, FcntlArg::F_GETFD};
33use nix::sys::stat::fstat;
34use std::os::unix::io::RawFd;
35use std::path::Path;
36use std::process::exit;
37
38use compos_aidl_interface::aidl::com::android::compos::{
Victor Hsieha64194b2021-08-06 17:43:36 -070039 ICompOsService::ICompOsService, InputFdAnnotation::InputFdAnnotation, Metadata::Metadata,
Victor Hsieh272aa242021-02-01 14:19:20 -080040 OutputFdAnnotation::OutputFdAnnotation,
41};
42use compos_aidl_interface::binder::Strong;
43
Victor Hsieha7d37862021-06-04 17:14:20 -070044mod common;
45use common::{SERVICE_NAME, VSOCK_PORT};
Victor Hsieh272aa242021-02-01 14:19:20 -080046
Victor Hsieha7d37862021-06-04 17:14:20 -070047const FD_SERVER_BIN: &str = "/apex/com.android.virt/bin/fd_server";
48
Victor Hsieha64194b2021-08-06 17:43:36 -070049fn get_local_service() -> Result<Strong<dyn ICompOsService>> {
Victor Hsieha7d37862021-06-04 17:14:20 -070050 compos_aidl_interface::binder::get_interface(SERVICE_NAME).context("get local binder")
51}
52
Victor Hsieha64194b2021-08-06 17:43:36 -070053fn get_rpc_binder(cid: u32) -> Result<Strong<dyn ICompOsService>> {
Victor Hsieha7d37862021-06-04 17:14:20 -070054 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
55 // safely taken by new_spibinder.
56 let ibinder = unsafe {
57 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(cid, VSOCK_PORT) as *mut AIBinder)
58 };
59 if let Some(ibinder) = ibinder {
Victor Hsieha64194b2021-08-06 17:43:36 -070060 <dyn ICompOsService>::try_from(ibinder).context("Cannot connect to RPC service")
Victor Hsieha7d37862021-06-04 17:14:20 -070061 } else {
62 bail!("Invalid raw AIBinder")
63 }
Victor Hsieh272aa242021-02-01 14:19:20 -080064}
65
66fn spawn_fd_server(metadata: &Metadata, debuggable: bool) -> Result<Minijail> {
67 let mut inheritable_fds = if debuggable {
68 vec![1, 2] // inherit/redirect stdout/stderr for debugging
69 } else {
70 vec![]
71 };
72
Victor Hsieha7d37862021-06-04 17:14:20 -070073 let mut args = vec![FD_SERVER_BIN.to_string(), "--rpc-binder".to_string()];
Victor Hsieh272aa242021-02-01 14:19:20 -080074 for metadata in &metadata.input_fd_annotations {
75 args.push("--ro-fds".to_string());
76 args.push(metadata.fd.to_string());
77 inheritable_fds.push(metadata.fd);
78 }
79 for metadata in &metadata.output_fd_annotations {
80 args.push("--rw-fds".to_string());
81 args.push(metadata.fd.to_string());
82 inheritable_fds.push(metadata.fd);
83 }
84
85 let jail = Minijail::new()?;
86 let _pid = jail.run(Path::new(FD_SERVER_BIN), &inheritable_fds, &args)?;
87 Ok(jail)
88}
89
90fn is_fd_valid(fd: RawFd) -> Result<bool> {
91 let retval = fcntl(fd, F_GETFD)?;
92 Ok(retval >= 0)
93}
94
95fn parse_arg_fd(arg: &str) -> Result<RawFd> {
96 let fd = arg.parse::<RawFd>()?;
97 if !is_fd_valid(fd)? {
98 bail!("Bad FD: {}", fd);
99 }
100 Ok(fd)
101}
102
103struct Config {
104 args: Vec<String>,
105 metadata: Metadata,
Victor Hsieha7d37862021-06-04 17:14:20 -0700106 cid: Option<u32>,
Victor Hsieh272aa242021-02-01 14:19:20 -0800107 debuggable: bool,
108}
109
110fn parse_args() -> Result<Config> {
111 #[rustfmt::skip]
112 let matches = clap::App::new("pvm_exec")
113 .arg(clap::Arg::with_name("in-fd")
114 .long("in-fd")
115 .takes_value(true)
116 .multiple(true)
117 .use_delimiter(true))
118 .arg(clap::Arg::with_name("out-fd")
119 .long("out-fd")
120 .takes_value(true)
121 .multiple(true)
122 .use_delimiter(true))
Victor Hsieha7d37862021-06-04 17:14:20 -0700123 .arg(clap::Arg::with_name("cid")
124 .takes_value(true)
125 .long("cid"))
Victor Hsieh272aa242021-02-01 14:19:20 -0800126 .arg(clap::Arg::with_name("debug")
127 .long("debug"))
128 .arg(clap::Arg::with_name("args")
129 .last(true)
130 .required(true)
131 .multiple(true))
132 .get_matches();
133
134 let results: Result<Vec<_>> = matches
135 .values_of("in-fd")
136 .unwrap_or_default()
137 .map(|arg| {
138 let fd = parse_arg_fd(arg)?;
139 let file_size = fstat(fd)?.st_size;
140 Ok(InputFdAnnotation { fd, file_size })
141 })
142 .collect();
143 let input_fd_annotations = results?;
144
145 let results: Result<Vec<_>> = matches
146 .values_of("out-fd")
147 .unwrap_or_default()
148 .map(|arg| {
149 let fd = parse_arg_fd(arg)?;
150 Ok(OutputFdAnnotation { fd })
151 })
152 .collect();
153 let output_fd_annotations = results?;
154
155 let args: Vec<_> = matches.values_of("args").unwrap().map(|s| s.to_string()).collect();
Victor Hsieha7d37862021-06-04 17:14:20 -0700156 let cid =
157 if let Some(arg) = matches.value_of("cid") { Some(arg.parse::<u32>()?) } else { None };
Victor Hsieh272aa242021-02-01 14:19:20 -0800158 let debuggable = matches.is_present("debug");
159
160 Ok(Config {
161 args,
162 metadata: Metadata { input_fd_annotations, output_fd_annotations },
Victor Hsieha7d37862021-06-04 17:14:20 -0700163 cid,
Victor Hsieh272aa242021-02-01 14:19:20 -0800164 debuggable,
165 })
166}
167
168fn main() -> Result<()> {
169 // 1. Parse the command line arguments for collect execution data.
Victor Hsieha7d37862021-06-04 17:14:20 -0700170 let Config { args, metadata, cid, debuggable } = parse_args()?;
Victor Hsieh272aa242021-02-01 14:19:20 -0800171
172 // 2. Spawn and configure a fd_server to serve remote read/write requests.
173 let fd_server_jail = spawn_fd_server(&metadata, debuggable)?;
174 let fd_server_lifetime = scopeguard::guard(fd_server_jail, |fd_server_jail| {
175 if let Err(e) = fd_server_jail.kill() {
176 if !matches!(e, minijail::Error::Killed(_)) {
177 warn!("Failed to kill fd_server: {}", e);
178 }
179 }
180 });
181
182 // 3. Send the command line args to the remote to execute.
Victor Hsieha7d37862021-06-04 17:14:20 -0700183 let service = if let Some(cid) = cid { get_rpc_binder(cid) } else { get_local_service() }?;
184 let exit_code = service.execute(&args, &metadata).context("Binder call failed")?;
Victor Hsieh272aa242021-02-01 14:19:20 -0800185
186 // Be explicit about the lifetime, which should last at least until the task is finished.
187 drop(fd_server_lifetime);
188
189 if exit_code > 0 {
190 error!("remote execution failed with exit code {}", exit_code);
191 exit(exit_code as i32);
192 }
193 Ok(())
194}