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