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 "tracked_command.h"
16
17 #include <cassert>
18 #include <cerrno>
19 #include <csignal>
20
21 #include <fcntl.h>
22 #include <sys/prctl.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25
26 #include "debug_logger.h"
27 #include "utilities.h"
28
29 namespace OHOS {
30 namespace Developtools {
31 namespace HiPerf {
CreateInstance(const std::vector<std::string> & args)32 std::unique_ptr<TrackedCommand> TrackedCommand::CreateInstance(const std::vector<std::string> &args)
33 {
34 std::unique_ptr<TrackedCommand> command {new (std::nothrow) TrackedCommand(args)};
35 if (!command) {
36 return nullptr;
37 }
38 if (!command->CreateChildProcess()) {
39 return nullptr;
40 }
41 return command;
42 }
43
TrackedCommand(const std::vector<std::string> & args)44 TrackedCommand::TrackedCommand(const std::vector<std::string> &args) : command_ {args}
45 {
46 // check sa_hanlder of SIGCHLD, set it to SIG_DFL if otherwise
47 struct sigaction oldAct;
48 if (memset_s(&oldAct, sizeof(oldAct), 0, sizeof(oldAct)) != EOK) {
49 HLOGE("memset_s() failed in TrackedCommand::TrackedCommand()");
50 return;
51 }
52 if (sigaction(SIGCHLD, nullptr, &oldAct) == -1) {
53 HLOGW("sigaction(SIGCHLD, nullptr, &oldAct) failed");
54 } else {
55 if (oldAct.sa_handler != SIG_DFL) {
56 struct sigaction newAct;
57 if (memset_s(&newAct, sizeof(newAct), 0, sizeof(newAct)) != EOK) {
58 HLOGE("memset_s() failed in TrackedCommand::TrackedCommand()");
59 return;
60 }
61 newAct.sa_handler = SIG_DFL;
62 if (sigaction(SIGCHLD, &newAct, &oldAct) == -1) {
63 HLOGW("sigaction(SIGCHLD, &newAct, &oldAct) failed");
64 }
65 }
66 }
67 }
68
~TrackedCommand()69 TrackedCommand::~TrackedCommand()
70 {
71 MakeInvalid();
72 }
73
Stop()74 void TrackedCommand::Stop()
75 {
76 MakeInvalid();
77 }
78
InitSignalPipes(int & startFd,int & ackFd)79 bool TrackedCommand::InitSignalPipes(int &startFd, int &ackFd)
80 {
81 int startSignalPipe[2] {-1, -1};
82 if (pipe2(startSignalPipe, O_CLOEXEC) != 0) {
83 HLOGE("pipe2() failed in TrackedCommand::InitSignalPipes()");
84 return false;
85 }
86 startFd = startSignalPipe[0];
87 startFd_ = startSignalPipe[1];
88
89 int ackSignalPipe[2] {-1, -1};
90 if (pipe2(ackSignalPipe, O_CLOEXEC) != 0) {
91 HLOGE("pipe2() failed in TrackedCommand::InitSignalPipes()");
92 close(startFd);
93 close(startFd_);
94 startFd = -1;
95 startFd_ = -1;
96 return false;
97 }
98 ackFd = ackSignalPipe[1];
99 ackFd_ = ackSignalPipe[0];
100 return true;
101 }
102
CreateChildProcess()103 bool TrackedCommand::CreateChildProcess()
104 {
105 int startFd {-1};
106 int ackFd {-1};
107 if (!InitSignalPipes(startFd, ackFd)) {
108 return false;
109 }
110 pid_t pid = fork();
111 if (pid == -1) {
112 HLOGE("fork() failed in TrackedCommand::CreateChildProcess()");
113 MakeInvalid();
114 return false;
115 } else if (pid == 0) {
116 close(startFd_);
117 close(ackFd_);
118 ExecuteCommand(startFd, ackFd);
119 _exit(0);
120 } else {
121 close(startFd);
122 close(ackFd);
123 childPid_ = pid;
124 state_ = State::COMMAND_WAITING;
125 return true;
126 }
127 }
128
StartCommand()129 bool TrackedCommand::StartCommand()
130 {
131 // send start signal to start execution of command
132 ssize_t nbyte {0};
133 char startSignal {1};
134 while (true) {
135 nbyte = write(startFd_, &startSignal, 1);
136 if (nbyte == -1) {
137 continue;
138 }
139 break;
140 }
141 HLOG_ASSERT(nbyte == 1);
142 // check execution state of command
143 // read acknowledgement signal
144 char ackSignal {0};
145 while (true) {
146 nbyte = read(ackFd_, &ackSignal, 1);
147 if (nbyte == -1 and (errno == EINTR or errno == EIO)) {
148 continue;
149 }
150 HLOGE("*** nbyte: %zd, ackSignal: %d ***\n", nbyte, ackSignal);
151 break;
152 }
153 if (nbyte == 0) {
154 state_ = State::COMMAND_STARTED;
155 return true;
156 }
157 HLOG_ASSERT(nbyte == 1);
158 state_ = State::COMMAND_FAILURE;
159 return false;
160 }
161
ExecuteCommand(const int & startFd,const int & ackFd)162 void TrackedCommand::ExecuteCommand(const int &startFd, const int &ackFd)
163 {
164 HLOG_ASSERT(startFd != -1);
165 HLOG_ASSERT(ackFd != -1);
166 prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
167 // waiting start signal
168 char startSignal {0};
169 ssize_t nbyte {0};
170 while (true) {
171 nbyte = read(startFd, &startSignal, 1);
172 if (nbyte == -1) {
173 continue;
174 }
175 break;
176 }
177 HLOG_ASSERT(nbyte == 1);
178 // execute command
179 char *argv[command_.size() + 1];
180 for (size_t index = 0; index < command_.size(); ++index) {
181 argv[index] = const_cast<char *>(command_[index].c_str());
182 }
183 argv[command_.size()] = nullptr;
184 // On sucees, startFd and ackFd will be closed hence parent process reads EPIPE;
185 if (IsPath(argv[0])) {
186 execv(argv[0], argv);
187 } else {
188 execvp(argv[0], argv);
189 }
190 // execv() or execvp() failed, send failure signal
191 char ackSignal {1};
192 while (true) {
193 nbyte = write(ackFd, &ackSignal, 1);
194 if (nbyte == -1) {
195 continue;
196 }
197 break;
198 }
199 HLOG_ASSERT(nbyte == 1);
200 HLOGE("child process failed to execute command");
201 }
202
WaitCommand(int & wstatus)203 bool TrackedCommand::WaitCommand(int &wstatus)
204 {
205 if (childPid_ != -1) {
206 HLOG_ASSERT(state_ != State::COMMAND_STOPPED);
207 pid_t pid = waitpid(childPid_, &wstatus, WNOHANG);
208 if (pid == childPid_) {
209 childPid_ = -1;
210 state_ = State::COMMAND_STOPPED;
211 return true;
212 } else {
213 return false;
214 }
215 }
216 return true;
217 }
218
MakeInvalid()219 void TrackedCommand::MakeInvalid()
220 {
221 if (childPid_ != -1) {
222 HLOG_ASSERT(state_ != State::COMMAND_STOPPED);
223 int wstatus;
224 pid_t pid = waitpid(childPid_, &wstatus, WNOHANG);
225 if (pid != childPid_) {
226 kill(childPid_, SIGKILL);
227 }
228 childPid_ = -1;
229 state_ = State::COMMAND_STOPPED;
230 }
231 if (startFd_ != -1) {
232 close(startFd_);
233 startFd_ = -1;
234 }
235 if (ackFd_ != -1) {
236 close(ackFd_);
237 ackFd_ = -1;
238 }
239 }
240 } // namespace HiPerf
241 } // namespace Developtools
242 } // namespace OHOS