1 // 2 // Copyright (C) 2012 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 "update_engine/common/subprocess.h" 18 19 #include <fcntl.h> 20 #include <stdlib.h> 21 #include <string.h> 22 #include <unistd.h> 23 24 #include <memory> 25 #include <string> 26 #include <vector> 27 28 #include <base/bind.h> 29 #include <base/logging.h> 30 #include <base/posix/eintr_wrapper.h> 31 #include <base/strings/string_util.h> 32 #include <base/strings/stringprintf.h> 33 #include <brillo/process.h> 34 #include <brillo/secure_blob.h> 35 36 #include "update_engine/common/utils.h" 37 38 using brillo::MessageLoop; 39 using std::string; 40 using std::unique_ptr; 41 using std::vector; 42 43 namespace chromeos_update_engine { 44 45 namespace { 46 47 bool SetupChild(const std::map<string, string>& env, uint32_t flags) { 48 // Setup the environment variables. 49 clearenv(); 50 for (const auto& key_value : env) { 51 setenv(key_value.first.c_str(), key_value.second.c_str(), 0); 52 } 53 54 if ((flags & Subprocess::kRedirectStderrToStdout) != 0) { 55 if (HANDLE_EINTR(dup2(STDOUT_FILENO, STDERR_FILENO)) != STDERR_FILENO) 56 return false; 57 } 58 59 int fd = HANDLE_EINTR(open("/dev/null", O_RDONLY)); 60 if (fd < 0) 61 return false; 62 if (HANDLE_EINTR(dup2(fd, STDIN_FILENO)) != STDIN_FILENO) 63 return false; 64 IGNORE_EINTR(close(fd)); 65 66 return true; 67 } 68 69 // Helper function to launch a process with the given Subprocess::Flags. 70 // This function only sets up and starts the process according to the |flags|. 71 // The caller is responsible for watching the termination of the subprocess. 72 // Return whether the process was successfully launched and fills in the |proc| 73 // Process. 74 bool LaunchProcess(const vector<string>& cmd, 75 uint32_t flags, 76 const vector<int>& output_pipes, 77 brillo::Process* proc) { 78 for (const string& arg : cmd) 79 proc->AddArg(arg); 80 proc->SetSearchPath((flags & Subprocess::kSearchPath) != 0); 81 82 // Create an environment for the child process with just the required PATHs. 83 std::map<string, string> env; 84 for (const char* key : {"LD_LIBRARY_PATH", "PATH"}) { 85 const char* value = getenv(key); 86 if (value) 87 env.emplace(key, value); 88 } 89 90 for (const int fd : output_pipes) { 91 proc->RedirectUsingPipe(fd, false); 92 } 93 proc->SetCloseUnusedFileDescriptors(true); 94 proc->RedirectUsingPipe(STDOUT_FILENO, false); 95 proc->SetPreExecCallback(base::Bind(&SetupChild, env, flags)); 96 97 return proc->Start(); 98 } 99 100 } // namespace 101 102 void Subprocess::Init( 103 brillo::AsynchronousSignalHandlerInterface* async_signal_handler) { 104 if (subprocess_singleton_ == this) 105 return; 106 CHECK(subprocess_singleton_ == nullptr); 107 subprocess_singleton_ = this; 108 109 process_reaper_.Register(async_signal_handler); 110 } 111 112 Subprocess::~Subprocess() { 113 if (subprocess_singleton_ == this) 114 subprocess_singleton_ = nullptr; 115 } 116 117 void Subprocess::OnStdoutReady(SubprocessRecord* record) { 118 char buf[1024]; 119 size_t bytes_read; 120 do { 121 bytes_read = 0; 122 bool eof; 123 bool ok = utils::ReadAll( 124 record->stdout_fd, buf, arraysize(buf), &bytes_read, &eof); 125 record->stdout.append(buf, bytes_read); 126 if (!ok || eof) { 127 // There was either an error or an EOF condition, so we are done watching 128 // the file descriptor. 129 MessageLoop::current()->CancelTask(record->stdout_task_id); 130 record->stdout_task_id = MessageLoop::kTaskIdNull; 131 return; 132 } 133 } while (bytes_read); 134 } 135 136 void Subprocess::ChildExitedCallback(const siginfo_t& info) { 137 auto pid_record = subprocess_records_.find(info.si_pid); 138 if (pid_record == subprocess_records_.end()) 139 return; 140 SubprocessRecord* record = pid_record->second.get(); 141 142 // Make sure we read any remaining process output and then close the pipe. 143 OnStdoutReady(record); 144 145 MessageLoop::current()->CancelTask(record->stdout_task_id); 146 record->stdout_task_id = MessageLoop::kTaskIdNull; 147 148 // Don't print any log if the subprocess exited with exit code 0. 149 if (info.si_code != CLD_EXITED) { 150 LOG(INFO) << "Subprocess terminated with si_code " << info.si_code; 151 } else if (info.si_status != 0) { 152 LOG(INFO) << "Subprocess exited with si_status: " << info.si_status; 153 } 154 155 if (!record->stdout.empty()) { 156 LOG(INFO) << "Subprocess output:\n" << record->stdout; 157 } 158 if (!record->callback.is_null()) { 159 record->callback.Run(info.si_status, record->stdout); 160 } 161 // Release and close all the pipes after calling the callback so our 162 // redirected pipes are still alive. Releasing the process first makes 163 // Reset(0) not attempt to kill the process, which is already a zombie at this 164 // point. 165 record->proc.Release(); 166 record->proc.Reset(0); 167 168 subprocess_records_.erase(pid_record); 169 } 170 171 pid_t Subprocess::Exec(const vector<string>& cmd, 172 const ExecCallback& callback) { 173 return ExecFlags(cmd, kRedirectStderrToStdout, {}, callback); 174 } 175 176 pid_t Subprocess::ExecFlags(const vector<string>& cmd, 177 uint32_t flags, 178 const vector<int>& output_pipes, 179 const ExecCallback& callback) { 180 unique_ptr<SubprocessRecord> record(new SubprocessRecord(callback)); 181 182 if (!LaunchProcess(cmd, flags, output_pipes, &record->proc)) { 183 LOG(ERROR) << "Failed to launch subprocess"; 184 return 0; 185 } 186 187 pid_t pid = record->proc.pid(); 188 CHECK(process_reaper_.WatchForChild(FROM_HERE, pid, base::Bind( 189 &Subprocess::ChildExitedCallback, 190 base::Unretained(this)))); 191 192 record->stdout_fd = record->proc.GetPipe(STDOUT_FILENO); 193 // Capture the subprocess output. Make our end of the pipe non-blocking. 194 int fd_flags = fcntl(record->stdout_fd, F_GETFL, 0) | O_NONBLOCK; 195 if (HANDLE_EINTR(fcntl(record->stdout_fd, F_SETFL, fd_flags)) < 0) { 196 LOG(ERROR) << "Unable to set non-blocking I/O mode on fd " 197 << record->stdout_fd << "."; 198 } 199 200 record->stdout_task_id = MessageLoop::current()->WatchFileDescriptor( 201 FROM_HERE, 202 record->stdout_fd, 203 MessageLoop::WatchMode::kWatchRead, 204 true, 205 base::Bind(&Subprocess::OnStdoutReady, record.get())); 206 207 subprocess_records_[pid] = std::move(record); 208 return pid; 209 } 210 211 void Subprocess::KillExec(pid_t pid) { 212 auto pid_record = subprocess_records_.find(pid); 213 if (pid_record == subprocess_records_.end()) 214 return; 215 pid_record->second->callback.Reset(); 216 // We don't care about output/return code, so we use SIGKILL here to ensure it 217 // will be killed, SIGTERM might lead to leaked subprocess. 218 if (kill(pid, SIGKILL) != 0) { 219 PLOG(WARNING) << "Error sending SIGKILL to " << pid; 220 } 221 // Release the pid now so we don't try to kill it if Subprocess is destroyed 222 // before the corresponding ChildExitedCallback() is called. 223 pid_record->second->proc.Release(); 224 } 225 226 int Subprocess::GetPipeFd(pid_t pid, int fd) const { 227 auto pid_record = subprocess_records_.find(pid); 228 if (pid_record == subprocess_records_.end()) 229 return -1; 230 return pid_record->second->proc.GetPipe(fd); 231 } 232 233 bool Subprocess::SynchronousExec(const vector<string>& cmd, 234 int* return_code, 235 string* stdout) { 236 // The default for SynchronousExec is to use kSearchPath since the code relies 237 // on that. 238 return SynchronousExecFlags( 239 cmd, 240 kRedirectStderrToStdout | kSearchPath, 241 return_code, 242 stdout); 243 } 244 245 bool Subprocess::SynchronousExecFlags(const vector<string>& cmd, 246 uint32_t flags, 247 int* return_code, 248 string* stdout) { 249 brillo::ProcessImpl proc; 250 // It doesn't make sense to redirect some pipes in the synchronous case 251 // because we won't be reading on our end, so we don't expose the output_pipes 252 // in this case. 253 if (!LaunchProcess(cmd, flags, {}, &proc)) { 254 LOG(ERROR) << "Failed to launch subprocess"; 255 return false; 256 } 257 258 if (stdout) { 259 stdout->clear(); 260 } 261 262 int fd = proc.GetPipe(STDOUT_FILENO); 263 vector<char> buffer(32 * 1024); 264 while (true) { 265 int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size())); 266 if (rc < 0) { 267 PLOG(ERROR) << "Reading from child's output"; 268 break; 269 } else if (rc == 0) { 270 break; 271 } else { 272 if (stdout) 273 stdout->append(buffer.data(), rc); 274 } 275 } 276 // At this point, the subprocess already closed the output, so we only need to 277 // wait for it to finish. 278 int proc_return_code = proc.Wait(); 279 if (return_code) 280 *return_code = proc_return_code; 281 return proc_return_code != brillo::Process::kErrorExitStatus; 282 } 283 284 bool Subprocess::SubprocessInFlight() { 285 for (const auto& pid_record : subprocess_records_) { 286 if (!pid_record.second->callback.is_null()) 287 return true; 288 } 289 return false; 290 } 291 292 Subprocess* Subprocess::subprocess_singleton_ = nullptr; 293 294 } // namespace chromeos_update_engine 295