blob: e97c0ba6fe678e1f5682cafb7c22834d6bb3f52a [file] [log] [blame]
Andreas Gampee1a40392018-11-30 09:47:17 -08001/*
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
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090020#include <chrono>
Nikita Ioffea82b0a82019-02-15 18:59:47 +000021#include <filesystem>
Andreas Gampee1a40392018-11-30 09:47:17 -080022#include <string>
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090023#include <thread>
Andreas Gampee1a40392018-11-30 09:47:17 -080024#include <vector>
25
Martijn Coenen610909b2019-01-18 13:49:38 +010026#include <dirent.h>
Nikita Ioffea8453da2019-01-30 21:29:13 +000027#include <sys/stat.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080028#include <sys/types.h>
29#include <sys/wait.h>
30
Jiyong Parkd8a82ce2019-02-25 02:21:18 +090031#include <android-base/chrono_utils.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080032#include <android-base/logging.h>
Nikita Ioffe31dc34d2019-02-23 15:59:04 +000033#include <cutils/android_reboot.h>
Andreas Gampee1a40392018-11-30 09:47:17 -080034
Jooyung Han451cc342019-04-12 04:52:42 +090035#include "status_or.h"
Andreas Gampee1a40392018-11-30 09:47:17 -080036#include "string_log.h"
37
38namespace android {
39namespace apex {
40
41inline 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
57inline 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
Jooyung Hanaed3a032019-05-03 00:42:31 +090085template <typename Fn>
86Status WalkDir(const std::string& path, Fn fn) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +000087 namespace fs = std::filesystem;
Jooyung Han86b0e382019-04-24 10:11:56 +090088 std::error_code ec;
Jooyung Han86b0e382019-04-24 10:11:56 +090089 auto it = fs::directory_iterator(path, ec);
90 auto end = fs::directory_iterator();
91 while (!ec && it != end) {
Jooyung Hanaed3a032019-05-03 00:42:31 +090092 fn(*it);
Jooyung Han86b0e382019-04-24 10:11:56 +090093 it.increment(ec);
94 }
Martijn Coenen809c3632019-05-01 09:20:42 +020095 if (ec) {
Jooyung Han86b0e382019-04-24 10:11:56 +090096 return Status::Fail(StringLog() << "Can't open " << path
97 << " for reading : " << ec.message());
Martijn Coenen809c3632019-05-01 09:20:42 +020098 }
Jooyung Hanaed3a032019-05-03 00:42:31 +090099 return Status::Success();
100}
101
102template <typename FilterFn>
103StatusOr<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 }
Jooyung Han86b0e382019-04-24 10:11:56 +0900117 return Status(std::move(ret));
Jooyung Hanaed3a032019-05-03 00:42:31 +0900118}
Martijn Coenen610909b2019-01-18 13:49:38 +0100119
Jiyong Park715e23d2019-02-22 22:14:37 +0900120inline bool IsEmptyDirectory(const std::string& path) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +0000121 auto res = ReadDir(path, [](auto _) { return true; });
Jiyong Park715e23d2019-02-22 22:14:37 +0900122 return res.Ok() && res->empty();
123}
124
Nikita Ioffea8453da2019-01-30 21:29:13 +0000125inline 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
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000151inline Status DeleteDirContent(const std::string& path) {
Mohammad Samiul Islamd428a182019-03-19 14:36:24 +0000152 auto files = ReadDir(path, [](auto _) { return true; });
Nikita Ioffea82b0a82019-02-15 18:59:47 +0000153 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
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000165inline StatusOr<bool> PathExists(const std::string& path) {
166 namespace fs = std::filesystem;
Jooyung Han86b0e382019-04-24 10:11:56 +0900167 using Status = StatusOr<bool>;
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000168
169 std::error_code ec;
170 if (!fs::exists(fs::path(path), ec)) {
171 if (ec) {
Jooyung Han86b0e382019-04-24 10:11:56 +0900172 return Status::Fail(StringLog() << "Failed to access " << path << " : "
173 << ec.message());
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000174 } else {
Jooyung Han86b0e382019-04-24 10:11:56 +0900175 return Status(false);
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000176 }
177 }
Jooyung Han86b0e382019-04-24 10:11:56 +0900178 return Status(true);
Nikita Ioffe9ae986a2019-02-18 22:39:27 +0000179}
180
Nikita Ioffe31dc34d2019-02-23 15:59:04 +0000181inline 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
Jiyong Parkd8a82ce2019-02-25 02:21:18 +0900188inline 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
Andreas Gampee1a40392018-11-30 09:47:17 -0800207} // namespace apex
208} // namespace android
209
210#endif // ANDROID_APEXD_APEXD_UTILS_H_