Server.cc
Go to the documentation of this file.
1 //
2 // Copyright (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)
3 // Network Research Group (NET)
4 // Schloss Birlinghoven, 53754 Sankt Augustin, GERMANY
5 // Contact: support@wiback.org
6 //
7 // This file is part of the SENF code tree.
8 // It is licensed under the 3-clause BSD License (aka New BSD License).
9 // See LICENSE.txt in the top level directory for details or visit
10 // https://opensource.org/licenses/BSD-3-Clause
11 //
12 
13 
17 #include "Server.hh"
18 //#include "Server.ih"
19 
20 // Custom includes
21 #include <boost/algorithm/string/trim.hpp>
22 #include <boost/bind.hpp>
24 #include <senf/Utils/membind.hh>
26 #include <senf/Utils/CpuStat.hh>
27 #include <senf/Version.hh>
28 #include "LineEditor.hh"
29 #include "ScopedDirectory.hh"
30 #include "Sysdir.hh"
31 #include "SysInfo.hh"
32 #include "ParsedCommand.hh"
33 
34 #define prefix_
35 //-/////////////////////////////////////////////////////////////////////////////////////////////////
36 
37 #ifdef SENF_DEBUG
38 # define BUILD_TYPE "development"
39 #else
40 # define BUILD_TYPE "production"
41 #endif
42 
43 namespace {
44  senf::console::SysInfo::Proxy addSysInfo ("SENF",
45  "SENF: The Simple and Extensible Network Framework\n"
46  " (c) 2020 Fraunhofer Institute for Applied Information Technology (FIT)\n"
47  " Contact: http://wiback.org\n"
48  " Version: " SENF_LIB_VERSION " Revision number: " SENF_REVISION "\n"
49  " Build-type: " BUILD_TYPE ", SenfLog compile time limit: " +
50  senf::str(senf::log::LEVELNAMES[senf::SenfLog::compileLimit::value]), 0);
51 
52  senf::CpuStatConsole console;
53 }
54 
55 //-/////////////////////////////////////////////////////////////////////////////////////////////////
56 // senf::console::detail::SocketStreamSink
57 
58 prefix_ std::streamsize senf::console::detail::SocketStreamSink::write(const char * s,
59  std::streamsize n)
60 {
61 // since handle is now non blocking we done check for writeable
62  try {
63 // if (client_.handle().writeable()) {
64  std::string data (s, n);
65  client_.write(data);
66 // }
67  }
68  catch (...) {}
69  return n;
70 }
71 
72 //-/////////////////////////////////////////////////////////////////////////////////////////////////
73 // senf::console::Server
74 
75 senf::console::ScopedDirectory<> senf::console::Server::sysConsoleDir_;
76 
77 prefix_ senf::console::DirectoryNode & senf::console::Server::sysConsoleDir()
78 {
79  if (not sysdir().hasChild("console"))
80  sysdir().add("console", sysConsoleDir_);
81  return sysConsoleDir_.node();
82 }
83 
86 {
87  senf::TCPv4ServerSocketHandle handle (address);
88  Server & server (senf::console::Server::start(handle));
89  SENF_LOG((Server::SENFLogArea)(log::NOTICE)(
90  "Console server started at " << address ));
91  return server;
92 }
93 
96 {
97  senf::TCPv6ServerSocketHandle handle (address);
98  Server & server (senf::console::Server::start(handle));
99  SENF_LOG((Server::SENFLogArea)(log::NOTICE)(
100  "Console server started at " << address ));
101  return server;
102 }
103 
105 {
106  boost::intrusive_ptr<Server> p (new Server(handle));
107  detail::ServerManager::add(boost::intrusive_ptr<Server>(p));
108  return *p;
109 }
110 
111 prefix_ senf::console::Server::Server(ServerHandle handle)
112  : handle_ (handle),
113  event_ ("senf::console::Server", senf::membind(&Server::newClient, this),
114  handle_, scheduler::FdEvent::EV_READ),
115  root_ (senf::console::root().thisptr()), mode_ (Automatic),
116  name_ (::program_invocation_short_name)
117 {
118  namespace fty = console::factory;
119  sysConsoleDir().add("self", fty::Command(&Server::consoleSelf, this)
120  .doc("Get the directory of the current network client. Example usage:\n"
121  "\n"
122  "Just get the directory\n"
123  " $ /sys/console/self\n"
124  " <Directory '/sys/console/client-xxx.xxx.xxx.xxx:xxx'>\n"
125  "\n"
126  "Get all properties of the currently connected client\n"
127  " $ /sys/console/self { properties; }") );
128 }
129 
130 prefix_ void senf::console::Server::newClient(int event)
131 {
132  ServerHandle::ClientHandle client (handle_.accept());
133  boost::intrusive_ptr<Client> p (new Client(*this, client));
134  clients_.insert( p );
135  SENF_LOG(( "Registered new client " << client.peer() ));
136 }
137 
138 prefix_ void senf::console::Server::removeClient(Client & client)
139 {
140  SENF_LOG_BLOCK(({
141  log << "Disposing client ";
142  try {
143  log << client.handle().peer();
144  }
145  catch (senf::SystemException & ex) {
146  log << "(dead socket)";
147  }
148  }));
149  // THIS DELETES THE CLIENT INSTANCE !!
150  clients_.erase(boost::intrusive_ptr<Client>(&client));
151 }
152 
154 {
155  welcomeMsg_ = message;
156  if (!message.empty() && message[message.size()-1] != '\n')
157  welcomeMsg_ += '\n';
158  return *this;
159 }
160 
161 prefix_ boost::shared_ptr<senf::console::DirectoryNode> senf::console::Server::consoleSelf(std::ostream & os)
162 {
163  return Client::get(os).dir_.node().thisptr();
164 }
165 
166 //-/////////////////////////////////////////////////////////////////////////////////////////////////
167 // senf::console::detail::DumbClientReader
168 
169 prefix_ senf::console::detail::DumbClientReader::DumbClientReader(Client & client)
170  : ClientReader(client), promptLen_ (0), promptActive_ (false)
171 {
172  showPrompt();
174  handle(), 16384u, ReadUntil("\n"), senf::membind(&DumbClientReader::clientData, this) );
175 }
176 
177 prefix_ void
178 senf::console::detail::DumbClientReader::clientData(senf::ReadHelper<ClientHandle>::ptr helper)
179 {
180  if (helper->error() || handle().eof()) {
181  // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
182  stopClient();
183  return;
184  }
185 
186  promptLen_ = 0;
187  promptActive_ = false;
188 
189  std::string data (tail_ + helper->data());
190  tail_ = helper->tail();
191  boost::trim(data); // Gets rid of superfluous \r or \n characters
192  handleInput(data);
193 
194  showPrompt();
196  handle(), 16384u, ReadUntil("\n"), senf::membind(&DumbClientReader::clientData, this) );
197 }
198 
199 prefix_ void senf::console::detail::DumbClientReader::showPrompt()
200 {
201  std::string prompt (promptString());
202  prompt += " ";
203 
204  stream() << std::flush;
205  promptLen_ = prompt.size();
206  promptActive_ = true;
207  v_write(prompt);
208 }
209 
210 prefix_ void senf::console::detail::DumbClientReader::v_disablePrompt()
211 {
212  if (promptActive_ && promptLen_ > 0) {
213  stream() << '\r' << std::string(' ', promptLen_) << '\r';
214  promptLen_ = 0;
215  }
216 }
217 
218 prefix_ void senf::console::detail::DumbClientReader::v_enablePrompt()
219 {
220  if (promptActive_ && ! promptLen_)
221  showPrompt();
222 }
223 
224 prefix_ void senf::console::detail::DumbClientReader::v_write(std::string const & data)
225 {
226  try {
227  handle().write(data);
228  }
229  catch (senf::ExceptionMixin & ex) {
230  SENF_LOG(("unexpected failure writing to socket:" << ex.message()));
231  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
232  catch (...) {}
233  }
234  catch (std::exception & ex) {
235  SENF_LOG(("unexpected failure writing to socket:" << ex.what()));
236  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
237  catch (...) {}
238  }
239  catch (...) {
240  SENF_LOG(("unexpected failure writing to socket: unknown exception"));
241  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
242  catch (...) {}
243  }
244 }
245 
246 prefix_ unsigned senf::console::detail::DumbClientReader::v_width()
247  const
248 {
249  return 80;
250 }
251 
252 //-/////////////////////////////////////////////////////////////////////////////////////////////////
253 // senf::console::detail::NoninteractiveClientReader
254 
255 prefix_
256 senf::console::detail::NoninteractiveClientReader::NoninteractiveClientReader(Client & client)
257  : ClientReader (client), streamBufferMaxSize_( 1024*1024),
258  readevent_ ("senf::console::detail::NoninteractiveClientReader",
259  senf::membind(&NoninteractiveClientReader::newData, this),
261  writeevent_ ("senf::console::detail::NoninteractiveClientReader",
262  membind(&NoninteractiveClientReader::writeHandler, this), handle(),
264 {}
265 
266 prefix_ void senf::console::detail::NoninteractiveClientReader::v_disablePrompt()
267 {}
268 
269 prefix_ void senf::console::detail::NoninteractiveClientReader::v_enablePrompt()
270 {}
271 
272 prefix_ void senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize(SendQueue::size_type size)
273 {
274  streamBufferMaxSize_ = size;
275 }
276 
277 prefix_ senf::console::detail::NoninteractiveClientReader::SendQueue::size_type
278 senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize()
279  const
280 {
281  return streamBufferMaxSize_;
282 }
283 
284 prefix_ void senf::console::detail::NoninteractiveClientReader::v_write(std::string const & data)
285 {
286  if (sendQueue_.size() > streamBufferMaxSize_)
287  return;
288  sendQueue_.insert( sendQueue_.end(), data.begin(), data.end());
289  writeHandler(scheduler::FdEvent::EV_WRITE);
290  if (! sendQueue_.empty())
291  writeevent_.enable();
292 }
293 
294 prefix_ unsigned senf::console::detail::NoninteractiveClientReader::v_width()
295  const
296 {
297  return 80;
298 }
299 
300 prefix_ void
301 senf::console::detail::NoninteractiveClientReader::newData(int event)
302 {
303  if (event != senf::scheduler::FdEvent::EV_READ || handle().eof()) {
304  if (! buffer_.empty())
305  handleInput(buffer_);
306  stopClient();
307  return;
308  }
309 
310  std::string::size_type n (buffer_.size());
311  buffer_.resize(n + handle().available());
312  buffer_.erase(handle().read(boost::make_iterator_range(buffer_.begin()+n, buffer_.end())),
313  buffer_.end());
314  buffer_.erase(0, handleInput(buffer_, true));
315  stream() << std::flush;
316 }
317 
318 prefix_ void
319 senf::console::detail::NoninteractiveClientReader::writeHandler(int event)
320 {
321  if (event != senf::scheduler::FdEvent::EV_WRITE) {
322  writeevent_.disable();
323  readevent_.disable();
324  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
325  catch (...) {}
326  return;
327  }
328  try {
329  sendQueue_.erase(sendQueue_.begin(),
330  handle().write(boost::make_iterator_range(sendQueue_.begin(), sendQueue_.end())));
331  }
332  catch (senf::ExceptionMixin & ex) {
333  SENF_LOG(("unexpected failure writing to socket:" << ex.message()));
334  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
335  catch (...) {}
336  }
337  catch (std::exception & ex) {
338  SENF_LOG(("unexpected failure writing to socket:" << ex.what()));
339  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
340  catch (...) {}
341  }
342  catch (...) {
343  SENF_LOG(("unexpected failure writing to socket: unknown exception"));
344  try { handle().facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
345  catch (...) {}
346  }
347  if (sendQueue_.empty())
348  writeevent_.disable();
349 }
350 
351 //-/////////////////////////////////////////////////////////////////////////////////////////////////
352 // senf::console::Client
353 
354 prefix_ senf::console::Client::Client(Server & server, ClientHandle handle)
355  : out_t(boost::ref(*this)),
356  senf::log::IOStreamTarget("client-" + senf::str(handle.peer()), out_t::member),
357  server_ (server), handle_ (handle),
358  readevent_ ("senf::console::Client::interactive_check",
359  boost::bind(&Client::setNoninteractive,this),
360  handle, scheduler::FdEvent::EV_READ, false),
361  timer_ ("senf::console::Client::interactive_timer",
362  boost::bind(&Client::setInteractive, this),
363  scheduler::eventTime() + ClockService::milliseconds(INTERACTIVE_TIMEOUT),
364  false),
365  name_ (server.name()), reader_ (), mode_ (server.mode())
366 {
367  handle_.facet<senf::TCPSocketProtocol>().nodelay(true);
368  handle_.blocking(false);
369  executor_.chroot(root());
370  switch (mode_) {
371  case Server::Interactive :
372  setInteractive();
373  break;
375  setNoninteractive();
376  break;
377  case Server::Automatic :
378  readevent_.enable();
379  timer_.enable();
380  break;
381  }
382 
383  namespace kw = console::kw;
384  namespace fty = console::factory;
385  Server::sysConsoleDir().add( "client-" + senf::str(handle.peer()), dir_);
386  dir_.add("log", fty::Link(consoleDir()) );
387  dir_.add("property", fty::Command<std::string (std::string const &)>(
388  boost::bind(SENF_MEMFNP(std::string, Client, getProperty, (std::string const &, std::string const &) const), this, _1, ""))
389  .doc("Get a property for this client")
390  .arg("key", "property key") );
391  dir_.add("property", fty::Command(&Client::setProperty, this) );
392  dir_.add("properties", fty::Command(&Client::dumpProperties, this) );
393  dir_.add("backtrace", fty::Command(&Client::backtrace, this)
394  .doc("Display the backtrace of the last exception") );
395 }
396 
397 prefix_ void senf::console::Client::setInteractive()
398 {
399  readevent_.disable();
400  timer_.disable();
401  mode_ = Server::Interactive;
402  properties_["mode"] = "Interactive";
403  reader_.reset(new detail::LineEditorSwitcher(*this));
404  executor_.autocd(true).autocomplete(true);
405  if (! server_.welcomeMsg_.empty())
406  write( server_.welcomeMsg_);
407 }
408 
409 prefix_ void senf::console::Client::setNoninteractive()
410 {
411  readevent_.disable();
412  timer_.disable();
413  mode_ = Server::Noninteractive;
414  properties_["mode"] = "NonInteractive";
415  detail::NoninteractiveClientReader * newReader (new detail::NoninteractiveClientReader(*this));
416  reader_.reset( newReader);
417  consoleDir().add( "streamBuffer", factory::Command( senf::membind(
418  SENF_MEMFNP( detail::NoninteractiveClientReader::SendQueue::size_type, detail::NoninteractiveClientReader, streamBufferMaxSize, () const ),
419  newReader )));
420  consoleDir().add( "streamBuffer", factory::Command( senf::membind(
421  SENF_MEMFNP( void, detail::NoninteractiveClientReader, streamBufferMaxSize, (detail::NoninteractiveClientReader::SendQueue::size_type) ),
422  newReader )));
423 }
424 
425 prefix_ std::string::size_type senf::console::Client::handleInput(std::string data,
426  bool incremental)
427 {
428  std::string::size_type n (data.size());
429 
430  try {
431  if (incremental)
432  n = parser_.parseIncremental(data, boost::bind<void>(
433  boost::ref(executor_), boost::ref(stream()), _1 ));
434  else
435  parser_.parse(data, boost::bind<void>(
436  boost::ref(executor_), boost::ref(stream()), _1 ));
437  }
438  catch (Executor::ExitException &) {
439  // This generates an EOF condition on the Handle. This EOF condition is expected to be
440  // handled gracefully by the ClientReader. We cannot call stop() here, since we are called
441  // from the client reader callback and that will continue executing after stop() has been
442  // called. stop() however will delete *this instance ... BANG ...
443  try { handle_.facet<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD); }
444  catch (...) {}
445  }
446  catch (std::exception & ex) {
447  std::string msg (ex.what());
448  std::string::size_type i (msg.find("-- \n"));
449  if (i != std::string::npos) {
450  backtrace_ = msg.substr(0,i);
451  msg = msg.substr(i+4);
452  } else {
453  backtrace_.clear();
454  }
455 
456  stream() << msg << std::endl;
457  }
458  catch (...) {
459  stream() << "unidentified error (unknown exception thrown)" << std::endl;
460  }
461  return n;
462 }
463 
464 prefix_ void senf::console::Client::v_write(senf::log::time_type timestamp,
465  std::string const & stream,
466  std::string const & area, unsigned level,
467  std::string const & message)
468 {
469  reader_->disablePrompt();
470  IOStreamTarget::v_write(timestamp, stream, area, level, message);
471  out_t::member << std::flush;
472  reader_->enablePrompt();
473 }
474 
475 prefix_ unsigned senf::console::Client::getWidth(std::ostream & os, unsigned defaultWidth,
476  unsigned minWidth)
477 {
478  unsigned rv (defaultWidth);
479  try {
480  rv = get(os).width();
481  }
482  catch (std::bad_cast &) {}
483  return rv < minWidth ? defaultWidth : rv;
484 }
485 
486 prefix_ void senf::console::Client::setProperty(std::string const & key, std::string const & value)
487 {
488  properties_[key] = value;
489 }
490 
491 prefix_ boost::optional<std::string> senf::console::Client::getProperty(std::string const & key)
492  const
493 {
494  PropertyMap::const_iterator entry (properties_.find(key));
495  return entry != properties_.end() ? boost::make_optional(entry->second) : boost::none;
496 }
497 
498 prefix_ std::string senf::console::Client::getProperty(std::string const & key, std::string const & defaultValue)
499  const
500 {
501  PropertyMap::const_iterator entry (properties_.find(key));
502  return entry != properties_.end() ? entry->second : defaultValue;
503 }
504 
505 prefix_ void senf::console::Client::dumpProperties(std::ostream & os)
506  const
507 {
508  for (PropertyMap::const_iterator entry (properties_.begin()); entry != properties_.end(); ++entry)
509  os << entry->first << " : " << entry->second << std::endl;
510 }
511 
512 
513 //-/////////////////////////////////////////////////////////////////////////////////////////////////
514 // senf::console::Client::SysBacktrace
515 
516 prefix_ senf::console::Client::SysBacktrace::SysBacktrace()
517 {
518  Server::sysConsoleDir().add("backtrace", factory::Command(&SysBacktrace::backtrace)
519  .doc("Display the backtrace of the last error / exception in this console") );
520 }
521 
523 {
524  Client & client (Client::get(os));
525  if (client.backtrace().empty())
526  os << "(no backtrace)\n";
527  else
528  os << client.backtrace();
529 }
530 
531 senf::console::Client::SysBacktrace senf::console::Client::SysBacktrace::instance_;
532 
533 //-/////////////////////////////////////////////////////////////////////////////////////////////////
534 #undef prefix_
535 
536 
537 // Local Variables:
538 // mode: c++
539 // fill-column: 100
540 // comment-column: 40
541 // c-file-style: "senf"
542 // indent-tabs-mode: nil
543 // ispell-local-dictionary: "american"
544 // compile-command: "scons -u test"
545 // End:
#define SENF_LOG_BLOCK(args)
ScopedDirectory public header.
ScopedDirectory & sysdir()
friend class Client
Definition: Server.hh:132
static ptr dispatch(Handle handle, std::string::size_type maxSize, Callback callback)
Sysdir public header.
std::string const & data() const
void setProperty(std::string const &key, std::string const &value)
Definition: Server.cc:486
static Server & start(senf::INet4SocketAddress const &address)
Start server on given IPv4 address/port.
Definition: Server.cc:85
#define SENF_MEMFNP(ret, cls, fn, args)
#define BUILD_TYPE
Definition: Server.cc:40
Config/console tree directory node.
Definition: Node.hh:406
Mode mode() const
Get mode.
u8 data[SPECTRAL_HT20_NUM_BINS]
boost::function< R(Args)> membind(R(T::*fn)(Args), T *ob)
DirectoryNode member proxy.
Server public header.
OverloadedCommandNode factory.
static Client & get(std::ostream &os)
Access client instance.
bool eof() const
static unsigned getWidth(std::ostream &os, unsigned defaultWidth=0, unsigned minWidth=0)
Get width of client console if possible.
Definition: Server.cc:475
static SENF_CLOCKSERVICE_CONSTEXPR clock_type milliseconds(int64_type const &v)
console::ScopedDirectory & consoleDir()
ParsedCommand public header.
NodeType & add(std::string const &name, boost::shared_ptr< NodeType > node)
Add node to tree.
bool error() const
Thrown by built-in &#39;exit&#39; command.
Definition: Executor.hh:58
Server client instance.
Definition: Server.hh:143
#define prefix_
Definition: Server.cc:34
SysInfo public header.
LineEditor public header.
boost::intrusive_ptr< ReadHelper > ptr
DirectoryNode & node() const
Access the proxied DirectoryNode.
DirectoryNode & root()
Get console root node.
Definition: Node.cc:26
std::string const & tail() const
std::string getProperty(std::string const &key, std::string const &defaultValue) const
Definition: Server.cc:498
DirectoryNode & root() const
Get root node.
ClientHandle handle() const
Get the client&#39;s network socket handle.
std::string const & name() const
Get server name.
ProtocolServerSocketHandle< TCPv4SocketProtocol > TCPv4ServerSocketHandle
std::string message() const
unsigned available() const
Internal: Client reader switching between LineEditorClientReader or DumbClientReader.
Definition: LineEditor.hh:42
Interactive console server.
Definition: Server.hh:53
void backtrace(std::ostream &os, int numEntries)
#define SENF_LOG(args)
ProtocolServerSocketHandle< TCPv6SocketProtocol > TCPv6ServerSocketHandle
std::string const & backtrace() const
Get backtrace of last console error, if any.
NodeType & add(std::string const &name, boost::shared_ptr< NodeType > node)
Server & welcomeMessage(std::string const &message)
Set server welcome message.
Definition: Server.cc:153
detail::ServerHandle ServerHandle
Definition: Server.hh:62