blob: af8eec3174e9e898e7b2d2128f7013965c253693 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
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//
adlr@google.com3defe6a2009-12-04 20:57:17 +000016
Alex Deymoaab50e32014-11-10 19:55:35 -080017#include "update_engine/subprocess.h"
18
Alex Deymo5d527802014-07-18 14:24:13 -070019#include <fcntl.h>
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070020#include <netinet/in.h>
21#include <netinet/ip.h>
22#include <poll.h>
23#include <sys/socket.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000024#include <sys/stat.h>
25#include <sys/types.h>
26#include <unistd.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080027
Alex Deymo461b2592015-07-24 20:10:52 -070028#include <set>
adlr@google.com3defe6a2009-12-04 20:57:17 +000029#include <string>
30#include <vector>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080031
Alex Deymo60ca1a72015-06-18 18:19:15 -070032#include <base/bind.h>
33#include <base/location.h>
Alex Deymo0b3db6b2015-08-10 15:19:37 -070034#include <base/message_loop/message_loop.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070035#include <base/strings/string_util.h>
36#include <base/strings/stringprintf.h>
37#include <base/time/time.h>
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070038#include <brillo/bind_lambda.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>
adlr@google.com3defe6a2009-12-04 20:57:17 +000043#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080044
adlr@google.com3defe6a2009-12-04 20:57:17 +000045#include "update_engine/test_utils.h"
46#include "update_engine/utils.h"
47
Gilad Arnold8e3f1262013-01-08 14:59:54 -080048using base::TimeDelta;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070049using brillo::MessageLoop;
adlr@google.com3defe6a2009-12-04 20:57:17 +000050using std::string;
51using std::vector;
52
53namespace chromeos_update_engine {
54
55class SubprocessTest : public ::testing::Test {
56 protected:
Alex Deymo60ca1a72015-06-18 18:19:15 -070057 void SetUp() override {
58 loop_.SetAsCurrent();
Alex Deymob7ca0962014-10-01 17:58:07 -070059 async_signal_handler_.Init();
60 subprocess_.Init(&async_signal_handler_);
Alex Deymo60ca1a72015-06-18 18:19:15 -070061 }
62
Alex Deymo0b3db6b2015-08-10 15:19:37 -070063 base::MessageLoopForIO base_loop_;
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070064 brillo::BaseMessageLoop loop_{&base_loop_};
65 brillo::AsynchronousSignalHandler async_signal_handler_;
Alex Deymo461b2592015-07-24 20:10:52 -070066 Subprocess subprocess_;
adlr@google.com3defe6a2009-12-04 20:57:17 +000067};
68
69namespace {
Alex Deymo461b2592015-07-24 20:10:52 -070070
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070071int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000072
Alex Deymo461b2592015-07-24 20:10:52 -070073void ExpectedResults(int expected_return_code, const string& expected_output,
74 int return_code, const string& output) {
75 EXPECT_EQ(expected_return_code, return_code);
76 EXPECT_EQ(expected_output, output);
Alex Deymo60ca1a72015-06-18 18:19:15 -070077 MessageLoop::current()->BreakLoop();
adlr@google.com3defe6a2009-12-04 20:57:17 +000078}
79
Alex Deymo461b2592015-07-24 20:10:52 -070080void ExpectedEnvVars(int return_code, const string& output) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080081 EXPECT_EQ(0, return_code);
Alex Deymo461b2592015-07-24 20:10:52 -070082 const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -070083 for (string key_value : brillo::string_utils::Split(output, "\n")) {
84 auto key_value_pair = brillo::string_utils::SplitAtFirst(
Alex Deymo461b2592015-07-24 20:10:52 -070085 key_value, "=", true);
86 EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
87 }
Alex Deymo29b81532015-07-09 11:51:49 -070088 MessageLoop::current()->BreakLoop();
89}
90
Alex Vakulenkod2779df2014-06-16 13:19:00 -070091} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000092
Alex Deymo461b2592015-07-24 20:10:52 -070093TEST_F(SubprocessTest, IsASingleton) {
94 EXPECT_EQ(&subprocess_, &Subprocess::Get());
95}
96
97TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
98 std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
99 EXPECT_EQ(&subprocess_, &Subprocess::Get());
100 another_subprocess.reset();
101 EXPECT_EQ(&subprocess_, &Subprocess::Get());
102}
103
Alex Deymo60ca1a72015-06-18 18:19:15 -0700104TEST_F(SubprocessTest, SimpleTest) {
Alex Deymo461b2592015-07-24 20:10:52 -0700105 EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
106 base::Bind(&ExpectedResults, 1, "")));
Alex Deymo60ca1a72015-06-18 18:19:15 -0700107 loop_.Run();
adlr@google.com3defe6a2009-12-04 20:57:17 +0000108}
109
Alex Deymo60ca1a72015-06-18 18:19:15 -0700110TEST_F(SubprocessTest, EchoTest) {
Alex Deymo461b2592015-07-24 20:10:52 -0700111 EXPECT_TRUE(subprocess_.Exec(
112 {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
113 base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
Alex Deymo29b81532015-07-09 11:51:49 -0700114 loop_.Run();
115}
116
117TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
Alex Deymo461b2592015-07-24 20:10:52 -0700118 EXPECT_TRUE(subprocess_.ExecFlags(
119 {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
120 0,
121 base::Bind(&ExpectedResults, 0, "on stdout\n")));
Alex Deymo60ca1a72015-06-18 18:19:15 -0700122 loop_.Run();
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800123}
124
Alex Deymo461b2592015-07-24 20:10:52 -0700125TEST_F(SubprocessTest, EnvVarsAreFiltered) {
126 EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
127 loop_.Run();
128}
129
130TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
131 int rc = -1;
132 EXPECT_TRUE(Subprocess::SynchronousExecFlags(
133 {"true"}, Subprocess::kSearchPath, &rc, nullptr));
134 EXPECT_EQ(0, rc);
135}
136
Alex Deymo60ca1a72015-06-18 18:19:15 -0700137TEST_F(SubprocessTest, SynchronousEchoTest) {
138 vector<string> cmd = {
139 "/bin/sh",
140 "-c",
141 "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700142 int rc = -1;
143 string stdout;
144 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
145 EXPECT_EQ(0, rc);
146 EXPECT_EQ("stdout-herestderr-there", stdout);
147}
148
Alex Deymo60ca1a72015-06-18 18:19:15 -0700149TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700150 int rc = -1;
Alex Deymo461b2592015-07-24 20:10:52 -0700151 ASSERT_TRUE(Subprocess::SynchronousExec(
152 {"/bin/sh", "-c", "echo test"},
153 &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700154 EXPECT_EQ(0, rc);
155}
156
adlr@google.com3defe6a2009-12-04 20:57:17 +0000157namespace {
Alex Deymo461b2592015-07-24 20:10:52 -0700158void CallbackBad(int return_code, const string& output) {
159 ADD_FAILURE() << "should never be called.";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000160}
161
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700162// TODO(garnold) this test method uses test_http_server as a representative for
163// interactive processes that can be spawned/terminated at will. This causes us
164// to go through hoops when spawning this process (e.g. obtaining the port
165// number it uses so we can control it with wget). It would have been much
166// preferred to use something else and thus simplify both test_http_server
167// (doesn't have to be able to communicate through a temp file) and the test
168// code below; for example, it sounds like a brain dead sleep loop with proper
169// signal handlers could be used instead.
Alex Deymo60ca1a72015-06-18 18:19:15 -0700170void StartAndCancelInRunLoop(bool* spawned) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700171 // Create a temp file for test_http_server to communicate its port number.
172 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
173 int temp_fd = mkstemp(temp_file_name);
174 CHECK_GE(temp_fd, 0);
175 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
176 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
177
adlr@google.com3defe6a2009-12-04 20:57:17 +0000178 vector<string> cmd;
179 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700180 cmd.push_back(temp_file_name);
Alex Deymo461b2592015-07-24 20:10:52 -0700181 uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000182 EXPECT_NE(0, tag);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700183 *spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700184 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000185 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800186 TimeDelta total_wait_time;
187 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
188 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700189 local_server_port = 0;
190 static const char* kServerListeningMsgPrefix = "listening on port ";
191 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
192 char line[80];
193 int line_len = read(temp_fd, line, sizeof(line) - 1);
194 if (line_len > 0) {
195 line[line_len] = '\0';
196 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
197 const char* listening_port_str =
198 line + strlen(kServerListeningMsgPrefix);
199 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700200 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
201 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700202 CHECK(!*end_ptr || *end_ptr == '\n');
203 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000204 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700205 } else if (line_len < 0 && errno != EAGAIN) {
206 LOG(INFO) << "error reading from " << temp_file_name << ": "
207 << strerror(errno);
208 break;
209 }
Alex Deymoea4444d2015-08-07 10:51:18 -0700210 usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700211 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000212 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700213 close(temp_fd);
214 remove(temp_file_name);
215 CHECK_GT(local_server_port, 0);
216 LOG(INFO) << "server listening on port " << local_server_port;
Alex Deymo29b81532015-07-09 11:51:49 -0700217 Subprocess::Get().KillExec(tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000218}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000219
Alex Deymo60ca1a72015-06-18 18:19:15 -0700220void ExitWhenDone(bool* spawned) {
221 if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000222 // tear down the sub process
223 printf("tear down time\n");
Alex Deymo10875d92014-11-10 21:52:57 -0800224 int status = test_utils::System(
Alex Vakulenko75039d72014-03-25 12:36:28 -0700225 base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
226 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000227 EXPECT_NE(-1, status) << "system() failed";
228 EXPECT_TRUE(WIFEXITED(status))
229 << "command failed to run or died abnormally";
Alex Deymo60ca1a72015-06-18 18:19:15 -0700230 MessageLoop::current()->BreakLoop();
231 } else {
232 // Re-run this callback again in 10 ms.
233 MessageLoop::current()->PostDelayedTask(
234 FROM_HERE,
235 base::Bind(&ExitWhenDone, spawned),
236 TimeDelta::FromMilliseconds(10));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000237 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000238}
239
Alex Deymo60ca1a72015-06-18 18:19:15 -0700240} // namespace
241
242TEST_F(SubprocessTest, CancelTest) {
243 bool spawned = false;
244 loop_.PostDelayedTask(
245 FROM_HERE,
246 base::Bind(&StartAndCancelInRunLoop, &spawned),
247 TimeDelta::FromMilliseconds(100));
248 loop_.PostDelayedTask(
249 FROM_HERE,
250 base::Bind(&ExitWhenDone, &spawned),
251 TimeDelta::FromMilliseconds(10));
252 loop_.Run();
253 // This test would leak a callback that runs when the child process exits
254 // unless we wait for it to run.
Alex Vakulenko3f39d5c2015-10-13 09:27:13 -0700255 brillo::MessageLoopRunUntil(
Alex Deymo60ca1a72015-06-18 18:19:15 -0700256 &loop_,
257 TimeDelta::FromSeconds(10),
258 base::Bind([] {
259 return Subprocess::Get().subprocess_records_.empty();
260 }));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000261}
262
263} // namespace chromeos_update_engine