| // Copyright 2021 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "socket_logger.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <future> |
| |
| #include <src/lib/fsl/socket/socket_drainer.h> |
| |
| #include "logger.h" |
| |
| namespace { |
| |
| // Posts a task to invoke |handler| and waits for it to complete. |
| // |
| // The current thread must not be a dispatcher thread, otherwise |
| // the call will deadlock. |
| zx_status_t PostTaskAndWait(async_dispatcher_t* dispatcher, fit::closure handler) { |
| std::promise<void> signal; |
| std::future<void> done = signal.get_future(); |
| zx_status_t status = async::PostTask(dispatcher, [&handler, &signal]() { |
| handler(); |
| signal.set_value(); |
| }); |
| if (status != ZX_OK) { |
| return status; |
| } |
| done.wait(); |
| return ZX_OK; |
| } |
| |
| } // namespace |
| |
| class LogClient : public fsl::SocketDrainer::Client { |
| public: |
| explicit LogClient(Logger* logger) : logger_(logger) {} |
| |
| void OnDataAvailable(const void* data, size_t num_bytes) override { |
| logger_->Write(std::string_view(static_cast<const char*>(data), num_bytes)); |
| } |
| |
| void OnDataComplete() override { logger_->Write("<guest serial connection closed>"); } |
| |
| private: |
| Logger* logger_; |
| }; |
| |
| SocketLogger::SocketLogger(Logger* logger, zx::socket socket) { |
| client_ = std::make_unique<LogClient>(logger); |
| |
| // Create a thread for the async loop. |
| zx_status_t status = loop_.StartThread("serial_logger_loop"); |
| FX_CHECK(status == ZX_OK) << "Failed to start logging thread"; |
| |
| // SocketDrainer (and hence LogClient) is thread-hostile, and must be |
| // started and stopped on the remote thread. |
| status = PostTaskAndWait(loop_.dispatcher(), [this, socket = std::move(socket)]() mutable { |
| drainer_.emplace(client_.get(), loop_.dispatcher()); |
| drainer_->Start(std::move(socket)); |
| }); |
| ZX_ASSERT(status == ZX_OK); |
| } |
| |
| SocketLogger::~SocketLogger() { |
| // Destroy the drainer on the dispatcher's thread. |
| zx_status_t status = PostTaskAndWait(loop_.dispatcher(), [this]() { drainer_.reset(); }); |
| ZX_ASSERT(status == ZX_OK); |
| } |