2
0

xmlrpc_pstream.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  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. #include "cmdline_parser.hpp"
  14. #include "xmlrpc-c/girerr.hpp"
  15. using girerr::throwf;
  16. #include <features.h> // for __BEGIN_DECLS
  17. __BEGIN_DECLS
  18. #include "dumpvalue.h" /* An internal Xmlrpc-c header file ! */
  19. __END_DECLS
  20. #include <xmlrpc-c/base.hpp>
  21. #include <xmlrpc-c/client.hpp>
  22. #include <xmlrpc-c/client_transport.hpp>
  23. using namespace std;
  24. using namespace xmlrpc_c;
  25. /*----------------------------------------------------------------------------
  26. Command line
  27. -----------------------------------------------------------------------------*/
  28. class cmdlineInfo {
  29. public:
  30. int serverfd;
  31. bool interactive;
  32. // Valid only if !interactive:
  33. string methodName;
  34. vector<string> params;
  35. cmdlineInfo(int const argc,
  36. const char ** const argv);
  37. private:
  38. cmdlineInfo();
  39. };
  40. static void
  41. parseCommandLine(cmdlineInfo * const cmdlineP,
  42. int const argc,
  43. const char ** const argv) {
  44. CmdlineParser cp;
  45. cp.defineOption("serverfd", CmdlineParser::UINT);
  46. try {
  47. cp.processOptions(argc, argv);
  48. } catch (exception const& e) {
  49. throwf("Command syntax error. %s", e.what());
  50. }
  51. if (cp.optionIsPresent("serverfd")) {
  52. cmdlineP->serverfd = cp.getOptionValueUint("serverfd");
  53. } else
  54. cmdlineP->serverfd = 3;
  55. if (cp.argumentCount() < 1)
  56. cmdlineP->interactive = true;
  57. else {
  58. cmdlineP->interactive = false;
  59. cmdlineP->methodName = cp.getArgument(0);
  60. for (uint argI = 1; argI < cp.argumentCount(); ++argI)
  61. cmdlineP->params.push_back(cp.getArgument(argI));
  62. }
  63. }
  64. cmdlineInfo::
  65. cmdlineInfo(int const argc,
  66. const char ** const argv) {
  67. try {
  68. parseCommandLine(this, argc, argv);
  69. } catch (exception const& e) {
  70. throwf("Command syntax error. %s", e.what());
  71. }
  72. }
  73. static value
  74. bytestringValFromParm(string const& valueString) {
  75. value retval;
  76. if (valueString.length() / 2 * 2 != valueString.length())
  77. throwf("Hexadecimal text is not an even "
  78. "number of characters (it is %u characters)",
  79. valueString.length());
  80. else {
  81. vector<unsigned char> byteString(valueString.length() / 2);
  82. size_t strCursor;
  83. strCursor = 0;
  84. while (strCursor < valueString.length()) {
  85. string const hexByte(valueString.substr(strCursor, 2));
  86. unsigned char byte;
  87. int rc;
  88. rc = sscanf(hexByte.c_str(), "%2hhx", &byte);
  89. byteString.push_back(byte);
  90. if (rc != 1)
  91. throwf("Invalid hex data '%s'", hexByte.c_str());
  92. else
  93. strCursor += 2;
  94. }
  95. retval = value_bytestring(byteString);
  96. }
  97. return retval;
  98. }
  99. static value
  100. intValFromParm(string const& valueString) {
  101. value retval;
  102. if (valueString.length() < 1)
  103. throwf("Integer argument has nothing after the 'i/'");
  104. else {
  105. long longValue;
  106. char * tailptr;
  107. errno = 0;
  108. longValue = strtol(valueString.c_str(), &tailptr, 10);
  109. if (errno == ERANGE)
  110. throwf("'%s' is out of range for a 32 bit integer",
  111. valueString.c_str());
  112. else if (errno != 0)
  113. throwf("Mysterious failure of strtol(), errno=%d (%s)",
  114. errno, strerror(errno));
  115. else {
  116. if (*tailptr != '\0')
  117. throwf("Integer argument has non-digit crap in it: '%s'",
  118. tailptr);
  119. else
  120. retval = value_int(longValue);
  121. }
  122. }
  123. return retval;
  124. }
  125. static value
  126. boolValFromParm(string const& valueString) {
  127. value retval;
  128. if (valueString == "t" || valueString == "true")
  129. retval = value_boolean(true);
  130. else if (valueString == "f" || valueString == "false")
  131. retval = value_boolean(false);
  132. else
  133. throwf("Boolean argument has unrecognized value '%s'. "
  134. "recognized values are 't', 'f', 'true', and 'false'.",
  135. valueString.c_str());
  136. return retval;
  137. }
  138. static value
  139. doubleValFromParm(string const& valueString) {
  140. value retval;
  141. if (valueString.length() < 1)
  142. throwf("\"Double\" argument has nothing after the 'd/'");
  143. else {
  144. double value;
  145. char * tailptr;
  146. value = strtod(valueString.c_str(), &tailptr);
  147. if (*tailptr != '\0')
  148. throwf("\"Double\" argument has non-decimal crap in it: '%s'",
  149. tailptr);
  150. else
  151. retval = value_double(value);
  152. }
  153. return retval;
  154. }
  155. static value
  156. nilValFromParm(string const& valueString) {
  157. value retval;
  158. if (valueString.length() > 0)
  159. throwf("Nil argument has something after the 'n/'");
  160. else
  161. retval = value_nil();
  162. return retval;
  163. }
  164. static value
  165. i8ValFromParm(string const& valueString) {
  166. value retval;
  167. if (valueString.length() < 1)
  168. throwf("Integer argument has nothing after the 'I/'");
  169. else {
  170. long long value;
  171. char * tailptr;
  172. errno = 0;
  173. value = strtoll(valueString.c_str(), &tailptr, 10);
  174. if (errno == ERANGE)
  175. throwf("'%s' is out of range for a 64 bit integer",
  176. valueString.c_str());
  177. else if (errno != 0)
  178. throwf("Mysterious failure of strtoll(), errno=%d (%s)",
  179. errno, strerror(errno));
  180. else {
  181. if (*tailptr != '\0')
  182. throwf("64 bit integer argument has non-digit crap "
  183. "in it: '%s'",
  184. tailptr);
  185. else
  186. retval = value_i8(value);
  187. }
  188. }
  189. return retval;
  190. }
  191. static value
  192. parameterFromArg(string const& paramArg) {
  193. value param;
  194. try {
  195. if (paramArg.substr(0, 2) == "s/")
  196. param = value_string(paramArg.substr(2));
  197. else if (paramArg.substr(0, 2) == "h/")
  198. param = bytestringValFromParm(paramArg.substr(2));
  199. else if (paramArg.substr(0, 2) == "i/")
  200. param = intValFromParm(paramArg.substr(2));
  201. else if (paramArg.substr(0, 2) == "I/")
  202. param = i8ValFromParm(paramArg.substr(2));
  203. else if (paramArg.substr(0, 2) == "d/")
  204. param = doubleValFromParm(paramArg.substr(2));
  205. else if (paramArg.substr(0, 2) == "b/")
  206. param = boolValFromParm(paramArg.substr(2));
  207. else if (paramArg.substr(0, 2) == "n/")
  208. param = nilValFromParm(paramArg.substr(2));
  209. else {
  210. /* It's not in normal type/value format, so we take it to be
  211. the shortcut string notation
  212. */
  213. param = value_string(paramArg);
  214. }
  215. } catch (exception const& e) {
  216. throwf("Failed to interpret parameter argument '%s'. %s",
  217. paramArg.c_str(), e.what());
  218. }
  219. return param;
  220. }
  221. static paramList
  222. paramListFromParamArgs(vector<string> const& params) {
  223. paramList paramList;
  224. for (vector<string>::const_iterator p = params.begin();
  225. p != params.end(); ++p)
  226. paramList.add(parameterFromArg(*p));
  227. return paramList;
  228. }
  229. static void
  230. callWithClient(client * const clientP,
  231. string const& methodName,
  232. paramList const& paramList,
  233. value * const resultP) {
  234. rpcPtr myRpcP(methodName, paramList);
  235. carriageParm_pstream myCarriageParm; // Empty - no parm needed
  236. try {
  237. myRpcP->call(clientP, &myCarriageParm);
  238. } catch (exception const& e) {
  239. throwf("RPC failed. %s", e.what());
  240. }
  241. *resultP = myRpcP->getResult();
  242. }
  243. static void
  244. dumpResult(value const& result) {
  245. cout << "Result:" << endl << endl;
  246. /* Here we borrow code from inside Xmlrpc-c, and also use an
  247. internal interface of xmlrpc_c::value. This sliminess is one
  248. reason that this is Bryan's private code instead of part of the
  249. Xmlrpc-c package.
  250. Note that you must link with the dumpvalue.o object module from
  251. inside an Xmlrpc-c build tree.
  252. */
  253. dumpValue("", result.cValueP);
  254. }
  255. static list<string>
  256. parseWordList(string const& wordString) {
  257. list<string> retval;
  258. unsigned int pos;
  259. pos = 0;
  260. while (pos < wordString.length()) {
  261. pos = wordString.find_first_not_of(' ', pos);
  262. if (pos < wordString.length()) {
  263. unsigned int const end = wordString.find_first_of(' ', pos);
  264. retval.push_back(wordString.substr(pos, end - pos));
  265. pos = end;
  266. }
  267. }
  268. return retval;
  269. }
  270. static void
  271. parseCommand(string const& cmd,
  272. string * const methodNameP,
  273. vector<string> * const paramListP) {
  274. list<string> const wordList(parseWordList(cmd));
  275. list<string>::const_iterator cmdWordP;
  276. cmdWordP = wordList.begin();
  277. if (cmdWordP == wordList.end())
  278. throwf("Command '%s' does not have a method name", cmd.c_str());
  279. else {
  280. *methodNameP = *cmdWordP++;
  281. *paramListP = vector<string>(); // Start empty
  282. while (cmdWordP != wordList.end())
  283. paramListP->push_back(*cmdWordP++);
  284. }
  285. }
  286. static void
  287. doCommand(client_xml * const clientP,
  288. string const& methodName,
  289. vector<string> const& paramArgs) {
  290. value result;
  291. callWithClient(clientP, methodName, paramListFromParamArgs(paramArgs),
  292. &result);
  293. try {
  294. dumpResult(result);
  295. } catch(exception const& e) {
  296. throwf("Error showing result after RPC completed normally. %s",
  297. e.what());
  298. }
  299. }
  300. static void
  301. getCommand(string * const cmdP,
  302. bool* const eofP) {
  303. const char * cmd;
  304. cmd = readline(">");
  305. *eofP = (cmd == NULL);
  306. if (cmd != NULL) {
  307. *cmdP = string(cmd);
  308. free(const_cast<char *>(cmd));
  309. }
  310. }
  311. static void
  312. doInteractive(client_xml * const clientP) {
  313. bool quitRequested;
  314. quitRequested = false;
  315. while (!quitRequested) {
  316. string cmd;
  317. bool eof;
  318. getCommand(&cmd, &eof);
  319. if (eof) {
  320. quitRequested = true;
  321. cout << endl;
  322. } else {
  323. try {
  324. string methodName;
  325. vector<string> paramArgs;
  326. parseCommand(cmd, &methodName, &paramArgs);
  327. doCommand(clientP, methodName, paramArgs);
  328. } catch (exception const& e) {
  329. cout << "Command failed. " << e.what() << endl;
  330. }
  331. }
  332. }
  333. }
  334. int
  335. main(int const argc,
  336. const char ** const argv) {
  337. try {
  338. cmdlineInfo cmdline(argc, argv);
  339. signal(SIGPIPE, SIG_IGN);
  340. clientXmlTransport_pstream myTransport(
  341. clientXmlTransport_pstream::constrOpt()
  342. .fd(cmdline.serverfd));
  343. client_xml myClient(&myTransport);
  344. if (cmdline.interactive) {
  345. if (cmdline.serverfd == STDIN_FILENO ||
  346. cmdline.serverfd == STDOUT_FILENO)
  347. throwf("Can't use Stdin or Stdout for the server fd when "
  348. "running interactively.");
  349. doInteractive(&myClient);
  350. } else
  351. doCommand(&myClient, cmdline.methodName, cmdline.params);
  352. } catch (exception const& e) {
  353. cerr << "Failed. " << e.what() << endl;
  354. } catch (...) {
  355. cerr << "Code threw unrecognized exception" << endl;
  356. abort();
  357. }
  358. return 0;
  359. }