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 : }
|