This source file includes following definitions.
- OnMessageReceived
- num_fds_received_
- GotExpectedNumberOfDescriptors
- OnChannelError
- HandleFD
- RunServer
- TEST_F
- SendFdsClientCommon
- MULTIPROCESS_IPC_TEST_CLIENT_MAIN
- TEST_F
- MULTIPROCESS_IPC_TEST_CLIENT_MAIN
- cb_
- HandleFD
- make_socket_pair
- null_cb
- null_listener_
- Init
- DestroyChannel
- Connect
- Send
- Producer
- ConsumerHandleFD
- CreateThread
- Run
- TEST_F
#include "build/build_config.h"
#if defined(OS_POSIX)
#if defined(OS_MACOSX)
extern "C" {
#include <sandbox.h>
}
#endif
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <unistd.h>
#include <queue>
#include "base/callback.h"
#include "base/file_descriptor_posix.h"
#include "base/message_loop/message_loop.h"
#include "base/pickle.h"
#include "base/posix/eintr_wrapper.h"
#include "base/synchronization/waitable_event.h"
#include "ipc/ipc_message_utils.h"
#include "ipc/ipc_test_base.h"
namespace {
const unsigned kNumFDsToSend = 20;
const char* kDevZeroPath = "/dev/zero";
class MyChannelDescriptorListenerBase : public IPC::Listener {
public:
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
PickleIterator iter(message);
base::FileDescriptor descriptor;
IPC::ParamTraits<base::FileDescriptor>::Read(&message, &iter, &descriptor);
HandleFD(descriptor.fd);
return true;
}
protected:
virtual void HandleFD(int fd) = 0;
};
class MyChannelDescriptorListener : public MyChannelDescriptorListenerBase {
public:
explicit MyChannelDescriptorListener(ino_t expected_inode_num)
: MyChannelDescriptorListenerBase(),
expected_inode_num_(expected_inode_num),
num_fds_received_(0) {
}
bool GotExpectedNumberOfDescriptors() const {
return num_fds_received_ == kNumFDsToSend;
}
virtual void OnChannelError() OVERRIDE {
base::MessageLoop::current()->Quit();
}
protected:
virtual void HandleFD(int fd) OVERRIDE {
char buf;
ssize_t amt_read = read(fd, &buf, 1);
ASSERT_EQ(amt_read, 1);
ASSERT_EQ(buf, 0);
struct stat st;
ASSERT_EQ(fstat(fd, &st), 0);
ASSERT_EQ(close(fd), 0);
ASSERT_EQ(expected_inode_num_, st.st_ino);
++num_fds_received_;
if (num_fds_received_ == kNumFDsToSend)
base::MessageLoop::current()->Quit();
}
private:
ino_t expected_inode_num_;
unsigned num_fds_received_;
};
class IPCSendFdsTest : public IPCTestBase {
protected:
void RunServer() {
MyChannelDescriptorListener listener(-1);
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
for (unsigned i = 0; i < kNumFDsToSend; ++i) {
const int fd = open(kDevZeroPath, O_RDONLY);
ASSERT_GE(fd, 0);
base::FileDescriptor descriptor(fd, true);
IPC::Message* message =
new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
ASSERT_TRUE(sender()->Send(message));
}
base::MessageLoop::current()->Run();
channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
};
TEST_F(IPCSendFdsTest, DescriptorTest) {
Init("SendFdsClient");
RunServer();
}
int SendFdsClientCommon(const std::string& test_client_name,
ino_t expected_inode_num) {
base::MessageLoopForIO main_message_loop;
MyChannelDescriptorListener listener(expected_inode_num);
IPC::Channel channel(IPCTestBase::GetChannelName(test_client_name),
IPC::Channel::MODE_CLIENT,
&listener);
CHECK(channel.Connect());
base::MessageLoop::current()->Run();
CHECK(listener.GotExpectedNumberOfDescriptors());
return 0;
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsClient) {
struct stat st;
int fd = open(kDevZeroPath, O_RDONLY);
fstat(fd, &st);
EXPECT_GE(IGNORE_EINTR(close(fd)), 0);
return SendFdsClientCommon("SendFdsClient", st.st_ino);
}
#if defined(OS_MACOSX)
TEST_F(IPCSendFdsTest, DescriptorTestSandboxed) {
Init("SendFdsSandboxedClient");
RunServer();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(SendFdsSandboxedClient) {
struct stat st;
const int fd = open(kDevZeroPath, O_RDONLY);
fstat(fd, &st);
if (IGNORE_EINTR(close(fd)) < 0)
return -1;
char* error_buff = NULL;
int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED,
&error_buff);
bool success = (error == 0 && error_buff == NULL);
if (!success)
return -1;
sandbox_free_error(error_buff);
if (open(kDevZeroPath, O_RDONLY) != -1) {
LOG(ERROR) << "Sandbox wasn't properly enabled";
return -1;
}
return SendFdsClientCommon("SendFdsSandboxedClient", st.st_ino);
}
#endif
class MyCBListener : public MyChannelDescriptorListenerBase {
public:
MyCBListener(base::Callback<void(int)> cb, int fds_to_send)
: MyChannelDescriptorListenerBase(),
cb_(cb) {
}
protected:
virtual void HandleFD(int fd) OVERRIDE {
cb_.Run(fd);
}
private:
base::Callback<void(int)> cb_;
};
std::pair<int, int> make_socket_pair() {
int pipe_fds[2];
CHECK_EQ(0, HANDLE_EINTR(socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds)));
return std::pair<int, int>(pipe_fds[0], pipe_fds[1]);
}
static void null_cb(int unused_fd) {
NOTREACHED();
}
class PipeChannelHelper {
public:
PipeChannelHelper(base::Thread* in_thread,
base::Thread* out_thread,
base::Callback<void(int)> cb,
int fds_to_send) :
in_thread_(in_thread),
out_thread_(out_thread),
cb_listener_(cb, fds_to_send),
null_listener_(base::Bind(&null_cb), 0) {
}
void Init() {
IPC::ChannelHandle in_handle("IN");
in.reset(new IPC::Channel(in_handle,
IPC::Channel::MODE_SERVER,
&null_listener_));
base::FileDescriptor out_fd(in->TakeClientFileDescriptor(), false);
IPC::ChannelHandle out_handle("OUT", out_fd);
out.reset(new IPC::Channel(out_handle,
IPC::Channel::MODE_CLIENT,
&cb_listener_));
in_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&PipeChannelHelper::Connect, in.get()));
out_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&PipeChannelHelper::Connect, out.get()));
}
static void DestroyChannel(scoped_ptr<IPC::Channel> *c,
base::WaitableEvent *event) {
c->reset(0);
event->Signal();
}
~PipeChannelHelper() {
base::WaitableEvent a(true, false);
base::WaitableEvent b(true, false);
in_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&PipeChannelHelper::DestroyChannel, &in, &a));
out_thread_->message_loop()->PostTask(
FROM_HERE,
base::Bind(&PipeChannelHelper::DestroyChannel, &out, &b));
a.Wait();
b.Wait();
}
static void Connect(IPC::Channel *channel) {
EXPECT_TRUE(channel->Connect());
}
void Send(int fd) {
CHECK_EQ(base::MessageLoop::current(), in_thread_->message_loop());
ASSERT_GE(fd, 0);
base::FileDescriptor descriptor(fd, true);
IPC::Message* message =
new IPC::Message(0, 3, IPC::Message::PRIORITY_NORMAL);
IPC::ParamTraits<base::FileDescriptor>::Write(message, descriptor);
ASSERT_TRUE(in->Send(message));
}
private:
scoped_ptr<IPC::Channel> in, out;
base::Thread* in_thread_;
base::Thread* out_thread_;
MyCBListener cb_listener_;
MyCBListener null_listener_;
};
class IPCMultiSendingFdsTest : public testing::Test {
public:
IPCMultiSendingFdsTest() : received_(true, false) {}
void Producer(PipeChannelHelper* dest,
base::Thread* t,
int pipes_to_send) {
for (int i = 0; i < pipes_to_send; i++) {
received_.Reset();
std::pair<int, int> pipe_fds = make_socket_pair();
t->message_loop()->PostTask(
FROM_HERE,
base::Bind(&PipeChannelHelper::Send,
base::Unretained(dest),
pipe_fds.second));
char tmp = 'x';
CHECK_EQ(1, HANDLE_EINTR(write(pipe_fds.first, &tmp, 1)));
CHECK_EQ(0, IGNORE_EINTR(close(pipe_fds.first)));
received_.Wait();
}
}
void ConsumerHandleFD(int fd) {
char tmp = 'y';
CHECK_EQ(1, HANDLE_EINTR(read(fd, &tmp, 1)));
CHECK_EQ(tmp, 'x');
CHECK_EQ(0, IGNORE_EINTR(close(fd)));
received_.Signal();
}
base::Thread* CreateThread(const char* name) {
base::Thread* ret = new base::Thread(name);
base::Thread::Options options;
options.message_loop_type = base::MessageLoop::TYPE_IO;
ret->StartWithOptions(options);
return ret;
}
void Run() {
const int pipes_to_send = 20000;
scoped_ptr<base::Thread> producer(CreateThread("producer"));
scoped_ptr<base::Thread> middleman(CreateThread("middleman"));
scoped_ptr<base::Thread> consumer(CreateThread("consumer"));
PipeChannelHelper pipe1(
middleman.get(),
consumer.get(),
base::Bind(&IPCMultiSendingFdsTest::ConsumerHandleFD,
base::Unretained(this)),
pipes_to_send);
PipeChannelHelper pipe2(
producer.get(),
middleman.get(),
base::Bind(&PipeChannelHelper::Send, base::Unretained(&pipe1)),
pipes_to_send);
pipe1.Init();
pipe2.Init();
Producer(&pipe2, producer.get(), pipes_to_send);
}
private:
base::WaitableEvent received_;
};
TEST_F(IPCMultiSendingFdsTest, StressTest) {
Run();
}
}
#endif