interfaces.cpp raw

   1  // Copyright (c) 2021-present The Bitcoin Core 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 <tinyformat.h>
  13  #include <util/fs.h>
  14  #include <util/log.h>
  15  
  16  #include <csignal>
  17  #include <cstdio>
  18  #include <cstdlib>
  19  #include <cstring>
  20  #include <functional>
  21  #include <memory>
  22  #include <stdexcept>
  23  #include <string>
  24  #include <unistd.h>
  25  #include <utility>
  26  #include <vector>
  27  
  28  namespace ipc {
  29  namespace {
  30  #ifndef WIN32
  31  std::string g_ignore_ctrl_c;
  32  
  33  void HandleCtrlC(int)
  34  {
  35      // (void)! needed to suppress -Wunused-result warning from GCC
  36      (void)!write(STDOUT_FILENO, g_ignore_ctrl_c.data(), g_ignore_ctrl_c.size());
  37  }
  38  #endif
  39  
  40  void IgnoreCtrlC(std::string message)
  41  {
  42  #ifndef WIN32
  43      g_ignore_ctrl_c = std::move(message);
  44      struct sigaction sa{};
  45      sa.sa_handler = HandleCtrlC;
  46      sigemptyset(&sa.sa_mask);
  47      sa.sa_flags = SA_RESTART;
  48      sigaction(SIGINT, &sa, nullptr);
  49  #endif
  50  }
  51  
  52  class IpcImpl : public interfaces::Ipc
  53  {
  54  public:
  55      IpcImpl(const char* exe_name, const char* process_argv0, interfaces::Init& init)
  56          : m_exe_name(exe_name), m_process_argv0(process_argv0), m_init(init),
  57            m_protocol(ipc::capnp::MakeCapnpProtocol()), m_process(ipc::MakeProcess())
  58      {
  59      }
  60      std::unique_ptr<interfaces::Init> spawnProcess(const char* new_exe_name) override
  61      {
  62          int pid;
  63          int fd = m_process->spawn(new_exe_name, m_process_argv0, pid);
  64          LogDebug(::BCLog::IPC, "Process %s pid %i launched\n", new_exe_name, pid);
  65          auto init = m_protocol->connect(fd, m_exe_name);
  66          Ipc::addCleanup(*init, [this, new_exe_name, pid] {
  67              int status = m_process->waitSpawned(pid);
  68              LogDebug(::BCLog::IPC, "Process %s pid %i exited with status %i\n", new_exe_name, pid, status);
  69          });
  70          return init;
  71      }
  72      bool startSpawnedProcess(int argc, char* argv[], int& exit_status) override
  73      {
  74          exit_status = EXIT_FAILURE;
  75          int32_t fd = -1;
  76          if (!m_process->checkSpawned(argc, argv, fd)) {
  77              return false;
  78          }
  79          IgnoreCtrlC(strprintf("[%s] SIGINT received — waiting for parent to shut down.\n", m_exe_name));
  80          m_protocol->serve(fd, m_exe_name, m_init);
  81          exit_status = EXIT_SUCCESS;
  82          return true;
  83      }
  84      std::unique_ptr<interfaces::Init> connectAddress(std::string& address) override
  85      {
  86          if (address.empty() || address == "0") return nullptr;
  87          int fd;
  88          if (address == "auto") {
  89              // Treat "auto" the same as "unix" except don't treat it an as error
  90              // if the connection is not accepted. Just return null so the caller
  91              // can work offline without a connection, or spawn a new
  92              // bitcoin-node process and connect to it.
  93              address = "unix";
  94              try {
  95                  fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
  96              } catch (const std::system_error& e) {
  97                  // If connection type is auto and socket path isn't accepting connections, or doesn't exist, catch the error and return null;
  98                  if (e.code() == std::errc::connection_refused || e.code() == std::errc::no_such_file_or_directory || e.code() == std::errc::not_a_directory) {
  99                      return nullptr;
 100                  }
 101                  throw;
 102              } catch (const std::invalid_argument&) {
 103                 // Catch 'Unix address path "..." exceeded maximum socket path length' error
 104                 return nullptr;
 105              }
 106          } else {
 107              fd = m_process->connect(gArgs.GetDataDirNet(), "bitcoin-node", address);
 108          }
 109          return m_protocol->connect(fd, m_exe_name);
 110      }
 111      void listenAddress(std::string& address) override
 112      {
 113          int fd = m_process->bind(gArgs.GetDataDirNet(), m_exe_name, address);
 114          m_protocol->listen(fd, m_exe_name, m_init);
 115      }
 116      void disconnectIncoming() override
 117      {
 118          m_protocol->disconnectIncoming();
 119      }
 120      void addCleanup(std::type_index type, void* iface, std::function<void()> cleanup) override
 121      {
 122          m_protocol->addCleanup(type, iface, std::move(cleanup));
 123      }
 124      Context& context() override { return m_protocol->context(); }
 125      const char* m_exe_name;
 126      const char* m_process_argv0;
 127      interfaces::Init& m_init;
 128      std::unique_ptr<Protocol> m_protocol;
 129      std::unique_ptr<Process> m_process;
 130  };
 131  } // namespace
 132  } // namespace ipc
 133  
 134  namespace interfaces {
 135  std::unique_ptr<Ipc> MakeIpc(const char* exe_name, const char* process_argv0, Init& init)
 136  {
 137      return std::make_unique<ipc::IpcImpl>(exe_name, process_argv0, init);
 138  }
 139  } // namespace interfaces
 140