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/payload_consumer/postinstall_runner_action.h" 18 19#include <sys/stat.h> 20#include <sys/types.h> 21#include <unistd.h> 22 23#include <memory> 24#include <string> 25#include <vector> 26 27#include <base/bind.h> 28#include <base/files/file_util.h> 29#include <base/message_loop/message_loop.h> 30#include <base/strings/string_util.h> 31#include <base/strings/stringprintf.h> 32#include <brillo/bind_lambda.h> 33#include <brillo/message_loops/base_message_loop.h> 34#include <brillo/message_loops/message_loop_utils.h> 35#include <gmock/gmock.h> 36#include <gtest/gtest.h> 37 38#include "update_engine/common/constants.h" 39#include "update_engine/common/fake_boot_control.h" 40#include "update_engine/common/fake_hardware.h" 41#include "update_engine/common/test_utils.h" 42#include "update_engine/common/utils.h" 43 44using brillo::MessageLoop; 45using chromeos_update_engine::test_utils::ScopedLoopbackDeviceBinder; 46using std::string; 47using std::vector; 48 49namespace chromeos_update_engine { 50 51class PostinstActionProcessorDelegate : public ActionProcessorDelegate { 52 public: 53 PostinstActionProcessorDelegate() = default; 54 void ProcessingDone(const ActionProcessor* processor, 55 ErrorCode code) override { 56 MessageLoop::current()->BreakLoop(); 57 processing_done_called_ = true; 58 } 59 void ProcessingStopped(const ActionProcessor* processor) override { 60 MessageLoop::current()->BreakLoop(); 61 processing_stopped_called_ = true; 62 } 63 64 void ActionCompleted(ActionProcessor* processor, 65 AbstractAction* action, 66 ErrorCode code) override { 67 if (action->Type() == PostinstallRunnerAction::StaticType()) { 68 code_ = code; 69 code_set_ = true; 70 } 71 } 72 73 ErrorCode code_{ErrorCode::kError}; 74 bool code_set_{false}; 75 bool processing_done_called_{false}; 76 bool processing_stopped_called_{false}; 77}; 78 79class MockPostinstallRunnerActionDelegate 80 : public PostinstallRunnerAction::DelegateInterface { 81 public: 82 MOCK_METHOD1(ProgressUpdate, void(double progress)); 83}; 84 85class PostinstallRunnerActionTest : public ::testing::Test { 86 protected: 87 void SetUp() override { 88 loop_.SetAsCurrent(); 89 async_signal_handler_.Init(); 90 subprocess_.Init(&async_signal_handler_); 91 // These tests use the postinstall files generated by "generate_images.sh" 92 // stored in the "disk_ext2_unittest.img" image. 93 postinstall_image_ = 94 test_utils::GetBuildArtifactsPath("gen/disk_ext2_unittest.img"); 95 } 96 97 // Setup an action processor and run the PostinstallRunnerAction with a single 98 // partition |device_path|, running the |postinstall_program| command from 99 // there. 100 void RunPosinstallAction(const string& device_path, 101 const string& postinstall_program, 102 bool powerwash_required); 103 104 public: 105 void ResumeRunningAction() { 106 ASSERT_NE(nullptr, postinstall_action_); 107 postinstall_action_->ResumeAction(); 108 } 109 110 void SuspendRunningAction() { 111 if (!postinstall_action_ || !postinstall_action_->current_command_ || 112 test_utils::Readlink(base::StringPrintf( 113 "/proc/%d/fd/0", postinstall_action_->current_command_)) != 114 "/dev/zero") { 115 // We need to wait for the postinstall command to start and flag that it 116 // is ready by redirecting its input to /dev/zero. 117 loop_.PostDelayedTask( 118 FROM_HERE, 119 base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction, 120 base::Unretained(this)), 121 base::TimeDelta::FromMilliseconds(100)); 122 } else { 123 postinstall_action_->SuspendAction(); 124 // Schedule to be resumed in a little bit. 125 loop_.PostDelayedTask( 126 FROM_HERE, 127 base::Bind(&PostinstallRunnerActionTest::ResumeRunningAction, 128 base::Unretained(this)), 129 base::TimeDelta::FromMilliseconds(100)); 130 } 131 } 132 133 void CancelWhenStarted() { 134 if (!postinstall_action_ || !postinstall_action_->current_command_) { 135 // Wait for the postinstall command to run. 136 loop_.PostDelayedTask( 137 FROM_HERE, 138 base::Bind(&PostinstallRunnerActionTest::CancelWhenStarted, 139 base::Unretained(this)), 140 base::TimeDelta::FromMilliseconds(10)); 141 } else { 142 CHECK(processor_); 143 processor_->StopProcessing(); 144 } 145 } 146 147 protected: 148 base::MessageLoopForIO base_loop_; 149 brillo::BaseMessageLoop loop_{&base_loop_}; 150 brillo::AsynchronousSignalHandler async_signal_handler_; 151 Subprocess subprocess_; 152 153 // The path to the postinstall sample image. 154 string postinstall_image_; 155 156 FakeBootControl fake_boot_control_; 157 FakeHardware fake_hardware_; 158 PostinstActionProcessorDelegate processor_delegate_; 159 160 // The PostinstallRunnerAction delegate receiving the progress updates. 161 PostinstallRunnerAction::DelegateInterface* setup_action_delegate_{nullptr}; 162 163 // A pointer to the posinstall_runner action and the processor. 164 PostinstallRunnerAction* postinstall_action_{nullptr}; 165 ActionProcessor* processor_{nullptr}; 166}; 167 168void PostinstallRunnerActionTest::RunPosinstallAction( 169 const string& device_path, 170 const string& postinstall_program, 171 bool powerwash_required) { 172 ActionProcessor processor; 173 processor_ = &processor; 174 ObjectFeederAction<InstallPlan> feeder_action; 175 InstallPlan::Partition part; 176 part.name = "part"; 177 part.target_path = device_path; 178 part.run_postinstall = true; 179 part.postinstall_path = postinstall_program; 180 InstallPlan install_plan; 181 install_plan.partitions = {part}; 182 install_plan.download_url = "http://127.0.0.1:8080/update"; 183 install_plan.powerwash_required = powerwash_required; 184 feeder_action.set_obj(install_plan); 185 PostinstallRunnerAction runner_action(&fake_boot_control_, &fake_hardware_); 186 postinstall_action_ = &runner_action; 187 runner_action.set_delegate(setup_action_delegate_); 188 BondActions(&feeder_action, &runner_action); 189 ObjectCollectorAction<InstallPlan> collector_action; 190 BondActions(&runner_action, &collector_action); 191 processor.EnqueueAction(&feeder_action); 192 processor.EnqueueAction(&runner_action); 193 processor.EnqueueAction(&collector_action); 194 processor.set_delegate(&processor_delegate_); 195 196 loop_.PostTask( 197 FROM_HERE, 198 base::Bind( 199 [](ActionProcessor* processor) { processor->StartProcessing(); }, 200 base::Unretained(&processor))); 201 loop_.Run(); 202 ASSERT_FALSE(processor.IsRunning()); 203 postinstall_action_ = nullptr; 204 processor_ = nullptr; 205 EXPECT_TRUE(processor_delegate_.processing_stopped_called_ || 206 processor_delegate_.processing_done_called_); 207 if (processor_delegate_.processing_done_called_) { 208 // Sanity check that the code was set when the processor finishes. 209 EXPECT_TRUE(processor_delegate_.code_set_); 210 } 211} 212 213TEST_F(PostinstallRunnerActionTest, ProcessProgressLineTest) { 214 PostinstallRunnerAction action(&fake_boot_control_, &fake_hardware_); 215 testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_; 216 action.set_delegate(&mock_delegate_); 217 218 action.current_partition_ = 1; 219 action.partition_weight_ = {1, 2, 5}; 220 action.accumulated_weight_ = 1; 221 action.total_weight_ = 8; 222 223 // 50% of the second action is 2/8 = 0.25 of the total. 224 EXPECT_CALL(mock_delegate_, ProgressUpdate(0.25)); 225 action.ProcessProgressLine("global_progress 0.5"); 226 testing::Mock::VerifyAndClearExpectations(&mock_delegate_); 227 228 // 1.5 should be read as 100%, to catch rounding error cases like 1.000001. 229 // 100% of the second is 3/8 of the total. 230 EXPECT_CALL(mock_delegate_, ProgressUpdate(0.375)); 231 action.ProcessProgressLine("global_progress 1.5"); 232 testing::Mock::VerifyAndClearExpectations(&mock_delegate_); 233 234 // None of these should trigger a progress update. 235 action.ProcessProgressLine("foo_bar"); 236 action.ProcessProgressLine("global_progress"); 237 action.ProcessProgressLine("global_progress "); 238 action.ProcessProgressLine("global_progress NaN"); 239 action.ProcessProgressLine("global_progress Exception in ... :)"); 240} 241 242// Test that postinstall succeeds in the simple case of running the default 243// /postinst command which only exits 0. 244TEST_F(PostinstallRunnerActionTest, RunAsRootSimpleTest) { 245 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 246 RunPosinstallAction(loop.dev(), kPostinstallDefaultScript, false); 247 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 248 EXPECT_TRUE(processor_delegate_.processing_done_called_); 249 250 // Since powerwash_required was false, this should not trigger a powerwash. 251 EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled()); 252} 253 254TEST_F(PostinstallRunnerActionTest, RunAsRootRunSymlinkFileTest) { 255 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 256 RunPosinstallAction(loop.dev(), "bin/postinst_link", false); 257 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 258} 259 260TEST_F(PostinstallRunnerActionTest, RunAsRootPowerwashRequiredTest) { 261 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 262 // Run a simple postinstall program but requiring a powerwash. 263 RunPosinstallAction(loop.dev(), "bin/postinst_example", true); 264 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 265 266 // Check that powerwash was scheduled. 267 EXPECT_TRUE(fake_hardware_.IsPowerwashScheduled()); 268} 269 270// Runs postinstall from a partition file that doesn't mount, so it should 271// fail. 272TEST_F(PostinstallRunnerActionTest, RunAsRootCantMountTest) { 273 RunPosinstallAction("/dev/null", kPostinstallDefaultScript, false); 274 EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_); 275 276 // In case of failure, Postinstall should not signal a powerwash even if it 277 // was requested. 278 EXPECT_FALSE(fake_hardware_.IsPowerwashScheduled()); 279} 280 281// Check that the failures from the postinstall script cause the action to 282// fail. 283TEST_F(PostinstallRunnerActionTest, RunAsRootErrScriptTest) { 284 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 285 RunPosinstallAction(loop.dev(), "bin/postinst_fail1", false); 286 EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_); 287} 288 289// The exit code 3 and 4 are a specials cases that would be reported back to 290// UMA with a different error code. Test those cases are properly detected. 291TEST_F(PostinstallRunnerActionTest, RunAsRootFirmwareBErrScriptTest) { 292 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 293 RunPosinstallAction(loop.dev(), "bin/postinst_fail3", false); 294 EXPECT_EQ(ErrorCode::kPostinstallBootedFromFirmwareB, 295 processor_delegate_.code_); 296} 297 298// Check that you can't specify an absolute path. 299TEST_F(PostinstallRunnerActionTest, RunAsRootAbsolutePathNotAllowedTest) { 300 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 301 RunPosinstallAction(loop.dev(), "/etc/../bin/sh", false); 302 EXPECT_EQ(ErrorCode::kPostinstallRunnerError, processor_delegate_.code_); 303} 304 305#ifdef __ANDROID__ 306// Check that the postinstall file is relabeled to the postinstall label. 307// SElinux labels are only set on Android. 308TEST_F(PostinstallRunnerActionTest, RunAsRootCheckFileContextsTest) { 309 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 310 RunPosinstallAction(loop.dev(), "bin/self_check_context", false); 311 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 312} 313#endif // __ANDROID__ 314 315// Check that you can suspend/resume postinstall actions. 316TEST_F(PostinstallRunnerActionTest, RunAsRootSuspendResumeActionTest) { 317 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 318 319 // We need to wait for the child to run and setup its signal handler. 320 loop_.PostTask(FROM_HERE, 321 base::Bind(&PostinstallRunnerActionTest::SuspendRunningAction, 322 base::Unretained(this))); 323 RunPosinstallAction(loop.dev(), "bin/postinst_suspend", false); 324 // postinst_suspend returns 0 only if it was suspended at some point. 325 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 326 EXPECT_TRUE(processor_delegate_.processing_done_called_); 327} 328 329// Test that we can cancel a postinstall action while it is running. 330TEST_F(PostinstallRunnerActionTest, RunAsRootCancelPostinstallActionTest) { 331 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 332 333 // Wait for the action to start and then cancel it. 334 CancelWhenStarted(); 335 RunPosinstallAction(loop.dev(), "bin/postinst_suspend", false); 336 // When canceling the action, the action never finished and therefore we had 337 // a ProcessingStopped call instead. 338 EXPECT_FALSE(processor_delegate_.code_set_); 339 EXPECT_TRUE(processor_delegate_.processing_stopped_called_); 340} 341 342// Test that we parse and process the progress reports from the progress 343// file descriptor. 344TEST_F(PostinstallRunnerActionTest, RunAsRootProgressUpdatesTest) { 345 testing::StrictMock<MockPostinstallRunnerActionDelegate> mock_delegate_; 346 testing::InSequence s; 347 EXPECT_CALL(mock_delegate_, ProgressUpdate(0)); 348 349 // The postinst_progress program will call with 0.25, 0.5 and 1. 350 EXPECT_CALL(mock_delegate_, ProgressUpdate(0.25)); 351 EXPECT_CALL(mock_delegate_, ProgressUpdate(0.5)); 352 EXPECT_CALL(mock_delegate_, ProgressUpdate(1.)); 353 354 EXPECT_CALL(mock_delegate_, ProgressUpdate(1.)); 355 356 ScopedLoopbackDeviceBinder loop(postinstall_image_, false, nullptr); 357 setup_action_delegate_ = &mock_delegate_; 358 RunPosinstallAction(loop.dev(), "bin/postinst_progress", false); 359 EXPECT_EQ(ErrorCode::kSuccess, processor_delegate_.code_); 360} 361 362} // namespace chromeos_update_engine 363