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].reset(record.release()); 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 if (kill(pid, SIGTERM) != 0) { 217 PLOG(WARNING) << "Error sending SIGTERM to " << pid; 218 } 219 // Release the pid now so we don't try to kill it if Subprocess is destroyed 220 // before the corresponding ChildExitedCallback() is called. 221 pid_record->second->proc.Release(); 222 } 223 224 int Subprocess::GetPipeFd(pid_t pid, int fd) const { 225 auto pid_record = subprocess_records_.find(pid); 226 if (pid_record == subprocess_records_.end()) 227 return -1; 228 return pid_record->second->proc.GetPipe(fd); 229 } 230 231 bool Subprocess::SynchronousExec(const vector<string>& cmd, 232 int* return_code, 233 string* stdout) { 234 // The default for SynchronousExec is to use kSearchPath since the code relies 235 // on that. 236 return SynchronousExecFlags( 237 cmd, 238 kRedirectStderrToStdout | kSearchPath, 239 return_code, 240 stdout); 241 } 242 243 bool Subprocess::SynchronousExecFlags(const vector<string>& cmd, 244 uint32_t flags, 245 int* return_code, 246 string* stdout) { 247 brillo::ProcessImpl proc; 248 // It doesn't make sense to redirect some pipes in the synchronous case 249 // because we won't be reading on our end, so we don't expose the output_pipes 250 // in this case. 251 if (!LaunchProcess(cmd, flags, {}, &proc)) { 252 LOG(ERROR) << "Failed to launch subprocess"; 253 return false; 254 } 255 256 if (stdout) { 257 stdout->clear(); 258 } 259 260 int fd = proc.GetPipe(STDOUT_FILENO); 261 vector<char> buffer(32 * 1024); 262 while (true) { 263 int rc = HANDLE_EINTR(read(fd, buffer.data(), buffer.size())); 264 if (rc < 0) { 265 PLOG(ERROR) << "Reading from child's output"; 266 break; 267 } else if (rc == 0) { 268 break; 269 } else { 270 if (stdout) 271 stdout->append(buffer.data(), rc); 272 } 273 } 274 // At this point, the subprocess already closed the output, so we only need to 275 // wait for it to finish. 276 int proc_return_code = proc.Wait(); 277 if (return_code) 278 *return_code = proc_return_code; 279 return proc_return_code != brillo::Process::kErrorExitStatus; 280 } 281 282 bool Subprocess::SubprocessInFlight() { 283 for (const auto& pid_record : subprocess_records_) { 284 if (!pid_record.second->callback.is_null()) 285 return true; 286 } 287 return false; 288 } 289 290 Subprocess* Subprocess::subprocess_singleton_ = nullptr; 291 292 } // namespace chromeos_update_engine 293