]>
Commit | Line | Data |
---|---|---|
2a03a390 WL |
1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | // Copyright (c) 2009-2013 The Bitcoin developers | |
3 | // Distributed under the MIT/X11 software license, see the accompanying | |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
5 | ||
6 | #include "util.h" | |
7 | #include "init.h" | |
fb78cc23 | 8 | #include "rpcclient.h" |
a7199038 | 9 | #include "rpcprotocol.h" |
2a03a390 | 10 | #include "ui_interface.h" /* for _(...) */ |
9d2b73d1 | 11 | #include "chainparams.h" |
2a03a390 | 12 | |
51ed9ec9 BD |
13 | #include <boost/filesystem/operations.hpp> |
14 | ||
b750cf1f WL |
15 | using namespace boost; |
16 | using namespace boost::asio; | |
17 | using namespace json_spirit; | |
18 | ||
19 | std::string HelpMessageCli() | |
20 | { | |
21 | string strUsage; | |
22 | strUsage += _("Options:") + "\n"; | |
23 | strUsage += " -? " + _("This help message") + "\n"; | |
24 | strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; | |
25 | strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n"; | |
26 | strUsage += " -testnet " + _("Use the test network") + "\n"; | |
27 | strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be " | |
28 | "solved instantly. This is intended for regression testing tools and app development.") + "\n"; | |
29 | strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n"; | |
30 | strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n"; | |
31 | strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; | |
32 | strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n"; | |
33 | strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n"; | |
34 | ||
35 | strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; | |
36 | strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; | |
37 | ||
38 | return strUsage; | |
39 | } | |
40 | ||
2a03a390 WL |
41 | ////////////////////////////////////////////////////////////////////////////// |
42 | // | |
43 | // Start | |
44 | // | |
45 | static bool AppInitRPC(int argc, char* argv[]) | |
46 | { | |
47 | // | |
48 | // Parameters | |
49 | // | |
50 | ParseParameters(argc, argv); | |
51 | if (!boost::filesystem::is_directory(GetDataDir(false))) | |
52 | { | |
53 | fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str()); | |
54 | return false; | |
55 | } | |
4ae5e721 WL |
56 | try { |
57 | ReadConfigFile(mapArgs, mapMultiArgs); | |
58 | } catch(std::exception &e) { | |
59 | fprintf(stderr,"Error reading configuration file: %s\n", e.what()); | |
60 | return false; | |
61 | } | |
a3d946eb | 62 | // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) |
9d2b73d1 WL |
63 | if (!SelectParamsFromCommandLine()) { |
64 | fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n"); | |
65 | return false; | |
66 | } | |
2a03a390 | 67 | |
96b733e9 | 68 | if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) |
2a03a390 | 69 | { |
96b733e9 WL |
70 | std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n"; |
71 | if (!mapArgs.count("-version")) | |
72 | { | |
73 | strUsage += "\n" + _("Usage:") + "\n" + | |
74 | " bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\n" + | |
75 | " bitcoin-cli [options] help " + _("List commands") + "\n" + | |
76 | " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; | |
2a03a390 | 77 | |
b750cf1f | 78 | strUsage += "\n" + HelpMessageCli(); |
96b733e9 | 79 | } |
2a03a390 WL |
80 | |
81 | fprintf(stdout, "%s", strUsage.c_str()); | |
82 | return false; | |
83 | } | |
84 | return true; | |
85 | } | |
86 | ||
b750cf1f WL |
87 | Object CallRPC(const string& strMethod, const Array& params) |
88 | { | |
89 | if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") | |
90 | throw runtime_error(strprintf( | |
91 | _("You must set rpcpassword=<password> in the configuration file:\n%s\n" | |
92 | "If the file does not exist, create it with owner-readable-only file permissions."), | |
93 | GetConfigFile().string().c_str())); | |
94 | ||
95 | // Connect to localhost | |
96 | bool fUseSSL = GetBoolArg("-rpcssl", false); | |
97 | asio::io_service io_service; | |
98 | ssl::context context(io_service, ssl::context::sslv23); | |
99 | context.set_options(ssl::context::no_sslv2); | |
100 | asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context); | |
101 | SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL); | |
102 | iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d); | |
103 | ||
104 | bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started | |
105 | do { | |
106 | bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))); | |
107 | if (fConnected) break; | |
108 | if (fWait) | |
109 | MilliSleep(1000); | |
110 | else | |
111 | throw runtime_error("couldn't connect to server"); | |
112 | } while (fWait); | |
113 | ||
114 | // HTTP basic authentication | |
115 | string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); | |
116 | map<string, string> mapRequestHeaders; | |
117 | mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; | |
118 | ||
119 | // Send request | |
120 | string strRequest = JSONRPCRequest(strMethod, params, 1); | |
121 | string strPost = HTTPPost(strRequest, mapRequestHeaders); | |
122 | stream << strPost << std::flush; | |
123 | ||
124 | // Receive HTTP reply status | |
125 | int nProto = 0; | |
126 | int nStatus = ReadHTTPStatus(stream, nProto); | |
127 | ||
128 | // Receive HTTP reply message headers and body | |
129 | map<string, string> mapHeaders; | |
130 | string strReply; | |
131 | ReadHTTPMessage(stream, mapHeaders, strReply, nProto); | |
132 | ||
133 | if (nStatus == HTTP_UNAUTHORIZED) | |
134 | throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); | |
135 | else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) | |
136 | throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); | |
137 | else if (strReply.empty()) | |
138 | throw runtime_error("no response from server"); | |
139 | ||
140 | // Parse reply | |
141 | Value valReply; | |
142 | if (!read_string(strReply, valReply)) | |
143 | throw runtime_error("couldn't parse reply from server"); | |
144 | const Object& reply = valReply.get_obj(); | |
145 | if (reply.empty()) | |
146 | throw runtime_error("expected reply to have result, error and id properties"); | |
147 | ||
148 | return reply; | |
149 | } | |
150 | ||
151 | int CommandLineRPC(int argc, char *argv[]) | |
152 | { | |
153 | string strPrint; | |
154 | int nRet = 0; | |
155 | try | |
156 | { | |
157 | // Skip switches | |
158 | while (argc > 1 && IsSwitchChar(argv[1][0])) | |
159 | { | |
160 | argc--; | |
161 | argv++; | |
162 | } | |
163 | ||
164 | // Method | |
165 | if (argc < 2) | |
166 | throw runtime_error("too few parameters"); | |
167 | string strMethod = argv[1]; | |
168 | ||
169 | // Parameters default to strings | |
170 | std::vector<std::string> strParams(&argv[2], &argv[argc]); | |
171 | Array params = RPCConvertValues(strMethod, strParams); | |
172 | ||
173 | // Execute | |
174 | Object reply = CallRPC(strMethod, params); | |
175 | ||
176 | // Parse reply | |
177 | const Value& result = find_value(reply, "result"); | |
178 | const Value& error = find_value(reply, "error"); | |
179 | ||
180 | if (error.type() != null_type) | |
181 | { | |
182 | // Error | |
183 | strPrint = "error: " + write_string(error, false); | |
184 | int code = find_value(error.get_obj(), "code").get_int(); | |
185 | nRet = abs(code); | |
186 | } | |
187 | else | |
188 | { | |
189 | // Result | |
190 | if (result.type() == null_type) | |
191 | strPrint = ""; | |
192 | else if (result.type() == str_type) | |
193 | strPrint = result.get_str(); | |
194 | else | |
195 | strPrint = write_string(result, true); | |
196 | } | |
197 | } | |
198 | catch (boost::thread_interrupted) { | |
199 | throw; | |
200 | } | |
201 | catch (std::exception& e) { | |
202 | strPrint = string("error: ") + e.what(); | |
203 | nRet = EXIT_FAILURE; | |
204 | } | |
205 | catch (...) { | |
206 | PrintExceptionContinue(NULL, "CommandLineRPC()"); | |
207 | throw; | |
208 | } | |
209 | ||
210 | if (strPrint != "") | |
211 | { | |
212 | fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); | |
213 | } | |
214 | return nRet; | |
215 | } | |
216 | ||
2a03a390 WL |
217 | int main(int argc, char* argv[]) |
218 | { | |
5248ff40 SC |
219 | SetupEnvironment(); |
220 | ||
2a03a390 WL |
221 | try |
222 | { | |
223 | if(!AppInitRPC(argc, argv)) | |
0cafb630 | 224 | return EXIT_FAILURE; |
2a03a390 WL |
225 | } |
226 | catch (std::exception& e) { | |
227 | PrintExceptionContinue(&e, "AppInitRPC()"); | |
0cafb630 | 228 | return EXIT_FAILURE; |
2a03a390 WL |
229 | } catch (...) { |
230 | PrintExceptionContinue(NULL, "AppInitRPC()"); | |
0cafb630 | 231 | return EXIT_FAILURE; |
2a03a390 WL |
232 | } |
233 | ||
0cafb630 | 234 | int ret = EXIT_FAILURE; |
2a03a390 WL |
235 | try |
236 | { | |
a7199038 | 237 | ret = CommandLineRPC(argc, argv); |
2a03a390 WL |
238 | } |
239 | catch (std::exception& e) { | |
240 | PrintExceptionContinue(&e, "CommandLineRPC()"); | |
241 | } catch (...) { | |
242 | PrintExceptionContinue(NULL, "CommandLineRPC()"); | |
243 | } | |
a7199038 | 244 | return ret; |
2a03a390 | 245 | } |