netipmid: move sol console sockets to asio

Rewrite the SOL console sockets use boost::asio. This reduces code size
and ties better into the main asio io loop.

Change-Id: Ia79b9aa3fa3c7ce1ddd9b609b032160a88394f8c
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/main.cpp b/main.cpp
index abb7958..1e35b9a 100644
--- a/main.cpp
+++ b/main.cpp
@@ -29,7 +29,7 @@
 session::Manager manager;
 command::Table table;
 eventloop::EventLoop loop(io);
-sol::Manager solManager;
+sol::Manager solManager(io);
 
 std::tuple<session::Manager&, command::Table&, eventloop::EventLoop&,
            sol::Manager&>
diff --git a/sd_event_loop.cpp b/sd_event_loop.cpp
index 990426f..8d24d23 100644
--- a/sd_event_loop.cpp
+++ b/sd_event_loop.cpp
@@ -61,50 +61,6 @@
                           });
 }
 
-static int consoleInputHandler(sd_event_source* es, int fd, uint32_t revents,
-                               void* userdata)
-{
-    try
-    {
-        int readSize = 0;
-
-        if (ioctl(fd, FIONREAD, &readSize) < 0)
-        {
-            log<level::ERR>("ioctl failed for FIONREAD:",
-                            entry("ERRNO=%d", errno));
-            return 0;
-        }
-
-        std::vector<uint8_t> buffer(readSize);
-        auto bufferSize = buffer.size();
-        ssize_t readDataLen = 0;
-
-        readDataLen = read(fd, buffer.data(), bufferSize);
-
-        // Update the Console buffer with data read from the socket
-        if (readDataLen > 0)
-        {
-            buffer.resize(readDataLen);
-            std::get<sol::Manager&>(singletonPool).dataBuffer.write(buffer);
-        }
-        else if (readDataLen == 0)
-        {
-            log<level::ERR>("Connection Closed for host console socket");
-        }
-        else if (readDataLen < 0) // Error
-        {
-            log<level::ERR>("Reading from host console socket failed:",
-                            entry("ERRNO=%d", errno));
-        }
-    }
-    catch (std::exception& e)
-    {
-        log<level::ERR>(e.what());
-    }
-
-    return 0;
-}
-
 static int charAccTimerHandler(sd_event_source* s, uint64_t usec,
                                void* userdata)
 {
@@ -223,45 +179,6 @@
     return EXIT_SUCCESS;
 }
 
-void EventLoop::startHostConsole(const sol::CustomFD& fd)
-{
-    int rc = 0;
-
-    if ((fd() == -1) || hostConsole.get())
-    {
-        throw std::runtime_error("Console descriptor already added");
-    }
-
-    sd_event_source* source = nullptr;
-
-    // Add the fd to the event loop for EPOLLIN
-    rc = sd_event_add_io(event, &source, fd(), EPOLLIN, consoleInputHandler,
-                         nullptr);
-    if (rc < 0)
-    {
-        throw std::runtime_error("Failed to add socket descriptor");
-    }
-
-    hostConsole.reset(source);
-    source = nullptr;
-}
-
-void EventLoop::stopHostConsole()
-{
-    if (hostConsole.get())
-    {
-        // Disable the host console payload
-        int rc = sd_event_source_set_enabled(hostConsole.get(), SD_EVENT_OFF);
-        if (rc < 0)
-        {
-            log<level::ERR>("Failed to disable the host console socket",
-                            entry("RC=%d", rc));
-        }
-
-        hostConsole.reset();
-    }
-}
-
 void EventLoop::startSOLPayloadInstance(uint8_t payloadInst,
                                         IntervalType accumulateInterval,
                                         IntervalType retryInterval)
diff --git a/sd_event_loop.hpp b/sd_event_loop.hpp
index c81218a..0d9c268 100644
--- a/sd_event_loop.hpp
+++ b/sd_event_loop.hpp
@@ -76,15 +76,6 @@
      */
     int startEventLoop();
 
-    /** @brief Add host console I/O event source to the event loop.
-     *
-     *  @param[in] fd - File descriptor for host console socket.
-     */
-    void startHostConsole(const sol::CustomFD& fd);
-
-    /** @brief Remove host console I/O event source. */
-    void stopHostConsole();
-
     /** @brief Initialize the timers for the SOL payload instance
      *
      *  This API would add the Character accumulate interval timer event
@@ -145,9 +136,6 @@
     /** @brief register the async handler for incoming udp packets */
     void startRmcpReceive();
 
