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