Electroneum
daemon.cpp
Go to the documentation of this file.
1 // Copyrights(c) 2017-2021, The Electroneum Project
2 // Copyrights(c) 2014-2019, The Monero Project
3 //
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without modification, are
7 // permitted provided that the following conditions are met:
8 //
9 // 1. Redistributions of source code must retain the above copyright notice, this list of
10 // conditions and the following disclaimer.
11 //
12 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
13 // of conditions and the following disclaimer in the documentation and/or other
14 // materials provided with the distribution.
15 //
16 // 3. Neither the name of the copyright holder nor the names of its contributors may be
17 // used to endorse or promote products derived from this software without specific
18 // prior written permission.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
21 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
22 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 //
30 // Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
31 
32 #include <memory>
33 #include <stdexcept>
34 #include <boost/algorithm/string/split.hpp>
35 #include "misc_log_ex.h"
36 #include "daemon/daemon.h"
37 #include "rpc/daemon_handler.h"
38 #include "rpc/zmq_server.h"
39 
40 #include "common/password.h"
41 #include "common/util.h"
42 #include "daemon/core.h"
43 #include "daemon/p2p.h"
44 #include "daemon/protocol.h"
45 #include "daemon/rpc.h"
46 #include "daemon/command_server.h"
47 #include "daemon/command_server.h"
49 #include "net/net_ssl.h"
50 #include "version.h"
51 
52 using namespace epee;
53 
54 #include <functional>
55 
56 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
57 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "daemon"
58 
59 namespace daemonize {
60 
61 struct t_internals {
62 private:
63  t_protocol protocol;
64 public:
67  std::vector<std::unique_ptr<t_rpc>> rpcs;
68 
70  boost::program_options::variables_map const & vm
71  )
72  : core{vm}
73  , protocol{vm, core, command_line::get_arg(vm, cryptonote::arg_offline)}
74  , p2p{vm, protocol}
75  {
76  // Handle circular dependencies
77  protocol.set_p2p_endpoint(p2p.get());
78  core.set_protocol(protocol.get());
79 
82  rpcs.emplace_back(new t_rpc{vm, core, p2p, restricted, main_rpc_port, "core"});
83 
84  auto restricted_rpc_port_arg = cryptonote::core_rpc_server::arg_rpc_restricted_bind_port;
85  if(!command_line::is_arg_defaulted(vm, restricted_rpc_port_arg))
86  {
87  auto restricted_rpc_port = command_line::get_arg(vm, restricted_rpc_port_arg);
88  rpcs.emplace_back(new t_rpc{vm, core, p2p, true, restricted_rpc_port, "restricted"});
89  }
90  }
91 };
92 
93 void t_daemon::init_options(boost::program_options::options_description & option_spec)
94 {
95  t_core::init_options(option_spec);
96  t_p2p::init_options(option_spec);
97  t_rpc::init_options(option_spec);
98 }
99 
101  boost::program_options::variables_map const & vm,
102  uint16_t public_rpc_port
103  )
104  : mp_internals{new t_internals{vm}},
105  public_rpc_port(public_rpc_port)
106 {
108  zmq_rpc_bind_address = command_line::get_arg(vm, daemon_args::arg_zmq_rpc_bind_ip);
110 }
111 
112 t_daemon::~t_daemon() = default;
113 
114 // MSVC is brain-dead and can't default this...
116 {
117  if (this != &other)
118  {
119  mp_internals = std::move(other.mp_internals);
120  other.mp_internals.reset(nullptr);
121  public_rpc_port = other.public_rpc_port;
122  }
123 }
124 
125 // or this
127 {
128  if (this != &other)
129  {
130  mp_internals = std::move(other.mp_internals);
131  other.mp_internals.reset(nullptr);
132  public_rpc_port = other.public_rpc_port;
133  }
134  return *this;
135 }
136 
137 bool t_daemon::run(bool interactive)
138 {
139  if (nullptr == mp_internals)
140  {
141  throw std::runtime_error{"Can't run stopped daemon"};
142  }
143 
144  std::atomic<bool> stop(false), shutdown(false);
145  boost::thread stop_thread = boost::thread([&stop, &shutdown, this] {
146  while (!stop)
148  if (shutdown)
149  this->stop_p2p();
150  });
152  stop = true;
153  stop_thread.join();
154  });
155  tools::signal_handler::install([&stop, &shutdown](int){ stop = shutdown = true; });
156 
157  try
158  {
159  if (!mp_internals->core.run())
160  return false;
161 
162  for(auto& rpc: mp_internals->rpcs)
163  rpc->run();
164 
165  std::unique_ptr<daemonize::t_command_server> rpc_commands;
166  if (interactive && mp_internals->rpcs.size())
167  {
168  // The first three variables are not used when the fourth is false
169  rpc_commands.reset(new daemonize::t_command_server(0, 0, boost::none, epee::net_utils::ssl_support_t::e_ssl_support_disabled, false, mp_internals->rpcs.front()->get_server()));
170  rpc_commands->start_handling(std::bind(&daemonize::t_daemon::stop_p2p, this));
171  }
172 
173  cryptonote::rpc::DaemonHandler rpc_daemon_handler(mp_internals->core.get(), mp_internals->p2p.get());
174  cryptonote::rpc::ZmqServer zmq_server(rpc_daemon_handler);
175 
176  if (!zmq_rpc_disabled)
177  {
178  if (!zmq_server.addTCPSocket(zmq_rpc_bind_address, zmq_rpc_bind_port))
179  {
180  LOG_ERROR(std::string("Failed to add TCP Socket (") + zmq_rpc_bind_address
181  + ":" + zmq_rpc_bind_port + ") to ZMQ RPC Server");
182 
183  if (rpc_commands)
184  rpc_commands->stop_handling();
185 
186  for(auto& rpc : mp_internals->rpcs)
187  rpc->stop();
188 
189  return false;
190  }
191 
192  MINFO("Starting ZMQ server...");
193  zmq_server.run();
194 
195  MINFO(std::string("ZMQ server started at ") + zmq_rpc_bind_address
196  + ":" + zmq_rpc_bind_port + ".");
197  }
198  else
199  MINFO("ZMQ server disabled");
200 
201  if (public_rpc_port > 0)
202  {
203  MGINFO("Public RPC port " << public_rpc_port << " will be advertised to other peers over P2P");
204  mp_internals->p2p.get().set_rpc_port(public_rpc_port);
205  }
206 
207  mp_internals->p2p.run(); // blocks until p2p goes down
208 
209  if (rpc_commands)
210  rpc_commands->stop_handling();
211 
212  if (!zmq_rpc_disabled)
213  zmq_server.stop();
214 
215  for(auto& rpc : mp_internals->rpcs)
216  rpc->stop();
217  MGINFO("Node stopped.");
218  return true;
219  }
220  catch (std::exception const & ex)
221  {
222  MFATAL("Uncaught exception! " << ex.what());
223  return false;
224  }
225  catch (...)
226  {
227  MFATAL("Uncaught exception!");
228  return false;
229  }
230 }
231 
233 {
234  if (nullptr == mp_internals)
235  {
236  throw std::runtime_error{"Can't stop stopped daemon"};
237  }
238  mp_internals->p2p.stop();
239  for(auto& rpc : mp_internals->rpcs)
240  rpc->stop();
241 
242  mp_internals.reset(nullptr); // Ensure resources are cleaned up before we return
243 }
244 
245 void t_daemon::stop_p2p()
246 {
247  if (nullptr == mp_internals)
248  {
249  throw std::runtime_error{"Can't send stop signal to a stopped daemon"};
250  }
251  mp_internals->p2p.get().send_stop_signal();
252 }
253 
254 } // namespace daemonize
void init_options(boost::program_options::options_description &hidden_options, boost::program_options::options_description &normal_options)
#define MINFO(x)
Definition: misc_log_ex.h:75
#define MFATAL(x)
Definition: misc_log_ex.h:72
std::vector< std::unique_ptr< t_rpc > > rpcs
Definition: daemon.cpp:67
::std::string string
Definition: gtest-port.h:1097
auto_scope_leave_caller create_scope_leave_handler(t_scope_leave_handler f)
t_internals(boost::program_options::variables_map const &vm)
Definition: daemon.cpp:69
const command_line::arg_descriptor< std::string > arg_zmq_rpc_bind_ip
unsigned short uint16_t
Definition: stdint.h:125
t_daemon(boost::program_options::variables_map const &vm, uint16_t public_rpc_port=0)
Definition: daemon.cpp:100
#define MGINFO(x)
Definition: misc_log_ex.h:80
t_daemon(boost::program_options::variables_map const &_vm)
t_daemon & operator=(t_daemon &&other)
Definition: daemon.cpp:126
const command_line::arg_descriptor< std::string, false, true, 2 > arg_zmq_rpc_bind_port
static const command_line::arg_descriptor< std::string > arg_rpc_restricted_bind_port
t_node_server & get()
Definition: p2p.h:72
void set_protocol(t_protocol_raw &protocol)
Definition: core.h:66
boost::shared_ptr< call_befor_die_base > auto_scope_leave_caller
bool sleep_no_w(long ms)
bool start_handling(std::function< void(void)> exit_handler=NULL)
const command_line::arg_descriptor< bool > arg_zmq_rpc_disabled
const command_line::arg_descriptor< bool > arg_offline
static bool install(T t)
installs a signal handler
Definition: util.h:164
const T & move(const T &t)
Definition: gtest-port.h:1317
#define LOG_ERROR(x)
Definition: misc_log_ex.h:98
t_protocol_raw & get()
Definition: protocol.h:62
T get_arg(const boost::program_options::variables_map &vm, const arg_descriptor< T, false, true > &arg)
Definition: command_line.h:271
static const command_line::arg_descriptor< std::string, false, true, 2 > arg_rpc_bind_port
static const command_line::arg_descriptor< bool > arg_restricted_rpc
void set_p2p_endpoint(t_node_server &server)
Definition: protocol.h:67
bool is_arg_defaulted(const boost::program_options::variables_map &vm, const arg_descriptor< T, required, dependent, NUM_DEPS > &arg)
Definition: command_line.h:265
bool run(bool interactive=false)
Definition: daemon.cpp:137