// 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 "aemu/base/containers/Lookup.h" #include "aemu/base/Result.h" #include "android/crashreport/crash-handler.h" #include "host-common/android_pipe_device.h" #include "host-common/AndroidPipe.h" #include "host-common/crash-handler.h" #include "host-common/testing/TestVmLock.h" #include "host-common/VmLock.h" #include #include #include #include #include #include #include #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::create(int fd) { return std::make_unique(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 hwPipe = HostHwPipe::create(hwPipeFd); InternalPipe* hostPipe = static_cast( android_pipe_guest_open(hwPipe.get(), nullptr)); if (!hostPipe) { mErrno = ENOENT; return kNoFd; } const ssize_t len = static_cast(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(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 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& 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 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 hwPipe = HostHwPipe::create(fd); HOST_PIPE_DLOG("attempt to load a host pipe"); char forceClose = 0; InternalPipe* hostPipe = static_cast( 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 hwPipe = HostHwPipe::create(fd); char forceClose = 0; InternalPipe* hostPipe = static_cast( 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 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(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(hwPipeRaw)->getFd(); HostGoldfishPipeDevice::get()->signalWake(fd, wakes); } // static int HostGoldfishPipeDevice::getPipeIdCallback(void* hwPipeRaw) { return static_cast(hwPipeRaw)->getFd(); } } // namespace android