1 /*
2  * Copyright (c) 2021-2024 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 "common_utils.h"
17 
18 #include <cstdint>
19 #include <cstdio>
20 #include <dirent.h>
21 #include <fcntl.h>
22 #include <sstream>
23 #include <string>
24 #include <sys/wait.h>
25 #include <sys/syscall.h>
26 #include <unistd.h>
27 #include <vector>
28 
29 #include "securec.h"
30 #include "time_util.h"
31 
32 using namespace std;
33 namespace OHOS {
34 namespace HiviewDFX {
35 namespace CommonUtils {
36 namespace {
GetProcessNameFromProcCmdline(int32_t pid)37 std::string GetProcessNameFromProcCmdline(int32_t pid)
38 {
39     std::string procCmdlinePath = "/proc/" + std::to_string(pid) + "/cmdline";
40     std::string procCmdlineContent = FileUtil::GetFirstLine(procCmdlinePath);
41     if (procCmdlineContent.empty()) {
42         return "";
43     }
44 
45     size_t procNameStartPos = 0;
46     size_t procNameEndPos = procCmdlineContent.size();
47     for (size_t i = 0; i < procCmdlineContent.size(); i++) {
48         if (procCmdlineContent[i] == '/') {
49             // for the format '/system/bin/hiview' of the cmdline file
50             procNameStartPos = i + 1; // 1 for next char
51         } else if (procCmdlineContent[i] == '\0') {
52             // for the format 'hiview \0 3 \0 hiview' of the cmdline file
53             procNameEndPos = i;
54             break;
55         }
56     }
57     return procCmdlineContent.substr(procNameStartPos, procNameEndPos - procNameStartPos);
58 }
59 
GetProcessNameFromProcStat(int32_t pid)60 std::string GetProcessNameFromProcStat(int32_t pid)
61 {
62     std::string procStatFilePath = "/proc/" + std::to_string(pid) + "/stat";
63     std::string procStatFileContent = FileUtil::GetFirstLine(procStatFilePath);
64     if (procStatFileContent.empty()) {
65         return "";
66     }
67     // for the format '40 (hiview) I ...'
68     auto procNameStartPos = procStatFileContent.find('(') + 1; // 1: for '(' next char
69     if (procNameStartPos == std::string::npos) {
70         return "";
71     }
72     auto procNameEndPos = procStatFileContent.find(')');
73     if (procNameEndPos == std::string::npos) {
74         return "";
75     }
76     if (procNameEndPos <= procNameStartPos) {
77         return "";
78     }
79     return procStatFileContent.substr(procNameStartPos, procNameEndPos - procNameStartPos);
80 }
81 }
82 
ExecCommand(const std::string & cmd,const std::vector<std::string> & args)83 int ExecCommand(const std::string &cmd, const std::vector<std::string> &args)
84 {
85     pid_t pid = fork();
86     if (pid < 0) {
87         return -1;
88     } else if (pid == 0) {
89         // Redirect the stdout to /dev/null
90         int fd = open("/dev/null", O_WRONLY);
91         // follow standard, although dup2 may handle the case of invalid oldfd
92         if (fd >= 0) {
93             dup2(fd, STDOUT_FILENO);
94             close(fd);
95         }
96 
97         std::vector<char *> argv;
98         argv.push_back(const_cast<char *>(cmd.c_str()));
99         for (const auto &arg : args) {
100             argv.push_back(const_cast<char *>(arg.c_str()));
101         }
102         argv.push_back(0);
103         execv(argv[0], &argv[0]);
104     }
105     constexpr uint64_t maxWaitingTime = 60; // 60 seconds
106     uint64_t remainedTime = maxWaitingTime * NS_PER_SECOND;
107     while (remainedTime > 0) {
108         uint64_t startTime = TimeUtil::GetNanoTime();
109         int status = 0;
110         waitpid(pid, &status, WNOHANG);
111         if (WIFEXITED(status)) {
112             return 0;
113         }
114         sleep(1);
115         uint64_t duration = TimeUtil::GetNanoTime() - startTime;
116         remainedTime = (remainedTime > duration) ? (remainedTime - duration) : 0;
117     }
118 
119     return -1;
120 }
121 
GetProcNameByPid(pid_t pid)122 std::string GetProcNameByPid(pid_t pid)
123 {
124     std::string result;
125     char buf[BUF_SIZE_256] = {0};
126     (void)snprintf_s(buf, BUF_SIZE_256, BUF_SIZE_256 - 1, "/proc/%d/comm", pid);
127     FileUtil::LoadStringFromFile(std::string(buf, strlen(buf)), result);
128     auto pos = result.find_last_not_of(" \n\r\t");
129     if (pos == std::string::npos) {
130         return result;
131     }
132     result.erase(pos + 1);
133     return result;
134 }
135 
GetProcFullNameByPid(pid_t pid)136 std::string GetProcFullNameByPid(pid_t pid)
137 {
138     std::string procName = GetProcessNameFromProcCmdline(pid);
139     if (procName.empty() && errno != ESRCH) { // ESRCH means 'no such process'
140         procName = GetProcessNameFromProcStat(pid);
141     }
142     return procName;
143 }
144 
GetPidByName(const std::string & processName)145 pid_t GetPidByName(const std::string& processName)
146 {
147     pid_t pid = -1;
148     std::string cmd = "pidof " + processName;
149 
150     FILE* fp = popen(cmd.c_str(), "r");
151     if (fp != nullptr) {
152         char buffer[BUF_SIZE_256] = {'\0'};
153         while (fgets(buffer, sizeof(buffer) - 1, fp) != nullptr) {}
154         std::istringstream istr(buffer);
155         istr >> pid;
156         pclose(fp);
157     }
158     return pid;
159 }
160 
IsTheProcessExist(pid_t pid)161 bool IsTheProcessExist(pid_t pid)
162 {
163     int ret = syscall(SYS_tgkill, pid, pid, 0);
164     if (ret != 0 && errno == ESRCH) {
165         return false;
166     }
167     return true;
168 }
169 
IsPidExist(pid_t pid)170 bool IsPidExist(pid_t pid)
171 {
172     std::string procDir = "/proc/" + std::to_string(pid);
173     return FileUtil::IsDirectory(procDir);
174 }
175 
IsSpecificCmdExist(const std::string & fullPath)176 bool IsSpecificCmdExist(const std::string& fullPath)
177 {
178     return access(fullPath.c_str(), X_OK) == 0;
179 }
180 
WriteCommandResultToFile(int fd,const std::string & cmd)181 bool WriteCommandResultToFile(int fd, const std::string& cmd)
182 {
183     if (cmd.empty()) {
184         return false;
185     }
186 
187     FILE* fp = popen(cmd.c_str(), "r");
188     if (fp != nullptr) {
189         char buffer[BUF_SIZE_256] = {0};
190         while (fgets(buffer, sizeof(buffer), fp) != nullptr) {
191             FileUtil::SaveStringToFd(fd, buffer);
192         }
193         pclose(fp);
194         return true;
195     }
196     return false;
197 }
198 
WriteCommandResultToFile(int fd,const std::string & cmd,const std::vector<std::string> & args)199 int WriteCommandResultToFile(int fd, const std::string &cmd, const std::vector<std::string> &args)
200 {
201     if (cmd.empty()) {
202         return -1;
203     }
204 
205     pid_t pid = fork();
206     if (pid < 0) {
207         return -1;
208     } else if (pid == 0) {
209         // follow standard, although dup2 may handle the case of invalid oldfd
210         if (fd >= 0) {
211             dup2(fd, STDOUT_FILENO);
212             dup2(fd, STDIN_FILENO);
213             dup2(fd, STDERR_FILENO);
214         }
215 
216         std::vector<char *> argv;
217         for (const auto &arg : args) {
218             argv.push_back(const_cast<char *>(arg.c_str()));
219         }
220         argv.push_back(0);
221         execv(cmd.c_str(), &argv[0]);
222     }
223 
224     constexpr uint64_t maxWaitingTime = 120; // 120 seconds
225     uint64_t endTime = TimeUtil::GetMilliseconds() + maxWaitingTime * TimeUtil::SEC_TO_MILLISEC;
226     while (endTime > TimeUtil::GetMilliseconds()) {
227         int status = 0;
228         pid_t p = waitpid(pid, &status, WNOHANG);
229         if (p < 0) {
230             return -1;
231         }
232 
233         if (p == pid) {
234             return WEXITSTATUS(status);
235         }
236     }
237 
238     return -1;
239 }
240 }
241 } // namespace HiviewDFX
242 } // namespace OHOS
243