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