blob: d9b212b44a9a53b23e7d1c3acdb9c3a3d1f2fc83 [file] [log] [blame]
Mike Frysinger8155d082012-04-06 15:23:18 -04001// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
adlr@google.com3defe6a2009-12-04 20:57:17 +00002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Deymoaab50e32014-11-10 19:55:35 -08005#include "update_engine/subprocess.h"
6
Alex Deymo5d527802014-07-18 14:24:13 -07007#include <fcntl.h>
Gilad Arnoldb6c562a2013-07-01 02:19:26 -07008#include <netinet/in.h>
9#include <netinet/ip.h>
10#include <poll.h>
11#include <sys/socket.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000012#include <sys/stat.h>
13#include <sys/types.h>
14#include <unistd.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080015
Alex Deymo461b2592015-07-24 20:10:52 -070016#include <set>
adlr@google.com3defe6a2009-12-04 20:57:17 +000017#include <string>
18#include <vector>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080019
Alex Deymo60ca1a72015-06-18 18:19:15 -070020#include <base/bind.h>
21#include <base/location.h>
Alex Deymo0b3db6b2015-08-10 15:19:37 -070022#include <base/message_loop/message_loop.h>
Alex Vakulenko75039d72014-03-25 12:36:28 -070023#include <base/strings/string_util.h>
24#include <base/strings/stringprintf.h>
25#include <base/time/time.h>
Alex Deymo60ca1a72015-06-18 18:19:15 -070026#include <chromeos/bind_lambda.h>
Alex Deymo0b3db6b2015-08-10 15:19:37 -070027#include <chromeos/message_loops/base_message_loop.h>
Alex Deymo60ca1a72015-06-18 18:19:15 -070028#include <chromeos/message_loops/message_loop.h>
29#include <chromeos/message_loops/message_loop_utils.h>
Alex Deymo461b2592015-07-24 20:10:52 -070030#include <chromeos/strings/string_utils.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000031#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080032
adlr@google.com3defe6a2009-12-04 20:57:17 +000033#include "update_engine/test_utils.h"
34#include "update_engine/utils.h"
35
Gilad Arnold8e3f1262013-01-08 14:59:54 -080036using base::TimeDelta;
Alex Deymo60ca1a72015-06-18 18:19:15 -070037using chromeos::MessageLoop;
adlr@google.com3defe6a2009-12-04 20:57:17 +000038using std::string;
39using std::vector;
40
41namespace chromeos_update_engine {
42
43class SubprocessTest : public ::testing::Test {
44 protected:
Alex Deymo60ca1a72015-06-18 18:19:15 -070045 void SetUp() override {
46 loop_.SetAsCurrent();
Alex Deymob7ca0962014-10-01 17:58:07 -070047 async_signal_handler_.Init();
48 subprocess_.Init(&async_signal_handler_);
Alex Deymo60ca1a72015-06-18 18:19:15 -070049 }
50
Alex Deymo0b3db6b2015-08-10 15:19:37 -070051 base::MessageLoopForIO base_loop_;
52 chromeos::BaseMessageLoop loop_{&base_loop_};
Alex Deymob7ca0962014-10-01 17:58:07 -070053 chromeos::AsynchronousSignalHandler async_signal_handler_;
Alex Deymo461b2592015-07-24 20:10:52 -070054 Subprocess subprocess_;
adlr@google.com3defe6a2009-12-04 20:57:17 +000055};
56
57namespace {
Alex Deymo461b2592015-07-24 20:10:52 -070058
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070059int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000060
Alex Deymo461b2592015-07-24 20:10:52 -070061void ExpectedResults(int expected_return_code, const string& expected_output,
62 int return_code, const string& output) {
63 EXPECT_EQ(expected_return_code, return_code);
64 EXPECT_EQ(expected_output, output);
Alex Deymo60ca1a72015-06-18 18:19:15 -070065 MessageLoop::current()->BreakLoop();
adlr@google.com3defe6a2009-12-04 20:57:17 +000066}
67
Alex Deymo461b2592015-07-24 20:10:52 -070068void ExpectedEnvVars(int return_code, const string& output) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080069 EXPECT_EQ(0, return_code);
Alex Deymo461b2592015-07-24 20:10:52 -070070 const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
71 for (string key_value : chromeos::string_utils::Split(output, "\n")) {
72 auto key_value_pair = chromeos::string_utils::SplitAtFirst(
73 key_value, "=", true);
74 EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
75 }
Alex Deymo29b81532015-07-09 11:51:49 -070076 MessageLoop::current()->BreakLoop();
77}
78
Alex Vakulenkod2779df2014-06-16 13:19:00 -070079} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000080
Alex Deymo461b2592015-07-24 20:10:52 -070081TEST_F(SubprocessTest, IsASingleton) {
82 EXPECT_EQ(&subprocess_, &Subprocess::Get());
83}
84
85TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
86 std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
87 EXPECT_EQ(&subprocess_, &Subprocess::Get());
88 another_subprocess.reset();
89 EXPECT_EQ(&subprocess_, &Subprocess::Get());
90}
91
Alex Deymo60ca1a72015-06-18 18:19:15 -070092TEST_F(SubprocessTest, SimpleTest) {
Alex Deymo461b2592015-07-24 20:10:52 -070093 EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
94 base::Bind(&ExpectedResults, 1, "")));
Alex Deymo60ca1a72015-06-18 18:19:15 -070095 loop_.Run();
adlr@google.com3defe6a2009-12-04 20:57:17 +000096}
97
Alex Deymo60ca1a72015-06-18 18:19:15 -070098TEST_F(SubprocessTest, EchoTest) {
Alex Deymo461b2592015-07-24 20:10:52 -070099 EXPECT_TRUE(subprocess_.Exec(
100 {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
101 base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
Alex Deymo29b81532015-07-09 11:51:49 -0700102 loop_.Run();
103}
104
105TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
Alex Deymo461b2592015-07-24 20:10:52 -0700106 EXPECT_TRUE(subprocess_.ExecFlags(
107 {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
108 0,
109 base::Bind(&ExpectedResults, 0, "on stdout\n")));
Alex Deymo60ca1a72015-06-18 18:19:15 -0700110 loop_.Run();
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800111}
112
Alex Deymo461b2592015-07-24 20:10:52 -0700113TEST_F(SubprocessTest, EnvVarsAreFiltered) {
114 EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
115 loop_.Run();
116}
117
118TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
119 int rc = -1;
120 EXPECT_TRUE(Subprocess::SynchronousExecFlags(
121 {"true"}, Subprocess::kSearchPath, &rc, nullptr));
122 EXPECT_EQ(0, rc);
123}
124
Alex Deymo60ca1a72015-06-18 18:19:15 -0700125TEST_F(SubprocessTest, SynchronousEchoTest) {
126 vector<string> cmd = {
127 "/bin/sh",
128 "-c",
129 "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700130 int rc = -1;
131 string stdout;
132 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
133 EXPECT_EQ(0, rc);
134 EXPECT_EQ("stdout-herestderr-there", stdout);
135}
136
Alex Deymo60ca1a72015-06-18 18:19:15 -0700137TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700138 int rc = -1;
Alex Deymo461b2592015-07-24 20:10:52 -0700139 ASSERT_TRUE(Subprocess::SynchronousExec(
140 {"/bin/sh", "-c", "echo test"},
141 &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700142 EXPECT_EQ(0, rc);
143}
144
adlr@google.com3defe6a2009-12-04 20:57:17 +0000145namespace {
Alex Deymo461b2592015-07-24 20:10:52 -0700146void CallbackBad(int return_code, const string& output) {
147 ADD_FAILURE() << "should never be called.";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000148}
149
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700150// TODO(garnold) this test method uses test_http_server as a representative for
151// interactive processes that can be spawned/terminated at will. This causes us
152// to go through hoops when spawning this process (e.g. obtaining the port
153// number it uses so we can control it with wget). It would have been much
154// preferred to use something else and thus simplify both test_http_server
155// (doesn't have to be able to communicate through a temp file) and the test
156// code below; for example, it sounds like a brain dead sleep loop with proper
157// signal handlers could be used instead.
Alex Deymo60ca1a72015-06-18 18:19:15 -0700158void StartAndCancelInRunLoop(bool* spawned) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700159 // Create a temp file for test_http_server to communicate its port number.
160 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
161 int temp_fd = mkstemp(temp_file_name);
162 CHECK_GE(temp_fd, 0);
163 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
164 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
165
adlr@google.com3defe6a2009-12-04 20:57:17 +0000166 vector<string> cmd;
167 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700168 cmd.push_back(temp_file_name);
Alex Deymo461b2592015-07-24 20:10:52 -0700169 uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000170 EXPECT_NE(0, tag);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700171 *spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700172 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000173 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800174 TimeDelta total_wait_time;
175 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
176 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700177 local_server_port = 0;
178 static const char* kServerListeningMsgPrefix = "listening on port ";
179 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
180 char line[80];
181 int line_len = read(temp_fd, line, sizeof(line) - 1);
182 if (line_len > 0) {
183 line[line_len] = '\0';
184 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
185 const char* listening_port_str =
186 line + strlen(kServerListeningMsgPrefix);
187 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700188 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
189 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700190 CHECK(!*end_ptr || *end_ptr == '\n');
191 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000192 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700193 } else if (line_len < 0 && errno != EAGAIN) {
194 LOG(INFO) << "error reading from " << temp_file_name << ": "
195 << strerror(errno);
196 break;
197 }
Alex Deymoea4444d2015-08-07 10:51:18 -0700198 usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700199 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000200 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700201 close(temp_fd);
202 remove(temp_file_name);
203 CHECK_GT(local_server_port, 0);
204 LOG(INFO) << "server listening on port " << local_server_port;
Alex Deymo29b81532015-07-09 11:51:49 -0700205 Subprocess::Get().KillExec(tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000206}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000207
Alex Deymo60ca1a72015-06-18 18:19:15 -0700208void ExitWhenDone(bool* spawned) {
209 if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000210 // tear down the sub process
211 printf("tear down time\n");
Alex Deymo10875d92014-11-10 21:52:57 -0800212 int status = test_utils::System(
Alex Vakulenko75039d72014-03-25 12:36:28 -0700213 base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
214 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000215 EXPECT_NE(-1, status) << "system() failed";
216 EXPECT_TRUE(WIFEXITED(status))
217 << "command failed to run or died abnormally";
Alex Deymo60ca1a72015-06-18 18:19:15 -0700218 MessageLoop::current()->BreakLoop();
219 } else {
220 // Re-run this callback again in 10 ms.
221 MessageLoop::current()->PostDelayedTask(
222 FROM_HERE,
223 base::Bind(&ExitWhenDone, spawned),
224 TimeDelta::FromMilliseconds(10));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000225 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000226}
227
Alex Deymo60ca1a72015-06-18 18:19:15 -0700228} // namespace
229
230TEST_F(SubprocessTest, CancelTest) {
231 bool spawned = false;
232 loop_.PostDelayedTask(
233 FROM_HERE,
234 base::Bind(&StartAndCancelInRunLoop, &spawned),
235 TimeDelta::FromMilliseconds(100));
236 loop_.PostDelayedTask(
237 FROM_HERE,
238 base::Bind(&ExitWhenDone, &spawned),
239 TimeDelta::FromMilliseconds(10));
240 loop_.Run();
241 // This test would leak a callback that runs when the child process exits
242 // unless we wait for it to run.
243 chromeos::MessageLoopRunUntil(
244 &loop_,
245 TimeDelta::FromSeconds(10),
246 base::Bind([] {
247 return Subprocess::Get().subprocess_records_.empty();
248 }));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000249}
250
251} // namespace chromeos_update_engine