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