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 <cutils/android_reboot.h>
34
35 #include "status_or.h"
36 #include "string_log.h"
37
38 namespace android {
39 namespace apex {
40
WaitChild(pid_t pid)41 inline int WaitChild(pid_t pid) {
42 int status;
43 pid_t got_pid = TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
44
45 if (got_pid != pid) {
46 PLOG(WARNING) << "waitpid failed: wanted " << pid << ", got " << got_pid;
47 return 1;
48 }
49
50 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
51 return 0;
52 } else {
53 return status;
54 }
55 }
56
ForkAndRun(const std::vector<std::string> & args,std::string * error_msg)57 inline int ForkAndRun(const std::vector<std::string>& args,
58 std::string* error_msg) {
59 std::vector<const char*> argv;
60 argv.resize(args.size() + 1, nullptr);
61 std::transform(args.begin(), args.end(), argv.begin(),
62 [](const std::string& in) { return in.c_str(); });
63
64 // 3) Fork.
65 pid_t pid = fork();
66 if (pid == -1) {
67 // Fork failed.
68 *error_msg = PStringLog() << "Unable to fork";
69 return -1;
70 }
71
72 if (pid == 0) {
73 execv(argv[0], const_cast<char**>(argv.data()));
74 PLOG(ERROR) << "execv failed";
75 _exit(1);
76 }
77
78 int rc = WaitChild(pid);
79 if (rc != 0) {
80 *error_msg = StringLog() << "Failed run: status=" << rc;
81 }
82 return rc;
83 }
84
85 template <typename Fn>
WalkDir(const std::string & path,Fn fn)86 Status WalkDir(const std::string& path, Fn fn) {
87 namespace fs = std::filesystem;
88 std::error_code ec;
89 auto it = fs::directory_iterator(path, ec);
90 auto end = fs::directory_iterator();
91 while (!ec && it != end) {
92 fn(*it);
93 it.increment(ec);
94 }
95 if (ec) {
96 return Status::Fail(StringLog() << "Can't open " << path
97 << " for reading : " << ec.message());
98 }
99 return Status::Success();
100 }
101
102 template <typename FilterFn>
ReadDir(const std::string & path,FilterFn fn)103 StatusOr<std::vector<std::string>> ReadDir(const std::string& path,
104 FilterFn fn) {
105 namespace fs = std::filesystem;
106 using Status = StatusOr<std::vector<std::string>>;
107
108 std::vector<std::string> ret;
109 auto status = WalkDir(path, [&](const fs::directory_entry& entry) {
110 if (fn(entry)) {
111 ret.push_back(entry.path());
112 }
113 });
114 if (!status.Ok()) {
115 return Status::Fail(status.ErrorMessage());
116 }
117 return Status(std::move(ret));
118 }
119
IsEmptyDirectory(const std::string & path)120 inline bool IsEmptyDirectory(const std::string& path) {
121 auto res = ReadDir(path, [](auto _) { return true; });
122 return res.Ok() && res->empty();
123 }
124
createDirIfNeeded(const std::string & path,mode_t mode)125 inline Status createDirIfNeeded(const std::string& path, mode_t mode) {
126 struct stat stat_data;
127
128 if (stat(path.c_str(), &stat_data) != 0) {
129 if (errno == ENOENT) {
130 if (mkdir(path.c_str(), mode) != 0) {
131 return Status::Fail(PStringLog() << "Could not mkdir " << path);
132 }
133 } else {
134 return Status::Fail(PStringLog() << "Could not stat " << path);
135 }
136 } else {
137 if (!S_ISDIR(stat_data.st_mode)) {
138 return Status::Fail(path + " exists and is not a directory.");
139 }
140 }
141
142 // Need to manually call chmod because mkdir will create a folder with
143 // permissions mode & ~umask.
144 if (chmod(path.c_str(), mode) != 0) {
145 return Status::Fail(PStringLog() << "Could not chmod " << path);
146 }
147
148 return Status::Success();
149 }
150
DeleteDirContent(const std::string & path)151 inline Status DeleteDirContent(const std::string& path) {
152 auto files = ReadDir(path, [](auto _) { return true; });
153 if (!files.Ok()) {
154 return Status::Fail(StringLog() << "Failed to delete " << path << " : "
155 << files.ErrorMessage());
156 }
157 for (const std::string& file : *files) {
158 if (unlink(file.c_str()) != 0) {
159 return Status::Fail(PStringLog() << "Failed to delete " << file);
160 }
161 }
162 return Status::Success();
163 }
164
PathExists(const std::string & path)165 inline StatusOr<bool> PathExists(const std::string& path) {
166 namespace fs = std::filesystem;
167 using Status = StatusOr<bool>;
168
169 std::error_code ec;
170 if (!fs::exists(fs::path(path), ec)) {
171 if (ec) {
172 return Status::Fail(StringLog() << "Failed to access " << path << " : "
173 << ec.message());
174 } else {
175 return Status(false);
176 }
177 }
178 return Status(true);
179 }
180
Reboot()181 inline void Reboot() {
182 LOG(INFO) << "Rebooting device";
183 if (android_reboot(ANDROID_RB_RESTART2, 0, nullptr) != 0) {
184 LOG(ERROR) << "Failed to reboot device";
185 }
186 }
187
WaitForFile(const std::string & path,std::chrono::nanoseconds timeout)188 inline Status WaitForFile(const std::string& path,
189 std::chrono::nanoseconds timeout) {
190 android::base::Timer t;
191 bool has_slept = false;
192 while (t.duration() < timeout) {
193 struct stat sb;
194 if (stat(path.c_str(), &sb) != -1) {
195 if (has_slept) {
196 LOG(INFO) << "wait for '" << path << "' took " << t;
197 }
198 return Status::Success();
199 }
200 std::this_thread::sleep_for(5ms);
201 has_slept = true;
202 }
203 return Status::Fail(PStringLog()
204 << "wait for '" << path << "' timed out and took " << t);
205 }
206
207 } // namespace apex
208 } // namespace android
209
210 #endif // ANDROID_APEXD_APEXD_UTILS_H_
211