blob: 110d838d079436074747f83deff465c25dfd738e [file] [log] [blame]
Victor Hsiehae67d3b2021-10-19 12:59:42 -07001/*
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//! This is a test helper program that opens files and/or directories, then passes the file
18//! descriptors to the specified command. When passing the file descriptors, they are mapped to the
19//! specified numbers in the child process.
20
21use anyhow::{bail, Context, Result};
22use clap::{App, Arg, Values};
23use command_fds::{CommandFdExt, FdMapping};
24use log::{debug, error};
Victor Hsiehe0d1e202022-08-26 10:19:40 -070025use std::fs::OpenOptions;
26use std::os::unix::fs::OpenOptionsExt;
27use std::os::unix::io::{AsRawFd, OwnedFd, RawFd};
Victor Hsiehae67d3b2021-10-19 12:59:42 -070028use std::process::Command;
29
30// `PseudoRawFd` is just an integer and not necessarily backed by a real FD. It is used to denote
31// the expecting FD number, when trying to set up FD mapping in the child process. The intention
32// with this alias is to improve readability by distinguishing from actual RawFd.
33type PseudoRawFd = RawFd;
34
Victor Hsiehe0d1e202022-08-26 10:19:40 -070035struct OwnedFdMapping {
36 owned_fd: OwnedFd,
Victor Hsiehae67d3b2021-10-19 12:59:42 -070037 target_fd: PseudoRawFd,
38}
39
Victor Hsiehe0d1e202022-08-26 10:19:40 -070040impl OwnedFdMapping {
Victor Hsiehae67d3b2021-10-19 12:59:42 -070041 fn as_fd_mapping(&self) -> FdMapping {
Victor Hsiehe0d1e202022-08-26 10:19:40 -070042 FdMapping { parent_fd: self.owned_fd.as_raw_fd(), child_fd: self.target_fd }
Victor Hsiehae67d3b2021-10-19 12:59:42 -070043 }
44}
45
46struct Args {
Victor Hsiehe0d1e202022-08-26 10:19:40 -070047 ro_file_fds: Vec<OwnedFdMapping>,
48 rw_file_fds: Vec<OwnedFdMapping>,
49 dir_fds: Vec<OwnedFdMapping>,
Victor Hsiehae67d3b2021-10-19 12:59:42 -070050 cmdline_args: Vec<String>,
51}
52
Victor Hsiehe0d1e202022-08-26 10:19:40 -070053fn parse_and_create_file_mapping<F>(
Victor Hsiehae67d3b2021-10-19 12:59:42 -070054 values: Option<Values<'_>>,
55 opener: F,
Victor Hsiehe0d1e202022-08-26 10:19:40 -070056) -> Result<Vec<OwnedFdMapping>>
Victor Hsiehae67d3b2021-10-19 12:59:42 -070057where
Victor Hsiehe0d1e202022-08-26 10:19:40 -070058 F: Fn(&str) -> Result<OwnedFd>,
Victor Hsiehae67d3b2021-10-19 12:59:42 -070059{
60 if let Some(options) = values {
61 options
62 .map(|option| {
63 // Example option: 10:/some/path
64 let strs: Vec<&str> = option.split(':').collect();
65 if strs.len() != 2 {
66 bail!("Invalid option: {}", option);
67 }
68 let fd = strs[0].parse::<PseudoRawFd>().context("Invalid FD format")?;
69 let path = strs[1];
Victor Hsiehe0d1e202022-08-26 10:19:40 -070070 Ok(OwnedFdMapping { target_fd: fd, owned_fd: opener(path)? })
Victor Hsiehae67d3b2021-10-19 12:59:42 -070071 })
72 .collect::<Result<_>>()
73 } else {
74 Ok(Vec::new())
75 }
76}
77
78fn parse_args() -> Result<Args> {
79 #[rustfmt::skip]
80 let matches = App::new("open_then_run")
81 .arg(Arg::with_name("open-ro")
82 .long("open-ro")
83 .value_name("FD:PATH")
84 .help("Open <PATH> read-only to pass as fd <FD>")
85 .multiple(true)
86 .number_of_values(1))
87 .arg(Arg::with_name("open-rw")
88 .long("open-rw")
89 .value_name("FD:PATH")
90 .help("Open/create <PATH> read-write to pass as fd <FD>")
91 .multiple(true)
92 .number_of_values(1))
93 .arg(Arg::with_name("open-dir")
94 .long("open-dir")
95 .value_name("FD:DIR")
96 .help("Open <DIR> to pass as fd <FD>")
97 .multiple(true)
98 .number_of_values(1))
99 .arg(Arg::with_name("args")
100 .help("Command line to execute with pre-opened FD inherited")
101 .last(true)
102 .required(true)
103 .multiple(true))
104 .get_matches();
105
Victor Hsiehe0d1e202022-08-26 10:19:40 -0700106 let ro_file_fds = parse_and_create_file_mapping(matches.values_of("open-ro"), |path| {
107 Ok(OwnedFd::from(
108 OpenOptions::new()
109 .read(true)
110 .open(path)
111 .with_context(|| format!("Open {} read-only", path))?,
112 ))
Victor Hsiehae67d3b2021-10-19 12:59:42 -0700113 })?;
114
Victor Hsiehe0d1e202022-08-26 10:19:40 -0700115 let rw_file_fds = parse_and_create_file_mapping(matches.values_of("open-rw"), |path| {
116 Ok(OwnedFd::from(
117 OpenOptions::new()
118 .read(true)
119 .write(true)
120 .create(true)
121 .open(path)
122 .with_context(|| format!("Open {} read-write", path))?,
123 ))
Victor Hsiehae67d3b2021-10-19 12:59:42 -0700124 })?;
125
Victor Hsiehe0d1e202022-08-26 10:19:40 -0700126 let dir_fds = parse_and_create_file_mapping(matches.values_of("open-dir"), |path| {
127 Ok(OwnedFd::from(
128 OpenOptions::new()
129 .custom_flags(libc::O_DIRECTORY)
130 .read(true) // O_DIRECTORY can only be opened with read
131 .open(path)
132 .with_context(|| format!("Open {} directory", path))?,
133 ))
Victor Hsiehae67d3b2021-10-19 12:59:42 -0700134 })?;
135
136 let cmdline_args: Vec<_> = matches.values_of("args").unwrap().map(|s| s.to_string()).collect();
137
Victor Hsiehe0d1e202022-08-26 10:19:40 -0700138 Ok(Args { ro_file_fds, rw_file_fds, dir_fds, cmdline_args })
Victor Hsiehae67d3b2021-10-19 12:59:42 -0700139}
140
141fn try_main() -> Result<()> {
142 let args = parse_args()?;
143
144 let mut command = Command::new(&args.cmdline_args[0]);
145 command.args(&args.cmdline_args[1..]);
146
147 // Set up FD mappings in the child process.
148 let mut fd_mappings = Vec::new();
Victor Hsiehe0d1e202022-08-26 10:19:40 -0700149 fd_mappings.extend(args.ro_file_fds.iter().map(OwnedFdMapping::as_fd_mapping));
150 fd_mappings.extend(args.rw_file_fds.iter().map(OwnedFdMapping::as_fd_mapping));
151 fd_mappings.extend(args.dir_fds.iter().map(OwnedFdMapping::as_fd_mapping));
Victor Hsiehae67d3b2021-10-19 12:59:42 -0700152 command.fd_mappings(fd_mappings)?;
153
154 debug!("Spawning {:?}", command);
155 command.spawn()?;
156 Ok(())
157}
158
159fn main() {
160 android_logger::init_once(
161 android_logger::Config::default()
162 .with_tag("open_then_run")
163 .with_min_level(log::Level::Debug),
164 );
165
166 if let Err(e) = try_main() {
167 error!("Failed with {:?}", e);
168 std::process::exit(1);
169 }
170}