This source file includes following definitions.
- quit_only_on_message_
- OnMessageReceived
- OnChannelConnected
- OnChannelError
- OnChannelDenied
- OnChannelListenError
- status
- QuitRunLoop
- GetChannelDirName
- GetConnectionSocketName
- SetUp
- TearDown
- SetUpSocket
- SpinRunLoop
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- TEST_F
- MULTIPROCESS_TEST_MAIN
- MULTIPROCESS_TEST_MAIN
#include "ipc/ipc_channel_posix.h"
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include "base/basictypes.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/kill.h"
#include "base/test/multiprocess_test.h"
#include "base/test/test_timeouts.h"
#include "ipc/ipc_listener.h"
#include "ipc/unix_domain_socket_util.h"
#include "testing/multiprocess_func_list.h"
namespace {
static const uint32 kQuitMessage = 47;
class IPCChannelPosixTestListener : public IPC::Listener {
public:
enum STATUS {
DISCONNECTED,
MESSAGE_RECEIVED,
CHANNEL_ERROR,
CONNECTED,
DENIED,
LISTEN_ERROR
};
IPCChannelPosixTestListener(bool quit_only_on_message)
: status_(DISCONNECTED),
quit_only_on_message_(quit_only_on_message) {
}
virtual ~IPCChannelPosixTestListener() {}
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
EXPECT_EQ(message.type(), kQuitMessage);
status_ = MESSAGE_RECEIVED;
QuitRunLoop();
return true;
}
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
status_ = CONNECTED;
if (!quit_only_on_message_) {
QuitRunLoop();
}
}
virtual void OnChannelError() OVERRIDE {
status_ = CHANNEL_ERROR;
QuitRunLoop();
}
virtual void OnChannelDenied() OVERRIDE {
status_ = DENIED;
if (!quit_only_on_message_) {
QuitRunLoop();
}
}
virtual void OnChannelListenError() OVERRIDE {
status_ = LISTEN_ERROR;
if (!quit_only_on_message_) {
QuitRunLoop();
}
}
STATUS status() { return status_; }
void QuitRunLoop() {
base::MessageLoopForIO* loop = base::MessageLoopForIO::current();
if (loop->is_running()) {
loop->QuitNow();
} else {
loop->PostTask(FROM_HERE, loop->QuitClosure());
}
}
private:
STATUS status_;
bool quit_only_on_message_;
};
class IPCChannelPosixTest : public base::MultiProcessTest {
public:
static void SetUpSocket(IPC::ChannelHandle *handle,
IPC::Channel::Mode mode);
static void SpinRunLoop(base::TimeDelta delay);
static const std::string GetConnectionSocketName();
static const std::string GetChannelDirName();
protected:
virtual void SetUp();
virtual void TearDown();
private:
scoped_ptr<base::MessageLoopForIO> message_loop_;
};
const std::string IPCChannelPosixTest::GetChannelDirName() {
#if defined(OS_ANDROID)
base::FilePath tmp_dir;
PathService::Get(base::DIR_CACHE, &tmp_dir);
return tmp_dir.value();
#else
return "/var/tmp";
#endif
}
const std::string IPCChannelPosixTest::GetConnectionSocketName() {
return GetChannelDirName() + "/chrome_IPCChannelPosixTest__ConnectionSocket";
}
void IPCChannelPosixTest::SetUp() {
MultiProcessTest::SetUp();
message_loop_.reset(new base::MessageLoopForIO());
}
void IPCChannelPosixTest::TearDown() {
message_loop_.reset(NULL);
MultiProcessTest::TearDown();
}
void IPCChannelPosixTest::SetUpSocket(IPC::ChannelHandle *handle,
IPC::Channel::Mode mode) {
const std::string& name = handle->name;
int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0);
ASSERT_GE(socket_fd, 0) << name;
ASSERT_GE(fcntl(socket_fd, F_SETFL, O_NONBLOCK), 0);
struct sockaddr_un server_address = { 0 };
memset(&server_address, 0, sizeof(server_address));
server_address.sun_family = AF_UNIX;
int path_len = snprintf(server_address.sun_path, IPC::kMaxSocketNameLength,
"%s", name.c_str());
DCHECK_EQ(static_cast<int>(name.length()), path_len);
size_t server_address_len = offsetof(struct sockaddr_un,
sun_path) + path_len + 1;
if (mode == IPC::Channel::MODE_NAMED_SERVER) {
unlink(name.c_str());
base::FilePath path(name);
base::FilePath dir_path = path.DirName();
ASSERT_TRUE(base::CreateDirectory(dir_path));
ASSERT_GE(bind(socket_fd,
reinterpret_cast<struct sockaddr *>(&server_address),
server_address_len), 0) << server_address.sun_path
<< ": " << strerror(errno)
<< "(" << errno << ")";
ASSERT_GE(listen(socket_fd, SOMAXCONN), 0) << server_address.sun_path
<< ": " << strerror(errno)
<< "(" << errno << ")";
} else if (mode == IPC::Channel::MODE_NAMED_CLIENT) {
ASSERT_GE(connect(socket_fd,
reinterpret_cast<struct sockaddr *>(&server_address),
server_address_len), 0) << server_address.sun_path
<< ": " << strerror(errno)
<< "(" << errno << ")";
} else {
FAIL() << "Unknown mode " << mode;
}
handle->socket.fd = socket_fd;
}
void IPCChannelPosixTest::SpinRunLoop(base::TimeDelta delay) {
base::MessageLoopForIO* loop = base::MessageLoopForIO::current();
loop->PostDelayedTask(FROM_HERE, loop->QuitClosure(), delay);
loop->Run();
}
TEST_F(IPCChannelPosixTest, BasicListen) {
const std::string kChannelName =
GetChannelDirName() + "/IPCChannelPosixTest_BasicListen";
IPC::ChannelHandle handle(kChannelName);
SetUpSocket(&handle, IPC::Channel::MODE_NAMED_SERVER);
unlink(handle.name.c_str());
IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
ASSERT_TRUE(channel.Connect());
ASSERT_TRUE(channel.AcceptsConnections());
ASSERT_FALSE(channel.HasAcceptedConnection());
channel.ResetToAcceptingConnectionState();
ASSERT_FALSE(channel.HasAcceptedConnection());
}
TEST_F(IPCChannelPosixTest, BasicConnected) {
int pipe_fds[2];
ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds));
std::string socket_name("/var/tmp/IPCChannelPosixTest_BasicConnected");
ASSERT_GE(fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK), 0);
base::FileDescriptor fd(pipe_fds[0], false);
IPC::ChannelHandle handle(socket_name, fd);
IPC::Channel channel(handle, IPC::Channel::MODE_SERVER, NULL);
ASSERT_TRUE(channel.Connect());
ASSERT_FALSE(channel.AcceptsConnections());
channel.Close();
ASSERT_TRUE(IGNORE_EINTR(close(pipe_fds[1])) == 0);
IPC::Channel channel2(socket_name, IPC::Channel::MODE_SERVER, NULL);
ASSERT_TRUE(channel2.Connect());
ASSERT_FALSE(channel2.AcceptsConnections());
}
TEST_F(IPCChannelPosixTest, SendHangTest) {
IPCChannelPosixTestListener out_listener(true);
IPCChannelPosixTestListener in_listener(true);
IPC::ChannelHandle in_handle("IN");
IPC::Channel in_chan(in_handle, IPC::Channel::MODE_SERVER, &in_listener);
base::FileDescriptor out_fd(in_chan.TakeClientFileDescriptor(), false);
IPC::ChannelHandle out_handle("OUT", out_fd);
IPC::Channel out_chan(out_handle, IPC::Channel::MODE_CLIENT, &out_listener);
ASSERT_TRUE(in_chan.Connect());
ASSERT_TRUE(out_chan.Connect());
in_chan.Close();
ASSERT_FALSE(out_chan.Send(new IPC::Message(
0,
kQuitMessage,
IPC::Message::PRIORITY_NORMAL)));
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, out_listener.status());
}
TEST_F(IPCChannelPosixTest, AcceptHangTest) {
IPCChannelPosixTestListener out_listener(true);
IPCChannelPosixTestListener in_listener(true);
IPC::ChannelHandle in_handle("IN");
IPC::Channel in_chan(in_handle, IPC::Channel::MODE_SERVER, &in_listener);
base::FileDescriptor out_fd(in_chan.TakeClientFileDescriptor(), false);
IPC::ChannelHandle out_handle("OUT", out_fd);
IPC::Channel out_chan(out_handle, IPC::Channel::MODE_CLIENT, &out_listener);
ASSERT_TRUE(in_chan.Connect());
in_chan.Close();
ASSERT_FALSE(out_chan.Connect());
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, out_listener.status());
}
TEST_F(IPCChannelPosixTest, AdvancedConnected) {
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle chan_handle(GetConnectionSocketName());
SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
ASSERT_TRUE(channel.Connect());
ASSERT_TRUE(channel.AcceptsConnections());
ASSERT_FALSE(channel.HasAcceptedConnection());
base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc");
ASSERT_TRUE(handle);
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
ASSERT_TRUE(channel.HasAcceptedConnection());
IPC::Message* message = new IPC::Message(0,
kQuitMessage,
IPC::Message::PRIORITY_NORMAL);
channel.Send(message);
SpinRunLoop(TestTimeouts::action_timeout());
int exit_code = 0;
EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
EXPECT_EQ(0, exit_code);
ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
ASSERT_FALSE(channel.HasAcceptedConnection());
}
TEST_F(IPCChannelPosixTest, ResetState) {
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle chan_handle(GetConnectionSocketName());
SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
ASSERT_TRUE(channel.Connect());
ASSERT_TRUE(channel.AcceptsConnections());
ASSERT_FALSE(channel.HasAcceptedConnection());
base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc");
ASSERT_TRUE(handle);
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
ASSERT_TRUE(channel.HasAcceptedConnection());
channel.ResetToAcceptingConnectionState();
ASSERT_FALSE(channel.HasAcceptedConnection());
base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixTestConnectionProc");
ASSERT_TRUE(handle2);
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
ASSERT_TRUE(channel.HasAcceptedConnection());
IPC::Message* message = new IPC::Message(0,
kQuitMessage,
IPC::Message::PRIORITY_NORMAL);
channel.Send(message);
SpinRunLoop(TestTimeouts::action_timeout());
EXPECT_TRUE(base::KillProcess(handle, 0, false));
int exit_code = 0;
EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
EXPECT_EQ(0, exit_code);
ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
ASSERT_FALSE(channel.HasAcceptedConnection());
}
TEST_F(IPCChannelPosixTest, BadChannelName) {
IPC::ChannelHandle handle("");
IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_SERVER, NULL);
ASSERT_FALSE(channel.Connect());
const char *kTooLongName = "This_is_a_very_long_name_to_proactively_implement"
"client-centered_synergy_through_top-line"
"platforms_Phosfluorescently_disintermediate_"
"clicks-and-mortar_best_practices_without_"
"future-proof_growth_strategies_Continually"
"pontificate_proactive_potentialities_before"
"leading-edge_processes";
EXPECT_GE(strlen(kTooLongName), IPC::kMaxSocketNameLength);
IPC::ChannelHandle handle2(kTooLongName);
IPC::Channel channel2(handle2, IPC::Channel::MODE_NAMED_SERVER, NULL);
EXPECT_FALSE(channel2.Connect());
}
TEST_F(IPCChannelPosixTest, MultiConnection) {
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle chan_handle(GetConnectionSocketName());
SetUpSocket(&chan_handle, IPC::Channel::MODE_NAMED_SERVER);
IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
ASSERT_TRUE(channel.Connect());
ASSERT_TRUE(channel.AcceptsConnections());
ASSERT_FALSE(channel.HasAcceptedConnection());
base::ProcessHandle handle = SpawnChild("IPCChannelPosixTestConnectionProc");
ASSERT_TRUE(handle);
SpinRunLoop(TestTimeouts::action_max_timeout());
ASSERT_EQ(IPCChannelPosixTestListener::CONNECTED, listener.status());
ASSERT_TRUE(channel.HasAcceptedConnection());
base::ProcessHandle handle2 = SpawnChild("IPCChannelPosixFailConnectionProc");
ASSERT_TRUE(handle2);
SpinRunLoop(TestTimeouts::action_max_timeout());
int exit_code = 0;
EXPECT_TRUE(base::WaitForExitCode(handle2, &exit_code));
EXPECT_EQ(exit_code, 0);
ASSERT_EQ(IPCChannelPosixTestListener::DENIED, listener.status());
ASSERT_TRUE(channel.HasAcceptedConnection());
IPC::Message* message = new IPC::Message(0,
kQuitMessage,
IPC::Message::PRIORITY_NORMAL);
channel.Send(message);
SpinRunLoop(TestTimeouts::action_timeout());
EXPECT_TRUE(base::WaitForExitCode(handle, &exit_code));
EXPECT_EQ(exit_code, 0);
ASSERT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
ASSERT_FALSE(channel.HasAcceptedConnection());
}
TEST_F(IPCChannelPosixTest, DoubleServer) {
IPCChannelPosixTestListener listener(false);
IPCChannelPosixTestListener listener2(false);
IPC::ChannelHandle chan_handle(GetConnectionSocketName());
IPC::Channel channel(chan_handle, IPC::Channel::MODE_SERVER, &listener);
IPC::Channel channel2(chan_handle, IPC::Channel::MODE_SERVER, &listener2);
ASSERT_TRUE(channel.Connect());
ASSERT_FALSE(channel2.Connect());
}
TEST_F(IPCChannelPosixTest, BadMode) {
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle chan_handle(GetConnectionSocketName());
IPC::Channel channel(chan_handle, IPC::Channel::MODE_NONE, &listener);
ASSERT_FALSE(channel.Connect());
}
TEST_F(IPCChannelPosixTest, IsNamedServerInitialized) {
const std::string& connection_socket_name = GetConnectionSocketName();
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle chan_handle(connection_socket_name);
ASSERT_TRUE(base::DeleteFile(base::FilePath(connection_socket_name), false));
ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
connection_socket_name));
IPC::Channel channel(chan_handle, IPC::Channel::MODE_NAMED_SERVER, &listener);
ASSERT_TRUE(IPC::Channel::IsNamedServerInitialized(
connection_socket_name));
channel.Close();
ASSERT_FALSE(IPC::Channel::IsNamedServerInitialized(
connection_socket_name));
}
MULTIPROCESS_TEST_MAIN(IPCChannelPosixTestConnectionProc) {
base::MessageLoopForIO message_loop;
IPCChannelPosixTestListener listener(true);
IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName());
IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
EXPECT_TRUE(channel.Connect());
IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout());
EXPECT_EQ(IPCChannelPosixTestListener::MESSAGE_RECEIVED, listener.status());
return 0;
}
MULTIPROCESS_TEST_MAIN(IPCChannelPosixFailConnectionProc) {
base::MessageLoopForIO message_loop;
IPCChannelPosixTestListener listener(false);
IPC::ChannelHandle handle(IPCChannelPosixTest::GetConnectionSocketName());
IPCChannelPosixTest::SetUpSocket(&handle, IPC::Channel::MODE_NAMED_CLIENT);
IPC::Channel channel(handle, IPC::Channel::MODE_NAMED_CLIENT, &listener);
bool connected = channel.Connect();
if (connected) {
IPCChannelPosixTest::SpinRunLoop(TestTimeouts::action_max_timeout());
EXPECT_EQ(IPCChannelPosixTestListener::CHANNEL_ERROR, listener.status());
} else {
EXPECT_EQ(IPCChannelPosixTestListener::DISCONNECTED, listener.status());
}
return 0;
}
}