X-Git-Url: http://g0dil.de/git?a=blobdiff_plain;f=Console%2FServer.cc;h=310e3413109757c92f6821140b1d9a48551100b1;hb=feeec0e9cd78825120bd52f9ef4e115d383bf6a8;hp=569a7cff32961119f1b90cf3b811cc1edda8f73b;hpb=5e9e6057a4e5c1241ff3f1b75b0f797eb570725d;p=senf.git diff --git a/Console/Server.cc b/Console/Server.cc index 569a7cf..310e341 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -24,10 +24,9 @@ \brief Server non-inline non-template implementation */ #include "Server.hh" -//#include "Server.ih" +#include "Server.ih" // Custom includes -#include #include #include #include @@ -36,44 +35,75 @@ #include "../Utils/senfassert.hh" #include "../Utils/membind.hh" #include "../Utils/Logger/SenfLog.hh" +#include "Readline.hh" //#include "Server.mpp" #define prefix_ ///////////////////////////////cc.p//////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// +// senf::console::detail::NonBlockingSocketSink + +prefix_ std::streamsize senf::console::detail::NonblockingSocketSink::write(const char * s, + std::streamsize n) +{ + try { + if (client_.handle().writeable()) { + std::string data (s, n); + client_.translate(data); + client_.handle().write( data ); + } + } + catch (SystemException & ex) { + ; + } + return n; +} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::Server + prefix_ senf::console::Server & senf::console::Server::start(senf::INet4SocketAddress const & address) { senf::TCPv4ServerSocketHandle handle (address); - senf::console::Server::start(handle); + Server & server (senf::console::Server::start(handle)); SENF_LOG((Server::SENFLogArea)(log::NOTICE)( "Console server started at " << address )); - return *instance_; + return server; } prefix_ senf::console::Server & senf::console::Server::start(senf::INet6SocketAddress const & address) { senf::TCPv6ServerSocketHandle handle (address); - senf::console::Server::start(handle); + Server & server (senf::console::Server::start(handle)); SENF_LOG((Server::SENFLogArea)(log::NOTICE)( "Console server started at " << address )); - return *instance_; + return server; } -/////////////////////////////////////////////////////////////////////////// -// senf::console::Server - -boost::scoped_ptr senf::console::Server::instance_; +prefix_ boost::scoped_ptr & senf::console::Server::instancePtr() +{ + // We cannot make 'instance' a global or class-static variable, since it will then be destructed + // at an unknown time which may fail if the scheduler or the file-handle pool allocators have + // already been destructed. + static boost::scoped_ptr instance; + return instance; +} -prefix_ void senf::console::Server::start(ServerHandle handle) +prefix_ senf::console::Server & senf::console::Server::start(ServerHandle handle) { - SENF_ASSERT( ! instance_ ); - instance_.reset(new Server(handle)); + // Uah .... ensure the scheduler is created before the instance pointer so it get's destructed + // AFTER it. + (void) senf::Scheduler::instance(); + SENF_ASSERT( ! instancePtr() ); + instancePtr().reset(new Server(handle)); + return * instancePtr(); } prefix_ senf::console::Server::Server(ServerHandle handle) - : handle_ (handle) + : handle_ (handle), mode_ (Automatic) { Scheduler::instance().add( handle_, senf::membind(&Server::newClient, this) ); } @@ -86,7 +116,7 @@ prefix_ senf::console::Server::~Server() prefix_ void senf::console::Server::newClient(Scheduler::EventId event) { ServerHandle::ClientSocketHandle client (handle_.accept()); - boost::intrusive_ptr p (new Client(client, name_)); + boost::intrusive_ptr p (new Client(*this, client)); clients_.insert( p ); SENF_LOG(( "Registered new client " << p.get() )); } @@ -99,60 +129,230 @@ prefix_ void senf::console::Server::removeClient(Client & client) } /////////////////////////////////////////////////////////////////////////// -// senf::console::Client +// senf::console::detail::DumbClientReader -prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name) - : handle_ (handle), name_ (name), out_(::dup(handle.fd())) +prefix_ senf::console::detail::DumbClientReader::DumbClientReader(Client & client) + : ClientReader(client), promptLen_ (0), promptActive_ (false) { showPrompt(); - ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), - senf::membind(&Client::clientData, this) ); + ReadHelper::dispatch( handle(), 16384u, ReadUntil("\n"), + senf::membind(&DumbClientReader::clientData, this) ); } -prefix_ senf::console::Client::~Client() -{} +prefix_ void +senf::console::detail::DumbClientReader::clientData(senf::ReadHelper::ptr helper) +{ + if (helper->error() || handle().eof()) { + // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS + stopClient(); + return; + } + + promptLen_ = 0; + promptActive_ = false; + + std::string data (tail_ + helper->data()); + tail_ = helper->tail(); + boost::trim(data); // Gets rid of superfluous \r or \n characters + handleInput(data); + + showPrompt(); + ReadHelper::dispatch( handle(), 16384u, ReadUntil("\n"), + senf::membind(&DumbClientReader::clientData, this) ); -prefix_ void senf::console::Client::stopClient() +} + +prefix_ void senf::console::detail::DumbClientReader::showPrompt() { - // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS - Server::instance_->removeClient(*this); + std::string prompt (promptString()); + + stream() << std::flush; + handle().write(prompt); + promptLen_ = prompt.size(); + promptActive_ = true; } -prefix_ void senf::console::Client::clientData(ReadHelper::ptr helper) +prefix_ void senf::console::detail::DumbClientReader::v_disablePrompt() { - if (helper->error() || handle_.eof()) { - // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS + if (promptActive_ && promptLen_ > 0) { + stream() << '\r' << std::string(' ', promptLen_) << '\r'; + promptLen_ = 0; + } +} + +prefix_ void senf::console::detail::DumbClientReader::v_enablePrompt() +{ + if (promptActive_ && ! promptLen_) + showPrompt(); +} + +prefix_ void senf::console::detail::DumbClientReader::v_translate(std::string & data) +{} + +/////////////////////////////////////////////////////////////////////////// +// senf::console::detail::NoninteractiveClientReader + +prefix_ +senf::console::detail::NoninteractiveClientReader::NoninteractiveClientReader(Client & client) + : ClientReader (client), binding_ (handle(), + senf::membind(&NoninteractiveClientReader::newData, this), + senf::Scheduler::EV_READ) +{} + +prefix_ void senf::console::detail::NoninteractiveClientReader::v_disablePrompt() +{} + +prefix_ void senf::console::detail::NoninteractiveClientReader::v_enablePrompt() +{} + +prefix_ void senf::console::detail::NoninteractiveClientReader::v_translate(std::string & data) +{} + +prefix_ void +senf::console::detail::NoninteractiveClientReader::newData(senf::Scheduler::EventId event) +{ + if (event != senf::Scheduler::EV_READ || handle().eof()) { + if (! buffer_.empty()) + handleInput(buffer_); stopClient(); return; } -# warning fix Client::clientData implementation - // Remove the 'dup' needed here so we don't close the same fd twice (see Client constructor) - // Make output non-blocking - // Don't register a new ReadHelper every round + std::string::size_type n (buffer_.size()); + buffer_.resize(n + handle().available()); + buffer_.erase(handle().read(boost::make_iterator_range(buffer_.begin()+n, buffer_.end())), + buffer_.end()); + buffer_.erase(0, handleInput(buffer_, true)); + stream() << std::flush; +} - std::string data (tail_ + helper->data()); - tail_ = helper->tail(); - boost::trim(data); // Gets rid of superfluous \r or \n characters +/////////////////////////////////////////////////////////////////////////// +// senf::console::Client + +prefix_ senf::console::Client::Client(Server & server, ClientHandle handle) + : out_t(boost::ref(*this)), senf::log::IOStreamTarget(out_t::member), server_ (server), + handle_ (handle), + binding_ (handle, boost::bind(&Client::setNoninteractive,this), Scheduler::EV_READ, false), + timer_ (Scheduler::instance().eventTime() + ClockService::milliseconds(INTERACTIVE_TIMEOUT), + boost::bind(&Client::setInteractive, this), false), + name_ (server.name()), reader_ (), mode_ (server.mode()) +{ + handle_.facet().nodelay(); + switch (mode_) { + case Server::Interactive : + setInteractive(); + break; + case Server::Noninteractive : + setNoninteractive(); + break; + case Server::Automatic : + binding_.enable(); + timer_.enable(); + break; + } +} + +prefix_ void senf::console::Client::setInteractive() +{ + SENF_LOG(("Set client interactive")); + binding_.disable(); + timer_.disable(); + mode_ = Server::Interactive; + reader_.reset(new detail::SafeReadlineClientReader (*this)); + executor_.autocd(true).autocomplete(true); +} + +prefix_ void senf::console::Client::setNoninteractive() +{ + SENF_LOG(("Set client non-interactive")); + binding_.disable(); + timer_.disable(); + mode_ = Server::Noninteractive; + reader_.reset(new detail::NoninteractiveClientReader(*this)); +} + +prefix_ void senf::console::Client::translate(std::string & data) +{ + reader_->translate(data); +} + +prefix_ std::string::size_type senf::console::Client::handleInput(std::string data, + bool incremental) +{ + SENF_LOG(("Data: " << data)); + + if (data.empty() && ! incremental) + data = lastCommand_; + else + lastCommand_ = data; + + bool state (true); + std::string::size_type n (data.size()); try { - if (! parser_.parse(data, boost::bind(boost::ref(executor_), _1, boost::ref(out_)))) - out_ << "syntax error" << std::endl; + if (incremental) + n = parser_.parseIncremental(data, boost::bind( boost::ref(executor_), + boost::ref(stream()), + _1 )); + else + state = parser_.parse(data, boost::bind( boost::ref(executor_), + boost::ref(stream()), + _1 )); + if (! state ) + stream() << "syntax error" << std::endl; } catch (Executor::ExitException &) { - // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS - stopClient(); - return; - } + // This generates an EOF condition on the Handle. This EOF condition is expected + // to be handled gracefully by the ClientReader. We cannot call stop() here, since we + // are called from the client reader callback and that will continue executing even if we + // call stop here ... + handle_.facet().shutdown(senf::TCPSocketProtocol::ShutRD); + } + catch (std::exception & ex) { + stream() << ex.what() << std::endl; + } + catch (...) { + stream() << "unidentified error (unknown exception thrown)" << std::endl; + } + return n; +} - showPrompt(); - ReadHelper::dispatch( handle_, 16384u, ReadUntil("\n"), - senf::membind(&Client::clientData, this) ); +prefix_ void senf::console::Client::v_write(senf::log::time_type timestamp, + std::string const & stream, + std::string const & area, unsigned level, + std::string const & message) +{ + reader_->disablePrompt(); + IOStreamTarget::v_write(timestamp, stream, area, level, message); + out_t::member << std::flush; + reader_->enablePrompt(); +} + +prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Client const & client) +{ + typedef ClientSocketHandle< MakeSocketPolicy< + INet4AddressingPolicy,ConnectedCommunicationPolicy>::policy > V4Socket; + typedef ClientSocketHandle< MakeSocketPolicy< + INet6AddressingPolicy,ConnectedCommunicationPolicy>::policy > V6Socket; + + try { + if (check_socket_cast(client.handle())) + os << dynamic_socket_cast(client.handle()).peer(); + else if (check_socket_cast(client.handle())) + os << dynamic_socket_cast(client.handle()).peer(); + else + os << static_cast(&client); + } + catch (SystemException &) { + os << "0.0.0.0:0"; + } + + return os; } -prefix_ void senf::console::Client::showPrompt() +prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Client * client) { - out_ << name_ << ":" << executor_.cwd().path() << "# " << std::flush; + return os << *client; } ///////////////////////////////cc.e////////////////////////////////////////