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