1 /*
2  * Copyright (c) 2022 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 "kernel_interface.h"
17 
18 #include <climits>
19 #include <csignal>
20 #include <dirent.h>
21 #include <fstream>
22 #include <regex>
23 #include <securec.h>
24 #include <sstream>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 
28 #include "directory_ex.h"
29 #include "file_ex.h"
30 #include "memmgr_log.h"
31 
32 namespace OHOS {
33 namespace Memory {
34 namespace {
35 const std::string TAG = "KernelInterface";
36 }
37 
38 IMPLEMENT_SINGLE_INSTANCE(KernelInterface);
39 
40 const std::string KernelInterface::ROOT_PROC_PATH = "/proc";
41 const std::string KernelInterface::MEMCG_BASE_PATH = "/dev/memcg";
42 const std::string KernelInterface::FILE_MEMCG_PROCS = "cgroup.procs";
43 
44 #ifdef USE_HYPERHOLD_MEMORY
45 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/dev/memcg/memory.zswapd_pressure_show";
46 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "buffer_size";
47 #else
48 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_PATH = "/proc/meminfo";
49 const std::string KernelInterface::ZWAPD_PRESSURE_SHOW_BUFFER_SIZE = "MemAvailable";
50 #endif
51 const std::string KernelInterface::MEMINFO_PATH = "/proc/meminfo";
52 const std::string KernelInterface::FILE_PROC_STATUS = "status";
53 const std::string KernelInterface::TOTAL_MEMORY = "MemTotal";
54 
EchoToPath(const char * path,const char * content)55 bool KernelInterface::EchoToPath(const char* path, const char* content)
56 {
57     int fd = open(path, O_WRONLY);
58     if (fd == -1) {
59         HILOGE("echo %{public}s > %{public}s failed: file is not open", content, path);
60         return false;
61     }
62     if (write(fd, content, strlen(content)) < 0) {
63         HILOGE("echo %{public}s > %{public}s failed: write failed", content, path);
64         close(fd);
65         return false;
66     }
67     close(fd);
68     HILOGI("echo %{public}s > %{public}s", content, path);
69     return true;
70 }
71 
IsFileExists(const std::string & path)72 bool KernelInterface::IsFileExists(const std::string& path)
73 {
74     if (path.empty()) {
75         return false;
76     }
77     struct stat st = {};
78     if (stat(path.c_str(), &st) == 0 && S_ISREG(st.st_mode)) {
79         return true;
80     }
81     return false;
82 }
83 
CreateFile(const std::string & path,const mode_t & mode)84 bool KernelInterface::CreateFile(const std::string& path, const mode_t& mode)
85 {
86     if (path.empty()) {
87         return false;
88     }
89     if (IsFileExists(path)) {
90         return true;
91     }
92 
93     std::ofstream fout(path);
94     if (!fout.is_open()) {
95         return false;
96     }
97     fout.flush();
98     fout.close();
99     if (chmod(path.c_str(), mode) != 0) {
100         return false;
101     }
102 
103     return true;
104 }
105 
CreateFile(const std::string & path)106 bool KernelInterface::CreateFile(const std::string& path)
107 {
108     return CreateFile(path, FILE_MODE_644);
109 }
110 
RemoveFile(const std::string & path)111 bool KernelInterface::RemoveFile(const std::string& path)
112 {
113     return OHOS::RemoveFile(path);
114 }
115 
WriteToFile(const std::string & path,const std::string & content,bool truncated)116 bool KernelInterface::WriteToFile(const std::string& path, const std::string& content, bool truncated)
117 {
118     int fd;
119     char actualPath[PATH_MAX + 1] = {0};
120     char* ptrRet = NULL;
121 
122     if (strlen(path.c_str()) == 0 || strlen(path.c_str()) > PATH_MAX) {
123         HILOGE("file path is invalid");
124         return false;
125     }
126     ptrRet = realpath(path.c_str(), actualPath);
127     if (!ptrRet) {
128         HILOGE("file path cannot be canonicalized");
129         return false;
130     }
131     HILOGD("path:%{public}s, actualPath:%{public}s", path.c_str(), actualPath);
132     fd = open(actualPath, O_RDWR | (truncated ? O_TRUNC : O_APPEND));
133     if (fd == -1) {
134         HILOGE("echo %{public}s %{public}s %{public}s failed: file is not open",
135             content.c_str(), truncated ? ">" : ">>", path.c_str());
136         ptrRet = NULL;
137         return false;
138     }
139     if (write(fd, content.c_str(), strlen(content.c_str())) < 0) {
140         HILOGE("echo %{public}s %{public}s %{public}s failed: write failed",
141             content.c_str(), truncated ? ">" : ">>", path.c_str());
142         ptrRet = NULL;
143         close(fd);
144         return false;
145     }
146     ptrRet = NULL;
147     close(fd);
148     HILOGD("echo %{public}s %{public}s %{public}s", content.c_str(), truncated ? ">" : ">>", path.c_str());
149     return true;
150 }
151 
ReadFromFile(const std::string & path,std::string & content)152 bool KernelInterface::ReadFromFile(const std::string& path, std::string& content)
153 {
154     return OHOS::LoadStringFromFile(path, content);
155 }
156 
ReadLinesFromFile(const std::string & path,std::vector<std::string> & lines)157 bool KernelInterface::ReadLinesFromFile(const std::string& path, std::vector<std::string>& lines)
158 {
159     if (!IsFileExists(path)) {
160         HILOGE("no such file: %{public}s", path.c_str());
161         return false;
162     }
163     std::string line;
164     std::ifstream inf(path, std::ifstream::in);
165     if (!inf) {
166         HILOGE("ifstream(%{public}s) failed", path.c_str());
167         return false;
168     }
169     lines.clear();
170     while (!inf.eof()) {
171         getline(inf, line);
172         lines.push_back(line);
173     }
174     inf.close();
175     return true;
176 }
177 
IsDirExists(const std::string & path)178 bool KernelInterface::IsDirExists(const std::string& path)
179 {
180     if (path.empty()) {
181         return false;
182     }
183     struct stat st = {};
184     if (stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
185         return true;
186     }
187     return false;
188 }
189 
GetSystemCurTime()190 int64_t KernelInterface::GetSystemCurTime()
191 {
192     return std::chrono::duration_cast<std::chrono::milliseconds>
193         (std::chrono::system_clock::now().time_since_epoch()).count();
194 }
195 
GetSystemTimeMs()196 int64_t KernelInterface::GetSystemTimeMs()
197 {
198     return std::chrono::duration_cast<std::chrono::seconds>
199         (std::chrono::system_clock::now().time_since_epoch()).count();
200 }
201 
IsExists(const std::string & path)202 bool KernelInterface::IsExists(const std::string& path)
203 {
204     return OHOS::FileExists(path);
205 }
206 
IsEmptyDir(const std::string & path)207 bool KernelInterface::IsEmptyDir(const std::string& path)
208 {
209     return OHOS::IsEmptyFolder(path);
210 }
211 
CreateDir(const std::string & path)212 bool KernelInterface::CreateDir(const std::string& path)
213 {
214     return OHOS::ForceCreateDirectory(path); // default mode 755
215 }
216 
RemoveDirRecursively(const std::string & path)217 bool KernelInterface::RemoveDirRecursively(const std::string& path)
218 {
219     return OHOS::ForceRemoveDirectory(path) || (remove(path.c_str()) == 0);
220 }
221 
RmDelimiter(const std::string & path)222 std::string KernelInterface::RmDelimiter(const std::string& path)
223 {
224     if (path.empty()) {
225         return path;
226     }
227     return OHOS::ExcludeTrailingPathDelimiter(path);
228 }
229 
AddDelimiter(const std::string & path)230 std::string KernelInterface::AddDelimiter(const std::string& path)
231 {
232     if (path.empty()) {
233         return path;
234     }
235     return OHOS::IncludeTrailingPathDelimiter(path);
236 }
237 
JoinPath(const std::string & prefixPath,const std::string & subPath)238 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& subPath)
239 {
240     return AddDelimiter(prefixPath) + subPath;
241 }
242 
JoinPath(const std::string & prefixPath,const std::string & midPath,const std::string & subPath)243 std::string KernelInterface::JoinPath(const std::string& prefixPath, const std::string& midPath,
244                                       const std::string& subPath)
245 {
246     return JoinPath(JoinPath(prefixPath, midPath), subPath);
247 }
248 
GetPidProcInfo(struct ProcInfo & procInfo)249 bool KernelInterface::GetPidProcInfo(struct ProcInfo &procInfo)
250 {
251     HILOGD("called!");
252 
253     std::string statPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/stat");
254 
255     // format like:
256     // 1 (init) S 0 0 0 0 -1 4210944 1 ...
257     std::string stat;
258     std::string statm;
259     std::string statPid;
260     std::string vss;
261     std::string rss;
262     if (!ReadFromFile(statPath, stat)) {
263         HILOGD("stat file error!");
264         return false;
265     }
266     std::istringstream isStat(stat);
267     isStat >> statPid >> procInfo.name >> procInfo.status;
268 
269     if (statPid != std::to_string(procInfo.pid)) {
270         HILOGD("pid error!");
271         return false;
272     }
273 
274     std::string statmPath = JoinPath("/proc/", std::to_string(procInfo.pid), "/statm");
275     // format like:
276     // 640 472 369 38 0 115 0
277     if (!ReadFromFile(statmPath, statm)) {
278         HILOGD("statm file error!");
279         return false;
280     }
281     std::istringstream isStatm(statm);
282     isStatm >> vss >> rss; // pages
283     int rssValue = 0;
284     try {
285         rssValue = std::stoi(rss);
286     } catch (...) {
287         HILOGE("stoi(%{public}s) failed!", rss.c_str());
288         return false;
289     }
290 
291     if (rssValue < 0 || rssValue > INT_MAX / PAGE_TO_KB) {
292         HILOGE("rssValue=%{public}d, rss is less than 0 or overflow!", rssValue);
293         return false;
294     }
295     procInfo.size = rssValue * PAGE_TO_KB;
296     HILOGI("GetProcInfo success: name is %{public}s, status is %{public}s, size = %{public}d KB",
297            procInfo.name.c_str(), procInfo.status.c_str(), procInfo.size);
298     return true;
299 }
300 
GetProcNameByPid(int pid,std::string & name)301 bool KernelInterface::GetProcNameByPid(int pid, std::string &name)
302 {
303     std::string statusPath = JoinPath("/proc/", std::to_string(pid), "/status");
304     std::string statusContent;
305     std::string nameTag;
306     if (!ReadFromFile(statusPath, statusContent)) {
307         HILOGE("status file [%{public}s] error!", statusPath.c_str());
308         return false;
309     }
310     std::istringstream statusStream(statusContent);
311     statusStream >> nameTag >> name;
312     return true;
313 }
314 
ReadZswapdPressureShow(std::map<std::string,std::string> & result)315 void KernelInterface::ReadZswapdPressureShow(std::map<std::string, std::string>& result)
316 {
317     std::string contentStr;
318     if (!ReadFromFile(ZWAPD_PRESSURE_SHOW_PATH, contentStr)) {
319         HILOGE("read %{public}s faild, content=[%{public}s]", ZWAPD_PRESSURE_SHOW_PATH.c_str(), contentStr.c_str());
320         return;
321     }
322     char *contentPtr = new char[contentStr.size() + 1];
323     if (contentPtr == nullptr) {
324         HILOGE("alloc buffer fail");
325         return;
326     }
327     if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
328         HILOGE("copy fail");
329         delete [] contentPtr;
330         return;
331     }
332     char *restPtr;
333     char *line = strtok_r(contentPtr, "\n", &restPtr);
334     do {
335         for (size_t i = 0; i < strlen(line); i++) {
336             if (line[i] == ':') {
337                 line[i] = ' ';
338             }
339         }
340         std::string lineStr(line);
341         std::istringstream is(lineStr);
342         std::string name;
343         std::string value;
344         is >> name >> value;
345         result.insert(std::make_pair(name, value));
346 
347         line = strtok_r(NULL, "\n", &restPtr);
348     } while (line);
349     if (restPtr) {
350         delete [] restPtr;
351     }
352     if (contentPtr) {
353         delete [] contentPtr;
354     }
355     return;
356 }
357 
GetCurrentBuffer()358 int KernelInterface::GetCurrentBuffer()
359 {
360     std::map<std::string, std::string> result;
361     ReadZswapdPressureShow(result);
362     auto value = result.find(ZWAPD_PRESSURE_SHOW_BUFFER_SIZE);
363     if (value != result.end()) {
364 #ifdef USE_HYPERHOLD_MEMORY
365         HILOGD("buffer_size=%{public}s MB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
366         return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str()) * KB_PER_MB;
367 #else
368         HILOGD("buffer_size=%{public}s KB", result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
369         return atoi(result[ZWAPD_PRESSURE_SHOW_BUFFER_SIZE].c_str());
370 #endif
371     }
372     return MAX_BUFFER_KB;
373 }
374 
KillOneProcessByPid(int pid)375 int KernelInterface::KillOneProcessByPid(int pid)
376 {
377     HILOGD("called! pid=%{public}d", pid);
378     struct ProcInfo procInfo;
379     int freedBuffer = 0;
380     procInfo.pid = pid;
381 
382     if (!GetPidProcInfo(procInfo)) {
383         HILOGE("GetPidProcInfo fail !!!");
384         goto out;
385     }
386 
387     if (procInfo.status == "D") {
388         HILOGE("Task %{public}s is at D status!", procInfo.name.c_str());
389         goto out;
390     }
391 
392     if (kill(pid, SIGKILL)) {
393         HILOGE("Kill %{public}s errno=%{public}d!", procInfo.name.c_str(), errno);
394     }
395     HILOGE("%{public}s has been Killed ! (pid=%{public}d, freedSize=%{public}d KB)",
396         procInfo.name.c_str(), procInfo.pid, procInfo.size);
397 
398     freedBuffer = procInfo.size;
399 out:
400     return freedBuffer;
401 }
402 
GetAllProcPids(std::vector<unsigned int> & pids)403 bool KernelInterface::GetAllProcPids(std::vector<unsigned int> &pids)
404 {
405     pids.clear();
406     DIR *dir = opendir(ROOT_PROC_PATH.c_str());
407     if (dir == nullptr) {
408         HILOGE("dir %{public}s is not exist", ROOT_PROC_PATH.c_str());
409         return false;
410     }
411     struct dirent *ptr = nullptr;
412     while ((ptr = readdir(dir)) != nullptr) {
413         if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
414             // current dir OR parent dir
415             continue;
416         } else if (ptr->d_type == DT_DIR) {
417             int pid = atoi(ptr->d_name);
418             if (pid > 0) {
419                 pids.push_back((unsigned int)pid);
420             }
421         }
422     }
423     if (dir) {
424         closedir(dir);
425     }
426     HILOGD("there are %{public}zu pids under %{public}s", pids.size(), ROOT_PROC_PATH.c_str());
427     return true;
428 }
429 
GetUidByPid(unsigned int pid,unsigned int & uid)430 bool KernelInterface::GetUidByPid(unsigned int pid, unsigned int& uid)
431 {
432     std::string path = JoinPath(ROOT_PROC_PATH, std::to_string(pid), "status");
433     std::string content;
434     if (!ReadFromFile(path, content)) {
435         HILOGE("read file failed. %{public}s", path.c_str());
436         return false;
437     }
438     content = std::regex_replace(content, std::regex("\n+"), " "); // replace \n with space
439     std::regex re(".*Uid:[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+)[[:s:]]*([[:d:]]+).*");
440     std::smatch res;
441     if (!std::regex_match(content, res, re)) {
442         HILOGD("re not match. %{public}s", content.c_str());
443         return false;
444     }
445     try {
446         uid = (unsigned int)std::stoi(res.str(1)); // 1: Uid index
447     } catch (...) {
448         HILOGE("stoi(%{public}s) failed", res.str(1).c_str());
449         return false;
450     }
451     return true;
452 }
453 
DeleteCharArrayIfNotNull(char * charArray)454 void DeleteCharArrayIfNotNull(char * charArray)
455 {
456     if (charArray) {
457         delete [] charArray;
458         charArray = nullptr;
459     }
460 }
461 
ReadSwapOutKBSinceKernelBoot(const std::string & path,const std::string & tagStr,unsigned long long & ret)462 bool KernelInterface::ReadSwapOutKBSinceKernelBoot(const std::string &path, const std::string &tagStr,
463     unsigned long long &ret)
464 {
465     std::string contentStr;
466     if (!ReadFromFile(path, contentStr)) {
467         return false;
468     }
469     char *contentPtr = new char[contentStr.size() + 1];
470     if (contentPtr == nullptr) {
471         HILOGE("alloc buffer fail");
472         return false;
473     }
474     if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
475         HILOGE("copy fail");
476         DeleteCharArrayIfNotNull(contentPtr);
477         return false;
478     }
479     bool success = false;
480     char *restPtr;
481     char *line = strtok_r(contentPtr, "\n", &restPtr);
482     do {
483         std::string lineStr(line);
484 
485         size_t i = 0;
486         for (; i < strlen(line); i++) {
487             if (line[i] == ':') {
488                 break;
489             }
490         }
491         if (i >= strlen(line) - 2) { // 2: no : in the line or : is at end of line
492             line = strtok_r(NULL, "\n", &restPtr);
493             continue;
494         }
495         std::string tag = lineStr.substr(0, i);
496         if (tag == tagStr) {
497             std::string value = lineStr.substr(i + 1);
498             std::istringstream iss(value);
499             std::string sizeStr;
500             std::string unitStr;
501             iss >> sizeStr >> unitStr;
502             try {
503                 ret = std::strtoull(sizeStr.c_str(), nullptr, 10); // 10:Decimal
504                 success = true;
505             } catch (...) {
506                 HILOGE("parse [%{public}s] to unsigned long long error!", sizeStr.c_str());
507             }
508             break;
509         }
510 
511         line = strtok_r(NULL, "\n", &restPtr);
512     } while (line);
513     DeleteCharArrayIfNotNull(contentPtr);
514     return success;
515 }
516 
ParseMeminfo(const std::string & contentStr,const std::string & itemName)517 int KernelInterface::ParseMeminfo(const std::string &contentStr, const std::string &itemName)
518 {
519     char *contentPtr = new (std::nothrow) char[contentStr.size() + 1];
520     if (contentPtr == nullptr) {
521         HILOGE("alloc buffer fail");
522         return -1;
523     }
524     if (strcpy_s(contentPtr, contentStr.size() + 1, contentStr.c_str()) != EOK) {
525         HILOGE("copy fail");
526         delete [] contentPtr;
527         return -1;
528     }
529     char *restPtr = nullptr;
530     char *line = strtok_r(contentPtr, "\n", &restPtr);
531     std::string name;
532     std::string value;
533     bool findTotalMem = false;
534     do {
535         for (size_t i = 0; i < strlen(line); i++) {
536             if (line[i] == ':') {
537                 line[i] = ' ';
538             }
539         }
540         std::string lineStr(line);
541         std::istringstream is(lineStr);
542 
543         is >> name >> value;
544         if (name == itemName) {
545             findTotalMem = true;
546             break;
547         }
548         line = strtok_r(NULL, "\n", &restPtr);
549     } while (line);
550     if (contentPtr) {
551         delete [] contentPtr;
552     }
553 
554     if (findTotalMem == false) {
555         return -1;
556     }
557     std::string valueTemp = "";
558     for (auto c : value) {
559         if (c >= '0' && c <= '9') {
560             valueTemp = valueTemp + c;
561         }
562     }
563     return atoi(valueTemp.c_str());
564 }
565 
GetTotalBuffer()566 int KernelInterface::GetTotalBuffer()
567 {
568     if (totalBuffer_ >= 0) {
569         return totalBuffer_;
570     }
571 
572     std::string contentStr;
573     if (!ReadFromFile(MEMINFO_PATH, contentStr)) {
574         HILOGE("read %{public}s faild, content=[%{public}s]", MEMINFO_PATH.c_str(), contentStr.c_str());
575         return -1;
576     }
577     totalBuffer_ = ParseMeminfo(contentStr, TOTAL_MEMORY);
578     return totalBuffer_;
579 }
580 
GetMemcgPids(const std::string & memcgPath,std::vector<int> & memcgPids)581 bool KernelInterface::GetMemcgPids(const std::string &memcgPath, std::vector<int> &memcgPids)
582 {
583     std::string path = JoinPath(memcgPath, FILE_MEMCG_PROCS);
584     std::vector<std::string> strLines;
585     if (!ReadLinesFromFile(path, strLines)) {
586         HILOGE("read file and split to lines failed : %{public}s", path.c_str());
587         return false;
588     }
589 
590     memcgPids.clear();
591     int pid;
592     for (auto &it : strLines) {
593         try {
594             pid = stoi(it);
595         } catch (...) {
596             continue;
597         }
598         memcgPids.emplace_back(pid);
599     }
600     HILOGD("there are %{public}zu pids in %{public}s", memcgPids.size(), path.c_str());
601     return true;
602 }
603 
GetAllUserIds(std::vector<int> & userIds)604 bool KernelInterface::GetAllUserIds(std::vector<int> &userIds)
605 {
606     userIds.clear();
607     DIR *dir = opendir(MEMCG_BASE_PATH.c_str());
608     if (dir == nullptr) {
609         HILOGE("dir %{public}s is not exist", MEMCG_BASE_PATH.c_str());
610         return false;
611     }
612     struct dirent *ptr = nullptr;
613     while ((ptr = readdir(dir)) != nullptr) {
614         if ((strcmp(ptr->d_name, ".") == 0) || (strcmp(ptr->d_name, "..") == 0)) {
615             // current dir OR parent dir
616             continue;
617         } else if (ptr->d_type == DT_DIR) {
618             int userId = atoi(ptr->d_name);
619             if (userId > 0) {
620                 userIds.push_back(userId);
621             }
622         }
623     }
624     if (dir) {
625         closedir(dir);
626     }
627     HILOGD("there are %{public}zu userIds under %{public}s", userIds.size(), MEMCG_BASE_PATH.c_str());
628     return true;
629 }
630 
SplitOneLineByDelim(const std::string & input,const char delimiter,std::vector<std::string> & res)631 void KernelInterface::SplitOneLineByDelim(const std::string &input, const char delimiter,
632     std::vector<std::string> &res)
633 {
634     std::stringstream ss(input);
635     std::string temp;
636     while (getline(ss, temp, delimiter)) {
637         if (!temp.empty()) {
638             res.emplace_back(temp);
639         }
640     }
641 }
642 
SplitOneLineByBlank(const std::string & input,std::vector<std::string> & res)643 void KernelInterface::SplitOneLineByBlank(const std::string &input, std::vector<std::string> &res)
644 {
645     std::stringstream ss(input);
646     std::string temp;
647     while (ss >> temp) {
648         if (!temp.empty()) {
649             res.emplace_back(temp);
650         }
651     }
652 }
653 } // namespace Memory
654 } // namespace OHOS
655