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