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