Socket/Protocols/INet: Add 'shutdown' member to TCPSocketProtocol
g0dil [Wed, 23 Apr 2008 23:34:08 +0000 (23:34 +0000)]
Utils/Daemon: Ignore SIGPIPE signals by default
Console: Refactor console server to prepare for readline integration

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@822 270642c3-0616-0410-b53a-bc976706d245

Console/Server.cc
Console/Server.cci
Console/Server.hh
Console/Server.ih
Console/testServer.cc
Socket/Protocols/INet/TCPSocketProtocol.cc
Socket/Protocols/INet/TCPSocketProtocol.hh
Utils/Daemon/Daemon.cc

index 53db4e7..13820cf 100644 (file)
@@ -126,41 +126,85 @@ prefix_ void senf::console::Server::removeClient(Client & client)
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::DumbClientReader
+
+prefix_ senf::console::detail::DumbClientReader::DumbClientReader(Client & client)
+    : ClientReader(client), promptLen_ (0), promptActive_ (false)
+{
+    showPrompt();
+    ReadHelper<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
+                                        senf::membind(&DumbClientReader::clientData, this) );
+}
+
+prefix_ void
+senf::console::detail::DumbClientReader::clientData(senf::ReadHelper<ClientHandle>::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<ClientHandle>::dispatch( handle(), 16384u, ReadUntil("\n"),
+                                        senf::membind(&DumbClientReader::clientData, this) );
+
+}
+
+prefix_ void senf::console::detail::DumbClientReader::showPrompt()
+{
+    std::string prompt (promptString());
+
+    stream() << prompt << std::flush;
+    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();
+}
+
+///////////////////////////////////////////////////////////////////////////
 // senf::console::Client
 
 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) 
+      handle_ (handle), name_ (name), reader_ (0)
 {
-    showPrompt();
-    ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
-                                        senf::membind(&Client::clientData, this) );
+    reader_.reset( new detail::DumbClientReader (*this) );
     route< senf::SenfLog, senf::log::NOTICE >();
 }
 
 prefix_ senf::console::Client::~Client()
 {}
 
-prefix_ void senf::console::Client::stopClient()
+prefix_ void senf::console::Client::stop()
 {
     // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS
     server_.removeClient(*this);
 }
 
-prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr helper)
+prefix_ void senf::console::Client::handleInput(std::string data)
 {
-    promptLen_ = 0;
-    if (helper->error() || handle_.eof()) {
-        // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS
-        stopClient();
-        return;
-    }
-
-    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
@@ -168,32 +212,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,
@@ -201,11 +237,9 @@ 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();
+    reader_->enablePrompt();
 }
 
 ///////////////////////////////cc.e////////////////////////////////////////
index d88e82f..5147dfd 100644 (file)
@@ -45,6 +45,81 @@ prefix_ void senf::console::Server::name(std::string const & name)
     name_ = name;
 }
 
+///////////////////////////////////////////////////////////////////////////
+// senf::console::detail::ClientReader
+
+prefix_ senf::console::detail::ClientReader::~ClientReader()
+{}
+
+prefix_ senf::console::Client & senf::console::detail::ClientReader::client()
+    const
+{
+    return client_;
+}
+
+prefix_ std::string senf::console::detail::ClientReader::promptString()
+    const
+{
+    return client().promptString();
+}
+
+prefix_ senf::console::detail::ClientReader::ClientHandle senf::console::detail::ClientReader::handle()
+    const
+{
+    return client().handle();
+}
+
+prefix_ senf::console::detail::ClientReader::OutputStream & senf::console::detail::ClientReader::stream()
+    const
+{
+    return client().stream();
+}
+
+prefix_ void senf::console::detail::ClientReader::stopClient()
+{
+    client().stop();
+}
+
+prefix_ void senf::console::detail::ClientReader::handleInput(std::string const & input)
+    const
+{
+    client().handleInput(input);
+}
+
+prefix_ void senf::console::detail::ClientReader::disablePrompt()
+{
+    v_disablePrompt();
+}
+
+prefix_ void senf::console::detail::ClientReader::enablePrompt()
+{
+    v_enablePrompt();
+}
+
+prefix_ senf::console::detail::ClientReader::ClientReader(Client & client)
+    : client_ (client)
+{}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::Client
+
+prefix_ std::string senf::console::Client::promptString()
+    const
+{
+    return name_ + ":" + executor_.cwd().path() + "$ ";
+}
+
+prefix_ senf::console::Client::ClientHandle senf::console::Client::handle()
+    const
+{
+    return handle_;
+}
+
+prefix_ senf::console::detail::NonblockingSocketOStream & senf::console::Client::stream()
+{
+    return out_t::member;
+}
+
 ///////////////////////////////cci.e///////////////////////////////////////
 #undef prefix_
 
