21 #include <boost/algorithm/string/trim.hpp> 22 #include <boost/bind.hpp> 27 #include <senf/Version.hh> 38 # define BUILD_TYPE "development" 40 # define BUILD_TYPE "production" 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);
58 prefix_ std::streamsize senf::console::detail::SocketStreamSink::write(
const char * s,
64 std::string
data (s, n);
79 if (not
sysdir().hasChild(
"console"))
81 return sysConsoleDir_.
node();
90 "Console server started at " << address ));
100 "Console server started at " << address ));
106 boost::intrusive_ptr<Server> p (
new Server(handle));
107 detail::ServerManager::add(boost::intrusive_ptr<Server>(p));
113 event_ (
"senf::console::Server",
senf::membind(&Server::newClient,
this),
116 name_ (::program_invocation_short_name)
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" 122 "Just get the directory\n" 123 " $ /sys/console/self\n" 124 " <Directory '/sys/console/client-xxx.xxx.xxx.xxx:xxx'>\n" 126 "Get all properties of the currently connected client\n" 127 " $ /sys/console/self { properties; }") );
130 prefix_ void senf::console::Server::newClient(
int event)
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() ));
138 prefix_ void senf::console::Server::removeClient(
Client & client)
141 log <<
"Disposing client ";
143 log << client.
handle().peer();
146 log <<
"(dead socket)";
150 clients_.erase(boost::intrusive_ptr<Client>(&client));
155 welcomeMsg_ = message;
156 if (!message.empty() && message[message.size()-1] !=
'\n')
161 prefix_ boost::shared_ptr<senf::console::DirectoryNode> senf::console::Server::consoleSelf(std::ostream & os)
169 prefix_ senf::console::detail::DumbClientReader::DumbClientReader(
Client & client)
170 : ClientReader(client), promptLen_ (0), promptActive_ (
false)
180 if (helper->
error() || handle().eof()) {
187 promptActive_ =
false;
189 std::string
data (tail_ + helper->
data());
190 tail_ = helper->
tail();
199 prefix_ void senf::console::detail::DumbClientReader::showPrompt()
201 std::string prompt (promptString());
204 stream() << std::flush;
205 promptLen_ = prompt.size();
206 promptActive_ =
true;
210 prefix_ void senf::console::detail::DumbClientReader::v_disablePrompt()
212 if (promptActive_ && promptLen_ > 0) {
213 stream() <<
'\r' << std::string(
' ', promptLen_) <<
'\r';
218 prefix_ void senf::console::detail::DumbClientReader::v_enablePrompt()
220 if (promptActive_ && ! promptLen_)
224 prefix_ void senf::console::detail::DumbClientReader::v_write(std::string
const &
data)
227 handle().write(data);
234 catch (std::exception & ex) {
235 SENF_LOG((
"unexpected failure writing to socket:" << ex.what()));
240 SENF_LOG((
"unexpected failure writing to socket: unknown exception"));
246 prefix_ unsigned senf::console::detail::DumbClientReader::v_width()
256 senf::console::detail::NoninteractiveClientReader::NoninteractiveClientReader(
Client & client)
257 : ClientReader (client), streamBufferMaxSize_( 1024*1024),
258 readevent_ (
"senf::console::detail::NoninteractiveClientReader",
261 writeevent_ (
"senf::console::detail::NoninteractiveClientReader",
262 membind(&NoninteractiveClientReader::writeHandler,
this), handle(),
266 prefix_ void senf::console::detail::NoninteractiveClientReader::v_disablePrompt()
269 prefix_ void senf::console::detail::NoninteractiveClientReader::v_enablePrompt()
272 prefix_ void senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize(SendQueue::size_type size)
274 streamBufferMaxSize_ = size;
277 prefix_ senf::console::detail::NoninteractiveClientReader::SendQueue::size_type
278 senf::console::detail::NoninteractiveClientReader::streamBufferMaxSize()
281 return streamBufferMaxSize_;
284 prefix_ void senf::console::detail::NoninteractiveClientReader::v_write(std::string
const &
data)
286 if (sendQueue_.size() > streamBufferMaxSize_)
288 sendQueue_.insert( sendQueue_.end(), data.begin(), data.end());
290 if (! sendQueue_.empty())
291 writeevent_.enable();
294 prefix_ unsigned senf::console::detail::NoninteractiveClientReader::v_width()
301 senf::console::detail::NoninteractiveClientReader::newData(
int event)
304 if (! buffer_.empty())
305 handleInput(buffer_);
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())),
314 buffer_.erase(0, handleInput(buffer_,
true));
315 stream() << std::flush;
319 senf::console::detail::NoninteractiveClientReader::writeHandler(
int event)
322 writeevent_.disable();
323 readevent_.disable();
329 sendQueue_.erase(sendQueue_.begin(),
330 handle().write(boost::make_iterator_range(sendQueue_.begin(), sendQueue_.end())));
337 catch (std::exception & ex) {
338 SENF_LOG((
"unexpected failure writing to socket:" << ex.what()));
343 SENF_LOG((
"unexpected failure writing to socket: unknown exception"));
347 if (sendQueue_.empty())
348 writeevent_.disable();
354 prefix_ senf::console::Client::Client(
Server & server, ClientHandle handle)
355 : out_t(boost::ref(*
this)),
357 server_ (server), handle_ (handle),
358 readevent_ (
"senf::console::Client::interactive_check",
359 boost::bind(&Client::setNoninteractive,
this),
361 timer_ (
"senf::console::Client::interactive_timer",
362 boost::bind(&Client::setInteractive,
this),
365 name_ (server.
name()), reader_ (), mode_ (server.
mode())
368 handle_.blocking(
false);
369 executor_.chroot(
root());
383 namespace kw = console::kw;
384 namespace fty = console::factory;
385 Server::sysConsoleDir().add(
"client-" + senf::str(handle.peer()), dir_);
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") );
392 dir_.add(
"properties", fty::Command(&Client::dumpProperties,
this) );
394 .doc(
"Display the backtrace of the last exception") );
397 prefix_ void senf::console::Client::setInteractive()
399 readevent_.disable();
402 properties_[
"mode"] =
"Interactive";
404 executor_.autocd(
true).autocomplete(
true);
405 if (! server_.welcomeMsg_.empty())
406 write( server_.welcomeMsg_);
409 prefix_ void senf::console::Client::setNoninteractive()
411 readevent_.disable();
414 properties_[
"mode"] =
"NonInteractive";
415 detail::NoninteractiveClientReader * newReader (
new detail::NoninteractiveClientReader(*
this));
416 reader_.reset( newReader);
418 SENF_MEMFNP( detail::NoninteractiveClientReader::SendQueue::size_type, detail::NoninteractiveClientReader, streamBufferMaxSize, ()
const ),
421 SENF_MEMFNP(
void, detail::NoninteractiveClientReader, streamBufferMaxSize, (detail::NoninteractiveClientReader::SendQueue::size_type) ),
425 prefix_ std::string::size_type senf::console::Client::handleInput(std::string
data,
428 std::string::size_type n (
data.size());
432 n = parser_.parseIncremental(
data, boost::bind<void>(
433 boost::ref(executor_), boost::ref(stream()), _1 ));
435 parser_.parse(
data, boost::bind<void>(
436 boost::ref(executor_), boost::ref(stream()), _1 ));
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);
456 stream() << msg << std::endl;
459 stream() <<
"unidentified error (unknown exception thrown)" << std::endl;
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)
469 reader_->disablePrompt();
470 IOStreamTarget::v_write(timestamp, stream, area, level, message);
471 out_t::member << std::flush;
472 reader_->enablePrompt();
478 unsigned rv (defaultWidth);
480 rv =
get(os).width();
482 catch (std::bad_cast &) {}
483 return rv < minWidth ? defaultWidth : rv;
488 properties_[key] = value;
494 PropertyMap::const_iterator entry (properties_.find(key));
495 return entry != properties_.end() ? boost::make_optional(entry->second) : boost::none;
501 PropertyMap::const_iterator entry (properties_.find(key));
502 return entry != properties_.end() ? entry->second : defaultValue;
505 prefix_ void senf::console::Client::dumpProperties(std::ostream & os)
508 for (PropertyMap::const_iterator entry (properties_.begin()); entry != properties_.end(); ++entry)
509 os << entry->first <<
" : " << entry->second << std::endl;
516 prefix_ senf::console::Client::SysBacktrace::SysBacktrace()
519 .doc(
"Display the backtrace of the last error / exception in this console") );
526 os <<
"(no backtrace)\n";
531 senf::console::Client::SysBacktrace senf::console::Client::SysBacktrace::instance_;
#define SENF_LOG_BLOCK(args)
ScopedDirectory public header.
ScopedDirectory & sysdir()
static ptr dispatch(Handle handle, std::string::size_type maxSize, Callback callback)
std::string const & data() const
void setProperty(std::string const &key, std::string const &value)
static Server & start(senf::INet4SocketAddress const &address)
Start server on given IPv4 address/port.
#define SENF_MEMFNP(ret, cls, fn, args)
Config/console tree directory node.
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.
OverloadedCommandNode factory.
static Client & get(std::ostream &os)
Access client instance.
static unsigned getWidth(std::ostream &os, unsigned defaultWidth=0, unsigned minWidth=0)
Get width of client console if possible.
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.
Thrown by built-in 'exit' command.
LineEditor public header.
boost::intrusive_ptr< ReadHelper > ptr
DirectoryNode & node() const
Access the proxied DirectoryNode.
DirectoryNode & root()
Get console root node.
std::string const & tail() const
std::string getProperty(std::string const &key, std::string const &defaultValue) const
DirectoryNode & root() const
Get root node.
ClientHandle handle() const
Get the client'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.
Interactive console server.
void backtrace(std::ostream &os, int numEntries)
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.
detail::ServerHandle ServerHandle