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( countFreeFDs() < FREE_FD_BUFFER )
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  // NOTE(austin): this function is not free, but in a few small tests it only
221  // takes about 1.2mS when querying 50k file descriptors.
222  //
223  // If the underlying system calls here fail, this will print an error and
224  // return 0
225 
226 #if !defined(_WINDOWS)
227  int free_fds = 0;
228 
229  struct rlimit limit = { .rlim_cur = 0, .rlim_max = 0 };
230 
231  // Get the current soft limit on the number of file descriptors.
232  if(getrlimit(RLIMIT_NOFILE, &limit) == 0) {
233  // If we have infinite file descriptors, always return FREE_FD_BUFFER so
234  // that we never hit the low-water mark.
235  if( limit.rlim_max == RLIM_INFINITY ) {
236  return FREE_FD_BUFFER;
237  }
238 
239  // Poll the available file descriptors.
240  // The POSIX specification guarantees that rlim_cur will always be less or
241  // equal to the process's initial rlim_max, so we don't need an additonal
242  // bounds check here.
243  if(poll(&pollfds[0], limit.rlim_cur, 1) >= 0) {
244  for(rlim_t i=0; i<limit.rlim_cur; i++) {
245  if(pollfds[i].revents & POLLNVAL) {
246  free_fds++;
247  }
248  }
249  } else {
250  // poll() may fail if interrupted, if the pollfds array is a bad pointer,
251  // if nfds exceeds RLIMIT_NOFILE, or if the system is out of memory.
252  XmlRpcUtil::error("XmlRpcServer::countFreeFDs: poll() failed: %s",
253  strerror(errno));
254  }
255  } else {
256  // The man page for getrlimit says that it can fail if the requested
257  // resource is invalid or the second argument is invalid. I'm not sure
258  // either of these can actually fail in this code, but it's better to
259  // check.
260  XmlRpcUtil::error("XmlRpcServer::countFreeFDs: Could not get open file "
261  "limit, getrlimit() failed: %s", strerror(errno));
262  }
263 
264  return free_fds;
265 #else
266  return FREE_FD_BUFFER;
267 #endif
268 }
269 
270 
271 // Create a new connection object for processing requests from a specific client.
274 {
275  // Specify that the connection object be deleted when it is closed
276  return new XmlRpcServerConnection(s, this, true);
277 }
278 
279 
280 void
282 {
283  _disp.removeSource(sc);
284 }
285 
286 
287 // Stop processing client requests
288 void
290 {
291  _disp.exit();
292 }
293 
294 
295 // Close the server socket file descriptor and stop monitoring connections
296 void
298 {
299  // This closes and destroys all connections as well as closing this socket
300  _disp.clear();
301 }
302 
303 
304 // Introspection support
305 static const std::string LIST_METHODS("system.listMethods");
306 static const std::string METHOD_HELP("system.methodHelp");
307 static const std::string MULTICALL("system.multicall");
308 
309 
310 // List all methods available on a server
312 {
313 public:
315 
317  {
318  _server->listMethods(result);
319  }
320 
321  std::string help() { return std::string("List all methods available on a server as an array of strings"); }
322 };
323 
324 
325 // Retrieve the help string for a named method
327 {
328 public:
330 
331  void execute(XmlRpcValue& params, XmlRpcValue& result)
332  {
333  if (params[0].getType() != XmlRpcValue::TypeString)
334  throw XmlRpcException(METHOD_HELP + ": Invalid argument type");
335 
336  XmlRpcServerMethod* m = _server->findMethod(params[0]);
337  if ( ! m)
338  throw XmlRpcException(METHOD_HELP + ": Unknown method name");
339 
340  result = m->help();
341  }
342 
343  std::string help() { return std::string("Retrieve the help string for a named method"); }
344 };
345 
346 
347 // Specify whether introspection is enabled or not. Default is enabled.
348 void
350 {
351  if (_introspectionEnabled == enabled)
352  return;
353 
354  _introspectionEnabled = enabled;
355 
356  if (enabled)
357  {
358  if ( ! _listMethods)
359  {
360  _listMethods = new ListMethods(this);
361  _methodHelp = new MethodHelp(this);
362  } else {
365  }
366  }
367  else
368  {
371  }
372 }
373 
374 
375 void
377 {
378  int i = 0;
379  result.setSize(_methods.size()+1);
380  for (MethodMap::iterator it=_methods.begin(); it != _methods.end(); ++it)
381  result[i++] = it->first;
382 
383  // Multicall support is built into XmlRpcServerConnection
384  result[i] = MULTICALL;
385 }
386 
387 
388 
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.
int countFreeFDs()
Count number of free file descriptors.
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:123
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)
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 Mar 22 2020 03:20:32