-    /** @brief Event source object for host console. */
-    EventSource hostConsole = nullptr;
-
     /** @brief boost::asio io context to run with
      */
     std::shared_ptr<boost::asio::io_context> io;
diff --git a/sol/sol_manager.cpp b/sol/sol_manager.cpp
index 859f371..fc0efe9 100644
--- a/sol/sol_manager.cpp
+++ b/sol/sol_manager.cpp
@@ -6,6 +6,10 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <boost/asio/basic_stream_socket.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/local/stream_protocol.hpp>
+#include <boost/asio/write.hpp>
 #include <chrono>
 #include <cmath>
 #include <phosphor-logging/log.hpp>
@@ -15,80 +19,78 @@
 
 using namespace phosphor::logging;
 
-CustomFD::~CustomFD()
+void Manager::initConsoleSocket()
 {
-    if (fd >= 0)
-    {
-        // Remove the host console descriptor from the sd_event_loop
-        std::get<eventloop::EventLoop&>(singletonPool).stopHostConsole();
-        close(fd);
-    }
+    // explicit length constructor for NUL-prefixed abstract path
+    std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN);
+    boost::asio::local::stream_protocol::endpoint ep(path);
+    consoleSocket =
+        std::make_unique<boost::asio::local::stream_protocol::socket>(*io);
+    consoleSocket->connect(ep);
 }
 
-void Manager::initHostConsoleFd()
+void Manager::consoleInputHandler()
 {
-    struct sockaddr_un addr;
-    int rc = 0;
-    int fd = 0;
-
-    fd = socket(AF_UNIX, SOCK_STREAM, 0);
-    if (fd < 0)
+    boost::system::error_code ec;
+    boost::asio::socket_base::bytes_readable cmd(true);
+    consoleSocket->io_control(cmd, ec);
+    size_t readSize;
+    if (!ec)
     {
-        log<level::ERR>("Failed to open the host console socket",
-                        entry("ERRNO=%d", errno));
-        throw std::runtime_error("Failed to open the host console socket");
+        readSize = cmd.get();
+    }
+    else
+    {
+        log<level::ERR>("Reading ready count from host console socket failed:",
+                        entry("EXCEPTION=%s", ec.message().c_str()));
+        return;
+    }
+    std::vector<uint8_t> buffer(readSize);
+    ec.clear();
+    size_t readDataLen =
+        consoleSocket->read_some(boost::asio::buffer(buffer), ec);
+    if (ec)
+    {
+        log<level::ERR>("Reading from host console socket failed:",
+                        entry("EXCEPTION=%s", ec.message().c_str()));
+        return;
     }
 
-    memset(&addr, 0, sizeof(addr));
-    addr.sun_family = AF_UNIX;
-    memcpy(&addr.sun_path, &CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN);
-    consoleFD = std::make_unique<CustomFD>(fd);
-    auto& conFD = *(consoleFD.get());
-
-    rc =
-        connect(conFD(), (struct sockaddr*)&addr,
-                sizeof(addr) - sizeof(addr.sun_path) + CONSOLE_SOCKET_PATH_LEN);
-    if (rc < 0)
-    {
-        log<level::ERR>("Failed to connect to host console socket address",
-                        entry("ERRNO=%d", errno));
-        consoleFD.reset();
-        throw std::runtime_error("Failed to connect to console server");
-    }
+    // Update the Console buffer with data read from the socket
+    buffer.resize(readDataLen);
+    dataBuffer.write(buffer);
 }
 
 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input) const
 {
-    auto inBuffer = input.data();
-    auto inBufferSize = input.size();
-    size_t pos = 0;
-    ssize_t rc = 0;
-    int errVal = 0;
-    auto& conFD = *(consoleFD.get());
+    boost::system::error_code ec;
+    boost::asio::write(*consoleSocket, boost::asio::buffer(input), ec);
+    return ec.value();
+}
 
