1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker * Copyright (C) 2022 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker *
4*795d594fSAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker *
8*795d594fSAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker *
10*795d594fSAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker */
16*795d594fSAndroid Build Coastguard Worker
17*795d594fSAndroid Build Coastguard Worker #include <stdlib.h>
18*795d594fSAndroid Build Coastguard Worker #include <sys/capability.h>
19*795d594fSAndroid Build Coastguard Worker #include <sys/resource.h>
20*795d594fSAndroid Build Coastguard Worker #include <unistd.h>
21*795d594fSAndroid Build Coastguard Worker
22*795d594fSAndroid Build Coastguard Worker #include <filesystem>
23*795d594fSAndroid Build Coastguard Worker #include <iostream>
24*795d594fSAndroid Build Coastguard Worker #include <iterator>
25*795d594fSAndroid Build Coastguard Worker #include <optional>
26*795d594fSAndroid Build Coastguard Worker #include <string>
27*795d594fSAndroid Build Coastguard Worker #include <string_view>
28*795d594fSAndroid Build Coastguard Worker #include <system_error>
29*795d594fSAndroid Build Coastguard Worker #include <unordered_map>
30*795d594fSAndroid Build Coastguard Worker #include <unordered_set>
31*795d594fSAndroid Build Coastguard Worker #include <vector>
32*795d594fSAndroid Build Coastguard Worker
33*795d594fSAndroid Build Coastguard Worker #include "android-base/logging.h"
34*795d594fSAndroid Build Coastguard Worker #include "android-base/parseint.h"
35*795d594fSAndroid Build Coastguard Worker #include "android-base/result.h"
36*795d594fSAndroid Build Coastguard Worker #include "android-base/strings.h"
37*795d594fSAndroid Build Coastguard Worker #include "base/macros.h"
38*795d594fSAndroid Build Coastguard Worker #include "base/os.h"
39*795d594fSAndroid Build Coastguard Worker #include "base/scoped_cap.h"
40*795d594fSAndroid Build Coastguard Worker #include "palette/palette.h"
41*795d594fSAndroid Build Coastguard Worker #include "system/thread_defs.h"
42*795d594fSAndroid Build Coastguard Worker
43*795d594fSAndroid Build Coastguard Worker namespace {
44*795d594fSAndroid Build Coastguard Worker
45*795d594fSAndroid Build Coastguard Worker using ::android::base::ConsumePrefix;
46*795d594fSAndroid Build Coastguard Worker using ::android::base::Join;
47*795d594fSAndroid Build Coastguard Worker using ::android::base::ParseInt;
48*795d594fSAndroid Build Coastguard Worker using ::android::base::Result;
49*795d594fSAndroid Build Coastguard Worker using ::android::base::Split;
50*795d594fSAndroid Build Coastguard Worker using ::art::OS;
51*795d594fSAndroid Build Coastguard Worker
52*795d594fSAndroid Build Coastguard Worker constexpr const char* kUsage =
53*795d594fSAndroid Build Coastguard Worker R"(A wrapper binary that configures the process and executes a command.
54*795d594fSAndroid Build Coastguard Worker
55*795d594fSAndroid Build Coastguard Worker By default, it closes all open file descriptors except stdin, stdout, and stderr. `--keep-fds` can
56*795d594fSAndroid Build Coastguard Worker be passed to keep some more file descriptors open.
57*795d594fSAndroid Build Coastguard Worker
58*795d594fSAndroid Build Coastguard Worker Usage: art_exec [OPTIONS]... -- [COMMAND]...
59*795d594fSAndroid Build Coastguard Worker
60*795d594fSAndroid Build Coastguard Worker Supported options:
61*795d594fSAndroid Build Coastguard Worker --help: Print this text.
62*795d594fSAndroid Build Coastguard Worker --set-task-profile=PROFILES: Apply a set of task profiles (see
63*795d594fSAndroid Build Coastguard Worker https://source.android.com/devices/tech/perf/cgroups). Requires root access. PROFILES can be a
64*795d594fSAndroid Build Coastguard Worker comma-separated list of task profile names.
65*795d594fSAndroid Build Coastguard Worker --set-priority=PRIORITY: Apply the process priority. Currently, the only supported value of
66*795d594fSAndroid Build Coastguard Worker PRIORITY is "background".
67*795d594fSAndroid Build Coastguard Worker --drop-capabilities: Drop all root capabilities. Note that this has effect only if `art_exec` runs
68*795d594fSAndroid Build Coastguard Worker with some root capabilities but not as the root user.
69*795d594fSAndroid Build Coastguard Worker --keep-fds=FILE_DESCRIPTORS: A semicolon-separated list of file descriptors to keep open.
70*795d594fSAndroid Build Coastguard Worker --env=KEY=VALUE: Set an environment variable. This flag can be passed multiple times to set
71*795d594fSAndroid Build Coastguard Worker multiple environment variables.
72*795d594fSAndroid Build Coastguard Worker --process-name-suffix=SUFFIX: Add a suffix in parentheses to argv[0] when calling `execv`. This
73*795d594fSAndroid Build Coastguard Worker suffix will show up as part of the process name in tombstone when the process crashes.
74*795d594fSAndroid Build Coastguard Worker )";
75*795d594fSAndroid Build Coastguard Worker
76*795d594fSAndroid Build Coastguard Worker constexpr int kErrorUsage = 100;
77*795d594fSAndroid Build Coastguard Worker constexpr int kErrorOther = 101;
78*795d594fSAndroid Build Coastguard Worker
79*795d594fSAndroid Build Coastguard Worker struct Options {
80*795d594fSAndroid Build Coastguard Worker int command_pos = -1;
81*795d594fSAndroid Build Coastguard Worker std::vector<std::string> task_profiles;
82*795d594fSAndroid Build Coastguard Worker std::optional<int> priority = std::nullopt;
83*795d594fSAndroid Build Coastguard Worker bool drop_capabilities = false;
84*795d594fSAndroid Build Coastguard Worker std::unordered_set<int> keep_fds{fileno(stdin), fileno(stdout), fileno(stderr)};
85*795d594fSAndroid Build Coastguard Worker std::unordered_map<std::string, std::string> envs;
86*795d594fSAndroid Build Coastguard Worker std::string chroot;
87*795d594fSAndroid Build Coastguard Worker std::string process_name_suffix;
88*795d594fSAndroid Build Coastguard Worker };
89*795d594fSAndroid Build Coastguard Worker
Usage(const std::string & error_msg)90*795d594fSAndroid Build Coastguard Worker [[noreturn]] void Usage(const std::string& error_msg) {
91*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << error_msg;
92*795d594fSAndroid Build Coastguard Worker std::cerr << error_msg << "\n" << kUsage << "\n";
93*795d594fSAndroid Build Coastguard Worker exit(kErrorUsage);
94*795d594fSAndroid Build Coastguard Worker }
95*795d594fSAndroid Build Coastguard Worker
ParseOptions(int argc,char ** argv)96*795d594fSAndroid Build Coastguard Worker Options ParseOptions(int argc, char** argv) {
97*795d594fSAndroid Build Coastguard Worker Options options;
98*795d594fSAndroid Build Coastguard Worker for (int i = 1; i < argc; i++) {
99*795d594fSAndroid Build Coastguard Worker std::string_view arg = argv[i];
100*795d594fSAndroid Build Coastguard Worker if (arg == "--help") {
101*795d594fSAndroid Build Coastguard Worker std::cerr << kUsage << "\n";
102*795d594fSAndroid Build Coastguard Worker exit(0);
103*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--set-task-profile=")) {
104*795d594fSAndroid Build Coastguard Worker options.task_profiles = Split(std::string(arg), ",");
105*795d594fSAndroid Build Coastguard Worker if (options.task_profiles.empty()) {
106*795d594fSAndroid Build Coastguard Worker Usage("Empty task profile list");
107*795d594fSAndroid Build Coastguard Worker }
108*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--set-priority=")) {
109*795d594fSAndroid Build Coastguard Worker if (arg == "background") {
110*795d594fSAndroid Build Coastguard Worker options.priority = ANDROID_PRIORITY_BACKGROUND;
111*795d594fSAndroid Build Coastguard Worker } else {
112*795d594fSAndroid Build Coastguard Worker Usage("Unknown priority " + std::string(arg));
113*795d594fSAndroid Build Coastguard Worker }
114*795d594fSAndroid Build Coastguard Worker } else if (arg == "--drop-capabilities") {
115*795d594fSAndroid Build Coastguard Worker options.drop_capabilities = true;
116*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--keep-fds=")) {
117*795d594fSAndroid Build Coastguard Worker for (const std::string& fd_str : Split(std::string(arg), ":")) {
118*795d594fSAndroid Build Coastguard Worker int fd;
119*795d594fSAndroid Build Coastguard Worker if (!ParseInt(fd_str, &fd)) {
120*795d594fSAndroid Build Coastguard Worker Usage("Invalid fd " + fd_str);
121*795d594fSAndroid Build Coastguard Worker }
122*795d594fSAndroid Build Coastguard Worker options.keep_fds.insert(fd);
123*795d594fSAndroid Build Coastguard Worker }
124*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--env=")) {
125*795d594fSAndroid Build Coastguard Worker size_t pos = arg.find('=');
126*795d594fSAndroid Build Coastguard Worker if (pos == std::string_view::npos) {
127*795d594fSAndroid Build Coastguard Worker Usage("Malformed environment variable. Must contain '='");
128*795d594fSAndroid Build Coastguard Worker }
129*795d594fSAndroid Build Coastguard Worker options.envs[std::string(arg.substr(/*pos=*/0, /*n=*/pos))] =
130*795d594fSAndroid Build Coastguard Worker std::string(arg.substr(pos + 1));
131*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--chroot=")) {
132*795d594fSAndroid Build Coastguard Worker options.chroot = arg;
133*795d594fSAndroid Build Coastguard Worker } else if (ConsumePrefix(&arg, "--process-name-suffix=")) {
134*795d594fSAndroid Build Coastguard Worker options.process_name_suffix = arg;
135*795d594fSAndroid Build Coastguard Worker } else if (arg == "--") {
136*795d594fSAndroid Build Coastguard Worker if (i + 1 >= argc) {
137*795d594fSAndroid Build Coastguard Worker Usage("Missing command after '--'");
138*795d594fSAndroid Build Coastguard Worker }
139*795d594fSAndroid Build Coastguard Worker options.command_pos = i + 1;
140*795d594fSAndroid Build Coastguard Worker return options;
141*795d594fSAndroid Build Coastguard Worker } else {
142*795d594fSAndroid Build Coastguard Worker Usage("Unknown option " + std::string(arg));
143*795d594fSAndroid Build Coastguard Worker }
144*795d594fSAndroid Build Coastguard Worker }
145*795d594fSAndroid Build Coastguard Worker Usage("Missing '--'");
146*795d594fSAndroid Build Coastguard Worker }
147*795d594fSAndroid Build Coastguard Worker
DropInheritableCaps()148*795d594fSAndroid Build Coastguard Worker Result<void> DropInheritableCaps() {
149*795d594fSAndroid Build Coastguard Worker art::ScopedCap cap(cap_get_proc());
150*795d594fSAndroid Build Coastguard Worker if (cap.Get() == nullptr) {
151*795d594fSAndroid Build Coastguard Worker return ErrnoErrorf("Failed to call cap_get_proc");
152*795d594fSAndroid Build Coastguard Worker }
153*795d594fSAndroid Build Coastguard Worker if (cap_clear_flag(cap.Get(), CAP_INHERITABLE) != 0) {
154*795d594fSAndroid Build Coastguard Worker return ErrnoErrorf("Failed to call cap_clear_flag");
155*795d594fSAndroid Build Coastguard Worker }
156*795d594fSAndroid Build Coastguard Worker if (cap_set_proc(cap.Get()) != 0) {
157*795d594fSAndroid Build Coastguard Worker return ErrnoErrorf("Failed to call cap_set_proc");
158*795d594fSAndroid Build Coastguard Worker }
159*795d594fSAndroid Build Coastguard Worker return {};
160*795d594fSAndroid Build Coastguard Worker }
161*795d594fSAndroid Build Coastguard Worker
CloseFds(const std::unordered_set<int> & keep_fds)162*795d594fSAndroid Build Coastguard Worker Result<void> CloseFds(const std::unordered_set<int>& keep_fds) {
163*795d594fSAndroid Build Coastguard Worker std::vector<int> open_fds;
164*795d594fSAndroid Build Coastguard Worker std::error_code ec;
165*795d594fSAndroid Build Coastguard Worker for (const std::filesystem::directory_entry& dir_entry :
166*795d594fSAndroid Build Coastguard Worker std::filesystem::directory_iterator("/proc/self/fd", ec)) {
167*795d594fSAndroid Build Coastguard Worker int fd;
168*795d594fSAndroid Build Coastguard Worker if (!ParseInt(dir_entry.path().filename(), &fd)) {
169*795d594fSAndroid Build Coastguard Worker return Errorf("Invalid entry in /proc/self/fd {}", dir_entry.path().filename());
170*795d594fSAndroid Build Coastguard Worker }
171*795d594fSAndroid Build Coastguard Worker open_fds.push_back(fd);
172*795d594fSAndroid Build Coastguard Worker }
173*795d594fSAndroid Build Coastguard Worker if (ec) {
174*795d594fSAndroid Build Coastguard Worker return Errorf("Failed to list open FDs: {}", ec.message());
175*795d594fSAndroid Build Coastguard Worker }
176*795d594fSAndroid Build Coastguard Worker for (int fd : open_fds) {
177*795d594fSAndroid Build Coastguard Worker if (keep_fds.find(fd) == keep_fds.end()) {
178*795d594fSAndroid Build Coastguard Worker if (close(fd) != 0) {
179*795d594fSAndroid Build Coastguard Worker Result<void> error = ErrnoErrorf("Failed to close FD {}", fd);
180*795d594fSAndroid Build Coastguard Worker if (std::filesystem::exists(ART_FORMAT("/proc/self/fd/{}", fd))) {
181*795d594fSAndroid Build Coastguard Worker return error;
182*795d594fSAndroid Build Coastguard Worker }
183*795d594fSAndroid Build Coastguard Worker }
184*795d594fSAndroid Build Coastguard Worker }
185*795d594fSAndroid Build Coastguard Worker }
186*795d594fSAndroid Build Coastguard Worker return {};
187*795d594fSAndroid Build Coastguard Worker }
188*795d594fSAndroid Build Coastguard Worker
189*795d594fSAndroid Build Coastguard Worker } // namespace
190*795d594fSAndroid Build Coastguard Worker
main(int argc,char ** argv)191*795d594fSAndroid Build Coastguard Worker int main(int argc, char** argv) {
192*795d594fSAndroid Build Coastguard Worker android::base::InitLogging(argv);
193*795d594fSAndroid Build Coastguard Worker
194*795d594fSAndroid Build Coastguard Worker Options options = ParseOptions(argc, argv);
195*795d594fSAndroid Build Coastguard Worker
196*795d594fSAndroid Build Coastguard Worker if (auto result = CloseFds(options.keep_fds); !result.ok()) {
197*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Failed to close open FDs: " << result.error();
198*795d594fSAndroid Build Coastguard Worker return kErrorOther;
199*795d594fSAndroid Build Coastguard Worker }
200*795d594fSAndroid Build Coastguard Worker
201*795d594fSAndroid Build Coastguard Worker if (!options.task_profiles.empty()) {
202*795d594fSAndroid Build Coastguard Worker if (int ret = PaletteSetTaskProfiles(/*tid=*/0, options.task_profiles);
203*795d594fSAndroid Build Coastguard Worker ret != PALETTE_STATUS_OK) {
204*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Failed to set task profile: " << ret;
205*795d594fSAndroid Build Coastguard Worker return kErrorOther;
206*795d594fSAndroid Build Coastguard Worker }
207*795d594fSAndroid Build Coastguard Worker }
208*795d594fSAndroid Build Coastguard Worker
209*795d594fSAndroid Build Coastguard Worker if (options.priority.has_value()) {
210*795d594fSAndroid Build Coastguard Worker if (setpriority(PRIO_PROCESS, /*who=*/0, options.priority.value()) != 0) {
211*795d594fSAndroid Build Coastguard Worker PLOG(ERROR) << "Failed to setpriority";
212*795d594fSAndroid Build Coastguard Worker return kErrorOther;
213*795d594fSAndroid Build Coastguard Worker }
214*795d594fSAndroid Build Coastguard Worker }
215*795d594fSAndroid Build Coastguard Worker
216*795d594fSAndroid Build Coastguard Worker if (options.drop_capabilities) {
217*795d594fSAndroid Build Coastguard Worker if (auto result = DropInheritableCaps(); !result.ok()) {
218*795d594fSAndroid Build Coastguard Worker LOG(ERROR) << "Failed to drop inheritable capabilities: " << result.error();
219*795d594fSAndroid Build Coastguard Worker return kErrorOther;
220*795d594fSAndroid Build Coastguard Worker }
221*795d594fSAndroid Build Coastguard Worker }
222*795d594fSAndroid Build Coastguard Worker
223*795d594fSAndroid Build Coastguard Worker for (const auto& [key, value] : options.envs) {
224*795d594fSAndroid Build Coastguard Worker setenv(key.c_str(), value.c_str(), /*overwrite=*/1);
225*795d594fSAndroid Build Coastguard Worker }
226*795d594fSAndroid Build Coastguard Worker
227*795d594fSAndroid Build Coastguard Worker if (!options.chroot.empty()) {
228*795d594fSAndroid Build Coastguard Worker if (chroot(options.chroot.c_str()) != 0) {
229*795d594fSAndroid Build Coastguard Worker PLOG(ERROR) << ART_FORMAT("Failed to chroot to '{}'", options.chroot);
230*795d594fSAndroid Build Coastguard Worker return kErrorOther;
231*795d594fSAndroid Build Coastguard Worker }
232*795d594fSAndroid Build Coastguard Worker }
233*795d594fSAndroid Build Coastguard Worker
234*795d594fSAndroid Build Coastguard Worker // `argv[argc]` is `nullptr`, which `execv` needs.
235*795d594fSAndroid Build Coastguard Worker std::vector<char*> command_args(&argv[options.command_pos], &argv[argc + 1]);
236*795d594fSAndroid Build Coastguard Worker std::string program_path = argv[options.command_pos];
237*795d594fSAndroid Build Coastguard Worker // "/mnt/compat_env" is prepared by dexopt_chroot_setup on Android V.
238*795d594fSAndroid Build Coastguard Worker constexpr const char* kCompatArtdPath = "/mnt/compat_env/apex/com.android.art/bin/artd";
239*795d594fSAndroid Build Coastguard Worker if (program_path == "/apex/com.android.art/bin/artd" && OS::FileExists(kCompatArtdPath)) {
240*795d594fSAndroid Build Coastguard Worker LOG(INFO) << "Overriding program path to " << kCompatArtdPath;
241*795d594fSAndroid Build Coastguard Worker program_path = kCompatArtdPath;
242*795d594fSAndroid Build Coastguard Worker command_args[0] = program_path.data();
243*795d594fSAndroid Build Coastguard Worker }
244*795d594fSAndroid Build Coastguard Worker std::string override_program_name;
245*795d594fSAndroid Build Coastguard Worker if (!options.process_name_suffix.empty()) {
246*795d594fSAndroid Build Coastguard Worker override_program_name = ART_FORMAT("{} ({})", command_args[0], options.process_name_suffix);
247*795d594fSAndroid Build Coastguard Worker command_args[0] = override_program_name.data();
248*795d594fSAndroid Build Coastguard Worker }
249*795d594fSAndroid Build Coastguard Worker
250*795d594fSAndroid Build Coastguard Worker execv(program_path.c_str(), command_args.data());
251*795d594fSAndroid Build Coastguard Worker
252*795d594fSAndroid Build Coastguard Worker // Remove the trialing `nullptr`.
253*795d594fSAndroid Build Coastguard Worker command_args.resize(command_args.size() - 1);
254*795d594fSAndroid Build Coastguard Worker
255*795d594fSAndroid Build Coastguard Worker PLOG(FATAL) << "Failed to execute (" << Join(command_args, ' ') << ")";
256*795d594fSAndroid Build Coastguard Worker UNREACHABLE();
257*795d594fSAndroid Build Coastguard Worker }
258