1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "mojo/embedder/platform_channel_pair.h"
6
7 #include <errno.h>
8 #include <poll.h>
9 #include <signal.h>
10 #include <stdio.h>
11 #include <sys/socket.h>
12 #include <sys/types.h>
13 #include <sys/uio.h>
14 #include <unistd.h>
15
16 #include <deque>
17
18 #include "base/file_util.h"
19 #include "base/files/file_path.h"
20 #include "base/files/scoped_file.h"
21 #include "base/logging.h"
22 #include "base/macros.h"
23 #include "mojo/common/test/test_utils.h"
24 #include "mojo/embedder/platform_channel_utils_posix.h"
25 #include "mojo/embedder/platform_handle.h"
26 #include "mojo/embedder/platform_handle_vector.h"
27 #include "mojo/embedder/scoped_platform_handle.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29
30 namespace mojo {
31 namespace embedder {
32 namespace {
33
WaitReadable(PlatformHandle h)34 void WaitReadable(PlatformHandle h) {
35 struct pollfd pfds = {};
36 pfds.fd = h.fd;
37 pfds.events = POLLIN;
38 CHECK_EQ(poll(&pfds, 1, -1), 1);
39 }
40
41 class PlatformChannelPairPosixTest : public testing::Test {
42 public:
PlatformChannelPairPosixTest()43 PlatformChannelPairPosixTest() {}
~PlatformChannelPairPosixTest()44 virtual ~PlatformChannelPairPosixTest() {}
45
SetUp()46 virtual void SetUp() OVERRIDE {
47 // Make sure |SIGPIPE| isn't being ignored.
48 struct sigaction action = {};
49 action.sa_handler = SIG_DFL;
50 ASSERT_EQ(0, sigaction(SIGPIPE, &action, &old_action_));
51 }
52
TearDown()53 virtual void TearDown() OVERRIDE {
54 // Restore the |SIGPIPE| handler.
55 ASSERT_EQ(0, sigaction(SIGPIPE, &old_action_, NULL));
56 }
57
58 private:
59 struct sigaction old_action_;
60
61 DISALLOW_COPY_AND_ASSIGN(PlatformChannelPairPosixTest);
62 };
63
TEST_F(PlatformChannelPairPosixTest,NoSigPipe)64 TEST_F(PlatformChannelPairPosixTest, NoSigPipe) {
65 PlatformChannelPair channel_pair;
66 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
67 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
68
69 // Write to the client.
70 static const char kHello[] = "hello";
71 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
72 write(client_handle.get().fd, kHello, sizeof(kHello)));
73
74 // Close the client.
75 client_handle.reset();
76
77 // Read from the server; this should be okay.
78 char buffer[100] = {};
79 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
80 read(server_handle.get().fd, buffer, sizeof(buffer)));
81 EXPECT_STREQ(kHello, buffer);
82
83 // Try reading again.
84 ssize_t result = read(server_handle.get().fd, buffer, sizeof(buffer));
85 // We should probably get zero (for "end of file"), but -1 would also be okay.
86 EXPECT_TRUE(result == 0 || result == -1);
87 if (result == -1)
88 PLOG(WARNING) << "read (expected 0 for EOF)";
89
90 // Test our replacement for |write()|/|send()|.
91 result = PlatformChannelWrite(server_handle.get(), kHello, sizeof(kHello));
92 EXPECT_EQ(-1, result);
93 if (errno != EPIPE)
94 PLOG(WARNING) << "write (expected EPIPE)";
95
96 // Test our replacement for |writev()|/|sendv()|.
97 struct iovec iov[2] = {
98 { const_cast<char*>(kHello), sizeof(kHello) },
99 { const_cast<char*>(kHello), sizeof(kHello) }
100 };
101 result = PlatformChannelWritev(server_handle.get(), iov, 2);
102 EXPECT_EQ(-1, result);
103 if (errno != EPIPE)
104 PLOG(WARNING) << "write (expected EPIPE)";
105 }
106
TEST_F(PlatformChannelPairPosixTest,SendReceiveData)107 TEST_F(PlatformChannelPairPosixTest, SendReceiveData) {
108 PlatformChannelPair channel_pair;
109 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
110 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
111
112 for (size_t i = 0; i < 10; i++) {
113 std::string send_string(1 << i, 'A' + i);
114
115 EXPECT_EQ(static_cast<ssize_t>(send_string.size()),
116 PlatformChannelWrite(server_handle.get(), send_string.data(),
117 send_string.size()));
118
119 WaitReadable(client_handle.get());
120
121 char buf[10000] = {};
122 std::deque<PlatformHandle> received_handles;
123 ssize_t result = PlatformChannelRecvmsg(client_handle.get(), buf,
124 sizeof(buf), &received_handles);
125 EXPECT_EQ(static_cast<ssize_t>(send_string.size()), result);
126 EXPECT_EQ(send_string, std::string(buf, static_cast<size_t>(result)));
127 EXPECT_TRUE(received_handles.empty());
128 }
129 }
130
TEST_F(PlatformChannelPairPosixTest,SendReceiveFDs)131 TEST_F(PlatformChannelPairPosixTest, SendReceiveFDs) {
132 static const char kHello[] = "hello";
133
134 PlatformChannelPair channel_pair;
135 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
136 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
137
138 for (size_t i = 1; i < kPlatformChannelMaxNumHandles; i++) {
139 // Make |i| files, with the j-th file consisting of j copies of the digit i.
140 PlatformHandleVector platform_handles;
141 for (size_t j = 1; j <= i; j++) {
142 base::FilePath ignored;
143 base::ScopedFILE fp(base::CreateAndOpenTemporaryFile(&ignored));
144 ASSERT_TRUE(fp);
145 fwrite(std::string(j, '0' + i).data(), 1, j, fp.get());
146 platform_handles.push_back(
147 test::PlatformHandleFromFILE(fp.Pass()).release());
148 ASSERT_TRUE(platform_handles.back().is_valid());
149 }
150
151 // Send the FDs (+ "hello").
152 struct iovec iov = { const_cast<char*>(kHello), sizeof(kHello) };
153 // We assume that the |sendmsg()| actually sends all the data.
154 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
155 PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
156 &platform_handles[0],
157 platform_handles.size()));
158
159 WaitReadable(client_handle.get());
160
161 char buf[100] = {};
162 std::deque<PlatformHandle> received_handles;
163 // We assume that the |recvmsg()| actually reads all the data.
164 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
165 PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
166 &received_handles));
167 EXPECT_STREQ(kHello, buf);
168 EXPECT_EQ(i, received_handles.size());
169
170 for (size_t j = 0; !received_handles.empty(); j++) {
171 base::ScopedFILE fp(test::FILEFromPlatformHandle(
172 ScopedPlatformHandle(received_handles.front()), "rb"));
173 received_handles.pop_front();
174 ASSERT_TRUE(fp);
175 rewind(fp.get());
176 char read_buf[100];
177 size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
178 EXPECT_EQ(j + 1, bytes_read);
179 EXPECT_EQ(std::string(j + 1, '0' + i), std::string(read_buf, bytes_read));
180 }
181 }
182 }
183
TEST_F(PlatformChannelPairPosixTest,AppendReceivedFDs)184 TEST_F(PlatformChannelPairPosixTest, AppendReceivedFDs) {
185 static const char kHello[] = "hello";
186
187 PlatformChannelPair channel_pair;
188 ScopedPlatformHandle server_handle = channel_pair.PassServerHandle().Pass();
189 ScopedPlatformHandle client_handle = channel_pair.PassClientHandle().Pass();
190
191 const std::string file_contents("hello world");
192
193 {
194 base::FilePath ignored;
195 base::ScopedFILE fp(base::CreateAndOpenTemporaryFile(&ignored));
196 ASSERT_TRUE(fp);
197 fwrite(file_contents.data(), 1, file_contents.size(), fp.get());
198 PlatformHandleVector platform_handles;
199 platform_handles.push_back(
200 test::PlatformHandleFromFILE(fp.Pass()).release());
201 ASSERT_TRUE(platform_handles.back().is_valid());
202
203 // Send the FD (+ "hello").
204 struct iovec iov = { const_cast<char*>(kHello), sizeof(kHello) };
205 // We assume that the |sendmsg()| actually sends all the data.
206 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
207 PlatformChannelSendmsgWithHandles(server_handle.get(), &iov, 1,
208 &platform_handles[0],
209 platform_handles.size()));
210 }
211
212 WaitReadable(client_handle.get());
213
214 // Start with an invalid handle in the deque.
215 std::deque<PlatformHandle> received_handles;
216 received_handles.push_back(PlatformHandle());
217
218 char buf[100] = {};
219 // We assume that the |recvmsg()| actually reads all the data.
220 EXPECT_EQ(static_cast<ssize_t>(sizeof(kHello)),
221 PlatformChannelRecvmsg(client_handle.get(), buf, sizeof(buf),
222 &received_handles));
223 EXPECT_STREQ(kHello, buf);
224 ASSERT_EQ(2u, received_handles.size());
225 EXPECT_FALSE(received_handles[0].is_valid());
226 EXPECT_TRUE(received_handles[1].is_valid());
227
228 {
229 base::ScopedFILE fp(test::FILEFromPlatformHandle(
230 ScopedPlatformHandle(received_handles[1]), "rb"));
231 received_handles[1] = PlatformHandle();
232 ASSERT_TRUE(fp);
233 rewind(fp.get());
234 char read_buf[100];
235 size_t bytes_read = fread(read_buf, 1, sizeof(read_buf), fp.get());
236 EXPECT_EQ(file_contents.size(), bytes_read);
237 EXPECT_EQ(file_contents, std::string(read_buf, bytes_read));
238 }
239 }
240
241 } // namespace
242 } // namespace embedder
243 } // namespace mojo
244