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