Socket: Add additional port-only constructor for INet[46]SocketAddress
[senf.git] / Console / Server.cc
index 72fd25c..53db4e7 100644 (file)
@@ -24,7 +24,7 @@
     \brief Server non-inline non-template implementation */
 
 #include "Server.hh"
-//#include "Server.ih"
+#include "Server.ih"
 
 // Custom includes
 #include <unistd.h>
 #define prefix_
 ///////////////////////////////cc.p////////////////////////////////////////
 
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::NonBlockingSocketSink
+
+prefix_ std::streamsize senf::console::detail::NonblockingSocketSink::write(const char * s,
+                                                                            std::streamsize n)
+{
+    try {
+        if (handle_.writeable()) 
+            handle_.write(s, s+n);
+    }
+    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> senf::console::Server::instance_;
+prefix_ boost::scoped_ptr<senf::console::Server> & 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<senf::console::Server> 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)
@@ -86,7 +113,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() ));
 }
@@ -101,12 +128,15 @@ prefix_ void senf::console::Server::removeClient(Client & client)
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::Client
 
-prefix_ senf::console::Client::Client(ClientHandle handle, std::string const & name)
-    : handle_ (handle), name_ (name), out_(::dup(handle.fd()))
+prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
+                                      std::string const & name)
+    : out_t(handle), senf::log::IOStreamTarget(out_t::member), server_ (server),
+      handle_ (handle), name_ (name), promptLen_(0) 
 {
     showPrompt();
     ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
                                         senf::membind(&Client::clientData, this) );
+    route< senf::SenfLog, senf::log::NOTICE >();
 }
 
 prefix_ senf::console::Client::~Client()
@@ -115,29 +145,32 @@ 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);
+    server_.removeClient(*this);
 }
 
 prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr helper)
 {
+    promptLen_ = 0;
     if (helper->error() || handle_.eof()) {
         // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
         stopClient();
         return;
     }
 
-    ///\fixme 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 data (tail_ + helper->data());
     tail_ = helper->tail();
     boost::trim(data); // Gets rid of superfluous  \r or \n characters
 
+    if (data.empty())
+        data = lastCommand_;
+    else
+        lastCommand_ = data;
+
     try {
-        if (! parser_.parse(data, boost::bind<void>(boost::ref(executor_), _1, boost::ref(out_))))
-            out_ << "syntax error" << std::endl;
+        if (! parser_.parse(data, boost::bind<void>( boost::ref(executor_),
+                                                     boost::ref(out_t::member),
+                                                     _1 )) )
+            out_t::member << "syntax error" << std::endl;
     }
     catch (Executor::ExitException &) {
         // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
@@ -145,10 +178,10 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
         return;
     }
     catch (std::exception & ex) {
-        out_ << ex.what() << std::endl;
+        out_t::member << ex.what() << std::endl;
     }
     catch (...) {
-        out_ << "unidentified error (unknown exception thrown)" << std::endl;
+        out_t::member << "unidentified error (unknown exception thrown)" << std::endl;
     }
 
     showPrompt();
@@ -158,7 +191,21 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
 
 prefix_ void senf::console::Client::showPrompt()
 {
-    out_ << name_ << ":" << executor_.cwd().path() << "# " << std::flush;
+    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,
+                                            std::string const & stream,
+                                            std::string const & area, unsigned level,
+                                            std::string const & message)
+{
+    if (promptLen_)
+        out_t::member << '\r' << std::string(' ', promptLen_) << '\r';
+    IOStreamTarget::v_write(timestamp, stream, area, level, message);
+    if (promptLen_)
+        showPrompt();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////