blob: 6518394d4d9859faa74af69e2893f82904700a8d [file]
// Copyright 2023 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "ipc_utils.h"
#include <string.h>
#include "util.h"
#ifndef _WIN32
#include <sys/signal.h>
#include <sys/socket.h>
#include <unistd.h>
// helper macro to loop on EINTR during syscalls.
// Important: Do not use it for close(), use CloseFd() instead.
#define HANDLE_EINTR(x) \
({ \
decltype(x) eintr_wrapper_result; \
do { \
eintr_wrapper_result = (x); \
} while (eintr_wrapper_result == -1 && errno == EINTR); \
eintr_wrapper_result; \
})
#endif // !_WIN32
RemoteArguments::RemoteArguments(int argc, char** argv) {
if (argc > 0) {
args_.reserve(static_cast<size_t>(argc));
for (int n = 0; n < argc; ++n) {
args_.push_back(argv[n]);
}
}
}
void RemoteArguments::Reset(const std::vector<std::string>& args) {
args_ = args;
}
char** RemoteArguments::argv() const {
if (args_.empty())
return nullptr;
argv_.clear();
argv_.reserve(args_.size());
for (const auto& arg : args_)
argv_.push_back(const_cast<char*>(arg.data()));
return argv_.data();
}
void RemoteArguments::InsertAt(size_t pos, std::string arg) {
if (pos > args_.size())
pos = args_.size();
args_.insert(args_.begin() + pos, std::move(arg));
}
template <>
bool RemoteWrite<std::string>(const std::string& obj, const ScopedHandle& con,
std::string* error) {
size_t size = obj.size();
return RemoteWrite(size, con, error) &&
con.WriteFull(obj.data(), size, error);
}
template <>
bool RemoteWrite<RemoteArguments>(const RemoteArguments& obj,
const ScopedHandle& con, std::string* error) {
size_t size = obj.args().size();
if (!RemoteWrite(size, con, error))
return false;
for (const auto& arg : obj.args()) {
if (!RemoteWrite(arg, con, error))
return false;
}
return true;
}
template <>
bool RemoteRead<std::string>(std::string& obj, const ScopedHandle& con,
std::string* error) {
size_t size;
if (!RemoteRead(size, con, error))
return false;
obj.resize(size);
if (!size)
return true;
return con.ReadFull(const_cast<char*>(obj.data()), size, error);
}
template <>
bool RemoteRead<RemoteArguments>(RemoteArguments& obj, const ScopedHandle& con,
std::string* error) {
size_t size;
if (!RemoteRead(size, con, error))
return false;
std::vector<std::string> vec;
vec.resize(size);
for (auto& arg : vec) {
if (!RemoteRead(arg, con, error))
return false;
}
obj.Reset(std::move(vec));
return true;
}
#ifdef _WIN32
namespace {
std::string Win32ErrorMessage(const char* prefix, LONG error) {
return StringFormat("%s: %s", prefix, GetLastErrorString(error).c_str());
}
std::string Win32ErrorMessage(const char* prefix) {
return Win32ErrorMessage(prefix, GetLastError());
}
struct HandleMessage {
HANDLE process_id;
HANDLE handle;
};
} // namespace
bool RemoteSendNativeHandle(const ScopedHandle& con,
ScopedHandle::NativeType handle,
std::string* error_message) {
// Send a message that contains the current process ID, and the handle
// through the named pipe. The ReceiveNativeHandle() method will use them
// to call DuplicateHandle(). Note that this does not work for console
// input/output handles (the handles can be duplicated, but trying to use them
// from a different process returns an error), so in addition to using this
// method, the sender should use a specialized class to redirect stdout/stderr
// using new named pipe handles. See the Win32StdOutputBridge class.
HandleMessage msg;
msg.process_id =
OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
msg.handle = handle;
ssize_t count = con.Write(&msg, sizeof(msg), error_message);
if (count < 0)
return false;
if (count != static_cast<ssize_t>(sizeof(msg))) {
*error_message = "Error when sending handle";
return false;
}
return true;
}
bool RemoteReceiveNativeHandle(const ScopedHandle& con, ScopedHandle* handle,
std::string* error_message) {
HandleMessage msg;
ssize_t count = con.Read(&msg, sizeof(msg), error_message);
if (count < 0)
return false;
if (count != static_cast<ssize_t>(sizeof(msg))) {
*error_message = "Error when receiving handle";
return false;
}
// Create a duplicate of the source handle in the current process.
HANDLE native = INVALID_HANDLE_VALUE;
if (!DuplicateHandle(msg.process_id, // source process
msg.handle, // source handle
GetCurrentProcess(), // target process
&native, // target handle pointer
0, // ignored with DUPLICATE_SAME_ACCESS
FALSE, // not inheritable
DUPLICATE_SAME_ACCESS)) {
*error_message = Win32ErrorMessage("Could not duplicate handle");
return false;
}
*handle = ScopedHandle(native);
return true;
}
#else // !_WIN32
bool RemoteSendNativeHandle(const ScopedHandle& con,
ScopedHandle::NativeType handle,
std::string* error_message) {
char ch = 'x';
struct iovec iov = { &ch, 1 };
union {
char buf[CMSG_SPACE(sizeof(int))];
cmsghdr align;
} control;
memset(control.buf, 0, sizeof(control.buf));
struct msghdr header = {};
header.msg_iov = &iov;
header.msg_iovlen = 1;
header.msg_control = control.buf;
header.msg_controllen = sizeof(control.buf);
struct cmsghdr* control_header = CMSG_FIRSTHDR(&header);
control_header->cmsg_len = CMSG_LEN(sizeof(int));
control_header->cmsg_level = SOL_SOCKET;
control_header->cmsg_type = SCM_RIGHTS;
reinterpret_cast<int*>(CMSG_DATA(control_header))[0] = handle;
ssize_t ret = HANDLE_EINTR(sendmsg(con.get(), &header, 0));
if (ret == -1) {
*error_message = strerror(errno);
return false;
}
return true;
}
bool RemoteReceiveNativeHandle(const ScopedHandle& con, ScopedHandle* handle,
std::string* error_message) {
char ch = '\0';
iovec iov = { &ch, 1 };
union {
char buf[CMSG_SPACE(sizeof(int))];
cmsghdr align;
} control;
memset(control.buf, 0, sizeof(control.buf));
msghdr header = {};
header.msg_iov = &iov;
header.msg_iovlen = 1;
header.msg_control = control.buf;
header.msg_controllen = sizeof(control.buf);
ssize_t ret = HANDLE_EINTR(recvmsg(con.get(), &header, 0));
if (ret == -1) {
*error_message = strerror(errno);
return false;
}
cmsghdr* control_header = CMSG_FIRSTHDR(&header);
if (!control_header || control_header->cmsg_len != CMSG_LEN(sizeof(int)) ||
control_header->cmsg_level != SOL_SOCKET ||
control_header->cmsg_type != SCM_RIGHTS) {
*error_message =
std::string("Invalid data when receiving file descriptor!");
return false;
}
*handle = ScopedHandle(reinterpret_cast<int*>(CMSG_DATA(control_header))[0]);
return true;
}
#endif // !_WIN32
void WireEncoder::Write(const void* buffer, size_t size) {
result_.append(static_cast<const char*>(buffer), size);
}
void WireEncoder::Write(const std::string& str) {
Write(static_cast<uint32_t>(str.size()));
Write(str.data(), str.size());
}
WireDecoder::WireDecoder(const void* buffer, size_t size)
: p_(static_cast<const char*>(buffer)), end_(p_ + size) {}
WireDecoder::WireDecoder(const std::string& str)
: WireDecoder(str.data(), str.size()) {}
void WireDecoder::Read(void* buffer, size_t size) {
if (p_ + size <= end_) {
::memcpy(buffer, p_, size);
p_ += size;
} else {
// To avoid relying on un-initialized memory at runtime
// if the client fails to check for errors.
::memset(buffer, '\0', size);
p_ = end_;
has_error_ = true;
}
}
void WireDecoder::Read(std::string& str) {
uint32_t size = 0;
Read(size);
std::string result;
if (size) {
result.resize(size);
Read(const_cast<char*>(result.data()), result.size());
}
str = std::move(result);
}
#ifndef _WIN32
SigPipeBlocker::SigPipeBlocker() {
struct sigaction action = {};
action.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &action, &prev_action_) < 0)
ErrnoFatal("sigaction");
}
SigPipeBlocker::~SigPipeBlocker() {
if (sigaction(SIGPIPE, &prev_action_, nullptr) < 0)
ErrnoFatal("sigaction");
}
#endif // !_WIN32