| // Copyright (C) 2018 The Android Open Source Project |
| // |
| // 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 "host-common/HostGoldfishPipe.h" |
| |
| #include "base/Lookup.h" |
| #include "base/Result.h" |
| #include "android/crashreport/crash-handler.h" |
| #include "host-common/android_pipe_device.h" |
| #include "host-common/AndroidPipe.h" |
| #include "host-common/testing/TestVmLock.h" |
| #include "host-common/VmLock.h" |
| |
| #include <algorithm> |
| #include <unordered_map> |
| #include <unordered_set> |
| #include <vector> |
| |
| #include <errno.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #ifdef _MSC_VER |
| #ifdef ERROR |
| #undef ERROR |
| #endif |
| #endif |
| |
| #define HOST_PIPE_DEBUG 0 |
| |
| #if HOST_PIPE_DEBUG |
| |
| #define HOST_PIPE_DLOG(fmt,...) fprintf(stderr, "%s:%d " fmt "\n", __func__, __LINE__, ##__VA_ARGS__); |
| |
| #else |
| |
| #define HOST_PIPE_DLOG(fmt,...) |
| |
| #endif |
| |
| using android::base::Result; |
| using android::base::Ok; |
| using android::base::Err; |
| |
| namespace android { |
| |
| const AndroidPipeHwFuncs HostGoldfishPipeDevice::HostHwPipe::vtbl = { |
| &closeFromHostCallback, |
| &signalWakeCallback, |
| &getPipeIdCallback, |
| }; |
| |
| HostGoldfishPipeDevice::HostHwPipe::HostHwPipe(int fd) : vtblPtr(&vtbl), mFd(fd) {} |
| |
| HostGoldfishPipeDevice::HostHwPipe::~HostHwPipe() {} |
| |
| std::unique_ptr<HostGoldfishPipeDevice::HostHwPipe> |
| HostGoldfishPipeDevice::HostHwPipe::create(int fd) { |
| return std::make_unique<HostHwPipe>(fd); |
| } |
| |
| HostGoldfishPipeDevice::HostGoldfishPipeDevice() {} |
| |
| HostGoldfishPipeDevice::~HostGoldfishPipeDevice() { |
| clearLocked(); // no lock required in dctor |
| } |
| |
| int HostGoldfishPipeDevice::getErrno() const { |
| ScopedVmLock lock; |
| return mErrno; |
| } |
| |
| // Also opens. |
| int HostGoldfishPipeDevice::connect(const char* name) { |
| const auto handshake = std::string("pipe:") + name; |
| |
| ScopedVmLock lock; |
| const int hwPipeFd = ++mFdGenerator; |
| std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(hwPipeFd); |
| |
| InternalPipe* hostPipe = static_cast<InternalPipe*>( |
| android_pipe_guest_open(hwPipe.get())); |
| if (!hostPipe) { |
| mErrno = ENOENT; |
| return kNoFd; |
| } |
| |
| const ssize_t len = static_cast<ssize_t>(handshake.size()) + 1; |
| const ssize_t ret = writeInternal(&hostPipe, handshake.c_str(), len); |
| |
| if (ret == len) { |
| HostHwPipe* hwPipeWeak = associatePipes(std::move(hwPipe), hostPipe); |
| if (hwPipeWeak) { |
| HOST_PIPE_DLOG("New pipe: service: for '%s', hwpipe: %p hostpipe: %p", |
| name, hwPipeWeak, hostPipe); |
| |
| return hwPipeWeak->getFd(); |
| } else { |
| mErrno = ENOENT; |
| return kNoFd; |
| } |
| } else { |
| LOG(ERROR) << "Could not connect to goldfish pipe name: " << name; |
| android_pipe_guest_close(hostPipe, PIPE_CLOSE_GRACEFUL); |
| mErrno = EIO; |
| return kNoFd; |
| } |
| } |
| |
| void HostGoldfishPipeDevice::close(const int fd) { |
| HOST_PIPE_DLOG("Close fd=%d", fd); |
| |
| ScopedVmLock lock; |
| if (!eraseFdInfo(fd)) { |
| LOG(INFO) << "Could not close pipe, ENOENT."; |
| mErrno = ENOENT; |
| } |
| } |
| |
| // Read/write/poll but for a particular pipe. |
| ssize_t HostGoldfishPipeDevice::read(const int fd, void* buffer, size_t len) { |
| ScopedVmLock lock; |
| |
| FdInfo* fdInfo = lookupFdInfo(fd); |
| if (!fdInfo) { |
| LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd"; |
| mErrno = EINVAL; |
| return PIPE_ERROR_INVAL; |
| } |
| |
| AndroidPipeBuffer buf = { static_cast<uint8_t*>(buffer), len }; |
| ssize_t res = android_pipe_guest_recv(fdInfo->hostPipe, &buf, 1); |
| setErrno(res); |
| return res; |
| } |
| |
| HostGoldfishPipeDevice::ReadResult HostGoldfishPipeDevice::read(int fd, size_t maxLength) { |
| std::vector<uint8_t> resultBuffer(maxLength); |
| ssize_t read_size = read(fd, resultBuffer.data(), maxLength); |
| |
| if (read_size < 0) { |
| return Err(mErrno); |
| } else { |
| if (read_size < maxLength) { |
| resultBuffer.resize(read_size); |
| } |
| return Ok(resultBuffer); |
| } |
| } |
| |
| ssize_t HostGoldfishPipeDevice::write(const int fd, const void* buffer, size_t len) { |
| ScopedVmLock lock; |
| |
| FdInfo* fdInfo = lookupFdInfo(fd); |
| if (!fdInfo) { |
| LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd"; |
| mErrno = EINVAL; |
| return PIPE_ERROR_INVAL; |
| } |
| |
| return writeInternal(&fdInfo->hostPipe, buffer, len); |
| } |
| |
| HostGoldfishPipeDevice::WriteResult |
| HostGoldfishPipeDevice::write(const int fd, const std::vector<uint8_t>& data) { |
| ssize_t res = write(fd, data.data(), data.size()); |
| |
| if (res < 0) { |
| return Err(mErrno); |
| } else { |
| return Ok(res); |
| } |
| } |
| |
| unsigned HostGoldfishPipeDevice::poll(const int fd) const { |
| ScopedVmLock lock; |
| |
| const FdInfo* fdInfo = lookupFdInfo(fd); |
| if (!fdInfo) { |
| LOG(ERROR) << "fd=" << fd << " is not a valid pipe fd"; |
| return 0; |
| } |
| |
| return android_pipe_guest_poll(fdInfo->hostPipe); |
| } |
| |
| void HostGoldfishPipeDevice::setWakeCallback(const int fd, |
| std::function<void(int)> callback) { |
| ScopedVmLock lock; |
| |
| FdInfo* fdInfo = lookupFdInfo(fd); |
| if (fdInfo) { |
| fdInfo->wakeCallback = std::move(callback); |
| } |
| } |
| |
| void* HostGoldfishPipeDevice::getHostPipe(const int fd) const { |
| ScopedVmLock lock; |
| |
| const FdInfo* fdInfo = lookupFdInfo(fd); |
| return fdInfo ? fdInfo->hostPipe : nullptr; |
| } |
| |
| void HostGoldfishPipeDevice::saveSnapshot(base::Stream* stream) { |
| HOST_PIPE_DLOG("Saving snapshot"); |
| |
| auto cStream = reinterpret_cast<::Stream*>(stream); |
| |
| ScopedVmLock lock; |
| |
| android_pipe_guest_pre_save(cStream); |
| stream_put_be32(cStream, mFdInfo.size()); |
| for (const auto& kv : mFdInfo) { |
| HOST_PIPE_DLOG("save pipe: fd=%d hwPipe=%p hostPipe=%p", |
| kv.first, kv.second.hwPipe.get(), kv.second.hostPipe); |
| stream_put_be32(cStream, kv.first); |
| android_pipe_guest_save(kv.second.hostPipe, cStream); |
| } |
| android_pipe_guest_post_save(cStream); |
| } |
| |
| void HostGoldfishPipeDevice::loadSnapshot(base::Stream* stream) { |
| auto cStream = reinterpret_cast<::Stream*>(stream); |
| |
| ScopedVmLock lock; |
| clearLocked(); |
| |
| android_pipe_guest_pre_load(cStream); |
| const uint32_t pipeCount = stream_get_be32(cStream); |
| |
| for (uint32_t i = 0; i < pipeCount; ++i) { |
| const int fd = stream_get_be32(cStream); |
| mFdGenerator = std::max(mFdGenerator, fd); |
| |
| std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(fd); |
| |
| HOST_PIPE_DLOG("attempt to load a host pipe"); |
| |
| char forceClose = 0; |
| InternalPipe* hostPipe = static_cast<InternalPipe*>( |
| android_pipe_guest_load(cStream, hwPipe.get(), &forceClose)); |
| |
| if (!forceClose) { |
| HostHwPipe* hwPipePtr = associatePipes(std::move(hwPipe), hostPipe); |
| HOST_PIPE_DLOG("Successfully loaded host pipe %p for hw pipe %p", |
| hostPipe, hwPipePtr); |
| } else { |
| HOST_PIPE_DLOG("Failed to load host pipe for hw pipe %p", hwpipe); |
| LOG(ERROR) << "Could not load goldfish pipe"; |
| mErrno = EIO; |
| return; |
| } |
| } |
| |
| android_pipe_guest_post_load(cStream); |
| } |
| |
| void HostGoldfishPipeDevice::saveSnapshot(base::Stream* stream, const int fd) { |
| HOST_PIPE_DLOG("Saving snapshot for fd=%d", fd); |
| |
| ScopedVmLock lock; |
| FdInfo* fdInfo = lookupFdInfo(fd); |
| if (!fdInfo) { |
| crashhandler_die_format("%s:%d fd=%d is a valid pipe fd", |
| __func__, __LINE__, fd); |
| } |
| |
| auto cStream = reinterpret_cast<::Stream*>(stream); |
| |
| android_pipe_guest_pre_save(cStream); |
| |
| stream_put_be32(cStream, 1); |
| stream_put_be32(cStream, fd); |
| android_pipe_guest_save(fdInfo->hostPipe, cStream); |
| android_pipe_guest_post_save(cStream); |
| } |
| |
| int HostGoldfishPipeDevice::loadSnapshotSinglePipe(base::Stream* stream) { |
| HOST_PIPE_DLOG("Loading snapshot for a single pipe"); |
| |
| auto cStream = reinterpret_cast<::Stream*>(stream); |
| |
| ScopedVmLock lock; |
| |
| android_pipe_guest_pre_load(cStream); |
| uint32_t pipeCount = stream_get_be32(cStream); |
| |
| if (pipeCount != 1) { |
| LOG(ERROR) << "Invalid pipe count from stream"; |
| mErrno = EIO; |
| return kNoFd; |
| } |
| |
| const int fd = stream_get_be32(cStream); |
| eraseFdInfo(fd); |
| |
| HOST_PIPE_DLOG("attempt to load a host pipe"); |
| |
| std::unique_ptr<HostHwPipe> hwPipe = HostHwPipe::create(fd); |
| |
| char forceClose = 0; |
| InternalPipe* hostPipe = static_cast<InternalPipe*>( |
| android_pipe_guest_load(cStream, hwPipe.get(), &forceClose)); |
| if (!forceClose) { |
| associatePipes(std::move(hwPipe), hostPipe); |
| HOST_PIPE_DLOG("Successfully loaded host pipe %p for fd=%d", hostPipe, fd); |
| } else { |
| HOST_PIPE_DLOG("Failed to load host pipe for hw pipe %p", hwPipeFromStream); |
| LOG(ERROR) << "Could not load goldfish pipe"; |
| mErrno = EIO; |
| } |
| |
| android_pipe_guest_post_load(cStream); |
| |
| return fd; |
| } |
| |
| void HostGoldfishPipeDevice::clear() { |
| ScopedVmLock lock; |
| clearLocked(); |
| } |
| |
| void HostGoldfishPipeDevice::initialize() { |
| if (mInitialized) return; |
| AndroidPipe::Service::resetAll(); |
| AndroidPipe::initThreading(android::HostVmLock::getInstance()); |
| mInitialized = true; |
| } |
| |
| // locked |
| void HostGoldfishPipeDevice::clearLocked() { |
| while (true) { |
| const auto i = mFdInfo.begin(); |
| if (i == mFdInfo.end()) { |
| break; |
| } else { |
| eraseFdInfo(i->first); |
| } |
| } |
| } |
| |
| // locked |
| HostGoldfishPipeDevice::FdInfo* HostGoldfishPipeDevice::lookupFdInfo(int fd) { |
| const auto i = mFdInfo.find(fd); |
| return (i == mFdInfo.end()) ? nullptr : &i->second; |
| } |
| |
| // locked |
| const HostGoldfishPipeDevice::FdInfo* HostGoldfishPipeDevice::lookupFdInfo(int fd) const { |
| const auto i = mFdInfo.find(fd); |
| return (i == mFdInfo.end()) ? nullptr : &i->second; |
| } |
| |
| // locked |
| HostGoldfishPipeDevice::HostHwPipe* |
| HostGoldfishPipeDevice::associatePipes(std::unique_ptr<HostGoldfishPipeDevice::HostHwPipe> hwPipe, |
| HostGoldfishPipeDevice::InternalPipe* hostPipe) { |
| HostHwPipe* hwPipePtr = hwPipe.get(); |
| const int hwPipeFd = hwPipePtr->getFd(); |
| |
| mPipeToHwPipe[hostPipe] = hwPipePtr; |
| |
| FdInfo info; |
| info.hwPipe = std::move(hwPipe); |
| info.hostPipe = hostPipe; |
| |
| if (!mFdInfo.insert({hwPipeFd, std::move(info)}).second) { |
| crashhandler_die_format("%s:%d hwPipeFd=%d already exists", |
| __func__, __LINE__, hwPipeFd); |
| } |
| |
| return hwPipePtr; |
| } |
| |
| // locked |
| bool HostGoldfishPipeDevice::eraseFdInfo(const int fd) { |
| const auto i = mFdInfo.find(fd); |
| if (i == mFdInfo.end()) { |
| return false; |
| } |
| |
| if (mPipeToHwPipe.erase(i->second.hostPipe) == 0) { |
| crashhandler_die_format("%s:%d hostPipe=%p does not exit while fd=%d exists", |
| __func__, __LINE__, i->second.hostPipe, fd); |
| } |
| |
| android_pipe_guest_close(i->second.hostPipe, PIPE_CLOSE_GRACEFUL); |
| mFdInfo.erase(i); |
| |
| return true; |
| } |
| |
| ssize_t HostGoldfishPipeDevice::writeInternal(InternalPipe** ppipe, |
| const void* buffer, |
| size_t len) { |
| AndroidPipeBuffer buf = {(uint8_t*)buffer, len}; |
| ssize_t res = android_pipe_guest_send((void**)ppipe, &buf, 1); |
| setErrno(res); |
| return res; |
| } |
| |
| void HostGoldfishPipeDevice::setErrno(ssize_t res) { |
| if (res >= 0) return; |
| switch (res) { |
| case PIPE_ERROR_INVAL: |
| mErrno = EINVAL; |
| break; |
| case PIPE_ERROR_AGAIN: |
| mErrno = EAGAIN; |
| break; |
| case PIPE_ERROR_NOMEM: |
| mErrno = ENOMEM; |
| break; |
| case PIPE_ERROR_IO: |
| mErrno = EIO; |
| break; |
| } |
| } |
| |
| void HostGoldfishPipeDevice::signalWake(const int fd, const int wakes) { |
| ScopedVmLock lock; |
| const FdInfo* fdInfo = lookupFdInfo(fd); |
| if (fdInfo) { |
| fdInfo->wakeCallback(wakes); |
| } |
| } |
| |
| static HostGoldfishPipeDevice* sDevice() { |
| static HostGoldfishPipeDevice* d = new HostGoldfishPipeDevice; |
| return d; |
| } |
| |
| // static |
| HostGoldfishPipeDevice* HostGoldfishPipeDevice::get() { |
| auto res = sDevice(); |
| // Must be separate from construction |
| // as some initialization routines require |
| // the instance to be constructed. |
| res->initialize(); |
| return res; |
| } |
| |
| // Callbacks for AndroidPipeHwFuncs. |
| // static |
| void HostGoldfishPipeDevice::closeFromHostCallback(void* hwPipeRaw) { |
| const int fd = static_cast<const HostHwPipe*>(hwPipeRaw)->getFd(); |
| |
| // PIPE_WAKE_CLOSED gets translated to closeFromHostCallback. |
| // To simplify detecting a close-from-host, signal a wake callback so that |
| // the event can be detected. |
| HostGoldfishPipeDevice::get()->signalWake(fd, PIPE_WAKE_CLOSED); |
| HostGoldfishPipeDevice::get()->close(fd); |
| } |
| |
| // static |
| void HostGoldfishPipeDevice::signalWakeCallback(void* hwPipeRaw, unsigned wakes) { |
| const int fd = static_cast<const HostHwPipe*>(hwPipeRaw)->getFd(); |
| HostGoldfishPipeDevice::get()->signalWake(fd, wakes); |
| } |
| |
| // static |
| int HostGoldfishPipeDevice::getPipeIdCallback(void* hwPipeRaw) { |
| return static_cast<const HostHwPipe*>(hwPipeRaw)->getFd(); |
| } |
| |
| } // namespace android |