LCOV - code coverage report
Current view: top level - src - bitcoin-cli.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 95 170 55.9 %
Date: 2015-10-12 22:39:14 Functions: 8 12 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2009-2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2013 The Bitcoin Core developers
       3             : // Distributed under the MIT software license, see the accompanying
       4             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       5             : 
       6             : #include "chainparamsbase.h"
       7             : #include "clientversion.h"
       8             : #include "rpcclient.h"
       9             : #include "rpcprotocol.h"
      10             : #include "util.h"
      11             : #include "utilstrencodings.h"
      12             : 
      13             : #include <boost/filesystem/operations.hpp>
      14             : #include <stdio.h>
      15             : 
      16             : #include <event2/event.h>
      17             : #include <event2/http.h>
      18             : #include <event2/buffer.h>
      19             : #include <event2/keyvalq_struct.h>
      20             : 
      21             : #include <univalue.h>
      22             : 
      23             : using namespace std;
      24             : 
      25             : static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
      26             : 
      27           0 : std::string HelpMessageCli()
      28             : {
      29             :     string strUsage;
      30           0 :     strUsage += HelpMessageGroup(_("Options:"));
      31           0 :     strUsage += HelpMessageOpt("-?", _("This help message"));
      32           0 :     strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
      33           0 :     strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
      34           0 :     strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
      35           0 :     strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be "
      36             :                                              "solved instantly. This is intended for regression testing tools and app development."));
      37           0 :     strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
      38           0 :     strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8332, 18332));
      39           0 :     strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
      40           0 :     strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
      41           0 :     strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
      42           0 :     strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
      43             : 
      44           0 :     return strUsage;
      45             : }
      46             : 
      47             : //////////////////////////////////////////////////////////////////////////////
      48             : //
      49             : // Start
      50             : //
      51             : 
      52             : //
      53             : // Exception thrown on connection error.  This error is used to determine
      54             : // when to wait if -rpcwait is given.
      55             : //
      56          93 : class CConnectionFailed : public std::runtime_error
      57             : {
      58             : public:
      59             : 
      60           0 :     explicit inline CConnectionFailed(const std::string& msg) :
      61          93 :         std::runtime_error(msg)
      62           0 :     {}
      63             : 
      64             : };
      65             : 
      66          93 : static bool AppInitRPC(int argc, char* argv[])
      67             : {
      68             :     //
      69             :     // Parameters
      70             :     //
      71          93 :     ParseParameters(argc, argv);
      72         930 :     if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
      73           0 :         std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
      74           0 :         if (!mapArgs.count("-version")) {
      75           0 :             strUsage += "\n" + _("Usage:") + "\n" +
      76           0 :                   "  bitcoin-cli [options] <command> [params]  " + _("Send command to Bitcoin Core") + "\n" +
      77           0 :                   "  bitcoin-cli [options] help                " + _("List commands") + "\n" +
      78           0 :                   "  bitcoin-cli [options] help <command>      " + _("Get help for a command") + "\n";
      79             : 
      80           0 :             strUsage += "\n" + HelpMessageCli();
      81             :         }
      82             : 
      83           0 :         fprintf(stdout, "%s", strUsage.c_str());
      84           0 :         return false;
      85             :     }
      86         186 :     if (!boost::filesystem::is_directory(GetDataDir(false))) {
      87           0 :         fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
      88           0 :         return false;
      89             :     }
      90             :     try {
      91          93 :         ReadConfigFile(mapArgs, mapMultiArgs);
      92           0 :     } catch (const std::exception& e) {
      93           0 :         fprintf(stderr,"Error reading configuration file: %s\n", e.what());
      94             :         return false;
      95             :     }
      96             :     // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
      97          93 :     if (!SelectBaseParamsFromCommandLine()) {
      98           0 :         fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
      99           0 :         return false;
     100             :     }
     101         279 :     if (GetBoolArg("-rpcssl", false))
     102             :     {
     103           0 :         fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
     104           0 :         return false;
     105             :     }
     106             :     return true;
     107             : }
     108             : 
     109             : 
     110             : /** Reply structure for request_done to fill in */
     111         558 : struct HTTPReply
     112             : {
     113             :     int status;
     114             :     std::string body;
     115             : };
     116             : 
     117         186 : static void http_request_done(struct evhttp_request *req, void *ctx)
     118             : {
     119         186 :     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
     120             : 
     121         186 :     if (req == NULL) {
     122             :         /* If req is NULL, it means an error occurred while connecting, but
     123             :          * I'm not sure how to find out which one. We also don't really care.
     124             :          */
     125           0 :         reply->status = 0;
     126         186 :         return;
     127             :     }
     128             : 
     129         186 :     reply->status = evhttp_request_get_response_code(req);
     130             : 
     131         186 :     struct evbuffer *buf = evhttp_request_get_input_buffer(req);
     132         186 :     if (buf)
     133             :     {
     134         186 :         size_t size = evbuffer_get_length(buf);
     135         186 :         const char *data = (const char*)evbuffer_pullup(buf, size);
     136         186 :         if (data)
     137         282 :             reply->body = std::string(data, size);
     138         186 :         evbuffer_drain(buf, size);
     139             :     }
     140             : }
     141             : 
     142         186 : UniValue CallRPC(const string& strMethod, const UniValue& params)
     143             : {
     144         930 :     std::string host = GetArg("-rpcconnect", "127.0.0.1");
     145         650 :     int port = GetArg("-rpcport", BaseParams().RPCPort());
     146             : 
     147             :     // Create event base
     148         186 :     struct event_base *base = event_base_new(); // TODO RAII
     149         186 :     if (!base)
     150           0 :         throw runtime_error("cannot create event_base");
     151             : 
     152             :     // Synchronously look up hostname
     153         372 :     struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
     154         186 :     if (evcon == NULL)
     155           0 :         throw runtime_error("create connection failed");
     156         650 :     evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
     157             : 
     158             :     HTTPReply response;
     159         186 :     struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
     160         186 :     if (req == NULL)
     161           0 :         throw runtime_error("create http request failed");
     162             : 
     163             :     // Get credentials
     164             :     std::string strRPCUserColonPass;
     165         836 :     if (mapArgs["-rpcpassword"] == "") {
     166             :         // Try fall back to cookie-based authentication if no password is provided
     167           0 :         if (!GetAuthCookie(&strRPCUserColonPass)) {
     168             :             throw runtime_error(strprintf(
     169             :                 _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
     170           0 :                     GetConfigFile().string().c_str()));
     171             : 
     172             :         }
     173             :     } else {
     174        1394 :         strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
     175             :     }
     176             : 
     177         186 :     struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
     178         186 :     assert(output_headers);
     179         186 :     evhttp_add_header(output_headers, "Host", host.c_str());
     180         186 :     evhttp_add_header(output_headers, "Connection", "close");
     181        1116 :     evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
     182             : 
     183             :     // Attach request data
     184         186 :     std::string strRequest = JSONRPCRequest(strMethod, params, 1);
     185         186 :     struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
     186         186 :     assert(output_buffer);
     187         186 :     evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
     188             : 
     189         186 :     int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
     190         186 :     if (r != 0) {
     191           0 :         evhttp_connection_free(evcon);
     192           0 :         event_base_free(base);
     193           0 :         throw CConnectionFailed("send http request failed");
     194             :     }
     195             : 
     196         186 :     event_base_dispatch(base);
     197         186 :     evhttp_connection_free(evcon);
     198         186 :     event_base_free(base);
     199             : 
     200         186 :     if (response.status == 0)
     201         276 :         throw CConnectionFailed("couldn't connect to server");
     202          94 :     else if (response.status == HTTP_UNAUTHORIZED)
     203           0 :         throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
     204          94 :     else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
     205           0 :         throw runtime_error(strprintf("server returned HTTP error %d", response.status));
     206          94 :     else if (response.body.empty())
     207           0 :         throw runtime_error("no response from server");
     208             : 
     209             :     // Parse reply
     210         468 :     UniValue valReply(UniValue::VSTR);
     211          94 :     if (!valReply.read(response.body))
     212           0 :         throw runtime_error("couldn't parse reply from server");
     213          94 :     const UniValue& reply = valReply.get_obj();
     214          94 :     if (reply.empty())
     215           0 :         throw runtime_error("expected reply to have result, error and id properties");
     216             : 
     217         188 :     return reply;
     218             : }
     219             : 
     220          93 : int CommandLineRPC(int argc, char *argv[])
     221             : {
     222             :     string strPrint;
     223          93 :     int nRet = 0;
     224             :     try {
     225             :         // Skip switches
     226         372 :         while (argc > 1 && IsSwitchChar(argv[1][0])) {
     227         186 :             argc--;
     228         186 :             argv++;
     229             :         }
     230             : 
     231             :         // Method
     232          93 :         if (argc < 2)
     233           0 :             throw runtime_error("too few parameters");
     234         186 :         string strMethod = argv[1];
     235             : 
     236             :         // Parameters default to strings
     237         279 :         std::vector<std::string> strParams(&argv[2], &argv[argc]);
     238         186 :         UniValue params = RPCConvertValues(strMethod, strParams);
     239             : 
     240             :         // Execute and handle connection failures with -rpcwait
     241         279 :         const bool fWait = GetBoolArg("-rpcwait", false);
     242          93 :         do {
     243             :             try {
     244         186 :                 const UniValue reply = CallRPC(strMethod, params);
     245             : 
     246             :                 // Parse reply
     247         283 :                 const UniValue& result = find_value(reply, "result");
     248         283 :                 const UniValue& error  = find_value(reply, "error");
     249             : 
     250          94 :                 if (!error.isNull()) {
     251             :                     // Error
     252           4 :                     int code = error["code"].get_int();
     253           1 :                     if (fWait && code == RPC_IN_WARMUP)
     254           3 :                         throw CConnectionFailed("server in warmup");
     255           0 :                     strPrint = "error: " + error.write();
     256           0 :                     nRet = abs(code);
     257           0 :                     if (error.isObject())
     258             :                     {
     259           1 :                         UniValue errCode = find_value(error, "code");
     260           0 :                         UniValue errMsg  = find_value(error, "message");
     261           0 :                         strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
     262             : 
     263           0 :                         if (errMsg.isStr())
     264           0 :                             strPrint += "error message:\n"+errMsg.get_str();
     265             :                     }
     266             :                 } else {
     267             :                     // Result
     268          93 :                     if (result.isNull())
     269             :                         strPrint = "";
     270          93 :                     else if (result.isStr())
     271           0 :                         strPrint = result.get_str();
     272             :                     else
     273         186 :                         strPrint = result.write(2);
     274             :                 }
     275             :                 // Connection succeeded, no need to retry.
     276          94 :                 break;
     277             :             }
     278          93 :             catch (const CConnectionFailed&) {
     279          93 :                 if (fWait)
     280          93 :                     MilliSleep(1000);
     281             :                 else
     282           0 :                     throw;
     283             :             }
     284             :         } while (fWait);
     285             :     }
     286           0 :     catch (const boost::thread_interrupted&) {
     287           0 :         throw;
     288             :     }
     289           0 :     catch (const std::exception& e) {
     290           0 :         strPrint = string("error: ") + e.what();
     291           0 :         nRet = EXIT_FAILURE;
     292             :     }
     293           0 :     catch (...) {
     294           0 :         PrintExceptionContinue(NULL, "CommandLineRPC()");
     295           0 :         throw;
     296             :     }
     297             : 
     298          93 :     if (strPrint != "") {
     299          93 :         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
     300             :     }
     301         186 :     return nRet;
     302             : }
     303             : 
     304          93 : int main(int argc, char* argv[])
     305             : {
     306          93 :     SetupEnvironment();
     307          93 :     if (!SetupNetworking()) {
     308           0 :         fprintf(stderr, "Error: Initializing networking failed\n");
     309           0 :         exit(1);
     310             :     }
     311             : 
     312             :     try {
     313          93 :         if(!AppInitRPC(argc, argv))
     314             :             return EXIT_FAILURE;
     315             :     }
     316           0 :     catch (const std::exception& e) {
     317           0 :         PrintExceptionContinue(&e, "AppInitRPC()");
     318             :         return EXIT_FAILURE;
     319           0 :     } catch (...) {
     320           0 :         PrintExceptionContinue(NULL, "AppInitRPC()");
     321             :         return EXIT_FAILURE;
     322             :     }
     323             : 
     324          93 :     int ret = EXIT_FAILURE;
     325             :     try {
     326          93 :         ret = CommandLineRPC(argc, argv);
     327             :     }
     328           0 :     catch (const std::exception& e) {
     329           0 :         PrintExceptionContinue(&e, "CommandLineRPC()");
     330           0 :     } catch (...) {
     331           0 :         PrintExceptionContinue(NULL, "CommandLineRPC()");
     332             :     }
     333          93 :     return ret;
     334         279 : }

Generated by: LCOV version 1.11