• 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 } // 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