blob: 266045609f0d0e872a5e7569882d6e008d3298ae [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 Vakulenko75039d72014-03-25 12:36:28 -070022#include <base/strings/string_util.h>
23#include <base/strings/stringprintf.h>
24#include <base/time/time.h>
Alex Deymo60ca1a72015-06-18 18:19:15 -070025#include <chromeos/bind_lambda.h>
26#include <chromeos/message_loops/glib_message_loop.h>
27#include <chromeos/message_loops/message_loop.h>
28#include <chromeos/message_loops/message_loop_utils.h>
Alex Deymo461b2592015-07-24 20:10:52 -070029#include <chromeos/strings/string_utils.h>
adlr@google.com3defe6a2009-12-04 20:57:17 +000030#include <gtest/gtest.h>
Gilad Arnold8e3f1262013-01-08 14:59:54 -080031
adlr@google.com3defe6a2009-12-04 20:57:17 +000032#include "update_engine/test_utils.h"
33#include "update_engine/utils.h"
34
Gilad Arnold8e3f1262013-01-08 14:59:54 -080035using base::TimeDelta;
Alex Deymo60ca1a72015-06-18 18:19:15 -070036using chromeos::MessageLoop;
adlr@google.com3defe6a2009-12-04 20:57:17 +000037using std::string;
38using std::vector;
39
40namespace chromeos_update_engine {
41
42class SubprocessTest : public ::testing::Test {
43 protected:
Alex Deymo60ca1a72015-06-18 18:19:15 -070044 void SetUp() override {
45 loop_.SetAsCurrent();
Alex Deymo461b2592015-07-24 20:10:52 -070046 subprocess_.Init();
Alex Deymo60ca1a72015-06-18 18:19:15 -070047 }
48
49 // TODO(deymo): Replace this with a FakeMessageLoop. Subprocess uses glib to
50 // asynchronously spawn a process, so we need to run a GlibMessageLoop here.
51 chromeos::GlibMessageLoop loop_;
Alex Deymo461b2592015-07-24 20:10:52 -070052 Subprocess subprocess_;
adlr@google.com3defe6a2009-12-04 20:57:17 +000053};
54
55namespace {
Alex Deymo461b2592015-07-24 20:10:52 -070056
Gilad Arnoldb6c562a2013-07-01 02:19:26 -070057int local_server_port = 0;
adlr@google.com3defe6a2009-12-04 20:57:17 +000058
Alex Deymo461b2592015-07-24 20:10:52 -070059void ExpectedResults(int expected_return_code, const string& expected_output,
60 int return_code, const string& output) {
61 EXPECT_EQ(expected_return_code, return_code);
62 EXPECT_EQ(expected_output, output);
Alex Deymo60ca1a72015-06-18 18:19:15 -070063 MessageLoop::current()->BreakLoop();
adlr@google.com3defe6a2009-12-04 20:57:17 +000064}
65
Alex Deymo461b2592015-07-24 20:10:52 -070066void ExpectedEnvVars(int return_code, const string& output) {
Darin Petkov6f03a3b2010-11-10 14:27:14 -080067 EXPECT_EQ(0, return_code);
Alex Deymo461b2592015-07-24 20:10:52 -070068 const std::set<string> allowed_envs = {"LD_LIBRARY_PATH", "PATH"};
69 for (string key_value : chromeos::string_utils::Split(output, "\n")) {
70 auto key_value_pair = chromeos::string_utils::SplitAtFirst(
71 key_value, "=", true);
72 EXPECT_NE(allowed_envs.end(), allowed_envs.find(key_value_pair.first));
73 }
Alex Deymo29b81532015-07-09 11:51:49 -070074 MessageLoop::current()->BreakLoop();
75}
76
Alex Vakulenkod2779df2014-06-16 13:19:00 -070077} // namespace
adlr@google.com3defe6a2009-12-04 20:57:17 +000078
Alex Deymo461b2592015-07-24 20:10:52 -070079TEST_F(SubprocessTest, IsASingleton) {
80 EXPECT_EQ(&subprocess_, &Subprocess::Get());
81}
82
83TEST_F(SubprocessTest, InactiveInstancesDontChangeTheSingleton) {
84 std::unique_ptr<Subprocess> another_subprocess(new Subprocess());
85 EXPECT_EQ(&subprocess_, &Subprocess::Get());
86 another_subprocess.reset();
87 EXPECT_EQ(&subprocess_, &Subprocess::Get());
88}
89
Alex Deymo60ca1a72015-06-18 18:19:15 -070090TEST_F(SubprocessTest, SimpleTest) {
Alex Deymo461b2592015-07-24 20:10:52 -070091 EXPECT_TRUE(subprocess_.Exec({"/bin/false"},
92 base::Bind(&ExpectedResults, 1, "")));
Alex Deymo60ca1a72015-06-18 18:19:15 -070093 loop_.Run();
adlr@google.com3defe6a2009-12-04 20:57:17 +000094}
95
Alex Deymo60ca1a72015-06-18 18:19:15 -070096TEST_F(SubprocessTest, EchoTest) {
Alex Deymo461b2592015-07-24 20:10:52 -070097 EXPECT_TRUE(subprocess_.Exec(
98 {"/bin/sh", "-c", "echo this is stdout; echo this is stderr >&2"},
99 base::Bind(&ExpectedResults, 0, "this is stdout\nthis is stderr\n")));
Alex Deymo29b81532015-07-09 11:51:49 -0700100 loop_.Run();
101}
102
103TEST_F(SubprocessTest, StderrNotIncludedInOutputTest) {
Alex Deymo461b2592015-07-24 20:10:52 -0700104 EXPECT_TRUE(subprocess_.ExecFlags(
105 {"/bin/sh", "-c", "echo on stdout; echo on stderr >&2"},
106 0,
107 base::Bind(&ExpectedResults, 0, "on stdout\n")));
Alex Deymo60ca1a72015-06-18 18:19:15 -0700108 loop_.Run();
Darin Petkov6f03a3b2010-11-10 14:27:14 -0800109}
110
Alex Deymo461b2592015-07-24 20:10:52 -0700111TEST_F(SubprocessTest, EnvVarsAreFiltered) {
112 EXPECT_TRUE(subprocess_.Exec({"/usr/bin/env"}, base::Bind(&ExpectedEnvVars)));
113 loop_.Run();
114}
115
116TEST_F(SubprocessTest, SynchronousTrueSearchsOnPath) {
117 int rc = -1;
118 EXPECT_TRUE(Subprocess::SynchronousExecFlags(
119 {"true"}, Subprocess::kSearchPath, &rc, nullptr));
120 EXPECT_EQ(0, rc);
121}
122
Alex Deymo60ca1a72015-06-18 18:19:15 -0700123TEST_F(SubprocessTest, SynchronousEchoTest) {
124 vector<string> cmd = {
125 "/bin/sh",
126 "-c",
127 "echo -n stdout-here; echo -n stderr-there > /dev/stderr"};
Darin Petkov85d02b72011-05-17 13:25:51 -0700128 int rc = -1;
129 string stdout;
130 ASSERT_TRUE(Subprocess::SynchronousExec(cmd, &rc, &stdout));
131 EXPECT_EQ(0, rc);
132 EXPECT_EQ("stdout-herestderr-there", stdout);
133}
134
Alex Deymo60ca1a72015-06-18 18:19:15 -0700135TEST_F(SubprocessTest, SynchronousEchoNoOutputTest) {
Darin Petkov85d02b72011-05-17 13:25:51 -0700136 int rc = -1;
Alex Deymo461b2592015-07-24 20:10:52 -0700137 ASSERT_TRUE(Subprocess::SynchronousExec(
138 {"/bin/sh", "-c", "echo test"},
139 &rc, nullptr));
Darin Petkov85d02b72011-05-17 13:25:51 -0700140 EXPECT_EQ(0, rc);
141}
142
adlr@google.com3defe6a2009-12-04 20:57:17 +0000143namespace {
Alex Deymo461b2592015-07-24 20:10:52 -0700144void CallbackBad(int return_code, const string& output) {
145 ADD_FAILURE() << "should never be called.";
adlr@google.com3defe6a2009-12-04 20:57:17 +0000146}
147
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700148// TODO(garnold) this test method uses test_http_server as a representative for
149// interactive processes that can be spawned/terminated at will. This causes us
150// to go through hoops when spawning this process (e.g. obtaining the port
151// number it uses so we can control it with wget). It would have been much
152// preferred to use something else and thus simplify both test_http_server
153// (doesn't have to be able to communicate through a temp file) and the test
154// code below; for example, it sounds like a brain dead sleep loop with proper
155// signal handlers could be used instead.
Alex Deymo60ca1a72015-06-18 18:19:15 -0700156void StartAndCancelInRunLoop(bool* spawned) {
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700157 // Create a temp file for test_http_server to communicate its port number.
158 char temp_file_name[] = "/tmp/subprocess_unittest-test_http_server-XXXXXX";
159 int temp_fd = mkstemp(temp_file_name);
160 CHECK_GE(temp_fd, 0);
161 int temp_flags = fcntl(temp_fd, F_GETFL, 0) | O_NONBLOCK;
162 CHECK_EQ(fcntl(temp_fd, F_SETFL, temp_flags), 0);
163
adlr@google.com3defe6a2009-12-04 20:57:17 +0000164 vector<string> cmd;
165 cmd.push_back("./test_http_server");
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700166 cmd.push_back(temp_file_name);
Alex Deymo461b2592015-07-24 20:10:52 -0700167 uint32_t tag = Subprocess::Get().Exec(cmd, base::Bind(&CallbackBad));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000168 EXPECT_NE(0, tag);
Alex Deymo60ca1a72015-06-18 18:19:15 -0700169 *spawned = true;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700170 printf("test http server spawned\n");
adlr@google.com3defe6a2009-12-04 20:57:17 +0000171 // Wait for server to be up and running
Gilad Arnold8e3f1262013-01-08 14:59:54 -0800172 TimeDelta total_wait_time;
173 const TimeDelta kSleepTime = TimeDelta::FromMilliseconds(100);
174 const TimeDelta kMaxWaitTime = TimeDelta::FromSeconds(3);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700175 local_server_port = 0;
176 static const char* kServerListeningMsgPrefix = "listening on port ";
177 while (total_wait_time.InMicroseconds() < kMaxWaitTime.InMicroseconds()) {
178 char line[80];
179 int line_len = read(temp_fd, line, sizeof(line) - 1);
180 if (line_len > 0) {
181 line[line_len] = '\0';
182 CHECK_EQ(strstr(line, kServerListeningMsgPrefix), line);
183 const char* listening_port_str =
184 line + strlen(kServerListeningMsgPrefix);
185 char* end_ptr;
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700186 long raw_port = strtol(listening_port_str, // NOLINT(runtime/int)
187 &end_ptr, 10);
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700188 CHECK(!*end_ptr || *end_ptr == '\n');
189 local_server_port = static_cast<in_port_t>(raw_port);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000190 break;
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700191 } else if (line_len < 0 && errno != EAGAIN) {
192 LOG(INFO) << "error reading from " << temp_file_name << ": "
193 << strerror(errno);
194 break;
195 }
Alex Deymoea4444d2015-08-07 10:51:18 -0700196 usleep(kSleepTime.InMicroseconds());
Darin Petkov27fa9c52010-07-15 15:11:55 -0700197 total_wait_time += kSleepTime;
adlr@google.com3defe6a2009-12-04 20:57:17 +0000198 }
Gilad Arnoldb6c562a2013-07-01 02:19:26 -0700199 close(temp_fd);
200 remove(temp_file_name);
201 CHECK_GT(local_server_port, 0);
202 LOG(INFO) << "server listening on port " << local_server_port;
Alex Deymo29b81532015-07-09 11:51:49 -0700203 Subprocess::Get().KillExec(tag);
adlr@google.com3defe6a2009-12-04 20:57:17 +0000204}
adlr@google.com3defe6a2009-12-04 20:57:17 +0000205
Alex Deymo60ca1a72015-06-18 18:19:15 -0700206void ExitWhenDone(bool* spawned) {
207 if (*spawned && !Subprocess::Get().SubprocessInFlight()) {
adlr@google.com3defe6a2009-12-04 20:57:17 +0000208 // tear down the sub process
209 printf("tear down time\n");
Alex Deymo10875d92014-11-10 21:52:57 -0800210 int status = test_utils::System(
Alex Vakulenko75039d72014-03-25 12:36:28 -0700211 base::StringPrintf("wget -O /dev/null http://127.0.0.1:%d/quitquitquit",
212 local_server_port));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000213 EXPECT_NE(-1, status) << "system() failed";
214 EXPECT_TRUE(WIFEXITED(status))
215 << "command failed to run or died abnormally";
Alex Deymo60ca1a72015-06-18 18:19:15 -0700216 MessageLoop::current()->BreakLoop();
217 } else {
218 // Re-run this callback again in 10 ms.
219 MessageLoop::current()->PostDelayedTask(
220 FROM_HERE,
221 base::Bind(&ExitWhenDone, spawned),
222 TimeDelta::FromMilliseconds(10));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000223 }
adlr@google.com3defe6a2009-12-04 20:57:17 +0000224}
225
Alex Deymo60ca1a72015-06-18 18:19:15 -0700226} // namespace
227
228TEST_F(SubprocessTest, CancelTest) {
229 bool spawned = false;
230 loop_.PostDelayedTask(
231 FROM_HERE,
232 base::Bind(&StartAndCancelInRunLoop, &spawned),
233 TimeDelta::FromMilliseconds(100));
234 loop_.PostDelayedTask(
235 FROM_HERE,
236 base::Bind(&ExitWhenDone, &spawned),
237 TimeDelta::FromMilliseconds(10));
238 loop_.Run();
239 // This test would leak a callback that runs when the child process exits
240 // unless we wait for it to run.
241 chromeos::MessageLoopRunUntil(
242 &loop_,
243 TimeDelta::FromSeconds(10),
244 base::Bind([] {
245 return Subprocess::Get().subprocess_records_.empty();
246 }));
adlr@google.com3defe6a2009-12-04 20:57:17 +0000247}
248
249} // namespace chromeos_update_engine