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