interfaces.cpp raw

   1  // Copyright (c) 2021-2022 The Limenka developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <common/args.h>
   6  #include <common/system.h>
   7  #include <interfaces/init.h>
   8  #include <interfaces/ipc.h>
   9  #include <ipc/capnp/protocol.h>
  10  #include <ipc/process.h>
  11  #include <ipc/protocol.h>
  12  #include <logging.h>
  13  #include <tinyformat.h>
  14  #include <util/fs.h>
  15  
  16  #include <cstdio>
  17  #include <cstdlib>
  18  #include <functional>
  19  #include <memory>
  20  #include <stdexcept>
  21  #include <string.h>
  22  #include <string>
  23  #include <unistd.h>
  24  #include <utility>
  25  #include <vector>
  26  
  27  namespace ipc {
  28  namespace {
  29  class IpcImpl : public interfaces::Ipc
  30  {
  31  public:
  32      IpcImpl(const char* exe_name, const char* process_argv0, interfaces::Init& init)
  33          : m_exe_name(exe_name), m_process_argv0(process_argv0), m_init(init),
  34            m_protocol(ipc::capnp::MakeCapnpProtocol()), m_process(ipc::MakeProcess())
  35      {
  36      }
  37      std::unique_ptr<interfaces::Init> spawnProcess(const char* new_exe_name) override
  38      {
  39          int pid;
  40          int fd = m_process->spawn(new_exe_name, m_process_argv0, pid);
  41          LogDebug(::BCLog::IPC, "Process %s pid %i launched\n", new_exe_name, pid);
  42          auto init = m_protocol->connect(fd, m_exe_name);
  43          Ipc::addCleanup(*init, [this, new_exe_name, pid] {
  44              int status = m_process->waitSpawned(pid);
  45              LogDebug(::BCLog::IPC, "Process %s pid %i exited with status %i\n", new_exe_name, pid, status);
  46          });
  47          return init;
  48      }
  49      bool startSpawnedProcess(int argc, char* argv[], int& exit_status) override
  50      {
  51          exit_status = EXIT_FAILURE;
  52          int32_t fd = -1;
  53          if (!m_process->checkSpawned(argc, argv, fd)) {
  54              return false;
  55          }
  56          m_protocol->serve(fd, m_exe_name, m_init);
  57          exit_status = EXIT_SUCCESS;
  58          return true;
  59      }
  60      std::unique_ptr<interfaces::Init> connectAddress(std::string& address) override
  61      {
  62          if (address.empty() || address == "0") return nullptr;
  63          int fd;
  64          if (address == "auto") {
  65              // Treat "auto" the same as "unix" except don't treat it an as error
  66              // if the connection is not accepted. Just return null so the caller
  67              // can work offline without a connection, or spawn a new
  68              // limenka-node process and connect to it.
  69              address = "unix";
  70              try {
  71                  fd = m_process->connect(gArgs.GetDataDirNet(), "limenka-node", address);
  72              } catch (const std::system_error& e) {
  73                  // If connection type is auto and socket path isn't accepting connections, or doesn't exist, catch the error and return null;
  74                  if (e.code() == std::errc::connection_refused || e.code() == std::errc::no_such_file_or_directory) {
  75                      return nullptr;
  76                  }
  77                  throw;
  78              }
  79          } else {
  80              fd = m_process->connect(gArgs.GetDataDirNet(), "limenka-node", address);
  81          }
  82          return m_protocol->connect(fd, m_exe_name);
  83      }
  84      void listenAddress(std::string& address) override
  85      {
  86          int fd = m_process->bind(gArgs.GetDataDirNet(), m_exe_name, address);
  87          m_protocol->listen(fd, m_exe_name, m_init);
  88      }
  89      void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
  90      {
  91          m_protocol->addCleanup(type, iface, std::move(cleanup));
  92      }
  93      Context& context() override { return m_protocol->context(); }
  94      const char* m_exe_name;
  95      const char* m_process_argv0;
  96      interfaces::Init& m_init;
  97      std::unique_ptr<Protocol> m_protocol;
  98      std::unique_ptr<Process> m_process;
  99  };
 100  } // namespace
 101  } // namespace ipc
 102  
 103  namespace interfaces {
 104  std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
 105  {
 106      return std::make_unique<ipc::IpcImpl>(exe_name, process_argv0, init);
 107  }
 108  } // namespace interfaces
 109