This source file includes following definitions.
- GetInstance
- Lookup
- Remove
- Insert
- SocketWriteErrorIsRecoverable
- NotifyProcessForkedForTesting
- must_unlink_
- SocketPair
- CreatePipe
- Connect
- CloseFileDescriptors
- ProcessOutgoingMessages
- Send
- GetClientFileDescriptor
- TakeClientFileDescriptor
- CloseClientFileDescriptor
- AcceptsConnections
- HasAcceptedConnection
- GetPeerEuid
- ResetToAcceptingConnectionState
- IsNamedServerInitialized
- SetGlobalPid
- OnFileCanReadWithoutBlocking
- OnFileCanWriteWithoutBlocking
- AcceptConnection
- ClosePipeOnError
- GetHelloMessageProcId
- QueueHelloMessage
- ReadData
- ReadFileDescriptorsFromFDPipe
- WillDispatchInputMessage
- DidEmptyInputBuffers
- ExtractFileDescriptorsFromMsghdr
- ClearInputFDs
- QueueCloseFDMessage
- HandleInternalMessage
- Close
- Connect
- Close
- peer_pid
- Send
- GetClientFileDescriptor
- TakeClientFileDescriptor
- AcceptsConnections
- HasAcceptedConnection
- GetPeerEuid
- ResetToAcceptingConnectionState
- IsNamedServerInitialized
- GenerateVerifiedChannelID
- SetGlobalPid
#include "ipc/ipc_channel_posix.h"
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#if defined(OS_OPENBSD)
#include <sys/uio.h>
#endif
#include <map>
#include <string>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/singleton.h"
#include "base/posix/eintr_wrapper.h"
#include "base/posix/global_descriptors.h"
#include "base/process/process_handle.h"
#include "base/rand_util.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/synchronization/lock.h"
#include "ipc/file_descriptor_set_posix.h"
#include "ipc/ipc_descriptors.h"
#include "ipc/ipc_listener.h"
#include "ipc/ipc_logging.h"
#include "ipc/ipc_message_utils.h"
#include "ipc/ipc_switches.h"
#include "ipc/unix_domain_socket_util.h"
namespace IPC {
namespace {
class PipeMap {
public:
static PipeMap* GetInstance() {
return Singleton<PipeMap>::get();
}
~PipeMap() {
DCHECK(map_.empty());
}
int Lookup(const std::string& channel_id) {
base::AutoLock locked(lock_);
ChannelToFDMap::const_iterator i = map_.find(channel_id);
if (i == map_.end())
return -1;
return i->second;
}
void Remove(const std::string& channel_id) {
base::AutoLock locked(lock_);
map_.erase(channel_id);
}
void Insert(const std::string& channel_id, int fd) {
base::AutoLock locked(lock_);
DCHECK_NE(-1, fd);
ChannelToFDMap::const_iterator i = map_.find(channel_id);
CHECK(i == map_.end()) << "Creating second IPC server (fd " << fd << ") "
<< "for '" << channel_id << "' while first "
<< "(fd " << i->second << ") still exists";
map_[channel_id] = fd;
}
private:
base::Lock lock_;
typedef std::map<std::string, int> ChannelToFDMap;
ChannelToFDMap map_;
friend struct DefaultSingletonTraits<PipeMap>;
#if defined(OS_ANDROID)
friend void ::IPC::Channel::NotifyProcessForkedForTesting();
#endif
};
bool SocketWriteErrorIsRecoverable() {
#if defined(OS_MACOSX)
return errno == EAGAIN || errno == EMSGSIZE;
#else
return errno == EAGAIN;
#endif
}
}
#if defined(OS_ANDROID)
void Channel::NotifyProcessForkedForTesting() {
PipeMap::GetInstance()->map_.clear();
}
#endif
#if defined(OS_LINUX)
int Channel::ChannelImpl::global_pid_ = 0;
#endif
Channel::ChannelImpl::ChannelImpl(const IPC::ChannelHandle& channel_handle,
Mode mode, Listener* listener)
: ChannelReader(listener),
mode_(mode),
peer_pid_(base::kNullProcessId),
is_blocked_on_write_(false),
waiting_connect_(true),
message_send_bytes_written_(0),
server_listen_pipe_(-1),
pipe_(-1),
client_pipe_(-1),
#if defined(IPC_USES_READWRITE)
fd_pipe_(-1),
remote_fd_pipe_(-1),
#endif
pipe_name_(channel_handle.name),
must_unlink_(false) {
memset(input_cmsg_buf_, 0, sizeof(input_cmsg_buf_));
if (!CreatePipe(channel_handle)) {
const char *modestr = (mode_ & MODE_SERVER_FLAG) ? "server" : "client";
LOG(WARNING) << "Unable to create pipe named \"" << channel_handle.name
<< "\" in " << modestr << " mode";
}
}
Channel::ChannelImpl::~ChannelImpl() {
Close();
}
bool SocketPair(int* fd1, int* fd2) {
int pipe_fds[2];
if (socketpair(AF_UNIX, SOCK_STREAM, 0, pipe_fds) != 0) {
PLOG(ERROR) << "socketpair()";
return false;
}
if (fcntl(pipe_fds[0], F_SETFL, O_NONBLOCK) == -1 ||
fcntl(pipe_fds[1], F_SETFL, O_NONBLOCK) == -1) {
PLOG(ERROR) << "fcntl(O_NONBLOCK)";
if (IGNORE_EINTR(close(pipe_fds[0])) < 0)
PLOG(ERROR) << "close";
if (IGNORE_EINTR(close(pipe_fds[1])) < 0)
PLOG(ERROR) << "close";
return false;
}
*fd1 = pipe_fds[0];
*fd2 = pipe_fds[1];
return true;
}
bool Channel::ChannelImpl::CreatePipe(
const IPC::ChannelHandle& channel_handle) {
DCHECK(server_listen_pipe_ == -1 && pipe_ == -1);
int local_pipe = -1;
if (channel_handle.socket.fd != -1) {
local_pipe = channel_handle.socket.fd;
#if defined(IPC_USES_READWRITE)
int value = fcntl(local_pipe, F_GETFL);
if (value == -1) {
PLOG(ERROR) << "fcntl(F_GETFL) " << pipe_name_;
return false;
}
if (!(value & O_NONBLOCK)) {
LOG(ERROR) << "Socket " << pipe_name_ << " must be O_NONBLOCK";
return false;
}
#endif
} else if (mode_ & MODE_NAMED_FLAG) {
if (mode_ & MODE_SERVER_FLAG) {
if (!CreateServerUnixDomainSocket(base::FilePath(pipe_name_),
&local_pipe)) {
return false;
}
must_unlink_ = true;
} else if (mode_ & MODE_CLIENT_FLAG) {
if (!CreateClientUnixDomainSocket(base::FilePath(pipe_name_),
&local_pipe)) {
return false;
}
} else {
LOG(ERROR) << "Bad mode: " << mode_;
return false;
}
} else {
local_pipe = PipeMap::GetInstance()->Lookup(pipe_name_);
if (mode_ & MODE_CLIENT_FLAG) {
if (local_pipe != -1) {
local_pipe = HANDLE_EINTR(dup(local_pipe));
PipeMap::GetInstance()->Remove(pipe_name_);
} else {
static bool used_initial_channel = false;
if (used_initial_channel) {
LOG(FATAL) << "Denying attempt to reuse initial IPC channel for "
<< pipe_name_;
return false;
}
used_initial_channel = true;
local_pipe =
base::GlobalDescriptors::GetInstance()->Get(kPrimaryIPCChannel);
}
} else if (mode_ & MODE_SERVER_FLAG) {
if (local_pipe != -1) {
LOG(ERROR) << "Server already exists for " << pipe_name_;
return false;
}
base::AutoLock lock(client_pipe_lock_);
if (!SocketPair(&local_pipe, &client_pipe_))
return false;
PipeMap::GetInstance()->Insert(pipe_name_, client_pipe_);
} else {
LOG(ERROR) << "Bad mode: " << mode_;
return false;
}
}
#if defined(IPC_USES_READWRITE)
if (mode_ & MODE_CLIENT_FLAG) {
if (!SocketPair(&fd_pipe_, &remote_fd_pipe_)) {
return false;
}
}
#endif
if ((mode_ & MODE_SERVER_FLAG) && (mode_ & MODE_NAMED_FLAG)) {
server_listen_pipe_ = local_pipe;
local_pipe = -1;
}
pipe_ = local_pipe;
return true;
}
bool Channel::ChannelImpl::Connect() {
if (server_listen_pipe_ == -1 && pipe_ == -1) {
DLOG(WARNING) << "Channel creation failed: " << pipe_name_;
return false;
}
bool did_connect = true;
if (server_listen_pipe_ != -1) {
base::MessageLoopForIO::current()->WatchFileDescriptor(
server_listen_pipe_,
true,
base::MessageLoopForIO::WATCH_READ,
&server_listen_connection_watcher_,
this);
} else {
did_connect = AcceptConnection();
}
return did_connect;
}
void Channel::ChannelImpl::CloseFileDescriptors(Message* msg) {
#if defined(OS_MACOSX)
std::vector<int> to_close;
msg->file_descriptor_set()->ReleaseFDsToClose(&to_close);
for (size_t i = 0; i < to_close.size(); i++) {
fds_to_close_.insert(to_close[i]);
QueueCloseFDMessage(to_close[i], 2);
}
#else
msg->file_descriptor_set()->CommitAll();
#endif
}
bool Channel::ChannelImpl::ProcessOutgoingMessages() {
DCHECK(!waiting_connect_);
if (output_queue_.empty())
return true;
if (pipe_ == -1)
return false;
while (!output_queue_.empty()) {
Message* msg = output_queue_.front();
size_t amt_to_write = msg->size() - message_send_bytes_written_;
DCHECK_NE(0U, amt_to_write);
const char* out_bytes = reinterpret_cast<const char*>(msg->data()) +
message_send_bytes_written_;
struct msghdr msgh = {0};
struct iovec iov = {const_cast<char*>(out_bytes), amt_to_write};
msgh.msg_iov = &iov;
msgh.msg_iovlen = 1;
char buf[CMSG_SPACE(
sizeof(int) * FileDescriptorSet::kMaxDescriptorsPerMessage)];
ssize_t bytes_written = 1;
int fd_written = -1;
if (message_send_bytes_written_ == 0 &&
!msg->file_descriptor_set()->empty()) {
struct cmsghdr *cmsg;
const unsigned num_fds = msg->file_descriptor_set()->size();
DCHECK(num_fds <= FileDescriptorSet::kMaxDescriptorsPerMessage);
if (msg->file_descriptor_set()->ContainsDirectoryDescriptor()) {
LOG(FATAL) << "Panic: attempting to transport directory descriptor over"
" IPC. Aborting to maintain sandbox isolation.";
}
msgh.msg_control = buf;
msgh.msg_controllen = CMSG_SPACE(sizeof(int) * num_fds);
cmsg = CMSG_FIRSTHDR(&msgh);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int) * num_fds);
msg->file_descriptor_set()->GetDescriptors(
reinterpret_cast<int*>(CMSG_DATA(cmsg)));
msgh.msg_controllen = cmsg->cmsg_len;
msg->header()->num_fds = static_cast<uint16>(num_fds);
#if defined(IPC_USES_READWRITE)
if (!IsHelloMessage(*msg)) {
struct iovec fd_pipe_iov = { const_cast<char *>(""), 1 };
msgh.msg_iov = &fd_pipe_iov;
fd_written = fd_pipe_;
bytes_written = HANDLE_EINTR(sendmsg(fd_pipe_, &msgh, MSG_DONTWAIT));
msgh.msg_iov = &iov;
msgh.msg_controllen = 0;
if (bytes_written > 0) {
CloseFileDescriptors(msg);
}
}
#endif
}
if (bytes_written == 1) {
fd_written = pipe_;
#if defined(IPC_USES_READWRITE)
if ((mode_ & MODE_CLIENT_FLAG) && IsHelloMessage(*msg)) {
DCHECK_EQ(msg->file_descriptor_set()->size(), 1U);
}
if (!msgh.msg_controllen) {
bytes_written = HANDLE_EINTR(write(pipe_, out_bytes, amt_to_write));
} else
#endif
{
bytes_written = HANDLE_EINTR(sendmsg(pipe_, &msgh, MSG_DONTWAIT));
}
}
if (bytes_written > 0)
CloseFileDescriptors(msg);
if (bytes_written < 0 && !SocketWriteErrorIsRecoverable()) {
#if defined(OS_MACOSX)
if (errno == EPERM) {
return false;
}
#endif
if (errno == EPIPE) {
return false;
}
PLOG(ERROR) << "pipe error on "
<< fd_written
<< " Currently writing message of size: "
<< msg->size();
return false;
}
if (static_cast<size_t>(bytes_written) != amt_to_write) {
if (bytes_written > 0) {
message_send_bytes_written_ += bytes_written;
}
is_blocked_on_write_ = true;
base::MessageLoopForIO::current()->WatchFileDescriptor(
pipe_,
false,
base::MessageLoopForIO::WATCH_WRITE,
&write_watcher_,
this);
return true;
} else {
message_send_bytes_written_ = 0;
DVLOG(2) << "sent message @" << msg << " on channel @" << this
<< " with type " << msg->type() << " on fd " << pipe_;
delete output_queue_.front();
output_queue_.pop();
}
}
return true;
}
bool Channel::ChannelImpl::Send(Message* message) {
DVLOG(2) << "sending message @" << message << " on channel @" << this
<< " with type " << message->type()
<< " (" << output_queue_.size() << " in queue)";
#ifdef IPC_MESSAGE_LOG_ENABLED
Logging::GetInstance()->OnSendMessage(message, "");
#endif
message->TraceMessageBegin();
output_queue_.push(message);
if (!is_blocked_on_write_ && !waiting_connect_) {
return ProcessOutgoingMessages();
}
return true;
}
int Channel::ChannelImpl::GetClientFileDescriptor() {
base::AutoLock lock(client_pipe_lock_);
return client_pipe_;
}
int Channel::ChannelImpl::TakeClientFileDescriptor() {
base::AutoLock lock(client_pipe_lock_);
int fd = client_pipe_;
if (client_pipe_ != -1) {
PipeMap::GetInstance()->Remove(pipe_name_);
client_pipe_ = -1;
}
return fd;
}
void Channel::ChannelImpl::CloseClientFileDescriptor() {
base::AutoLock lock(client_pipe_lock_);
if (client_pipe_ != -1) {
PipeMap::GetInstance()->Remove(pipe_name_);
if (IGNORE_EINTR(close(client_pipe_)) < 0)
PLOG(ERROR) << "close " << pipe_name_;
client_pipe_ = -1;
}
}
bool Channel::ChannelImpl::AcceptsConnections() const {
return server_listen_pipe_ != -1;
}
bool Channel::ChannelImpl::HasAcceptedConnection() const {
return AcceptsConnections() && pipe_ != -1;
}
bool Channel::ChannelImpl::GetPeerEuid(uid_t* peer_euid) const {
DCHECK(!(mode_ & MODE_SERVER) || HasAcceptedConnection());
return IPC::GetPeerEuid(pipe_, peer_euid);
}
void Channel::ChannelImpl::ResetToAcceptingConnectionState() {
read_watcher_.StopWatchingFileDescriptor();
write_watcher_.StopWatchingFileDescriptor();
if (pipe_ != -1) {
if (IGNORE_EINTR(close(pipe_)) < 0)
PLOG(ERROR) << "close pipe_ " << pipe_name_;
pipe_ = -1;
}
#if defined(IPC_USES_READWRITE)
if (fd_pipe_ != -1) {
if (IGNORE_EINTR(close(fd_pipe_)) < 0)
PLOG(ERROR) << "close fd_pipe_ " << pipe_name_;
fd_pipe_ = -1;
}
if (remote_fd_pipe_ != -1) {
if (IGNORE_EINTR(close(remote_fd_pipe_)) < 0)
PLOG(ERROR) << "close remote_fd_pipe_ " << pipe_name_;
remote_fd_pipe_ = -1;
}
#endif
while (!output_queue_.empty()) {
Message* m = output_queue_.front();
output_queue_.pop();
delete m;
}
ClearInputFDs();
#if defined(OS_MACOSX)
for (std::set<int>::iterator i = fds_to_close_.begin();
i != fds_to_close_.end();
++i) {
if (IGNORE_EINTR(close(*i)) < 0)
PLOG(ERROR) << "close";
}
fds_to_close_.clear();
#endif
}
bool Channel::ChannelImpl::IsNamedServerInitialized(
const std::string& channel_id) {
return base::PathExists(base::FilePath(channel_id));
}
#if defined(OS_LINUX)
void Channel::ChannelImpl::SetGlobalPid(int pid) {
global_pid_ = pid;
}
#endif
void Channel::ChannelImpl::OnFileCanReadWithoutBlocking(int fd) {
if (fd == server_listen_pipe_) {
int new_pipe = 0;
if (!ServerAcceptConnection(server_listen_pipe_, &new_pipe) ||
new_pipe < 0) {
Close();
listener()->OnChannelListenError();
}
if (pipe_ != -1) {
if (HANDLE_EINTR(shutdown(new_pipe, SHUT_RDWR)) < 0)
DPLOG(ERROR) << "shutdown " << pipe_name_;
if (IGNORE_EINTR(close(new_pipe)) < 0)
DPLOG(ERROR) << "close " << pipe_name_;
listener()->OnChannelDenied();
return;
}
pipe_ = new_pipe;
if ((mode_ & MODE_OPEN_ACCESS_FLAG) == 0) {
uid_t client_euid;
if (!GetPeerEuid(&client_euid)) {
DLOG(ERROR) << "Unable to query client euid";
ResetToAcceptingConnectionState();
return;
}
if (client_euid != geteuid()) {
DLOG(WARNING) << "Client euid is not authorised";
ResetToAcceptingConnectionState();
return;
}
}
if (!AcceptConnection()) {
NOTREACHED() << "AcceptConnection should not fail on server";
}
waiting_connect_ = false;
} else if (fd == pipe_) {
if (waiting_connect_ && (mode_ & MODE_SERVER_FLAG)) {
waiting_connect_ = false;
}
if (!ProcessIncomingMessages()) {
ClosePipeOnError();
return;
}
} else {
NOTREACHED() << "Unknown pipe " << fd;
}
if (!is_blocked_on_write_) {
if (!ProcessOutgoingMessages()) {
ClosePipeOnError();
}
}
}
void Channel::ChannelImpl::OnFileCanWriteWithoutBlocking(int fd) {
DCHECK_EQ(pipe_, fd);
is_blocked_on_write_ = false;
if (!ProcessOutgoingMessages()) {
ClosePipeOnError();
}
}
bool Channel::ChannelImpl::AcceptConnection() {
base::MessageLoopForIO::current()->WatchFileDescriptor(
pipe_, true, base::MessageLoopForIO::WATCH_READ, &read_watcher_, this);
QueueHelloMessage();
if (mode_ & MODE_CLIENT_FLAG) {
waiting_connect_ = false;
return ProcessOutgoingMessages();
} else if (mode_ & MODE_SERVER_FLAG) {
waiting_connect_ = true;
return true;
} else {
NOTREACHED();
return false;
}
}
void Channel::ChannelImpl::ClosePipeOnError() {
if (HasAcceptedConnection()) {
ResetToAcceptingConnectionState();
listener()->OnChannelError();
} else {
Close();
if (AcceptsConnections()) {
listener()->OnChannelListenError();
} else {
listener()->OnChannelError();
}
}
}
int Channel::ChannelImpl::GetHelloMessageProcId() {
int pid = base::GetCurrentProcId();
#if defined(OS_LINUX)
if (global_pid_) {
pid = global_pid_;
}
#endif
return pid;
}
void Channel::ChannelImpl::QueueHelloMessage() {
scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
HELLO_MESSAGE_TYPE,
IPC::Message::PRIORITY_NORMAL));
if (!msg->WriteInt(GetHelloMessageProcId())) {
NOTREACHED() << "Unable to pickle hello message proc id";
}
#if defined(IPC_USES_READWRITE)
scoped_ptr<Message> hello;
if (remote_fd_pipe_ != -1) {
if (!msg->WriteFileDescriptor(base::FileDescriptor(remote_fd_pipe_,
false))) {
NOTREACHED() << "Unable to pickle hello message file descriptors";
}
DCHECK_EQ(msg->file_descriptor_set()->size(), 1U);
}
#endif
output_queue_.push(msg.release());
}
Channel::ChannelImpl::ReadState Channel::ChannelImpl::ReadData(
char* buffer,
int buffer_len,
int* bytes_read) {
if (pipe_ == -1)
return READ_FAILED;
struct msghdr msg = {0};
struct iovec iov = {buffer, static_cast<size_t>(buffer_len)};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = input_cmsg_buf_;
#if defined(IPC_USES_READWRITE)
if (fd_pipe_ >= 0) {
*bytes_read = HANDLE_EINTR(read(pipe_, buffer, buffer_len));
msg.msg_controllen = 0;
} else
#endif
{
msg.msg_controllen = sizeof(input_cmsg_buf_);
*bytes_read = HANDLE_EINTR(recvmsg(pipe_, &msg, MSG_DONTWAIT));
}
if (*bytes_read < 0) {
if (errno == EAGAIN) {
return READ_PENDING;
#if defined(OS_MACOSX)
} else if (errno == EPERM) {
return READ_FAILED;
#endif
} else if (errno == ECONNRESET || errno == EPIPE) {
return READ_FAILED;
} else {
PLOG(ERROR) << "pipe error (" << pipe_ << ")";
return READ_FAILED;
}
} else if (*bytes_read == 0) {
return READ_FAILED;
}
DCHECK(*bytes_read);
CloseClientFileDescriptor();
if (!ExtractFileDescriptorsFromMsghdr(&msg))
return READ_FAILED;
return READ_SUCCEEDED;
}
#if defined(IPC_USES_READWRITE)
bool Channel::ChannelImpl::ReadFileDescriptorsFromFDPipe() {
char dummy;
struct iovec fd_pipe_iov = { &dummy, 1 };
struct msghdr msg = { 0 };
msg.msg_iov = &fd_pipe_iov;
msg.msg_iovlen = 1;
msg.msg_control = input_cmsg_buf_;
msg.msg_controllen = sizeof(input_cmsg_buf_);
ssize_t bytes_received = HANDLE_EINTR(recvmsg(fd_pipe_, &msg, MSG_DONTWAIT));
if (bytes_received != 1)
return true;
if (!ExtractFileDescriptorsFromMsghdr(&msg))
return false;
return true;
}
#endif
bool Channel::ChannelImpl::WillDispatchInputMessage(Message* msg) {
uint16 header_fds = msg->header()->num_fds;
if (!header_fds)
return true;
const char* error = NULL;
if (header_fds > input_fds_.size()) {
#if defined(IPC_USES_READWRITE)
if (!ReadFileDescriptorsFromFDPipe())
return false;
if (header_fds > input_fds_.size())
#endif
error = "Message needs unreceived descriptors";
}
if (header_fds > FileDescriptorSet::kMaxDescriptorsPerMessage)
error = "Message requires an excessive number of descriptors";
if (error) {
LOG(WARNING) << error
<< " channel:" << this
<< " message-type:" << msg->type()
<< " header()->num_fds:" << header_fds;
ClearInputFDs();
return false;
}
msg->file_descriptor_set()->SetDescriptors(&input_fds_.front(),
header_fds);
input_fds_.erase(input_fds_.begin(), input_fds_.begin() + header_fds);
return true;
}
bool Channel::ChannelImpl::DidEmptyInputBuffers() {
return input_fds_.empty();
}
bool Channel::ChannelImpl::ExtractFileDescriptorsFromMsghdr(msghdr* msg) {
if (msg->msg_controllen == 0)
return true;
for (cmsghdr* cmsg = CMSG_FIRSTHDR(msg);
cmsg;
cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS) {
unsigned payload_len = cmsg->cmsg_len - CMSG_LEN(0);
DCHECK_EQ(0U, payload_len % sizeof(int));
const int* file_descriptors = reinterpret_cast<int*>(CMSG_DATA(cmsg));
unsigned num_file_descriptors = payload_len / 4;
input_fds_.insert(input_fds_.end(),
file_descriptors,
file_descriptors + num_file_descriptors);
if (msg->msg_flags & MSG_CTRUNC) {
ClearInputFDs();
return false;
}
return true;
}
}
return true;
}
void Channel::ChannelImpl::ClearInputFDs() {
for (size_t i = 0; i < input_fds_.size(); ++i) {
if (IGNORE_EINTR(close(input_fds_[i])) < 0)
PLOG(ERROR) << "close ";
}
input_fds_.clear();
}
void Channel::ChannelImpl::QueueCloseFDMessage(int fd, int hops) {
switch (hops) {
case 1:
case 2: {
scoped_ptr<Message> msg(new Message(MSG_ROUTING_NONE,
CLOSE_FD_MESSAGE_TYPE,
IPC::Message::PRIORITY_NORMAL));
if (!msg->WriteInt(hops - 1) || !msg->WriteInt(fd)) {
NOTREACHED() << "Unable to pickle close fd.";
}
output_queue_.push(msg.release());
break;
}
default:
NOTREACHED();
break;
}
}
void Channel::ChannelImpl::HandleInternalMessage(const Message& msg) {
PickleIterator iter(msg);
switch (msg.type()) {
default:
NOTREACHED();
break;
case Channel::HELLO_MESSAGE_TYPE:
int pid;
if (!msg.ReadInt(&iter, &pid))
NOTREACHED();
#if defined(IPC_USES_READWRITE)
if (mode_ & MODE_SERVER_FLAG) {
DCHECK_EQ(msg.file_descriptor_set()->size(), 1U);
base::FileDescriptor descriptor;
if (!msg.ReadFileDescriptor(&iter, &descriptor)) {
NOTREACHED();
}
fd_pipe_ = descriptor.fd;
CHECK(descriptor.auto_close);
}
#endif
peer_pid_ = pid;
listener()->OnChannelConnected(pid);
break;
#if defined(OS_MACOSX)
case Channel::CLOSE_FD_MESSAGE_TYPE:
int fd, hops;
if (!msg.ReadInt(&iter, &hops))
NOTREACHED();
if (!msg.ReadInt(&iter, &fd))
NOTREACHED();
if (hops == 0) {
if (fds_to_close_.erase(fd) > 0) {
if (IGNORE_EINTR(close(fd)) < 0)
PLOG(ERROR) << "close";
} else {
NOTREACHED();
}
} else {
QueueCloseFDMessage(fd, hops);
}
break;
#endif
}
}
void Channel::ChannelImpl::Close() {
ResetToAcceptingConnectionState();
if (must_unlink_) {
unlink(pipe_name_.c_str());
must_unlink_ = false;
}
if (server_listen_pipe_ != -1) {
if (IGNORE_EINTR(close(server_listen_pipe_)) < 0)
DPLOG(ERROR) << "close " << server_listen_pipe_;
server_listen_pipe_ = -1;
server_listen_connection_watcher_.StopWatchingFileDescriptor();
}
CloseClientFileDescriptor();
}
Channel::Channel(const IPC::ChannelHandle& channel_handle, Mode mode,
Listener* listener)
: channel_impl_(new ChannelImpl(channel_handle, mode, listener)) {
}
Channel::~Channel() {
delete channel_impl_;
}
bool Channel::Connect() {
return channel_impl_->Connect();
}
void Channel::Close() {
if (channel_impl_)
channel_impl_->Close();
}
base::ProcessId Channel::peer_pid() const {
return channel_impl_->peer_pid();
}
bool Channel::Send(Message* message) {
return channel_impl_->Send(message);
}
int Channel::GetClientFileDescriptor() const {
return channel_impl_->GetClientFileDescriptor();
}
int Channel::TakeClientFileDescriptor() {
return channel_impl_->TakeClientFileDescriptor();
}
bool Channel::AcceptsConnections() const {
return channel_impl_->AcceptsConnections();
}
bool Channel::HasAcceptedConnection() const {
return channel_impl_->HasAcceptedConnection();
}
bool Channel::GetPeerEuid(uid_t* peer_euid) const {
return channel_impl_->GetPeerEuid(peer_euid);
}
void Channel::ResetToAcceptingConnectionState() {
channel_impl_->ResetToAcceptingConnectionState();
}
bool Channel::IsNamedServerInitialized(const std::string& channel_id) {
return ChannelImpl::IsNamedServerInitialized(channel_id);
}
std::string Channel::GenerateVerifiedChannelID(const std::string& prefix) {
std::string id = prefix;
if (!id.empty())
id.append(".");
return id.append(GenerateUniqueRandomChannelID());
}
#if defined(OS_LINUX)
void Channel::SetGlobalPid(int pid) {
ChannelImpl::SetGlobalPid(pid);
}
#endif
}