123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335 |
- // Copyright (c) 2009-2010 Satoshi Nakamoto
- // Copyright (c) 2009-2015 The Bitcoin Core developers
- // Distributed under the MIT software license, see the accompanying
- // file COPYING or http://www.opensource.org/licenses/mit-license.php.
-
- #include "chainparamsbase.h"
- #include "clientversion.h"
- #include "rpcclient.h"
- #include "rpcprotocol.h"
- #include "util.h"
- #include "utilstrencodings.h"
-
- #include <boost/filesystem/operations.hpp>
- #include <stdio.h>
-
- #include <event2/event.h>
- #include <event2/http.h>
- #include <event2/buffer.h>
- #include <event2/keyvalq_struct.h>
-
- #include <univalue.h>
-
- using namespace std;
-
- static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
- static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
-
- std::string HelpMessageCli()
- {
- string strUsage;
- strUsage += HelpMessageGroup(_("Options:"));
- strUsage += HelpMessageOpt("-?", _("This help message"));
- strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
- strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
- AppendParamsHelpMessages(strUsage);
- strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
- strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
- strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
- strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
- strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
- strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
-
- return strUsage;
- }
-
- //////////////////////////////////////////////////////////////////////////////
- //
- // Start
- //
-
- //
- // Exception thrown on connection error. This error is used to determine
- // when to wait if -rpcwait is given.
- //
- class CConnectionFailed : public std::runtime_error
- {
- public:
-
- explicit inline CConnectionFailed(const std::string& msg) :
- std::runtime_error(msg)
- {}
-
- };
-
- static bool AppInitRPC(int argc, char* argv[])
- {
- //
- // Parameters
- //
- ParseParameters(argc, argv);
- if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
- std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
- if (!mapArgs.count("-version")) {
- strUsage += "\n" + _("Usage:") + "\n" +
- " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" +
- " bitcoin-cli [options] help " + _("List commands") + "\n" +
- " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n";
-
- strUsage += "\n" + HelpMessageCli();
- }
-
- fprintf(stdout, "%s", strUsage.c_str());
- return false;
- }
- if (!boost::filesystem::is_directory(GetDataDir(false))) {
- fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
- return false;
- }
- try {
- ReadConfigFile(mapArgs, mapMultiArgs);
- } catch (const std::exception& e) {
- fprintf(stderr,"Error reading configuration file: %s\n", e.what());
- return false;
- }
- // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
- try {
- SelectBaseParams(ChainNameFromCommandLine());
- } catch (const std::exception& e) {
- fprintf(stderr, "Error: %s\n", e.what());
- return false;
- }
- if (GetBoolArg("-rpcssl", false))
- {
- fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
- return false;
- }
- return true;
- }
-
-
- /** Reply structure for request_done to fill in */
- struct HTTPReply
- {
- int status;
- std::string body;
- };
-
- static void http_request_done(struct evhttp_request *req, void *ctx)
- {
- HTTPReply *reply = static_cast<HTTPReply*>(ctx);
-
- if (req == NULL) {
- /* If req is NULL, it means an error occurred while connecting, but
- * I'm not sure how to find out which one. We also don't really care.
- */
- reply->status = 0;
- return;
- }
-
- reply->status = evhttp_request_get_response_code(req);
-
- struct evbuffer *buf = evhttp_request_get_input_buffer(req);
- if (buf)
- {
- size_t size = evbuffer_get_length(buf);
- const char *data = (const char*)evbuffer_pullup(buf, size);
- if (data)
- reply->body = std::string(data, size);
- evbuffer_drain(buf, size);
- }
- }
-
- UniValue CallRPC(const string& strMethod, const UniValue& params)
- {
- std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
- int port = GetArg("-rpcport", BaseParams().RPCPort());
-
- // Create event base
- struct event_base *base = event_base_new(); // TODO RAII
- if (!base)
- throw runtime_error("cannot create event_base");
-
- // Synchronously look up hostname
- struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
- if (evcon == NULL)
- throw runtime_error("create connection failed");
- evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
-
- HTTPReply response;
- struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
- if (req == NULL)
- throw runtime_error("create http request failed");
-
- // Get credentials
- std::string strRPCUserColonPass;
- if (mapArgs["-rpcpassword"] == "") {
- // Try fall back to cookie-based authentication if no password is provided
- if (!GetAuthCookie(&strRPCUserColonPass)) {
- throw runtime_error(strprintf(
- _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
- GetConfigFile().string().c_str()));
-
- }
- } else {
- strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
- }
-
- struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
- assert(output_headers);
- evhttp_add_header(output_headers, "Host", host.c_str());
- evhttp_add_header(output_headers, "Connection", "close");
- evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
-
- // Attach request data
- std::string strRequest = JSONRPCRequest(strMethod, params, 1);
- struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
- assert(output_buffer);
- evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
-
- int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
- if (r != 0) {
- evhttp_connection_free(evcon);
- event_base_free(base);
- throw CConnectionFailed("send http request failed");
- }
-
- event_base_dispatch(base);
- evhttp_connection_free(evcon);
- event_base_free(base);
-
- if (response.status == 0)
- throw CConnectionFailed("couldn't connect to server");
- else if (response.status == HTTP_UNAUTHORIZED)
- throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
- else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
- throw runtime_error(strprintf("server returned HTTP error %d", response.status));
- else if (response.body.empty())
- throw runtime_error("no response from server");
-
- // Parse reply
- UniValue valReply(UniValue::VSTR);
- if (!valReply.read(response.body))
- throw runtime_error("couldn't parse reply from server");
- const UniValue& reply = valReply.get_obj();
- if (reply.empty())
- throw runtime_error("expected reply to have result, error and id properties");
-
- return reply;
- }
-
- int CommandLineRPC(int argc, char *argv[])
- {
- string strPrint;
- int nRet = 0;
- try {
- // Skip switches
- while (argc > 1 && IsSwitchChar(argv[1][0])) {
- argc--;
- argv++;
- }
-
- // Method
- if (argc < 2)
- throw runtime_error("too few parameters");
- string strMethod = argv[1];
-
- // Parameters default to strings
- std::vector<std::string> strParams(&argv[2], &argv[argc]);
- UniValue params = RPCConvertValues(strMethod, strParams);
-
- // Execute and handle connection failures with -rpcwait
- const bool fWait = GetBoolArg("-rpcwait", false);
- do {
- try {
- const UniValue reply = CallRPC(strMethod, params);
-
- // Parse reply
- const UniValue& result = find_value(reply, "result");
- const UniValue& error = find_value(reply, "error");
-
- if (!error.isNull()) {
- // Error
- int code = error["code"].get_int();
- if (fWait && code == RPC_IN_WARMUP)
- throw CConnectionFailed("server in warmup");
- strPrint = "error: " + error.write();
- nRet = abs(code);
- if (error.isObject())
- {
- UniValue errCode = find_value(error, "code");
- UniValue errMsg = find_value(error, "message");
- strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
-
- if (errMsg.isStr())
- strPrint += "error message:\n"+errMsg.get_str();
- }
- } else {
- // Result
- if (result.isNull())
- strPrint = "";
- else if (result.isStr())
- strPrint = result.get_str();
- else
- strPrint = result.write(2);
- }
- // Connection succeeded, no need to retry.
- break;
- }
- catch (const CConnectionFailed&) {
- if (fWait)
- MilliSleep(1000);
- else
- throw;
- }
- } while (fWait);
- }
- catch (const boost::thread_interrupted&) {
- throw;
- }
- catch (const std::exception& e) {
- strPrint = string("error: ") + e.what();
- nRet = EXIT_FAILURE;
- }
- catch (...) {
- PrintExceptionContinue(NULL, "CommandLineRPC()");
- throw;
- }
-
- if (strPrint != "") {
- fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
- }
- return nRet;
- }
-
- int main(int argc, char* argv[])
- {
- SetupEnvironment();
- if (!SetupNetworking()) {
- fprintf(stderr, "Error: Initializing networking failed\n");
- exit(1);
- }
-
- try {
- if(!AppInitRPC(argc, argv))
- return EXIT_FAILURE;
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "AppInitRPC()");
- return EXIT_FAILURE;
- } catch (...) {
- PrintExceptionContinue(NULL, "AppInitRPC()");
- return EXIT_FAILURE;
- }
-
- int ret = EXIT_FAILURE;
- try {
- ret = CommandLineRPC(argc, argv);
- }
- catch (const std::exception& e) {
- PrintExceptionContinue(&e, "CommandLineRPC()");
- } catch (...) {
- PrintExceptionContinue(NULL, "CommandLineRPC()");
- }
- return ret;
- }
|