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
30namespace mojo {
31namespace embedder {
32namespace {
33
34void 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
41class PlatformChannelPairPosixTest : public testing::Test {
42 public:
43  PlatformChannelPairPosixTest() {}
44  virtual ~PlatformChannelPairPosixTest() {}
45
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
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
64TEST_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
107TEST_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
131TEST_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
184TEST_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