Fabcoin Core  0.16.2
P2P Digital Currency
fabcoin-cli.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2017 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 #if defined(HAVE_CONFIG_H)
8 #endif
9 
10 #include <chainparamsbase.h>
11 #include <clientversion.h>
12 #include <fs.h>
13 #include <rpc/client.h>
14 #include <rpc/protocol.h>
15 #include <util.h>
16 #include <utilstrencodings.h>
17 
18 #include <stdio.h>
19 
20 #include <event2/buffer.h>
21 #include <event2/keyvalq_struct.h>
22 #include <support/events.h>
23 
24 #include <univalue.h>
25 
26 static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
27 static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
28 static const bool DEFAULT_NAMED=false;
29 static const int CONTINUE_EXECUTION=-1;
30 
31 std::string HelpMessageCli()
32 {
33  const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
34  const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
35  std::string strUsage;
36  strUsage += HelpMessageGroup(_("Options:"));
37  strUsage += HelpMessageOpt("-?", _("This help message"));
38  strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), FABCOIN_CONF_FILENAME));
39  strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
40  AppendParamsHelpMessages(strUsage);
41  strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
42  strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
43  strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
44  strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
45  strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
46  strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
47  strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
48  strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
49  strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in fabcoind directory, required if fabcoind/-Qt runs with multiple wallets)"));
50 
51  return strUsage;
52 }
53 
55 //
56 // Start
57 //
58 
59 //
60 // Exception thrown on connection error. This error is used to determine
61 // when to wait if -rpcwait is given.
62 //
63 class CConnectionFailed : public std::runtime_error
64 {
65 public:
66 
67  explicit inline CConnectionFailed(const std::string& msg) :
68  std::runtime_error(msg)
69  {}
70 
71 };
72 
73 //
74 // This function returns either one of EXIT_ codes when it's expected to stop the process or
75 // CONTINUE_EXECUTION when it's expected to continue further.
76 //
77 static int AppInitRPC(int argc, char* argv[])
78 {
79  //
80  // Parameters
81  //
82  gArgs.ParseParameters(argc, argv);
83  if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
84  std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
85  if (!gArgs.IsArgSet("-version")) {
86  strUsage += "\n" + _("Usage:") + "\n" +
87  " fabcoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" +
88  " fabcoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" +
89  " fabcoin-cli [options] help " + _("List commands") + "\n" +
90  " fabcoin-cli [options] help <command> " + _("Get help for a command") + "\n";
91 
92  strUsage += "\n" + HelpMessageCli();
93  }
94 
95  fprintf(stdout, "%s", strUsage.c_str());
96  if (argc < 2) {
97  fprintf(stderr, "Error: too few parameters\n");
98  return EXIT_FAILURE;
99  }
100  return EXIT_SUCCESS;
101  }
102  if (!fs::is_directory(GetDataDir(false))) {
103  fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
104  return EXIT_FAILURE;
105  }
106  try {
108  } catch (const std::exception& e) {
109  fprintf(stderr,"Error reading configuration file: %s\n", e.what());
110  return EXIT_FAILURE;
111  }
112  // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
113  try {
115  } catch (const std::exception& e) {
116  fprintf(stderr, "Error: %s\n", e.what());
117  return EXIT_FAILURE;
118  }
119  if (gArgs.GetBoolArg("-rpcssl", false))
120  {
121  fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
122  return EXIT_FAILURE;
123  }
124  return CONTINUE_EXECUTION;
125 }
126 
127 
129 struct HTTPReply
130 {
131  HTTPReply(): status(0), error(-1) {}
132 
133  int status;
134  int error;
135  std::string body;
136 };
137 
138 const char *http_errorstring(int code)
139 {
140  switch(code) {
141 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
142  case EVREQ_HTTP_TIMEOUT:
143  return "timeout reached";
144  case EVREQ_HTTP_EOF:
145  return "EOF reached";
146  case EVREQ_HTTP_INVALID_HEADER:
147  return "error while reading header, or invalid header";
148  case EVREQ_HTTP_BUFFER_ERROR:
149  return "error encountered while reading or writing";
150  case EVREQ_HTTP_REQUEST_CANCEL:
151  return "request was canceled";
152  case EVREQ_HTTP_DATA_TOO_LONG:
153  return "response body is larger than allowed";
154 #endif
155  default:
156  return "unknown";
157  }
158 }
159 
160 static void http_request_done(struct evhttp_request *req, void *ctx)
161 {
162  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
163 
164  if (req == nullptr) {
165  /* If req is nullptr, it means an error occurred while connecting: the
166  * error code will have been passed to http_error_cb.
167  */
168  reply->status = 0;
169  return;
170  }
171 
172  reply->status = evhttp_request_get_response_code(req);
173 
174  struct evbuffer *buf = evhttp_request_get_input_buffer(req);
175  if (buf)
176  {
177  size_t size = evbuffer_get_length(buf);
178  const char *data = (const char*)evbuffer_pullup(buf, size);
179  if (data)
180  reply->body = std::string(data, size);
181  evbuffer_drain(buf, size);
182  }
183 }
184 
185 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
186 static void http_error_cb(enum evhttp_request_error err, void *ctx)
187 {
188  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
189  reply->error = err;
190 }
191 #endif
192 
193 UniValue CallRPC(const std::string& strMethod, const UniValue& params)
194 {
195  std::string host;
196  // In preference order, we choose the following for the port:
197  // 1. -rpcport
198  // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
199  // 3. default port for chain
200  int port = BaseParams().RPCPort();
201  SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
202  port = gArgs.GetArg("-rpcport", port);
203 
204  // Obtain event base
205  raii_event_base base = obtain_event_base();
206 
207  // Synchronously look up hostname
208  raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
209  evhttp_connection_set_timeout(evcon.get(), gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
210 
211  HTTPReply response;
212  raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
213  if (req == nullptr)
214  throw std::runtime_error("create http request failed");
215 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
216  evhttp_request_set_error_cb(req.get(), http_error_cb);
217 #endif
218 
219  // Get credentials
220  std::string strRPCUserColonPass;
221  if (gArgs.GetArg("-rpcpassword", "") == "") {
222  // Try fall back to cookie-based authentication if no password is provided
223  if (!GetAuthCookie(&strRPCUserColonPass)) {
224  throw std::runtime_error(strprintf(
225  _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
226  GetConfigFile(gArgs.GetArg("-conf", FABCOIN_CONF_FILENAME)).string().c_str()));
227 
228  }
229  } else {
230  strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
231  }
232 
233  struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
234  assert(output_headers);
235  evhttp_add_header(output_headers, "Host", host.c_str());
236  evhttp_add_header(output_headers, "Connection", "close");
237  evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
238 
239  // Attach request data
240  std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n";
241  struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
242  assert(output_buffer);
243  evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
244 
245  // check if we should use a special wallet endpoint
246  std::string endpoint = "/";
247  std::string walletName = gArgs.GetArg("-rpcwallet", "");
248  if (!walletName.empty()) {
249  char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false);
250  if (encodedURI) {
251  endpoint = "/wallet/"+ std::string(encodedURI);
252  free(encodedURI);
253  }
254  else {
255  throw CConnectionFailed("uri-encode failed");
256  }
257  }
258  int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
259  req.release(); // ownership moved to evcon in above call
260  if (r != 0) {
261  throw CConnectionFailed("send http request failed");
262  }
263 
264  event_base_dispatch(base.get());
265 
266  if (response.status == 0)
267  throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
268  else if (response.status == HTTP_UNAUTHORIZED)
269  throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
270  else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
271  throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
272  else if (response.body.empty())
273  throw std::runtime_error("no response from server");
274 
275  // Parse reply
276  UniValue valReply(UniValue::VSTR);
277  if (!valReply.read(response.body))
278  throw std::runtime_error("couldn't parse reply from server");
279  const UniValue& reply = valReply.get_obj();
280  if (reply.empty())
281  throw std::runtime_error("expected reply to have result, error and id properties");
282 
283  return reply;
284 }
285 
286 int CommandLineRPC(int argc, char *argv[])
287 {
288  std::string strPrint;
289  int nRet = 0;
290  try {
291  // Skip switches
292  while (argc > 1 && IsSwitchChar(argv[1][0])) {
293  argc--;
294  argv++;
295  }
296  std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
297  if (gArgs.GetBoolArg("-stdin", false)) {
298  // Read one arg per line from stdin and append
299  std::string line;
300  while (std::getline(std::cin,line))
301  args.push_back(line);
302  }
303  if (args.size() < 1)
304  throw std::runtime_error("too few parameters (need at least command)");
305  std::string strMethod = args[0];
306  args.erase(args.begin()); // Remove trailing method name from arguments vector
307 
308  UniValue params;
309  if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
310  params = RPCConvertNamedValues(strMethod, args);
311  } else {
312  params = RPCConvertValues(strMethod, args);
313  }
314 
315  // Execute and handle connection failures with -rpcwait
316  const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
317  do {
318  try {
319  const UniValue reply = CallRPC(strMethod, params);
320 
321  // Parse reply
322  const UniValue& result = find_value(reply, "result");
323  const UniValue& error = find_value(reply, "error");
324 
325  if (!error.isNull()) {
326  // Error
327  int code = error["code"].get_int();
328  if (fWait && code == RPC_IN_WARMUP)
329  throw CConnectionFailed("server in warmup");
330  strPrint = "error: " + error.write();
331  nRet = abs(code);
332  if (error.isObject())
333  {
334  UniValue errCode = find_value(error, "code");
335  UniValue errMsg = find_value(error, "message");
336  strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
337 
338  if (errMsg.isStr())
339  strPrint += "error message:\n"+errMsg.get_str();
340 
341  if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) {
342  strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to fabcoin-cli command line.";
343  }
344  }
345  } else {
346  // Result
347  if (result.isNull())
348  strPrint = "";
349  else if (result.isStr())
350  strPrint = result.get_str();
351  else
352  strPrint = result.write(2);
353  }
354  // Connection succeeded, no need to retry.
355  break;
356  }
357  catch (const CConnectionFailed&) {
358  if (fWait)
359  MilliSleep(1000);
360  else
361  throw;
362  }
363  } while (fWait);
364  }
365  catch (const boost::thread_interrupted&) {
366  throw;
367  }
368  catch (const std::exception& e) {
369  strPrint = std::string("error: ") + e.what();
370  nRet = EXIT_FAILURE;
371  }
372  catch (...) {
373  PrintExceptionContinue(nullptr, "CommandLineRPC()");
374  throw;
375  }
376 
377  if (strPrint != "") {
378  fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
379  }
380  return nRet;
381 }
382 
383 int main(int argc, char* argv[])
384 {
386  if (!SetupNetworking()) {
387  fprintf(stderr, "Error: Initializing networking failed\n");
388  return EXIT_FAILURE;
389  }
390 
391  try {
392  int ret = AppInitRPC(argc, argv);
393  if (ret != CONTINUE_EXECUTION)
394  return ret;
395  }
396  catch (const std::exception& e) {
397  PrintExceptionContinue(&e, "AppInitRPC()");
398  return EXIT_FAILURE;
399  } catch (...) {
400  PrintExceptionContinue(nullptr, "AppInitRPC()");
401  return EXIT_FAILURE;
402  }
403 
404  int ret = EXIT_FAILURE;
405  try {
406  ret = CommandLineRPC(argc, argv);
407  }
408  catch (const std::exception& e) {
409  PrintExceptionContinue(&e, "CommandLineRPC()");
410  } catch (...) {
411  PrintExceptionContinue(nullptr, "CommandLineRPC()");
412  }
413  return ret;
414 }
int get_int() const
No wallet specified (error when there are multiple wallets loaded)
Definition: protocol.h:86
std::string HelpMessageOpt(const std::string &option, const std::string &message)
Format a string to be used as option description in help messages.
Definition: util.cpp:563
bool error(const char *fmt, const Args &...args)
Definition: util.h:178
const std::string & get_str() const
std::unique_ptr< CBaseChainParams > CreateBaseChainParams(const std::string &chain)
Creates and returns a std::unique_ptr<CBaseChainParams> of the chosen chain.
void AppendParamsHelpMessages(std::string &strUsage, bool debugHelp)
Append the help messages for the chainparams options to the parameter string.
bool SetupNetworking()
Definition: util.cpp:933
raii_event_base obtain_event_base()
Definition: events.h:30
void ParseParameters(int argc, const char *const argv[])
Definition: util.cpp:454
void MilliSleep(int64_t n)
Definition: utiltime.cpp:60
UniValue CallRPC(const std::string &strMethod, const UniValue &params)
bool IsArgSet(const std::string &strArg)
Return true if the given argument has been manually set.
Definition: util.cpp:498
#define PACKAGE_NAME
bool read(const char *raw, size_t len)
#define strprintf
Definition: tinyformat.h:1054
UniValue JSONRPCRequestObj(const std::string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition: protocol.cpp:27
bool empty() const
Definition: univalue.h:68
const char * http_errorstring(int code)
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:520
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: util.cpp:586
std::hash for asio::adress
Definition: Common.h:323
raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg)
Definition: events.h:45
assert(len-trim+(2 *lenIndices)<=WIDTH)
int CommandLineRPC(int argc, char *argv[])
bytes code
Definition: SmartVM.cpp:45
void ReadConfigFile(const std::string &confPath)
Definition: util.cpp:669
Client still warming up.
Definition: protocol.h:59
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:245
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition: protocol.cpp:113
const char *const FABCOIN_CONF_FILENAME
Definition: util.cpp:91
const Object_type::value_type::Value_type & find_value(const Object_type &obj, const String_type &name)
bool isNum() const
Definition: univalue.h:84
raii_evhttp_connection obtain_evhttp_connection_base(struct event_base *base, std::string host, uint16_t port)
Definition: events.h:49
int RPCPort() const
std::string FormatFullVersion()
CConnectionFailed(const std::string &msg)
Definition: fabcoin-cli.cpp:67
ArgsManager gArgs
Definition: util.cpp:94
bool IsSwitchChar(char c)
Definition: util.h:206
const std::string & getValStr() const
Definition: univalue.h:67
void SplitHostPort(std::string in, int &portOut, std::string &hostOut)
uint8_t const size_t const size
Definition: sha3.h:20
fs::path GetConfigFile(const std::string &confPath)
Definition: util.cpp:660
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:504
std::string ChainNameFromCommandLine()
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
std::string body
std::string HelpMessageCli()
Definition: fabcoin-cli.cpp:31
#define e(i)
Definition: sha.cpp:733
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert named arguments to command-specific RPC representation.
Definition: client.cpp:264
static const std::string TESTNET
bool isObject() const
Definition: univalue.h:86
const fs::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:623
void SetupEnvironment()
Definition: util.cpp:904
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
void SelectBaseParams(const std::string &chain)
Sets the params returned by Params() to those for the given network.
const UniValue & get_obj() const
bool isNull() const
Definition: univalue.h:79
uint8_t const * data
Definition: sha3.h:19
std::string HelpMessageGroup(const std::string &message)
Format a string to be used as group of options in help messages.
Definition: util.cpp:559
bool isStr() const
Definition: univalue.h:83
int main(int argc, char *argv[])
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result...
Definition: util.h:71
Reply structure for request_done to fill in.
std::string EncodeBase64(const unsigned char *pch, size_t len)