diff --git a/src/daemon/command_parser_executor.cpp b/src/daemon/command_parser_executor.cpp index 8daaac611..4e6e83eb5 100644 --- a/src/daemon/command_parser_executor.cpp +++ b/src/daemon/command_parser_executor.cpp @@ -552,4 +552,15 @@ bool t_command_parser_executor::print_blockchain_dynamic_stats(const std::vector return m_executor.print_blockchain_dynamic_stats(nblocks); } +bool t_command_parser_executor::update(const std::vector& args) +{ + if(args.size() != 1) + { + std::cout << "Exactly one parameter is needed: check, download, or update" << std::endl; + return false; + } + + return m_executor.update(args.front()); +} + } // namespace daemonize diff --git a/src/daemon/command_parser_executor.h b/src/daemon/command_parser_executor.h index 37f431974..0a5ca8dd9 100644 --- a/src/daemon/command_parser_executor.h +++ b/src/daemon/command_parser_executor.h @@ -130,6 +130,8 @@ public: bool alt_chain_info(const std::vector& args); bool print_blockchain_dynamic_stats(const std::vector& args); + + bool update(const std::vector& args); }; } // namespace daemonize diff --git a/src/daemon/command_server.cpp b/src/daemon/command_server.cpp index 4a063268f..0081e00dc 100644 --- a/src/daemon/command_server.cpp +++ b/src/daemon/command_server.cpp @@ -243,6 +243,11 @@ t_command_server::t_command_server( , std::bind(&t_command_parser_executor::print_blockchain_dynamic_stats, &m_parser, p::_1) , "Print information about current blockchain dynamic state" ); + m_command_lookup.set_handler( + "update" + , std::bind(&t_command_parser_executor::update, &m_parser, p::_1) + , "subcommands: check (check if an update is available), download (download it is there is), update (not implemented)" + ); } bool t_command_server::process_command_str(const std::string& cmd) diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index b33736839..68b29d93a 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -1580,4 +1580,51 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) return true; } +bool t_rpc_command_executor::update(const std::string &command) +{ + cryptonote::COMMAND_RPC_UPDATE::request req; + cryptonote::COMMAND_RPC_UPDATE::response res; + epee::json_rpc::error error_resp; + + std::string fail_message = "Problem fetching info"; + + req.command = command; + if (m_is_rpc) + { + if (!m_rpc_client->rpc_request(req, res, "/update", fail_message.c_str())) + { + return true; + } + } + else + { + if (!m_rpc_server->on_update(req, res) || res.status != CORE_RPC_STATUS_OK) + { + tools::fail_msg_writer() << fail_message.c_str(); + return true; + } + } + + if (!res.update) + { + tools::msg_writer() << "No update available"; + return true; + } + + tools::msg_writer() << "Update available: v" << res.version << ": " << res.user_uri << ", hash " << res.hash; + if (command == "check") + return true; + + if (!res.path.empty()) + tools::msg_writer() << "Update downloaded to: " << res.path; + else + tools::msg_writer() << "Update download failed: " << res.status; + if (command == "download") + return true; + + tools::msg_writer() << "'update' not implemented yet"; + + return true; +} + }// namespace daemonize diff --git a/src/daemon/rpc_command_executor.h b/src/daemon/rpc_command_executor.h index eb95101ce..a015c83ea 100644 --- a/src/daemon/rpc_command_executor.h +++ b/src/daemon/rpc_command_executor.h @@ -151,6 +151,8 @@ public: bool alt_chain_info(); bool print_blockchain_dynamic_stats(uint64_t nblocks); + + bool update(const std::string &command); }; } // namespace daemonize diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index d79621de4..a01125120 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -33,6 +33,9 @@ using namespace epee; #include "core_rpc_server.h" #include "common/command_line.h" +#include "common/updates.h" +#include "common/download.h" +#include "common/util.h" #include "cryptonote_basic/cryptonote_format_utils.h" #include "cryptonote_basic/account.h" #include "cryptonote_basic/cryptonote_basic_impl.h" @@ -1455,6 +1458,98 @@ namespace cryptonote return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool core_rpc_server::on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res) + { + static const char software[] = "monero"; +#ifdef BUILDTAG + static const char buildtag[] = BOOST_PP_STRINGIZE(BUILDTAG); +#else + static const char buildtag[] = "source"; +#endif + + if (req.command != "check" && req.command != "download" && req.command != "update") + { + res.status = std::string("unknown command: '") + req.command + "'"; + return true; + } + + std::string version, hash; + if (!tools::check_updates(software, buildtag, version, hash)) + { + res.status = "Error checking for updates"; + return true; + } + if (tools::vercmp(version.c_str(), MONERO_VERSION) <= 0) + { + res.update = false; + res.status = CORE_RPC_STATUS_OK; + return true; + } + res.update = true; + res.version = version; + res.user_uri = tools::get_update_url(software, "cli", buildtag, version, true); + res.auto_uri = tools::get_update_url(software, "cli", buildtag, version, false); + res.hash = hash; + if (req.command == "check") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + boost::filesystem::path path; + if (req.path.empty()) + { + std::string filename; + const char *slash = strrchr(res.auto_uri.c_str(), '/'); + if (slash) + filename = slash + 1; + else + filename = std::string(software) + "-update-" + version; + path = epee::string_tools::get_current_module_folder(); + path /= filename; + } + else + { + path = req.path; + } + + crypto::hash file_hash; + if (!tools::sha256sum(path.string(), file_hash) || (hash != epee::string_tools::pod_to_hex(file_hash))) + { + MDEBUG("We don't have that file already, downloading"); + if (!tools::download(path.string(), res.auto_uri)) + { + MERROR("Failed to download " << res.auto_uri); + return false; + } + if (!tools::sha256sum(path.string(), file_hash)) + { + MERROR("Failed to hash " << path); + return false; + } + if (hash != epee::string_tools::pod_to_hex(file_hash)) + { + MERROR("Download from " << res.auto_uri << " does not match the expected hash"); + return false; + } + MINFO("New version downloaded to " << path); + } + else + { + MDEBUG("We already have " << path << " with expected hash"); + } + res.path = path.string(); + + if (req.command == "download") + { + res.status = CORE_RPC_STATUS_OK; + return true; + } + + res.status = "'update' not implemented yet"; + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ const command_line::arg_descriptor core_rpc_server::arg_rpc_bind_port = { "rpc-bind-port" diff --git a/src/rpc/core_rpc_server.h b/src/rpc/core_rpc_server.h index 00db47c2d..900f473a9 100644 --- a/src/rpc/core_rpc_server.h +++ b/src/rpc/core_rpc_server.h @@ -98,6 +98,7 @@ namespace cryptonote MAP_URI_AUTO_JON2_IF("/start_save_graph", on_start_save_graph, COMMAND_RPC_START_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2_IF("/stop_save_graph", on_stop_save_graph, COMMAND_RPC_STOP_SAVE_GRAPH, !m_restricted) MAP_URI_AUTO_JON2("/get_outs", on_get_outs, COMMAND_RPC_GET_OUTPUTS) + MAP_URI_AUTO_JON2_IF("/update", on_update, COMMAND_RPC_UPDATE, !m_restricted) BEGIN_JSON_RPC_MAP("/json_rpc") MAP_JON_RPC("getblockcount", on_getblockcount, COMMAND_RPC_GETBLOCKCOUNT) MAP_JON_RPC_WE("on_getblockhash", on_getblockhash, COMMAND_RPC_GETBLOCKHASH) @@ -148,6 +149,7 @@ namespace cryptonote bool on_out_peers(const COMMAND_RPC_OUT_PEERS::request& req, COMMAND_RPC_OUT_PEERS::response& res); bool on_start_save_graph(const COMMAND_RPC_START_SAVE_GRAPH::request& req, COMMAND_RPC_START_SAVE_GRAPH::response& res); bool on_stop_save_graph(const COMMAND_RPC_STOP_SAVE_GRAPH::request& req, COMMAND_RPC_STOP_SAVE_GRAPH::response& res); + bool on_update(const COMMAND_RPC_UPDATE::request& req, COMMAND_RPC_UPDATE::response& res); //json_rpc bool on_getblockcount(const COMMAND_RPC_GETBLOCKCOUNT::request& req, COMMAND_RPC_GETBLOCKCOUNT::response& res); diff --git a/src/rpc/core_rpc_server_commands_defs.h b/src/rpc/core_rpc_server_commands_defs.h index 9f15da82a..47fa10d85 100644 --- a/src/rpc/core_rpc_server_commands_defs.h +++ b/src/rpc/core_rpc_server_commands_defs.h @@ -1438,4 +1438,39 @@ namespace cryptonote END_KV_SERIALIZE_MAP() }; }; + + struct COMMAND_RPC_UPDATE + { + struct request + { + std::string command; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(command); + KV_SERIALIZE(path); + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + bool update; + std::string version; + std::string user_uri; + std::string auto_uri; + std::string hash; + std::string path; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) + KV_SERIALIZE(update) + KV_SERIALIZE(version) + KV_SERIALIZE(user_uri) + KV_SERIALIZE(auto_uri) + KV_SERIALIZE(hash) + KV_SERIALIZE(path) + END_KV_SERIALIZE_MAP() + }; + }; }