1 #include <cassert>
2 #include <cerrno>
3 #include <string>
4 #include <vector>
5 #include <list>
6 #include <cstring>
7 #include <cstdio>
8 #include <cstdlib>
9 #include <iostream>
10 #include <unistd.h>
11 #include <signal.h>
12 #include <readline/readline.h>
13
14 #include "cmdline_parser.hpp"
15 #include "xmlrpc-c/girerr.hpp"
16 using girerr::throwf;
17
18 #ifdef __cplusplus
19 extern "C" {
20 #endif
21
22 #include "dumpvalue.h" /* An internal Xmlrpc-c header file ! */
23
24 #ifdef __cplusplus
25 }
26 #endif
27
28 #include <xmlrpc-c/base.hpp>
29 #include <xmlrpc-c/client.hpp>
30 #include <xmlrpc-c/client_transport.hpp>
31
32 using namespace std;
33 using namespace xmlrpc_c;
34
35 /*----------------------------------------------------------------------------
36 Command line
37 -----------------------------------------------------------------------------*/
38
39 class cmdlineInfo {
40 public:
41 int serverfd;
42 bool interactive;
43
44 // Valid only if !interactive:
45 string methodName;
46 vector<string> params;
47
48 cmdlineInfo(int const argc,
49 const char ** const argv);
50
51 private:
52 cmdlineInfo();
53 };
54
55
56
57 static void
parseCommandLine(cmdlineInfo * const cmdlineP,int const argc,const char ** const argv)58 parseCommandLine(cmdlineInfo * const cmdlineP,
59 int const argc,
60 const char ** const argv) {
61
62 CmdlineParser cp;
63
64 cp.defineOption("serverfd", CmdlineParser::UINT);
65
66 try {
67 cp.processOptions(argc, argv);
68 } catch (exception const& e) {
69 throwf("Command syntax error. %s", e.what());
70 }
71
72 if (cp.optionIsPresent("serverfd")) {
73 cmdlineP->serverfd = cp.getOptionValueUint("serverfd");
74 } else
75 cmdlineP->serverfd = 3;
76
77 if (cp.argumentCount() < 1)
78 cmdlineP->interactive = true;
79 else {
80 cmdlineP->interactive = false;
81 cmdlineP->methodName = cp.getArgument(0);
82 for (uint argI = 1; argI < cp.argumentCount(); ++argI)
83 cmdlineP->params.push_back(cp.getArgument(argI));
84 }
85 }
86
87
88
89 cmdlineInfo::
cmdlineInfo(int const argc,const char ** const argv)90 cmdlineInfo(int const argc,
91 const char ** const argv) {
92
93 try {
94 parseCommandLine(this, argc, argv);
95 } catch (exception const& e) {
96 throwf("Command syntax error. %s", e.what());
97 }
98 }
99
100
101
102 static value
bytestringValFromParm(string const & valueString)103 bytestringValFromParm(string const& valueString) {
104
105 value retval;
106
107 if (valueString.length() / 2 * 2 != valueString.length())
108 throwf("Hexadecimal text is not an even "
109 "number of characters (it is %u characters)",
110 (unsigned)valueString.length());
111 else {
112 vector<unsigned char> byteString(valueString.length() / 2);
113 size_t strCursor;
114
115 strCursor = 0;
116
117 while (strCursor < valueString.length()) {
118 string const hexByte(valueString.substr(strCursor, 2));
119
120 unsigned char byte;
121 int rc;
122
123 rc = sscanf(hexByte.c_str(), "%2hhx", &byte);
124
125 byteString.push_back(byte);
126
127 if (rc != 1)
128 throwf("Invalid hex data '%s'", hexByte.c_str());
129 else
130 strCursor += 2;
131 }
132 retval = value_bytestring(byteString);
133 }
134 return retval;
135 }
136
137
138
139 static value
intValFromParm(string const & valueString)140 intValFromParm(string const& valueString) {
141
142 value retval;
143
144 if (valueString.length() < 1)
145 throwf("Integer argument has nothing after the 'i/'");
146 else {
147 long longValue;
148 char * tailptr;
149
150 errno = 0;
151
152 longValue = strtol(valueString.c_str(), &tailptr, 10);
153
154 if (errno == ERANGE)
155 throwf("'%s' is out of range for a 32 bit integer",
156 valueString.c_str());
157 else if (errno != 0)
158 throwf("Mysterious failure of strtol(), errno=%d (%s)",
159 errno, strerror(errno));
160 else {
161 if (*tailptr != '\0')
162 throwf("Integer argument has non-digit crap in it: '%s'",
163 tailptr);
164 else
165 retval = value_int(longValue);
166 }
167 }
168 return retval;
169 }
170
171
172
173 static value
boolValFromParm(string const & valueString)174 boolValFromParm(string const& valueString) {
175
176 value retval;
177
178 if (valueString == "t" || valueString == "true")
179 retval = value_boolean(true);
180 else if (valueString == "f" || valueString == "false")
181 retval = value_boolean(false);
182 else
183 throwf("Boolean argument has unrecognized value '%s'. "
184 "recognized values are 't', 'f', 'true', and 'false'.",
185 valueString.c_str());
186
187 return retval;
188 }
189
190
191
192 static value
doubleValFromParm(string const & valueString)193 doubleValFromParm(string const& valueString) {
194
195 value retval;
196
197 if (valueString.length() < 1)
198 throwf("\"Double\" argument has nothing after the 'd/'");
199 else {
200 double value;
201 char * tailptr;
202
203 value = strtod(valueString.c_str(), &tailptr);
204
205 if (*tailptr != '\0')
206 throwf("\"Double\" argument has non-decimal crap in it: '%s'",
207 tailptr);
208 else
209 retval = value_double(value);
210 }
211 return retval;
212 }
213
214
215
216 static value
nilValFromParm(string const & valueString)217 nilValFromParm(string const& valueString) {
218
219 value retval;
220
221 if (valueString.length() > 0)
222 throwf("Nil argument has something after the 'n/'");
223 else
224 retval = value_nil();
225
226 return retval;
227 }
228
229
230
231 static value
i8ValFromParm(string const & valueString)232 i8ValFromParm(string const& valueString) {
233
234 value retval;
235
236 if (valueString.length() < 1)
237 throwf("Integer argument has nothing after the 'I/'");
238 else {
239 long long value;
240 char * tailptr;
241
242 errno = 0;
243
244 value = strtoll(valueString.c_str(), &tailptr, 10);
245
246 if (errno == ERANGE)
247 throwf("'%s' is out of range for a 64 bit integer",
248 valueString.c_str());
249 else if (errno != 0)
250 throwf("Mysterious failure of strtoll(), errno=%d (%s)",
251 errno, strerror(errno));
252 else {
253 if (*tailptr != '\0')
254 throwf("64 bit integer argument has non-digit crap "
255 "in it: '%s'",
256 tailptr);
257 else
258 retval = value_i8(value);
259 }
260 }
261 return retval;
262 }
263
264
265
266 static value
parameterFromArg(string const & paramArg)267 parameterFromArg(string const& paramArg) {
268
269 value param;
270
271 try {
272 if (paramArg.substr(0, 2) == "s/")
273 param = value_string(paramArg.substr(2));
274 else if (paramArg.substr(0, 2) == "h/")
275 param = bytestringValFromParm(paramArg.substr(2));
276 else if (paramArg.substr(0, 2) == "i/")
277 param = intValFromParm(paramArg.substr(2));
278 else if (paramArg.substr(0, 2) == "I/")
279 param = i8ValFromParm(paramArg.substr(2));
280 else if (paramArg.substr(0, 2) == "d/")
281 param = doubleValFromParm(paramArg.substr(2));
282 else if (paramArg.substr(0, 2) == "b/")
283 param = boolValFromParm(paramArg.substr(2));
284 else if (paramArg.substr(0, 2) == "n/")
285 param = nilValFromParm(paramArg.substr(2));
286 else {
287 /* It's not in normal type/value format, so we take it to be
288 the shortcut string notation
289 */
290 param = value_string(paramArg);
291 }
292 } catch (exception const& e) {
293 throwf("Failed to interpret parameter argument '%s'. %s",
294 paramArg.c_str(), e.what());
295 }
296 return param;
297 }
298
299
300
301 static paramList
paramListFromParamArgs(vector<string> const & params)302 paramListFromParamArgs(vector<string> const& params) {
303
304 paramList paramList;
305
306 for (vector<string>::const_iterator p = params.begin();
307 p != params.end(); ++p)
308 paramList.add(parameterFromArg(*p));
309
310 return paramList;
311 }
312
313
314
315 static void
callWithClient(client * const clientP,string const & methodName,paramList const & paramList,value * const resultP)316 callWithClient(client * const clientP,
317 string const& methodName,
318 paramList const& paramList,
319 value * const resultP) {
320
321 rpcPtr myRpcP(methodName, paramList);
322
323 carriageParm_pstream myCarriageParm; // Empty - no parm needed
324
325 try {
326 myRpcP->call(clientP, &myCarriageParm);
327 } catch (exception const& e) {
328 throwf("RPC failed. %s", e.what());
329 }
330 *resultP = myRpcP->getResult();
331 }
332
333
334
335 static void
dumpResult(value const & result)336 dumpResult(value const& result) {
337
338 cout << "Result:" << endl << endl;
339
340 /* Here we borrow code from inside Xmlrpc-c, and also use an
341 internal interface of xmlrpc_c::value. This sliminess is one
342 reason that this is Bryan's private code instead of part of the
343 Xmlrpc-c package.
344
345 Note that you must link with the dumpvalue.o object module from
346 inside an Xmlrpc-c build tree.
347 */
348
349 dumpValue("", result.cValueP);
350 }
351
352
353
354 static list<string>
parseWordList(string const & wordString)355 parseWordList(string const& wordString) {
356
357 list<string> retval;
358
359 unsigned int pos;
360
361 pos = 0;
362
363 while (pos < wordString.length()) {
364 pos = wordString.find_first_not_of(' ', pos);
365
366 if (pos < wordString.length()) {
367 unsigned int const end = wordString.find_first_of(' ', pos);
368
369 retval.push_back(wordString.substr(pos, end - pos));
370
371 pos = end;
372 }
373 }
374 return retval;
375 }
376
377
378
379 static void
parseCommand(string const & cmd,string * const methodNameP,vector<string> * const paramListP)380 parseCommand(string const& cmd,
381 string * const methodNameP,
382 vector<string> * const paramListP) {
383
384 list<string> const wordList(parseWordList(cmd));
385
386 list<string>::const_iterator cmdWordP;
387
388 cmdWordP = wordList.begin();
389
390 if (cmdWordP == wordList.end())
391 throwf("Command '%s' does not have a method name", cmd.c_str());
392 else {
393 *methodNameP = *cmdWordP++;
394
395 *paramListP = vector<string>(); // Start empty
396
397 while (cmdWordP != wordList.end())
398 paramListP->push_back(*cmdWordP++);
399 }
400 }
401
402
403
404 static void
doCommand(client_xml * const clientP,string const & methodName,vector<string> const & paramArgs)405 doCommand(client_xml * const clientP,
406 string const& methodName,
407 vector<string> const& paramArgs) {
408
409 value result;
410
411 callWithClient(clientP, methodName, paramListFromParamArgs(paramArgs),
412 &result);
413
414 try {
415 dumpResult(result);
416 } catch(exception const& e) {
417 throwf("Error showing result after RPC completed normally. %s",
418 e.what());
419 }
420 }
421
422
423
424 static void
getCommand(string * const cmdP,bool * const eofP)425 getCommand(string * const cmdP,
426 bool* const eofP) {
427
428 const char * cmd;
429
430 cmd = readline(">");
431
432 *eofP = (cmd == NULL);
433
434 if (cmd != NULL) {
435 *cmdP = string(cmd);
436
437 free(const_cast<char *>(cmd));
438 }
439 }
440
441
442
443 static void
doInteractive(client_xml * const clientP)444 doInteractive(client_xml * const clientP) {
445
446 bool quitRequested;
447
448 quitRequested = false;
449
450 while (!quitRequested) {
451 string cmd;
452 bool eof;
453
454 getCommand(&cmd, &eof);
455
456 if (eof) {
457 quitRequested = true;
458 cout << endl;
459 } else {
460 try {
461 string methodName;
462 vector<string> paramArgs;
463
464 parseCommand(cmd, &methodName, ¶mArgs);
465
466 doCommand(clientP, methodName, paramArgs);
467 } catch (exception const& e) {
468 cout << "Command failed. " << e.what() << endl;
469 }
470 }
471 }
472 }
473
474
475
476 int
main(int const argc,const char ** const argv)477 main(int const argc,
478 const char ** const argv) {
479
480 try {
481 cmdlineInfo cmdline(argc, argv);
482
483 signal(SIGPIPE, SIG_IGN);
484
485 clientXmlTransport_pstream myTransport(
486 clientXmlTransport_pstream::constrOpt()
487 .fd(cmdline.serverfd));
488
489 client_xml myClient(&myTransport);
490
491 if (cmdline.interactive) {
492 if (cmdline.serverfd == STDIN_FILENO ||
493 cmdline.serverfd == STDOUT_FILENO)
494 throwf("Can't use Stdin or Stdout for the server fd when "
495 "running interactively.");
496 doInteractive(&myClient);
497 } else
498 doCommand(&myClient, cmdline.methodName, cmdline.params);
499
500 } catch (exception const& e) {
501 cerr << "Failed. " << e.what() << endl;
502 } catch (...) {
503 cerr << "Code threw unrecognized exception" << endl;
504 abort();
505 }
506 return 0;
507 }
508