|
|
#include <cassert> |
|
|
#include <cerrno> |
|
|
#include <string> |
|
|
#include <vector> |
|
|
#include <list> |
|
|
#include <cstring> |
|
|
#include <cstdio> |
|
|
#include <cstdlib> |
|
|
#include <iostream> |
|
|
#include <unistd.h> |
|
|
#include <signal.h> |
|
|
#include <readline/readline.h> |
|
|
|
|
|
#include "cmdline_parser.hpp" |
|
|
#include "xmlrpc-c/girerr.hpp" |
|
|
using girerr::throwf; |
|
|
|
|
|
#ifdef __cplusplus |
|
|
extern "C" { |
|
|
#endif |
|
|
|
|
|
#include "dumpvalue.h" |
|
|
|
|
|
#ifdef __cplusplus |
|
|
} |
|
|
#endif |
|
|
|
|
|
#include <xmlrpc-c/base.hpp> |
|
|
#include <xmlrpc-c/client.hpp> |
|
|
#include <xmlrpc-c/client_transport.hpp> |
|
|
|
|
|
using namespace std; |
|
|
using namespace xmlrpc_c; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class cmdlineInfo { |
|
|
public: |
|
|
int serverfd; |
|
|
bool interactive; |
|
|
|
|
|
|
|
|
string methodName; |
|
|
vector<string> params; |
|
|
|
|
|
cmdlineInfo(int const argc, |
|
|
const char ** const argv); |
|
|
|
|
|
private: |
|
|
cmdlineInfo(); |
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
parseCommandLine(cmdlineInfo * const cmdlineP, |
|
|
int const argc, |
|
|
const char ** const argv) { |
|
|
|
|
|
CmdlineParser cp; |
|
|
|
|
|
cp.defineOption("serverfd", CmdlineParser::UINT); |
|
|
|
|
|
try { |
|
|
cp.processOptions(argc, argv); |
|
|
} catch (exception const& e) { |
|
|
throwf("Command syntax error. %s", e.what()); |
|
|
} |
|
|
|
|
|
if (cp.optionIsPresent("serverfd")) { |
|
|
cmdlineP->serverfd = cp.getOptionValueUint("serverfd"); |
|
|
} else |
|
|
cmdlineP->serverfd = 3; |
|
|
|
|
|
if (cp.argumentCount() < 1) |
|
|
cmdlineP->interactive = true; |
|
|
else { |
|
|
cmdlineP->interactive = false; |
|
|
cmdlineP->methodName = cp.getArgument(0); |
|
|
for (uint argI = 1; argI < cp.argumentCount(); ++argI) |
|
|
cmdlineP->params.push_back(cp.getArgument(argI)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
cmdlineInfo:: |
|
|
cmdlineInfo(int const argc, |
|
|
const char ** const argv) { |
|
|
|
|
|
try { |
|
|
parseCommandLine(this, argc, argv); |
|
|
} catch (exception const& e) { |
|
|
throwf("Command syntax error. %s", e.what()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
bytestringValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString.length() / 2 * 2 != valueString.length()) |
|
|
throwf("Hexadecimal text is not an even " |
|
|
"number of characters (it is %u characters)", |
|
|
(unsigned)valueString.length()); |
|
|
else { |
|
|
vector<unsigned char> byteString(valueString.length() / 2); |
|
|
size_t strCursor; |
|
|
|
|
|
strCursor = 0; |
|
|
|
|
|
while (strCursor < valueString.length()) { |
|
|
string const hexByte(valueString.substr(strCursor, 2)); |
|
|
|
|
|
unsigned char byte; |
|
|
int rc; |
|
|
|
|
|
rc = sscanf(hexByte.c_str(), "%2hhx", &byte); |
|
|
|
|
|
byteString.push_back(byte); |
|
|
|
|
|
if (rc != 1) |
|
|
throwf("Invalid hex data '%s'", hexByte.c_str()); |
|
|
else |
|
|
strCursor += 2; |
|
|
} |
|
|
retval = value_bytestring(byteString); |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
intValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString.length() < 1) |
|
|
throwf("Integer argument has nothing after the 'i/'"); |
|
|
else { |
|
|
long longValue; |
|
|
char * tailptr; |
|
|
|
|
|
errno = 0; |
|
|
|
|
|
longValue = strtol(valueString.c_str(), &tailptr, 10); |
|
|
|
|
|
if (errno == ERANGE) |
|
|
throwf("'%s' is out of range for a 32 bit integer", |
|
|
valueString.c_str()); |
|
|
else if (errno != 0) |
|
|
throwf("Mysterious failure of strtol(), errno=%d (%s)", |
|
|
errno, strerror(errno)); |
|
|
else { |
|
|
if (*tailptr != '\0') |
|
|
throwf("Integer argument has non-digit crap in it: '%s'", |
|
|
tailptr); |
|
|
else |
|
|
retval = value_int(longValue); |
|
|
} |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
boolValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString == "t" || valueString == "true") |
|
|
retval = value_boolean(true); |
|
|
else if (valueString == "f" || valueString == "false") |
|
|
retval = value_boolean(false); |
|
|
else |
|
|
throwf("Boolean argument has unrecognized value '%s'. " |
|
|
"recognized values are 't', 'f', 'true', and 'false'.", |
|
|
valueString.c_str()); |
|
|
|
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
doubleValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString.length() < 1) |
|
|
throwf("\"Double\" argument has nothing after the 'd/'"); |
|
|
else { |
|
|
double value; |
|
|
char * tailptr; |
|
|
|
|
|
value = strtod(valueString.c_str(), &tailptr); |
|
|
|
|
|
if (*tailptr != '\0') |
|
|
throwf("\"Double\" argument has non-decimal crap in it: '%s'", |
|
|
tailptr); |
|
|
else |
|
|
retval = value_double(value); |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
nilValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString.length() > 0) |
|
|
throwf("Nil argument has something after the 'n/'"); |
|
|
else |
|
|
retval = value_nil(); |
|
|
|
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
i8ValFromParm(string const& valueString) { |
|
|
|
|
|
value retval; |
|
|
|
|
|
if (valueString.length() < 1) |
|
|
throwf("Integer argument has nothing after the 'I/'"); |
|
|
else { |
|
|
long long value; |
|
|
char * tailptr; |
|
|
|
|
|
errno = 0; |
|
|
|
|
|
value = strtoll(valueString.c_str(), &tailptr, 10); |
|
|
|
|
|
if (errno == ERANGE) |
|
|
throwf("'%s' is out of range for a 64 bit integer", |
|
|
valueString.c_str()); |
|
|
else if (errno != 0) |
|
|
throwf("Mysterious failure of strtoll(), errno=%d (%s)", |
|
|
errno, strerror(errno)); |
|
|
else { |
|
|
if (*tailptr != '\0') |
|
|
throwf("64 bit integer argument has non-digit crap " |
|
|
"in it: '%s'", |
|
|
tailptr); |
|
|
else |
|
|
retval = value_i8(value); |
|
|
} |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static value |
|
|
parameterFromArg(string const& paramArg) { |
|
|
|
|
|
value param; |
|
|
|
|
|
try { |
|
|
if (paramArg.substr(0, 2) == "s/") |
|
|
param = value_string(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "h/") |
|
|
param = bytestringValFromParm(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "i/") |
|
|
param = intValFromParm(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "I/") |
|
|
param = i8ValFromParm(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "d/") |
|
|
param = doubleValFromParm(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "b/") |
|
|
param = boolValFromParm(paramArg.substr(2)); |
|
|
else if (paramArg.substr(0, 2) == "n/") |
|
|
param = nilValFromParm(paramArg.substr(2)); |
|
|
else { |
|
|
|
|
|
|
|
|
|
|
|
param = value_string(paramArg); |
|
|
} |
|
|
} catch (exception const& e) { |
|
|
throwf("Failed to interpret parameter argument '%s'. %s", |
|
|
paramArg.c_str(), e.what()); |
|
|
} |
|
|
return param; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static paramList |
|
|
paramListFromParamArgs(vector<string> const& params) { |
|
|
|
|
|
paramList paramList; |
|
|
|
|
|
for (vector<string>::const_iterator p = params.begin(); |
|
|
p != params.end(); ++p) |
|
|
paramList.add(parameterFromArg(*p)); |
|
|
|
|
|
return paramList; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
callWithClient(client * const clientP, |
|
|
string const& methodName, |
|
|
paramList const& paramList, |
|
|
value * const resultP) { |
|
|
|
|
|
rpcPtr myRpcP(methodName, paramList); |
|
|
|
|
|
carriageParm_pstream myCarriageParm; |
|
|
|
|
|
try { |
|
|
myRpcP->call(clientP, &myCarriageParm); |
|
|
} catch (exception const& e) { |
|
|
throwf("RPC failed. %s", e.what()); |
|
|
} |
|
|
*resultP = myRpcP->getResult(); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
dumpResult(value const& result) { |
|
|
|
|
|
cout << "Result:" << endl << endl; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
dumpValue("", result.cValueP); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static list<string> |
|
|
parseWordList(string const& wordString) { |
|
|
|
|
|
list<string> retval; |
|
|
|
|
|
unsigned int pos; |
|
|
|
|
|
pos = 0; |
|
|
|
|
|
while (pos < wordString.length()) { |
|
|
pos = wordString.find_first_not_of(' ', pos); |
|
|
|
|
|
if (pos < wordString.length()) { |
|
|
unsigned int const end = wordString.find_first_of(' ', pos); |
|
|
|
|
|
retval.push_back(wordString.substr(pos, end - pos)); |
|
|
|
|
|
pos = end; |
|
|
} |
|
|
} |
|
|
return retval; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
parseCommand(string const& cmd, |
|
|
string * const methodNameP, |
|
|
vector<string> * const paramListP) { |
|
|
|
|
|
list<string> const wordList(parseWordList(cmd)); |
|
|
|
|
|
list<string>::const_iterator cmdWordP; |
|
|
|
|
|
cmdWordP = wordList.begin(); |
|
|
|
|
|
if (cmdWordP == wordList.end()) |
|
|
throwf("Command '%s' does not have a method name", cmd.c_str()); |
|
|
else { |
|
|
*methodNameP = *cmdWordP++; |
|
|
|
|
|
*paramListP = vector<string>(); |
|
|
|
|
|
while (cmdWordP != wordList.end()) |
|
|
paramListP->push_back(*cmdWordP++); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
doCommand(client_xml * const clientP, |
|
|
string const& methodName, |
|
|
vector<string> const& paramArgs) { |
|
|
|
|
|
value result; |
|
|
|
|
|
callWithClient(clientP, methodName, paramListFromParamArgs(paramArgs), |
|
|
&result); |
|
|
|
|
|
try { |
|
|
dumpResult(result); |
|
|
} catch(exception const& e) { |
|
|
throwf("Error showing result after RPC completed normally. %s", |
|
|
e.what()); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
getCommand(string * const cmdP, |
|
|
bool* const eofP) { |
|
|
|
|
|
const char * cmd; |
|
|
|
|
|
cmd = readline(">"); |
|
|
|
|
|
*eofP = (cmd == NULL); |
|
|
|
|
|
if (cmd != NULL) { |
|
|
*cmdP = string(cmd); |
|
|
|
|
|
free(const_cast<char *>(cmd)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
static void |
|
|
doInteractive(client_xml * const clientP) { |
|
|
|
|
|
bool quitRequested; |
|
|
|
|
|
quitRequested = false; |
|
|
|
|
|
while (!quitRequested) { |
|
|
string cmd; |
|
|
bool eof; |
|
|
|
|
|
getCommand(&cmd, &eof); |
|
|
|
|
|
if (eof) { |
|
|
quitRequested = true; |
|
|
cout << endl; |
|
|
} else { |
|
|
try { |
|
|
string methodName; |
|
|
vector<string> paramArgs; |
|
|
|
|
|
parseCommand(cmd, &methodName, ¶mArgs); |
|
|
|
|
|
doCommand(clientP, methodName, paramArgs); |
|
|
} catch (exception const& e) { |
|
|
cout << "Command failed. " << e.what() << endl; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int |
|
|
main(int const argc, |
|
|
const char ** const argv) { |
|
|
|
|
|
try { |
|
|
cmdlineInfo cmdline(argc, argv); |
|
|
|
|
|
signal(SIGPIPE, SIG_IGN); |
|
|
|
|
|
clientXmlTransport_pstream myTransport( |
|
|
clientXmlTransport_pstream::constrOpt() |
|
|
.fd(cmdline.serverfd)); |
|
|
|
|
|
client_xml myClient(&myTransport); |
|
|
|
|
|
if (cmdline.interactive) { |
|
|
if (cmdline.serverfd == STDIN_FILENO || |
|
|
cmdline.serverfd == STDOUT_FILENO) |
|
|
throwf("Can't use Stdin or Stdout for the server fd when " |
|
|
"running interactively."); |
|
|
doInteractive(&myClient); |
|
|
} else |
|
|
doCommand(&myClient, cmdline.methodName, cmdline.params); |
|
|
|
|
|
} catch (exception const& e) { |
|
|
cerr << "Failed. " << e.what() << endl; |
|
|
} catch (...) { |
|
|
cerr << "Code threw unrecognized exception" << endl; |
|
|
abort(); |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|