• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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