system/core
修訂 | 7e1d397d1a0fb9397b91a636dc6a71d132e7286d (tree) |
---|---|
時間 | 2019-04-11 03:13:39 |
作者 | Alex Buynytskyy <alexbuy@goog...> |
Commiter | Josh Gao |
Binding err to inout for raw protocol for in-process execute.
As raw protocol does not allow for splitting err - it has to be redirected to inout.
Before this change it was not done for in-process and all err data was lost.
Bug: 130086616
Test: manual + atest adbd_test
Change-Id: I6cd11c940673d73e2993a6eb23c46d31bd8bf504
(cherry picked from commit 704c97d6c244093a94e0a39deb0f2265c3bf83dd)
@@ -406,11 +406,16 @@ bool Subprocess::ExecInProcess(Command command, std::string* _Nonnull error) { | ||
406 | 406 | strerror(errno)); |
407 | 407 | return false; |
408 | 408 | } |
409 | - // Raw subprocess + shell protocol allows for splitting stderr. | |
410 | - if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) { | |
411 | - *error = android::base::StringPrintf("failed to create socketpair for stderr: %s", | |
412 | - strerror(errno)); | |
413 | - return false; | |
409 | + if (protocol_ == SubprocessProtocol::kShell) { | |
410 | + // Shell protocol allows for splitting stderr. | |
411 | + if (!CreateSocketpair(&stderr_sfd_, &child_stderr_sfd)) { | |
412 | + *error = android::base::StringPrintf("failed to create socketpair for stderr: %s", | |
413 | + strerror(errno)); | |
414 | + return false; | |
415 | + } | |
416 | + } else { | |
417 | + // Raw protocol doesn't support multiple output streams, so combine stdout and stderr. | |
418 | + child_stderr_sfd.reset(dup(child_stdinout_sfd)); | |
414 | 419 | } |
415 | 420 | |
416 | 421 | D("execinprocess: stdin/stdout FD = %d, stderr FD = %d", stdinout_sfd_.get(), |
@@ -35,7 +35,6 @@ class ShellServiceTest : public ::testing::Test { | ||
35 | 35 | static void SetUpTestCase() { |
36 | 36 | // This is normally done in main.cpp. |
37 | 37 | saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN); |
38 | - | |
39 | 38 | } |
40 | 39 | |
41 | 40 | static void TearDownTestCase() { |
@@ -49,26 +48,32 @@ class ShellServiceTest : public ::testing::Test { | ||
49 | 48 | SubprocessProtocol protocol); |
50 | 49 | void CleanupTestSubprocess(); |
51 | 50 | |
52 | - virtual void TearDown() override { | |
53 | - void CleanupTestSubprocess(); | |
54 | - } | |
51 | + void StartTestCommandInProcess(std::string name, Command command, SubprocessProtocol protocol); | |
52 | + | |
53 | + virtual void TearDown() override { CleanupTestSubprocess(); } | |
55 | 54 | |
56 | 55 | static sighandler_t saved_sigpipe_handler_; |
57 | 56 | |
58 | - unique_fd subprocess_fd_; | |
57 | + unique_fd command_fd_; | |
59 | 58 | }; |
60 | 59 | |
61 | 60 | sighandler_t ShellServiceTest::saved_sigpipe_handler_ = nullptr; |
62 | 61 | |
63 | 62 | void ShellServiceTest::StartTestSubprocess( |
64 | 63 | const char* command, SubprocessType type, SubprocessProtocol protocol) { |
65 | - subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol); | |
66 | - ASSERT_TRUE(subprocess_fd_ >= 0); | |
64 | + command_fd_ = StartSubprocess(command, nullptr, type, protocol); | |
65 | + ASSERT_TRUE(command_fd_ >= 0); | |
67 | 66 | } |
68 | 67 | |
69 | 68 | void ShellServiceTest::CleanupTestSubprocess() { |
70 | 69 | } |
71 | 70 | |
71 | +void ShellServiceTest::StartTestCommandInProcess(std::string name, Command command, | |
72 | + SubprocessProtocol protocol) { | |
73 | + command_fd_ = StartCommandInProcess(std::move(name), std::move(command), protocol); | |
74 | + ASSERT_TRUE(command_fd_ >= 0); | |
75 | +} | |
76 | + | |
72 | 77 | namespace { |
73 | 78 | |
74 | 79 | // Reads raw data from |fd| until it closes or errors. |
@@ -93,7 +98,7 @@ int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) { | ||
93 | 98 | stdout->clear(); |
94 | 99 | stderr->clear(); |
95 | 100 | |
96 | - ShellProtocol* protocol = new ShellProtocol(fd); | |
101 | + auto protocol = std::make_unique<ShellProtocol>(fd); | |
97 | 102 | while (protocol->Read()) { |
98 | 103 | switch (protocol->id()) { |
99 | 104 | case ShellProtocol::kIdStdout: |
@@ -111,7 +116,6 @@ int ReadShellProtocol(int fd, std::string* stdout, std::string* stderr) { | ||
111 | 116 | ADD_FAILURE() << "Unidentified packet ID: " << protocol->id(); |
112 | 117 | } |
113 | 118 | } |
114 | - delete protocol; | |
115 | 119 | |
116 | 120 | return exit_code; |
117 | 121 | } |
@@ -154,7 +158,7 @@ TEST_F(ShellServiceTest, RawNoProtocolSubprocess) { | ||
154 | 158 | |
155 | 159 | // [ -t 0 ] == 0 means we have a terminal (PTY). Even when requesting a raw subprocess, without |
156 | 160 | // the shell protocol we should always force a PTY to ensure proper cleanup. |
157 | - ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"}); | |
161 | + ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"}); | |
158 | 162 | } |
159 | 163 | |
160 | 164 | // Tests a PTY subprocess with no protocol. |
@@ -165,7 +169,7 @@ TEST_F(ShellServiceTest, PtyNoProtocolSubprocess) { | ||
165 | 169 | SubprocessType::kPty, SubprocessProtocol::kNone)); |
166 | 170 | |
167 | 171 | // [ -t 0 ] == 0 means we have a terminal (PTY). |
168 | - ExpectLinesEqual(ReadRaw(subprocess_fd_), {"foo", "bar", "0"}); | |
172 | + ExpectLinesEqual(ReadRaw(command_fd_), {"foo", "bar", "0"}); | |
169 | 173 | } |
170 | 174 | |
171 | 175 | // Tests a raw subprocess with the shell protocol. |
@@ -175,7 +179,7 @@ TEST_F(ShellServiceTest, RawShellProtocolSubprocess) { | ||
175 | 179 | SubprocessType::kRaw, SubprocessProtocol::kShell)); |
176 | 180 | |
177 | 181 | std::string stdout, stderr; |
178 | - EXPECT_EQ(24, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
182 | + EXPECT_EQ(24, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
179 | 183 | ExpectLinesEqual(stdout, {"foo", "baz"}); |
180 | 184 | ExpectLinesEqual(stderr, {"bar"}); |
181 | 185 | } |
@@ -189,7 +193,7 @@ TEST_F(ShellServiceTest, PtyShellProtocolSubprocess) { | ||
189 | 193 | // PTY always combines stdout and stderr but the shell protocol should |
190 | 194 | // still give us an exit code. |
191 | 195 | std::string stdout, stderr; |
192 | - EXPECT_EQ(50, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
196 | + EXPECT_EQ(50, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
193 | 197 | ExpectLinesEqual(stdout, {"foo", "bar", "baz"}); |
194 | 198 | ExpectLinesEqual(stderr, {}); |
195 | 199 | } |
@@ -204,7 +208,7 @@ TEST_F(ShellServiceTest, InteractivePtySubprocess) { | ||
204 | 208 | "echo --${TEST_STR}--", |
205 | 209 | "exit"}; |
206 | 210 | |
207 | - ShellProtocol* protocol = new ShellProtocol(subprocess_fd_); | |
211 | + ShellProtocol* protocol = new ShellProtocol(command_fd_); | |
208 | 212 | for (std::string command : commands) { |
209 | 213 | // Interactive shell requires a newline to complete each command. |
210 | 214 | command.push_back('\n'); |
@@ -214,7 +218,7 @@ TEST_F(ShellServiceTest, InteractivePtySubprocess) { | ||
214 | 218 | delete protocol; |
215 | 219 | |
216 | 220 | std::string stdout, stderr; |
217 | - EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
221 | + EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
218 | 222 | // An unpredictable command prompt makes parsing exact output difficult but |
219 | 223 | // it should at least contain echoed input and the expected output. |
220 | 224 | for (const char* command : commands) { |
@@ -230,14 +234,14 @@ TEST_F(ShellServiceTest, CloseClientStdin) { | ||
230 | 234 | SubprocessType::kRaw, SubprocessProtocol::kShell)); |
231 | 235 | |
232 | 236 | std::string input = "foo\nbar"; |
233 | - ShellProtocol* protocol = new ShellProtocol(subprocess_fd_); | |
237 | + ShellProtocol* protocol = new ShellProtocol(command_fd_); | |
234 | 238 | memcpy(protocol->data(), input.data(), input.length()); |
235 | 239 | ASSERT_TRUE(protocol->Write(ShellProtocol::kIdStdin, input.length())); |
236 | 240 | ASSERT_TRUE(protocol->Write(ShellProtocol::kIdCloseStdin, 0)); |
237 | 241 | delete protocol; |
238 | 242 | |
239 | 243 | std::string stdout, stderr; |
240 | - EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
244 | + EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
241 | 245 | ExpectLinesEqual(stdout, {"foo", "barTEST_DONE"}); |
242 | 246 | ExpectLinesEqual(stderr, {}); |
243 | 247 | } |
@@ -249,7 +253,7 @@ TEST_F(ShellServiceTest, CloseStdinStdoutSubprocess) { | ||
249 | 253 | SubprocessType::kRaw, SubprocessProtocol::kShell)); |
250 | 254 | |
251 | 255 | std::string stdout, stderr; |
252 | - EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
256 | + EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
253 | 257 | ExpectLinesEqual(stdout, {}); |
254 | 258 | ExpectLinesEqual(stderr, {"bar"}); |
255 | 259 | } |
@@ -261,7 +265,56 @@ TEST_F(ShellServiceTest, CloseStderrSubprocess) { | ||
261 | 265 | SubprocessType::kRaw, SubprocessProtocol::kShell)); |
262 | 266 | |
263 | 267 | std::string stdout, stderr; |
264 | - EXPECT_EQ(0, ReadShellProtocol(subprocess_fd_, &stdout, &stderr)); | |
268 | + EXPECT_EQ(0, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
265 | 269 | ExpectLinesEqual(stdout, {"foo"}); |
266 | 270 | ExpectLinesEqual(stderr, {}); |
267 | 271 | } |
272 | + | |
273 | +// Tests an inprocess command with no protocol. | |
274 | +TEST_F(ShellServiceTest, RawNoProtocolInprocess) { | |
275 | + ASSERT_NO_FATAL_FAILURE( | |
276 | + StartTestCommandInProcess("123", | |
277 | + [](auto args, auto in, auto out, auto err) -> int { | |
278 | + EXPECT_EQ("123", args); | |
279 | + char input[10]; | |
280 | + EXPECT_TRUE(ReadFdExactly(in, input, 2)); | |
281 | + input[2] = 0; | |
282 | + EXPECT_STREQ("in", input); | |
283 | + WriteFdExactly(out, "out\n"); | |
284 | + WriteFdExactly(err, "err\n"); | |
285 | + return 0; | |
286 | + }, | |
287 | + SubprocessProtocol::kNone)); | |
288 | + | |
289 | + WriteFdExactly(command_fd_, "in"); | |
290 | + ExpectLinesEqual(ReadRaw(command_fd_), {"out", "err"}); | |
291 | +} | |
292 | + | |
293 | +// Tests an inprocess command with the shell protocol. | |
294 | +TEST_F(ShellServiceTest, RawShellProtocolInprocess) { | |
295 | + ASSERT_NO_FATAL_FAILURE( | |
296 | + StartTestCommandInProcess("321", | |
297 | + [](auto args, auto in, auto out, auto err) -> int { | |
298 | + EXPECT_EQ("321", args); | |
299 | + char input[10]; | |
300 | + EXPECT_TRUE(ReadFdExactly(in, input, 2)); | |
301 | + input[2] = 0; | |
302 | + EXPECT_STREQ("in", input); | |
303 | + WriteFdExactly(out, "out\n"); | |
304 | + WriteFdExactly(err, "err\n"); | |
305 | + return 0; | |
306 | + }, | |
307 | + SubprocessProtocol::kShell)); | |
308 | + | |
309 | + { | |
310 | + auto write_protocol = std::make_unique<ShellProtocol>(command_fd_); | |
311 | + memcpy(write_protocol->data(), "in", 2); | |
312 | + write_protocol->Write(ShellProtocol::kIdStdin, 2); | |
313 | + } | |
314 | + | |
315 | + std::string stdout, stderr; | |
316 | + // For in-process commands the exit code is always the default (1). | |
317 | + EXPECT_EQ(1, ReadShellProtocol(command_fd_, &stdout, &stderr)); | |
318 | + ExpectLinesEqual(stdout, {"out"}); | |
319 | + ExpectLinesEqual(stderr, {"err"}); | |
320 | +} |