From: g0dil Date: Wed, 23 Apr 2008 23:34:08 +0000 (+0000) Subject: Socket/Protocols/INet: Add 'shutdown' member to TCPSocketProtocol X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=6e417a227d417cef20d55af94e2474fbe9c280b8;p=senf.git Socket/Protocols/INet: Add 'shutdown' member to TCPSocketProtocol 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 --- diff --git a/Console/Server.cc b/Console/Server.cc index 53db4e7..13820cf 100644 --- a/Console/Server.cc +++ b/Console/Server.cc @@ -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::dispatch( handle(), 16384u, ReadUntil("\n"), + senf::membind(&DumbClientReader::clientData, this) ); +} + +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::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::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::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::ptr hel try { if (! parser_.parse(data, boost::bind( 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().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::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//////////////////////////////////////// diff --git a/Console/Server.cci b/Console/Server.cci index d88e82f..5147dfd 100644 --- a/Console/Server.cci +++ b/Console/Server.cci @@ -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_ diff --git a/Console/Server.hh b/Console/Server.hh index 59111e6..7a8b831 100644 --- a/Console/Server.hh +++ b/Console/Server.hh @@ -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::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 reader_; friend class Server; + friend class detail::ClientReader; }; }} diff --git a/Console/Server.ih b/Console/Server.ih index eb6ab72..de12c41 100644 --- a/Console/Server.ih +++ b/Console/Server.ih @@ -34,6 +34,10 @@ 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 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::ptr helper); + void showPrompt(); + + std::string tail_; + unsigned promptLen_; + bool promptActive_; + }; + }}} ///////////////////////////////ih.e//////////////////////////////////////// diff --git a/Console/testServer.cc b/Console/testServer.cc index 0ede865..a1c3c2b 100644 --- a/Console/testServer.cc +++ b/Console/testServer.cc @@ -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() diff --git a/Socket/Protocols/INet/TCPSocketProtocol.cc b/Socket/Protocols/INet/TCPSocketProtocol.cc index a11ac98..781b5e6 100644 --- a/Socket/Protocols/INet/TCPSocketProtocol.cc +++ b/Socket/Protocols/INet/TCPSocketProtocol.cc @@ -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 { diff --git a/Socket/Protocols/INet/TCPSocketProtocol.hh b/Socket/Protocols/INet/TCPSocketProtocol.hh index 2fb2b3e..b9850d1 100644 --- a/Socket/Protocols/INet/TCPSocketProtocol.hh +++ b/Socket/Protocols/INet/TCPSocketProtocol.hh @@ -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 ///@{ diff --git a/Utils/Daemon/Daemon.cc b/Utils/Daemon/Daemon.cc index 9457d8c..94d95ea 100644 --- a/Utils/Daemon/Daemon.cc +++ b/Utils/Daemon/Daemon.cc @@ -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;