-    for (pos = 0; pos < inBufferSize; pos += rc)
+void Manager::startHostConsole()
+{
+    if (!consoleSocket)
     {
-        rc = write(conFD(), inBuffer + pos, inBufferSize - pos);
-        if (rc <= 0)
-        {
-            if (errno == EINTR)
-            {
-                log<level::INFO>(" Retrying to handle EINTR",
-                                 entry("ERRNO=%d", errno));
-                rc = 0;
-                continue;
-            }
-            else
-            {
-                errVal = errno;
-                log<level::ERR>("Failed to write to host console socket",
-                                entry("ERRNO=%d", errno));
-                return -errVal;
-            }
-        }
+        initConsoleSocket();
     }
+    consoleSocket->async_wait(boost::asio::socket_base::wait_read,
+                              [this](const boost::system::error_code& ec) {
+                                  if (!ec)
+                                  {
+                                      consoleInputHandler();
+                                      startHostConsole();
+                                  }
+                              });
+}
 
-    return 0;
+void Manager::stopHostConsole()
+{
+    if (consoleSocket)
+    {
+        consoleSocket->cancel();
+        consoleSocket.reset();
+    }
 }
 
 void Manager::startPayloadInstance(uint8_t payloadInstance,
@@ -96,11 +98,7 @@
 {
     if (payloadMap.empty())
     {
-        initHostConsoleFd();
-
-        // Register the fd in the sd_event_loop
-        std::get<eventloop::EventLoop&>(singletonPool)
-            .startHostConsole(*(consoleFD.get()));
+        startHostConsole();
     }
 
     // Create the SOL Context data for payload instance
@@ -132,7 +130,7 @@
 
     if (payloadMap.empty())
     {
-        consoleFD.reset();
+        stopHostConsole();
 
         dataBuffer.erase(dataBuffer.size());
     }
diff --git a/sol/sol_manager.hpp b/sol/sol_manager.hpp
index 5a0c18c..5d96890 100644
--- a/sol/sol_manager.hpp
+++ b/sol/sol_manager.hpp
@@ -4,6 +4,8 @@
 #include "session.hpp"
 #include "sol_context.hpp"
 
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/local/stream_protocol.hpp>
 #include <map>
 #include <memory>
 
@@ -23,32 +25,6 @@
 
 using Instance = uint8_t;
 
-/** @struct CustomFD
- *
- *  RAII wrapper for file descriptor.
- */
-struct CustomFD
-{
-    CustomFD(const CustomFD&) = delete;
-    CustomFD& operator=(const CustomFD&) = delete;
-    CustomFD(CustomFD&&) = delete;
-    CustomFD& operator=(CustomFD&&) = delete;
-
-    CustomFD(int fd) : fd(fd)
-    {
-    }
-
-    ~CustomFD();
-
-    int operator()() const
-    {
-        return fd;
-    }
-
-  private:
-    int fd = -1;
-};
-
 using namespace std::chrono_literals;
 
 /** @class Manager
@@ -65,13 +41,20 @@
      */
     using SOLPayloadMap = std::map<Instance, std::unique_ptr<Context>>;
 
-    Manager() = default;
+    Manager() = delete;
     ~Manager() = default;
     Manager(const Manager&) = delete;
     Manager& operator=(const Manager&) = delete;
     Manager(Manager&&) = default;
     Manager& operator=(Manager&&) = default;
 
+    Manager(std::shared_ptr<boost::asio::io_context> io) : io(io)
+    {
+    }
+
+    /** @brief io context to add events to */
+    std::shared_ptr<boost::asio::io_context> io;
+
     /** @brief Host Console Buffer. */
     ConsoleData dataBuffer;
 
@@ -180,6 +163,12 @@
      */
     uint8_t channel = 1;
 
+    /** @brief Add host console I/O event source to the event loop.  */
+    void startHostConsole();
+
+    /** @brief Remove host console I/O event source. */
+    void stopHostConsole();
+
     /** @brief Start a SOL payload instance.
      *
      *  Starting a payload instance involves creating the context object,
@@ -263,11 +252,15 @@
   private:
     SOLPayloadMap payloadMap;
 
-    /** @brief File descriptor for the host console. */
-    std::unique_ptr<CustomFD> consoleFD = nullptr;
+    /** @brief Local stream socket for the host console. */
+    std::unique_ptr<boost::asio::local::stream_protocol::socket> consoleSocket =
+        nullptr;
 
     /** @brief Initialize the host console file descriptor. */
-    void initHostConsoleFd();
+    void initConsoleSocket();
+
+    /** @brief Handle incoming console data on the console socket */
+    void consoleInputHandler();
 };
 
 } // namespace sol