index 59111e6..7a8b831 100644 (file)
@@ -76,10 +76,8 @@ namespace console {
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
-
-        typedef senf::ServerSocketHandle<
-            senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy, 
-                                    senf::UnspecifiedAddressingPolicy>::policy > ServerHandle;
+        
+        typedef detail::ServerHandle ServerHandle;
 
         ~Server();
 
@@ -128,12 +126,13 @@ namespace console {
 
         SENF_LOG_CLASS_AREA();
         SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
+
     public:
         typedef Server::ServerHandle::ClientSocketHandle ClientHandle;
 
         ~Client();
 
-        void stopClient();              ///< Stop the client
+        void stop();                    ///< Stop the client
                                         /**< This will close the client socket. */
 
     protected:
@@ -141,8 +140,11 @@ namespace console {
     private:
         Client(Server & server, ClientHandle handle, std::string const & name);
 
-        void clientData(ReadHelper<ClientHandle>::ptr helper);
-        void showPrompt();
+        std::string promptString() const;
+        ClientHandle handle() const;
+        detail::NonblockingSocketOStream & stream();
+
+        void handleInput(std::string input);
 
         virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream, 
                              std::string const & area, unsigned level, 
@@ -150,14 +152,14 @@ namespace console {
         
         Server & server_;
         ClientHandle handle_;
-        std::string tail_;
         CommandParser parser_;
         Executor executor_;
         std::string name_;
-        unsigned promptLen_;
         std::string lastCommand_;
+        boost::scoped_ptr<detail::ClientReader> reader_;
 
         friend class Server;
+        friend class detail::ClientReader;
     };
 
 }}
index eb6ab72..de12c41 100644 (file)
 
 namespace senf {
 namespace console {
+
+    class Server;
+    class Client;
+
 namespace detail {
 
     /** \brief Internal: Nonblocking boost::iostreams::sink
@@ -58,6 +62,67 @@ namespace detail {
 
     typedef boost::iostreams::stream<NonblockingSocketSink> NonblockingSocketOStream;
 
+    typedef senf::ServerSocketHandle<
+        senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy, 
+                                senf::UnspecifiedAddressingPolicy>::policy > ServerHandle;
+
+    /** \brief Internal: Generic client interface
+
+        The ClientReader encapsulates the interaction of a single network client with the user: It
+        manages prompt display and reading an interactive command.
+     */
+    class ClientReader
+    {
+    public:
+        typedef ServerHandle::ClientSocketHandle ClientHandle;
+        typedef NonblockingSocketOStream OutputStream;
+
+        virtual ~ClientReader() = 0;
+
+        Client & client() const;
+        std::string promptString() const;
+        ClientHandle handle() const;
+        OutputStream & stream() const;
+        void stopClient();
+        
+        void handleInput(std::string const & input) const;
+
+        void disablePrompt();
+        void enablePrompt();
+
+    protected:
+        ClientReader(Client & client);
+
+    private:
+        virtual void v_disablePrompt() = 0;
+        virtual void v_enablePrompt() = 0;
+
+        Client & client_;
+    };
+
+    /** \brief Internal: Primitive ClientReader implementation
+        
+        This implementation uses the cooked telnet mode to read lines from the console. It does not
+        support explicit line editing or any other advanced features.
+     */
+    class DumbClientReader
+        : public ClientReader
+    {
+    public:
+        DumbClientReader(Client & client);
+
+    private:
+        virtual void v_disablePrompt();
+        virtual void v_enablePrompt();
+
+        void clientData(senf::ReadHelper<ClientHandle>::ptr helper);
+        void showPrompt();
+
+        std::string tail_;
+        unsigned promptLen_;
+        bool promptActive_;
+    };
+    
 }}}
 
 ///////////////////////////////ih.e////////////////////////////////////////
index 0ede865..a1c3c2b 100644 (file)
@@ -71,6 +71,7 @@ void shutdownServer()
 
 int main(int, char **)
 {
+    ::signal(SIGPIPE, SIG_IGN);
     senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
 
     senf::console::root()
index a11ac98..781b5e6 100644 (file)
@@ -75,6 +75,13 @@ prefix_ unsigned senf::TCPSocketProtocol::siocoutq()
     return n;
 }
 
+prefix_ void senf::TCPSocketProtocol::shutdown(ShutType type)
+    const
+{
+    if (::shutdown(fd(), type) < 0)
+        SENF_THROW_SYSTEM_EXCEPTION("");
+}
+
 prefix_ unsigned senf::TCPSocketProtocol::available()
     const
 {
index 2fb2b3e..b9850d1 100644 (file)
@@ -59,6 +59,9 @@ namespace senf {
         unsigned siocinq() const;       ///< Return current size of the input queue
         unsigned siocoutq() const;      ///< Return current size of the output queue
 
+        enum ShutType { ShutRD, ShutWR, ShutRDWR };
+        void shutdown(ShutType type) const;
+
         ///\name Abstract Interface Implementation
         ///@{
 
index 9457d8c..94d95ea 100644 (file)
@@ -541,6 +541,9 @@ prefix_ void senf::Daemon::installSighandlers()
 
     ::sigaction(SIGHUP,   &sa, NULL);
 
+    sa.sa_handler = SIG_IGN;
+    ::sigaction(SIGPIPE, &sa, NULL);
+
 #ifdef SENF_DEBUG
     sa.sa_sigaction = &fatalSignalsHandler;
     sa.sa_flags = SA_RESTART | SA_SIGINFO;