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 <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 34static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd); 35 36bool Workload::CreateNewProcess() { 37 CHECK_EQ(work_state_, NotYetCreateNewProcess); 38 39 int start_signal_pipe[2]; 40 if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) { 41 PLOG(ERROR) << "pipe2() failed"; 42 return false; 43 } 44 45 int exec_child_pipe[2]; 46 if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) { 47 PLOG(ERROR) << "pipe2() failed"; 48 close(start_signal_pipe[0]); 49 close(start_signal_pipe[1]); 50 return false; 51 } 52 53 pid_t pid = fork(); 54 if (pid == -1) { 55 PLOG(ERROR) << "fork() failed"; 56 close(start_signal_pipe[0]); 57 close(start_signal_pipe[1]); 58 close(exec_child_pipe[0]); 59 close(exec_child_pipe[1]); 60 return false; 61 } else if (pid == 0) { 62 // In child process. 63 close(start_signal_pipe[1]); 64 close(exec_child_pipe[0]); 65 ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]); 66 } 67 // In parent process. 68 close(start_signal_pipe[0]); 69 close(exec_child_pipe[1]); 70 start_signal_fd_ = start_signal_pipe[1]; 71 exec_child_fd_ = exec_child_pipe[0]; 72 work_pid_ = pid; 73 work_state_ = NotYetStartNewProcess; 74 return true; 75} 76 77static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) { 78 std::vector<char*> argv(args.size() + 1); 79 for (size_t i = 0; i < args.size(); ++i) { 80 argv[i] = &args[i][0]; 81 } 82 argv[args.size()] = nullptr; 83 84 char start_signal = 0; 85 ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1)); 86 if (nread == 1 && start_signal == 1) { 87 close(start_signal_fd); 88 execvp(argv[0], argv.data()); 89 // If execvp() succeed, we will not arrive here. But if it failed, we need to 90 // report the failure to the parent process by writing 1 to exec_child_fd. 91 int saved_errno = errno; 92 char exec_child_failed = 1; 93 TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1)); 94 close(exec_child_fd); 95 errno = saved_errno; 96 PLOG(ERROR) << "execvp(" << argv[0] << ") failed"; 97 } else { 98 PLOG(DEBUG) << "child process failed to receive start_signal, nread = " << nread; 99 } 100 exit(1); 101} 102 103bool Workload::Start() { 104 CHECK_EQ(work_state_, NotYetStartNewProcess); 105 char start_signal = 1; 106 ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1)); 107 if (nwrite != 1) { 108 PLOG(ERROR) << "write start signal failed"; 109 return false; 110 } 111 char exec_child_failed; 112 ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1)); 113 if (nread != 0) { 114 LOG(ERROR) << "exec child failed"; 115 return false; 116 } 117 work_state_ = Started; 118 return true; 119} 120 121bool Workload::IsFinished() { 122 if (work_state_ == Started) { 123 WaitChildProcess(true); 124 } 125 return work_state_ == Finished; 126} 127 128void Workload::WaitFinish() { 129 CHECK(work_state_ == Started || work_state_ == Finished); 130 if (work_state_ == Started) { 131 WaitChildProcess(false); 132 } 133} 134 135void Workload::WaitChildProcess(bool no_hang) { 136 int status; 137 pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (no_hang ? WNOHANG : 0))); 138 if (result == work_pid_) { 139 work_state_ = Finished; 140 if (WIFSIGNALED(status)) { 141 LOG(ERROR) << "work process was terminated by signal " << strsignal(WTERMSIG(status)); 142 } else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) { 143 LOG(ERROR) << "work process exited with exit code " << WEXITSTATUS(status); 144 } 145 } else if (result == -1) { 146 PLOG(FATAL) << "waitpid() failed"; 147 } 148} 149