• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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