xref: /aosp_15_r20/system/update_engine/common/subprocess_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/common/subprocess.h"
18 
19 #include <fcntl.h>
20 #include <poll.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23 
24 #include <set>
25 #include <string>
26 #include <vector>
27 
28 #include <base/bind.h>
29 #include <base/files/scoped_temp_dir.h>
30 #include <base/location.h>
31 #if BASE_VER < 780000  // Android
32 #include <base/message_loop/message_loop.h>
33 #endif  // BASE_VER < 780000
34 #include <android-base/stringprintf.h>
35 #if BASE_VER >= 780000  // Chrome OS
36 #include <base/task/single_thread_task_executor.h>
37 #endif  // BASE_VER >= 780000
38 #include <base/time/time.h>
39 #include <brillo/message_loops/base_message_loop.h>
40 #include <brillo/message_loops/message_loop.h>
41 #include <brillo/message_loops/message_loop_utils.h>
42 #include <brillo/strings/string_utils.h>
43 #include <brillo/unittest_utils.h>
44 #include <gtest/gtest.h>
45 
46 #include "update_engine/common/test_utils.h"
47 #include "update_engine/common/utils.h"
48 
49 using base::TimeDelta;
50 using brillo::MessageLoop;
51 using std::string;
52 using std::unique_ptr;
53 using std::vector;
54 
55 namespace {
56 
57 #ifdef __ANDROID__
58 #define kBinPath "/system/bin"
59 #define kUsrBinPath "/system/bin"
60 #else
61 #define kBinPath "/bin"
62 #define kUsrBinPath "/usr/bin"
63 #endif  // __ANDROID__
64 
65 }  // namespace
66 
67 namespace chromeos_update_engine {
68 
69 class SubprocessTest : public ::testing::Test {
70  protected:
SetUp()71   void SetUp() override {
72     loop_.SetAsCurrent();
73     async_signal_handler_.Init();
74     subprocess_.Init(&async_signal_handler_);
75   }
76 
77 #if BASE_VER < 780000  // Android
78   base::MessageLoopForIO base_loop_;
79   brillo::BaseMessageLoop loop_{&base_loop_};
80 #else   // Chrome OS
81   base::SingleThreadTaskExecutor base_loop_{base::MessagePumpType::IO};
82   brillo::BaseMessageLoop loop_{base_loop_.task_runner()};
83 #endif  // BASE_VER < 780000
84   brillo::AsynchronousSignalHandler async_signal_handler_;
85   Subprocess subprocess_;
86   unique_ptr<base::FileDescriptorWatcher::Controller> watcher_;
87 };
88 
89 namespace {
90 
ExpectedResults(int expected_return_code,const string & expected_output,int return_code,const string & output)91 void ExpectedResults(int expected_return_code,
92                      const string& expected_output,
93                      int return_code,
94                      const string& output) {
95   EXPECT_EQ(expected_return_code, return_code);
96   EXPECT_EQ(expected_output, output);
97   MessageLoop::current()->BreakLoop();
98 }
99 
ExpectedEnvVars(int return_code,const string & output)100 void ExpectedEnvVars(int return_code, const string& output) {
101   EXPECT_EQ(0, return_code);
102   const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
103   for (const string& key_value : brillo::string_utils::Split(output, "\n")) {
104     auto key_value_pair =
105         brillo::string_utils::SplitAtFirst(key_value, "=", true);
106     EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
107   }
108   MessageLoop::current()->BreakLoop();
109 }
110 
ExpectedDataOnPipe(const Subprocess * subprocess,pid_t * pid,int child_fd,const string & child_fd_data,int expected_return_code,int return_code,const string &)111 void ExpectedDataOnPipe(const Subprocess* subprocess,
112                         pid_t* pid,
113                         int child_fd,
114                         const string& child_fd_data,
115                         int expected_return_code,
116                         int return_code,
117                         const string& /* output */) {
118   EXPECT_EQ(expected_return_code, return_code);
119 
120   // Verify that we can read the data from our end of |child_fd|.
121   int fd = subprocess->GetPipeFd(*pid, child_fd);
122   EXPECT_NE(-1, fd);
123   vector<char> buf(child_fd_data.size() + 1);
124   EXPECT_EQ(static_cast<ssize_t>(child_fd_data.size()),
125             HANDLE_EINTR(read(fd, buf.data(), buf.size())));
126   EXPECT_EQ(child_fd_data,
127             string(buf.begin(), buf.begin() + child_fd_data.size()));
128 
129   MessageLoop::current()->BreakLoop();
130 }
131 
132 }  // namespace
133 
TEST_F(SubprocessTest,IsASingleton)134 TEST_F(SubprocessTest, IsASingleton) {
135   EXPECT_EQ(&subprocess_, &Subprocess::Get());
136 }
137 
TEST_F(SubprocessTest,InactiveInstancesDontChangeTheSingleton)138 TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
139   std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
140   EXPECT_EQ(&subprocess_, &Subprocess::Get());
141   another_subprocess.reset();
142   EXPECT_EQ(&subprocess_, &Subprocess::Get());
143 }
144 
TEST_F(SubprocessTest,SimpleTest)145 TEST_F(SubprocessTest, SimpleTest) {
146   ASSERT_TRUE(subprocess_.Exec({kBinPath "/false"},
147                                base::Bind(&ExpectedResults, 1, "")));
148   loop_.Run();
149 }
150 
TEST_F(SubprocessTest,EchoTest)151 TEST_F(SubprocessTest, EchoTest) {
152   ASSERT_TRUE(subprocess_.Exec(
153       {kBinPath "/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
154       base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
155   loop_.Run();
156 }
157 
TEST_F(SubprocessTest,StderrNotIncludedInOutputTest)158 TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
159   ASSERT_TRUE(subprocess_.ExecFlags(
160       {kBinPath "/sh", "-c", "echo on stdout; echo on stderr >&2"},
161       0,
162       {},
163       base::Bind(&ExpectedResults, 0, "on stdout\n")));
164   loop_.Run();
165 }
166 
TEST_F(SubprocessTest,PipeRedirectFdTest)167 TEST_F(SubprocessTest, PipeRedirectFdTest) {
168   pid_t pid;
169   pid = subprocess_.ExecFlags(
170       {kBinPath "/sh", "-c", "echo on pipe >&3"},
171       0,
172       {3},
173       base::Bind(&ExpectedDataOnPipe, &subprocess_, &pid, 3, "on pipe\n", 0));
174   ASSERT_NE(0, pid);
175 
176   // Wrong file descriptor values should return -1.
177   ASSERT_EQ(-1, subprocess_.GetPipeFd(pid, 123));
178   loop_.Run();
179   // Calling GetPipeFd() after the callback runs is invalid.
180   ASSERT_EQ(-1, subprocess_.GetPipeFd(pid, 3));
181 }
182 
183 // Test that a pipe file descriptor open in the parent is not open in the child.
TEST_F(SubprocessTest,PipeClosedWhenNotRedirectedTest)184 TEST_F(SubprocessTest, PipeClosedWhenNotRedirectedTest) {
185   brillo::ScopedPipe pipe;
186 
187   // test_subprocess will return with the errno of fstat, which should be EBADF
188   // if the passed file descriptor is closed in the child.
189   const vector<string> cmd = {
190       test_utils::GetBuildArtifactsPath("test_subprocess"),
191       "fstat",
192       std::to_string(pipe.writer)};
193   ASSERT_TRUE(subprocess_.ExecFlags(
194       cmd, 0, {}, base::Bind(&ExpectedResults, EBADF, "")));
195   loop_.Run();
196 }
197 
TEST_F(SubprocessTest,EnvVarsAreFiltered)198 TEST_F(SubprocessTest, EnvVarsAreFiltered) {
199   ASSERT_TRUE(
200       subprocess_.Exec({kUsrBinPath "/env"}, base::Bind(&ExpectedEnvVars)));
201   loop_.Run();
202 }
203 
TEST_F(SubprocessTest,SynchronousTrueSearchsOnPath)204 TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
205   int rc = -1;
206   ASSERT_TRUE(Subprocess::SynchronousExecFlags(
207       {"true"}, Subprocess::kSearchPath, &rc, nullptr, nullptr));
208   EXPECT_EQ(0, rc);
209 }
210 
TEST_F(SubprocessTest,SynchronousEchoTest)211 TEST_F(SubprocessTest, SynchronousEchoTest) {
212   vector<string> cmd = {
213       kBinPath "/sh", "-c", "echo -n stdout-here; echo -n stderr-there >&2"};
214   int rc = -1;
215   string stdout, stderr;
216   ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout, &stderr));
217   EXPECT_EQ(0, rc);
218   EXPECT_EQ("stdout-here", stdout);
219   EXPECT_EQ("stderr-there", stderr);
220 }
221 
TEST_F(SubprocessTest,SynchronousEchoNoOutputTest)222 TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
223   int rc = -1;
224   ASSERT_TRUE(Subprocess::SynchronousExec(
225       {kBinPath "/sh", "-c", "echo test"}, &rc, nullptr, nullptr));
226   EXPECT_EQ(0, rc);
227 }
228 
229 namespace {
CallbackBad(int return_code,const string & output)230 void CallbackBad(int return_code, const string& output) {
231   ADD_FAILURE() << "should never be called.";
232 }
233 }  // namespace
234 
235 // Test that you can cancel a program that's already running.
TEST_F(SubprocessTest,CancelTest)236 TEST_F(SubprocessTest, CancelTest) {
237   base::ScopedTempDir tempdir;
238   ASSERT_TRUE(tempdir.CreateUniqueTempDir());
239   string fifo_path = tempdir.GetPath().Append("fifo").value();
240   ASSERT_EQ(0, mkfifo(fifo_path.c_str(), 0666));
241 
242   // Start a process, make sure it is running and try to cancel it. We write
243   // two bytes to the fifo, the first one marks that the program is running and
244   // the second one marks that the process waited for a timeout and was not
245   // killed. We should read the first byte but not the second one.
246   vector<string> cmd = {
247       kBinPath "/sh",
248       "-c",
249       android::base::StringPrintf(
250           // The 'sleep' launched below could be left behind as an orphaned
251           // process when the 'sh' process is terminated by SIGTERM. As a
252           // remedy, trap SIGTERM and kill the 'sleep' process, which requires
253           // launching 'sleep' in background and then waiting for it.
254           "cleanup() { kill \"${sleep_pid}\"; exit 0; }; "
255           "trap cleanup TERM; "
256           "sleep 60 & "
257           "sleep_pid=$!; "
258           "printf X >\"%s\"; "
259           "wait; "
260           "printf Y >\"%s\"; "
261           "exit 1",
262           fifo_path.c_str(),
263           fifo_path.c_str())};
264   uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
265   ASSERT_NE(0U, tag);
266 
267   int fifo_fd = HANDLE_EINTR(open(fifo_path.c_str(), O_RDONLY));
268   ASSERT_GE(fifo_fd, 0);
269 
270   watcher_ = base::FileDescriptorWatcher::WatchReadable(
271       fifo_fd,
272       base::Bind(
273           [](brillo::BaseMessageLoop* loop,
274              unique_ptr<base::FileDescriptorWatcher::Controller>* watcher,
275              int fifo_fd,
276              uint32_t tag) {
277             char c{};
278             EXPECT_EQ(1, HANDLE_EINTR(read(fifo_fd, &c, 1)));
279             EXPECT_EQ('X', c);
280             LOG(INFO) << "Killing tag " << tag;
281             Subprocess::Get().KillExec(tag);
282             loop->BreakLoop();
283             ASSERT_TRUE(Subprocess::Get().subprocess_records_.empty());
284             *watcher = nullptr;
285           },
286           &loop_,
287           // watcher_ is no longer used outside the clousure.
288           base::Unretained(&watcher_),
289           fifo_fd,
290           tag));
291 
292   // This test would leak a callback that runs when the child process exits
293   // unless we wait for it to run.
294   brillo::MessageLoopRunUntil(
295       &loop_, TimeDelta::FromSeconds(20), base::Bind([] {
296         return Subprocess::Get().subprocess_records_.empty();
297       }));
298   EXPECT_TRUE(Subprocess::Get().subprocess_records_.empty());
299   // Check that there isn't anything else to read from the pipe.
300   char c;
301   EXPECT_EQ(0, HANDLE_EINTR(read(fifo_fd, &c, 1)));
302   IGNORE_EINTR(close(fifo_fd));
303 }
304 
305 }  // namespace chromeos_update_engine
306