Server logs client address
[senf.git] / Console / Server.cc
index 856a67c..ac9f034 100644 (file)
@@ -27,7 +27,6 @@
 #include "Server.ih"
 
 // Custom includes
-#include <unistd.h>
 #include <iostream>
 #include <boost/algorithm/string/trim.hpp>
 #include <boost/iostreams/device/file_descriptor.hpp>
@@ -36,6 +35,7 @@
 #include "../Utils/senfassert.hh"
 #include "../Utils/membind.hh"
 #include "../Utils/Logger/SenfLog.hh"
+#include "Readline.hh"
 
 //#include "Server.mpp"
 #define prefix_
@@ -48,8 +48,11 @@ prefix_ std::streamsize senf::console::detail::NonblockingSocketSink::write(cons
                                                                             std::streamsize n)
 {
     try {
-        if (handle_.writeable()) 
-            handle_.write(s, s+n);
+        if (client_.handle().writeable()) {
+            std::string data (s, n);
+            client_.translate(data);
+            client_.handle().write( data );
+        }
     }
     catch (SystemException & ex) {
         ;
@@ -64,26 +67,20 @@ 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();
-}
-
-prefix_ senf::console::Server & senf::console::Server::instance()
-{
-    SENF_ASSERT( instancePtr() );
-    return *instancePtr();
+    return server;
 }
 
 prefix_ boost::scoped_ptr<senf::console::Server> & senf::console::Server::instancePtr()
@@ -95,13 +92,14 @@ prefix_ boost::scoped_ptr<senf::console::Server> & senf::console::Server::instan
     return instance;
 }
 
-prefix_ void senf::console::Server::start(ServerHandle handle)
+prefix_ senf::console::Server & senf::console::Server::start(ServerHandle 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)
@@ -118,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<Client> p (new Client(client, name_));
+    boost::intrusive_ptr<Client> p (new Client(*this, client, name_));
     clients_.insert( p );
     SENF_LOG(( "Registered new client " << p.get() ));
 }
@@ -131,40 +129,86 @@ 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)
-    : out_t(handle), senf::log::IOStreamTarget(out_t::member),
-      handle_ (handle), name_ (name), promptLen_(0)
+prefix_ senf::console::detail::DumbClientReader::DumbClientReader(Client & client)
+    : ClientReader(client), promptLen_ (0), promptActive_ (false)
 {
     showPrompt();
-    ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
-                                        senf::membind(&Client::clientData, this) );
-    route< senf::SenfLog, senf::log::NOTICE >();
-}
-
-prefix_ senf::console::Client::~Client()
-{}
-
-prefix_ void senf::console::Client::stopClient()
-{
-    // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS
-    Server::instance().removeClient(*this);
+    ReadHelper<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
+                                        senf::membind(&DumbClientReader::clientData, this) );
 }
 
-prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr helper)
+prefix_ void
+senf::console::detail::DumbClientReader::clientData(senf::ReadHelper<ClientHandle>::ptr helper)
 {
-    promptLen_ = 0;
-    if (helper->error() || handle_.eof()) {
+    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
+    boost::trim(data);                  // Gets rid of superfluous  \r or \n characters
+    handleInput(data);
+
+    showPrompt();
+    ReadHelper<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
+                                        senf::membind(&DumbClientReader::clientData, this) );
+
+}
+
+prefix_ void senf::console::detail::DumbClientReader::showPrompt()
+{
+    std::string prompt (promptString());
+
+    stream() << std::flush;
+    handle().write(prompt);
+    promptLen_ = prompt.size();
+    promptActive_ = true;
+}
+
+prefix_ void senf::console::detail::DumbClientReader::v_disablePrompt()
+{
+    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::Client
+
+prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
+                                      std::string const & name)
+    : out_t(boost::ref(*this)), senf::log::IOStreamTarget(out_t::member), server_ (server),
+      handle_ (handle), name_ (name), reader_ (new detail::SafeReadlineClientReader (*this))
+{
+    executor_.autocd(true).autocomplete(true);
+    handle_.facet<senf::TCPSocketProtocol>().nodelay();
+    // route< senf::SenfLog, senf::log::NOTICE >();
+}
+
+prefix_ void senf::console::Client::translate(std::string & data)
+{
+    reader_->translate(data);
+}
+
+prefix_ void senf::console::Client::handleInput(std::string data)
+{
     if (data.empty())
         data = lastCommand_;
     else
@@ -172,32 +216,24 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
 
     try {
         if (! parser_.parse(data, boost::bind<void>( boost::ref(executor_),
-                                                     boost::ref(out_t::member),
+                                                     boost::ref(stream()),
                                                      _1 )) )
-            out_t::member << "syntax error" << std::endl;
+            stream() << "syntax error" << std::endl;
     }
     catch (Executor::ExitException &) {
-        // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
-        stopClient();
+        // 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<senf::TCPSocketProtocol>().shutdown(senf::TCPSocketProtocol::ShutRD);
         return;
     }
     catch (std::exception & ex) {
-        out_t::member << ex.what() << std::endl;
+        stream() << ex.what() << std::endl;
     }
     catch (...) {
-        out_t::member << "unidentified error (unknown exception thrown)" << std::endl;
+        stream() << "unidentified error (unknown exception thrown)" << std::endl;
     }
-
-    showPrompt();
-    ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
-                                        senf::membind(&Client::clientData, this) );
-}
-
-prefix_ void senf::console::Client::showPrompt()
-{
-    std::string path (executor_.cwd().path());
-    out_t::member << name_ << ":" << path << "# " << std::flush;
-    promptLen_ = name_.size() + 1 + path.size() + 1;
 }
 
 prefix_ void senf::console::Client::v_write(boost::posix_time::ptime timestamp,
@@ -205,13 +241,30 @@ prefix_ void senf::console::Client::v_write(boost::posix_time::ptime timestamp,
                                             std::string const & area, unsigned level,
                                             std::string const & message)
 {
-    if (promptLen_)
-        out_t::member << '\r' << std::string(' ', promptLen_) << '\r';
+    reader_->disablePrompt();
     IOStreamTarget::v_write(timestamp, stream, area, level, message);
-    if (promptLen_)
-        showPrompt();
+    out_t::member << std::flush;
+    reader_->enablePrompt();
 }
 
+prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Client const & client)
+{
+    //    typedef senf::ClientSocketHandle<MakeSocketPolicy<INet4AddressingPolicy>::policy > v4Socket;
+    if( senf::check_socket_cast<TCPv4ServerSocketHandle::ClientSocketHandle>( client.handle())) {
+        os<<senf::dynamic_socket_cast<TCPv4ServerSocketHandle::ClientSocketHandle>( client.handle()).peer();
+    }
+    else if( senf::check_socket_cast<TCPv6ServerSocketHandle::ClientSocketHandle>( client.handle())) {
+        os<<senf::dynamic_socket_cast<TCPv6ServerSocketHandle::ClientSocketHandle>( client.handle()).peer();
+    }
+    else{
+        os<<((void *)&client);
+    }
+    return os;
+}
+prefix_ std::ostream & senf::console::operator<<(std::ostream & os, Client * client)
+{
+    return os<<*client;
+}
 ///////////////////////////////cc.e////////////////////////////////////////
 #undef prefix_
 //#include "Server.mpp"