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