1 /*
2 * Copyright (C) 2024 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 #include <fcntl.h>
18 #include <stdlib.h>
19 #include <sys/prctl.h>
20 #include <unistd.h>
21
22 #include <cerrno>
23 #include <memory>
24 #include <optional>
25 #include <string>
26 #include <string_view>
27 #include <utility>
28 #include <vector>
29
30 #include <absl/base/log_severity.h>
31 #include <absl/flags/flag.h>
32 #include <absl/flags/parse.h>
33 #include <absl/log/check.h>
34 #include <absl/log/globals.h>
35 #include <absl/log/initialize.h>
36 #include <absl/log/log.h>
37 #include <absl/status/status.h>
38 #include <absl/strings/match.h>
39 #include <absl/strings/str_cat.h>
40 #include <sandboxed_api/util/fileops.h>
41 #include <sandboxed_api/util/path.h>
42
43 #include "host/commands/process_sandboxer/logs.h"
44 #include "host/commands/process_sandboxer/pidfd.h"
45 #include "host/commands/process_sandboxer/policies.h"
46 #include "host/commands/process_sandboxer/sandbox_manager.h"
47
48 inline constexpr char kCuttlefishConfigEnvVarName[] = "CUTTLEFISH_CONFIG_FILE";
49
50 ABSL_FLAG(std::string, assembly_dir, "", "cuttlefish/assembly build dir");
51 ABSL_FLAG(std::string, host_artifacts_path, "", "Host exes and libs");
52 ABSL_FLAG(std::string, environments_dir, "", "Cross-instance environment dir");
53 ABSL_FLAG(std::string, guest_image_path, "", "Directory with `system.img`");
54 ABSL_FLAG(std::string, sandboxer_log_dir, "", "Where to write log files");
55 ABSL_FLAG(std::vector<std::string>, log_files, std::vector<std::string>(),
56 "File paths outside the sandbox to write logs to");
57 ABSL_FLAG(std::string, runtime_dir, "",
58 "Working directory of host executables");
59 ABSL_FLAG(bool, verbose_stderr, false, "Write debug messages to stderr");
60
61 namespace cuttlefish::process_sandboxer {
62 namespace {
63
64 using sapi::file::CleanPath;
65 using sapi::file::JoinPath;
66 using sapi::file_util::fileops::FDCloser;
67
FromEnv(const std::string & name)68 std::optional<std::string_view> FromEnv(const std::string& name) {
69 char* value = getenv(name.c_str());
70 return value == NULL ? std::optional<std::string_view>() : value;
71 }
72
ProcessSandboxerMain(int argc,char ** argv)73 absl::Status ProcessSandboxerMain(int argc, char** argv) {
74 std::vector<char*> args = absl::ParseCommandLine(argc, argv);
75 /* When building in AOSP, the flags in absl/log/flags.cc are missing. This
76 * uses the absl/log/globals.h interface to log ERROR severity to stderr, and
77 * write all LOG and VLOG(1) messages to log sinks pointing to log files. */
78 absl::InitializeLog();
79 if (absl::GetFlag(FLAGS_verbose_stderr)) {
80 absl::SetStderrThreshold(absl::LogSeverity::kError);
81 } else {
82 absl::SetStderrThreshold(absl::LogSeverity::kInfo);
83 }
84 absl::EnableLogPrefix(true);
85 absl::SetGlobalVLogLevel(1);
86
87 if (prctl(PR_SET_CHILD_SUBREAPER, 1) < 0) {
88 return absl::ErrnoToStatus(errno, "prctl(PR_SET_CHILD_SUBREAPER failed");
89 }
90
91 std::string tmp_dir(FromEnv("TMPDIR").value_or("/tmp"));
92 tmp_dir += "/process_sandboxer.XXXXXX";
93 if (mkdtemp(tmp_dir.data()) == nullptr) {
94 return absl::ErrnoToStatus(errno, "mkdtemp failed");
95 }
96
97 HostInfo host{
98 .assembly_dir = CleanPath(absl::GetFlag(FLAGS_assembly_dir)),
99 .cuttlefish_config_path =
100 CleanPath(FromEnv(kCuttlefishConfigEnvVarName).value_or("")),
101 .environments_dir = CleanPath(absl::GetFlag(FLAGS_environments_dir)),
102 .guest_image_path = CleanPath(absl::GetFlag(FLAGS_guest_image_path)),
103 .host_artifacts_path =
104 CleanPath(absl::GetFlag(FLAGS_host_artifacts_path)),
105 .log_dir = CleanPath(absl::GetFlag(FLAGS_sandboxer_log_dir)),
106 .runtime_dir = CleanPath(absl::GetFlag(FLAGS_runtime_dir)),
107 .tmp_dir = tmp_dir,
108 };
109
110 // TODO: schuffelen - try to guess these from the cvd_internal_start arguments
111
112 std::optional<std::string_view> home = FromEnv("HOME");
113
114 // CleanPath will set empty strings to ".", so consider that the unset value.
115 if (host.assembly_dir == "." && home.has_value()) {
116 host.assembly_dir = CleanPath(JoinPath(*home, "cuttlefish", "assembly"));
117 }
118 if (host.cuttlefish_config_path == "." && home.has_value()) {
119 host.cuttlefish_config_path = CleanPath(
120 JoinPath(*home, "cuttlefish", "assembly", "cuttlefish_config.json"));
121 }
122 if (host.environments_dir == "." && home.has_value()) {
123 host.environments_dir =
124 CleanPath(JoinPath(*home, "cuttlefish", "environments"));
125 }
126 if (host.log_dir == "." && home.has_value()) {
127 host.log_dir =
128 CleanPath(JoinPath(*home, "cuttlefish", "instances", "cvd-1", "logs"));
129 }
130 if (host.runtime_dir == "." && home.has_value()) {
131 host.runtime_dir =
132 CleanPath(JoinPath(*home, "cuttlefish", "instances", "cvd-1"));
133 }
134
135 std::optional<std::string_view> product_out = FromEnv("ANDROID_PRODUCT_OUT");
136
137 if (host.guest_image_path == ".") {
138 if (product_out.has_value()) {
139 host.guest_image_path = CleanPath(*product_out);
140 } else if (home.has_value()) {
141 host.guest_image_path = CleanPath(*home);
142 }
143 }
144
145 std::optional<std::string_view> host_out = FromEnv("ANDROID_HOST_OUT");
146
147 if (host.host_artifacts_path == ".") {
148 if (host_out.has_value()) {
149 host.host_artifacts_path = CleanPath(*host_out);
150 } else if (home.has_value()) {
151 host.host_artifacts_path = CleanPath(*home);
152 }
153 }
154
155 absl::Status dir_creation = host.EnsureOutputDirectoriesExist();
156 if (!dir_creation.ok()) {
157 return dir_creation;
158 }
159
160 absl::Status logs_status;
161 if (absl::GetFlag(FLAGS_log_files).empty()) {
162 std::string default_log_path = JoinPath(host.log_dir, "launcher.log");
163 unlink(default_log_path.c_str()); // Clean from previous run
164 logs_status = LogToFiles({default_log_path});
165 } else {
166 logs_status = LogToFiles(absl::GetFlag(FLAGS_log_files));
167 if (!logs_status.ok()) {
168 return logs_status;
169 }
170 }
171 if (!logs_status.ok()) {
172 return logs_status;
173 }
174
175 VLOG(1) << host;
176
177 setenv("LD_LIBRARY_PATH", JoinPath(host.host_artifacts_path, "lib64").c_str(),
178 1);
179
180 if (args.size() < 2) {
181 std::string err = absl::StrCat("Wanted argv.size() > 1, was ", args.size());
182 return absl::InvalidArgumentError(err);
183 }
184 std::string exe = CleanPath(args[1]);
185 std::vector<std::string> exe_argv(++args.begin(), args.end());
186
187 if (absl::EndsWith(exe, "cvd_internal_start")) {
188 setenv("TMPDIR", host.tmp_dir.c_str(), 1);
189 }
190
191 auto sandbox_manager_res = SandboxManager::Create(std::move(host));
192 if (!sandbox_manager_res.ok()) {
193 return sandbox_manager_res.status();
194 }
195 std::unique_ptr<SandboxManager> manager = std::move(*sandbox_manager_res);
196
197 std::vector<std::pair<FDCloser, int>> fds;
198 for (int i = 0; i <= 2; i++) {
199 auto duped = fcntl(i, F_DUPFD_CLOEXEC, 0);
200 if (duped < 0) {
201 static constexpr char kErr[] = "Failed to `dup` stdio file descriptor";
202 return absl::ErrnoToStatus(errno, kErr);
203 }
204 fds.emplace_back(FDCloser(duped), i);
205 }
206
207 std::vector<std::string> this_env;
208 for (size_t i = 0; environ[i] != nullptr; i++) {
209 this_env.emplace_back(environ[i]);
210 }
211
212 absl::Status status = manager->RunProcess(std::nullopt, std::move(exe_argv),
213 std::move(fds), this_env);
214 if (!status.ok()) {
215 return status;
216 }
217
218 while (manager->Running()) {
219 absl::Status iter = manager->Iterate();
220 if (!iter.ok()) {
221 LOG(ERROR) << "Error in SandboxManager::Iterate: " << iter.ToString();
222 }
223 }
224
225 absl::StatusOr<PidFd> self_pidfd = PidFd::FromRunningProcess(getpid());
226 if (!self_pidfd.ok()) {
227 return self_pidfd.status();
228 }
229
230 return self_pidfd->HaltChildHierarchy();
231 }
232
233 } // namespace
234 } // namespace cuttlefish::process_sandboxer
235
main(int argc,char ** argv)236 int main(int argc, char** argv) {
237 auto status = cuttlefish::process_sandboxer::ProcessSandboxerMain(argc, argv);
238 if (status.ok()) {
239 VLOG(1) << "process_sandboxer exiting normally";
240 return 0;
241 }
242 LOG(ERROR) << status.ToString();
243 return status.raw_code();
244 }
245