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