1 /*
2 * Copyright (c) 2021 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 #include "process_utils.h"
16
17 #include <fcntl.h>
18 #include <poll.h>
19 #include <sstream>
20 #include <sys/types.h>
21 #include <sys/wait.h>
22 #include <unistd.h>
23 #include <unordered_map>
24
25 #include "file_utils.h"
26 #include "logging.h"
27 #include "string_utils.h"
28
29 namespace {
30 constexpr int RD = 0;
31 constexpr int WR = 1;
32 } // namespace
33
34 struct PipedSigHandler {
PipedSigHandlerPipedSigHandler35 explicit PipedSigHandler(int sig) : sig_(sig)
36 {
37 Init();
38 }
39
~PipedSigHandlerPipedSigHandler40 ~PipedSigHandler()
41 {
42 Finalize();
43 }
44
GetNotifyFdPipedSigHandler45 int GetNotifyFd() const
46 {
47 return pipe_[RD];
48 }
49
50 private:
InitPipedSigHandler51 int Init()
52 {
53 handler_ = signal(sig_, &PipedSigHandler::SigHandler);
54 HILOG_INFO(LOG_CORE, "set signal handler for sig %d done!", sig_);
55
56 CHECK_TRUE(pipe(pipe_) != -1, -1, "create pipe failed, %d", errno);
57 CHECK_TRUE(fcntl(pipe_[RD], F_SETFL, O_NONBLOCK) != -1, -1, "set non block to pipe[WR] failed!");
58 return 0;
59 }
60
FinalizePipedSigHandler61 int Finalize()
62 {
63 CHECK_TRUE(close(pipe_[RD]) != -1, -1, "close pipe_[RD] failed, %d", errno);
64 CHECK_TRUE(close(pipe_[WR]) != -1, -1, "close pipe_[WR] failed, %d", errno);
65 signal(sig_, nullptr);
66 HILOG_INFO(LOG_CORE, "restore signal handler for sig %d done!", sig_);
67 return 0;
68 }
69
SigHandlerPipedSigHandler70 static void SigHandler(int sig)
71 {
72 write(pipe_[WR], &sig, sizeof(sig));
73 }
74
75 private:
76 int sig_ = 0;
77 static int pipe_[2];
78 sighandler_t handler_ = nullptr;
79 };
80
81 int PipedSigHandler::pipe_[2] = {-1, -1};
82
83 struct Poller {
84 using EventCallback = std::function<void(void)>;
85
AddFdPoller86 void AddFd(int fd, short events, const EventCallback& onEvent)
87 {
88 struct pollfd pfd = {0};
89 pfd.fd = fd;
90 pfd.events = events;
91 pollSet_.push_back(pfd);
92 callbacks_[fd] = onEvent;
93 HILOG_INFO(LOG_CORE, "Add fd %d to poll set done!", fd);
94 }
95
PollEventsPoller96 int PollEvents(int timeout)
97 {
98 int nready = poll(pollSet_.data(), pollSet_.size(), timeout);
99 CHECK_TRUE(nready >= 0, -1, "poll failed, %d", errno);
100 if (nready == 0) {
101 HILOG_INFO(LOG_CORE, "poll %dms timeout!", timeout);
102 return 0;
103 }
104 return nready;
105 }
106
DispatchEventsPoller107 void DispatchEvents(int nready) const
108 {
109 for (int i = 0; i < nready; i++) {
110 int fd = pollSet_[i].fd;
111 if (pollSet_[i].revents & pollSet_[i].events) { // match interests events
112 auto it = callbacks_.find(fd);
113 if (it != callbacks_.end()) {
114 it->second();
115 }
116 }
117 }
118 }
119
120 private:
121 std::vector<struct pollfd> pollSet_;
122 std::unordered_map<int, EventCallback> callbacks_;
123 };
124
ExecuteProcess(const std::string & bin,const std::vector<std::string> & args,int pipeFd,bool out2pipe,bool err2pipe)125 static bool ExecuteProcess(const std::string& bin,
126 const std::vector<std::string>& args,
127 int pipeFd,
128 bool out2pipe,
129 bool err2pipe)
130 {
131 // redirect /dev/null to stdin
132 int nullFd = open("/dev/null", O_RDONLY);
133 int inFd = nullFd;
134 int outFd = nullFd;
135 int errFd = nullFd;
136 CHECK_TRUE(inFd >= 0, false, "open /dev/null failed, %d", errno);
137 CHECK_TRUE(dup2(inFd, STDIN_FILENO) != -1, false, "dup nullFD to stdin failed, %d", errno);
138
139 // redirect outFd to stdout
140 if (out2pipe) {
141 outFd = pipeFd;
142 }
143 CHECK_TRUE(dup2(outFd, STDOUT_FILENO) != -1, false, "dup fd %d to stdout failed, %d", outFd, errno);
144
145 // redirect errFd to stderr
146 if (err2pipe) {
147 errFd = pipeFd;
148 }
149 CHECK_TRUE(dup2(errFd, STDERR_FILENO) != -1, false, "dup fd %d to stderr failed, %d", errFd, errno);
150
151 CHECK_TRUE(close(nullFd) != -1, false, "close nullFd failed, %d", errno);
152 CHECK_TRUE(close(pipeFd) != -1, false, "close pipeFd failed, %d", errno);
153
154 std::vector<char*> argv;
155 for (size_t i = 0; i < args.size(); i++) {
156 argv.push_back(const_cast<char*>(args[i].c_str()));
157 }
158 argv.push_back(nullptr); // last item in argv must be NULL
159
160 int retval = execv(bin.c_str(), argv.data());
161 std::string cmdline = StringUtils::Join(args, " ");
162 CHECK_TRUE(retval != -1, false, "execv %s failed, %d!", cmdline.c_str(), errno);
163 _exit(EXIT_FAILURE);
164 return true;
165 }
166
ReceiveOutputAndSigchld(int pipeFd,const PipedSigHandler & handler)167 static std::string ReceiveOutputAndSigchld(int pipeFd, const PipedSigHandler& handler)
168 {
169 std::string output;
170 Poller poller = {};
171 poller.AddFd(pipeFd, POLLIN, [&]() {
172 std::string out = FileUtils::ReadFile(pipeFd);
173 output += out;
174 });
175 volatile bool childExit = false;
176 poller.AddFd(handler.GetNotifyFd(), POLLIN, [&]() {
177 childExit = true;
178 int sig = 0;
179 read(handler.GetNotifyFd(), &sig, sizeof(sig));
180 HILOG_INFO(LOG_CORE, "sig %d received!", sig);
181 });
182
183 while (!childExit) {
184 int timeout = 1000;
185 int events = poller.PollEvents(timeout);
186 if (events < 0) {
187 HILOG_INFO(LOG_CORE, "poll failed!");
188 break;
189 }
190 poller.DispatchEvents(events);
191 }
192 return output;
193 }
194
GetProcessExitCode(int pid)195 static int GetProcessExitCode(int pid)
196 {
197 // get child exit code
198 int wstatus = 0;
199 pid_t w = waitpid(pid, &wstatus, 0);
200 CHECK_TRUE(w > 0, -1, "waitpid failed, %d!", errno);
201
202 // determine child exit status
203 int retval = 0;
204 if (WIFEXITED(wstatus)) {
205 retval = WEXITSTATUS(wstatus);
206 HILOG_INFO(LOG_CORE, "process %d exited with status %d", pid, retval);
207 } else if (WIFSIGNALED(wstatus)) {
208 retval = -1;
209 HILOG_INFO(LOG_CORE, "process %d killed by signal %d\n", pid, WTERMSIG(wstatus));
210 } else {
211 retval = -1;
212 HILOG_WARN(LOG_CORE, "process %d exited with unknow status!", pid);
213 }
214 return retval;
215 }
216
Execute(const ExecuteArgs & args,std::string & output)217 int ProcessUtils::Execute(const ExecuteArgs& args, std::string& output)
218 {
219 CHECK_TRUE(args.bin_.size() > 0, -1, "bin_ empty");
220 CHECK_TRUE(args.argv_.size() > 0, -1, "args_ empty");
221
222 int pipeFds[2] = {-1, -1};
223 CHECK_TRUE(pipe(pipeFds) != -1, -1, "create pipe failed, %d", errno);
224 CHECK_TRUE(fcntl(pipeFds[RD], F_SETFL, O_NONBLOCK) != -1, -1, "set non block to pipe[WR] failed!");
225
226 std::string cmdline = StringUtils::Join(args.argv_, " ");
227 HILOG_INFO(LOG_CORE, "ExecuteCommand(%s): prepare ...", cmdline.c_str());
228
229 PipedSigHandler sigChldHandler(SIGCHLD);
230 pid_t pid = fork();
231 CHECK_TRUE(pid >= 0, -1, "fork failed!");
232 if (pid == 0) {
233 // child process
234 CHECK_TRUE(close(pipeFds[RD]) != -1, -1, "close pipeFds[RD] failed, %d", errno);
235 ExecuteProcess(args.bin_, args.argv_, pipeFds[WR], args.out2pipe_, args.err2pipe_);
236 }
237
238 // parent process only read data from pipe, so close write end as soon as possible
239 CHECK_TRUE(close(pipeFds[WR]) != -1, -1, "close pipeFds[WR] failed, %d", errno);
240
241 output = ReceiveOutputAndSigchld(pipeFds[RD], sigChldHandler);
242 auto lines = StringUtils::Split(output, "\n");
243 HILOG_INFO(LOG_CORE, "ExecuteCommand(%s): output %zuB, %zuLn", cmdline.c_str(), output.size(), lines.size());
244
245 int retval = GetProcessExitCode(pid);
246
247 // close pipe fds
248 CHECK_TRUE(close(pipeFds[RD]) != -1, -1, "close pipe[RD] failed, %d", errno);
249 if (retval != 0 && cmdline != "hitrace -l") {
250 HILOG_ERROR(LOG_CORE, "ExecuteCommand(%s): exit with %d, hitrace output is %s", cmdline.c_str(),
251 retval, output.c_str());
252 } else {
253 HILOG_INFO(LOG_CORE, "ExecuteCommand(%s): exit with %d", cmdline.c_str(), retval);
254 }
255 return retval;
256 }
257