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