XmlRpcServer.cpp
Go to the documentation of this file.
1 // this file modified by Morgan Quigley on 22 Apr 2008.
2 // added features: server can be opened on port 0 and you can read back
3 // what port the OS gave you
4 
9 #include "xmlrpcpp/XmlRpcUtil.h"
11 
12 #include <errno.h>
13 #include <string.h>
14 #if !defined(_WINDOWS)
15 # include <sys/resource.h>
16 #else
17 # include <winsock2.h>
18 #endif
19 
20 using namespace XmlRpc;
21 
22 const int XmlRpcServer::FREE_FD_BUFFER = 32;
24 
26  : _introspectionEnabled(false),
27  _listMethods(0),
28  _methodHelp(0),
29  _port(0),
30  _accept_error(false),
31  _accept_retry_time_sec(0.0)
32 {
33 #if !defined(_WINDOWS)
34  struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
35  unsigned int max_files = 1024;
36 
37  if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
38  max_files = limit.rlim_max;
39  if( limit.rlim_max == RLIM_INFINITY ) {
40  max_files = 0;
41  }
42  } else {
43  XmlRpcUtil::error("Could not get open file limit: %s", strerror(errno));
44  }
45  pollfds.resize(max_files);
46  for(unsigned int i=0; i<max_files; i++) {
47  // Set up file descriptor query for all events.
48  pollfds[i].fd = i;
49  pollfds[i].events = POLLIN | POLLPRI | POLLOUT;
50  }
51 #endif
52 
53  // Ask dispatch not to close this socket if it becomes unreadable.
54  setKeepOpen(true);
55 }
56 
57 
59 {
60  this->shutdown();
61  _methods.clear();
62  delete _listMethods;
63  delete _methodHelp;
64 }
65 
66 
67 // Add a command to the RPC server
68 void
70 {
71  _methods[method->name()] = method;
72 }
73 
74 // Remove a command from the RPC server
75 void
77 {
78  MethodMap::iterator i = _methods.find(method->name());
79  if (i != _methods.end())
80  _methods.erase(i);
81 }
82 
83 // Remove a command from the RPC server by name
84 void
85 XmlRpcServer::removeMethod(const std::string& methodName)
86 {
87  MethodMap::iterator i = _methods.find(methodName);
88  if (i != _methods.end())
89  _methods.erase(i);
90 }
91 
92 
93 // Look up a method by name
95 XmlRpcServer::findMethod(const std::string& name) const
96 {
97  MethodMap::const_iterator i = _methods.find(name);
98  if (i == _methods.end())
99  return 0;
100  return i->second;
101 }
102 
103 
104 // Create a socket, bind to the specified port, and
105 // set it in listen mode to make it available for clients.
106 bool
107 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
108 {
109  int fd = XmlRpcSocket::socket();
110  if (fd < 0)
111  {
112  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
113  return false;
114  }
115 
116  this->setfd(fd);
117 
118  // Don't block on reads/writes
119  if ( ! XmlRpcSocket::setNonBlocking(fd))
120  {
121  this->close();
122  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
123  return false;
124  }
125 
126  // Allow this port to be re-bound immediately so server re-starts are not delayed
127  if ( ! XmlRpcSocket::setReuseAddr(fd))
128  {
129  this->close();
130  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
131  return false;
132  }
133 
134  // Bind to the specified port on the default interface
135  if ( ! XmlRpcSocket::bind(fd, port))
136  {
137  this->close();
138  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
139  return false;
140  }
141 
142  // Set in listening mode
143  if ( ! XmlRpcSocket::listen(fd, backlog))
144  {
145  this->close();
146  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
147  return false;
148  }
149 
151 
152  XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", _port, fd);
153 
154  // Notify the dispatcher to listen on this source when we are in work()
156 
157  return true;
158 }
159 
160 
161 // Process client requests for the specified time
162 void
163 XmlRpcServer::work(double msTime)
164 {
165  XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
168  }
169  _disp.work(msTime);
170 }
171 
172 
173 
174 // Handle input on the server socket by accepting the connection
175 // and reading the rpc request.
176 unsigned
178 {
179  return acceptConnection();
180 }
181 
182 
183 // Accept a client connection request and create a connection to
184 // handle method calls from the client.
185 unsigned
187 {
188  int s = XmlRpcSocket::accept(this->getfd());
189  XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
190  if (s < 0)
191  {
192  //this->close();
193  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
194 
195  // Note that there was an accept error; retry in 1 second
196  _accept_error = true;
198  return 0; // Stop monitoring this FD
199  }
200  else if ( !enoughFreeFDs() )
201  {
203  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Rejecting client, not enough free file descriptors");
204  }
205  else if ( ! XmlRpcSocket::setNonBlocking(s))
206  {
208  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
209  }
210  else // Notify the dispatcher to listen for input on this source when we are in work()
211  {
212  _accept_error = false;
213  XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
215  }
216  return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd
217 }
218 
220  // This function is just to check if enough FDs are there.
221  //
222  // If the underlying system calls here fail, this will print an error and
223  // return false
224 
225 #if !defined(_WINDOWS)
226  int free_fds = 0;
227 
228  struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
229 
230  // Get the current soft limit on the number of file descriptors.
231  if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
232  // If we have infinite file descriptors, always return true.
233  if( limit.rlim_max == RLIM_INFINITY ) {
234  return true;
235  }
236 
237  // Poll the available file descriptors.
238  // The POSIX specification guarantees that rlim_cur will always be less or
239  // equal to the process's initial rlim_max, so we don't need an additional
240  // bounds check here.
241  if(poll(&pollfds[0], limit.rlim_cur, 1) >= 0) {
242  for(rlim_t i=0; i<limit.rlim_cur; i++) {
243  if(pollfds[i].revents & POLLNVAL) {
244  free_fds++;
245  }
246  if (free_fds >= FREE_FD_BUFFER) {
247  // Checked enough FDs are not opened.
248  return true;
249  }
250  }
251  } else {
252  // poll() may fail if interrupted, if the pollfds array is a bad pointer,
253  // if nfds exceeds RLIMIT_NOFILE, or if the system is out of memory.
254  XmlRpcUtil::error("XmlRpcServer::enoughFreeFDs: poll() failed: %s",
255  strerror(errno));
256  }
257  } else {
258  // The man page for getrlimit says that it can fail if the requested
259  // resource is invalid or the second argument is invalid. I'm not sure
260  // either of these can actually fail in this code, but it's better to
261  // check.
262  XmlRpcUtil::error("XmlRpcServer::enoughFreeFDs: Could not get open file "
263  "limit, getrlimit() failed: %s", strerror(errno));
264  }
265 
266  return false;
267 #else
268  return true;
269 #endif
270 }
271 
272 
273 // Create a new connection object for processing requests from a specific client.
276 {
277  // Specify that the connection object be deleted when it is closed
278  return new XmlRpcServerConnection(s, this, true);
279 }
280 
281 
282 void
284 {
285  _disp.removeSource(sc);
286 }
287 
288 
289 // Stop processing client requests
290 void
292 {
293  _disp.exit();
294 }
295 
296 
297 // Close the server socket file descriptor and stop monitoring connections
298 void
300 {
301  // This closes and destroys all connections as well as closing this socket
302  _disp.clear();
303 }
304 
305 
306 // Introspection support
307 static const std::string LIST_METHODS("system.listMethods");
308 static const std::string METHOD_HELP("system.methodHelp");
309 static const std::string MULTICALL("system.multicall");
310 
311 
312 // List all methods available on a server
314 {
315 public:
317 
319  {
320  _server->listMethods(result);
321  }
322 
323  std::string help() { return std::string("List all methods available on a server as an array of strings"); }
324 };
325 
326 
327 // Retrieve the help string for a named method
329 {
330 public:
332 
333  void execute(XmlRpcValue& params, XmlRpcValue& result)
334  {
335  if (params[0].getType() != XmlRpcValue::TypeString)
336  throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
337 
338  XmlRpcServerMethod* m = _server->findMethod(params[0]);
339  if ( ! m)
340  throw XmlRpcException(METHOD_HELP + ": Unknown method name");
341 
342  result = m->help();
343  }
344 
345  std::string help() { return std::string("Retrieve the help string for a named method"); }
346 };
347 
348 
349 // Specify whether introspection is enabled or not. Default is enabled.
350 void
352 {
353  if (_introspectionEnabled == enabled)
354  return;
355 
356  _introspectionEnabled = enabled;
357 
358  if (enabled)
359  {
360  if ( ! _listMethods)
361  {
362  _listMethods = new ListMethods(this);
363  _methodHelp = new MethodHelp(this);
364  } else {
367  }
368  }
369  else
370  {
373  }
374 }
375 
376 
377 void
379 {
380  int i = 0;
381  result.setSize(_methods.size()+1);
382  for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
383  result[i++] = it->first;
384 
385  // Multicall support is built into XmlRpcServerConnection
386  result[i] = MULTICALL;
387 }
388 
389 
390 
A class to handle XML RPC requests from a particular client.
virtual XmlRpcServerConnection * createConnection(int socket)
Create a new connection object for processing requests from a specific client.
int getfd() const
Return the file descriptor being monitored.
Definition: XmlRpcSource.h:27
static bool setReuseAddr(int socket)
static const double ACCEPT_RETRY_INTERVAL_SEC
Definition: XmlRpcServer.h:122
XmlRpcServerMethod * findMethod(const std::string &name) const
Look up a method by name.
RPC method arguments and results are represented by Values.
Definition: XmlRpcValue.h:24
static const std::string METHOD_HELP("system.methodHelp")
std::vector< struct pollfd > pollfds
Definition: XmlRpcServer.h:130
static int get_port(int socket)
void execute(XmlRpcValue &, XmlRpcValue &result)
Execute the method. Subclasses must provide a definition for this method.
static int accept(int socket)
Accept a client connection request.
std::string help()
void shutdown()
Close all connections with clients and the socket file descriptor.
void work(double msTime)
Process client requests for the specified time.
bool bindAndListen(int port, int backlog=5)
XmlRpcServer()
Create a server object.
void addMethod(XmlRpcServerMethod *method)
Add a command to the RPC server.
XmlRpcServer s
Definition: HelloServer.cpp:11
void removeMethod(XmlRpcServerMethod *method)
Remove a command from the RPC server.
void removeSource(XmlRpcSource *source)
void setKeepOpen(bool b=true)
Specify whether the file descriptor should be kept open if it is no longer monitored.
Definition: XmlRpcSource.h:34
static void error(const char *fmt,...)
Dump error messages somewhere.
Definition: XmlRpcUtil.cpp:95
virtual unsigned handleEvent(unsigned eventType)
Handle client connection requests.
void clear()
Clear all sources from the monitored sources list. Sources are closed.
virtual ~XmlRpcServer()
Destructor.
static std::string getErrorMsg()
Returns message corresponding to last error.
Abstract class representing a single RPC method.
XmlRpcServerMethod * _listMethods
Definition: XmlRpcServer.h:113
virtual void close()
Close the owned fd. If deleteOnClose was specified at construction, the object is deleted...
void setfd(int fd)
Specify the file descriptor to monitor.
Definition: XmlRpcSource.h:29
static bool bind(int socket, int port)
Bind to a specified port.
void work(double msTime)
XmlRpcDispatch _disp
Definition: XmlRpcServer.h:106
virtual void removeConnection(XmlRpcServerConnection *)
Remove a connection from the dispatcher.
void setSize(int size)
Specify the size for array values. Array values will grow beyond this size if needed.
Definition: XmlRpcValue.h:130
static const std::string MULTICALL("system.multicall")
void execute(XmlRpcValue &params, XmlRpcValue &result)
Execute the method. Subclasses must provide a definition for this method.
void enableIntrospection(bool enabled=true)
Specify whether introspection is enabled or not. Default is not enabled.
A class to handle XML RPC requests.
Definition: XmlRpcServer.h:41
static bool listen(int socket, int backlog)
Set socket in listen mode.
static const int FREE_FD_BUFFER
Definition: XmlRpcServer.h:127
static int socket()
Creates a stream (TCP) socket. Returns -1 on failure.
std::string help()
static void close(int socket)
Closes a socket.
XmlRpcServerMethod * _methodHelp
Definition: XmlRpcServer.h:114
void addSource(XmlRpcSource *source, unsigned eventMask)
bool enoughFreeFDs()
Check if enough number of free file descriptors.
virtual std::string help()
static const std::string LIST_METHODS("system.listMethods")
virtual unsigned acceptConnection()
Accept a client connection request.
void exit()
Exit from work routine.
static bool setNonBlocking(int socket)
Sets a stream (TCP) socket to perform non-blocking IO. Returns false on failure.
ListMethods(XmlRpcServer *s)
void exit()
Temporarily stop processing client requests and exit the work() method.
void listMethods(XmlRpcValue &result)
Introspection support.
MethodHelp(XmlRpcServer *s)
static void log(int level, const char *fmt,...)
Dump messages somewhere.
Definition: XmlRpcUtil.cpp:80
std::string & name()
Returns the name of the method.


xmlrpcpp
Author(s): Chris Morley, Konstantin Pilipchuk, Morgan Quigley, Austin Hendrix, Dirk Thomas
autogenerated on Mon Oct 19 2020 03:23:56