1 // 2 // Copyright (C) 2011 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/payload_consumer/postinstall_runner_action.h" 18 19 #include <fcntl.h> 20 #include <signal.h> 21 #include <stdlib.h> 22 #include <sys/mount.h> 23 #include <sys/types.h> 24 #include <unistd.h> 25 26 #include <base/files/file_path.h> 27 #include <base/files/file_util.h> 28 #include <base/logging.h> 29 #include <base/strings/string_split.h> 30 #include <base/strings/string_util.h> 31 32 #include "update_engine/common/action_processor.h" 33 #include "update_engine/common/boot_control_interface.h" 34 #include "update_engine/common/platform_constants.h" 35 #include "update_engine/common/subprocess.h" 36 #include "update_engine/common/utils.h" 37 38 namespace { 39 40 // The file descriptor number from the postinstall program's perspective where 41 // it can report status updates. This can be any number greater than 2 (stderr), 42 // but must be kept in sync with the "bin/postinst_progress" defined in the 43 // sample_images.sh file. 44 const int kPostinstallStatusFd = 3; 45 46 } // namespace 47 48 namespace chromeos_update_engine { 49 50 using brillo::MessageLoop; 51 using std::string; 52 using std::vector; 53 54 void PostinstallRunnerAction::PerformAction() { 55 CHECK(HasInputObject()); 56 install_plan_ = GetInputObject(); 57 58 if (install_plan_.powerwash_required) { 59 if (hardware_->SchedulePowerwash()) { 60 powerwash_scheduled_ = true; 61 } else { 62 return CompletePostinstall(ErrorCode::kPostinstallPowerwashError); 63 } 64 } 65 66 // Initialize all the partition weights. 67 partition_weight_.resize(install_plan_.partitions.size()); 68 total_weight_ = 0; 69 for (size_t i = 0; i < install_plan_.partitions.size(); ++i) { 70 // TODO(deymo): This code sets the weight to all the postinstall commands, 71 // but we could remember how long they took in the past and use those 72 // values. 73 partition_weight_[i] = install_plan_.partitions[i].run_postinstall; 74 total_weight_ += partition_weight_[i]; 75 } 76 accumulated_weight_ = 0; 77 ReportProgress(0); 78 79 PerformPartitionPostinstall(); 80 } 81 82 void PostinstallRunnerAction::PerformPartitionPostinstall() { 83 if (install_plan_.download_url.empty()) { 84 LOG(INFO) << "Skipping post-install during rollback"; 85 return CompletePostinstall(ErrorCode::kSuccess); 86 } 87 88 // Skip all the partitions that don't have a post-install step. 89 while (current_partition_ < install_plan_.partitions.size() && 90 !install_plan_.partitions[current_partition_].run_postinstall) { 91 VLOG(1) << "Skipping post-install on partition " 92 << install_plan_.partitions[current_partition_].name; 93 current_partition_++; 94 } 95 if (current_partition_ == install_plan_.partitions.size()) 96 return CompletePostinstall(ErrorCode::kSuccess); 97 98 const InstallPlan::Partition& partition = 99 install_plan_.partitions[current_partition_]; 100 101 const string mountable_device = 102 utils::MakePartitionNameForMount(partition.target_path); 103 if (mountable_device.empty()) { 104 LOG(ERROR) << "Cannot make mountable device from " << partition.target_path; 105 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 106 } 107 108 // Perform post-install for the current_partition_ partition. At this point we 109 // need to call CompletePartitionPostinstall to complete the operation and 110 // cleanup. 111 #ifdef __ANDROID__ 112 fs_mount_dir_ = "/postinstall"; 113 #else // __ANDROID__ 114 TEST_AND_RETURN( 115 utils::MakeTempDirectory("au_postint_mount.XXXXXX", &fs_mount_dir_)); 116 #endif // __ANDROID__ 117 118 base::FilePath postinstall_path(partition.postinstall_path); 119 if (postinstall_path.IsAbsolute()) { 120 LOG(ERROR) << "Invalid absolute path passed to postinstall, use a relative" 121 "path instead: " 122 << partition.postinstall_path; 123 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 124 } 125 126 string abs_path = 127 base::FilePath(fs_mount_dir_).Append(postinstall_path).value(); 128 if (!base::StartsWith( 129 abs_path, fs_mount_dir_, base::CompareCase::SENSITIVE)) { 130 LOG(ERROR) << "Invalid relative postinstall path: " 131 << partition.postinstall_path; 132 return CompletePostinstall(ErrorCode::kPostinstallRunnerError); 133 } 134 135 #ifdef __ANDROID__ 136 // In Chromium OS, the postinstall step is allowed to write to the block 137 // device on the target image, so we don't mark it as read-only and should 138 // be read-write since we just wrote to it during the update. 139 140 // Mark the block device as read-only before mounting for post-install. 141 if (!utils::SetBlockDeviceReadOnly(mountable_device, true)) { 142 return CompletePartitionPostinstall( 143 1, "Error marking the device " + mountable_device + " read only."); 144 } 145 #endif // __ANDROID__ 146 147 if (!utils::MountFilesystem(mountable_device, 148 fs_mount_dir_, 149 MS_RDONLY, 150 partition.filesystem_type, 151 constants::kPostinstallMountOptions)) { 152 return CompletePartitionPostinstall( 153 1, "Error mounting the device " + mountable_device); 154 } 155 156 LOG(INFO) << "Performing postinst (" << partition.postinstall_path << " at " 157 << abs_path << ") installed on device " << partition.target_path 158 << " and mountable device " << mountable_device; 159 160 // Logs the file format of the postinstall script we are about to run. This 161 // will help debug when the postinstall script doesn't match the architecture 162 // of our build. 163 LOG(INFO) << "Format file for new " << partition.postinstall_path 164 << " is: " << utils::GetFileFormat(abs_path); 165 166 // Runs the postinstall script asynchronously to free up the main loop while 167 // it's running. 168 vector<string> command = {abs_path}; 169 #ifdef __ANDROID__ 170 // In Brillo and Android, we pass the slot number and status fd. 171 command.push_back(std::to_string(install_plan_.target_slot)); 172 command.push_back(std::to_string(kPostinstallStatusFd)); 173 #else 174 // Chrome OS postinstall expects the target rootfs as the first parameter. 175 command.push_back(partition.target_path); 176 #endif // __ANDROID__ 177 178 current_command_ = Subprocess::Get().ExecFlags( 179 command, 180 Subprocess::kRedirectStderrToStdout, 181 {kPostinstallStatusFd}, 182 base::Bind(&PostinstallRunnerAction::CompletePartitionPostinstall, 183 base::Unretained(this))); 184 // Subprocess::Exec should never return a negative process id. 185 CHECK_GE(current_command_, 0); 186 187 if (!current_command_) { 188 CompletePartitionPostinstall(1, "Postinstall didn't launch"); 189 return; 190 } 191 192 // Monitor the status file descriptor. 193 progress_fd_ = 194 Subprocess::Get().GetPipeFd(current_command_, kPostinstallStatusFd); 195 int fd_flags = fcntl(progress_fd_, F_GETFL, 0) | O_NONBLOCK; 196 if (HANDLE_EINTR(fcntl(progress_fd_, F_SETFL, fd_flags)) < 0) { 197 PLOG(ERROR) << "Unable to set non-blocking I/O mode on fd " << progress_fd_; 198 } 199 200 progress_task_ = MessageLoop::current()->WatchFileDescriptor( 201 FROM_HERE, 202 progress_fd_, 203 MessageLoop::WatchMode::kWatchRead, 204 true, 205 base::Bind(&PostinstallRunnerAction::OnProgressFdReady, 206 base::Unretained(this))); 207 } 208 209 void PostinstallRunnerAction::OnProgressFdReady() { 210 char buf[1024]; 211 size_t bytes_read; 212 do { 213 bytes_read = 0; 214 bool eof; 215 bool ok = 216 utils::ReadAll(progress_fd_, buf, arraysize(buf), &bytes_read, &eof); 217 progress_buffer_.append(buf, bytes_read); 218 // Process every line. 219 vector<string> lines = base::SplitString( 220 progress_buffer_, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); 221 if (!lines.empty()) { 222 progress_buffer_ = lines.back(); 223 lines.pop_back(); 224 for (const auto& line : lines) { 225 ProcessProgressLine(line); 226 } 227 } 228 if (!ok || eof) { 229 // There was either an error or an EOF condition, so we are done watching 230 // the file descriptor. 231 MessageLoop::current()->CancelTask(progress_task_); 232 progress_task_ = MessageLoop::kTaskIdNull; 233 return; 234 } 235 } while (bytes_read); 236 } 237 238 bool PostinstallRunnerAction::ProcessProgressLine(const string& line) { 239 double frac = 0; 240 if (sscanf(line.c_str(), "global_progress %lf", &frac) == 1) { 241 ReportProgress(frac); 242 return true; 243 } 244 245 return false; 246 } 247 248 void PostinstallRunnerAction::ReportProgress(double frac) { 249 if (!delegate_) 250 return; 251 if (current_partition_ >= partition_weight_.size()) { 252 delegate_->ProgressUpdate(1.); 253 return; 254 } 255 if (!isfinite(frac) || frac < 0) 256 frac = 0; 257 if (frac > 1) 258 frac = 1; 259 double postinst_action_progress = 260 (accumulated_weight_ + partition_weight_[current_partition_] * frac) / 261 total_weight_; 262 delegate_->ProgressUpdate(postinst_action_progress); 263 } 264 265 void PostinstallRunnerAction::Cleanup() { 266 utils::UnmountFilesystem(fs_mount_dir_); 267 #ifndef __ANDROID__ 268 if (!base::DeleteFile(base::FilePath(fs_mount_dir_), false)) { 269 PLOG(WARNING) << "Not removing temporary mountpoint " << fs_mount_dir_; 270 } 271 #endif // !__ANDROID__ 272 fs_mount_dir_.clear(); 273 274 progress_fd_ = -1; 275 if (progress_task_ != MessageLoop::kTaskIdNull) { 276 MessageLoop::current()->CancelTask(progress_task_); 277 progress_task_ = MessageLoop::kTaskIdNull; 278 } 279 progress_buffer_.clear(); 280 } 281 282 void PostinstallRunnerAction::CompletePartitionPostinstall( 283 int return_code, const string& output) { 284 current_command_ = 0; 285 Cleanup(); 286 287 if (return_code != 0) { 288 LOG(ERROR) << "Postinst command failed with code: " << return_code; 289 ErrorCode error_code = ErrorCode::kPostinstallRunnerError; 290 291 if (return_code == 3) { 292 // This special return code means that we tried to update firmware, 293 // but couldn't because we booted from FW B, and we need to reboot 294 // to get back to FW A. 295 error_code = ErrorCode::kPostinstallBootedFromFirmwareB; 296 } 297 298 if (return_code == 4) { 299 // This special return code means that we tried to update firmware, 300 // but couldn't because we booted from FW B, and we need to reboot 301 // to get back to FW A. 302 error_code = ErrorCode::kPostinstallFirmwareRONotUpdatable; 303 } 304 305 // If postinstall script for this partition is optional we can ignore the 306 // result. 307 if (install_plan_.partitions[current_partition_].postinstall_optional) { 308 LOG(INFO) << "Ignoring postinstall failure since it is optional"; 309 } else { 310 return CompletePostinstall(error_code); 311 } 312 } 313 accumulated_weight_ += partition_weight_[current_partition_]; 314 current_partition_++; 315 ReportProgress(0); 316 317 PerformPartitionPostinstall(); 318 } 319 320 void PostinstallRunnerAction::CompletePostinstall(ErrorCode error_code) { 321 // We only attempt to mark the new slot as active if all the postinstall 322 // steps succeeded. 323 if (error_code == ErrorCode::kSuccess && 324 !boot_control_->SetActiveBootSlot(install_plan_.target_slot)) { 325 error_code = ErrorCode::kPostinstallRunnerError; 326 } 327 328 ScopedActionCompleter completer(processor_, this); 329 completer.set_code(error_code); 330 331 if (error_code != ErrorCode::kSuccess) { 332 LOG(ERROR) << "Postinstall action failed."; 333 334 // Undo any changes done to trigger Powerwash. 335 if (powerwash_scheduled_) 336 hardware_->CancelPowerwash(); 337 338 return; 339 } 340 341 LOG(INFO) << "All post-install commands succeeded"; 342 if (HasOutputPipe()) { 343 SetOutputObject(install_plan_); 344 } 345 } 346 347 void PostinstallRunnerAction::SuspendAction() { 348 if (!current_command_) 349 return; 350 if (kill(current_command_, SIGSTOP) != 0) { 351 PLOG(ERROR) << "Couldn't pause child process " << current_command_; 352 } 353 } 354 355 void PostinstallRunnerAction::ResumeAction() { 356 if (!current_command_) 357 return; 358 if (kill(current_command_, SIGCONT) != 0) { 359 PLOG(ERROR) << "Couldn't resume child process " << current_command_; 360 } 361 } 362 363 void PostinstallRunnerAction::TerminateProcessing() { 364 if (!current_command_) 365 return; 366 // Calling KillExec() will discard the callback we registered and therefore 367 // the unretained reference to this object. 368 Subprocess::Get().KillExec(current_command_); 369 current_command_ = 0; 370 Cleanup(); 371 } 372 373 } // namespace chromeos_update_engine 374