1 2#include "XmlRpcServer.h" 3#include "XmlRpcServerConnection.h" 4#include "XmlRpcServerMethod.h" 5#include "XmlRpcSocket.h" 6#include "XmlRpcUtil.h" 7#include "XmlRpcException.h" 8 9 10using namespace XmlRpc; 11 12 13XmlRpcServer::XmlRpcServer() 14{ 15 _introspectionEnabled = false; 16 _listMethods = 0; 17 _methodHelp = 0; 18} 19 20 21XmlRpcServer::~XmlRpcServer() 22{ 23 this->shutdown(); 24 _methods.clear(); 25 delete _listMethods; 26 delete _methodHelp; 27} 28 29 30// Add a command to the RPC server 31void 32XmlRpcServer::addMethod(XmlRpcServerMethod* method) 33{ 34 _methods[method->name()] = method; 35} 36 37// Remove a command from the RPC server 38void 39XmlRpcServer::removeMethod(XmlRpcServerMethod* method) 40{ 41 MethodMap::iterator i = _methods.find(method->name()); 42 if (i != _methods.end()) 43 _methods.erase(i); 44} 45 46// Remove a command from the RPC server by name 47void 48XmlRpcServer::removeMethod(const std::string& methodName) 49{ 50 MethodMap::iterator i = _methods.find(methodName); 51 if (i != _methods.end()) 52 _methods.erase(i); 53} 54 55 56// Look up a method by name 57XmlRpcServerMethod* 58XmlRpcServer::findMethod(const std::string& name) const 59{ 60 MethodMap::const_iterator i = _methods.find(name); 61 if (i == _methods.end()) 62 return 0; 63 return i->second; 64} 65 66 67// Create a socket, bind to the specified port, and 68// set it in listen mode to make it available for clients. 69bool 70XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/) 71{ 72 int fd = XmlRpcSocket::socket(); 73 if (fd < 0) 74 { 75 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str()); 76 return false; 77 } 78 79 this->setfd(fd); 80 81 // Don't block on reads/writes 82 if ( ! XmlRpcSocket::setNonBlocking(fd)) 83 { 84 this->close(); 85 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 86 return false; 87 } 88 89 // Allow this port to be re-bound immediately so server re-starts are not delayed 90 if ( ! XmlRpcSocket::setReuseAddr(fd)) 91 { 92 this->close(); 93 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str()); 94 return false; 95 } 96 97 // Bind to the specified port on the default interface 98 if ( ! XmlRpcSocket::bind(fd, port)) 99 { 100 this->close(); 101 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str()); 102 return false; 103 } 104 105 // Set in listening mode 106 if ( ! XmlRpcSocket::listen(fd, backlog)) 107 { 108 this->close(); 109 XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 110 return false; 111 } 112 113 XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", port, fd); 114 115 // Notify the dispatcher to listen on this source when we are in work() 116 _disp.addSource(this, XmlRpcDispatch::ReadableEvent); 117 118 return true; 119} 120 121 122// Process client requests for the specified time 123void 124XmlRpcServer::work(double msTime) 125{ 126 XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection"); 127 _disp.work(msTime); 128} 129 130 131 132// Handle input on the server socket by accepting the connection 133// and reading the rpc request. 134unsigned 135XmlRpcServer::handleEvent(unsigned /* mask */) 136{ 137 acceptConnection(); 138 return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd 139} 140 141 142// Accept a client connection request and create a connection to 143// handle method calls from the client. 144void 145XmlRpcServer::acceptConnection() 146{ 147 int s = XmlRpcSocket::accept(this->getfd()); 148 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s); 149 if (s < 0) 150 { 151 //this->close(); 152 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str()); 153 } 154 else if ( ! XmlRpcSocket::setNonBlocking(s)) 155 { 156 XmlRpcSocket::close(s); 157 XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str()); 158 } 159 else // Notify the dispatcher to listen for input on this source when we are in work() 160 { 161 XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection"); 162 _disp.addSource(this->createConnection(s), XmlRpcDispatch::ReadableEvent); 163 } 164} 165 166 167// Create a new connection object for processing requests from a specific client. 168XmlRpcServerConnection* 169XmlRpcServer::createConnection(int s) 170{ 171 // Specify that the connection object be deleted when it is closed 172 return new XmlRpcServerConnection(s, this, true); 173} 174 175 176void 177XmlRpcServer::removeConnection(XmlRpcServerConnection* sc) 178{ 179 _disp.removeSource(sc); 180} 181 182 183// Stop processing client requests 184void 185XmlRpcServer::exit() 186{ 187 _disp.exit(); 188} 189 190 191// Close the server socket file descriptor and stop monitoring connections 192void 193XmlRpcServer::shutdown() 194{ 195 // This closes and destroys all connections as well as closing this socket 196 _disp.clear(); 197} 198 199 200// Introspection support 201static const std::string LIST_METHODS("system.listMethods"); 202static const std::string METHOD_HELP("system.methodHelp"); 203static const std::string MULTICALL("system.multicall"); 204 205 206// List all methods available on a server 207class ListMethods : public XmlRpcServerMethod 208{ 209public: 210 ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {} 211 212 void execute(XmlRpcValue& /* params */, XmlRpcValue& result) 213 { 214 _server->listMethods(result); 215 } 216 217 std::string help() { return std::string("List all methods available on a server as an array of strings"); } 218}; 219 220 221// Retrieve the help string for a named method 222class MethodHelp : public XmlRpcServerMethod 223{ 224public: 225 MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {} 226 227 void execute(XmlRpcValue& params, XmlRpcValue& result) 228 { 229 if (params[0].getType() != XmlRpcValue::TypeString) 230 throw XmlRpcException(METHOD_HELP + ": Invalid argument type"); 231 232 XmlRpcServerMethod* m = _server->findMethod(params[0]); 233 if ( ! m) 234 throw XmlRpcException(METHOD_HELP + ": Unknown method name"); 235 236 result = m->help(); 237 } 238 239 std::string help() { return std::string("Retrieve the help string for a named method"); } 240}; 241 242 243// Specify whether introspection is enabled or not. Default is enabled. 244void 245XmlRpcServer::enableIntrospection(bool enabled) 246{ 247 if (_introspectionEnabled == enabled) 248 return; 249 250 _introspectionEnabled = enabled; 251 252 if (enabled) 253 { 254 if ( ! _listMethods) 255 { 256 _listMethods = new ListMethods(this); 257 _methodHelp = new MethodHelp(this); 258 } else { 259 addMethod(_listMethods); 260 addMethod(_methodHelp); 261 } 262 } 263 else 264 { 265 removeMethod(LIST_METHODS); 266 removeMethod(METHOD_HELP); 267 } 268} 269 270 271void 272XmlRpcServer::listMethods(XmlRpcValue& result) 273{ 274 int i = 0; 275 result.setSize(_methods.size()+1); 276 for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it) 277 result[i++] = it->first; 278 279 // Multicall support is built into XmlRpcServerConnection 280 result[i] = MULTICALL; 281} 282 283 284 285