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