From f68a7cfa7acfec709f61ebb7d0429e3ce825f414 Mon Sep 17 00:00:00 2001 From: dev Date: Sat, 16 Feb 2019 03:04:08 +0300 Subject: [PATCH] peer log downloading --- src/common/util.cpp | 61 ++++++++++ src/common/util.h | 2 + src/connectivity_tool/conn_tool.cpp | 173 +++++++++++++++++++++++++++- src/p2p/net_node.h | 4 + src/p2p/net_node.inl | 40 ++++++- src/p2p/p2p_protocol_defs.h | 70 ++++++++++- 6 files changed, 347 insertions(+), 3 deletions(-) diff --git a/src/common/util.cpp b/src/common/util.cpp index 338e07dc..f470a5e6 100644 --- a/src/common/util.cpp +++ b/src/common/util.cpp @@ -526,4 +526,65 @@ std::string get_nix_version_display_string() #endif return std::error_code(code, std::system_category()); } + +#define REQUEST_LOG_CHUNK_SIZE_MAX (10 * 1024 * 1024) + + bool get_log_chunk_gzipped(uint64_t offset, uint64_t size, std::string& output, std::string& error) + { + if (size > REQUEST_LOG_CHUNK_SIZE_MAX) + { + error = std::string("size is exceeding the limit = ") + epee::string_tools::num_to_string_fast(REQUEST_LOG_CHUNK_SIZE_MAX); + return false; + } + + std::string log_filename = epee::log_space::log_singletone::get_actual_log_file_path(); + if (std::ifstream log{ log_filename, std::ifstream::ate | std::ifstream::binary }) + { + uint64_t file_size = log.tellg(); + + if (offset >= file_size) + { + error = "offset is out of bounds"; + return false; + } + + if (offset + size > file_size) + { + error = "offset + size if out of bounds"; + return false; + } + + if (size != 0) + { + log.seekg(offset); + output.resize(size); + log.read(&output.front(), size); + uint64_t read_bytes = log.gcount(); + if (read_bytes != size) + { + error = std::string("read bytes: ") + epee::string_tools::num_to_string_fast(read_bytes); + return false; + } + + if (!epee::zlib_helper::pack(output)) + { + error = "zlib pack failed"; + return false; + } + } + + return true; + } + + error = std::string("can't open ") + log_filename; + return false; + } + + uint64_t get_log_file_size() + { + std::string log_filename = epee::log_space::log_singletone::get_actual_log_file_path(); + std::ifstream in(log_filename, std::ifstream::ate | std::ifstream::binary); + return static_cast(in.tellg()); + } + } diff --git a/src/common/util.h b/src/common/util.h index 2166b6ca..11374f8c 100644 --- a/src/common/util.h +++ b/src/common/util.h @@ -48,6 +48,8 @@ namespace tools return k; } + bool get_log_chunk_gzipped(uint64_t offset, uint64_t size, std::string& output, std::string& error); + uint64_t get_log_file_size(); class signal_handler { diff --git a/src/connectivity_tool/conn_tool.cpp b/src/connectivity_tool/conn_tool.cpp index 1678ed4c..e73cbe49 100644 --- a/src/connectivity_tool/conn_tool.cpp +++ b/src/connectivity_tool/conn_tool.cpp @@ -54,6 +54,8 @@ namespace const command_line::arg_descriptor arg_generate_genesis = {"generate-genesis", "Generate genesis coinbase based on config file", "", true }; const command_line::arg_descriptor arg_genesis_split_amount = { "genesis-split-amount", "Set split amount for generating genesis block", 0, true }; const command_line::arg_descriptor arg_get_info_flags = { "getinfo-flags-hex", "Set of bits for rpc-get-daemon-info", "", true }; + const command_line::arg_descriptor arg_set_peer_log_level = { "set-peer-log-level", "Set log level for remote peer", 0, true }; + const command_line::arg_descriptor arg_download_peer_log = { "download-peer-log", "Download log from remote peer (starting offset)", 0, true }; } typedef COMMAND_REQUEST_STAT_INFO_T::stat_info> COMMAND_REQUEST_STAT_INFO; @@ -839,7 +841,166 @@ bool generate_and_print_keys() << "PRIVATE KEY: " << epee::string_tools::pod_to_hex(sk); return true; } +//--------------------------------------------------------------------------------------------------------------- +template +bool invoke_debug_command(po::variables_map& vm, const crypto::secret_key& sk, levin::levin_client_impl2& transport, peerid_type& peer_id, typename command_t::request& req, typename command_t::response& rsp) +{ + if (!transport.is_connected()) + { + if (!transport.connect(command_line::get_arg(vm, arg_ip), static_cast(command_line::get_arg(vm, arg_port)), static_cast(command_line::get_arg(vm, arg_timeout)))) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}" << ENDL; + return false; + } + } + if (!peer_id) + { + COMMAND_REQUEST_PEER_ID::request id_req = AUTO_VAL_INIT(id_req); + COMMAND_REQUEST_PEER_ID::response id_rsp = AUTO_VAL_INIT(id_rsp); + if (!net_utils::invoke_remote_command2(COMMAND_REQUEST_PEER_ID::ID, id_req, id_rsp, transport)) + { + std::cout << "{" << ENDL << " \"status\": \"ERROR: " << "Failed to connect to " << command_line::get_arg(vm, arg_ip) << ":" << command_line::get_arg(vm, arg_port) << "\"" << ENDL << "}" << ENDL; + return false; + } + else + { + peer_id = id_rsp.my_id; + } + } + + nodetool::proof_of_trust pot = AUTO_VAL_INIT(pot); + pot.peer_id = peer_id; + pot.time = time(NULL); + crypto::public_key pubk = AUTO_VAL_INIT(pubk); + string_tools::hex_to_pod(P2P_MAINTAINERS_PUB_KEY, pubk); + crypto::hash h = tools::get_proof_of_trust_hash(pot); + crypto::generate_signature(h, pubk, sk, pot.sign); + + req.tr = pot; + + return net_utils::invoke_remote_command2(command_t::ID, req, rsp, transport); +} + +//--------------------------------------------------------------------------------------------------------------- +bool handle_set_peer_log_level(po::variables_map& vm) +{ + crypto::secret_key sk = AUTO_VAL_INIT(sk); + if (!get_private_key(sk, vm)) + { + std::cout << "ERROR: secret key error" << ENDL; + return false; + } + + int64_t log_level = command_line::get_arg(vm, arg_set_peer_log_level); + if (log_level < LOG_LEVEL_0 || log_level > LOG_LEVEL_MAX) + { + std::cout << "Error: invalid log level value: " << log_level << ENDL; + return false; + } + + levin::levin_client_impl2 transport; + peerid_type peer_id = 0; + + COMMAND_SET_LOG_LEVEL::request req = AUTO_VAL_INIT(req); + req.new_log_level = log_level; + + COMMAND_SET_LOG_LEVEL::response rsp = AUTO_VAL_INIT(rsp); + if (!invoke_debug_command(vm, sk, transport, peer_id, req, rsp)) + { + std::cout << "ERROR: invoking COMMAND_SET_LOG_LEVEL failed" << ENDL; + return false; + } + + std::cout << "OK! Log level changed: " << rsp.old_log_level << " -> " << rsp.current_log_level << ENDL; + + return true; +} +//--------------------------------------------------------------------------------------------------------------- +bool handle_download_peer_log(po::variables_map& vm) +{ + crypto::secret_key sk = AUTO_VAL_INIT(sk); + if (!get_private_key(sk, vm)) + { + std::cout << "ERROR: secret key error" << ENDL; + return false; + } + + uint64_t start_offset = command_line::get_arg(vm, arg_download_peer_log); + + levin::levin_client_impl2 transport; + peerid_type peer_id = 0; + + COMMAND_REQUEST_LOG::request req = AUTO_VAL_INIT(req); + COMMAND_REQUEST_LOG::response rsp = AUTO_VAL_INIT(rsp); + if (!invoke_debug_command(vm, sk, transport, peer_id, req, rsp) || !rsp.error.empty()) + { + std::cout << "ERROR: invoking COMMAND_REQUEST_LOG failed: " << rsp.error << ENDL; + return false; + } + + std::cout << "Current log level: " << rsp.current_log_level << ENDL; + std::cout << "Current log size: " << rsp.current_log_size << ENDL; + + if (start_offset >= rsp.current_log_size) + { + std::cout << "ERROR: invalid start offset: " << start_offset << ", log size: " << rsp.current_log_size << ENDL; + return false; + } + + std::cout << "Downloading..." << ENDL; + + std::string local_filename = tools::get_default_data_dir() + "/log_" + epee::string_tools::num_to_string_fast(peer_id) + ".log"; + std::ofstream log{ local_filename, std::ifstream::binary }; + if (!log) + { + std::cout << "Couldn't open " << local_filename << " for writing." << ENDL; + return false; + } + + const uint64_t chunk_size = 1024 * 1024 * 5; + uint64_t end_offset = start_offset; + uint64_t bytes = 0; + while (true) + { + req.log_chunk_offset = end_offset; + req.log_chunk_size = std::min(chunk_size, rsp.current_log_size - req.log_chunk_offset); + if (req.log_chunk_size == 0) + break; + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + if (!invoke_debug_command(vm, sk, transport, peer_id, req, rsp) || !rsp.error.empty()) + { + std::cout << "ERROR: invoking COMMAND_REQUEST_LOG failed: " << rsp.error << ENDL; + return false; + } + + if (!epee::zlib_helper::unpack(rsp.log_chunk)) + { + std::cout << "ERROR: zip unpack failed" << ENDL; + return false; + } + + if (rsp.log_chunk.size() != req.log_chunk_size) + { + std::cout << "ERROR: unpacked size: " << rsp.log_chunk.size() << ", requested: " << req.log_chunk_size << ENDL; + return false; + } + + log.write(rsp.log_chunk.c_str(), rsp.log_chunk.size()); + + end_offset += req.log_chunk_size; + + std::cout << end_offset - start_offset << " bytes downloaded" << ENDL; + } + + std::cout << "Remote log from offset " << start_offset << " to offset " << end_offset << " (" << end_offset - start_offset << " bytes) " << + "was successfully downloaded to " << local_filename; + + return true; +} +//--------------------------------------------------------------------------------------------------------------- int main(int argc, char* argv[]) { @@ -874,7 +1035,9 @@ int main(int argc, char* argv[]) command_line::add_arg(desc_params, arg_genesis_split_amount); command_line::add_arg(desc_params, arg_get_info_flags); command_line::add_arg(desc_params, arg_log_journal_len); - + command_line::add_arg(desc_params, arg_set_peer_log_level); + command_line::add_arg(desc_params, arg_download_peer_log); + po::options_description desc_all; @@ -931,6 +1094,14 @@ int main(int argc, char* argv[]) { return generate_genesis(command_line::get_arg(vm, arg_generate_genesis), 10000000000000000) ? 0 : 1; } + else if (command_line::has_arg(vm, arg_set_peer_log_level)) + { + return handle_set_peer_log_level(vm) ? 0 : 1; + } + else if (command_line::has_arg(vm, arg_download_peer_log)) + { + return handle_download_peer_log(vm) ? 0 : 1; + } else { std::cerr << "Not enough arguments." << ENDL; diff --git a/src/p2p/net_node.h b/src/p2p/net_node.h index 6ae42925..b80d1639 100644 --- a/src/p2p/net_node.h +++ b/src/p2p/net_node.h @@ -124,6 +124,8 @@ namespace nodetool HANDLE_INVOKE_T2(COMMAND_REQUEST_STAT_INFO, &node_server::handle_get_stat_info) HANDLE_INVOKE_T2(COMMAND_REQUEST_NETWORK_STATE, &node_server::handle_get_network_state) HANDLE_INVOKE_T2(COMMAND_REQUEST_PEER_ID, &node_server::handle_get_peer_id) + HANDLE_INVOKE_T2(COMMAND_REQUEST_LOG, &node_server::handle_request_log) + HANDLE_INVOKE_T2(COMMAND_SET_LOG_LEVEL, &node_server::handle_set_log_level) #endif CHAIN_INVOKE_MAP_TO_OBJ_FORCE_CONTEXT(m_payload_handler, typename t_payload_net_handler::connection_context&) END_INVOKE_MAP2() @@ -137,6 +139,8 @@ namespace nodetool int handle_get_stat_info(int command, typename COMMAND_REQUEST_STAT_INFO::request& arg, typename COMMAND_REQUEST_STAT_INFO::response& rsp, p2p_connection_context& context); int handle_get_network_state(int command, COMMAND_REQUEST_NETWORK_STATE::request& arg, COMMAND_REQUEST_NETWORK_STATE::response& rsp, p2p_connection_context& context); int handle_get_peer_id(int command, COMMAND_REQUEST_PEER_ID::request& arg, COMMAND_REQUEST_PEER_ID::response& rsp, p2p_connection_context& context); + int handle_request_log(int command, COMMAND_REQUEST_LOG::request& arg, COMMAND_REQUEST_LOG::response& rsp, p2p_connection_context& context); + int handle_set_log_level(int command, COMMAND_SET_LOG_LEVEL::request& arg, COMMAND_SET_LOG_LEVEL::response& rsp, p2p_connection_context& context); private: #endif bool init_config(); diff --git a/src/p2p/net_node.inl b/src/p2p/net_node.inl index 0ba340e4..b4813725 100644 --- a/src/p2p/net_node.inl +++ b/src/p2p/net_node.inl @@ -1039,6 +1039,7 @@ namespace nodetool rsp.connections_count = m_net_server.get_config_object().get_connections_count(); rsp.incoming_connections_count = rsp.connections_count - get_outgoing_connections_count(); rsp.version = PROJECT_VERSION_LONG; + rsp.current_log_size = tools::get_log_file_size(); m_payload_handler.get_stat_info(arg.pr, rsp.payload_info); return 1; } @@ -1080,7 +1081,44 @@ namespace nodetool rsp.my_id = m_config.m_peer_id; return 1; } -#endif + //----------------------------------------------------------------------------------- + template + int node_server::handle_request_log(int command, COMMAND_REQUEST_LOG::request& req, COMMAND_REQUEST_LOG::response& rsp, p2p_connection_context& context) + { + if (!check_trust(req.tr)) + { + drop_connection(context); + return 1; + } + + rsp.current_log_level = static_cast(log_space::get_set_log_detalisation_level()); + tools::get_log_chunk_gzipped(req.log_chunk_offset, req.log_chunk_size, rsp.log_chunk, rsp.error); + rsp.current_log_size = tools::get_log_file_size(); + + return 1; + } + //----------------------------------------------------------------------------------- + template + int node_server::handle_set_log_level(int command, COMMAND_SET_LOG_LEVEL::request& req, COMMAND_SET_LOG_LEVEL::response& rsp, p2p_connection_context& context) + { + if (!check_trust(req.tr)) + { + drop_connection(context); + return 1; + } + + rsp.old_log_level = static_cast(log_space::get_set_log_detalisation_level()); + log_space::get_set_log_detalisation_level(true, static_cast(req.new_log_level)); + rsp.current_log_level = static_cast(log_space::get_set_log_detalisation_level()); + + if (rsp.old_log_level != rsp.current_log_level) + { + LOG_PRINT_CC(context, "log level changed by debug command: " << rsp.old_log_level << " -> " << rsp.current_log_level, LOG_LEVEL_0); + } + + return 1; + } +#endif // #ifdef ALLOW_DEBUG_COMMANDS //----------------------------------------------------------------------------------- template void node_server::request_callback(const epee::net_utils::connection_context_base& context) diff --git a/src/p2p/p2p_protocol_defs.h b/src/p2p/p2p_protocol_defs.h index ed245e39..fe1225f4 100644 --- a/src/p2p/p2p_protocol_defs.h +++ b/src/p2p/p2p_protocol_defs.h @@ -328,12 +328,14 @@ namespace nodetool std::string version; uint64_t connections_count; uint64_t incoming_connections_count; + uint64_t current_log_size; payload_stat_info payload_info; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(version) KV_SERIALIZE(connections_count) KV_SERIALIZE(incoming_connections_count) + KV_SERIALIZE(current_log_size) KV_SERIALIZE(payload_info) END_KV_SERIALIZE_MAP() }; @@ -397,7 +399,73 @@ namespace nodetool }; }; -#endif + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_REQUEST_LOG + { + const static int ID = P2P_COMMANDS_POOL_BASE + 7; + + struct request + { + proof_of_trust tr; + uint64_t log_chunk_offset; + uint64_t log_chunk_size; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + KV_SERIALIZE(log_chunk_offset) + KV_SERIALIZE(log_chunk_size) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + int64_t current_log_level; + uint64_t current_log_size; + std::string error; + std::string log_chunk; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(current_log_level) + KV_SERIALIZE(current_log_size) + KV_SERIALIZE(error) + KV_SERIALIZE(log_chunk) + END_KV_SERIALIZE_MAP() + }; + }; + + /************************************************************************/ + /* */ + /************************************************************************/ + struct COMMAND_SET_LOG_LEVEL + { + const static int ID = P2P_COMMANDS_POOL_BASE + 8; + + struct request + { + proof_of_trust tr; + int64_t new_log_level; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tr) + KV_SERIALIZE(new_log_level) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + int64_t old_log_level; + int64_t current_log_level; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(old_log_level) + KV_SERIALIZE(current_log_level) + END_KV_SERIALIZE_MAP() + }; + }; + +#endif // #ifdef ALLOW_DEBUG_COMMANDS }