LCOV - code coverage report
Current view: top level - src/rpc - server.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 288 306 94.1 %
Date: 2020-09-26 01:30:44 Functions: 61 63 96.8 %

          Line data    Source code
       1             : // Copyright (c) 2010 Satoshi Nakamoto
       2             : // Copyright (c) 2009-2020 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 <rpc/server.h>
       7             : 
       8             : #include <rpc/util.h>
       9             : #include <shutdown.h>
      10             : #include <sync.h>
      11             : #include <util/strencodings.h>
      12             : #include <util/system.h>
      13             : 
      14             : #include <boost/algorithm/string/classification.hpp>
      15             : #include <boost/algorithm/string/split.hpp>
      16             : #include <boost/signals2/signal.hpp>
      17             : 
      18             : #include <cassert>
      19             : #include <memory> // for unique_ptr
      20             : #include <mutex>
      21             : #include <unordered_map>
      22             : 
      23         640 : static Mutex g_rpc_warmup_mutex;
      24             : static std::atomic<bool> g_rpc_running{false};
      25             : static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex) = true;
      26         640 : static std::string rpcWarmupStatus GUARDED_BY(g_rpc_warmup_mutex) = "RPC server started";
      27             : /* Timer-creating functions */
      28             : static RPCTimerInterface* timerInterface = nullptr;
      29             : /* Map of name to timer. */
      30         640 : static Mutex g_deadline_timers_mutex;
      31         640 : static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers GUARDED_BY(g_deadline_timers_mutex);
      32             : static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
      33             : 
      34      470712 : struct RPCCommandExecutionInfo
      35             : {
      36             :     std::string method;
      37             :     int64_t start;
      38             : };
      39             : 
      40        2560 : struct RPCServerInfo
      41             : {
      42             :     Mutex mutex;
      43             :     std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
      44             : };
      45             : 
      46         640 : static RPCServerInfo g_rpc_server_info;
      47             : 
      48             : struct RPCCommandExecution
      49             : {
      50             :     std::list<RPCCommandExecutionInfo>::iterator it;
      51      156904 :     explicit RPCCommandExecution(const std::string& method)
      52       78452 :     {
      53       78452 :         LOCK(g_rpc_server_info.mutex);
      54       78452 :         it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, GetTimeMicros()});
      55      156904 :     }
      56      156904 :     ~RPCCommandExecution()
      57       78452 :     {
      58       78452 :         LOCK(g_rpc_server_info.mutex);
      59       78452 :         g_rpc_server_info.active_commands.erase(it);
      60      156904 :     }
      61             : };
      62             : 
      63        2560 : static struct CRPCSignals
      64             : {
      65             :     boost::signals2::signal<void ()> Started;
      66             :     boost::signals2::signal<void ()> Stopped;
      67         640 : } g_rpcSignals;
      68             : 
      69         526 : void RPCServer::OnStarted(std::function<void ()> slot)
      70             : {
      71         526 :     g_rpcSignals.Started.connect(slot);
      72         526 : }
      73             : 
      74         526 : void RPCServer::OnStopped(std::function<void ()> slot)
      75             : {
      76         526 :     g_rpcSignals.Stopped.connect(slot);
      77         526 : }
      78             : 
      79         146 : std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
      80             : {
      81         146 :     std::string strRet;
      82         146 :     std::string category;
      83         146 :     std::set<intptr_t> setDone;
      84         146 :     std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
      85             : 
      86       22338 :     for (const auto& entry : mapCommands)
      87       22192 :         vCommands.push_back(make_pair(entry.second.front()->category + entry.first, entry.second.front()));
      88         146 :     sort(vCommands.begin(), vCommands.end());
      89             : 
      90         146 :     JSONRPCRequest jreq(helpreq);
      91         146 :     jreq.fHelp = true;
      92         146 :     jreq.params = UniValue();
      93             : 
      94       22338 :     for (const std::pair<std::string, const CRPCCommand*>& command : vCommands)
      95             :     {
      96       22192 :         const CRPCCommand *pcmd = command.second;
      97       22192 :         std::string strMethod = pcmd->name;
      98       22192 :         if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
      99       21636 :             continue;
     100         556 :         jreq.strMethod = strMethod;
     101             :         try
     102             :         {
     103         556 :             UniValue unused_result;
     104         556 :             if (setDone.insert(pcmd->unique_id).second)
     105         556 :                 pcmd->actor(jreq, unused_result, true /* last_handler */);
     106         556 :         }
     107             :         catch (const std::exception& e)
     108             :         {
     109             :             // Help text is returned in an exception
     110         556 :             std::string strHelp = std::string(e.what());
     111         556 :             if (strCommand == "")
     112             :             {
     113         414 :                 if (strHelp.find('\n') != std::string::npos)
     114         414 :                     strHelp = strHelp.substr(0, strHelp.find('\n'));
     115             : 
     116         414 :                 if (category != pcmd->category)
     117             :                 {
     118          27 :                     if (!category.empty())
     119          24 :                         strRet += "\n";
     120          27 :                     category = pcmd->category;
     121          27 :                     strRet += "== " + Capitalize(category) + " ==\n";
     122          27 :                 }
     123             :             }
     124         556 :             strRet += strHelp + "\n";
     125         556 :         }
     126       22192 :     }
     127         146 :     if (strRet == "")
     128           1 :         strRet = strprintf("help: unknown command: %s\n", strCommand);
     129         146 :     strRet = strRet.substr(0,strRet.size()-1);
     130             :     return strRet;
     131         702 : }
     132             : 
     133        2712 : static RPCHelpMan help()
     134             : {
     135        5424 :     return RPCHelpMan{"help",
     136        2712 :                 "\nList all commands, or get help for a specified command.\n",
     137        5424 :                 {
     138        2712 :                     {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
     139             :                 },
     140        2712 :                 RPCResult{
     141        2712 :                     RPCResult::Type::STR, "", "The help text"
     142             :                 },
     143        2712 :                 RPCExamples{""},
     144        2859 :         [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
     145             : {
     146         147 :     std::string strCommand;
     147         147 :     if (jsonRequest.params.size() > 0)
     148         144 :         strCommand = jsonRequest.params[0].get_str();
     149             : 
     150         146 :     return tableRPC.help(strCommand, jsonRequest);
     151         147 : },
     152             :     };
     153           0 : }
     154             : 
     155        3054 : static RPCHelpMan stop()
     156             : {
     157        3054 :     static const std::string RESULT{PACKAGE_NAME " stopping"};
     158        6108 :     return RPCHelpMan{"stop",
     159             :     // Also accept the hidden 'wait' integer argument (milliseconds)
     160             :     // For instance, 'stop 1000' makes the call wait 1 second before returning
     161             :     // to the client (intended for testing)
     162        3054 :                 "\nRequest a graceful shutdown of " PACKAGE_NAME ".",
     163        6108 :                 {
     164        3054 :                     {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /* hidden */ true},
     165             :                 },
     166        3054 :                 RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
     167        3054 :                 RPCExamples{""},
     168        3544 :         [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
     169             : {
     170             :     // Event loop will exit after current HTTP requests have been handled, so
     171             :     // this reply will get back to the client.
     172         490 :     StartShutdown();
     173         490 :     if (jsonRequest.params[0].isNum()) {
     174         490 :         UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
     175         490 :     }
     176         490 :     return RESULT;
     177             : },
     178             :     };
     179           0 : }
     180             : 
     181        2565 : static RPCHelpMan uptime()
     182             : {
     183        5130 :     return RPCHelpMan{"uptime",
     184        2565 :                 "\nReturns the total uptime of the server.\n",
     185        2565 :                             {},
     186        2565 :                             RPCResult{
     187        2565 :                                 RPCResult::Type::NUM, "", "The number of seconds that the server has been running"
     188             :                             },
     189        2565 :                 RPCExamples{
     190        2565 :                     HelpExampleCli("uptime", "")
     191        2565 :                 + HelpExampleRpc("uptime", "")
     192             :                 },
     193        2566 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     194             : {
     195           1 :     return GetTime() - GetStartupTime();
     196             : }
     197             :     };
     198           0 : }
     199             : 
     200        2567 : static RPCHelpMan getrpcinfo()
     201             : {
     202       10268 :     return RPCHelpMan{"getrpcinfo",
     203        2567 :                 "\nReturns details of the RPC server.\n",
     204        2567 :                 {},
     205        2567 :                 RPCResult{
     206        2567 :                     RPCResult::Type::OBJ, "", "",
     207        7701 :                     {
     208        5134 :                         {RPCResult::Type::ARR, "active_commands", "All active commands",
     209        5134 :                         {
     210        5134 :                             {RPCResult::Type::OBJ, "", "Information about an active command",
     211        7701 :                             {
     212        2567 :                                  {RPCResult::Type::STR, "method", "The name of the RPC command"},
     213        2567 :                                  {RPCResult::Type::NUM, "duration", "The running time in microseconds"},
     214             :                             }},
     215             :                         }},
     216        2567 :                         {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"},
     217             :                     }
     218             :                 },
     219        2567 :                 RPCExamples{
     220        2567 :                     HelpExampleCli("getrpcinfo", "")
     221        2567 :                 + HelpExampleRpc("getrpcinfo", "")},
     222        2570 :         [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
     223             : {
     224           3 :     LOCK(g_rpc_server_info.mutex);
     225           3 :     UniValue active_commands(UniValue::VARR);
     226           7 :     for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
     227           4 :         UniValue entry(UniValue::VOBJ);
     228           4 :         entry.pushKV("method", info.method);
     229           4 :         entry.pushKV("duration", GetTimeMicros() - info.start);
     230           4 :         active_commands.push_back(entry);
     231           4 :     }
     232             : 
     233           3 :     UniValue result(UniValue::VOBJ);
     234           3 :     result.pushKV("active_commands", active_commands);
     235             : 
     236           3 :     const std::string path = LogInstance().m_file_path.string();
     237           3 :     UniValue log_path(UniValue::VSTR, path);
     238           3 :     result.pushKV("logpath", log_path);
     239             : 
     240             :     return result;
     241           3 : }
     242             :     };
     243           0 : }
     244             : 
     245             : // clang-format off
     246        3840 : static const CRPCCommand vRPCCommands[] =
     247         640 : { //  category              name                      actor (function)         argNames
     248             :   //  --------------------- ------------------------  -----------------------  ----------
     249             :     /* Overall control/query calls */
     250         640 :     { "control",            "getrpcinfo",             &getrpcinfo,             {}  },
     251         640 :     { "control",            "help",                   &help,                   {"command"}  },
     252         640 :     { "control",            "stop",                   &stop,                   {"wait"}  },
     253         640 :     { "control",            "uptime",                 &uptime,                 {}  },
     254             : };
     255             : // clang-format on
     256             : 
     257        1280 : CRPCTable::CRPCTable()
     258         640 : {
     259        3200 :     for (const auto& c : vRPCCommands) {
     260        2560 :         appendCommand(c.name, &c);
     261             :     }
     262        1280 : }
     263             : 
     264       89410 : void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
     265             : {
     266       89410 :     CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
     267             : 
     268       89410 :     mapCommands[name].push_back(pcmd);
     269       89410 : }
     270             : 
     271       31860 : bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
     272             : {
     273       31860 :     auto it = mapCommands.find(name);
     274       31860 :     if (it != mapCommands.end()) {
     275       31860 :         auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
     276       31860 :         if (it->second.end() != new_end) {
     277       31860 :             it->second.erase(new_end, it->second.end());
     278       31860 :             return true;
     279             :         }
     280       31860 :     }
     281           0 :     return false;
     282       31860 : }
     283             : 
     284         526 : void StartRPC()
     285             : {
     286         526 :     LogPrint(BCLog::RPC, "Starting RPC\n");
     287         526 :     g_rpc_running = true;
     288         526 :     g_rpcSignals.Started();
     289         526 : }
     290             : 
     291         529 : void InterruptRPC()
     292             : {
     293             :     static std::once_flag g_rpc_interrupt_flag;
     294             :     // This function could be called twice if the GUI has been started with -server=1.
     295        1058 :     std::call_once(g_rpc_interrupt_flag, []() {
     296         529 :         LogPrint(BCLog::RPC, "Interrupting RPC\n");
     297             :         // Interrupt e.g. running longpolls
     298         529 :         g_rpc_running = false;
     299         529 :     });
     300         529 : }
     301             : 
     302         529 : void StopRPC()
     303             : {
     304             :     static std::once_flag g_rpc_stop_flag;
     305             :     // This function could be called twice if the GUI has been started with -server=1.
     306         529 :     assert(!g_rpc_running);
     307        1058 :     std::call_once(g_rpc_stop_flag, []() {
     308         529 :         LogPrint(BCLog::RPC, "Stopping RPC\n");
     309        1058 :         WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
     310         529 :         DeleteAuthCookie();
     311         529 :         g_rpcSignals.Stopped();
     312         529 :     });
     313         529 : }
     314             : 
     315       90567 : bool IsRPCRunning()
     316             : {
     317       90567 :     return g_rpc_running;
     318             : }
     319             : 
     320        1137 : void RpcInterruptionPoint()
     321             : {
     322        1137 :     if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
     323        1137 : }
     324             : 
     325        4599 : void SetRPCWarmupStatus(const std::string& newStatus)
     326             : {
     327        4599 :     LOCK(g_rpc_warmup_mutex);
     328        4599 :     rpcWarmupStatus = newStatus;
     329        4599 : }
     330             : 
     331         494 : void SetRPCWarmupFinished()
     332             : {
     333         494 :     LOCK(g_rpc_warmup_mutex);
     334         494 :     assert(fRPCInWarmup);
     335         494 :     fRPCInWarmup = false;
     336         494 : }
     337             : 
     338          97 : bool RPCIsInWarmup(std::string *outStatus)
     339             : {
     340          97 :     LOCK(g_rpc_warmup_mutex);
     341          97 :     if (outStatus)
     342          41 :         *outStatus = rpcWarmupStatus;
     343          97 :     return fRPCInWarmup;
     344          97 : }
     345             : 
     346        8637 : bool IsDeprecatedRPCEnabled(const std::string& method)
     347             : {
     348        8637 :     const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
     349             : 
     350        8637 :     return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
     351        8637 : }
     352             : 
     353         146 : static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req)
     354             : {
     355         146 :     UniValue rpc_result(UniValue::VOBJ);
     356             : 
     357             :     try {
     358         146 :         jreq.parse(req);
     359             : 
     360         146 :         UniValue result = tableRPC.execute(jreq);
     361         137 :         rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
     362         146 :     }
     363             :     catch (const UniValue& objError)
     364             :     {
     365           9 :         rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
     366           9 :     }
     367             :     catch (const std::exception& e)
     368             :     {
     369           0 :         rpc_result = JSONRPCReplyObj(NullUniValue,
     370           0 :                                      JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
     371           9 :     }
     372             : 
     373             :     return rpc_result;
     374         155 : }
     375             : 
     376          13 : std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq)
     377             : {
     378          13 :     UniValue ret(UniValue::VARR);
     379         159 :     for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
     380         146 :         ret.push_back(JSONRPCExecOne(jreq, vReq[reqIdx]));
     381             : 
     382          13 :     return ret.write() + "\n";
     383          13 : }
     384             : 
     385             : /**
     386             :  * Process named arguments into a vector of positional arguments, based on the
     387             :  * passed-in specification for the RPC call's arguments.
     388             :  */
     389       39661 : static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
     390             : {
     391       39661 :     JSONRPCRequest out = in;
     392       39661 :     out.params = UniValue(UniValue::VARR);
     393             :     // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
     394             :     // there is an unknown one.
     395       39661 :     const std::vector<std::string>& keys = in.params.getKeys();
     396       39661 :     const std::vector<UniValue>& values = in.params.getValues();
     397       39661 :     std::unordered_map<std::string, const UniValue*> argsIn;
     398       62678 :     for (size_t i=0; i<keys.size(); ++i) {
     399       23017 :         argsIn[keys[i]] = &values[i];
     400             :     }
     401             :     // Process expected parameters.
     402             :     int hole = 0;
     403       88336 :     for (const std::string &argNamePattern: argNames) {
     404       48675 :         std::vector<std::string> vargNames;
     405       48675 :         boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|"));
     406       48675 :         auto fr = argsIn.end();
     407       97607 :         for (const std::string & argName : vargNames) {
     408       48932 :             fr = argsIn.find(argName);
     409       48932 :             if (fr != argsIn.end()) {
     410       23016 :                 break;
     411             :             }
     412       25916 :         }
     413       48675 :         if (fr != argsIn.end()) {
     414       25734 :             for (int i = 0; i < hole; ++i) {
     415             :                 // Fill hole between specified parameters with JSON nulls,
     416             :                 // but not at the end (for backwards compatibility with calls
     417             :                 // that act based on number of specified parameters).
     418        2718 :                 out.params.push_back(UniValue());
     419             :             }
     420             :             hole = 0;
     421       23016 :             out.params.push_back(*fr->second);
     422       23016 :             argsIn.erase(fr);
     423       23016 :         } else {
     424       25659 :             hole += 1;
     425             :         }
     426       48675 :     }
     427             :     // If there are still arguments in the argsIn map, this is an error.
     428       39661 :     if (!argsIn.empty()) {
     429           1 :         throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
     430             :     }
     431             :     // Return request with named arguments transformed to positional arguments
     432             :     return out;
     433       39661 : }
     434             : 
     435       79224 : UniValue CRPCTable::execute(const JSONRPCRequest &request) const
     436             : {
     437             :     // Return immediately if in warmup
     438             :     {
     439       79224 :         LOCK(g_rpc_warmup_mutex);
     440       79224 :         if (fRPCInWarmup)
     441         767 :             throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
     442       80367 :     }
     443             : 
     444             :     // Find method
     445       78457 :     auto it = mapCommands.find(request.strMethod);
     446       78457 :     if (it != mapCommands.end()) {
     447       78452 :         UniValue result;
     448      156904 :         for (const auto& command : it->second) {
     449       78452 :             if (ExecuteCommand(*command, request, result, &command == &it->second.back())) {
     450       77314 :                 return result;
     451             :             }
     452           0 :         }
     453       78452 :     }
     454           5 :     throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
     455       80362 : }
     456             : 
     457       78452 : static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
     458             : {
     459             :     try
     460             :     {
     461       78452 :         RPCCommandExecution execution(request.strMethod);
     462             :         // Execute, convert arguments to array if necessary
     463       78452 :         if (request.params.isObject()) {
     464       39662 :             return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
     465             :         } else {
     466       38791 :             return command.actor(request, result, last_handler);
     467             :         }
     468       78497 :     }
     469             :     catch (const std::exception& e)
     470             :     {
     471          45 :         throw JSONRPCError(RPC_MISC_ERROR, e.what());
     472          45 :     }
     473       78497 : }
     474             : 
     475           0 : std::vector<std::string> CRPCTable::listCommands() const
     476             : {
     477           0 :     std::vector<std::string> commandList;
     478           0 :     for (const auto& i : mapCommands) commandList.emplace_back(i.first);
     479             :     return commandList;
     480           0 : }
     481             : 
     482           0 : void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
     483             : {
     484           0 :     if (!timerInterface)
     485           0 :         timerInterface = iface;
     486           0 : }
     487             : 
     488         525 : void RPCSetTimerInterface(RPCTimerInterface *iface)
     489             : {
     490         525 :     timerInterface = iface;
     491         525 : }
     492             : 
     493         525 : void RPCUnsetTimerInterface(RPCTimerInterface *iface)
     494             : {
     495         525 :     if (timerInterface == iface)
     496         525 :         timerInterface = nullptr;
     497         525 : }
     498             : 
     499          39 : void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds)
     500             : {
     501          39 :     if (!timerInterface)
     502           0 :         throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
     503          39 :     LOCK(g_deadline_timers_mutex);
     504          39 :     deadlineTimers.erase(name);
     505          39 :     LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
     506          39 :     deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
     507          39 : }
     508             : 
     509       18411 : int RPCSerializationFlags()
     510             : {
     511             :     int flag = 0;
     512       18411 :     if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
     513          24 :         flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
     514       18411 :     return flag;
     515           0 : }
     516             : 
     517         640 : CRPCTable tableRPC;

Generated by: LCOV version 1.15