1 /*
2 * Copyright (C) 2018 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 #ifndef ANDROID_APEXD_APEXD_UTILS_H_
18 #define ANDROID_APEXD_APEXD_UTILS_H_
19
20 #include <chrono>
21 #include <filesystem>
22 #include <string>
23 #include <thread>
24 #include <vector>
25
26 #include <dirent.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <sys/wait.h>
30
31 #include <android-base/chrono_utils.h>
32 #include <android-base/logging.h>
33 #include <android-base/result.h>
34 #include <android-base/scopeguard.h>
35 #include <android-base/strings.h>
36 #include <cutils/android_reboot.h>
37
38 #include "apex_constants.h"
39 #include "string_log.h"
40
41 using android::base::ErrnoError;
42 using android::base::Error;
43 using android::base::Result;
44
45 namespace android {
46 namespace apex {
47
WaitChild(pid_t pid)48 inline int WaitChild(pid_t pid) {
49 int status;
50 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
51
52 if (got_pid != pid) {
53 PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid;
54 return 1;
55 }
56
57 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
58 return 0;
59 } else {
60 return status;
61 }
62 }
63
ForkAndRun(const std::vector<std::string> & args)64 inline Result<void> ForkAndRun(const std::vector<std::string>& args) {
65 LOG(DEBUG) << "Forking : " << android::base::Join(args, " ");
66 std::vector<const char*> argv;
67 argv.resize(args.size() + 1, nullptr);
68 std::transform(args.begin(), args.end(), argv.begin(),
69 [](const std::string& in) { return in.c_str(); });
70
71 pid_t pid = fork();
72 if (pid == -1) {
73 // Fork failed.
74 return ErrnoError() << "Unable to fork";
75 }
76
77 if (pid == 0) {
78 execv(argv[0], const_cast<char**>(argv.data()));
79 PLOG(ERROR) << "execv failed";
80 _exit(1);
81 }
82
83 int rc = WaitChild(pid);
84 if (rc != 0) {
85 return Error() << "Failed run: status=" << rc;
86 }
87 return {};
88 }
89
90 template <typename Fn>
WalkDir(const std::string & path,Fn fn)91 Result<void> WalkDir(const std::string& path, Fn fn) {
92 namespace fs = std::filesystem;
93 std::error_code ec;
94 auto it = fs::directory_iterator(path, ec);
95 auto end = fs::directory_iterator();
96 while (!ec && it != end) {
97 fn(*it);
98 it.increment(ec);
99 }
100 if (ec) {
101 return Error() << "Can't open " << path
102 << " for reading : " << ec.message();
103 }
104 return {};
105 }
106
107 template <typename FilterFn>
ReadDir(const std::string & path,FilterFn fn)108 Result<std::vector<std::string>> ReadDir(const std::string& path, FilterFn fn) {
109 namespace fs = std::filesystem;
110
111 std::vector<std::string> ret;
112 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
113 if (fn(entry)) {
114 ret.push_back(entry.path());
115 }
116 });
117 if (!status.ok()) {
118 return status.error();
119 }
120 return ret;
121 }
122
IsEmptyDirectory(const std::string & path)123 inline bool IsEmptyDirectory(const std::string& path) {
124 auto res = ReadDir(path, [](auto _) { return true; });
125 return res.ok() && res->empty();
126 }
127
createDirIfNeeded(const std::string & path,mode_t mode)128 inline Result<void> createDirIfNeeded(const std::string& path, mode_t mode) {
129 struct stat stat_data;
130
131 if (stat(path.c_str(), &stat_data) != 0) {
132 if (errno == ENOENT) {
133 if (mkdir(path.c_str(), mode) != 0) {
134 return ErrnoError() << "Could not mkdir " << path;
135 }
136 } else {
137 return ErrnoError() << "Could not stat " << path;
138 }
139 } else {
140 if (!S_ISDIR(stat_data.st_mode)) {
141 return Error() << path << " exists and is not a directory.";
142 }
143 }
144
145 // Need to manually call chmod because mkdir will create a folder with
146 // permissions mode & ~umask.
147 if (chmod(path.c_str(), mode) != 0) {
148 return ErrnoError() << "Could not chmod " << path;
149 }
150
151 return {};
152 }
153
DeleteDirContent(const std::string & path)154 inline Result<void> DeleteDirContent(const std::string& path) {
155 auto files = ReadDir(path, [](auto _) { return true; });
156 if (!files.ok()) {
157 return Error() << "Failed to delete " << path << " : " << files.error();
158 }
159 for (const std::string& file : *files) {
160 if (unlink(file.c_str()) != 0) {
161 return ErrnoError() << "Failed to delete " << file;
162 }
163 }
164 return {};
165 }
166
DeleteDir(const std::string & path)167 inline Result<void> DeleteDir(const std::string& path) {
168 namespace fs = std::filesystem;
169 std::error_code ec;
170 fs::remove_all(path, ec);
171 if (ec) {
172 return Error() << "Failed to delete path " << path << " : " << ec.message();
173 }
174 return {};
175 }
176
get_path_inode(const std::string & path)177 inline Result<ino_t> get_path_inode(const std::string& path) {
178 struct stat buf;
179 memset(&buf, 0, sizeof(buf));
180 if (stat(path.c_str(), &buf) != 0) {
181 return ErrnoError() << "Failed to stat " << path;
182 } else {
183 return buf.st_ino;
184 }
185 }
186
PathExists(const std::string & path)187 inline Result<bool> PathExists(const std::string& path) {
188 namespace fs = std::filesystem;
189
190 std::error_code ec;
191 if (!fs::exists(fs::path(path), ec)) {
192 if (ec) {
193 return Error() << "Failed to access " << path << " : " << ec.message();
194 } else {
195 return false;
196 }
197 }
198 return true;
199 }
200
Reboot()201 inline void Reboot() {
202 LOG(INFO) << "Rebooting device";
203 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
204 LOG(ERROR) << "Failed to reboot device";
205 }
206 }
207
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)208 inline Result<void> WaitForFile(const std::string& path,
209 std::chrono::nanoseconds timeout) {
210 android::base::Timer t;
211 bool has_slept = false;
212 while (t.duration() < timeout) {
213 struct stat sb;
214 if (stat(path.c_str(), &sb) != -1) {
215 if (has_slept) {
216 LOG(INFO) << "wait for '" << path << "' took " << t;
217 }
218 return {};
219 }
220 std::this_thread::sleep_for(5ms);
221 has_slept = true;
222 }
223 return ErrnoError() << "wait for '" << path << "' timed out and took " << t;
224 }
225
GetSubdirs(const std::string & path)226 inline Result<std::vector<std::string>> GetSubdirs(const std::string& path) {
227 namespace fs = std::filesystem;
228 auto filter_fn = [](const std::filesystem::directory_entry& entry) {
229 std::error_code ec;
230 bool result = entry.is_directory(ec);
231 if (ec) {
232 LOG(ERROR) << "Failed to check is_directory : " << ec.message();
233 return false;
234 }
235 return result;
236 };
237 return ReadDir(path, filter_fn);
238 }
239
GetDeUserDirs()240 inline Result<std::vector<std::string>> GetDeUserDirs() {
241 return GetSubdirs(kDeNDataDir);
242 }
243
244 } // namespace apex
245 } // namespace android
246
247 #endif // ANDROID_APEXD_APEXD_UTILS_H_
248