• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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