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 
10 using namespace XmlRpc;
11 
12 
XmlRpcServer()13 XmlRpcServer::XmlRpcServer()
14 {
15   _introspectionEnabled = false;
16   _listMethods = 0;
17   _methodHelp = 0;
18 }
19 
20 
~XmlRpcServer()21 XmlRpcServer::~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
31 void
addMethod(XmlRpcServerMethod * method)32 XmlRpcServer::addMethod(XmlRpcServerMethod* method)
33 {
34   _methods[method->name()] = method;
35 }
36 
37 // Remove a command from the RPC server
38 void
removeMethod(XmlRpcServerMethod * method)39 XmlRpcServer::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
47 void
removeMethod(const std::string & methodName)48 XmlRpcServer::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
57 XmlRpcServerMethod*
findMethod(const std::string & name) const58 XmlRpcServer::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.
69 bool
bindAndListen(int port,int backlog)70 XmlRpcServer::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
123 void
work(double msTime)124 XmlRpcServer::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.
134 unsigned
handleEvent(unsigned)135 XmlRpcServer::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.
144 void
acceptConnection()145 XmlRpcServer::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.
168 XmlRpcServerConnection*
createConnection(int s)169 XmlRpcServer::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 
176 void
removeConnection(XmlRpcServerConnection * sc)177 XmlRpcServer::removeConnection(XmlRpcServerConnection* sc)
178 {
179   _disp.removeSource(sc);
180 }
181 
182 
183 // Stop processing client requests
184 void
exit()185 XmlRpcServer::exit()
186 {
187   _disp.exit();
188 }
189 
190 
191 // Close the server socket file descriptor and stop monitoring connections
192 void
shutdown()193 XmlRpcServer::shutdown()
194 {
195   // This closes and destroys all connections as well as closing this socket
196   _disp.clear();
197 }
198 
199 
200 // Introspection support
201 static const std::string LIST_METHODS("system.listMethods");
202 static const std::string METHOD_HELP("system.methodHelp");
203 static const std::string MULTICALL("system.multicall");
204 
205 
206 // List all methods available on a server
207 class ListMethods : public XmlRpcServerMethod
208 {
209 public:
ListMethods(XmlRpcServer * s)210   ListMethods(XmlRpcServer* s) : XmlRpcServerMethod(LIST_METHODS, s) {}
211 
execute(XmlRpcValue &,XmlRpcValue & result)212   void execute(XmlRpcValue& /* params */, XmlRpcValue& result)
213   {
214     _server->listMethods(result);
215   }
216 
help()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
222 class MethodHelp : public XmlRpcServerMethod
223 {
224 public:
MethodHelp(XmlRpcServer * s)225   MethodHelp(XmlRpcServer* s) : XmlRpcServerMethod(METHOD_HELP, s) {}
226 
execute(XmlRpcValue & params,XmlRpcValue & result)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 
help()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.
244 void
enableIntrospection(bool enabled)245 XmlRpcServer::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 
271 void
listMethods(XmlRpcValue & result)272 XmlRpcServer::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