1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "connection_detector.h"
17 
18 #include <dirent.h>
19 #include <iostream>
20 #include <sstream>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <vector>
24 
25 #include "utils_log.h"
26 
27 namespace OHOS {
28 namespace Storage {
29 namespace DistributedFile {
GetCellByIndex(const std::string & str,int targetIndex)30 std::string ConnectionDetector::GetCellByIndex(const std::string &str, int targetIndex)
31 {
32     size_t begin = 0;
33     size_t end = 0;
34     int count = 0;
35     do {
36         size_t i;
37         for (i = begin; i < str.size(); ++i) {
38             if (str[i] != '\t' && str[i] != ' ') {
39                 break;
40             }
41         }
42         begin = i;
43         for (i = begin; i < str.size(); ++i) {
44             if (!(str[i] != '\t' && str[i] != ' ')) {
45                 break;
46             }
47         }
48         end = i;
49         if (end == str.size()) {
50             break;
51         }
52         ++count;
53         if (count == targetIndex) {
54             break;
55         }
56         begin = end;
57     } while (true);
58     return str.substr(begin, end - begin);
59 }
60 
MatchConnectionStatus(ifstream & inputFile)61 bool ConnectionDetector::MatchConnectionStatus(ifstream &inputFile)
62 {
63     string str;
64     getline(inputFile, str);
65     if (str.find("connection_status") == string::npos) {
66         return false;
67     }
68     while (getline(inputFile, str)) {
69         if (str.empty()) {
70             return false;
71         }
72         auto connectionStatus = GetCellByIndex(str, 2);  // 2 indicates the position of connection status
73         auto tcpStatus = GetCellByIndex(str, 3);  // 3 indicates the position of tcp status
74         // "2"|"3" indicates socket status is connecting|connected;
75         if (connectionStatus == "2" && (tcpStatus == "2" || tcpStatus == "3")) {
76             return true;
77         }
78     }
79     return false;
80 }
81 
MatchConnectionGroup(const std::string & fileName,const string & networkId)82 bool ConnectionDetector::MatchConnectionGroup(const std::string &fileName, const string &networkId)
83 {
84     if (access(fileName.c_str(), F_OK) != 0) {
85         LOGE("Cannot find the status file");
86         return false;
87     }
88     ifstream statusFile(fileName);
89     std::string str;
90     getline(statusFile, str);
91     bool result = false;
92     while (getline(statusFile, str)) {
93         if (str.find(networkId) == 0) {
94             result = MatchConnectionStatus(statusFile);
95             break;
96         }
97     }
98     statusFile.close();
99     return result;
100 }
101 
FilterFunc(const struct dirent * filename)102 static int FilterFunc(const struct dirent *filename)
103 {
104     if (string_view(filename->d_name) == "." || string_view(filename->d_name) == "..") {
105         return DISMATCH;
106     }
107     return MATCH;
108 }
109 
110 struct NameList {
111     struct dirent **namelist = {nullptr};
112     int direntNum = 0;
113 };
114 
Deleter(struct NameList * arg)115 static void Deleter(struct NameList *arg)
116 {
117     if (arg == nullptr) {
118         return;
119     }
120 
121     if (arg->namelist != nullptr) {
122         for (int i = 0; i < arg->direntNum; i++) {
123             free((arg->namelist)[i]);
124             (arg->namelist)[i] = nullptr;
125         }
126         free(arg->namelist);
127         arg->namelist = nullptr;
128     }
129     delete arg;
130     arg = nullptr;
131 }
132 
CheckValidDir(const std::string & path)133 bool ConnectionDetector::CheckValidDir(const std::string &path)
134 {
135     struct stat buf {
136     };
137     auto ret = stat(path.c_str(), &buf);
138     if (ret == -1) {
139         LOGE("stat failed, errno = %{public}d", errno);
140         return false;
141     }
142     if ((buf.st_mode & S_IFMT) != S_IFDIR) {
143         LOGE("It is not a dir.");
144         return false;
145     }
146     return true;
147 }
148 
GetConnectionStatus(const std::string & targetDir,const std::string & networkId)149 bool ConnectionDetector::GetConnectionStatus(const std::string &targetDir, const std::string &networkId)
150 {
151     if (!CheckValidDir(SYS_HMDFS_PATH)) {
152         return false;
153     }
154     unique_ptr<struct NameList, decltype(Deleter) *> pNameList = {new (nothrow) struct NameList, Deleter};
155     if (pNameList == nullptr) {
156         LOGE("Failed to request heap memory.");
157         return false;
158     }
159     int num = scandir(SYS_HMDFS_PATH.c_str(), &(pNameList->namelist), FilterFunc, alphasort);
160     if (num < 0) {
161         LOGE("Failed to scandir.");
162         return false;
163     }
164     pNameList->direntNum = num;
165     for (int i = 0; i < num; i++) {
166         if ((pNameList->namelist[i])->d_name != targetDir) {
167             continue;
168         }
169         string dest = SYS_HMDFS_PATH + '/' + targetDir;
170         if ((pNameList->namelist[i])->d_type == DT_DIR) {
171             string statusFile = SYS_HMDFS_PATH + '/' + targetDir + '/' + CONNECTION_STATUS_FILE_NAME;
172             if (MatchConnectionGroup(statusFile, networkId)) {
173                 LOGI("Parse connection status success.");
174                 return true;
175             }
176             break;
177         }
178     }
179     return false;
180 }
181 
MocklispHash(const string & str)182 uint64_t ConnectionDetector::MocklispHash(const string &str)
183 {
184     uint64_t res = 0;
185     constexpr int mocklispHashPos = 5;
186     /* Mocklisp hash function. */
187     for (auto ch : str) {
188         res = (res << mocklispHashPos) - res + (uint64_t)ch;
189     }
190     return res;
191 }
192 
RepeatGetConnectionStatus(const std::string & targetDir,const std::string & networkId)193 int32_t ConnectionDetector::RepeatGetConnectionStatus(const std::string &targetDir, const std::string &networkId)
194 {
195     int retryCount = 0;
196     while (retryCount++ < MAX_RETRY - 1 && !GetConnectionStatus(targetDir, networkId)) {
197         usleep(CHECK_SESSION_DELAY_TIME);
198     }
199     return retryCount == MAX_RETRY ? -1 : NO_ERROR;
200 }
201 
GetCurrentUserId()202 int32_t ConnectionDetector::GetCurrentUserId()
203 {
204     std::vector<int32_t> userIds{};
205     auto ret = AccountSA::OsAccountManager::QueryActiveOsAccountIds(userIds);
206     if (ret != NO_ERROR || userIds.empty()) {
207         LOGE("query active os account id failed, ret = %{public}d", ret);
208         return INVALID_USER_ID;
209     }
210     return userIds[0];
211 }
212 
ParseHmdfsPath()213 std::string ConnectionDetector::ParseHmdfsPath()
214 {
215     auto userId = GetCurrentUserId();
216     if (userId == INVALID_USER_ID) {
217         return "";
218     }
219     std::string path = HMDFS_PATH;
220     size_t pos = path.find(CURRENT_USER_ID_FLAG);
221     if (pos == std::string::npos) {
222         return "";
223     }
224     return path.replace(pos, CURRENT_USER_ID_FLAG.length(), std::to_string(userId));
225 }
226 } // namespace DistributedFile
227 } // namespace Storage
228 } // namespace OHOS
229