1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include "workload.h" 18 19#include <errno.h> 20#include <fcntl.h> 21#include <sys/wait.h> 22#include <unistd.h> 23 24#include <android-base/logging.h> 25 26std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) { 27 std::unique_ptr<Workload> workload(new Workload(args)); 28 if (workload != nullptr && workload->CreateNewProcess()) { 29 return workload; 30 } 31 return nullptr; 32} 33 34Workload::~Workload() { 35 if (work_pid_ != -1 && work_state_ != NotYetCreateNewProcess) { 36 if (!Workload::WaitChildProcess(false)) { 37 kill(work_pid_, SIGKILL); 38 Workload::WaitChildProcess(true); 39 } 40 } 41 if (start_signal_fd_ != -1) { 42 close(start_signal_fd_); 43 } 44 if (exec_child_fd_ != -1) { 45 close(exec_child_fd_); 46 } 47} 48 49static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd); 50 51bool Workload::CreateNewProcess() { 52 CHECK_EQ(work_state_, NotYetCreateNewProcess); 53 54 int start_signal_pipe[2]; 55 if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) { 56 PLOG(ERROR) << "pipe2() failed"; 57 return false; 58 } 59 60 int exec_child_pipe[2]; 61 if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) { 62 PLOG(ERROR) << "pipe2() failed"; 63 close(start_signal_pipe[0]); 64 close(start_signal_pipe[1]); 65 return false; 66 } 67 68 pid_t pid = fork(); 69 if (pid == -1) { 70 PLOG(ERROR) << "fork() failed"; 71 close(start_signal_pipe[0]); 72 close(start_signal_pipe[1]); 73 close(exec_child_pipe[0]); 74 close(exec_child_pipe[1]); 75 return false; 76 } else if (pid == 0) { 77 // In child process. 78 close(start_signal_pipe[1]); 79 close(exec_child_pipe[0]); 80 ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]); 81 _exit(0); 82 } 83 // In parent process. 84 close(start_signal_pipe[0]); 85 close(exec_child_pipe[1]); 86 start_signal_fd_ = start_signal_pipe[1]; 87 exec_child_fd_ = exec_child_pipe[0]; 88 work_pid_ = pid; 89 work_state_ = NotYetStartNewProcess; 90 return true; 91} 92 93static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) { 94 std::vector<char*> argv(args.size() + 1); 95 for (size_t i = 0; i < args.size(); ++i) { 96 argv[i] = &args[i][0]; 97 } 98 argv[args.size()] = nullptr; 99 100 char start_signal = 0; 101 ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1)); 102 if (nread == 1 && start_signal == 1) { 103 close(start_signal_fd); 104 execvp(argv[0], argv.data()); 105 // If execvp() succeed, we will not arrive here. But if it failed, we need to 106 // report the failure to the parent process by writing 1 to exec_child_fd. 107 int saved_errno = errno; 108 char exec_child_failed = 1; 109 TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1)); 110 close(exec_child_fd); 111 errno = saved_errno; 112 PLOG(ERROR) << "child process failed to execvp(" << argv[0] << ")"; 113 } else { 114 PLOG(ERROR) << "child process failed to receive start_signal, nread = " << nread; 115 } 116} 117 118bool Workload::Start() { 119 CHECK_EQ(work_state_, NotYetStartNewProcess); 120 char start_signal = 1; 121 ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1)); 122 if (nwrite != 1) { 123 PLOG(ERROR) << "write start signal failed"; 124 return false; 125 } 126 char exec_child_failed; 127 ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1)); 128 if (nread != 0) { 129 if (nread == -1) { 130 PLOG(ERROR) << "failed to receive error message from child process"; 131 } else { 132 LOG(ERROR) << "received error message from child process"; 133 } 134 return false; 135 } 136 work_state_ = Started; 137 return true; 138} 139 140bool Workload::WaitChildProcess(bool wait_forever) { 141 bool finished = false; 142 int status; 143 pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (wait_forever ? 0 : WNOHANG))); 144 if (result == work_pid_) { 145 finished = true; 146 if (WIFSIGNALED(status)) { 147 LOG(WARNING) << "child process was terminated by signal " << strsignal(WTERMSIG(status)); 148 } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { 149 LOG(WARNING) << "child process exited with exit code " << WEXITSTATUS(status); 150 } 151 } else if (result == -1) { 152 PLOG(ERROR) << "waitpid() failed"; 153 } 154 return finished; 155} 156