• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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 "host/commands/cvd/common_utils.h"
18 
19 #include <unistd.h>
20 
21 #include <algorithm>
22 #include <memory>
23 #include <sstream>
24 #include <stack>
25 
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/strings.h>
29 
30 #include "common/libs/utils/contains.h"
31 #include "common/libs/utils/files.h"
32 #include "common/libs/utils/users.h"
33 
34 namespace cuttlefish {
35 
MakeRequest(const MakeRequestForm & request_form)36 cvd::Request MakeRequest(const MakeRequestForm& request_form) {
37   return MakeRequest(request_form, cvd::WAIT_BEHAVIOR_COMPLETE);
38 }
39 
MakeRequest(const MakeRequestForm & request_form,cvd::WaitBehavior wait_behavior)40 cvd::Request MakeRequest(const MakeRequestForm& request_form,
41                          cvd::WaitBehavior wait_behavior) {
42   const auto& args = request_form.cmd_args;
43   const auto& env = request_form.env;
44   const auto& selector_args = request_form.selector_args;
45   cvd::Request request;
46   auto command_request = request.mutable_command_request();
47   for (const std::string& arg : args) {
48     command_request->add_args(arg);
49   }
50   auto selector_opts = command_request->mutable_selector_opts();
51   for (const std::string& selector_arg : selector_args) {
52     selector_opts->add_args(selector_arg);
53   }
54 
55   for (const auto& [key, value] : env) {
56     (*command_request->mutable_env())[key] = value;
57   }
58 
59   /*
60    * the client must set the kAndroidHostOut environment variable. There were,
61    * however, a few branches where kAndroidSoongHostOut replaced
62    * kAndroidHostOut. Cvd server eventually read kAndroidHostOut only and set
63    * both for the subtools.
64    *
65    * If none of the two are set, cvd server tries to use the parent directory of
66    * the client cvd executable as env[kAndroidHostOut].
67    *
68    */
69   if (!Contains(command_request->env(), kAndroidHostOut)) {
70     const std::string new_android_host_out =
71         Contains(command_request->env(), kAndroidSoongHostOut)
72             ? (*command_request->mutable_env())[kAndroidSoongHostOut]
73             : android::base::Dirname(android::base::GetExecutableDirectory());
74     (*command_request->mutable_env())[kAndroidHostOut] = new_android_host_out;
75   }
76 
77   if (!request_form.working_dir) {
78     std::unique_ptr<char, void (*)(void*)> cwd(getcwd(nullptr, 0), &free);
79     command_request->set_working_directory(cwd.get());
80   } else {
81     command_request->set_working_directory(request_form.working_dir.value());
82   }
83   command_request->set_wait_behavior(wait_behavior);
84 
85   return request;
86 }
87 
88 // given /a/b/c/d/e, ensures
89 // all directories from /a through /a/b/c/d/e exist
EnsureDirectoryExistsAllTheWay(const std::string & dir)90 Result<void> EnsureDirectoryExistsAllTheWay(const std::string& dir) {
91   CF_EXPECT(!dir.empty() && dir.at(0) == '/',
92             "EnsureDirectoryExistsAllTheWay() handles absolute paths only.");
93   if (dir == "/") {
94     return {};
95   }
96   std::string path_exclude_root = dir.substr(1);
97   std::vector<std::string> tokens =
98       android::base::Tokenize(path_exclude_root, "/");
99   std::string current_dir = "/";
100   for (int i = 0; i < tokens.size(); i++) {
101     current_dir.append(tokens[i]);
102     CF_EXPECT(EnsureDirectoryExists(current_dir),
103               current_dir << " does not exist and cannot be created.");
104     current_dir.append("/");
105   }
106   return {};
107 }
108 
Reverse(std::stack<std::string> & s)109 static std::vector<std::string> Reverse(std::stack<std::string>& s) {
110   std::vector<std::string> reversed;
111   while (!s.empty()) {
112     reversed.push_back(s.top());
113     s.pop();
114   }
115   std::reverse(reversed.begin(), reversed.end());
116   return reversed;
117 }
118 
EmulateAbsolutePathImpl(std::stack<std::string> & so_far,const std::vector<std::string> & tokens,const size_t idx=0)119 static std::vector<std::string> EmulateAbsolutePathImpl(
120     std::stack<std::string>& so_far, const std::vector<std::string>& tokens,
121     const size_t idx = 0) {
122   if (idx == tokens.size()) {
123     return Reverse(so_far);
124   }
125   const std::string& token = tokens.at(idx);
126   if (token == "." || token.empty()) {
127     // If token is empty, it might be //, so should be simply ignored
128     return EmulateAbsolutePathImpl(so_far, tokens, idx + 1);
129   }
130   if (token == "..") {
131     if (!so_far.empty()) {
132       // at /, ls ../../.. shows just the root. So, if too many ..s are here,
133       // we silently ignore them
134       so_far.pop();
135     }
136     return EmulateAbsolutePathImpl(so_far, tokens, idx + 1);
137   }
138   so_far.push(token);
139   return EmulateAbsolutePathImpl(so_far, tokens, idx + 1);
140 }
141 
142 template <typename T>
operator <<(std::ostream & out,const std::vector<T> & v)143 std::ostream& operator<<(std::ostream& out, const std::vector<T>& v) {
144   if (v.empty()) {
145     out << "{}";
146     return out;
147   }
148   if (v.size() == 1) {
149     out << "{" << v.front() << "}";
150     return out;
151   }
152   out << "{";
153   for (size_t i = 0; i != v.size() - 1; i++) {
154     out << v.at(i) << ", ";
155   }
156   out << v.back() << "}";
157   return out;
158 }
159 
EmulateAbsolutePath(const InputPathForm & path_info)160 Result<std::string> EmulateAbsolutePath(const InputPathForm& path_info) {
161   const auto& path = path_info.path_to_convert;
162   std::string working_dir;
163   if (path_info.current_working_dir) {
164     working_dir = *path_info.current_working_dir;
165   } else {
166     std::unique_ptr<char, void (*)(void*)> cwd(getcwd(nullptr, 0), &free);
167     std::string process_cwd(cwd.get());
168     working_dir = std::move(process_cwd);
169   }
170   CF_EXPECT(android::base::StartsWith(working_dir, '/'),
171             "Current working directory should be given in an absolute path.");
172 
173   const std::string home_dir = path_info.home_dir
174                                    ? *path_info.home_dir
175                                    : CF_EXPECT(SystemWideUserHome());
176   CF_EXPECT(android::base::StartsWith(home_dir, '/'),
177             "Home directory should be given in an absolute path.");
178 
179   if (path.empty()) {
180     LOG(ERROR) << "The requested path to convert an absolute path is empty.";
181     return "";
182   }
183   if (path == "/") {
184     return path;
185   }
186   std::vector<std::string> tokens = android::base::Tokenize(path, "/");
187   std::stack<std::string> prefix_dir_stack;
188   if (path == "~" || android::base::StartsWith(path, "~/")) {
189     // tokens == {"~", "some", "dir", "file"}
190     std::vector<std::string> home_dir_tokens =
191         android::base::Tokenize(home_dir, "/");
192     tokens.erase(tokens.begin());
193     for (const auto& home_dir_token : home_dir_tokens) {
194       prefix_dir_stack.push(home_dir_token);
195     }
196   } else if (!android::base::StartsWith(path, "/")) {
197     // path was like "a/b/c", which should be expanded to $PWD/a/b/c
198     std::vector<std::string> working_dir_tokens =
199         android::base::Tokenize(working_dir, "/");
200     for (const auto& working_dir_token : working_dir_tokens) {
201       prefix_dir_stack.push(working_dir_token);
202     }
203   }
204 
205   auto result = EmulateAbsolutePathImpl(prefix_dir_stack, tokens, 0);
206   std::stringstream assemble_output;
207   assemble_output << "/";
208   if (!result.empty()) {
209     assemble_output << android::base::Join(result, "/");
210   }
211   if (path_info.follow_symlink) {
212     return AbsolutePath(assemble_output.str());
213   }
214   return assemble_output.str();
215 }
216 
217 }  // namespace cuttlefish
218