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 #include "Storage.h"
18 
19 #include <sstream>
20 
21 #include <android-base/chrono_utils.h>
22 #include <android-base/file.h>
23 #include <android-base/logging.h>
24 #include <android-base/strings.h>
25 #include <fstab/fstab.h>
26 
27 namespace android {
28 namespace hardware {
29 namespace health {
30 namespace storage {
31 namespace V1_0 {
32 namespace implementation {
33 
34 using base::ReadFileToString;
35 using base::Timer;
36 using base::Trim;
37 using base::WriteStringToFd;
38 using base::WriteStringToFile;
39 using fs_mgr::Fstab;
40 using fs_mgr::ReadDefaultFstab;
41 
getGarbageCollectPath()42 std::string getGarbageCollectPath() {
43     Fstab fstab;
44     ReadDefaultFstab(&fstab);
45 
46     for (const auto& entry : fstab) {
47         if (!entry.sysfs_path.empty()) {
48             return entry.sysfs_path + "/manual_gc";
49         }
50     }
51 
52     return "";
53 }
54 
garbageCollect(uint64_t timeoutSeconds,const sp<IGarbageCollectCallback> & cb)55 Return<void> Storage::garbageCollect(uint64_t timeoutSeconds,
56                                      const sp<IGarbageCollectCallback>& cb) {
57     Result result = Result::SUCCESS;
58     std::string path = getGarbageCollectPath();
59 
60     if (path.empty()) {
61         LOG(WARNING) << "Cannot find Dev GC path";
62         result = Result::UNKNOWN_ERROR;
63     } else {
64         Timer timer;
65         LOG(INFO) << "Start Dev GC on " << path;
66         while (1) {
67             std::string require;
68             if (!ReadFileToString(path, &require)) {
69                 PLOG(WARNING) << "Reading manual_gc failed in " << path;
70                 result = Result::IO_ERROR;
71                 break;
72             }
73             require = Trim(require);
74             if (require == "" || require == "off" || require == "disabled") {
75                 LOG(DEBUG) << "No more to do Dev GC";
76                 break;
77             }
78             LOG(DEBUG) << "Trigger Dev GC on " << path;
79             if (!WriteStringToFile("1", path)) {
80                 PLOG(WARNING) << "Start Dev GC failed on " << path;
81                 result = Result::IO_ERROR;
82                 break;
83             }
84             if (timer.duration() >= std::chrono::seconds(timeoutSeconds)) {
85                 LOG(WARNING) << "Dev GC timeout";
86                 // Timeout is not treated as an error. Try next time.
87                 break;
88             }
89             sleep(2);
90         }
91         LOG(INFO) << "Stop Dev GC on " << path;
92         if (!WriteStringToFile("0", path)) {
93             PLOG(WARNING) << "Stop Dev GC failed on " << path;
94             result = Result::IO_ERROR;
95         }
96     }
97 
98     if (cb != nullptr) {
99         auto ret = cb->onFinish(result);
100         if (!ret.isOk()) {
101             LOG(WARNING) << "Cannot return result to callback: " << ret.description();
102         }
103     }
104     return Void();
105 }
106 
debug(const hidl_handle & handle,const hidl_vec<hidl_string> &)107 Return<void> Storage::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {
108     if (handle == nullptr || handle->numFds < 1) {
109         return Void();
110     }
111 
112     int fd = handle->data[0];
113     std::stringstream output;
114 
115     std::string path = getGarbageCollectPath();
116     if (path.empty()) {
117         output << "Cannot find Dev GC path";
118     } else {
119         std::string require;
120 
121         if (ReadFileToString(path, &require)) {
122             output << path << ":" << require << std::endl;
123         }
124 
125         if (WriteStringToFile("0", path)) {
126             output << "stop success" << std::endl;
127         }
128     }
129 
130     if (!WriteStringToFd(output.str(), fd)) {
131         PLOG(WARNING) << "debug: cannot write to fd";
132     }
133 
134     fsync(fd);
135 
136     return Void();
137 }
138 
139 }  // namespace implementation
140 }  // namespace V1_0
141 }  // namespace storage
142 }  // namespace health
143 }  // namespace hardware
144 }  // namespace android
145