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 23namespace OHOS { 24namespace Developtools { 25namespace HiPerf { 26std::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 38TrackedCommand::TrackedCommand(const std::vector<std::string> &args) : command_ {args} {} 39 40TrackedCommand::~TrackedCommand() 41{ 42 MakeInvalid(); 43} 44 45void TrackedCommand::Stop() 46{ 47 MakeInvalid(); 48} 49 50bool 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 74bool 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 100bool 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 133void 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 174bool 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 190void 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