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