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