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 #include <sys/resource.h>
15 
16 using namespace XmlRpc;
17 
18 const int XmlRpcServer::FREE_FD_BUFFER = 32;
20 
22  : _introspectionEnabled(false),
23  _listMethods(0),
24  _methodHelp(0),
25  _port(0),
26  _accept_error(false),
27  _accept_retry_time_sec(0.0)
28 {
29  struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
30  unsigned int max_files = 1024;
31 
32  if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
33  max_files = limit.rlim_max;
34  if( limit.rlim_max == RLIM_INFINITY ) {
35  max_files = 0;
36  }
37  } else {
38  XmlRpcUtil::error("Could not get open file limit: %s", strerror(errno));
39  }
40  pollfds.resize(max_files);
41  for(unsigned int i=0; i<max_files; i++) {
42  // Set up file descriptor query for all events.
43  pollfds[i].fd = i;
44  pollfds[i].events = POLLIN | POLLPRI | POLLOUT;
45  }
46 
47  // Ask dispatch not to close this socket if it becomes unreadable.
48  setKeepOpen(true);
49 }
50 
51 
53 {
54  this->shutdown();
55  _methods.clear();
56  delete _listMethods;
57  delete _methodHelp;
58 }
59 
60 
61 // Add a command to the RPC server
62 void
64 {
65  _methods[method->name()] = method;
66 }
67 
68 // Remove a command from the RPC server
69 void
71 {
72  MethodMap::iterator i = _methods.find(method->name());
73  if (i != _methods.end())
74  _methods.erase(i);
75 }
76 
77 // Remove a command from the RPC server by name
78 void
79 XmlRpcServer::removeMethod(const std::string& methodName)
80 {
81  MethodMap::iterator i = _methods.find(methodName);
82  if (i != _methods.end())
83  _methods.erase(i);
84 }
85 
86 
87 // Look up a method by name
89 XmlRpcServer::findMethod(const std::string& name) const
90 {
91  MethodMap::const_iterator i = _methods.find(name);
92  if (i == _methods.end())
93  return 0;
94  return i->second;
95 }
96 
97 
98 // Create a socket, bind to the specified port, and
99 // set it in listen mode to make it available for clients.
100 bool
101 XmlRpcServer::bindAndListen(int port, int backlog /*= 5*/)
102 {
103  int fd = XmlRpcSocket::socket();
104  if (fd < 0)
105  {
106  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not create socket (%s).", XmlRpcSocket::getErrorMsg().c_str());
107  return false;
108  }
109 
110  this->setfd(fd);
111 
112  // Don't block on reads/writes
113  if ( ! XmlRpcSocket::setNonBlocking(fd))
114  {
115  this->close();
116  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
117  return false;
118  }
119 
120  // Allow this port to be re-bound immediately so server re-starts are not delayed
121  if ( ! XmlRpcSocket::setReuseAddr(fd))
122  {
123  this->close();
124  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set SO_REUSEADDR socket option (%s).", XmlRpcSocket::getErrorMsg().c_str());
125  return false;
126  }
127 
128  // Bind to the specified port on the default interface
129  if ( ! XmlRpcSocket::bind(fd, port))
130  {
131  this->close();
132  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not bind to specified port (%s).", XmlRpcSocket::getErrorMsg().c_str());
133  return false;
134  }
135 
136  // Set in listening mode
137  if ( ! XmlRpcSocket::listen(fd, backlog))
138  {
139  this->close();
140  XmlRpcUtil::error("XmlRpcServer::bindAndListen: Could not set socket in listening mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
141  return false;
142  }
143 
145 
146  XmlRpcUtil::log(2, "XmlRpcServer::bindAndListen: server listening on port %d fd %d", _port, fd);
147 
148  // Notify the dispatcher to listen on this source when we are in work()
150 
151  return true;
152 }
153 
154 
155 // Process client requests for the specified time
156 void
157 XmlRpcServer::work(double msTime)
158 {
159  XmlRpcUtil::log(2, "XmlRpcServer::work: waiting for a connection");
162  }
163  _disp.work(msTime);
164 }
165 
166 
167 
168 // Handle input on the server socket by accepting the connection
169 // and reading the rpc request.
170 unsigned
172 {
173  return acceptConnection();
174 }
175 
176 
177 // Accept a client connection request and create a connection to
178 // handle method calls from the client.
179 unsigned
181 {
182  int s = XmlRpcSocket::accept(this->getfd());
183  XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: socket %d", s);
184  if (s < 0)
185  {
186  //this->close();
187  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not accept connection (%s).", XmlRpcSocket::getErrorMsg().c_str());
188 
189  // Note that there was an accept error; retry in 1 second
190  _accept_error = true;
192  return 0; // Stop monitoring this FD
193  }
194  else if( countFreeFDs() < FREE_FD_BUFFER )
195  {
197  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Rejecting client, not enough free file descriptors");
198  }
199  else if ( ! XmlRpcSocket::setNonBlocking(s))
200  {
202  XmlRpcUtil::error("XmlRpcServer::acceptConnection: Could not set socket to non-blocking input mode (%s).", XmlRpcSocket::getErrorMsg().c_str());
203  }
204  else // Notify the dispatcher to listen for input on this source when we are in work()
205  {
206  XmlRpcUtil::log(2, "XmlRpcServer::acceptConnection: creating a connection");
208  }
209  return XmlRpcDispatch::ReadableEvent; // Continue to monitor this fd
210 }
211 
213  // NOTE(austin): this function is not free, but in a few small tests it only
214  // takes about 1.2mS when querying 50k file descriptors.
215  //
216  // If the underlying system calls here fail, this will print an error and
217  // return 0
218  int free_fds = 0;
219 
220  struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
221 
222  // Get the current soft limit on the number of file descriptors.
223  if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
224  // If we have infinite file descriptors, always return FREE_FD_BUFFER so
225  // that we never hit the low-water mark.
226  if( limit.rlim_max == RLIM_INFINITY ) {
227  return FREE_FD_BUFFER;
228  }
229 
230  // Poll the available file descriptors.
231  // The POSIX specification guarantees that rlim_cur will always be less or
232  // equal to the process's initial rlim_max, so we don't need an additonal
233  // bounds check here.
234  if(poll(&pollfds[0], limit.rlim_cur, 1) >= 0) {
235  for(rlim_t i=0; i<limit.rlim_cur; i++) {
236  if(pollfds[i].revents & POLLNVAL) {
237  free_fds++;
238  }
239  }
240  } else {
241  // poll() may fail if interrupted, if the pollfds array is a bad pointer,
242  // if nfds exceeds RLIMIT_NOFILE, or if the system is out of memory.
243  XmlRpcUtil::error("XmlRpcServer::countFreeFDs: poll() failed: %s",
244  strerror(errno));
245  }
246  } else {
247  // The man page for getrlimit says that it can fail if the requested
248  // resource is invalid or the second argument is invalid. I'm not sure
249  // either of these can actually fail in this code, but it's better to
250  // check.
251  XmlRpcUtil::error("XmlRpcServer::countFreeFDs: Could not get open file "
252  "limit, getrlimit() failed: %s", strerror(errno));
253  }
254 
255  return free_fds;
256 }
257 
258 
259 // Create a new connection object for processing requests from a specific client.
262 {
263  // Specify that the connection object be deleted when it is closed
264  return new XmlRpcServerConnection(s, this, true);
265 }
266 
267 
268 void
270 {
271  _disp.removeSource(sc);
272 }
273 
274 
275 // Stop processing client requests
276 void
278 {
279  _disp.exit();
280 }
281 
282 
283 // Close the server socket file descriptor and stop monitoring connections
284 void
286 {
287  // This closes and destroys all connections as well as closing this socket
288  _disp.clear();
289 }
290 
291 
292 // Introspection support
293 static const std::string LIST_METHODS("system.listMethods");
294 static const std::string METHOD_HELP("system.methodHelp");
295 static const std::string MULTICALL("system.multicall");
296 
297 
298 // List all methods available on a server
300 {
301 public:
303 
305  {
306  _server->listMethods(result);
307  }
308 
309  std::string help() { return std::string("List all methods available on a server as an array of strings"); }
310 };
311 
312 
313 // Retrieve the help string for a named method
315 {
316 public:
318 
319  void execute(XmlRpcValue& params, XmlRpcValue& result)
320  {
321  if (params[0].getType() != XmlRpcValue::TypeString)
322  throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
323 
324  XmlRpcServerMethod* m = _server->findMethod(params[0]);
325  if ( ! m)
326  throw XmlRpcException(METHOD_HELP + ": Unknown method name");
327 
328  result = m->help();
329  }
330 
331  std::string help() { return std::string("Retrieve the help string for a named method"); }
332 };
333 
334 
335 // Specify whether introspection is enabled or not. Default is enabled.
336 void
338 {
339  if (_introspectionEnabled == enabled)
340  return;
341 
342  _introspectionEnabled = enabled;
343 
344  if (enabled)
345  {
346  if ( ! _listMethods)
347  {
348  _listMethods = new ListMethods(this);
349  _methodHelp = new MethodHelp(this);
350  } else {
353  }
354  }
355  else
356  {
359  }
360 }
361 
362 
363 void
365 {
366  int i = 0;
367  result.setSize(_methods.size()+1);
368  for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
369  result[i++] = it->first;
370 
371  // Multicall support is built into XmlRpcServerConnection
372  result[i] = MULTICALL;
373 }
374 
375 
376 
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.
static bool setReuseAddr(int socket)
static const double ACCEPT_RETRY_INTERVAL_SEC
Definition: XmlRpcServer.h:120
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:127
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.
XmlRpcServerMethod * findMethod(const std::string &name) const
Look up a method by name.
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.
int countFreeFDs()
Count number of free file descriptors.
virtual ~XmlRpcServer()
Destructor.
static std::string getErrorMsg()
Returns message corresponding to last error.
int getfd() const
Return the file descriptor being monitored.
Definition: XmlRpcSource.h:27
Abstract class representing a single RPC method.
XmlRpcServerMethod * _listMethods
Definition: XmlRpcServer.h:111
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:104
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:117
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:39
static bool listen(int socket, int backlog)
Set socket in listen mode.
static const int FREE_FD_BUFFER
Definition: XmlRpcServer.h:125
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:112
void addSource(XmlRpcSource *source, unsigned eventMask)
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
autogenerated on Sun Feb 3 2019 03:29:51