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;
|