Skip to content
Snippets Groups Projects
Commit 5d600269 authored by g0dil's avatar g0dil
Browse files

Socket/Protocols/INet: Add 'shutdown' member to TCPSocketProtocol

Utils/Daemon: Ignore SIGPIPE signals by default
Console: Refactor console server to prepare for readline integration
parent 5e4ab772
No related branches found
No related tags found
No related merge requests found
...@@ -125,42 +125,86 @@ prefix_ void senf::console::Server::removeClient(Client & client) ...@@ -125,42 +125,86 @@ prefix_ void senf::console::Server::removeClient(Client & client)
clients_.erase(boost::intrusive_ptr<Client>(&client)); clients_.erase(boost::intrusive_ptr<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 // senf::console::Client
prefix_ senf::console::Client::Client(Server & server, ClientHandle handle, prefix_ senf::console::Client::Client(Server & server, ClientHandle handle,
std::string const & name) std::string const & name)
: out_t(handle), senf::log::IOStreamTarget(out_t::member), server_ (server), : out_t(handle), senf::log::IOStreamTarget(out_t::member), server_ (server),
handle_ (handle), name_ (name), promptLen_(0) handle_ (handle), name_ (name), reader_ (0)
{ {
showPrompt(); reader_.reset( new detail::DumbClientReader (*this) );
ReadHelper<ClientHandle>::dispatch( handle_, 16384u, ReadUntil("\n"),
senf::membind(&Client::clientData, this) );
route< senf::SenfLog, senf::log::NOTICE >(); route< senf::SenfLog, senf::log::NOTICE >();
} }
prefix_ senf::console::Client::~Client() 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 // THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER removeClient RETURNS
server_.removeClient(*this); 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()) if (data.empty())
data = lastCommand_; data = lastCommand_;
else else
...@@ -168,32 +212,24 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel ...@@ -168,32 +212,24 @@ prefix_ void senf::console::Client::clientData(ReadHelper<ClientHandle>::ptr hel
try { try {
if (! parser_.parse(data, boost::bind<void>( boost::ref(executor_), if (! parser_.parse(data, boost::bind<void>( boost::ref(executor_),
boost::ref(out_t::member), boost::ref(stream()),
_1 )) ) _1 )) )
out_t::member << "syntax error" << std::endl; stream() << "syntax error" << std::endl;
} }
catch (Executor::ExitException &) { catch (Executor::ExitException &) {
// THIS COMMITS SUICIDE. THE INSTANCE IS GONE AFTER stopClient RETURNS // This generates an EOF condition on the Handle. This EOF condition is expected
stopClient(); // 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; return;
} }
catch (std::exception & ex) { catch (std::exception & ex) {
out_t::member << ex.what() << std::endl; stream() << ex.what() << std::endl;
} }
catch (...) { 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, 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, ...@@ -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 & area, unsigned level,
std::string const & message) std::string const & message)
{ {
if (promptLen_) reader_->disablePrompt();
out_t::member << '\r' << std::string(' ', promptLen_) << '\r';
IOStreamTarget::v_write(timestamp, stream, area, level, message); IOStreamTarget::v_write(timestamp, stream, area, level, message);
if (promptLen_) reader_->enablePrompt();
showPrompt();
} }
///////////////////////////////cc.e//////////////////////////////////////// ///////////////////////////////cc.e////////////////////////////////////////
......
...@@ -45,6 +45,81 @@ prefix_ void senf::console::Server::name(std::string const & name) ...@@ -45,6 +45,81 @@ prefix_ void senf::console::Server::name(std::string const & name)
name_ = 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/////////////////////////////////////// ///////////////////////////////cci.e///////////////////////////////////////
#undef prefix_ #undef prefix_
......
...@@ -76,10 +76,8 @@ namespace console { ...@@ -76,10 +76,8 @@ namespace console {
public: public:
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Types // Types
typedef senf::ServerSocketHandle< typedef detail::ServerHandle ServerHandle;
senf::MakeSocketPolicy< senf::TCPv4SocketProtocol::Policy,
senf::UnspecifiedAddressingPolicy>::policy > ServerHandle;
~Server(); ~Server();
...@@ -128,12 +126,13 @@ namespace console { ...@@ -128,12 +126,13 @@ namespace console {
SENF_LOG_CLASS_AREA(); SENF_LOG_CLASS_AREA();
SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE ); SENF_LOG_DEFAULT_LEVEL( senf::log::NOTICE );
public: public:
typedef Server::ServerHandle::ClientSocketHandle ClientHandle; typedef Server::ServerHandle::ClientSocketHandle ClientHandle;
~Client(); ~Client();
void stopClient(); ///< Stop the client void stop(); ///< Stop the client
/**< This will close the client socket. */ /**< This will close the client socket. */
protected: protected:
...@@ -141,8 +140,11 @@ namespace console { ...@@ -141,8 +140,11 @@ namespace console {
private: private:
Client(Server & server, ClientHandle handle, std::string const & name); Client(Server & server, ClientHandle handle, std::string const & name);
void clientData(ReadHelper<ClientHandle>::ptr helper); std::string promptString() const;
void showPrompt(); ClientHandle handle() const;
detail::NonblockingSocketOStream & stream();
void handleInput(std::string input);
virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream, virtual void v_write(boost::posix_time::ptime timestamp, std::string const & stream,
std::string const & area, unsigned level, std::string const & area, unsigned level,
...@@ -150,14 +152,14 @@ namespace console { ...@@ -150,14 +152,14 @@ namespace console {
Server & server_; Server & server_;
ClientHandle handle_; ClientHandle handle_;
std::string tail_;
CommandParser parser_; CommandParser parser_;
Executor executor_; Executor executor_;
std::string name_; std::string name_;
unsigned promptLen_;
std::string lastCommand_; std::string lastCommand_;
boost::scoped_ptr<detail::ClientReader> reader_;
friend class Server; friend class Server;
friend class detail::ClientReader;
}; };
}} }}
......
...@@ -34,6 +34,10 @@ ...@@ -34,6 +34,10 @@
namespace senf { namespace senf {
namespace console { namespace console {
class Server;
class Client;
namespace detail { namespace detail {
/** \brief Internal: Nonblocking boost::iostreams::sink /** \brief Internal: Nonblocking boost::iostreams::sink
...@@ -58,6 +62,67 @@ namespace detail { ...@@ -58,6 +62,67 @@ namespace detail {
typedef boost::iostreams::stream<NonblockingSocketSink> NonblockingSocketOStream; 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//////////////////////////////////////// ///////////////////////////////ih.e////////////////////////////////////////
......
...@@ -71,6 +71,7 @@ void shutdownServer() ...@@ -71,6 +71,7 @@ void shutdownServer()
int main(int, char **) int main(int, char **)
{ {
::signal(SIGPIPE, SIG_IGN);
senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >(); senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
senf::console::root() senf::console::root()
......
...@@ -75,6 +75,13 @@ prefix_ unsigned senf::TCPSocketProtocol::siocoutq() ...@@ -75,6 +75,13 @@ prefix_ unsigned senf::TCPSocketProtocol::siocoutq()
return n; return n;
} }
prefix_ void senf::TCPSocketProtocol::shutdown(ShutType type)
const
{
if (::shutdown(fd(), type) < 0)
SENF_THROW_SYSTEM_EXCEPTION("");
}
prefix_ unsigned senf::TCPSocketProtocol::available() prefix_ unsigned senf::TCPSocketProtocol::available()
const const
{ {
......
...@@ -59,6 +59,9 @@ namespace senf { ...@@ -59,6 +59,9 @@ namespace senf {
unsigned siocinq() const; ///< Return current size of the input queue unsigned siocinq() const; ///< Return current size of the input queue
unsigned siocoutq() const; ///< Return current size of the output 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 ///\name Abstract Interface Implementation
///@{ ///@{
......
...@@ -541,6 +541,9 @@ prefix_ void senf::Daemon::installSighandlers() ...@@ -541,6 +541,9 @@ prefix_ void senf::Daemon::installSighandlers()
::sigaction(SIGHUP, &sa, NULL); ::sigaction(SIGHUP, &sa, NULL);
sa.sa_handler = SIG_IGN;
::sigaction(SIGPIPE, &sa, NULL);
#ifdef SENF_DEBUG #ifdef SENF_DEBUG
sa.sa_sigaction = &fatalSignalsHandler; sa.sa_sigaction = &fatalSignalsHandler;
sa.sa_flags = SA_RESTART | SA_SIGINFO; sa.sa_flags = SA_RESTART | SA_SIGINFO;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment